Cyborg Security 2020 CTF Solutions

Last weekend, Cyborg Security hosted our first Capture the Flag (CTF) event. The CTF was oriented for people interested in threat hunting, cyber defense, blue team, network traffic analysis, malware analysis, and forensics. There were challenges for beginners and more experienced players alike. Congratulations to the teams copo.banget and deadPix3l for a 1st place tie with a high score of 560 points! The solutions for all of the CTF challenges are below. We really enjoyed putting on this CTF event and we look forward to hosting more blue team oriented CTF events in the future.

New call-to-action

Forensics (25 points) – Our logo is feelin’ spidery

Challenge text: I think a spider may have crept into the Cyborg Security logo, could you help me get him out?

The Cyborg Security logo in PNG format was given:

By using binwalk an additional PNG is found to be hiding in the logo file:

$ binwalk --dd='.*' Cyborg_Logo.png

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 500 x 424, 8-bit/color RGBA, non-interlaced
41            0x29            Zlib compressed data, best compression
145201        0x23731         PNG image, 620 x 442, 8-bit/color RGB, non-interlaced
145329        0x237B1         Zlib compressed data, compressed

$ cd _Cyborg_Logo.png.extracted/ && file *
0:       PNG image data, 500 x 424, 8-bit/color RGBA, non-interlaced
29:      empty
29-0:    zlib compressed data
237B1:   empty
237B1-0: zlib compressed data
23731:   PNG image data, 620 x 442, 8-bit/color RGB, non-interlaced

The hidden PNG is Lucas the Spider, not so intimidating after all…

By checking the Exif data of the picture, an interesting comment is found which can then be base64 decoded to discover the flag.

$ exiftool 23731 | grep -i comment
Comment                         : Q3lib3JnQ1RGe2x1YzRzXzFzXzRfbjFjM19zcDFkM3JfdGgwfQo=
$ echo "Q3lib3JnQ1RGe2x1YzRzXzFzXzRfbjFjM19zcDFkM3JfdGgwfQo=" | base64 -d
CyborgCTF{luc4s_1s_4_n1c3_sp1d3r_th0}

The flag is: CyborgCTF{luc4s1s4n1c3sp1d3r_th0}

Forensics 50 – I don’t /run fast…

Challenge text: This partial Linux disk image is hiding a flag, can you find it?

A partial Linux ext4 file system image is given. It can be mounted and found to only contain the /run directory.

$ file i_dont_run_fast.img 
i_dont_run_fast.img: Linux rev 1.0 ext4 filesystem data, UUID=8c732098-890a-4939-868d-85f380b893c4 (extents) (64bit) (large files) (huge files)
$ sudo mount i_dont_run_fast.img /mnt/
$ ls /mnt/
run/

Searching recursively for the flag format discovers a file containing the flag within a systemd path.

sudo grep -r "CyborgCTF" /mnt/run/
/mnt/run/systemd/journal/streams/9:16969:FLAG=CyborgCTF{n3w_f1l3_syst3m_wh0_d1s}

The flag is: CyborgCTF{n3wf1l3syst3mwh0d1s}

Malware/RE 25 – corrupttt

Challenge text: I think my EXE is corrupted, maybe there’s still something useful inside?

You can use a variety of tools to find interesting static strings in this corrupted binary such as strings, FLOSS, or a hex editor. By inspecting the strings it can be determined that this binary is a Rust compiled executable for Windows that is at least missing a file header.

There are two interesting stings that appear to be base64 encoded:

$ strings corrupttt.exe | grep -E '[A-Za-z0-9+/]{4}*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)'
...
UWVSH
UATWVSH
H9Chv`H
H9Chw
X215X2IxbnpfZzN0X2MwcnJ1cHQzZH0=
Q3lib3JnQ1RGezFfaDR0M18xdF93aDNu
uespemosarenegylmodnarodsetybdetuespemosarenegylmodnarodsetybdet\
/rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\src\libcore\slice\mod.rs
/rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9\src\liballoc\collections\btree\map.rs
...

When appended and base64 decoded, the flag is discovered:

$ echo "Q3lib3JnQ1RGezFfaDR0M18xdF93aDNuX215X2IxbnpfZzN0X2MwcnJ1cHQzZH0=" | base64 -d
CyborgCTF{1_h4t3_1t_wh3n_my_b1nz_g3t_c0rrupt3d}

The flag is: CyborgCTF{1h4t31twh3nmyb1nzg3t_c0rrupt3d}

Malware/RE 50 – Call me on my Neuralink

Challenge text: My Neuralink went down, now all I got is this crummy old Nextel. What was the password to unlock it again?

The given file call_me_on_my_neuralink is a Linux ELF:

$ file call_me_on_my_neuralink
call_me_on_my_neuralink: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, 
BuildID[sha1]=633e63ae6e6a7021d175a461e867954c450d9f25, for GNU/Linux 3.2.0, not strippe

When executed, a password is wanted:

By opening the binary in a disassembler such as IDA we can see that the program control flow to “ACCESS GRANTED” versus “ACCESS DENIED” is dependent on the output of the ring_ring function:

Here’s a decompilation of the ring_ring function:

An array of data is pushed onto the stack and then that data is compared to the user input. The comparison happens in reverse order and the stored data is XOR’d by 99 (0x63 in hex) before compared. By taking the stored data and reversing these operations the flag is discovered.

>>> data = [30, 15, 80, 23, 27, 80, 13, 60, 26, 14, 60, 13, 83, 60, 80, 14, 60, 15, 15, 87, 0, 60, 83, 23, 60, 7, 80, 16, 22, 60, 80, 11, 16, 24, 37, 55, 32, 4, 17, 12, 1, 26, 32]
>>> ''.join([chr(i ^ 0x63) for i in data[::-1]])
'CyborgCTF{sh3_us3d_t0_c4ll_m3_0n_my_n3xt3l}'

The flag is: CyborgCTF{sh3us3dt0c4llm30nmy_n3xt3l}

Malware/RE 75 – Schwansomware

Challenge text: Some evil Schwansomware encrypted my favorite picture! Help me get it back? One of our forensic analysts looked at the malware and told us this number might important: 1585613911

A Linux ELF and an encrypted file are given:

$ file Schwansomware*
Schwansomware:      ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, 
BuildID[sha1]=c1a01dc366cc2c0060524c7a948c8571ee1fc4ca, for GNU/Linux 3.2.0, not stripped
Schwansomware.enc:  data

By decompiling the main function using IDA the logic of the program can be understood:

The encrypted file was originally Schwansomware.jpg and was encrypted using an array of random data that was seeded using a known epoch time (1585613911). This array of random data is the same length as the original file and was byte by byte XOR’d with the bytes of the original file.

In order to recover this encrypted file we can write some C code to re-create the “random” stream of data with the same seed.

/*
    gen_srand_array.c
*/
#include <stdio.h>
#include <stdlib.h>

int main()
{
    srand(1585613911);

    /*
        $ du -b Schwansomware.enc 
        657099  Schwansomware.enc
    */
    unsigned char *stream = malloc(657099);

    for (int i = 0; i < 657099; i++) {
        stream[i] = rand();
        printf("%d ", stream[i]);
    }

    return 0;
}
$ gcc gen_srand_array.c
$ ./a.out > Schwansomware_array.txt

Now we can reverse the XOR operation on our encrypted file to recover the JPG. This can be done in a variety of ways, in Python for example:

with open('Schwansomware.enc', 'rb') as f:
    encrypted_bytes = [i for i in f.read()]

with open('Schwansomware_array.txt', 'r') as f:
    srand_bytes = list(map(int, f.read().split()))

outf = []
for a, b in zip(encrypted_bytes, srand_bytes):
    outf.append(a ^ b)

with open('Schwansomware.jpg', 'wb') as f:
    f.write(bytearray(outf))

With the picture now recovered, the flag can be seen:

The flag is: CyborgCTF{n0pr0bl3m0b4by}

PCAP 25 – I don’t GET it?

Challenge text: N/A

A PCAP file is given which contains many HTTP requests. Almost all of the HTTP requests are POST requests.

Using the hint from the challenge title we can search specifically for HTTP GET requests by doing http.request.method=="GET".

Only one HTTP GET request is present, by following the TCP stream we discover a base64 encoded string. By decoding the string a flag is discovered.

echo "Q3lib3JnQ1RGe2QwX3kwdV9nM3RfMXRfbjB3fQo=" | base64 -d
CyborgCTF{d0_y0u_g3t_1t_n0w}

The flag is: CyborgCTF{d0y0ug3t1tn0w}

PCAP 50 – DNS on a roll

Challenge text: N/A

A PCAP file is given which contains many DNS requests.

Using a tshark and some bashfu we can combine all of these domains together, strip of the TLD, and then base64 decode to discover the flag.

$ tshark -r dns_on_a_roll.pcapng -T fields -e dns.qry.name -2R "dns.flags.response eq 0" | cut -d'.' -f1 | tr -d '\n' | base64 -d
We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
I just wanna tell you how I'm feeling
Gotta make you understand
Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say CyborgCTF{th3_0l3_dns_r1ckr0ll}
Never gonna tell a lie and hurt you

The flag is: CyborgCTF{th30l3dns_r1ckr0ll}

PCAP 75 – We4thertenko?

Challenge text: We’ve intercepted a communication stream between a rogue cyborg and an unknown accomplice. Can you figure out what they are talking about?

A PCAP file is given which contains one plain-jane TCP stream and two Websocket TCP streams. The Websocket data is encrypted.

By following the first TCP stream a password is discovered super-secret-passcode.

By looking at the Websocket communication the encrypted payloads are seen within the “Line-based text data” field of the packets:

All of these encrypted fields can be extracted using tshark:

$ tshark -r we4thertenko.pcapng -Y websocket -T fields -e text > websocket_dump.txt

The 4 in We4thertenko was a hint to use RC4 decryption. Using a RC4 Python implementation from GitHub, the websocket dump created above, and some Pythonfu we can decrypt the encrypted conversation to discover the flag.

$ python -i rc4-3.py
>>> rc4_decrypt = decrypt
>>> websocket_dump = open('websocket_dump.txt').read().splitlines()
>>> websocket_data = [i.split(',')[1].strip(' [truncated]') for i in websocket_dump if ',' in i]
>>> websocket_data
['F37DEC4FA21F4CAE254D6A6DF23C53DF15553D6020B3C7C1728E757EB7A07AF3899C7B1E0414DDDE473A07F863B2FD9E4C1FA547255988E498AAD2941DC84DDDA0F408D82E09E3E271739CBC', 
'F37DF253A20549EB7A4F7202943D7EC73172481510B6E6D26DAB5E45AACB1E81C49046201014E2F602547F9345BCED861304F31611', ... ]
>>> websocket_data_decrypt = [rc4_decrypt('super-secret-passcode', i) for i in websocket_data]
>>> websocket_data_decrypt
['{"sysid": "ODRjNWE2MWQyZjlm\\n", "message": "SGVsbG8sIHRoaXMgaXMgTXIuIFg=\\n"}', '{"message": "SGVsbG8gTXIuIFgsIHRoaXMgaXMgTXIuIFk=\\n"}', ... ]
>>> import base64, json
>>> for i in websocket_data_decrypt:
...     try:
...             base64.b64decode(json.loads(i)['message'])
...     except: pass
... 
b'Hello, this is Mr. X'
b'Hello Mr. X, this is Mr. Y'
b'Hello Mr. X, this is Mr. Y'
b'Please Code in'
b'Please Code in'
b'Code Weathertenko'
b'Thank you, code confirmed'
b'Thank you, code confirmed'
b'Please proceed with your message'
b'Please proceed with your message'
b'Stand by for passcode'
b'Stand by for passcode'
b'CyborgCTF{w3bs0ck3t_m1ss1l3s_4r3_4_g0}'
b'CyborgCTF{w3bs0ck3t_m1ss1l3s_4r3_4_g0}'
b'Thank you commander'
b'HAIL HYDRA'
b'Hail hydra, this chatroom will no self-destruct'
b'Hail hydra, this chatroom will no self-destruct'

The flag is: CyborgCTF{w3bs0ck3tm1ss1l3s4r34g0}

Threat Research 25 – Don’t wipe me bro!

Challenge text: The malware used in a cyberattack against the Ukrainian power grid in 2016 has a wiper component that is always named what?

The malware used in the cyberattack against the Ukrainian power grid in 2016 is called Industroyer (also known as CRASHOVERRIDE). A fantastic report on Industroyer was done by Anton Cherepanov of ESET. In the report on page 5 and 13 you can find information about the data wiper component, “The Data wiper component is always named haslo.dat.”

The flag is: CyborgCTF{haslo.dat}

Threat Research 50 – I hate hornets…

Challenge text: What is the last name of the person who created a credential harvesting tool used last year to kick a few hornets nests?

The hint here is to search for hornets nest and credential harvesting using a search engine. If you search “hornets nest credential harvesting” on Google the first link is “Untangling Legion Loader’s Hornet Nest of Malware” by Shaul Vilkomir-Preisman from Deep Instinct. In the report one piece of the malware downloads and reflectively loads an credential harvesting tool called SharpWeb which is open-source on GitHub. The authors name can easily be found via the GitHub repository, Dwight Hohnstein.

The flag is: CyborgCTF{Hohnstein}

Threat Research 75 – Worst Superhero

Challenge text: If the Snatch Ransomware was a Marvel character what would their name be?

Many reports can be found on the Snatch Ransomware, such as the report from the Carbon Black Threat Analysis Unit (TAU). In the report you’ll find that the ransomware names its windows service and registry key that it uses for persistence as SuperBackupMan.

The flag is: CyborgCTF{SuperBackupMan}

Threat Research 100 – My EXEs are always cozy

Challenge text: An APT group implicated in the hacking of the Democratic National Committee (DNC) re-emerged in late 2019, their new third-stage backdoor executable is named what?

One of the APT groups implicated in the hacking of the DNC was Cozy Bear (also known as APT29). In late 2019 ESET released a great report on the re-emergence of APT29, OPERATION GHOST The Dukes aren’t back — they never left. In the report on page 24 there are details about FatDuke the third stage backdoor executable which is named Canocpc.exe.

The flag is: CyborgCTF{Canocpc.exe}

Welcome 10 – Welcome, Humans!

Challenge text: Who’s ya sauce? https://ctf.cyborgsecurity.com/

By viewing the HTML source code of the CTF main page there’s a comment with the flag…

$ curl -s https://ctf.cyborgsecurity.com/ | grep CyborgCTF
        <!-- CyborgCTF{w3lc0m3_t0_th3_b33p_b00p} -->

The flag is: CyborgCTF{w3lc0m3t0th3b33pb00p}

New call-to-action

Join our newsletter

Follow Us

Discover More!