HITCON CTF 2022 – gocrygo
gocrygo is a reversing challenge that was part of saarCTF 2022. The challenge description is as follows:
There are three files in this challenge:
gocrygo
: A crypto-ransomware.gocrygo_victim_directory
: The directory fucked up by this ransomware.core
: The ransomware’s core dump when infectinggocrygo_victim_directory
.Your goal is to:
- Reverse gocrygo
- Figure out the encryption algorithm it uses
- Find the encryption key in the core dump
- Decrypt the entire infected directory.
- 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
:
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:
And there we have it. This was a neat challenge, especially because no ‘real’ go reverse engineering was involved.