This was the first CTF organized by McAfee Advanced Threat Research Team. It was held from February 5th, 2021 to February 18th, 2021 and was initially made for their internal employees.
I was able to complete all but one challenge (the crypto One Time Only!) and finished at the 9th place:
Here is my write-up…
WEB 100 - A DNS query to rule them all!
This was the unique web challenge and was a basic command injection. We acces a webpage where we can do DNS queries:
A basic request returns the following:
This look like the result of a script. Behind the scene what is probably done run is something like:
So we can try to add
;ls after the website name to try to inject system commands:
That’s it, we can read the flag with
MOBILE 100 - Looking for Droids?
The following APK is an application that contains encrypted passwords. Your task, should you choose to accept, is to find the flag in one of these passwords.
unzip the APK and then transform the
classes.dex file to a
jar file with
We can then use
jd-gui to disassemble and view the application source code. The
MainActivity.class shows that the application is protected by the password
notthefl@g. The password is then store in the
SECRET variable and the intent
PwdContainer is triggered:
PwdContainer.class shows that multiple strings are decrypted using AES with the password stored in the
AES.class shows that the mode used is
AES/ECB/PKCS5Padding and that the key is compose of the first 16 bytes of the password’s SHA-1 hash:
Now we have all the needed information and we can simply re-use the available code to decrypt all the strings:
The output contains our flag:
HW 200 - Shack the Secret
A researcher has discovered a file that has been encrypted with AES-128-ECB by an embedded device. The encrypted file has been captured through network analysis and the raw file is called Blob. With no debug ports available on the embedded device, one must extract the encryption key. Luckily, we have captured IC bus traffic while exercising functionality.
We get 2 shady files in the archive:
.logicdata file can be opened with tools like Salae. We open the file in Saleae and select the I2C protocol as the analyzer with the default settings. We see the password in the decoded protocol window:
The full password is
safepasswordcomm. We can now decrypt the
blob file with the following
openssl command and read the flag:
$ openssl aes-128-ecb -in blob -d -out flag.txt
$ cat flag.txt
RECON 200 - A Picture is Worth a Thousand Vulns
The image shows a D-LINK AC1200 Range Extender:
And we access a webpage with a few questions to answer about the product:
We find the answer to the first question on a Tom’s Hardware product review:
By looking at the specs of this SOC/CPU, we get the second answer:
To get the Linux kernel version we have to download the latest firmware. We can get it here. Make sure to download the firmware (1.03.B07) and not a hotfix.
After unzipping the archive we can analyze the binary file with
The firmware files are stored in a Squashfs filesystem. We can extract it with
binwalk -e DAP1650_FW103WWb07.bin. If
sasquatch is installed it will automatically extract the filesystem. If not, we can use
unsquashfs to retrieve all the files.
The filesystem is found under the
We can usually find the kernel version in files like
/var/log/dmesg but those files are missing. Another way to find this out is to get the
vermagic value from a kernel module:
By looking at the list of binaries in
/usr/bin we get the fourth answer:
Finally, to find the password used by
telnetd we can search where the deamon is started with
grep -Ri telnetd . and we get a hit in
The username is
Alphanetworks and the password is stored in the file
Once we submit all the answers, we get the flag:
FOR 200 - Password in a Haystack
You have acquired the Strings (*nix command) output for a file. You know that a user’s password is somewhere in the file and need to retrieve it. The username is steve557. Find the password based on the password rules. Only one string will match these rules.
– All passwords must be 6-12 printable characters in length.
– Each password must contain at least 3 unique digits
– Passwords cannot contain 3 consecutive characters of your username nor its reverse. This is case insensitive.
output.txt contains a huge list of possible passwords:
We just need to follow the rules in the description to find a unique password. The following Python code does exactly that:
The ouput is:
FOR 300 - Not Software, not Hardware
I grabbed this file from a Wireshark capture of one of my home devices doing an update of some kind. Can you find the flag?
We analyze the binary file with
This is the image of a firmware that contains a
squashfs filesystem. This is similar to what we had in A Picture is Worth a Thousand Vulns challenge. The same procedure can be followed to extract the filesystem.
We find a hidden folder
/root/.secret/ that contains the encrypted flag
flag.enc and a
README.txt that says:
This is the directory where I store all of my encrypted files.
I encrypt them using: echo “secret” | openssl enc -aes256 -salt -out file.enc -e -a -pbkdf2 -k ‘PASSWORD’
and decrypt them using: openssl enc -aes256 -in file.enc -out file.txt -d -a -pbkdf2
I haven’t gotten a password manager yet but I know my login password is supper secure so I don’t see a need…
From this message, we can guess that the file has been encrypted with a system user. Let’s look that the
root has a password set. Let’s try to crack it with
hashcat and the
rockyou.txt wordlist. The hash starts with
$6$ which means that it is a SHA512 hash and
19yJir3t is the salt:
But we get no result. After having tried with some
hashcat rules, we find the right one:
After almost 6h, we get the
P@55w0rd!. Maybe I should review the wordlists I use!
We can now decrypt the flag with
enter aes-256-cbc decryption password:
opensslis LibreSSL and does not support PBKDF2. Use
brew install opensslthen make sure to include the binary folder into your
REV 300 - Know your header (part II)
When you have solved the second part of “Know your Header”, you can enter the flag here.
You will need to have solved “Know your Header (Part I)” for this challenge. Use the binary from Part I, named “challenge_encoded” to solve this.
To complete this one, we need first to complete the challenge Know your header (part1). We get a
challenge binary that is a Linux binary:
challenge: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=2caaf9304b646a1f86ff892c534aa1483ae6c350, not stripped
When we launch, it we get:
To decode the flag, run:
Hint: IDA Free and Ghidra are great tools…
So let’s open it in Ghidra. The binary is not stripped so it will be easier to analyze. The
main function is simple:
transform_password() and if the returned value is
good job!, the flag is printed. The function looks like:
This is a basic XOR encryption and the key is
0x66. By XORing
good job! with
0x66 we get the password. For instance, in Python:
We can now validate the challenge:
…transforming 01090902460c090447 to normal string
Decoded string: F
Decoded password: good job!
The last 2 characters were not correct, the final flag is
REV 400 - Two’s Company
File me once, shame on you. File me twice, shame on me!
Let’s use again Ghidra to analyze the binary. All is happening in the function
FUN_0001124d. Two files are opened and checked. If all the checks pass, we get the message:
Now, figure out how to send your solution(s) to the fileserver at http://challenges.ctfd.io:30465/”
The details of the first check:
file1 will be:
00000000: 460a 4c0a 410a 0a0a 4731 F.L.A…G1
The second check is:
file2 will be:
00000000: 0046 004c 0041 0000 0000 0047 3200 0000 .F.L.A.....G2...
00000010: 0000 0000 0000 0000 0000 0000 0000 ..............
We upload the 2 files on the server to get the flag:
RF 400 - Smell like ham to you?
You find 3 files of the form #_samplerate_frequency_bandwidth.iq:
These files were captured over the air via a software-defined radio. You have a suspicion these broadcasts contain valuable information. As such, you have decided to try to determine the contents of each file.
We start with the first file. We have an IQ file that is the raw data of a radio capture. We have some specs of the signal in the filename. We know that it has been captured at a frequency of 50.09Mhz and than the sampling rate is of 2Msps. We can import the IQ file in Audacity for analysis. Click the menu
File -> Import -> Raw Data then simply change the sampling rate to 2Mhz. We play the file and quickly recognize Morse code. We can even “see” it:
The Morse code is:
Once decoded we get the specs of the second signal:
sample 2 was captured at 146520khz samp rate 2M.
For the second file, I had some issues importing it correctly into Audacity. Seems like it does not handle high frequencies very well. The solution I found was to convert the IQ file to WAVE with a Windows tool called
Back into Aucacity but nothing obvious came out. I played quite a long time with effects until I found out that I could hear some voice when playing it at four times the normal speed. But it was hardly audible and I could not make any sense of it. I exported accelerated sound and I tried some more tools. Finally, another Windows tool, SDRSharp, made it clearer by using the FM mode:
After playing a little with the frequency, we get the following message:
We clearly hear:
The third capture was trickier to get. By searching online, we quickly get that the frequency used is the one used by Slow-scan television (SSTV). We start by converting the IQ file to WAVE:
I tried many different tools for this one like Audacity, SDRSharp, qsstv, gnuradio and mmsstv …and failed for days. In the end, what worked was a combination of gqrx to play the sound and the Android application Robot36 to capture it.
You can follow this procedure to install gqrx on an Ubuntu box or use the macOS package. This is the device configuration I used:
The full device string is:
Then you have to first start the DSP (the start button) before starting the IQ file:
We start our mobile app in auto mode and play with the frequency in gqrx to find the right spot where we start to see an image. At around 7.1773kHz, the mode switched to Scottie1 and we started to see something:
The flag is:
CRYPTO 100 - Light Switch Crypto
Here is a 3-way light switch. Look closely, it may help you uncover the secret message! Solve the secretly coded message to capture the flag!
We are given an encrypted flag, a key file and a piece of Python code that we need to complete in order to decrypt the flag. With that, comes an image that explains how the flag is encrypted:
We have as well some instructions in the source code:
If we take the rules of the circuits one by one:
- Circuit 1 is an OR gate
- Circuit 2 is an NAND gate
- Circuit 3 is an AND gate
The overall circuits correspond to an XOR gate and the only line we need to add to the
o = A^B. The output of the script is:
CRYPTO 200 - IVe seen sites like this before
The website shows that we don’t have enough privileges to access it:
The URL we are redirected to is:
In the source code we get some comments that leak that the session ID is based on AES-256-CBC:
<!– 09/15/2018 - Changed from DES to AES-256-CBC after our latest data breach –>
<!– 05/23/2018 - Todd is and idiot and somehow never knows how to login so I have added the encryption IV and the encrypted session to the URL parameters –>
<!– 03/12/2018 - String compare of the users is acting up I found a different way to validate users –>
<!– 02/08/2018 - Moved the default user from root to unprivlaged users. This way only admins can access this page –>
We can try to play with the IV value and flip some bits to see how it impacts the decryption. We can automate this with Burp Intruder and the Bit Flipper payload. We launch the attack and see a call that returns something different. If we look at the response, we see that the UID changed to 0 and we got the flag:
CRYPTO 200 - Know your header (part I)
The challenge was encrypted, but we have the encryption script. Can you figure out the key?
For this challenge, the flag is the key to decrypt the next stage “Know your header (part II)”. The challenge will become available if you solve part I.
We get a few files from the given archive. The
The next challenge was encoded and we don’t know the key.
We were able to recover the script used to encode the challenge and a hint binary.
What we know is that it is a Linux binary running on a x64 Ubuntu 18.04.
Maybe by looking at the encoding algorithm and the hint binary you can figure out how to decode it?
The flag for this challenge is the ATR[key] where key is the key you found, in hex, and lower case
(for example, if the key is 5566778899aabbcc, the flag will be ATR[5566778899aabbcc])
We get as well the encrypted binary called
challenge_encoded and a
hint binary. When launched, the binary outputs:
I do nothing but I still can be helpful….
Did you know all linux binaries have a ELF header ?
We all start the same, isn’t that cool?
The last piece of information is the source code used to encrypt the binary. We get that the key is <=8 characters:
Knowing what the ELF first 8 bytes are, we can simply XOR them with the first 8 bytes of the encrypted binary to get the key. If this does not work, we try with the first 7 bytes..and so on.
This gives us the key:
9f05a8dd940a15dd. We can try to decode the binary with:
And 8 was the right size. The flag is:
This unlocks the second part of the challenge: Know your header (part II)
PWN 500 - A Winning Attitude
This binary has a function named “winner” which is never executed. Your task is to find a vulnerability in the binary and leverage it to execute “winner”. When “winner” is executed the flag will be sent to you.
The link above is running the executable and you can connect to it with netcat. Please use it to retrieve the flag after you get your exploit working on the binary.
We get an ELF binary with the following protections:
- Partial RELRO sets the
.gotsection as read-only, however, the section
.got.plt(PLT-dependent GOT) stays writable. You can find here a good explanation of those different binary sections,
- NX sets the stack as non-executable.
When we execute the binary, we can send 2 strings and we receive an acknowledgement each time:
Let’s open the binary in Ghidra. The binary is not stripped so this will be easier to analyze. The
main application looks like:
We have a buffer overflow on the heap. With the first overflow, we have control over the address where the second
strcpy call will write. The idea is to write the address of the
winner function in place of the address of a system call in the GOT. We can for instance replace the address of
puts that is called right after.
Let’s disable ASLR and have a look at the heap with
The last information we need to build our exploit is the address of
puts in the GOT and of the
winner function we have to call:
We can first test the exploit locally. For that we can create a file
flag.txt with any content and see if our exploit outputs its content. Then we launch it on the remote service. The full exploit is:
We finally get the flag:
PWN 500 - Shellcode Hollaback
We access the folowing webpage:
Ok, so we need to call the
store_credentials function located at
0x5555555551b5 and that takes 2 buffers as argument for the username and password. We are on a 64-bit Linux and have a limitation of 64 bytes for the shellcode.
Knowing that the calling convention for 64-bit is the following:
- first function argument must be stored in
- second function argument must be stored in
we come up with the following assembly code:
To transform this into hex bytes we can use the shell-storm online assembler. We get the following shellcode (33 bytes) that we can pass to the webpage:
We are successful and our credentials were injected into the database. We can log in to get the flag: