Artifacts created

This commit is contained in:
marcos.luna
2025-10-29 13:42:28 +01:00
parent aeb74259c7
commit df8e824f5f
7 changed files with 424 additions and 4 deletions
@@ -261,6 +261,9 @@ VOID CopiarFichero(PAnalizador argumentos) {
addString(respuestaTarea, tareaUuid, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
// Add File Write artifact for the destination file
addArtifact(respuestaTarea, "File Write", nuevoFichero);
mandarPaquete(respuestaTarea);
liberarPaquete(salida);
@@ -318,6 +321,9 @@ VOID CrearRuta(PAnalizador argumentos){
addString(respuestaTarea, tareaUuid, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
// Add File Write artifact for the created directory
addArtifact(respuestaTarea, "File Write", directorio);
mandarPaquete(respuestaTarea);
liberarPaquete(salida);
@@ -425,6 +431,9 @@ VOID EliminarRuta(PAnalizador argumentos){
addString(respuestaTarea, tareaUuid, FALSE);
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
// Add File Write artifact for the deleted file/directory
addArtifact(respuestaTarea, "File Write", ruta);
mandarPaquete(respuestaTarea);
liberarPaquete(salida);
@@ -29,7 +29,11 @@ BOOL ejecutarConsola(PAnalizador argumentos) {
addString(salida, ruta, FALSE);
}
// Add output to response
addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE);
// Add Process Create artifact using the helper function
addArtifact(respuestaTarea, "Process Create", comando);
_pclose(fp);
Analizador* respuestaAnalizador = mandarPaquete(respuestaTarea);
@@ -639,4 +639,25 @@ VOID paqueteCompletado(PCHAR taskUuid, PPaquete datosOpcionales) {
// Cleanup
liberarPaquete(respuesta);
if (resp) liberarAnalizador(resp);
}
// Helper function to add artifacts to response packages
// Format: [0xFF marker][artifact_type_len:4][artifact_type][artifact_value_len:4][artifact_value]
VOID addArtifact(PPaquete paquete, PCHAR artifact_type, PCHAR artifact_value) {
if (!paquete || !artifact_type || !artifact_value) {
return;
}
// Artifact marker
addByte(paquete, 0xFF);
// Artifact type length and value
SIZE_T type_len = strlen(artifact_type);
addInt32(paquete, (UINT32)type_len);
addString(paquete, artifact_type, FALSE);
// Artifact value length and value
SIZE_T value_len = strlen(artifact_value);
addInt32(paquete, (UINT32)value_len);
addString(paquete, artifact_value, FALSE);
}
@@ -31,4 +31,8 @@ BOOL addWString(PPaquete paquete, PWCHAR datos, BOOL tamanoCopia);
/* Formatted append helper */
BOOL PackageAddFormatPrintf(PPaquete package, BOOL copySize, char* fmt, ...);
/* Artifact helper: adds artifact marker and data to response package */
/* Format: [0xFF marker][artifact_type_len:4][artifact_type][artifact_value_len:4][artifact_value] */
VOID addArtifact(PPaquete paquete, PCHAR artifact_type, PCHAR artifact_value);
#endif
@@ -1,5 +1,6 @@
from mythic_container.MythicCommandBase import *
from mythic_container.MythicRPC import *
import json
class ShellArguments(TaskArguments):
@@ -40,5 +41,27 @@ class ShellCommand(CommandBase):
return task
async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
"""
Process response from agent and add artifacts for Process Create.
According to Mythic documentation:
https://docs.mythic-c2.net/customizing/hooking-features/artifacts
Artifacts should be added to the response JSON with an 'artifacts' array.
However, the current Mythic API doesn't allow commands to directly modify
the translator's JSON response. Instead, we could:
1. Use MythicRPC to create artifacts after the response (current limitation)
2. Modify the agent to send command context in responses
3. Have the translator detect shell commands automatically
For now, artifacts are prepared in the translator but need command context.
"""
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
# TODO: Add artifacts via MythicRPC or modify translator to detect shell commands
# command = task.args.get_arg("command")
# if command:
# # Could use MythicRPC CreateArtifact here if available
return resp
@@ -1,6 +1,7 @@
from translator.utils import *
import ipaddress
import json
import re
def checkIn(data):
@@ -86,6 +87,27 @@ def getTasking(data):
return dataJson
def create_artifact(base_artifact, artifact_value, needs_cleanup=False, resolved=False):
"""
Helper function to create an artifact entry according to Mythic documentation.
Args:
base_artifact: Type of artifact (e.g., "Process Create", "File Write")
artifact_value: The actual artifact (e.g., "cmd.exe /c whoami", "/path/to/file")
needs_cleanup: Whether this artifact needs cleanup later
resolved: Whether this artifact has been cleaned up
Returns:
Dictionary representing an artifact entry
"""
return {
"base_artifact": base_artifact,
"artifact": artifact_value,
"needs_cleanup": needs_cleanup,
"resolved": resolved
}
def parse_process_list(output_text):
"""
Parsea el texto tab-separated de procesos al formato JSON del Process Browser.
@@ -155,6 +177,57 @@ def postResponse(data):
output, data = getBytesWithSize(data)
print(f"Tamaño después de extraer UUID: {len(data)} bytes")
# Parse artifacts: format [0xFF][type_len:4][type][value_len:4][value]
artifacts_data = []
remaining_data = data
while len(remaining_data) >= 5: # At least: marker (1) + type_len (4)
if remaining_data[0] == 0xFF:
# Artifact marker found
offset = 1
# Extract artifact type
if len(remaining_data) < offset + 4:
break
type_len = int.from_bytes(remaining_data[offset:offset+4], 'big')
offset += 4
if type_len == 0 or len(remaining_data) < offset + type_len:
break
try:
artifact_type = remaining_data[offset:offset+type_len].decode('cp850')
offset += type_len
except:
break
# Extract artifact value
if len(remaining_data) < offset + 4:
break
value_len = int.from_bytes(remaining_data[offset:offset+4], 'big')
offset += 4
if value_len == 0 or len(remaining_data) < offset + value_len:
break
try:
artifact_value = remaining_data[offset:offset+value_len].decode('cp850')
offset += value_len
artifacts_data.append({
"type": artifact_type,
"value": artifact_value
})
print(f"[ARTIFACTS] Detectado artifact: {artifact_type} = {artifact_value[:50]}")
# Continue parsing for multiple artifacts
remaining_data = remaining_data[offset:]
except:
break
else:
# No more artifacts
break
try:
decoded_output = output.decode('cp850')
@@ -207,6 +280,23 @@ def postResponse(data):
if is_process_list and len(processes) > 0:
jsonTask["processes"] = processes
print(f"[PROCESS_BROWSER] Respuesta convertida al formato Process Browser con {len(processes)} procesos")
# Support for artifacts: create artifacts from parsed data
artifacts = []
# Process all detected artifacts
for artifact_info in artifacts_data:
artifacts.append(create_artifact(
artifact_info["type"],
artifact_info["value"],
needs_cleanup=False, # Can be made configurable per artifact type
resolved=False
))
# Add artifacts array if we have any
if len(artifacts) > 0:
jsonTask["artifacts"] = artifacts
print(f"[ARTIFACTS] Total de artifacts agregados: {len(artifacts)}")
resTaks.append(jsonTask)