Hello,
my Qnap Nas has been recently infected by a ransomware that is written in go and I had no choice than to pay … and I was “lucky” because hackers sent me a binary to decrypt my files.
I didn’t want to use their binary because who knows what it will happen again on my Nas or computer.
So I reversed engineered it and I wrote my own simple code to decrypt and when I tested it on jpeg image I tought it was working fine:
package main
import (
"crypto/aes"
"crypto/cipher"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
)
func decryptFile(key string, srcpath string) {
fmt.Printf("UnLock: %s\n", srcpath)
ext := filepath.Ext(srcpath)
if ext != ".encrypt" {
log.Fatal("not a ech0raix encrypted file - must end with .encrypt")
}
dstpath := strings.TrimSuffix(srcpath, ext)
content, err := ioutil.ReadFile(srcpath)
if err != nil {
panic(err.Error())
}
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
iv := content[:aes.BlockSize]
cyphertext := content[aes.BlockSize:]
plaintext := make([]byte, len(content)-aes.BlockSize)
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(plaintext, cyphertext)
f, err := os.Create(dstpath)
if err != nil {
panic(err.Error())
}
_, err = io.Copy(f, bytes.NewReader(plaintext))
if err != nil {
panic(err.Error())
}
}
Of course I have some very large files (like VM files) that have a size 40 Gb minimum and even on my computer the decryptor fails because of the huge memory allocation.
So I wrote the following code to decrypt by chunk:
package main
import (
"crypto/aes"
"crypto/cipher"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
)
//const key = "4STDs9cmUlkiujXuLkdTouoqOIfER4TE"
const default_buffer_size = 65536
func UNUSED(x ...interface{}) {}
func check(e error) {
if e != nil {
panic(e)
}
}
func decryptFile(key string, srcpath string) {
fmt.Printf("UnLock: %s\n", srcpath)
ext := filepath.Ext(srcpath)
if ext != ".encrypt" {
log.Fatal("not a ech0raix encrypted file - must end with .encrypt")
}
dstpath := strings.TrimSuffix(srcpath, ext)
// Create the cipher object and decrypt the data
block, err := aes.NewCipher([]byte(key))
check(err)
// Open the input and output files
input_file, err := os.Open(srcpath)
check(err)
output_file, err := os.Create(dstpath)
check(err)
// get the size of the ciphered data
//fi, _ := input_file.Stat()
//check(err)
//data_len := int(fi.Size()) - aes.BlockSize
// read the iv from input file
iv := make([]byte, aes.BlockSize)
n1, err := input_file.Read(iv)
UNUSED(n1)
check(err)
// Set buffer size
buffer_size := default_buffer_size
// read b
stream := cipher.NewCFBDecrypter(block, iv)
input_buffer := make([]byte, buffer_size)
decrypted_bytes := make([]byte, buffer_size)
read_len, total_len := 0, 0
for ok := true; ok; ok = (read_len > 0) {
read_len, _ = input_file.Read(input_buffer)
total_len += read_len
if read_len == buffer_size {
stream.XORKeyStream(decrypted_bytes, input_buffer)
_, _ = output_file.Write(decrypted_bytes)
} else if read_len > 0 {
stream.XORKeyStream(decrypted_bytes, input_buffer)
tmp_buffer := decrypted_bytes[:read_len]
_, _ = output_file.Write(tmp_buffer)
//fmt.Println("What should I do ?????")
}
}
input_file.Close()
output_file.Close()
}
But first when I have tested on a json file I could see that the decrypted file was ok at the beginning but with some garbage at the end.
Here is the results of the 2 methods:
https://github.com/vricosti/ech0raix_decryptor/tree/dev/decrypt_by_chunk/tests
So how do you think I could modify let’ say the second algorithm to get the right decrypted data.
The code for the chunk method is also available on the git repository here:
Thanks