0

I made a program in C that injects some code into a ELF64 binary. I search for some \0 bytes, put the code there and modify the offsets so it loads correctly. When I run the newly created binary, it prints the message saying the code was injected successfully but the moment I jump to the entry point, I get a segfault.

The code I'm trying to inject is:

BITS 64

    mov rax, 10
    push rax
    mov rax, ' Message'
    push rax
    mov rax, 'Injected'
    push rax

    mov rax, 1      ;;  write(
    mov rdi, 1      ;;  STDOUT_FILENO,
    mov rsi, rsp    ;;  "Injected Message\n",
    mov rdx, 17     ;;  17
    syscall             ;;  );

    mov rax, 0x69696969
    jmp rax

0x69696969 Is just some default value I change later.

The code I wrote to inject into the binary is:

void    ft_inject_payload(t_data *data)
{
    unsigned char   *ptr;
    unsigned int    *start_address;
    unsigned int    increment;
    unsigned int    i;

    ptr = (unsigned char *)data->mmap_ptr;
    start_address = (unsigned int *)&g_payload[g_payload_size - 6];
    *start_address = data->header->e_entry;
    i = data->section_header[data->last_section].sh_offset
        + data->section_header[data->last_section].sh_size;
    increment = 0;
    while (i % 16 != 0)
    {
        i++;
        increment++;
    }
    memcpy(&ptr[i], g_payload, g_payload_size);
    data->header->e_entry = i;
    data->section_header[data->last_section].sh_size
        += g_payload_size + increment;
    data->program_header[data->text_segment].p_filesz
        += g_payload_size + increment;
    data->program_header[data->text_segment].p_memsz
        += g_payload_size + increment;
}

Here is the binary code of the payload:

unsigned char   g_payload[] = {
    0xB8, 0x0A, 0x00, 0x00, 0x00, 0x50, 0x48, 0xB8, 0x20, 0x4D, 0x65, 0x73,
    0x73, 0x61, 0x67, 0x65, 0x50, 0x48, 0xB8, 0x49, 0x6E, 0x6A, 0x65, 0x63,
    0x74, 0x65, 0x64, 0x50, 0xB8, 0x01, 0x00, 0x00, 0x00, 0xBF, 0x01, 0x00,
    0x00, 0x00, 0x48, 0x89, 0xE6, 0xBA, 0x11, 0x00, 0x00, 0x00, 0x0F, 0x05,
    0xB8, 0x69, 0x69, 0x69, 0x69, 0xFF, 0xE0
};

EDIT I modified the code of the payload to clean the stack and preserve rdx. And for now, I'm still having the same error

New code of the payload:

BITS 64
    push rdx
    ;; Load Message Into the stack
    mov rax, 10
    push rax
    mov rax, ' Message'
    push rax
    mov rax, 'Injected'
    push rax

    mov rax, 1      ;;  write(
    mov rdi, 1      ;;  STDOUT_FILENO,
    mov rsi, rsp    ;;  "Injected Message\n",
    mov rdx, 17     ;;  17
    syscall             ;;  );

    pop rax
    pop rax
    pop rax
    pop rdx
    mov rax, 0x69696969
    jmp rax
PacoFrost
  • 11
  • 4
  • You do not clean up the stack. You also misalign it. You do not preserve `rdx`. – Jester Aug 21 '23 at 12:41
  • In x86_64, the stack should remain 16-byte aligned when making a syscall. Most likely your string push's have misaligned the stack. See [Should %rsp be aligned to 16-byte boundary before calling a function in NASM?](https://stackoverflow.com/questions/62714764/should-rsp-be-aligned-to-16-byte-boundary-before-calling-a-function-in-nasm) – vengy Aug 21 '23 at 12:57
  • I don't think misaligned stacks mattern when doing syscalls. – fuz Aug 21 '23 at 13:29
  • 1
    No, the stack alignment matters after the jump to the original entry point which might expect it. But it also expects a particular stack layout so leaving stuff on the stack is not good, no matter the alignment. – Jester Aug 21 '23 at 13:39
  • I've cleaned up the stack (pop rax 3 times) and the problem remains – PacoFrost Aug 21 '23 at 13:39
  • Did you also preserve `rdx`? According to ABI docs that may contain a pointer. Anyway, use a debugger to pinpoint the crash. – Jester Aug 21 '23 at 13:40
  • Yes, still the same error – PacoFrost Aug 21 '23 at 14:03
  • What does the debugger say? – Jester Aug 21 '23 at 14:06
  • This: Process 85292 stopped * thread #1, name = 'test.new', stop reason = signal SIGSEGV: invalid address (fault address: 0x1050) frame #0: 0x0000000000001050 error: memory read failed for 0x1000. 0x1050 Is the address of the old entry point – PacoFrost Aug 21 '23 at 14:10
  • That is highly suspicious. That looks more like an offset than an actual load address. Are you sure you accounted for PIE and ASLR? Would be best if you used a relative jump instead of absolute. – Jester Aug 21 '23 at 14:19
  • 0x1050 Is the address that shows up when I do readelf -h and the one in e_entry. Also, what is PIE and ASLR – PacoFrost Aug 21 '23 at 14:23
  • 1
    If you see `Type: DYN (Shared object file)` then you have a Position Independent Executable (PIE). This is highly likely given your address. Address Space Layout Randomization (ASLR) will choose a base address during loading. As I said, you should use relative jump, that will work. Or, if it's a program you compiled for yourself, use `gcc -no-pie` – Jester Aug 21 '23 at 14:38
  • Ohh, ok, The type is DYN as you said. But how can I make this relative jump. – PacoFrost Aug 21 '23 at 14:44
  • Instead of `jmp rax` use `jmp 0x1050` but make sure you assemble for the correct location where you inserted the code. Alternatively use `db 0xe9, 0, 0, 0, 0` (which is a `jmp rel32`) and patch the offset using the formula `address_of_jmp + 5 - 0x1050`. – Jester Aug 21 '23 at 18:38

0 Answers0