Defeating a Ransomware with Cutter
2019 Sep 11Introduction
Last week I attended the r2con conference for the first time (for those who haven't heard about it before, it is a reverse engineering conference focused on radare2) and apart from listening to great talks, I signed up for the basic trainings since I had not used r2 in the past and my RE experience is quite basic. One of the trainings was "Dissecting binaries with Cutter" given by Antide Petit, Itay cohen, and Florian Märkl. It was an introduction to the official GUI application for r2, Cutter.
During the training, there were 3 different exercises and one of them was about reversing a binary called "M1ghty Ransomware.exe" which by the name of it, you can guess what it is. Along with the binary, there was also a PNG file called "flag.png" that was encrypted by the malware. We had to use Cutter to analyse the binary and figure out the encryption algorithms, the keys used for the encryption, and finally write a script to decrypt the png file. Sounds easy, right? Well, it took me a while to figure it out. Let's dissect it.
Malware Dissection
When the binary is open with Cutter, the first thing that it shows is an overview of the binary that helps to have a general idea about it.
It shows useful information like the architecture, the format, the language it was written in, the entropy, etc.
As a first step for the analysis, I identified the main function. For that I just sorted the list of functions by size and assumed that the largest one was main. This assumption turned out to be right.
After this, I had to identify the encryption algorithm which I couldn't do and had to wait for the hints given during the training:
I was curious to know how I could have known that RC4 was the algorithm and the answer was that I should have reversed it in the past so I would have never figured that out at that time or it would have taken me a long time.
I googled a bit and found this post from 2014: An Introduction to Recognizing and Decoding RC4 Encryption in Malware. Hopefully, I'll be able to recognise it in the future.
After the hints, I renamed the functions and searched for the references of the rc4
function:
As you can see, I saw that there are two calls to this function:
- From the
fcn.1400012c0
function - From the
main
function
The code of the call from the main function looks like this:
Note that the call to the fcn.1400012c0
function happens before the call to rc4
and as shown in the previous image, that function also calls rc4
. You'll understand this in a bit. Since there are no syscalls between offsets 0x14000168b
and 0x1400016bc
, this chunk can be emulated with ESIL. I found this to be one of the most powerful features of r2 and the integration in Cutter is sleek.
Here's a gif showing the emulation:
After the emulation, I learnt that:
- The function
fcn.140001340
returns the string"EMUL4TION+RuLEZ!_R2C0N2o19"
which I'll call key1
- The function
fcn.1400012c0
receiveskey1
as parameter, calls the rc4 function, and it returns the string"DEFEATING_A_R4NSoMWAR3_W1TH_CUTT3R"
which I'll call key2
This means that key1
is used to decrypt key2
using RC4. With this knowledge, I renamed both functions and this is how the code looked like:
As I have seen before, after the call to decrypt_key2
there's a call to rc4
and now I knew that it receives key2
as parameter. After the call to rc4
, there are a couple of calls to the rand
function which I'm going to ignore for the moment and I'll come back to them later. Then, I see that there's a string with the value "RANS0MW4RED_"
which I'll call ransom_header and finally, there's a call to the WriteFile
function.
Up until this point, I knew 2 things that happen to the file in this block:
- It is encrypted using RC4
- A header,
ransom_header
, is written at the beginning
I confirmed this by reviewing a hexdump of the file:
The reason I didn't explain the rand
calls shown in the previous block of code is because I ignored them the first time I looked into this and I spent quite a lot of time creating a script to decrypt the file with the information I had from what I explained above.
After many failed attempts, frustrations, and discussions with different people (thanks all!), I came back to Cutter to continue analysing the binary because I was clearly missing something. Breathe.
I started analysing again by reviewing the references to the WriteFile
function:
That was interesting, there are 3 calls which means that the file is written 3 times and I have analysed only the first one so I decided to analyse the other two.
This is the relevant code for the second write:
Here's where the rand
calls are important. First, I saw that a random number is generated and stored in a variable I called random_number
. Then, a little bit after the first call to WriteFile
, there's the second call to it and what is it writing? the random_number
! The offset 0x14000174a
is specifying that only 4 bytes will be written.
Up until here, I knew that the structure of the file was something like [ransom_header|random_number|rc4_encrypted_data]
, but there was one more call to WriteFile
left.
Here's the relevant code for the third write:
Before the call to WriteFile
, there's a loop between offsets 0x140001786
and 0x1400017ef
. That loop is xoring the file's content at offset 0x1400017df
and before the xor instruction, there's a reference to random_number
at offset 0x1400017c0
. That means that the random_number
is the key used for the xor.
And that's all there was to analyse, after this the file is closed and the execution ends. This is the summary of the analysis on what the binary does:
- Gets
key1
- Gets
key2
by decrypting it usingkey1
- Use
key2
to encrypt the content of the file using rc4 - Writes
ransom_header
in the file - Writes
random_number
in the file - Use
random_number
to encrypt the content of the file again using XOR
So, the structure of the file is: [ransom_header|random_number|xor_and_rc4_encrypted_data]
The bytes in red are the ransom_header
, the bytes in green are the random_number
, and the rest is the encrypted data with xor and rc4.
Decrypting the file
To decrypt the file, I needed to write a script to:
- Read the file content ignoring
ransom_header
- Read the first 4 bytes of the file content which represent
random_number
- Use
random_number
to decrypt the file content using XOR - Use
key2
to decrypt the file content using RC4 - Write a new file with the decrypted content
To do this, I wrote the following script in Go.
package main
import (
"bufio"
"crypto/rc4"
"io/ioutil"
"os"
)
func main() {
path := "./flag.png"
ransomHeader := "RANS0MW4RED_"
f, err := os.Open(path)
defer f.Close()
check(err)
reader := bufio.NewReader(f)
_, err = reader.Discard(len(ransomHeader))
check(err)
randomNumber := make([]byte, 4)
_, err = reader.Read(randomNumber)
check(err)
data, err := ioutil.ReadAll(reader)
check(err)
xoredData := xorEncryptDecrypt(randomNumber, data)
key2 := []byte("DEFEATING_A_R4NSoMWAR3_W1TH_CUTT3R")
rc4edData := rc4EncryptDecrypt(key2, xoredData)
ioutil.WriteFile("./dflag.png", rc4edData, 0644)
}
func check(e error) {
if e != nil {
panic(e)
}
}
func rc4EncryptDecrypt(key, data []byte) []byte {
cipher, _ := rc4.NewCipher(key)
dst := make([]byte, len(data))
cipher.XORKeyStream(dst, data)
return dst
}
func xorEncryptDecrypt(key, data []byte) (output []byte) {
for i := 0; i < len(data); i++ {
output = append(output, data[i]^key[i%len(key)])
}
return output
}
After executing this with go run main.go
I got the following file:
Final Thoughts
That was it! I had a lot of fun with this challenge and I got to experience how powerful r2 and Cutter are so if you have any interest in RE, make sure to check them out! I can also say that the r2 community is super friendly and they are more than happy to answer any question.
If you'd like to play with this exercise, you can find find it at https://github.com/radareorg/r2con2019/tree/master/trainings/cutter along with the rest of the material from the conference.
I hope you found this useful and if you have any comment, suggestion, or question you can reach me on Twitter (my DMs are open) @_camaya or you can send me an email to <cam at camaya.co>
.