Home | Articles

NotWannasigh WPICTF 2020 Writeup

I competed in WPICTF 2020 (Worcester Polytechnic Institute Capture The Flag) event this weekend. I played on a “pick-up” CTF team that found each other via the #find-a-team channel in the WPICTF Discord. Our team was called “notheretowin” and we came in 13th place, not bad for a ragtag squad of nerds!

This is a write-up of my favorite challenge from WPICTF 2020, NotWannasigh. It’s a ransomware reverse engineering puzzle. Thanks “Abjuri5t (John F.)” for making it. See here for any of the code, files, or artifacts used below.

This was the challenge text:

NotWannasigh - 100 points (RE)

Please help! An evil script-kiddie (seriously, this is some bad code) was able to get this ransomware "NotWannasigh" onto one of our computers. The program ran and encrypted our file "flag.gif".

These are the resources we were able to gather for you:

    * NotWannasigh.zip - the malicious ransomware executable
    * flag-gif.EnCiPhErEd - our poor encrypted file that we need you to recover
    * ransomNote.txt - the note left behind by the ransomware. I'm not sure you'll find anything usefull here
    * 192-168-1-11_potential-malware.pcap - a packet capture that our IDS isolated, it seems that the program has some weird form of data exfiltration

We need you to reverse the malware and recover our flag.gif file. Good luck!

A note from the creator: Shoutout to Demonslay335 for challenge inspiration - he's done some awesome work in fighting ransomware. Also, the ransomware in this challenge is programmed to only target files named "flag.gif" so you shouldn't need to worry about the accidental execution, I just zipped it out of habit/good practice. Have fun and happy hacking!

    Abjuri5t (John F.)

http://us-east-1.linodeobjects.com/wpictf-challenge-files/192-168-1-11_potential-malware.pcap

http://us-east-1.linodeobjects.com/wpictf-challenge-files/ransomNote.txt

http://us-east-1.linodeobjects.com/wpictf-challenge-files/flag-gif.EnCiPhErEd

http://us-east-1.linodeobjects.com/wpictf-challenge-files/NotWannasigh.zip

Let’s take a look at the ransom note first:

$ cat ransomNote.txt 
Haha! Your precious file flag.gif has been encrypted by my new and improved ransomware NotWannasigh! You must send bitcoin to "bitpay.com/83768" to get the decryption key. You should act fast because in 48 hours I will delete the key. Muahahahaha!
 - def-not-h4ckah

(Hi, CTF challenge creator here. You should _NEVER_ pay the ransom. If you send bitcoin to that BTC wallet then you will ONLY be donating to charity (and hey, that's really nice of you, Mental Health Hackers is a great organization). I will NOT send you the decryption key)

Not much here. Let’s take a look at the encrypted flag:

$ file flag-gif.EnCiPhErEd 
flag-gif.EnCiPhErEd: data
$ xxd flag-gif.EnCiPhErEd | head
00000000: 92eb 1dec 0cfb 664c 858b 4183 03ce 59cf  ......fL..A...Y.
00000010: e19a fea0 2ca1 5776 4f76 785d 1305 e2cf  ....,.WvOvx]....
00000020: ee1d f232 8ccd 14f6 4352 6877 8bcb 88dc  ...2....CRhw....
00000030: 49ac 1024 0bd6 c554 908c d9af c987 b9bc  I..$...T........
00000040: dd79 b762 c0dd 04cb de58 35e3 38b3 b96f  .y.b.....X5.8..o
00000050: 9aa6 350b da73 61cf 08ea dbfd f08d a785  ..5..sa.........
00000060: dc83 9d62 31eb ecbe fa51 5373 e0aa 3b67  ...b1....QSs..;g
00000070: 0b28 5436 e2e4 54c9 2d63 e318 d760 8221  .(T6..T.-c...`.!
00000080: 8f39 baa8 1c92 fc17 5753 e04a 182d c56e  .9......WS.J.-.n
00000090: 7319 5c0f 0282 247b 1139 4bc3 7617 cdca  s.\...${.9K.v...

Not encrypted in any discernible way. Let’s take a look at the PCAP in Wireshark.

scrot1

There’s one HTTP request here we can take a look at:

scrot2

The “ransomware victim” sent out a number (1585599106) to the malicious server which is interesting. This looks like an epoch time, and sure enough it is:

$ date -d @1585599106
Mon 30 Mar 2020 03:11:46 PM CDT

Alright, let’s take a look at the ransomware now:

$ unzip NotWannasigh.zip
Archive:  NotWannasigh.zip
  inflating: NotWannasigh
$ file NotWannasigh
NotWannasigh: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=ca17985d5f493aded88f81b8bfa47206118c6c9f, for GNU/Linux 3.2.0, not stripped

It’s a single Linux ELF 64-bit executable. I decided to use Ghidra to inspect it.

scrot3

I decompiled the main function and right away I can see it got the epoch time it later sends stored in the variable local_50. This epoch time is then used to seed the random using srand, this will be very important later on.

scrot4

We can follow the decompilation and see a socket is created and stored in variable local_54, the same IP address we saw in the PCAP is being connected to here, the epoch time is stored as a string using sprintf into variable local_c2. Then local_c2 is sent to the malicious server. This is exactly what we saw in the PCAP!

So now the question is: why is this epoch time important? Let’s keep reading the decompiled C code…

scrot5

A few things interesting things happen:

This still hasn’t done any encryption, but it has created a random character array (local_78) of equal size to flag.gif. Let’s see what happens with this array…

scrot6

Reading decompilation can be messy & tricky, but let’s break it down:

Phew! Ok, we now understand the important parts of what this ransomware is doing. So how do we get our flag back? Since we know the random seed, we can create the same random data this ransomware is creating and then reverse the XOR operation. Let’s write some C to generate the same random characters:

/*
    gen_array.c
*/

#include <stdio.h>
#include <stdlib.h>

int main()
{
    /* seed random from the epoch time extracted from PCAP */
    srand(1585599106);

    /*
        An unsigned character array, the same length as the
        number of bytes in the encrypted file.

        $ du -b flag-gif.EnCiPhErEd
        374109	flag-gif.EnCiPhErEd
    */
    unsigned char *stream = malloc(374109);

    for (int i = 0; i < 374109; i++) {
        /* create a random character */
        stream[i] = rand();
        printf("%d ", stream[i]);
    }

    return 0;
}

Let’s compile this, run it, and drop the random characters to a file:

$ gcc gen_array.c -o gen_array
$ ./gen_array > srand_bytes.txt
$ cat srand_bytes.txt
213 162 91 212 53 154 186 76 249 139 182 131 3 206 89 207 ...

Now, I’m going to write some python to take these random characters and XOR them against the bytes of the encrypted file, which should decrypt the flag.gif!

#!/usr/bin/env python

#
# fix_gif.py
#

# read in the encrypted gif
with open('flag-gif.EnCiPhErEd', 'rb') as f:
    encrypted_bytes = [i for i in f.read()]

# read in the srand numbers we generated in C
with open('srand_bytes.txt', 'r') as f:
    srand_bytes = list(map(int, f.read().split()))

# XOR the encrypted bytes and the srand_bytes
gif = []
for a, b in zip(encrypted_bytes, srand_bytes):
    gif.append(a ^ b)

# write the XOR'd data
with open('flag.gif', 'wb') as f:
    f.write(bytearray(gif))

Let’s cross our fingers and run our python script:

$ python fix_gif.py
$ file flag.gif
flag.gif: GIF image data, version 89a, 220 x 124

Hell, yes!

flag

It’s a bit hard to see, but the flag is: WPI{It_always_feels_a_little_weird_writing_malware}

This was a great RE challenge, I really enjoyed it. Until next time, ~vesche