In the winter semester of 2023, our "Hacker Contest" will be held again at Technical University of Darmstadt (TU). In the popular course, students get real insights into IT security and gain hands-on experience with tools and methods to search for vulnerabilities in networks and systems within our PentestLab.
As in every semester, prospective participants took on the Hacker Contest Challenge to qualify for participation.
If you are curious to know what a Hacker Contest Challenge looks like, or which flags you might have missed this time: This is our sample solution for the winter semester Hacker Contest Challenge.
Table of Contents
Scenario
Your friend found an old hard drive in his drawer that contains his old operating system. He can still remember his passwords, but lost six very important access tokens for his Bitcoin-Wallet from 2017. They are hidden somewhere on the hard drive, but he doesn't remember where and doesn't know how to regain access. This is a welcome change from the usual printer issues by family members, and since he is a very good friend, you agree to help him.
Challenge
You are provided with a virtual disk image. It is your task to find all six tokens hidden somewhere on it.
All tokens are in the format of usd{$32_digit_hex_number}.
Since we already have passwords for both users, logging in is not a problem:
user: asdf root: usdsuperhero
Disclaimer
This challenge is a fairly artificial one, more akin to a CTF challenge than anything one would encounter in a real-world scenario. However, having a local disc image provides an opportunity to test for weaknesses that usually are not part of remotely hosted CTF challenges, such as offline brute-forcing.
Tokens
By examining the file system, we initially find some files not usually present in the user's home directory. Those files are worthy of further investigation but finding all six tokens requires a bit more digging. In the following, we go through each token on the machine in no particular order.
USB_IMAGE
The token is located in /home/user/Desktop/USB_IMAGE.img.
As we might suspect from the filename, the file contains a USB image. We can try to mount the USB image, but we are not able to find anything on it, since its contents were deleted. We could use elaborate forensic tools in order to recover the deleted contents, but the easiest solution merely requires running strings on the image in order to recover the token:
strings /home/user/Desktop/USB_IMAGE.img
Encrypted PDF
A hidden file is located in /home/user/.secret.pdf, which can be revealed using ls -la /home/user.
Since it is encrypted, we have to first extract the PDFs hash using John the Ripper:
pdf2john .secret.pdf >> hash
This makes it possible to view the hash separately from its original file and brute-force the password.
The password is part of the popular rockyou wordlist and can therefore be easily found.
john hash --wordlist=/usr/share/wordlists/rockyou.txt
Polyglot
The next token we will look at is located in /home/user/Picures/sloth.jpg.
We cannot open the file using an image viewer because contrary to its file extension sloth.jpg is a zip file.
We therefore can rename it to avoid further confusion.
mv sloth.jpg token.zip
We then have to repair its offset with
zip -FF token.zip --out fixed_token.zip
After that, we can unzip the repaired file, but it still has password protection. Therefore, we have to crack the zip's encryption. Again, we can use John the Ripper to do so.
zip2john fixed_token.zip >> hash2 john hash2 --wordlist=/usr/share/wordlists/rockyou.txt
Now we can unzip the file, using the revealed password.
unzip fixed_token.zip
Encrypted PDF 2
Two interesting files are located in the users Documents directory, important.enc.pdf and encryption_guidelines.txt.
We can use the hint in encryption_guidelines.txt in order to generate our own password wordlist. There are many different ways to do so. We could write a simple script, but there is also a useful and easy-to-use GitHub project we can use to generate the dates. We still have to prepend the username, which in our case is user.
python3 date_generator.py 1900 2030 0 > custom_date_wordlist sed -e 's/^/user/' custom_date_wordlist > accurate_list
Then we can use John the Ripper again to crack the PDF.
pdf2john important.enc.pdf > pdf_hash john pdf_hash --wordlist accurate_list
Find the hidden program
There is another hidden program with an interesting name, located in /usr/bin/thide .
This program is hidden by some shared library for tools like ps.
After locating the binary, we could reverse-engineer it with tools like ghidra in order to find out that it opens a connection to a socket and sends some data. We can then run ps -ef | grep thide which reveals that the program is running, as well as its process ID. Using wireshark, we can observe the local network traffic and see that there are several connection attempts to port 63333 being made. We now can listen on port 63333 and get the token.
nc -lvp 63333
Reverse engineer binary
The last token we look for can be found in the executable located at /home/user/bin/token.
It is necessary to run chmod -R 777 /home/user/bin which gives 'user' access to the directory.
We can use ghidra to reverse engineer the program. There are many different ways to solve this challenge. The following code serves as an example for one possible solution to the problem.
#!/usr/bin/env python3 # $ python3 cobb_solver.py /home/user/bin/token import os import re import sys if len(sys.argv) != 2: print("usage: {} <path to cobb>".format(sys.argv[0])) sys.exit() #call cobb to get the character count cobb_stream = os.popen(sys.argv[1]) char_count = int(re.search('<(.*) character', cobb_stream.read()).group(1)) print("app has a {} byte key".format(char_count)) byte_array = [] for i in range(0, char_count * 8): byte_array.append(False) objdump_stream = os.popen("objdump -d {}".format(sys.argv[1])) prev_func_number = None for line in objdump_stream: #0000000000001273 <check_2>: function_def_match = re.search('^00000.* <check_(.*)>:$', line) #1291: 0f 95 c0 setne %al bit_set_match = re.search('setne \%al', line) if bit_set_match and prev_func_number is not None: byte_array[int(prev_func_number)] = True prev_func_number = None if function_def_match: prev_func_number = int(function_def_match.group(1)) sys.stdout.write("The key is \"") character = 0 for idx,elem in enumerate(byte_array): if elem: character = character | (1 << idx % 8) if idx % 8 == 7: sys.stdout.write(chr(character)) character = 0 sys.stdout.write("\"\n")