Blog | G5 Cyber Security

ARM Linux BOF: Fixing Ret2Libc with system()

TL;DR

When exploiting a buffer overflow on ARM Linux using ret2libc and calling system(3), the parameter string passed to system() might be nullified by protections. This guide shows how to identify this issue and fix it by carefully crafting your payload to ensure the correct address is used.

Identifying the Problem

  1. Observe the Crash: After attempting a ret2libc exploit with system(), you likely see a crash. Debugging reveals that the pointer passed as an argument to system() is NULL (0x00000000).
  2. Check Protections: Use tools like checksec or pwntools's elf_info to identify stack canaries, ASLR status and other protections. While not directly causing this issue, these protections impact exploit development.
  3. Disassemble the Target: Examine the assembly code around the vulnerable buffer overflow. Look for how arguments are passed to functions (registers vs. stack). On ARM, arguments are typically passed in registers like r0, r1, r2, and r3.
  4. Confirm Parameter Nullification: Step through the execution with a debugger (GDB) to see exactly when and why the parameter string becomes NULL. This often happens if there’s an early return or unexpected control flow before the parameter is fully copied onto the stack.

Fixing the Exploit

The core issue is that the address of your shell command (e.g., /bin/sh) isn’t being correctly passed to system(). Here’s how to fix it:

  1. Find system() Address: Use tools like objdump or pwntools to locate the address of system() in libc.
    objdump -T /lib/arm-linux-gnueabihf/libc.so.6 | grep ' system$'
  2. Find a Gadget for Register Loading: You need an ARM gadget that allows you to load the address of your shell command string into r0 (the first argument register). Look for gadgets like:
    • pop {pc} – Simple return-to-address gadget.
    • Gadgets loading from stack or memory into registers.

    Use tools like ROPgadget to find suitable gadgets.

    ROPgadget --binary ./vulnerable_program | grep 'pop {pc}'
  3. Craft the Payload: Construct your payload carefully. The general structure will be:
    • Overflow buffer to overwrite return address.
    • Address of gadget that loads into r0.
    • Address of shell command string on stack.
    • Address of system() function.
  4. Stack Alignment: Ensure your shell command string is correctly aligned on the stack for the gadget to access it properly. This might involve padding the buffer before placing the address.
  5. Example Payload (Conceptual): Assuming system() is at 0x7674cdb8 and your shell command string is at 0xbffff7e0, and a pop {pc} gadget is at 0x12345678:
    payload = b'A'*offset + 
               p32(0x12345678) +  # Address of pop {pc} gadget
               p32(0xbffff7e0) +   # Address of /bin/sh string on stack
               p32(0x7674cdb8)     # Address of system() function

    Note: This is a simplified example. The exact payload will depend on the target architecture, protections, and available gadgets.

  6. Debugging: Use GDB to step through your exploit and verify that r0 contains the correct address of your shell command string before calling system().
    break *0x7674cdb8 # Break at system() function
    info registers      # Check r0 value

Additional Considerations

Exit mobile version