Context tracking implemented
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# Plan de Pruebas para Context Tracking
|
||||
|
||||
## Resumen
|
||||
Context Tracking permite ver información del callback (como `cwd` e `impersonation_context`) en tabs visibles sobre el área de tasking en Mythic.
|
||||
|
||||
## Cambios Implementados
|
||||
|
||||
### 1. Variables Globales
|
||||
- `gCurrentDirectory`: Mantiene el directorio actual
|
||||
- `gImpersonationContext`: Mantiene el usuario impersonado
|
||||
|
||||
### 2. Comandos que Actualizan Contexto
|
||||
- **`cd`**: Actualiza `cwd` cuando cambias de directorio
|
||||
- **`steal_token`**: Actualiza `impersonation_context` cuando robas un token
|
||||
- **`make_token`**: Actualiza `impersonation_context` cuando creas un token
|
||||
- **`rev2self`**: Limpia `impersonation_context` cuando reviertes la impersonación
|
||||
|
||||
### 3. Checkin
|
||||
- Incluye `cwd` inicial al hacer checkin
|
||||
|
||||
## Plan de Pruebas
|
||||
|
||||
### Prueba 1: Verificar cwd inicial en Checkin
|
||||
1. Compila y despliega el agente
|
||||
2. Verifica que al hacer checkin, aparezca un tab con `cwd` mostrando el directorio actual
|
||||
3. **Resultado esperado**: Tab "cwd" visible con el directorio actual (ej: `C:\Users\localuser`)
|
||||
|
||||
### Prueba 2: Actualizar cwd con cd
|
||||
1. Ejecuta `cd C:\Windows`
|
||||
2. Verifica que el tab `cwd` se actualice a `C:\Windows`
|
||||
3. **Resultado esperado**: Tab "cwd" cambia a `C:\Windows`
|
||||
|
||||
### Prueba 3: Impersonation Context con steal_token
|
||||
1. Ejecuta `list_tokens` para ver tokens disponibles
|
||||
2. Ejecuta `steal_token <pid>` donde `<pid>` es un proceso con privilegios elevados (ej: SYSTEM)
|
||||
3. Verifica que aparezca un tab `impersonation_context` con el usuario impersonado (ej: `NT AUTHORITY\SYSTEM`)
|
||||
4. **Resultado esperado**: Tab "impersonation_context" aparece con el usuario impersonado
|
||||
|
||||
### Prueba 4: Verificar whoami después de steal_token
|
||||
1. Después de `steal_token`, ejecuta `whoami`
|
||||
2. Verifica que `whoami` muestre el usuario impersonado
|
||||
3. **Resultado esperado**: `whoami` muestra el usuario del token robado
|
||||
|
||||
### Prueba 5: Limpiar impersonation_context con rev2self
|
||||
1. Después de `steal_token`, ejecuta `rev2self`
|
||||
2. Verifica que el tab `impersonation_context` se limpie (desaparezca o muestre vacío)
|
||||
3. Ejecuta `whoami` para verificar que volvió al usuario original
|
||||
4. **Resultado esperado**: Tab "impersonation_context" se limpia, `whoami` muestra usuario original
|
||||
|
||||
### Prueba 6: make_token actualiza impersonation_context
|
||||
1. Ejecuta `make_token domain username password`
|
||||
2. Verifica que el tab `impersonation_context` se actualice con el nuevo usuario
|
||||
3. **Resultado esperado**: Tab "impersonation_context" muestra el usuario del token creado
|
||||
|
||||
### Prueba 7: Verificar logs del translator
|
||||
1. Revisa los logs del translator para ver mensajes como:
|
||||
- `[CALLBACK_CONTEXT] cwd = 'C:\Windows'`
|
||||
- `[CALLBACK_CONTEXT] impersonation_context = 'NT AUTHORITY\SYSTEM'`
|
||||
2. **Resultado esperado**: Logs muestran los campos de callback context parseados correctamente
|
||||
|
||||
## Verificación de Implementación
|
||||
|
||||
### Verificar que los archivos se compilaron correctamente:
|
||||
```bash
|
||||
# En el contenedor de Mythic, verifica logs de compilación
|
||||
docker logs cazalla_translator | grep -i "error\|warning" | tail -20
|
||||
```
|
||||
|
||||
### Verificar en logs del agente:
|
||||
Busca logs que incluyan:
|
||||
- `[steal_token] Contexto de impersonación actualizado`
|
||||
- `[make_token] Contexto de impersonación actualizado`
|
||||
- `[rev2self] Contexto de impersonación limpiado`
|
||||
- `Checkin: Añadido cwd inicial`
|
||||
|
||||
### Verificar en logs del translator:
|
||||
Busca logs que incluyan:
|
||||
- `[CALLBACK_CONTEXT] cwd = '...'`
|
||||
- `[CALLBACK_CONTEXT] impersonation_context = '...'`
|
||||
- `[CHECKIN] Callback context: cwd = '...'`
|
||||
- `[CALLBACK_CONTEXT] Añadido callback context con X campos`
|
||||
|
||||
## Configuración en Mythic UI
|
||||
|
||||
1. Ve a **Settings** (configuración de usuario)
|
||||
2. Busca la sección de **Tasking Context Tabs**
|
||||
3. Asegúrate de que `cwd` e `impersonation_context` estén seleccionados
|
||||
4. Configura colores si lo deseas
|
||||
|
||||
## Solución de Problemas
|
||||
|
||||
### Si los tabs no aparecen:
|
||||
1. Verifica que los logs del translator muestren `[CALLBACK_CONTEXT]`
|
||||
2. Verifica que el JSON de respuesta incluya la clave `callback` con los campos
|
||||
3. Verifica la configuración de usuario en Mythic (Settings → Tasking Context Tabs)
|
||||
|
||||
### Si el cwd no se actualiza:
|
||||
1. Verifica logs del agente: `setCurrentDirectoryContext` debería llamarse
|
||||
2. Verifica que `cd` esté funcionando correctamente
|
||||
3. Revisa los logs del translator para ver si se parsea el callback context
|
||||
|
||||
### Si impersonation_context no se actualiza:
|
||||
1. Verifica que `steal_token`/`make_token` funcionen correctamente
|
||||
2. Verifica logs: `setImpersonationContext` debería llamarse
|
||||
3. Verifica logs del translator para ver si se parsea
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "SistemadeFicheros.h"
|
||||
#include "paquete.h"
|
||||
#include "transporte.h"
|
||||
#include "cazalla.h" // For context tracking functions
|
||||
|
||||
/* Forward decl to avoid implicit declaration on some toolchains */
|
||||
BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...);
|
||||
@@ -58,6 +59,11 @@ VOID CambiarDirectorio(PAnalizador argumentos) {
|
||||
char dir[2048];
|
||||
int length = GetCurrentDirectoryA(sizeof(dir), dir);
|
||||
|
||||
// Update global context tracking
|
||||
if (length > 0) {
|
||||
setCurrentDirectoryContext(dir);
|
||||
}
|
||||
|
||||
PPaquete salida = nuevoPaquete(0, FALSE);
|
||||
|
||||
// success
|
||||
@@ -67,6 +73,12 @@ VOID CambiarDirectorio(PAnalizador argumentos) {
|
||||
addString(salida, dir, FALSE);
|
||||
|
||||
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
|
||||
|
||||
// Add callback context with updated cwd
|
||||
if (length > 0) {
|
||||
addCallbackContext(respuestaTarea, "cwd", dir);
|
||||
}
|
||||
|
||||
mandarPaquete(respuestaTarea);
|
||||
liberarPaquete(salida);
|
||||
liberarPaquete(respuestaTarea);
|
||||
|
||||
@@ -5,6 +5,49 @@
|
||||
|
||||
CONFIG_CAZALLA* cazallaConfig = NULL;
|
||||
|
||||
/* Context tracking: Current working directory and impersonation context */
|
||||
static char gCurrentDirectory[MAX_PATH] = "";
|
||||
static char gImpersonationContext[256] = "";
|
||||
|
||||
/* Get current working directory for context tracking */
|
||||
PCHAR getCurrentDirectoryContext(void) {
|
||||
if (gCurrentDirectory[0] == '\0') {
|
||||
// Initialize with actual current directory if not set
|
||||
if (GetCurrentDirectoryA(sizeof(gCurrentDirectory), gCurrentDirectory) == 0) {
|
||||
gCurrentDirectory[0] = '\0';
|
||||
}
|
||||
}
|
||||
return gCurrentDirectory;
|
||||
}
|
||||
|
||||
/* Set current working directory for context tracking */
|
||||
VOID setCurrentDirectoryContext(PCHAR cwd) {
|
||||
if (cwd && strlen(cwd) < sizeof(gCurrentDirectory)) {
|
||||
strncpy(gCurrentDirectory, cwd, sizeof(gCurrentDirectory) - 1);
|
||||
gCurrentDirectory[sizeof(gCurrentDirectory) - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* Get impersonation context for context tracking */
|
||||
PCHAR getImpersonationContext(void) {
|
||||
return gImpersonationContext;
|
||||
}
|
||||
|
||||
/* Set impersonation context for context tracking */
|
||||
VOID setImpersonationContext(PCHAR context) {
|
||||
if (context) {
|
||||
strncpy(gImpersonationContext, context, sizeof(gImpersonationContext) - 1);
|
||||
gImpersonationContext[sizeof(gImpersonationContext) - 1] = '\0';
|
||||
} else {
|
||||
gImpersonationContext[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear impersonation context (e.g., after rev2self) */
|
||||
VOID clearImpersonationContext(void) {
|
||||
gImpersonationContext[0] = '\0';
|
||||
}
|
||||
|
||||
VOID cazallaMain() {
|
||||
_inf("Inicializando configuración de Cazalla");
|
||||
|
||||
|
||||
@@ -29,6 +29,13 @@ typedef struct {
|
||||
|
||||
extern PCONFIG_CAZALLA cazallaConfig;
|
||||
|
||||
/* Context tracking functions */
|
||||
PCHAR getCurrentDirectoryContext(void);
|
||||
VOID setCurrentDirectoryContext(PCHAR cwd);
|
||||
PCHAR getImpersonationContext(void);
|
||||
VOID setImpersonationContext(PCHAR context);
|
||||
VOID clearImpersonationContext(void);
|
||||
|
||||
VOID setUUID(PCHAR newUUID);
|
||||
VOID cazallaMain(void);
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "checkin.h"
|
||||
#include "cazalla.h"
|
||||
#include "socks_manager.h"
|
||||
#include "paquete.h" // For addCallbackContext
|
||||
#include "identity.h" // For IdentityGetUserInfo
|
||||
|
||||
#pragma comment(lib, "iphlpapi.lib")
|
||||
#include <iphlpapi.h>
|
||||
@@ -146,6 +148,68 @@ PAnalizador checkin() {
|
||||
addString(checkin, obtenerNombreProceso(), TRUE);
|
||||
addString(checkin, (PCHAR)"1.1.1.1", TRUE);
|
||||
|
||||
// Add callback context with initial cwd and impersonation_context
|
||||
// Get current directory and add to callback context
|
||||
char initial_cwd[MAX_PATH] = {0};
|
||||
if (GetCurrentDirectoryA(sizeof(initial_cwd), initial_cwd) > 0) {
|
||||
setCurrentDirectoryContext(initial_cwd); // Initialize global context
|
||||
addCallbackContext(checkin, "cwd", initial_cwd);
|
||||
_inf("Checkin: Añadido cwd inicial: %s", initial_cwd);
|
||||
} else {
|
||||
_wrn("Checkin: No se pudo obtener el directorio actual");
|
||||
}
|
||||
|
||||
// Get current user context (process token) for impersonation_context
|
||||
// This will show the user even if there's no impersonation active
|
||||
HANDLE hProcessToken = NULL;
|
||||
char initial_user[512] = {0};
|
||||
BOOL user_context_added = FALSE;
|
||||
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken)) {
|
||||
if (IdentityGetUserInfo(hProcessToken, initial_user, sizeof(initial_user))) {
|
||||
setImpersonationContext(initial_user); // Initialize global context
|
||||
addCallbackContext(checkin, "impersonation_context", initial_user);
|
||||
_inf("Checkin: Añadido impersonation_context inicial (process token): %s", initial_user);
|
||||
user_context_added = TRUE;
|
||||
} else {
|
||||
_wrn("Checkin: IdentityGetUserInfo falló");
|
||||
}
|
||||
CloseHandle(hProcessToken);
|
||||
} else {
|
||||
DWORD error = GetLastError();
|
||||
_wrn("Checkin: OpenProcessToken falló, error=%lu, usando fallback", error);
|
||||
}
|
||||
|
||||
if (!user_context_added) {
|
||||
// If we can't get process token, use the username from obtenerUsuarios()
|
||||
// Format: domain\username (we'll use hostname as domain if needed)
|
||||
char* username = obtenerUsuarios();
|
||||
char* domain = obtenerDominio();
|
||||
if (username && strlen(username) > 0) {
|
||||
if (domain && wcslen(domain) > 0) {
|
||||
// Convert domain from wide char to char
|
||||
char domain_narrow[256] = {0};
|
||||
WideCharToMultiByte(CP_ACP, 0, domain, -1, domain_narrow, sizeof(domain_narrow), NULL, NULL);
|
||||
snprintf(initial_user, sizeof(initial_user), "%s\\%s", domain_narrow, username);
|
||||
} else {
|
||||
// Use hostname as domain if domain is empty
|
||||
char* hostname = obtenerHostame();
|
||||
if (hostname && strlen(hostname) > 0) {
|
||||
snprintf(initial_user, sizeof(initial_user), "%s\\%s", hostname, username);
|
||||
} else {
|
||||
strncpy(initial_user, username, sizeof(initial_user) - 1);
|
||||
}
|
||||
}
|
||||
setImpersonationContext(initial_user);
|
||||
addCallbackContext(checkin, "impersonation_context", initial_user);
|
||||
_inf("Checkin: Añadido impersonation_context inicial (fallback): %s", initial_user);
|
||||
} else {
|
||||
_wrn("Checkin: No se pudo obtener username para impersonation_context");
|
||||
}
|
||||
}
|
||||
|
||||
_inf("Checkin: Tamaño del paquete antes de enviar: %zu bytes", checkin->length);
|
||||
|
||||
// === When mythic_encrypts=True, agent sends plaintext ===
|
||||
// The C2 profile (HTTP) handles encryption at HTTP layer
|
||||
// No need for ECC key exchange in checkin
|
||||
|
||||
@@ -904,3 +904,30 @@ VOID addCallbackToken(PPaquete paquete, PCHAR action, PCHAR host, UINT32 token_i
|
||||
// Token ID
|
||||
addInt32(paquete, token_id);
|
||||
}
|
||||
|
||||
// Helper function to add callback context (cwd, impersonation_context, etc.)
|
||||
// Format: [0xF0 marker][field_name_len:4][field_name][field_value_len:4][field_value]
|
||||
// Can be called multiple times to add multiple fields
|
||||
VOID addCallbackContext(PPaquete paquete, PCHAR field_name, PCHAR field_value) {
|
||||
if (!paquete || !field_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Callback context marker (0xF0) - Note: 0xF1 is CHECKIN action, so we use 0xF0
|
||||
// This marker indicates callback context data
|
||||
addByte(paquete, 0xF0);
|
||||
|
||||
// Field name length and value (e.g., "cwd", "impersonation_context")
|
||||
SIZE_T name_len = strlen(field_name);
|
||||
addInt32(paquete, (UINT32)name_len);
|
||||
addString(paquete, field_name, FALSE);
|
||||
|
||||
// Field value (optional - use empty string if NULL)
|
||||
if (field_value) {
|
||||
SIZE_T value_len = strlen(field_value);
|
||||
addInt32(paquete, (UINT32)value_len);
|
||||
addString(paquete, field_value, FALSE);
|
||||
} else {
|
||||
addInt32(paquete, 0);
|
||||
}
|
||||
}
|
||||
@@ -72,4 +72,10 @@ VOID addToken(PPaquete paquete, UINT32 token_id, PCHAR host, PCHAR description,
|
||||
/* action must be "add" or "remove" */
|
||||
VOID addCallbackToken(PPaquete paquete, PCHAR action, PCHAR host, UINT32 token_id);
|
||||
|
||||
/* Callback context helper: for updating callback context (cwd, impersonation_context, etc.) */
|
||||
/* Format: [0xF0 marker][field_name_len:4][field_name][field_value_len:4][field_value]... */
|
||||
/* Can be called multiple times to add multiple fields */
|
||||
/* Common fields: "cwd", "impersonation_context", "pid", "sleep_info", "extra_info" */
|
||||
VOID addCallbackContext(PPaquete paquete, PCHAR field_name, PCHAR field_value);
|
||||
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "analizador.h"
|
||||
#include "identity.h"
|
||||
#include "debug.h"
|
||||
#include "cazalla.h" // For context tracking functions
|
||||
#include <tlhelp32.h>
|
||||
#include <psapi.h>
|
||||
|
||||
@@ -480,6 +481,10 @@ VOID RobarToken(PAnalizador argumentos) {
|
||||
DWORD hostname_len = sizeof(hostname);
|
||||
GetComputerNameA(hostname, &hostname_len);
|
||||
|
||||
// Update global impersonation context
|
||||
setImpersonationContext(account_name);
|
||||
_inf("[steal_token] Contexto de impersonación actualizado: %s", account_name);
|
||||
|
||||
PPaquete respuestaFinal = nuevoPaquete(POST_RESPONSE, TRUE);
|
||||
addString(respuestaFinal, tareaUuid, FALSE);
|
||||
PPaquete salidaFinal = nuevoPaquete(0, FALSE);
|
||||
@@ -491,6 +496,9 @@ VOID RobarToken(PAnalizador argumentos) {
|
||||
snprintf(artifact_msg, sizeof(artifact_msg), "Process Token Impersonation: PID %lu -> User: %s", pid, account_name);
|
||||
addArtifact(respuestaFinal, "Token Impersonation", artifact_msg);
|
||||
|
||||
// Add callback context with impersonation_context
|
||||
addCallbackContext(respuestaFinal, "impersonation_context", account_name);
|
||||
|
||||
// Add callback token registration
|
||||
addCallbackToken(respuestaFinal, "add", hostname, token_id);
|
||||
|
||||
@@ -767,6 +775,10 @@ VOID CrearToken(PAnalizador argumentos) {
|
||||
snprintf(account_name, sizeof(account_name), "%s\\%s", domain_safe, user_safe);
|
||||
}
|
||||
|
||||
// Update global impersonation context
|
||||
setImpersonationContext(account_name);
|
||||
_inf("[make_token] Contexto de impersonación actualizado: %s", account_name);
|
||||
|
||||
// Generate token_id (simple hash of user+domain)
|
||||
// Use existing domain_len and user_len variables (they were set by getString)
|
||||
// If needed, recalculate lengths using strlen for actual string length (not buffer length)
|
||||
@@ -791,6 +803,9 @@ VOID CrearToken(PAnalizador argumentos) {
|
||||
snprintf(artifact_msg, sizeof(artifact_msg), "New Logon: User: %s\\%s (LogonType: %lu)", domain_safe, user_safe, logon_type);
|
||||
addArtifact(respuestaFinal, "Token Creation", artifact_msg);
|
||||
|
||||
// Add callback context with impersonation_context
|
||||
addCallbackContext(respuestaFinal, "impersonation_context", account_name);
|
||||
|
||||
// Register token with callback
|
||||
addCallbackToken(respuestaFinal, "add", hostname, token_id);
|
||||
|
||||
@@ -816,11 +831,19 @@ VOID RevertirToken(PAnalizador argumentos) {
|
||||
CloseHandle(gImpersonationToken);
|
||||
gImpersonationToken = NULL;
|
||||
|
||||
// Clear global impersonation context
|
||||
clearImpersonationContext();
|
||||
_inf("[rev2self] Contexto de impersonación limpiado");
|
||||
|
||||
PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);
|
||||
addString(respuesta, tareaUuid, FALSE);
|
||||
PPaquete salida = nuevoPaquete(0, FALSE);
|
||||
PackageAddFormatPrintf(salida, FALSE, "[rev2self] Token revertido exitosamente\n");
|
||||
addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
|
||||
|
||||
// Add callback context with empty impersonation_context to clear it
|
||||
addCallbackContext(respuesta, "impersonation_context", "");
|
||||
|
||||
mandarPaquete(respuesta);
|
||||
liberarPaquete(salida);
|
||||
liberarPaquete(respuesta);
|
||||
|
||||
@@ -53,16 +53,55 @@ def checkIn(data):
|
||||
# Retrieve External IP
|
||||
externalIP, data = getBytesWithSize(data)
|
||||
|
||||
# Nota: ECC Public Key ya no se usa (cifrado AESPSK se inyecta en build time)
|
||||
# Leemos y descartamos la longitud (4 bytes) que siempre será 0
|
||||
# Parse callback context from remaining data if present
|
||||
# Format: [0xF0 marker][field_name_len:4][field_name][field_value_len:4][field_value]...
|
||||
print(f"[CHECKIN] Datos restantes después de parsear campos estándar: {len(data)} bytes")
|
||||
if len(data) > 0:
|
||||
print(f"[CHECKIN] Primeros bytes de datos restantes: {data[:min(20, len(data))].hex()}")
|
||||
|
||||
callback_context = {}
|
||||
while len(data) >= 5: # At least: marker (1) + name_len (4)
|
||||
if data[0] == 0xF0:
|
||||
# Callback context marker found
|
||||
offset = 1
|
||||
|
||||
# Extract field name
|
||||
if len(data) < offset + 4:
|
||||
break
|
||||
name_len = int.from_bytes(data[offset:offset+4], 'big')
|
||||
offset += 4
|
||||
|
||||
if name_len == 0 or len(data) < offset + name_len:
|
||||
break
|
||||
|
||||
try:
|
||||
key_len = int.from_bytes(data[:4], "big")
|
||||
data = data[4:]
|
||||
if key_len > 0:
|
||||
# Descarta los datos de la clave si existen (código legacy)
|
||||
data = data[key_len:]
|
||||
field_name = data[offset:offset+name_len].decode('cp850', errors='ignore')
|
||||
offset += name_len
|
||||
except:
|
||||
pass
|
||||
break
|
||||
|
||||
# Extract field value
|
||||
if len(data) < offset + 4:
|
||||
break
|
||||
value_len = int.from_bytes(data[offset:offset+4], 'big')
|
||||
offset += 4
|
||||
|
||||
field_value = ""
|
||||
if value_len > 0:
|
||||
if len(data) < offset + value_len:
|
||||
break
|
||||
try:
|
||||
field_value = data[offset:offset+value_len].decode('cp850', errors='ignore')
|
||||
offset += value_len
|
||||
except:
|
||||
field_value = ""
|
||||
|
||||
callback_context[field_name] = field_value
|
||||
print(f"[CHECKIN] Callback context: {field_name} = '{field_value}'")
|
||||
data = data[offset:]
|
||||
else:
|
||||
# No more callback context markers
|
||||
break
|
||||
|
||||
dataJson = {
|
||||
"action": "checkin",
|
||||
@@ -78,6 +117,20 @@ def checkIn(data):
|
||||
"externalIP": externalIP.decode('cp850'),
|
||||
}
|
||||
|
||||
# Add callback context if present
|
||||
# For checkin, callback context is added directly to the root message (not in a "callback" key)
|
||||
# This matches Mythic's documentation: callback fields can be at root level for checkin
|
||||
if callback_context:
|
||||
for key, value in callback_context.items():
|
||||
dataJson[key] = value
|
||||
print(f"[CHECKIN] Añadido campo '{key}' = '{value}' al JSON de checkin")
|
||||
print(f"[CHECKIN] Total callback context añadido: {len(callback_context)} campos: {list(callback_context.keys())}")
|
||||
else:
|
||||
print(f"[CHECKIN] No se encontró callback context en el mensaje de checkin")
|
||||
|
||||
# Debug: Print final JSON structure
|
||||
print(f"[CHECKIN] JSON final tiene {len(dataJson)} campos: {list(dataJson.keys())}")
|
||||
|
||||
return dataJson
|
||||
|
||||
|
||||
@@ -401,7 +454,7 @@ def postResponse(data):
|
||||
# Verify that remaining_data starts with a marker if we expect markers
|
||||
# This helps detect protocol issues
|
||||
if len(remaining_data) > 0:
|
||||
KNOWN_MARKERS = {0xFF, 0xFE, 0xFD, 0xFC, 0xF9, 0xF8, 0xF7, 0xF6, 0xF4, 0xF3}
|
||||
KNOWN_MARKERS = {0xFF, 0xFE, 0xFD, 0xFC, 0xF9, 0xF8, 0xF7, 0xF6, 0xF4, 0xF3, 0xF2, 0xF0}
|
||||
if remaining_data[0] not in KNOWN_MARKERS and len(remaining_data) > 0:
|
||||
# This might be part of the output that wasn't properly extracted
|
||||
# This could happen if output_len was incorrectly interpreted
|
||||
@@ -430,6 +483,7 @@ def postResponse(data):
|
||||
tokens_data = []
|
||||
callback_tokens_data = []
|
||||
keylogs_data = []
|
||||
callback_context_data = {} # Dictionary to store callback context fields (cwd, impersonation_context, etc.)
|
||||
|
||||
print(f"[PARSER] Iniciando parsing de marcadores. remaining_data length: {len(remaining_data)} bytes")
|
||||
if len(remaining_data) > 0:
|
||||
@@ -978,6 +1032,47 @@ def postResponse(data):
|
||||
"token_id": token_id
|
||||
})
|
||||
print(f"[CALLBACK_TOKEN] action='{action}', host='{host}', token_id={token_id}")
|
||||
remaining_data = remaining_data[offset:]
|
||||
elif remaining_data[0] == 0xF0:
|
||||
# Callback context marker found
|
||||
# Format: [0xF0 marker][field_name_len:4][field_name][field_value_len:4][field_value]
|
||||
offset = 1
|
||||
|
||||
# Extract field name
|
||||
if len(remaining_data) < offset + 4:
|
||||
break
|
||||
name_len = int.from_bytes(remaining_data[offset:offset+4], 'big')
|
||||
offset += 4
|
||||
|
||||
if name_len == 0 or len(remaining_data) < offset + name_len:
|
||||
break
|
||||
|
||||
try:
|
||||
field_name = remaining_data[offset:offset+name_len].decode('cp850', errors='ignore')
|
||||
offset += name_len
|
||||
except:
|
||||
break
|
||||
|
||||
# Extract field value
|
||||
if len(remaining_data) < offset + 4:
|
||||
break
|
||||
value_len = int.from_bytes(remaining_data[offset:offset+4], 'big')
|
||||
offset += 4
|
||||
|
||||
field_value = ""
|
||||
if value_len > 0:
|
||||
if len(remaining_data) < offset + value_len:
|
||||
break
|
||||
try:
|
||||
field_value = remaining_data[offset:offset+value_len].decode('cp850', errors='ignore')
|
||||
offset += value_len
|
||||
except:
|
||||
field_value = ""
|
||||
|
||||
# Store callback context field
|
||||
callback_context_data[field_name] = field_value
|
||||
print(f"[CALLBACK_CONTEXT] {field_name} = '{field_value}'")
|
||||
|
||||
remaining_data = remaining_data[offset:]
|
||||
else:
|
||||
# No more markers
|
||||
@@ -1027,6 +1122,11 @@ def postResponse(data):
|
||||
"completed": True,
|
||||
}
|
||||
|
||||
# Add callback context if present
|
||||
if callback_context_data:
|
||||
jsonTask["callback"] = callback_context_data
|
||||
print(f"[CALLBACK_CONTEXT] Añadido callback context con {len(callback_context_data)} campos: {list(callback_context_data.keys())}")
|
||||
|
||||
# Incluir user_output para que se vea en la consola, EXCEPTO cuando hay download chunks
|
||||
# (los chunks de download no necesitan user_output)
|
||||
# Always include user_output if there's output text (needed for tokens command to show in GUI)
|
||||
|
||||
Reference in New Issue
Block a user