In questo articolo andremo a vedere una piccola analisi su una tipologia di tecnica per creare offuscamento all’interno di un malware e renderne difficile l’analisi e la comprensione del suo comportamento. Questa tecnica non è utilizzata solo dai file malevoli, ma anche da alcuni software legittimi per impedire il reverse engineering quindi la raggiunta del codice sorgente originale e/o il tentativo di crackare il software.
La tecnica in questione, che da anche il nome alla tipologia del malware, è il “packing”. Il “packing” viene utilizzato dal malware per eludere il rilevamento. Storicamente però, i Packer, erano stati originariamente creati per ridurre le dimensioni dei file in modo che potessero essere trasferiti facilmente e più velocemente. La tipologia più comune dei packer è il “Run time packer”: questo tipo scompatta il file in memoria dopo l’esecuzione. Questa tipologia è molto usata dai malware ed in passato uno dei più abusati era proprio il packer che vedremo oggi: UPX.
UPX: Cos’è e come funziona?
UPX, acronimo di “Ultimate Packer for eXecutables,” come detto prima, un “packer” di file eseguibili progettato per ridurre le dimensioni dei programmi senza comprometterne la funzionalità.
Il funzionamento può essere riassunto nel seguente modo:
Compressione: UPX utilizza una varietà di algoritmi di compressione, come LZMA o Huffman, per ridurre le dimensioni del file eseguibile. In particolare, UPX sfrutta tecniche di compressione lossless, che permettono di ridurre la dimensione del file senza perdere alcuna informazione o funzionalità.
Decompressione: Quando il file UPX-packed viene eseguito, il loader di UPX decomprime il contenuto del file in memoria prima di eseguirlo. Questo processo avviene in tempo reale, quindi l’utente non percepisce alcuna differenza nell’esecuzione dell’applicazione.
Per capire meglio com’è composto un file UPX-packed apriamo con Malcat il sample: edde6985b8097fc0e3a2132e8c88dcad6fde403329ea5d48ca877efcdc4bff1b

Notiamo subito come le Yara Rules eseguite sul file non trovano nessun IoC ma identificano solo il packer del sample.
Sulla sinistra invece vediamo che il file è diviso in 3 sezioni denominate UPX#.
La sezione UPX0 è una sezione vuota che non contiene dati, ma che ha un’enorme dimensione di memoria virtuale; questo si può vedere molto bene nella sezione “Report” sotto riportata.

La sezione UPX1 contiene il codice per decomprimere l’eseguibile packed, di nome Stub, il punto di ingresso dell’eseguibile e il sample originale compresso con UPX packer.
Il sample si scompatterà automaticamente una volta eseguito, grazie alla parte chiamata Stub, all’interno della memoria virtuale allocata dalla sezione UPX0.
Un’altra grande differenza tra un sample packed e uno unpacked riguarda la tabella degli Import.
La tabella degli Import non è altro che le funzioni che un software richiama da altri file, in genere le varie DLL che forniscono funzionalità al sistema operativo Windows.
Quando un sample è packed ne risultano molte meno come possiamo vedere nell’immagine qui sotto:

Analizziamo il sample alla ricerca di IoC
Abbiamo quindi constatato che il sample che stiamo cercando di analizzare è UPX-packed, ci è quindi complicato capirne il funzionamento e se si tratti effettivamente di un sample malevolo oppure legittimo.
Per proseguire abbiamo quindi due strade: la prima è quella di effettuare l’unpacking del sample in analisi tramite il software di UPX, la seconda sarebbe eseguire il sample dinamicamente e carcare di ricostruire il suo comportamento.
Noi, per questo articolo, useremo la prima strada. Procediamo quindi a scaricare il software di UPX dalla loro pagina ufficiale: https://upx.github.io/
Una volta scaricato UPX lanciamo semplicemente il comando upx.exe -d sul sample e in pochi attimi avremo il sample non più packed:

Ora, proviamo ad inserirlo all’interno di Malcat e vediamo se riusciamo a capire effettivamente cosa stiamo analizzando:

Notiamo subito che, al contrario di prima, le Yara Rules hanno effettuato il match su alcune parti di file, suggerendo che il sample che stiamo analizzando è inerente a CobalStrike.
I malware che hanno alla base Cobalstrike, fanno parte di una famiglia ben più vasta che potrebbe essere definita come Shellcode. Questi programmi fanno uso della process injection per nascondere la loro esecuzione di codice malevolo nello spazio degli indirizzi di un processo legittimo. Di solito vengono presi di mira processi legittimi necessari a Windows ed eseguiti su ogni sistema, come svchost.exe o rundll32.exe. Questi processi sono generalmente eseguiti con privilegi di amministratore, quindi, l’attaccante li prende di mira per ottenere il controllo completo del dispositivo.
Imphash ed uno script per la sua gestione
Prima abbiamo accennato alla Import Table, possiamo notare come tra le informazioni del file è presente una voce Imphash. Questo hash è una tecnica creata da Mandiant che permette di raggruppare facilmente malware simili all’interno di una famiglia che utilizza le stesse librerie, di seguito la documentazione ufficiale: Tracking Malware with Import Hashing
La logica alla base dell’Imphash, creato da Mandiant, sta che se due malware utilizzano le stesse librerie e le stesse funzioni delle librerie, importate anche nello stesso ordine, allora è corretto pensare che essi siano correlati.
L’Imphash non è però un indicatore molto affidabile per identificare se un sample è malevolo oppure meno, ma può essere un buon campanello d’allarme. Per sfruttarlo al meglio noi di 8bitsecurity abbiamo sviluppato un semplice script Python che, tramite la raccolta di informazioni di MalAPI.io, permette di capire se un sample utilizza librerie o funzioni di cui è stato documentato un abuso malevolo.
Per utilizzarlo anche voi sarà sufficiente importarlo all’interno di Malcat e il gioco sarà fatto.
PS: Lo script condiviso è utilizzabile solamente all’interno di Malcat e non funzionerà da riga di comando senza prima averlo modificato. Sarà inoltre necessario scaricare la libreria PEfile di Python e copiarla all’interno della cartella Script di Malcat insieme all’Ordlookup.
Di seguito possiamo vedere l’output dello script lanciato sul file:

Queste sono tutte funzioni importate dal sample riconducibile a Cobalstrike che, su MalAPI.io, sono state documentate come funzioni abusate da malware.
Come detto prima, le librerie importate possono essere utilizzate come un ottimo campanello d’allarme in caso si stia analizzando un sample sconosciuto. Ad esempio, in questo caso possiamo notare come questo sample esegua DLL decisamente sospette, come: QueryPerformanceCounter e CreateThread + VirtualAlloc.
QueryPerformanceCounter è comunemente utilizzata dai malware per scopi anti-debug. Il malware misurerà il tempo prima e dopo un’operazione; se il tempo supera il tempo previsto, il malware terminerà o attiverà una funzione legittima per “camuffarsi”.
VirtualAlloc è spesso usato dal malware per allocare memoria virtuale come parte dell’iniezione di processi, e CreateThread viene utilizzata per creare un thread da eseguire nello spazio degli indirizzi virtuali del processo chiamante. Questa funzione è comunemente utilizzata per l’esecuzione di shellcode.
Grazie alla raccolta di queste informazioni possiamo concludere che il sample packed che abbiamo preso in analisi si tratta al 100% di un malware e che è relativo alla famiglia degli shellcode, più precisamente Cobalstrike.
Lasciamo al termine dell’articolo lo script per l’analisi delle API in caso lo vogliate provare.
Buon’analisi a tutti!
Script Python per Malcat per l’analisi della Import table
import pefile
import sys
mal = ["CreateToolhelp32Snapshot","EnumDeviceDrivers","EnumProcesses","EnumProcessModules","EnumProcessModulesEx","FindFirstFileA","FindNextFileA","GetLogicalProcessorInformation","GetLogicalProcessorInformationEx","GetModuleBaseNameA","GetSystemDefaultLangId","GetVersionExA","GetWindowsDirectoryA","IsWoW64Process","Module32First","Module32Next","Process32First","Process32Next","ReadProcessMemory","Thread32First","Thread32Next","GetSystemDirectoryA","GetSystemTime","ReadFile","GetComputerNameA","VirtualQueryEx","GetProcessIdOfThread","GetProcessId","GetCurrentThread","GetCurrentThreadId","GetThreadId","GetThreadInformation","GetCurrentProcess","GetCurrentProcessId","SearchPathA","GetFileTime","GetFileAttributesA","LookupPrivilegeValueA","LookupAccountNameA","GetCurrentHwProfileA","GetUserNameA","RegEnumKeyExA","RegEnumValueA","RegQueryInfoKeyA","RegQueryMultipleValuesA","RegQueryValueExA","NtQueryDirectoryFile","NtQueryInformationProcess","NtQuerySystemEnvironmentValueEx","EnumDesktopWindows","EnumWindows","NetShareEnum","NetShareGetInfo","NetShareCheck","GetAdaptersInfo","PathFileExistsA","GetNativeSystemInfo","RtlGetVersion","GetIpNetTable","GetLogicalDrives","GetDriveTypeA","RegEnumKeyA","WNetEnumResourceA","WNetCloseEnum","FindFirstUrlCacheEntryA","FindNextUrlCacheEntryA","WNetAddConnection2A","WNetAddConnectionA","EnumResourceTypesA","EnumResourceTypesExA","GetSystemTimeAsFileTime","GetThreadLocale","EnumSystemLocalesA","CreateFileMappingA","CreateProcessA","CreateRemoteThread","CreateRemoteThreadEx","GetModuleHandleA","GetProcAddress","GetThreadContext","HeapCreate","LoadLibraryA","LoadLibraryExA","LocalAlloc","MapViewOfFile","MapViewOfFile2","MapViewOfFile3","MapViewOfFileEx","OpenThread","Process32First","Process32Next","QueueUserAPC","ReadProcessMemory","ResumeThread","SetProcessDEPPolicy","SetThreadContext","SuspendThread","Thread32First","Thread32Next","Toolhelp32ReadProcessMemory","VirtualAlloc","VirtualAllocEx","VirtualProtect","VirtualProtectEx","WriteProcessMemory","VirtualAllocExNuma","VirtualAlloc2","VirtualAlloc2FromApp","VirtualAllocFromApp","VirtualProtectFromApp","CreateThread","WaitForSingleObject","OpenProcess","OpenFileMappingA","GetProcessHeap","GetProcessHeaps","HeapAlloc","HeapReAlloc","GlobalAlloc","AdjustTokenPrivileges","CreateProcessAsUserA","OpenProcessToken","CreateProcessWithTokenW","NtAdjustPrivilegesToken","NtAllocateVirtualMemory","NtContinue","NtCreateProcess","NtCreateProcessEx","NtCreateSection","NtCreateThread","NtCreateThreadEx","NtCreateUserProcess","NtDuplicateObject","NtMapViewOfSection","NtOpenProcess","NtOpenThread","NtProtectVirtualMemory","NtQueueApcThread","NtQueueApcThreadEx","NtQueueApcThreadEx2","NtReadVirtualMemory","NtResumeThread","NtUnmapViewOfSection","NtWaitForMultipleObjects","NtWaitForSingleObject","NtWriteVirtualMemory","RtlCreateHeap","LdrLoadDll","RtlMoveMemory","RtlCopyMemory","SetPropA","WaitForSingleObjectEx","WaitForMultipleObjects","WaitForMultipleObjectsEx","KeInsertQueueApc","Wow64SetThreadContext","NtSuspendProcess","NtResumeProcess","DuplicateToken","NtReadVirtualMemoryEx","CreateProcessInternal","EnumSystemLocalesA","UuidFromStringA",
"CreateFileMappingA","DeleteFileA","GetModuleHandleA","GetProcAddress","LoadLibraryA","LoadLibraryExA","LoadResource","SetEnvironmentVariableA","SetFileTime","Sleep","WaitForSingleObject","SetFileAttributesA","SleepEx","NtDelayExecution","NtWaitForMultipleObjects","NtWaitForSingleObject","CreateWindowExA","RegisterHotKey","timeSetEvent","IcmpSendEcho","WaitForSingleObjectEx","WaitForMultipleObjects","WaitForMultipleObjectsEx","SetWaitableTimer","CreateTimerQueueTimer","CreateWaitableTimer","SetWaitableTimer","SetTimer","Select","ImpersonateLoggedOnUser","SetThreadToken","DuplicateToken","SizeOfResource","LockResource","CreateProcessInternal","TimeGetTime","EnumSystemLocalesA","UuidFromStringA",
"AttachThreadInput","CallNextHookEx","GetAsyncKeyState","GetClipboardData","GetDC","GetDCEx","GetForegroundWindow","GetKeyboardState","GetKeyState","GetMessageA","GetRawInputData","GetWindowDC","MapVirtualKeyA","MapVirtualKeyExA","PeekMessageA","PostMessageA","PostThreadMessageA","RegisterHotKey","RegisterRawInputDevices","SendMessageA","SendMessageCallbackA","SendMessageTimeoutA","SendNotifyMessageA","SetWindowsHookExA","SetWinEventHook","UnhookWindowsHookEx","BitBlt","StretchBlt","GetKeynameTextA","WinExec","FtpPutFileA","HttpOpenRequestA","HttpSendRequestA","HttpSendRequestExA","InternetCloseHandle","InternetOpenA","InternetOpenUrlA","InternetReadFile","InternetReadFileExA","InternetWriteFile","URLDownloadToFile","URLDownloadToCacheFile","URLOpenBlockingStream","URLOpenStream","Accept","Bind","Connect","Gethostbyname","Inet_addr","Recv","Send","WSAStartup","Gethostname","Socket","WSACleanup","Listen","ShellExecuteA","ShellExecuteExA","DnsQuery_A","DnsQueryEx","WNetOpenEnumA","FindFirstUrlCacheEntryA","FindNextUrlCacheEntryA","InternetConnectA","InternetSetOptionA","WSASocketA","Closesocket","WSAIoctl","ioctlsocket","HttpAddRequestHeaders","CreateToolhelp32Snapshot","GetLogicalProcessorInformation","GetLogicalProcessorInformationEx","GetTickCount","OutputDebugStringA","CheckRemoteDebuggerPresent","Sleep","GetSystemTime","GetComputerNameA","SleepEx","IsDebuggerPresent","GetUserNameA","NtQueryInformationProcess","ExitWindowsEx","FindWindowA","FindWindowExA","GetForegroundWindow","GetTickCount64","QueryPerformanceFrequency","QueryPerformanceCounter","GetNativeSystemInfo","RtlGetVersion","GetSystemTimeAsFileTime","CountClipboardFormats","CryptAcquireContextA","EncryptFileA","CryptEncrypt","CryptDecrypt","CryptCreateHash","CryptHashData","CryptDeriveKey","CryptSetKeyParam","CryptGetHashParam","CryptSetKeyParam","CryptDestroyKey","CryptGenRandom","DecryptFileA","FlushEfsCache","GetLogicalDrives","GetDriveTypeA","CryptStringToBinary","CryptBinaryToString","CryptReleaseContext","CryptDestroyHash","EnumSystemLocalesA","ConnectNamedPipe","CopyFileA","CreateFileA","CreateMutexA","CreateMutexExA","DeviceIoControl","FindResourceA","FindResourceExA","GetModuleBaseNameA","GetModuleFileNameA","GetModuleFileNameExA","GetTempPathA","IsWoW64Process","MoveFileA","MoveFileExA","PeekNamedPipe","WriteFile","TerminateThread","CopyFile2","CopyFileExA","CreateFile2","GetTempFileNameA","TerminateProcess","SetCurrentDirectory","FindClose","SetThreadPriority","UnmapViewOfFile","ControlService","ControlServiceExA","CreateServiceA","DeleteService","OpenSCManagerA","OpenServiceA","RegOpenKeyA","RegOpenKeyExA","StartServiceA","StartServiceCtrlDispatcherA","RegCreateKeyExA","RegCreateKeyA","RegSetValueExA","RegSetKeyValueA","RegDeleteValueA","RegOpenKeyExA","RegEnumKeyExA","RegEnumValueA","RegGetValueA","RegFlushKey","RegGetKeySecurity","RegLoadKeyA","RegLoadMUIStringA","RegOpenCurrentUser","RegOpenKeyTransactedA","RegOpenUserClassesRoot","RegOverridePredefKey","RegReplaceKeyA","RegRestoreKeyA","RegSaveKeyA","RegSaveKeyExA","RegSetKeySecurity","RegUnLoadKeyA","RegConnectRegistryA","RegCopyTreeA","RegCreateKeyTransactedA","RegDeleteKeyA","RegDeleteKeyExA","RegDeleteKeyTransactedA","RegDeleteKeyValueA","RegDeleteTreeA","RegDeleteValueA","RegCloseKey","NtClose","NtCreateFile","NtDeleteKey","NtDeleteValueKey","NtMakeTemporaryObject","NtSetContextThread","NtSetInformationProcess","NtSetInformationThread","NtSetSystemEnvironmentValueEx","NtSetValueKey","NtShutdownSystem","NtTerminateProcess","NtTerminateThread","RtlSetProcessIsCritical","DrawTextExA","GetDesktopWindow","SetClipboardData","SetWindowLongA","SetWindowLongPtrA","OpenClipboard","SetForegroundWindow","BringWindowToTop","SetFocus","ShowWindow","NetShareSetInfo","NetShareAdd","NtQueryTimer","GetIpNetTable","GetLogicalDrives","GetDriveTypeA","CreatePipe","RegEnumKeyA","WNetOpenEnumA","WNetEnumResourceA","WNetAddConnection2A","CallWindowProcA","NtResumeProcess","lstrcatA","ImpersonateLoggedOnUser","SetThreadToken","SizeOfResource","LockResource","UuidFromStringA"]
pe = pefile.PE(analysis.file.path)
for item in pe.DIRECTORY_ENTRY_IMPORT:
print ("DLL File: %s"%item.dll.lower())
print ("Functions: ")
for import_fn in item.imports:
if (import_fn.name).decode('utf-8') in mal:
print ((import_fn.name).decode('utf-8'))
print ('\n')






