oblique / elf-infector

ELF binary infector
Other
32 stars 19 forks source link

Infected binary either segfaults or loops indefinitely #1

Closed dipanjan closed 8 years ago

dipanjan commented 8 years ago

I am trying to inject a piece of shellcode inside a 32-bit LF executable. I am running Ubuntu 14.04 64 bit. Hence, all the commands I have run require explicit switches to produce 32 bit code.

C source of the host binary is given below

/* simple_if.c */

#include <stdio.h>
#include "s2e.h"

int main()
{
  int var_cond;
  printf("Enter 0/1: ");
  scanf("%d", &var_cond);
  if(!var_cond)
    printf("Entered value: 0\n");
  else
    printf("Entered value: 1\n");

  return 0;
}

First I tried with a "Hello World" ASM payload

; hello.asm

global _start

section .text

_start:
    jmp MESSAGE      ; 1) lets jump to MESSAGE

GOBACK:
    mov eax, 0x4
    mov ebx, 0x1
    pop ecx          ; 3) we are poping into `ecx`, now we have the
                     ; address of "Hello, World!\r\n" 
    mov edx, 0xF
    int 0x80
    jmp end          ; Injector will insert a jump to original entry point

    ;mov eax, 0x1    ; Intentionally not returning the control
    ;mov ebx, 0x0
    ;int 0x80

MESSAGE:
    call GOBACK       ; 2) we are going back, since we used `call`, that means
                      ; the return address, which is in this case the address 
                      ; of "Hello, World!\r\n", is pushed into the stack.
    db "Hello, World!", 0dh, 0ah

end:

Compiled both the host and the parasite and injected the shellcode at the end.

gcc -m32 simple_if.c -o simple_if
nasm -f elf hello.asm -o hello.o
ld -m elf_i386 hello.o -o hello

# Test the assembled code
./hello
Hello, World!
Segmentation fault (core dumped)
# Segfault is natural, we are not gracefully returning to OS

# Now extract the shellcode
for i in `objdump -d ./hello | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' `; do echo -n "\\\\x$i" ; done | paste -d '' -s | sed 's/^/"/' | sed 's/$/"/g'
"\xeb\x14\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\x59\xba\x0f\x00\x00\x00\xcd\x80\xeb\x14\xe8\xe7\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0d\x0a"

# Update parasite.h
make
./infector simple_if

Run the infected file
./simple_if
Hello, World!
Enter 0/1: 8
Entered value: 1
Segmentation fault (core dumped)

But, I expect the program should gracefully exit. Because, the infector inserts a jump to the OEP at the end of the shellcode. I tried the same with a different payload. But, in this case, the program runs in an infinite loop a never exits.

global     _start

section    .text

_start:
    mov    ecx,0x23f0
    mov    edx,0x804869c
    jmp    prog_name

stub:
    pop    eax
    push   ebx
    mov    ebx,edx
    ;db     0x0f,0x3f,0x00,0xaa,0x00,0x00,0x00,0x00,0x00,0x00
    pop    ebx
    xor    eax,eax
    jmp    jump_start

prog_name:
    call   stub
    db "simple_if",0x00,0x90

jump_start:

This is what the infected file produces.

./simple_if
Enter 0/1: 9
Entered value: 1
Enter 0/1: 7
Entered value: 1
Enter 0/1: 6
Entered value: 1
Enter 0/1: 5
Entered value: 1
Enter 0/1: ^C

With GDB, I can see the shellcode is executed first and then the program jumps to OEP. The crash occurs thereafter.

oblique commented 8 years ago

Probably it has to do with the registers. I don't save them before the parasite and restore them after its execution. You can workaround this if you push the registers before jmp MESSAGE and pop them in end:. I don't plan to fix it since this project is 5 years old.

If you want to infect an ELF you can use other projects, such as the-backdoor-factory.

dipanjan commented 8 years ago

Absolutely correct. A set of pusha and popa solved both the problems. In this connection, I'd like to know if there is any boilerplate/setup code gets executed before even control is transferred to EP (_start).

oblique commented 8 years ago

No it doesn't, the injector creates a new EP that has your parasite and a jump to the original EP.

dipanjan commented 8 years ago

Yes, that's what Silvio's algorithm says. But, what I am surprised at is, when the control entered to OEP in the unmodified program, it didn't assume any specific register state. The same program, when got a shellcode injected, how come started relying on register states? Otherwise, it's weird that pusha and popa solved the problem.

oblique commented 8 years ago

From the System V ABI - i386:

Only the registers listed below have specified values at process entry:

%ebp 
The content of this register is unspecified at process initialization time,
but the user code should mark the deepest stack frame by setting the frame
pointer to zero.

%esp 
The stack pointer holds the address of the byte with lowest address which
is part of the stack. It is guaranteed to be 16-byte aligned at process entry.

%edx
a function pointer that the application should register with atexit (BA_OS).

The edx is a function pointer that is used by atexit and because it gets destroyed the exit crashes. If edx is 0 then the function pointer is not called. So you can set edx to 0 or just restore its original value.

dipanjan commented 8 years ago

I can confirm that setting edx = 0 works! Many thanks for the accurate explanation. I am closing this issue.

oblique commented 8 years ago

No problem!