gocrygo is a reversing challenge that was part of saarCTF 2022. The challenge description is as follows:

There are three files in this challenge:

  1. gocrygo: A crypto-ransomware.
  2. gocrygo_victim_directory: The directory fucked up by this ransomware.
  3. core: The ransomware’s core dump when infecting gocrygo_victim_directory.

Your goal is to:

  1. Reverse gocrygo
  2. Figure out the encryption algorithm it uses
  3. Find the encryption key in the core dump
  4. Decrypt the entire infected directory.
  5. Find the flag in gocrygo_victim_directory

Note: The encryption algorithm is a common cryptographic algorithm (I’m too dumb to implement one myself).

Reversing gocrygo

As its name suggests, gocrygo is a linux go binary that implements a ransomware. Since the binary is stripped of its debug symbols and is statically linked, decompiling the binary won’t be much fun. We would need to sift through loads of compiled library code to find the actual logic of the program.

Before taking this route, we might want to gather some information using simpler means.

Figuring out the encryption algorithm

The first thing we can do is to look at the strings in the binary. If we’re lucky, we might be able to get some strong hints about the crypto library used by the program:

$ strings gocrygo | grep -i crypto
crypto/des: input not full blocksoftware caused connection abort
crypto/cipher: invalid buffer overlap
crypto/des: invalid key size 
crypto/des: invalid buffer overlap

In this case, we can see that the binary uses the crypto/des package. The package implements both DES and Triple DES encryption algorithms. Since this is a reversing challenge and not a crypto challenge, we might assume that the challenge author chose to use Triple DES.

Extracting the encryption key from the core dump

Triple DES expects a 24-byte key and an 8-byte initialization vector (IV). Since a core dump is provided, these values are likely to be generated randomly.

Let’s test this assumption by running the binary on some dummy data that we control. We first create a directory gocrygo_victim_directory and fill it with two text files flag.txt and flag2.txt containing the string flag{bogus}.

We then run the binary using strace to see what system calls it makes:

$ strace -i ./gocrygo
...
[0000000000215632] openat(AT_FDCWD, "/dev/urandom", O_RDONLY) = 4
[00000000002157c1] read(4, "#m36\313\200O\250\226n\7\327\204[\262\177\233\331\3670v\5\215\316", 24) = 24
[0000000000215632] openat(AT_FDCWD, "/home/user/hitcon/gocrygo/gocrygo_victim_directory/flag.txt", O_RDONLY) = 5
[000000000021ad91] fstat(5, {st_mode=S_IFREG|0644, st_size=12, ...}) = 0
[00000000002157c1] read(5, "flag{bogus}\n", 512) = 12
...
[00000000002157c1] read(4, "$\10\v+\2605\333\204", 8) = 8
[0000000000215632] openat(AT_FDCWD, "/home/user/hitcon/gocrygo/gocrygo_victim_directory/flag.txt.qq", O_WRONLY|O_CREAT|O_TRUNC, 0644) = 5
[000000000021a781] write(5, "$\10\v+\2605\333\204\273\21X]\231\36\362\306\276\306\301\17", 20) = 20
...

We immediately notice that the binary opens ‘/dev/urandom’ to generate 24 random bytes for the key and 8 random bytes for the IV. The generated IV is then prepended to the encrypted data and written to a file with the same name as the original file but with the .qq extension.

Let’s run the binary again and place a breakpoint on the sys_read system call that reads from ‘/dev/urandom’:

$ r2 -d -AA ./gocrygo
[0x002157c1]> db 0x2157c1
[0x002157c1]> dc
hit breakpoint at: 0x2157c1
[0x002157c1]> dr rdx
0x00000018
[0x002157c1]> px 64 @ rsi
- offset -      C0C1 C2C3 C4C5 C6C7 C8C9 CACB CCCD CECF  0123456789ABCDEF
0x7fb5e7f012c0  8635 9a17 77f2 a6dc 0ee1 763e 8158 4fb3  .5..w.....v>.XO.
0x7fb5e7f012d0  6c40 5ef1 ff38 2644 0000 0000 0000 0000  l@^..8&D........
0x7fb5e7f012e0  2f64 6576 2f75 7261 6e64 6f6d 0000 0000  /dev/urandom....
0x7fb5e7f012f0  0000 0000 0000 0000 0000 0000 0000 0000  ................

As expected, 0x18 bytes are read from /dev/urandom and stored in the buffer at rsi. While inspecting the buffer, we notice that it is followed by a null byte and then the string '/dev/urandom'. We might be able to use this information to extract the key from the core dump!

$ grep --byte-offset --only-matching --text "/dev/urandom" core
29944:/dev/urandom

We quickly pull up the core dump in Ghidra and search for the string /dev/urandom: Inspecting the core dump in Ghidra

It looks like we found ourselves a key! We can now decrypt the encrypted files in gocrygo_victim_directory.

Decrypting all the files

Decrypting the files is pretty straightforward. Recall that the IV is prepended to the encrypted data. Then, we can use the key and IV to decrypt the data using our old friend CyberChef:

Cyrillic letters are fun right?
First part: `HITCON{always_gonna_make_you_`
Hint: The second part is at `Pictures/rickroll.jpg`
 _    _.--.____.--._
( )=.-":;:;:;;':;:;:;"-._
 \\\:;:;:;:;:;;:;::;:;:;:\
  \\\:;:;:;:;:;;:;:;:;:;:;\
   \\\:;::;:;:;:;:;::;:;:;:\
    \\\:;:;:;:;:;;:;::;:;:;:\
     \\\:;::;:;:;:;:;::;:;:;:\
      \\\;;:;:_:--:_:_:--:_;:;\
       \\\_.-"             "-._\
        \\
         \\
          \\
           \\
            \\
             \\

Let’s hope this isn’t some kind of elaborate prank. Decrypting rickrolled.jpg is just as easy. Let’s take a look at the decrypted file: The second part of the flag

And there we have it. This was a neat challenge, especially because no ‘real’ go reverse engineering was involved.