Analysis scripts used in the ShellTea/PoSlurp reverse engineering: https://www.root9b.com/sites/default/files/whitepapers/PoS%20Malware%20ShellTea%20PoSlurp.pdf
Last active
August 28, 2018 17:56
-
-
Save root9b-zz/24b9b25f3b0b06a6939881e68d0bd2d0 to your computer and use it in GitHub Desktop.
YARA rules, scripts, and tools used in the ShellTea analysis that may be useful to other reverse engineers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/ruby | |
# Copyright root9B, 2017. License: GPLv2 | |
# Run like this in a console: ruby decrypt.rb < encrypted_file.dat | |
inp = STDIN | |
l = inp.read(1) | |
until l.nil? | |
len = l.unpack('C')[0] | |
key = inp.read(4).unpack('V')[0] | |
crypted = inp.read(len).unpack('C*') | |
plain = crypted.map do |c| | |
p = (c ^ key) & 0xFF | |
key = ((0x19660D * key) & 0xFFFFFFFF) - 0x49F91E6B | |
p | |
end | |
puts plain.pack('C*').inspect | |
l = inp.read(1) | |
end | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Copyright root9B, 2017. License: GPLv2 | |
//WARNING: Since this program executes snippets of malicious code, ALWAYS run in a sandbox. | |
//Step 1: export the disassembly of your original sample and grep for function hash constants, saving them in the hashes array | |
//Step 2: export the sample into a C array; run "xxd -i < sample.bin" and paste the output into the shellcode array | |
//Step 3: find the function resolver offset and replace 0x1B10 with that | |
//Step 4: compile for the same architecture (32 vs 64 bit) of your shellcode, then run IN A SANDBOX | |
//Step 5: paste the results into the IDA fixup script | |
#include <Windows.h> | |
#include <cstdio> | |
#include <Dbghelp.h> | |
#pragma comment(lib, "dbghelp.lib") | |
const DWORD hashes[] = { | |
0x0A26C0E5E, | |
0x0A2B16D2F//... TODO: INSERT YOUR HASHES HERE | |
}; | |
__declspec(align(0x100)) | |
const BYTE shellcode[] = { 0x00 //TODO: INSERT YOUR SHELLCODE HERE | |
};// The shellcode dumped from memory | |
void main() { | |
DWORD dontcare; | |
LoadLibraryA("Advapi32.dll"); | |
LoadLibraryA("ws2_32.dll"); | |
PBYTE mem = (PBYTE)VirtualAlloc((LPVOID)0x23D0000, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); | |
for (int i = 0; i < sizeof(shellcode); i++) { | |
mem[i] = shellcode[i]; | |
} | |
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_LOAD_ANYTHING); | |
if (!SymInitialize(GetCurrentProcess(), NULL, TRUE)){ //Initialize the Windows debug help API | |
DWORD error = GetLastError(); | |
printf("SymInitialize returned error : %d\n", error); | |
return; | |
} | |
PBYTE resolveraddr = mem + 0x1B10; //TODO: PUT THE OFFSET OF THE FUNCTION HASH RESOLUTION CODE HERE | |
for (int i = 0; i < sizeof(hashes) / sizeof(DWORD); i++) { //For each function hash extracted from the shellcode | |
auto resolver = (DWORD64(*)(DWORD))resolveraddr; | |
DWORD64 dwAddress = (*resolver)(hashes[i]); //Call the shellcode's resolver function to get the API function address | |
DWORD64 dwDisplacement = 0; | |
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = { 0 }; | |
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; | |
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); | |
pSymbol->MaxNameLen = MAX_SYM_NAME; | |
if (SymFromAddr(GetCurrentProcess(), dwAddress, &dwDisplacement, pSymbol)){ // Get the resolved API function's name | |
printf(" [0x%08X, \"%s\"],\n", hashes[i], pSymbol->Name); //Output the hash and name pastable into the IDA script | |
} else { | |
DWORD error = GetLastError(); | |
fprintf(stderr, "SymFromAddr returned error : %d on %p (from %08X)\n", error, dwAddress, hashes[i]); | |
} | |
} | |
ExitProcess(0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64dbfde4ca3dcaca04e1b7cbffa01057ecc1cc30948fb8f0582756adfa7d3261 decrypt.rb | |
a1764879ff7ad8c27ddac63bedceed6a74ca76585d77cc9e73d278ebd88dba69 FunctionHashResolution.cpp | |
82e275595f835ec2fbd35f758f507d8e9d07e0cb20fecf8681c3392a71931e37 ProcessNameCRC32er.cpp | |
0333a5934122d849968f7f122eb1f9d10324fb7189415071b7a6e0428a2b05f8 PyIdaFixupFunctions.py | |
e67355b117a164942060bc111b9d123d301a74b32a61a3e7450bcb5bd5da1f31 readme.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
root9B Yara Rules for SHELLTEA + POSLURP MALWARE blog entry | |
https://www.root9b.com/newsroom/shelltea-poslurp-malware | |
*/ | |
rule PoSlurpFile : PoSlurp | |
{ | |
meta: | |
copyright = "root9b, LLC" | |
authors = "Matt Weeks, Dax Morrow" | |
description = "ShellTea + PoSlurp PoS Malware on Disk PoSlurp executable" | |
reference = "https://www.root9b.com/newsroom/shelltea-poslurp-malware" | |
version = "1.0" | |
last_modified = "2017-06-27" | |
strings: | |
$hex1 = { 81 C2 FF 5C F3 22 52 56 E8 } // outer layer custom function resolver | |
condition: | |
uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550 and $hex1 | |
} | |
rule inRegPowerSniff : PowerSniff | |
{ | |
meta: | |
copyright = "root9b, LLC" | |
authors = "Matt Weeks, Dax Morrow" | |
description = "ShellTea + PoSlurp PoS Malware in Registry PowerSniff" | |
reference = "https://www.root9b.com/newsroom/shelltea-poslurp-malware" | |
version = "1.0" | |
last_modified = "2017-06-27" | |
strings: | |
$hex1 = { 41 2B CF 81 38 BE BA AD AB 48 8B D0 75 09 81 78 04 0D F0 AD 8B } // shellcode blob in registry | |
condition: | |
$hex1 | |
} | |
rule inRegShellTea : ShellTea | |
{ | |
meta: | |
copyright = "root9b, LLC" | |
authors = "Matt Weeks, Dax Morrow" | |
description = "ShellTea + PoSlurp PoS Malware in Registry ShellTea" | |
reference = "https://www.root9b.com/newsroom/shelltea-poslurp-malware" | |
version = "1.0" | |
last_modified = "2017-06-27" | |
strings: | |
$hex1 = { 48 83 EC 28 E8 F7 03 00 00 [1015] 48 89 5C 24 18 48 89 4C 24 08 55 56 57 41 54 41 } // Binary registry value with variable content for ShellTea config | |
condition: | |
$hex1 | |
} | |
rule inMemPowerSniff : PowerSniff | |
{ | |
meta: | |
copyright = "root9b, LLC" | |
authors = "Matt Weeks, Dax Morrow" | |
description = "ShellTea + PoSlurp in Memory PowerSniff" | |
reference = "https://www.root9b.com/newsroom/shelltea-poslurp-malware" | |
version = "1.0" | |
last_modified = "2017-06-27" | |
strings: | |
$wide_string = "/%s?user=%08x%08x%08x%08x&id=%u&ver=%u&os=%lu&os2=%lu&host=%u&k=%lu&type=%u" wide // PowerSniff URL Pattern | |
$wide_string2 = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT %u.%u%s)" wide // PowerSniff URL Pattern | |
condition: | |
all of them | |
} | |
rule inMemShellTea : ShellTea | |
{ | |
meta: | |
copyright = "root9b, LLC" | |
authors = "Matt Weeks, Dax Morrow" | |
description = "ShellTea + PoSlurp PoS Malware in Memory ShellTea" | |
reference = "https://www.root9b.com/newsroom/shelltea-poslurp-malware" | |
version = "1.0" | |
last_modified = "2017-06-27" | |
strings: | |
$hex1 = { B9 1D C7 12 45 E8 } // opcodes for function hash | |
$hex2 = { B9 52 7E 10 E1 E8 } // opcodes for function hash | |
$hex3 = { B9 CC 11 67 D6 E8 } // opcodes for function hash | |
condition: | |
all of them | |
} | |
rule inMemPoSlurp : PoSlurp | |
{ | |
meta: | |
copyright = "root9b, LLC" | |
authors = "Matt Weeks, Dax Morrow" | |
description = "ShellTea + PoSlurp PoS Malware in Memory PoSlurp" | |
reference = "https://www.root9b.com/newsroom/shelltea-poslurp-malware" | |
version = "1.0" | |
last_modified = "2017-06-27" | |
strings: | |
$hex1 = { C6 45 ED 65 C6 45 EE 72 C6 45 EF 6E C6 45 F0 65 } // Kernel32 obfuscated | |
$hex2 = { E8 EE FD FF FF 68 88 13 00 00 FF D6 8D 44 24 18 50 FF D7 8D 44 24 10 50 8D 44 24 1C 50 FF D3 8B 44 24 10 2B 05 80 50 40 00 8B 4C 24 14 1B 0D 84 50 40 00 6A 00 68 80 96 98 00 51 50 E8 B7 05 00 00 6A 3C 33 D2 59 F7 F1 3B 05 2C 40 40 00 72 B0 } // opcodes from top-level scan memory basic block | |
condition: | |
all of them | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Copyright root9B 2017, GPLv2 license | |
//This program calculates CRC32 checksums of each running process name (upper-case, UTF-8 encoded) | |
//(0xE10E9818 initial value) then checks against the list of "bad CRC's" in the ShellTea malware. | |
//You can use this code to troubleshoot running the sample, or to figure out what other processes | |
//may be detected by the malware; simply start a process, then run this sample and see if it is flagged. | |
//According to https://www.fireeye.com/blog/threat-research/2015/10/shifu-malware-analyzed-behavior-capabilities-and.html | |
//the Shifu malware also used a technique like this, although it was looking for different checksums. | |
#include <Windows.h> | |
#include <winnt.h> | |
#include <ntsecapi.h> | |
#include <ntstatus.h> | |
#include <cstdio> | |
typedef enum _SYSTEM_INFORMATION_CLASS { | |
SystemBasicInformation = 0, | |
SystemPerformanceInformation = 2, | |
SystemTimeOfDayInformation = 3, | |
SystemProcessInformation = 5, | |
SystemProcessorPerformanceInformation = 8, | |
SystemInterruptInformation = 23, | |
SystemExceptionInformation = 33, | |
SystemRegistryQuotaInformation = 37, | |
SystemLookasideInformation = 45 | |
} SYSTEM_INFORMATION_CLASS; | |
#pragma pack | |
struct SYSTEM_PROCESS_INFORMATION { | |
ULONG NextEntryOffset; | |
ULONG NumberOfThreads; | |
LARGE_INTEGER Reserved[3]; | |
LARGE_INTEGER CreateTime; | |
LARGE_INTEGER UserTime; | |
LARGE_INTEGER KernelTime; | |
UNICODE_STRING ImageName; | |
DWORD BasePriority; | |
HANDLE ProcessId; | |
HANDLE ParentProcessId; | |
ULONG HandleCount; | |
ULONG Reserved2[2]; | |
PVOID VMCounters; | |
IO_COUNTERS IOCounters; | |
PVOID Threads[1]; | |
}; // SYSTEM_PROCESS_INFORMATION | |
DWORD badones[] = { | |
0x025AFC10, | |
0x1A8BB751, | |
0x2395AB27, | |
0x2432EE64, | |
0x2CC74E5A, | |
0x30DA46EB, | |
0x37699FD6, | |
0x3C6C00C8, | |
0x5186DE03, | |
0x534DA75F, | |
0x545005AC, | |
0x567AF85A, | |
0x57430EAB, | |
0x5962855E, | |
0x62269259, | |
0x68EAD817, | |
0x6AD5AD6D, | |
0xAEF17831, | |
0xA2BD1E66, | |
0xB251BF84, | |
0xC3E03BDA, | |
0xC585CA71, | |
0xC6821D96, | |
0xCCF67C9C, | |
0xD3CC62EA, | |
0xFF517130, | |
0xF049D87A | |
}; | |
void main() { | |
auto ZwQuerySystemInformation = (NTSTATUS(WINAPI*)( | |
SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwQuerySystemInformation"); | |
auto RtlComputeCrc32 = (DWORD(WINAPI*)(DWORD, PVOID, ULONG))GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlComputeCrc32"); | |
SYSTEM_PROCESS_INFORMATION spi[1024] = { 0 }; | |
ULONG wrote = 0; | |
ZwQuerySystemInformation(SystemProcessInformation, &spi, sizeof(spi), &wrote); | |
SYSTEM_PROCESS_INFORMATION* pspi = spi; | |
while (pspi->NextEntryOffset) { | |
if (pspi->ImageName.Buffer) { | |
wcsupr(pspi->ImageName.Buffer); | |
char mbsname[100] = { 0 }; | |
wcstombs(mbsname, pspi->ImageName.Buffer, 31); | |
size_t len = lstrlenA(mbsname); | |
DWORD crc32 = RtlComputeCrc32(0xE10E9818, mbsname, len); | |
const char* result = "not on the list of bad ones"; | |
for (int i = 0; i < sizeof(badones) / sizeof(DWORD); i++) { | |
if (badones[i] == crc32) { | |
result = "BAD ONE FOUND!!!!!!"; | |
} | |
} | |
printf("%08X\t%s\t%s\n", crc32, mbsname, result); | |
} | |
pspi = (SYSTEM_PROCESS_INFORMATION*)((char*)pspi + pspi->NextEntryOffset); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright root9B, 2017. License: GPLv2 | |
# IDA Python script to fix up function hashes. Use the output of FunctionHashResolution.cpp to get the mappings. | |
mappings = [ | |
[0xA26C0E5E, "WSAStartup"], | |
[0xA2B16D2F, "GetTempPathW"], | |
[0xA56B3DAD, "GetVolumeInformationA"], | |
[0x0A7D3E26, "CryptHashData"], | |
[0xA83AEEF9, "RegCreateKeyExW"], | |
[0x0AAC2FB4, "GetTokenInformation"], | |
[0x0AAF81B7, "RegOpenKeyW"], | |
[0xACB6FA76, "lstrcat"], | |
[0xAE8B9853, "ExpandEnvironmentStringsW"], | |
[0xAF299BFD, "closesocket"], | |
[0xAFB90B81, "GetSystemTimeAsFileTime"], | |
[0xB0E0C901, "freeaddrinfo"], | |
[0xB6376199, "RtlDestroyHeap"], | |
[0xBBA200BB, "FreeSid"], | |
[0xBCCE050F, "GetProcAddress"], | |
[0xBE67CFAE, "CryptReleaseContext"], | |
[0xBF951469, "MoveFileExW"], | |
[0xC0DC10C1, "wcsupr"], | |
[0xC36DB63D, "CreateThread"], | |
[0xC478D039, "CryptCreateHash"], | |
[0xC600C325, "RegCloseKey"], | |
[0x0C87BAC9, "OpenProcess"], | |
[0xC8E01F76, "OpenProcessToken"], | |
[0xC9292DBB, "ZwQueryVirtualMemory"], | |
[0xD007FF23, "RtlCompareMemory"], | |
[0xD66711CC, "RtlComputeCrc32"], | |
[0xD7A60D7D, "send"], | |
[0xD7F81D46, "CryptGetHashParam"], | |
[0xD92154EB, "getaddrinfo"], | |
[0xDE2067EA, "Process32NextW"], | |
[0xDE8A1419, "GetModuleFileNameW"], | |
[0xDEC4A4A3, "SetWaitableTimer"], | |
[0xE1107E52, "RtlAllocateHeap"], | |
[0xE17FE92B, "ZwReadVirtualMemory"], | |
[0x0E2FD319, "CreateToolhelp32Snapshot"], | |
[0xE5C8C0B5, "GetComputerNameW"], | |
[0xE7CC317F, "GetSidSubAuthority"], | |
[0xED819B1F, "NtClose"], | |
[0xEE4DE75C, "gethostbyname"], | |
[0xEE7E89AC, "GetNativeSystemInfo"], | |
[0xF4E803FE, "socket"], | |
[0x1393995F, "ZwQueryInformationToken"], | |
[0x160CBCAC, "ZwQuerySystemInformation"], | |
[0x1A28A473, "lstrcmpiW"], | |
[0x1BCC6FC0, "lstrlenA"], | |
[0x1CBD67A0, "wcstombs"], | |
[0x1D7FC573, "RtlAddVectoredExceptionHandler"], | |
[0x24935EAC, "WriteProcessMemory"], | |
[0x250D3EA4, "GetTickCount"], | |
[0x2556D10F, "DeleteFileW"], | |
[0x02B7F017, "LoadLibraryA"], | |
[0x2C6EA9BA, "RtlCreateHeap"], | |
[0x2E5EB4B3, "strchr"], | |
[0x2E888727, "CloseHandle"], | |
[0x2FE94AC3, "WSACleanup"], | |
[0x030EE995, "strtoul"], | |
[0x34F69A2A, "GetExitCodeProcess"], | |
[0x3579F9EE, "RtlCreateUserThread"], | |
[0x3A289755, "GetVersionExW"], | |
[0x41B779DC, "AllocateAndInitializeSid"], | |
[0x42364470, "EqualSid"], | |
[0x42BA6B08, "CheckTokenMembership"], | |
[0x42C98E38, "GetCurrentProcess"], | |
[0x4512C71D, "GetUserNameW"], | |
[0x477B5F89, "vsnwprintf"], | |
[0x48761649, "GetCurrentThreadId"], | |
[0x48AC50F4, "RtlRemoveVectoredExceptionHandler"], | |
[0x4AC15208, "VirtualAlloc"], | |
[0x4F2FBB54, "CreateProcessW"], | |
[0x5079B7B8, "NtOpenProcess"], | |
[0x57B84A1D, "VirtualFree"], | |
[0x57EFD0A7, "FlushFileBuffers"], | |
[0x5B0798F6, "Process32FirstW"], | |
[0x5C0ABCEF, "GetVersion"], | |
[0x608C8199, "NtOpenProcessToken"], | |
[0x62E2AB99, "memcmp"], | |
[0x64301E38, "RtlFreeHeap"], | |
[0x6ABE2B38, "VirtualAllocEx"], | |
[0x06BD8D4A, "htons"], | |
[0x6C8A1D1C, "Sleep"], | |
[0x6D7BAA60, "WSAGetLastError"], | |
[0x6DA2004A, "CreateFileW"], | |
[0x6F628F58, "ReleaseMutex"], | |
[0x725BB0F0, "CsrGetProcessId"], | |
[0x74C83048, "GetSystemInfo"], | |
[0x7691D7CD, "LoadLibraryW"], | |
[0x79D65F10, "QueryPerformanceCounter"], | |
[0x7A2F5708, "GetSidSubAuthorityCount"], | |
[0x7EF68CF4, "GetCurrentProcessId"], | |
[0x80A51A9E, "recv"], | |
[0x841225F7, "shutdown"], | |
[0x84AA3AFA, "ReadFile"], | |
[0x87D571F4, "CryptDestroyHash"], | |
[0x899E4B5E, "lstrlenW"], | |
[0x8E73BAF5, "CreateMutexW"], | |
[0x91B3614C, "CryptAcquireContextW"], | |
[0x924E6624, "setsockopt"], | |
[0x93810452, "GetFileSizeEx"], | |
[0x93BB8A50, "connect"], | |
[0x967A0E28, "RegSetValueExW"], | |
[0x9A25ACA8, "RegQueryValueExW"], | |
[0x9B7BC889, "RtlAdjustPrivilege"], | |
[0x9F57B969, "WaitForSingleObject"], | |
[0x9FE0DD01, "GetLastError"], | |
[0x000432A5, "WriteFile"], | |
[0xB04A8956, "wsprintfW"], | |
[0xB4C799EA, "StrStrIWStub"], | |
[0x0630EC96, "StrChrWStub"], | |
[0x866E50A5, "StrStrAStub"], | |
[0x89D43C76, "wsprintfA"], | |
[0x987F83BE, "GetWindowThreadProcessId"], | |
[0x5741DAD9, "GetShellWindow"], | |
[0xCC0159FF, "CreateWaitableTimerW"] | |
] | |
idx = idaapi.get_enum_qty() | |
enumid = idaapi.get_enum("FunctionHashes") | |
if enumid == 0xffffffffffffffffL: | |
enumid = idaapi.add_enum(idx, "FunctionHashes", 0) | |
for mapping in mappings: | |
err = idaapi.add_enum_member(enumid, mapping[1], mapping[0], -1) | |
f= idaapi.find_imm(0, idaapi.SEARCH_DOWN, mapping[0]) | |
while f[1] > 0 and f[0] < 0xffffffffffffffffL: | |
print "Found %s at %s (%s)" % (mapping[1], hex(f[0]), hex(mapping[0])) | |
OpEnumEx(f[0], -1, enumid, 0) | |
f = idaapi.find_imm(f[0],idaapi.SEARCH_DOWN,mapping[0]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment