Recover Alabaster's Password WannaCookie ransomware

Objective #9.4
Santa's Secret Room

Recover Alabaster's password as found in the the encrypted password vault.

Now that we don't have to worry about new infections, I could sure use your L337 security skills for one last thing. As I mentioned, I made the mistake of analyzing the malware on my host computer and the ransomware encrypted my password database. Take this zip with a memory dump and my encrypted password database, and see if you can recover my passwords. One of the passwords will unlock our access to the vault so we can get in before the hackers.

For hints on achieving this objective, please visit Shinny Upatree and help him with the Sleigh Bell Lottery Cranberry Pi terminal challenge.

Shinny Upatree

Have you heard that Kringle Castle was hit by a new ransomware called Wannacookie?
Several elves reported receiving a cookie recipe Word doc. When opened, a PowerShell screen flashed by and their files were encrypted.
Many elves were affected, so Alabaster went to go see if he could help out.
I hope Alabaster watched the PowerShell Malware talk at KringleCon before he tried analyzing Wannacookie on his computer.
An elf I follow online said he analyzed Wannacookie and that it communicates over DNS.
He also said that Wannacookie transfers files over DNS and that it looks like it grabs a public key this way.
Another recent ransomware made it possible to retrieve crypto keys from memory. Hopefully the same is true for Wannacookie!
Of course, this all depends how the key was encrypted and managed in memory. Proper public key encryption requires a private key to decrypt.
Perhaps there is a flaw in the wannacookie author's DNS server that we can manipulate to retrieve what we need.
If so, we can retrieve our keys from memory, decrypt the key, and then decrypt our ransomed files.

Public / Private Key Encryption hint from Alabaster Snowball
wannacookie.min.ps1? I wonder if there is a non-minified version? If so, it may be easier to read and give us more information and maybe source comments?

Memory Strings hint from Alabaster Snowball
Pulling strings from a memory dump using the linux strings command requires you specify the -e option with the specific format required by the OS and processor. Of course, you could also use powerdump.

Using small helper utility, which was written for Stop the Malware challenge, abuse the functionality of wannacookie author's DNS and download the original source code by specifying to retrieve wannacookie.ps1 instead of wannacookie.min.ps1.

#!/usr/bin/env python3
import sys, dns.resolver
r = dns.resolver.Resolver()
r.nameservers = [r.query('', 'a')[0].address]
f = sys.argv[1].encode().hex()
q = lambda v: r.query(v + '', 'txt')[0].to_text().strip('"')
z = u''.join([q(u'{0}.{1}'.format(i, f)) for i in range(int(q(f)))])

$ ./ wannacookie.ps1 > wannacookie.ps1
$ sha256 wannacookie.ps1
SHA256 (wannacookie.ps1) = 81bff2602511f6ae29ea89202e4ec4d832a1ce3361caf4209b9f36c0ecd9f842
wannacookie.ps1 (, password protected: KringleCon2018)

Extract the Artifacts contain a PowerShell memory dump (in Mini DuMP crash report format) and ransomware's encrypted file.

$ unzip
 extracting: alabaster_passwords.elfdb.wannacookie
 extracting: powershell.exe_181109_104716.dmp

$ sha256 alabaster_passwords.elfdb.wannacookie powershell.exe_181109_104716.dmp
SHA256 (alabaster_passwords.elfdb.wannacookie) = 3604397d686956d439ca6814fed896c9c05ad8957ca4f5a1554e9c3b6695c4f2
SHA256 (powershell.exe_181109_104716.dmp) = f0b52b3f3832f9ea072c875a645da54be8ebd432784ab61df69f3e3de1bc73ac

To understand how ransomware encrypts files and what can be extracted from memory dump, source code must be analyzed.

Initially, ransomware downloads public key server.crt (7365727665722E637274) via DNS (in line 193). Then, a random private key (128-bit) is generated, converted to hex-string and hashed with sha1 (lines 194-196).

    $pub_key = [System.Convert]::FromBase64String($(get_over_dns("7365727665722E637274") ) )
    $Byte_key = ([System.Text.Encoding]::Unicode.GetBytes($(([char[]]([char]01..[char]255) + ([char[]]([char]01..[char]255)) + 0..9 | sort {Get-Random})[0..15] -join ''))  | ? {$_ -ne 0x00})
    $Hex_key = $(B2H $Byte_key)
    $Key_Hash = $(Sha1 $Hex_key)
It would be great if private key could be scraped from memory dump, but as it turns out, those variables are cleared from memory (lines 202-203).
    Clear-variable -Name "Hex_key"
    Clear-variable -Name "Byte_key"
Though, before clearing private key from memory, it is encrypted with previously downloaded public key server.crt.
function Pub_Key_Enc($key_bytes, [byte[]]$pub_bytes){
     $cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2
     $encKey = $cert.PublicKey.Key.Encrypt($key_bytes, $true)
     return $(B2H $encKey)
    $Pub_key_encrypted_Key = (Pub_Key_Enc $Byte_key $pub_key).ToString()
This value is not cleared from memory and should be available in memory dump. It will be there in hex-string format as $Pub_key_encrypted_Key variable, but to know the length, public key has to be examined. Download the server.crt, fix it with correct header and footer, and examine the public key size.
$ ./ server.crt > server.crt
$ sha256 server.crt
SHA256 (server.crt) = 97feeb01807f03c546127307942d3a7c2f747089a498f894cc8b3bfa4dad9bbf
printf -- "-----BEGIN CERTIFICATE-----\n" > server.fix.crt
cat server.crt >> server.fix.crt
printf -- "\n-----END CERTIFICATE----- " >> server.fix.crt
$ openssl x509 -in server.fix.crt -text -noout | grep Public-Key
                RSA Public-Key: (2048 bit)
As the data to encrypt, i.e., private key, is 16 bytes and public key is 2048-bit, raw encrypted data would be 256 bytes and hex-string will be 512 bytes.

Extracting such data from memory dump can be done with multiple methods, e.g., using PowerShell memory dumper utility. A simpler method, using strings can be used, but it must be used with correct options. As PowerShell was run on Windows computer, it stores all strings in memory a bit different that other operating systems, .e.g., it widely uses Unicode UTF-16 or UCS-2 encodings. Therefore, option -e b must be specified. Filter output for 512-byte hex-strings. Turns out, there is only one such string.

$ strings -e b powershell.exe_181109_104716.dmp  | grep -E '^[0-9a-fA-F]{512}$'

To decrypt this data and recover ransomware's randomly generated private key, a corresponding key of server.crt is required. By guessing the file name as server.key and abusing malware's DNS server, it can be retrieved.

$ ./ server.key > server.key
$ sha256 server.key
SHA256 (server.key) = c9b5fd47da9df96b263c2f6048baebc762b8058271f49167db0bb5cae7c27ae2
Decrypt ransomware's private key.
$ echo 3cf903522e1a3966805b50e7f7dd51dc7969c73cfb1663a75a56ebf4aa4a1849d1949005437dc44b8464dca05680d531b7a971672d87b24b7a6d672d1d811e6c34f42b2f8d7f2b43aab698b537d2df2f401c2a09fbe24c5833d2c5861139c4b4d3147abb55e671d0cac709d1cfe86860b6417bf019789950d0bf8d83218a56e69309a2bb17dcede7abfffd065ee0491b379be44029ca4321e60407d44e6e381691dae5e551cb2354727ac257d977722188a946c75a295e714b668109d75c00100b94861678ea16f8b79b756e45776d29268af1720bc49995217d814ffd1e4b6edce9ee57976f9ab398f9a8479cf911d7d47681a77152563906a2c29c6d12f971 | xxd -r -p | openssl rsautl -inkey server.key -decrypt -oaep | xxd -p

To decrypt alabaster_passwords.elfdb.wannacookie, ransomware's source code must be analyzed further to understand how it encrypts files.

Ransomware is using AES-128 in CBC block mode (lines 7-9). It generates random IV, writes it's length in first 4 bytes, following by IV itself and then following by encrypted data. Simple, enough.

        $AESP = New-Object 'System.Security.Cryptography.AesManaged'
        $AESP.Mode = [System.Security.Cryptography.CipherMode]::CBC
        $AESP.BlockSize = 128
            $FileSW.Write([System.BitConverter]::GetBytes($AESP.IV.Length), 0, 4)
            $FileSW.Write($AESP.IV, 0, $AESP.IV.Length)
            $CryptoS.Write($Data, 0, $Count)
Reversing that, first, read the length of IV and IV itself. Length is 16 bytes, which isn't surprising.
$ cat alabaster_passwords.elfdb.wannacookie | head -c 4 | xxd -p
$ python -c "import codecs, struct; print(struct.unpack('i', codecs.decode('10000000', 'hex'))[0])"
$ tail -c+5 alabaster_passwords.elfdb.wannacookie | head -c 16 | xxd -p
Extract raw encrypted data.
tail -c+21 alabaster_passwords.elfdb.wannacookie > alabaster_passwords.elfdb.enc
Decrypt the file, using recovered ransomware's private key and IV.
openssl enc -d -aes-128-cbc -in alabaster_passwords.elfdb.enc -K fbcfc121915d99cc20a3d3d5d84f8308 -iv 1f98ac13b187f791ab42b24bcd7fed55 -out alabaster_passwords.elfdb

Turns out, alabaster_passwords.elfdb is a simple SQLite database.

$ file alabaster_passwords.elfdb
alabaster_passwords.elfdb: SQLite 3.x database, last written using SQLite version 3015002
It contains a lot of passwords...

name password usedfor
alabaster.snowballCookiesR0cK!2!#active directory
alabaster.snowballMoarCookiesPreeze1928Barcode Scanner

Answer to this objective is ED#ED#EED#EF#G#F#G#ABA#BA#B.

I'm seriously impressed by your security skills.
How could I forget that I used Rachmaninoff as my musical password?