Skip to content

Instantly share code, notes, and snippets.

@root9b-zz
Last active August 28, 2018 17:56
Show Gist options
  • Save root9b-zz/24b9b25f3b0b06a6939881e68d0bd2d0 to your computer and use it in GitHub Desktop.
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.
#!/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