Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
YARA rules, scripts, and tools used in the ShellTea analysis that may be useful to other reverse engineers.
#!/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
//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);
}
64dbfde4ca3dcaca04e1b7cbffa01057ecc1cc30948fb8f0582756adfa7d3261 decrypt.rb
a1764879ff7ad8c27ddac63bedceed6a74ca76585d77cc9e73d278ebd88dba69 FunctionHashResolution.cpp
82e275595f835ec2fbd35f758f507d8e9d07e0cb20fecf8681c3392a71931e37 ProcessNameCRC32er.cpp
0333a5934122d849968f7f122eb1f9d10324fb7189415071b7a6e0428a2b05f8 PyIdaFixupFunctions.py
e67355b117a164942060bc111b9d123d301a74b32a61a3e7450bcb5bd5da1f31 readme.md
/*
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
}
//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);
}
}
# 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
You can’t perform that action at this time.