CyberCheese CTF (CCC) writeups

Robin François
Blog cybersécurité

In view of expanding its technical team, Navixia decided to recruit a new security engineer who would be able both to integrate security solutions provided by Navixia and take part in security assessments.

In order to best identify suitable candidates for this new position, Navixia decided to organise a short Capture The Flag (CTF) challenge that would mirror the situations encountered in the engineer's daily work and underline the various skills Navixia is looking for.

Unlike most CTFs, this one was targeting solo participants (as opposed to teams) and was not scored based. Like some teasers CTFs, this CTF was a succession of challenges, each unlocking the access to the next one. The final challenge would provide the participant with a Navixia contact email address by which he could apply for the open position.

Organisation

Hosting a CTF is a risky business: skilled players will look for vulnerabilities by all possible methods, less-skilled players will stress the servers with their scanning tools and finally disrespectful participants might try to disrupt the challenges by way of DDoS attacks.

In order to protect Navixia's current infrastructure against such problems, we decided to rely on cloud resources:

  • We used Google Drive to host the files for files-based challenges
  • For challenges that required a server-side service, we used Microsoft Azure virtual machines
  • We used Google Docs to provide instructions between challenges, with the advantage that they could be easily updated during the CTF
  • Google URL Shortener was used for all links provided to the participants, which provided enough statistics to perform basic progress monitoring.

Challenge 1 - DNS Zone Transfer (Recon)

This first challenge was simple yet it reflected real-world issues that Navixia often identifies when performing security assessments.

This challenge is based on an ill-configured DNS server that allows anyone to perform a zone transfer. Zone transfer (also known as AXFR) is a type of DNS query that allows DNS replication: the result of the query is a copy of the current configuration for the queried DNS zone.

Performing a zone transfer on the DNS server provides the zone configuration, revealing the first flag and additional information:

$ dig -t AXFR cybercheese.io @40.115.53.104
[...]
cybercheese.io. 600 IN SOA ns.cybercheese.io. root.cybercheese.io. 2015101901 10800 600 604800 600
cybercheese.io. 600 IN NS ns.cybercheese.io.
cybercheese.io. 600 IN A 40.115.53.104
file.cybercheese.io. 600 IN TXT "02b4520388c01f012c8a52bfa9f9140cfe4c6fdd1884ada7ef849a4e533b3451055325a4 ...
file.cybercheese.io. 600 IN TXT "068d19000340000000006d434c868c9a321a34f534c9a6441a0d003200d1a0000064c800 ...
file.cybercheese.io. 600 IN TXT "0c80034d00680001ea000003434d1a1a00d00000000680000000007a4d00f95c1193209a ...
file.cybercheese.io. 600 IN TXT "0e51541ce1c3d2aeaf5dea313f0c1d217c3d488a73ac4450a4b9406532e27f90b205c238 ...
file.cybercheese.io. 600 IN TXT "425a6839314159265359880751450000917fffffffddff75fd3f3ff7e57fb7ef8fff7dff ...
file.cybercheese.io. 600 IN TXT "7dbaa2229614a6192320713c5098d5c3fc590a70b546f281733c1e7577a7a070914168eb ...
file.cybercheese.io. 600 IN TXT "f2ea3bd4c344aafa3264860f358822dcd7459cc7573eb07c01fa58d5c2abe0d1785d5435 ...
file.cybercheese.io. 600 IN TXT "f519e37ac851700ee8c4260ad95fb864742f42724d4f7c2309424a31ea48cae1070e1148 ...
**flag1.cybercheese.io. 600 IN TXT "CCC1{Thr33_R1ngs_f0r_th3_3lv3n-k1ngs_und3r_th3_sky}"**
hint.cybercheese.io. 600 IN TXT "Unshuffle_and_decompress_me"
ns.cybercheese.io. 600 IN A 40.115.53.104

The first challenge is solved. The zone transfer also includes interesting entries (hint and file) which are the second challenge.

Challenge 2 - Shuffled Archive (PPC)

From the first challenge zone transfer, we recovered TXT entries for file.cybercheese.io and a hint:Unshuffle and decompress me

This hint should lead the participant to understand that the file.cybercheese.io DNS entry are shuffled pieces of a compressed archive converted to hexadecimal.

To solve the challenge, a player will need to write a program that reassembles the various shuffled parts into the correct file and then decompress it.

To guess which kind of archive file is used, a talented player will look at the beginning of each piece and try to find the header of a specific file format. He will quickly find that the piece that start with "7dba" matches the beginning of a bzip2 archive file.

However, to be absolutely certain, a player will check the filetype of any permutations of the 8 parts. The following Python program will output, for each permutation, the result of a filetype identification (using the magic python module).

import itertools, magic
parts = [
'''02b4520388c01f012c8a52bfa9f9140cfe4c6fdd1884ada7ef849a4e533b3451055325a4f279
e116d400a74b3afd7a6a3d5de9256e030d541557443eba4747708e95ff177245385090880751450''',
'''068d19000340000000006d434c868c9a321a34f534c9a6441a0d003200d1a0000064c8000000
3d4d000d0068c80343201e9a8d340f51a1a341a00064347a4680003405451b48d3d4681a0190340''',
'''0c80034d00680001ea000003434d1a1a00d00000000680000000007a4d00f95c1193209a7281
e471029b5b4a08000740646d6d12c40cea9b736fe9818b9824b377db3c41b1ad95cb98402483588''',
'''0e51541ce1c3d2aeaf5dea313f0c1d217c3d488a73ac4450a4b9406532e27f90b205c2382200
0264a995f461e0da824c79c80351dea802d056e5950510837f0aee2954b0e29f2c37d8f0e1660e0''',
'''425a6839314159265359880751450000917fffffffddff75fd3f3ff7e57fb7ef8fff7dfffffd
6bbf9fff73fdcdf66feff7ff54b0015962320d0d341a001a34d00d069a0068000068d069a341a00''',
'''7dbaa2229614a6192320713c5098d5c3fc590a70b546f281733c1e7577a7a070914168eb2351
ca19f6301742f3bb2501e9c545f84539e7408759035380c8876b7c8cbfcbc43eb56e79121c3fa2c''',
'''f2ea3bd4c344aafa3264860f358822dcd7459cc7573eb07c01fa58d5c2abe0d1785d54352014
0b19ea010c9ab84edd144097ca644221740d2e5fedf3fa5ebc94e92b2608cfa68f673bb6a0b11c4''',
'''f519e37ac851700ee8c4260ad95fb864742f42724d4f7c2309424a31ea48cae1070e1148bb0e
221c7b834eeb2080995fead718739dbe54213ebdfcc993037832de55264f0dd9c30781fecaa7c43'''
]
for i in itertools.permutations(parts, 8): 
  file = ''.join(i).decode('hex')
print(magic.from_buffer(file))

If we execute the Python program, we can confirm that the archive file is a bzip2 file (extension bz2) and we have 5040 candidates for decompression.

$ python identify.py | sort | uniq -c
5040 bzip2 compressed data, block size = 900k
35280 data

Now that we know we need to decompress a bzip2 archive, we will improve our Python program to call the bzip2 command on Linux. The main reason to use the Linux binary instead of native Python bzip2 decompression is that error handling is very well supported by the Linux binary and we can rely on the exit/return code.

import itertools, magic, os, sys
from subprocess import call<br>
parts = [
'''02b4520388c01f012c8a52bfa9f9140cfe4c6fdd1884ada7ef849a4e533b3451055325a4f279
e116d400a74b3afd7a6a3d5de9256e030d541557443eba4747708e95ff177245385090880751450''',
'''068d19000340000000006d434c868c9a321a34f534c9a6441a0d003200d1a0000064c8000000
3d4d000d0068c80343201e9a8d340f51a1a341a00064347a4680003405451b48d3d4681a0190340''',
'''0c80034d00680001ea000003434d1a1a00d00000000680000000007a4d00f95c1193209a7281
e471029b5b4a08000740646d6d12c40cea9b736fe9818b9824b377db3c41b1ad95cb98402483588''',
'''0e51541ce1c3d2aeaf5dea313f0c1d217c3d488a73ac4450a4b9406532e27f90b205c2382200
0264a995f461e0da824c79c80351dea802d056e5950510837f0aee2954b0e29f2c37d8f0e1660e0''',
'''425a6839314159265359880751450000917fffffffddff75fd3f3ff7e57fb7ef8fff7dfffffd
6bbf9fff73fdcdf66feff7ff54b0015962320d0d341a001a34d00d069a0068000068d069a341a00''',
'''7dbaa2229614a6192320713c5098d5c3fc590a70b546f281733c1e7577a7a070914168eb2351
ca19f6301742f3bb2501e9c545f84539e7408759035380c8876b7c8cbfcbc43eb56e79121c3fa2c''',
'''f2ea3bd4c344aafa3264860f358822dcd7459cc7573eb07c01fa58d5c2abe0d1785d54352014
0b19ea010c9ab84edd144097ca644221740d2e5fedf3fa5ebc94e92b2608cfa68f673bb6a0b11c4''',
'''f519e37ac851700ee8c4260ad95fb864742f42724d4f7c2309424a31ea48cae1070e1148bb0e
221c7b834eeb2080995fead718739dbe54213ebdfcc993037832de55264f0dd9c30781fecaa7c43'''
]
DEVNULL = open(os.devnull, 'wb')
for i in itertools.permutations(parts, 8):
file = ''.join(i).decode('hex')
if 'bzip2' in magic.from_buffer(file):
f = open('flag.bz2','w')
f.write(file)
f.close
return_code = call(["bzip2", "-d", "flag.bz2"], stderr=DEVNULL)
if return_code == 0:
print "[+] Successfully decompressed flag.bz2 into flag"
print "[+] flag filetype: %s" %(magic.from_file('flag'))
sys.exit(0)
else:
print "[!] bzip2 file could not decompress"
# python solve.py
[!] bzip2 could not decompress
[!] zip2 could not decompress
[!] bzip2 could not decompress
...
[!] bzip2 could not decompress
[!] bzip2 could not decompress
[*] Successfully decompressed flag.bz2 into flag
[*] flag filetype: PNG image data, 123 x 123, 1-bit colormap, non-interlaced

The result in a PNG image representing a QRcode. The QRcode contains the flag and a Google shorten URL to a Google Doc with the instructions for the challenge n°3. "CCC2{S0_L0ng,4nd_Th4nks_f0r_4ll_th3_F1sh} - - https://goo.gl/3UO7sa"

Challenge 3 - Encrypted Zip File (Crypto)

The instructions on the Google Doc gives a link to a zip file hosted on Google Drive.

The zip file contains two encrypted files (password protected):

$ zipinfo stage3.zip
Archive: stage3.zip
Zip file size: 39582 bytes, number of entries: 2
-rw-r--r-- 3.0 unx 42091 BX defN 15-Oct-07 19:23 34f0e06ec60bff06c501c0c358d4ef5f5df2f1d6.png
-rw-r--r-- 3.0 unx 91 TX stor 15-Oct-07 21:19 flag.txt
2 files, 42182 bytes uncompressed, 39144 bytes compressed: 7.2%

Using zipeinfo (https://github.com/hannob/zipeinfo), we can see that the encryption algorithm used is the insecure PKZIP. Known plaintext attacks exists for this algorithm.

./zipeinfo.py stage3.zip
* stage3.zip
-> 34f0e06ec60bff06c501c0c358d4ef5f5df2f1d6.png PKZIP/legacy [insecure]

The de-facto tool is pkcrack for known plaintext attack on PKZIP. However, we still need some known plaintext, which means we need data not encrypted/in cleartext inside the zip archive.

The PNG file in the zip has a very unique name. Googling this name leads to the following page http:// www.ultraimg.com/image/evOR. The image on this website is the same as the one in the ZIP file.

After fiddling with pkcrack and its parameters (which is not an easy task), a player would be able to recover the decryption key to decompress the zip.

In the flag.txt, he would find:

CCC3{M4g1c_1s_1mpr3ss1v3,but_n0w_M1nsc_l34ds!Sw0rds_f0r_3v3ry0n3!} - https://goo.gl/MO0QsD

Challenge 4 - SQL Injection (Web)

The source code of the main web page contains a commit ID, indicating that git has been used. "Unfortunately", the .git folder has been removed from the production environnement but we can find a hint about an "exclude file" still present:

<html> <body> Removed for security reason. Exception list merged from $GIT_DIR/info/exclude with gitignore </body> </html>

Therefore, the only file that could be recovered is "/info/exclude" which leads to the file /temp/886fc408bdb363e0f6bbaa42dc4fb34b/requests.tmp.

This file is basically a backup of the page requests.php. Careful reading of the source code will reveal that a blind SQL injection is possible using the X-Forwarded-For HTTP header.

The first tasks there is to enumerate all the tables in the database. Obviously, there is only one which will be useful for us: cyberEmployees.

We then extract all the data in the table using the following SQLI:

X-Forwarded-for: " OR 1=(SELECT IF(SUBSTRING((SELECT password FROM cyberEmployees LIMIT 4,1),22,1) = 'd',0,1))

For each row we get a username, a password (hashed) and a role. A quick analysis, reveals that passwords are hashed using SHA1.

We can crack most of the passwords in a short amount of time, but only the last one will be useful for us as the user "boris" is the only one with an admin role (password ="severnaya").

We can now connect to the admin part of the website, which reveals the flag and the link to the next Google Doc:

"CCC4{S3t_th3_c0ntr0ls_f0r_th3_h34rt_0f_th3_sun} - https://goo.gl/wvgJFl"

Challenge 5 - CheckPoint Firewall (Misc)

This challenge is quite unusual for a CTF but clearly reflects daily activities at Navixia. As a company selling security products, Navixia engineers will not only perform security assessments but will also need to be ready to integrate security solutions.

The player is provided with a hostname and credentials (trinity:Z1ON0101). On accessing f1r3w411.cybercheese.io, the player will get the Web UI of a CheckPoint firewall.

After authentication, a Message Of The Day (MOTD) will be displayed: 'One rule to rule them all!'

This hint invites the player to access the rules/policy of the firewall. To access to the CheckPoint Firewall policy, the player needs to download a desktop application (SmartConsole) and connect to the firewall through the SmartConsole.

The player, by reading the firewall policy, should understand that he needs to send an e-mail to the HR_Admin internal CheckPoint user. The HR_Admin user will have the last flag as one of its properties ("CCC5{Ch3ckP01nt_Ch4rl13}") and the player will be invited to send all the collected flag to a unique email address created for the CTF.