Contexto e Análise Inicial do BruteRatel C2 v1.7.3
Este documento apresenta uma análise técnica detalhada do mecanismo de licenciamento e da estrutura de parsing do arquivo xmodlib na versão 1.7.3 do BruteRatel C2 (brc4). É crucial destacar que a amostra vazada desta versão específica contém um arquivo xmodlib inconsistente. Embora os dados principais tenham sido submetidos à descriptografia, os offsets internos não correspondem à arquitetura esperada, o que inviabiliza completamente a execução das funcionalidades do agente Badger. A hipótese mais provável é que este arquivo tenha sido extraído ou modificado incorretamente a partir de uma versão crackeada superior.
Apesar da incapacidade de execução prática do payload vazado, a engenharia reversa de sua estrutura criptográfica é totlamente viável. O fluxo de validação e parsing do xmodlib nesta versão mantém paridade com a versão 1.4.4. Isso comprova que o arquivo pode ser forjado integralmente por um atacante, reforçando a necessidade de controles de mitigação robustos mesmo em cenários de comprometimento do binário.
Fluxo de Execução e Rotinas de Descriptografia
1. Extração da Chave de Licença (Função Principal 3)
A rotina identificada como main_main_func3 é responsável por invocar o decodificador de mensagens do Vortex. Utilizando o algoritmo AES no modo ECB com chave de 128 bits, o bloco license_encdata localizado no final do xmodlib é processado. O resultado desta operação é a separação dos dados no formato lic_key:base64(license_data).
2. Decodificação dos Metadados da Licença (Função Principal 4)
Posteriormente, a função main_main_func4 processa o license_data utilizando uma variante proprietária e modificada do AES. A descriptografia revela os metadados da licença estruturados da seguinte forma: data_registro:data_expiracao:usuario:email:observacao.
Exemplo de payload decodificado: 03-20-2023:01-01-3000:admin:admin@domain.com:Internal Test
3. Validação e Segmentação do Payload (Função Principal 6)
A função main_main_func6 orquestra a validação final e a preparação do payload:
- Deriva a chave de criptografia do
xmodlibutilizando os metadados da licença. - Descriptografa o blob de payloads utilizando AES-ECB-128 padrão.
- Valida o timestamp de expiração localizado no cabeçalho do
xmodlibdescriptografado. - Aplica uma operação XOR byte a byte em todo o payload utilizando a constante
0x493F1C27542B0D59. - Segmenta os dados do
xmodlibcom base em offsets fixos (hardcoded) para isolar os diferentes componentes.
Mapeamento das Rotinas Criptográficas:
main_EncryptVortexMsg => AES_ECB_128_ENC (Algoritmo Padrão)
main_DecryptVortexMsg => AES_ECB_128_DEC (Algoritmo Padrão)
main_encryptmsg => Custom_AES128_ECB_Encrypt (Variante Modificada)
main_decryptmsg => Custom_AES128_ECB_Decrypt (Variante Modificada)
Implementação em Python: Criptografia AES Customizada
Abaixo está a reimplementação das rotinas AES, incluindo o algoritmo padrão e a variante modificada utilizada pelo C2. A estrutura foi refatorada para melhor legibilidade, mantendo a equivalência matemática das operações de Galois Field.
import base64
from Crypto.Cipher import AES
# Matrizes e constantes do algoritmo
state_grid = [[0] * 4 for _ in range(4)]
expanded_key = [0] * 176
BLOCK_COLS = 4
KEY_WORDS = 4
KEY_BYTES = 16
NUM_ROUNDS = 10
S_BOX = [
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
]
INV_S_BOX = [
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
]
R_CON = [
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb
]
def expand_key_schedule(master_key):
temp_word = [0] * 4
for i in range(KEY_WORDS):
for j in range(4):
expanded_key[(i * 4) + j] = master_key[(i * 4) + j]
for i in range(KEY_WORDS, BLOCK_COLS * (NUM_ROUNDS + 1)):
for j in range(4):
temp_word[j] = expanded_key[(i - 1) * 4 + j]
if i % KEY_WORDS == 0:
temp_word = temp_word[1:] + [temp_word[0]]
temp_word = [S_BOX[b] for b in temp_word]
temp_word[0] ^= R_CON[i // KEY_WORDS]
elif KEY_WORDS > 6 and i % KEY_WORDS == 4:
temp_word = [S_BOX[b] for b in temp_word]
for j in range(4):
expanded_key[i * 4 + j] = expanded_key[(i - KEY_WORDS) * 4 + j] ^ temp_word[j]
def apply_round_key(round_idx):
for i in range(4):
for j in range(4):
state_grid[i][j] ^= expanded_key[round_idx * BLOCK_COLS * 4 + i * BLOCK_COLS + j]
def substitute_bytes():
for i in range(4):
for j in range(4):
state_grid[j][i] = S_BOX[state_grid[j][i]]
def shift_rows_left():
state_grid[0][1], state_grid[1][1], state_grid[2][1], state_grid[3][1] = state_grid[1][1], state_grid[2][1], state_grid[3][1], state_grid[0][1]
state_grid[0][2], state_grid[2][2] = state_grid[2][2], state_grid[0][2]
state_grid[1][2], state_grid[3][2] = state_grid[3][2], state_grid[1][2]
state_grid[0][3], state_grid[3][3], state_grid[2][3], state_grid[1][3] = state_grid[3][3], state_grid[2][3], state_grid[1][3], state_grid[0][3]
def galois_mult(x):
return (((x << 1) ^ (((x >> 7) & 1) * 0x1b)) % 256)
def mix_columns_op():
for i in range(4):
t = state_grid[i][0]
Tmp = state_grid[i][0] ^ state_grid[i][1] ^ state_grid[i][2] ^ state_grid[i][3]
Tm = state_grid[i][0] ^ state_grid[i][1]
Tm = galois_mult(Tm)
state_grid[i][0] ^= Tm ^ Tmp
Tm = state_grid[i][1] ^ state_grid[i][2]
Tm = galois_mult(Tm)
state_grid[i][1] ^= Tm ^ Tmp
Tm = state_grid[i][2] ^ state_grid[i][3]
Tm = galois_mult(Tm)
state_grid[i][2] ^= Tm ^ Tmp
Tm = state_grid[i][3] ^ t
Tm = galois_mult(Tm)
state_grid[i][3] ^= Tm ^ Tmp
def inv_mix_columns_op():
def gf_mul(a, b):
p = 0
for _ in range(8):
if b & 1: p ^= a
hi = a & 0x80
a = (a << 1) & 0xFF
if hi: a ^= 0x1b
b >>= 1
return p
for i in range(4):
a, b, c, d = state_grid[i]
state_grid[i][0] = gf_mul(a, 0x0e) ^ gf_mul(b, 0x0b) ^ gf_mul(c, 0x0d) ^ gf_mul(d, 0x09)
state_grid[i][1] = gf_mul(a, 0x09) ^ gf_mul(b, 0x0e) ^ gf_mul(c, 0x0b) ^ gf_mul(d, 0x0d)
state_grid[i][2] = gf_mul(a, 0x0d) ^ gf_mul(b, 0x09) ^ gf_mul(c, 0x0e) ^ gf_mul(d, 0x0b)
state_grid[i][3] = gf_mul(a, 0x0b) ^ gf_mul(b, 0x0d) ^ gf_mul(c, 0x09) ^ gf_mul(d, 0x0e)
def inv_substitute_bytes():
for i in range(4):
for j in range(4):
state_grid[j][i] = INV_S_BOX[state_grid[j][i]]
def inv_shift_rows_left():
state_grid[3][1], state_grid[2][1], state_grid[1][1], state_grid[0][1] = state_grid[2][1], state_grid[1][1], state_grid[0][1], state_grid[3][1]
state_grid[0][2], state_grid[2][2] = state_grid[2][2], state_grid[0][2]
state_grid[1][2], state_grid[3][2] = state_grid[3][2], state_grid[1][2]
state_grid[0][3], state_grid[1][3], state_grid[2][3], state_grid[3][3] = state_grid[1][3], state_grid[2][3], state_grid[3][3], state_grid[0][3]
def load_state(data_block):
for i in range(4):
for j in range(4):
state_grid[i][j] = data_block[4 * i + j]
def dump_state():
return bytes([state_grid[i][j] for i in range(4) for j in range(4)])
def standard_encrypt_block(block):
load_state(block)
apply_round_key(0)
for r in range(1, NUM_ROUNDS):
substitute_bytes()
shift_rows_left()
mix_columns_op()
apply_round_key(r)
substitute_bytes()
shift_rows_left()
apply_round_key(NUM_ROUNDS)
return dump_state()
def custom_encrypt_block(block):
load_state(block)
apply_round_key(0)
inv_mix_columns_op()
for r in range(1, NUM_ROUNDS):
substitute_bytes()
shift_rows_left()
mix_columns_op()
inv_shift_rows_left()
apply_round_key(r)
shift_rows_left()
substitute_bytes()
shift_rows_left()
apply_round_key(NUM_ROUNDS)
return dump_state()
def standard_decrypt_block(block):
load_state(block)
apply_round_key(NUM_ROUNDS)
for r in range(NUM_ROUNDS - 1, 0, -1):
inv_shift_rows_left()
inv_substitute_bytes()
apply_round_key(r)
inv_mix_columns_op()
inv_shift_rows_left()
inv_substitute_bytes()
apply_round_key(0)
return dump_state()
def custom_decrypt_block(block):
load_state(block)
apply_round_key(NUM_ROUNDS)
inv_shift_rows_left()
inv_substitute_bytes()
for r in range(NUM_ROUNDS - 1, 0, -1):
inv_shift_rows_left()
apply_round_key(r)
shift_rows_left()
inv_mix_columns_op()
inv_shift_rows_left()
inv_substitute_bytes()
mix_columns_op()
apply_round_key(0)
return dump_state()
def process_ecb_custom(data, key, mode='enc'):
if len(data) % 16 != 0 or len(key) != 16:
raise ValueError("Dados e chave devem ser múltiplos de 16 bytes.")
expand_key_schedule(key)
out = b''
func = custom_encrypt_block if mode == 'enc' else custom_decrypt_block
for i in range(0, len(data), 16):
out += func(data[i:i+16])
return out
def process_ecb_standard(data, key, mode='enc'):
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(data) if mode == 'enc' else cipher.decrypt(data)
Parser e Gerador do Arquivo xmodlib
O script a seguir lida com a serialização e desserialização dos timestamps no formato do Go, além de orquestrar a extração e a forja do arquivo xmodlib completo.
import struct
import os
from datetime import datetime, timedelta, timezone
import custom_aes_ecb as aes_mod
TIME_BIN_SZ = 0xF
PAYLOADS_SZ_173 = 0x3C0847
PAD_SZ_173 = 16 - ((TIME_BIN_SZ + PAYLOADS_SZ_173) % 16)
LIC_OFFSET_173 = TIME_BIN_SZ + PAYLOADS_SZ_173 + PAD_SZ_173
UNIX_TO_INTERNAL = (1969 * 365 + 1969 // 4 - 1969 // 100 + 1969 // 400) * 86400
def deserialize_go_time(data: bytes) -> str:
if data[0] != 1:
raise ValueError("Versão do timestamp não suportada.")
sec = struct.unpack(">Q", data[1:9])[0]
nsec = struct.unpack(">I", data[9:13])[0]
offset_h = struct.unpack(">h", data[13:15])[0]
loc = timezone.utc if offset_h == -1 else timezone(timedelta(hours=offset_h))
unix_sec = sec - UNIX_TO_INTERNAL
dt = datetime.fromtimestamp(unix_sec, loc)
return dt.strftime('%Y-%m-%d %H:%M:%S') + f'.{nsec:09d} {loc}'
def serialize_go_time(date_str: str, fmt='%Y-%m-%d %H:%M:%S') -> bytes:
dt = datetime.strptime(date_str, fmt).replace(tzinfo=timezone.utc)
sec = int(dt.timestamp()) + UNIX_TO_INTERNAL
return struct.pack('>BQIh', 1, sec, 0, -1)
def pad_to_block(data: bytes, block_size=16) -> bytes:
rem = len(data) % block_size
return data if rem == 0 else data + b'\0' * (block_size - rem)
def derive_payload_key(lic_key: bytes, lic_encinfo: bytes, email_key: bytes) -> bytes:
p1 = aes_mod.process_ecb_custom(pad_to_block(lic_key), email_key, 'enc')
p2 = aes_mod.process_ecb_custom(pad_to_block(lic_encinfo), email_key, 'enc')
# Ajuste de base64 customizado do BruteRatel
b64_p1 = base64.b64encode(p1).replace(b"+", b"-").replace(b"/", b":").replace(b"=", b"+")
b64_p2 = base64.b64encode(p2).replace(b"+", b"-").replace(b"/", b":").replace(b"=", b"+")
combined = b64_p1 + b"$" + b64_p2
return aes_mod.process_ecb_custom(pad_to_block(combined), email_key, 'enc')
def extract_and_decrypt_xmodlib(fpath: str, lic_offset: int, out_payload_path: str = ""):
with open(fpath, "rb") as f:
raw_data = f.read()
lic_enc = raw_data[lic_offset:]
lic_aeskey = bytes(aes_mod.INV_S_BOX[:16])
lic_data = aes_mod.process_ecb_standard(lic_enc, lic_aeskey, 'dec').rstrip(b"\x00")
lic_key, lic_encinfo = lic_data.split(b":")
lic_decinfo = aes_mod.process_ecb_custom(base64.b64decode(lic_encinfo), lic_key[:16], 'dec').rstrip(b"\x00")
info_parts = lic_decinfo.split(b":")
email_key = pad_to_block(info_parts[3])[:16]
payload_key = derive_payload_key(lic_key, lic_encinfo, email_key)
decrypted_blob = aes_mod.process_ecb_standard(raw_data[:lic_offset], payload_key[:16], 'dec')
time_bin = decrypted_blob[:TIME_BIN_SZ]
print(f"Timestamp Expiração: {deserialize_go_time(time_bin)}")
payloads = decrypted_blob[TIME_BIN_SZ:]
if out_payload_path:
with open(out_payload_path, "wb") as f:
f.write(payloads)
def forge_xmodlib_file(start_date: str, end_date: str, user: str, email: str, mark: str,
payload_path: str, out_xmodlib: str, master_key: bytes):
time_bin = serialize_go_time(end_date, '%m-%d-%Y')
lic_info = f"{start_date}:{end_date}:{user}:{email}:{mark}".encode()
enc_info = base64.b64encode(aes_mod.process_ecb_custom(pad_to_block(lic_info), master_key, 'enc'))
email_key = pad_to_block(email.encode())[:16]
payload_key = derive_payload_key(master_key, enc_info, email_key)
lic_data = master_key + b":" + enc_info
lic_enc = aes_mod.process_ecb_standard(pad_to_block(lic_data), bytes(aes_mod.INV_S_BOX[:16]), 'enc')
with open(payload_path, "rb") as f:
payloads = f.read()
blob = pad_to_block(time_bin + payloads[:PAYLOADS_SZ_173])
enc_blob = aes_mod.process_ecb_standard(blob, payload_key[:16], 'enc')
with open(out_xmodlib, "wb") as f:
f.write(enc_blob + lic_enc)
Modificação e Extração de Segmentos do Payload
O último componente do processo de análise envolve a mutação do blob de payloads. O script abaixo define o layout de memória para a versão 1.7.3, aplica a ofuscação XOR e permite a extração segmentada dos componentes (x86/x64).
import os
from payload_parser_v173 import forge_xmodlib_file, extract_and_decrypt_xmodlib, LIC_OFFSET_173
TARGET_VERSION = 173
XOR_CONSTANT = 0x493F1C27542B0D59
PAYLOAD_LAYOUT = {
"inject_http": {"x86": 0x38A00, "x64": 0x3BC00},
"inject_tcp": {"x86": 0x36A00, "x64": 0x39E00},
"inject_smb": {"x86": 0x36C00, "x64": 0x3A200},
"cryptvortex": {"x86": 0x6000, "x64": 0x7200},
"psreflect": {"x86": 0x7600, "x64": 0x8800},
"sharpreflect": {"x86": 0x2400, "x64": 0x2800},
"mimikatz": {"x86": 0x0FDE00, "x64": 0x131600},
"dllbase_o": {"x86": 0x1005, "x64": 0x19DD},
"svcbase_o": {"x86": 0x19A7, "x64": 0x27BE},
"stage_zero_rtl": {"x64": 0x2B90, "x86": 0x2320},
"stage_zero_wait": {"x64": 0x2BC0, "x86": 0x2370},
"stage_core_rtl": {"x64": 0x15A0, "x86": 0x1570},
"stage_core_wait": {"x64": 0x15C0, "x86": 0x15B0},
"stage_core_stealth_rtl": {"x64": 0x1E40},
"stage_core_stealth_wait": {"x64": 0x1E60}
}
TOTAL_PAYLOAD_SZ = sum(sum(archs.values()) for archs in PAYLOAD_LAYOUT.values())
def calculate_offsets():
offsets = {}
current_pos = 0
for comp, archs in PAYLOAD_LAYOUT.items():
offsets[comp] = {}
for arch, size in archs.items():
offsets[comp][arch] = current_pos
current_pos += size
return offsets
SEGMENT_OFFSETS = calculate_offsets()
def apply_xor_cipher(data: bytearray, key_int: int):
key_bytes = key_int.to_bytes(8, "little")
for i in range(len(data)):
data[i] ^= key_bytes[i % 8]
return data
def inject_markers(data: bytearray):
marker_idx = 0
for comp, archs in PAYLOAD_LAYOUT.items():
for arch, size in archs.items():
pos = SEGMENT_OFFSETS[comp][arch]
data[pos:pos+size] = marker_idx.to_bytes(1, 'little') * size
marker_idx += 1
def mutate_payload_blob(input_file: str = "", out_file: str = 'mutated_payloads.bin'):
if input_file and os.path.exists(input_file):
with open(input_file, "rb") as f:
blob = bytearray(f.read())
else:
blob = bytearray(TOTAL_PAYLOAD_SZ)
if input_file:
apply_xor_cipher(blob, XOR_CONSTANT)
inject_markers(blob)
if input_file:
apply_xor_cipher(blob, XOR_CONSTANT)
with open(out_file, 'wb') as f:
f.write(blob)
def dump_payload_segments(input_file: str = "mutated_payloads.bin", out_dir: str = 'extracted'):
with open(input_file, "rb") as f:
blob = bytearray(f.read())
apply_xor_cipher(blob, XOR_CONSTANT)
if not os.path.exists(out_dir):
os.makedirs(out_dir)
for comp, archs in PAYLOAD_LAYOUT.items():
for arch, size in archs.items():
pos = SEGMENT_OFFSETS[comp][arch]
out_path = os.path.join(out_dir, f"{comp}_{arch}.bin")
with open(out_path, 'wb') as f:
f.write(blob[pos:pos+size])