Skip to main content

Command Palette

Search for a command to run...

nx&aslr

Updated
4 min read

NX, ASLR

스택 버퍼 오버플로우로 공격하는 것을 방지하기 위해 이전에 스택 카나리라는 방법을 소개했다. 그러나 카나리 값을 실행 중에 가져오면 검증 과정을 속일 수 있기 때문에 여전히 스택 버퍼 오버플로우로 공격할 수 있다.

NX, ASLR은 카나리처럼 익스플로잇 방지 기법인데, 이는 스택 버퍼 오버플로우 공격을 할 때 RET까지 데이터를 덮어씌우는 과정에서 공격을 막는다고 할 수 있다.

공격 과정을 보면:

  • 반환 주소를 임의 주소로 덮기 -> 카나리

  • 스택 프레임 내 구성요소 주소를 알아내고 -> ASLR

  • 버퍼에 쉘코드 삽입하여 실행하게 하기 -> NX

의 과정이 이루어지는데 반환 주소를 임의 주소로 덮는 것은 카나리 검증을 통해 어렵게 할 수 있었다. 하지만 가능했다 ASLR은 스택 프레임 구성요소의 주소를 알아내는 것을 막고 NX는 버퍼에 쉘코드를 삽입하고 실행하는 것을 막기 위한 방법이다.


NX

NXNon-eXecutable 의 약자이다. 이름처럼 실행을 방지하는 친구인데 실행에 사용되는 메모리 영역과 쓰기에 사용되는 메모리 영역을 분리하는 역할을 한다. 실행쓰기 권한이 분리되지 않으면 시스템이 취약해질 수 있는데, 코드 영역에 쓰기 권한이 있으면 코드를 임의 수정해서 실행되게 할 수 있고 스택과 데이터 영역에 실행 권한이 있으면 같은 공간에 쉘코드를 집어넣고 실행하게 할 수 있다.

현대 CPU는 거의 모두가 NX를 지원하고 컴파일러도 기본적으로 NX를 적용한다. 참고로 윈도우에서는 NX를 DEP라고 한다. NX가 적용된 바이너리에는 코드 영역 외에 실행 권한이 없다. 그러나 NX가 적용되지 않으면 스택 영역에 실행 권한이 존재할 수 있다. 이는 gdbvmmap으로 확인할 수 있다.

권한 정보

잠깐 권한의 종류를 정리하자면

  • r(읽기)

  • w(쓰기)

  • x(실행) 이다.

예시로 drwxr-xr-x 이면 디렉토리(d)에 대해:

  • 소유자(첫번째: rwx): r(읽기), w(쓰기), x(실행)이 가능

  • 소유 그룹(두번째: r-x): r(읽기), x(실행) 가능

  • 모든 사용자(세번쨰: r-x): r(읽기), x(실행) 가능

그러니까 NX가 적용되지 않은 바이너리에는 스택 영역에 rwx 권한이 있을 수 있는 것.

vmmap, checksec

  • NX Disabled
0x7ffffffde000 0x7ffffffff000 rwxp 21000 0 [stack]
  • NX Enabled
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]

이전의 ssh_001 문제의 실행파일은

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
     Start        End Perm     Size Offset File
 0x8048000  0x8049000 r-xp     1000      0 /home/max/hacking/ssp_001
 0x8049000  0x804a000 r--p     1000      0 /home/max/hacking/ssp_001
 0x804a000  0x804b000 rw-p     1000   1000 /home/max/hacking/ssp_001
0xf7d66000 0xf7d85000 r--p    1f000      0 /usr/lib32/libc.so.6
0xf7d85000 0xf7f18000 r-xp   193000  1f000 /usr/lib32/libc.so.6
0xf7f18000 0xf7f93000 r--p    7b000 1b2000 /usr/lib32/libc.so.6
0xf7f93000 0xf7f95000 r--p     2000 22c000 /usr/lib32/libc.so.6
0xf7f95000 0xf7f96000 rw-p     1000 22e000 /usr/lib32/libc.so.6
0xf7f96000 0xf7f9b000 rw-p     5000      0 [anon_f7f96]
0xf7fc0000 0xf7fc2000 rw-p     2000      0 [anon_f7fc0]
0xf7fc2000 0xf7fc6000 r--p     4000      0 [vvar]
0xf7fc6000 0xf7fc8000 r-xp     2000      0 [vdso]
0xf7fc8000 0xf7fc9000 r--p     1000      0 /usr/lib32/ld-linux.so.2
0xf7fc9000 0xf7fed000 r-xp    24000   1000 /usr/lib32/ld-linux.so.2
0xf7fed000 0xf7ffb000 r--p     e000  25000 /usr/lib32/ld-linux.so.2
0xf7ffb000 0xf7ffd000 r--p     2000  33000 /usr/lib32/ld-linux.so.2
0xf7ffd000 0xf7ffe000 rw-p     1000  35000 /usr/lib32/ld-linux.so.2
0xfffdc000 0xffffe000 rw-p    22000      0 [stack]

NX 적용 상태임을 스택 영역에서 볼 수 있다.

checksec으로도 NX 활성화 여부를 알 수 있다.

❯ checksec ssp_001
[*] '/home/max/hacking/ssp_001'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No

5.4.0 미만 버전 리눅스 커널의 NX

5.4.0 이전 버전에서는 스택 영역뿐 아니라 힙, 데이터 영역 등 읽기 권한이 있는 모든 페이지에 실행 권한을 부여한다. 이는 READ_IMPLIES_EXEC플래그 때문인데 NX 비활성화시 프로세스의 personality에 읽기 권한이 있는 모든 페이지에 실행 권한을 부여한다.

5.4.0 이후에는 플래그가 아니라 로더가 따로 스택 영역에만 실행 권한을 부여한다.

NX 적용 후 익스플로잇

NX 미적용 때 익스플로잇에 성공했어도 같은 코드를 NX활성화 옵션으로 컴파일하면 스택 영역에 실행 권한이 사라지면서 쉘코드가 실행되지 못하고 Segmentation fault를 띄우고 죽어버릴 수 있다.


ASLR

ASLR은 Address Space Layout Randomization의 약자이다. 아까 스택 프레임의 구성요소 주소를 알아내는 걸 방해한다고 했는데, 이름처럼 주소 공간을 프로그램 실행 때마다 변경하는 것이다.

예를 들어 사용자 입력을 받는 문자 배열이나 gets() 함수처럼 각종 메모리에 올려야 하는 부분이 실행 때마다 달라지는 것이다. 따라서 미리 주소를 구하는 게 불가능하다는 것

ASLR은 커널에서 지원하는 보호 기법으로 다음 명령어로 확인할 수 있다.

cat /proc/sys/kernel/randomize_va_space

리눅스에서 이 값은 0, 1, 2 중에 출력값을 가지는데 숫자가 올라갈수록 강력하다:

  • No ASLR(0): ASLR 적용X

  • Conservative Randomization(1): 스택, 라이브러리, vdso

  • Conservative Randomization + brk(2): (1)의 영역+brk로 할당한 영역

ASLR의 동작 과정

아래 코드를 통해 ASLR의 실제 동작 과정을 볼 수 있다.

// Name: addr.c
// Compile: gcc addr.c -o addr -ldl -no-pie -fno-PIE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
  char buf_stack[0x10];
  char *buf_heap = (char *)malloc(0x10);

  printf("buf_stack addr: %p\n", buf_stack); // 스택 버퍼
  printf("buf_heap addr: %p\n", buf_heap);   // 힙 버퍼
  printf("libc_base addr: %p\n",
         *(void **)dlopen("libc.so.6", RTLD_LAZY)); // 라이브러리 주소
  printf("printf addr: %p\n", dlsym(dlopen("libc.so.6", RTLD_LAZY),
                                    "printf")); // 라이브러리 함수의 주소
  printf("main addr: %p\n", main);              // 코드 영역의 함수 주소
}

컴파일 후 실행해보면

   ~/hacking ─────────────────────────────   max-env at   19:24:57
❯ ./addr
buf_stack addr: 0x7ffda7520db0
buf_heap addr: 0x364a92a0
libc_base addr: 0x7326741c1000
printf addr: 0x732674219a80
main addr: 0x401166

   ~/hacking ─────────────────────────────   max-env at   19:25:06
❯ ./addr
buf_stack addr: 0x7ffc89981080
buf_heap addr: 0x151172a0
libc_base addr: 0x767808625000
printf addr: 0x76780867da80
main addr: 0x401166

관찰해보면:

  • 코드 영역의 main 함수를 제외한 다른 영역의 주소들이 실행 시마다 변경된다.

  • libc_baseprintf의 하위 12비트 값은 변경되지 않는다.

    • 리눅스는 ASLR 적용 시 파일을 페이지 단위로 임의 주소에 매핑하는데 페이지 크기가 12비트라 그 이하로는 주소가 변경되지 않는다.
  • libc_baseprintf의 주소 차이는 항상 같다.

    • ASLR은 라이브러리를 임의 주소에 매핑되게 하지만 파일 그대로 매핑만 하는 것이라 매핑된 주소로부터 다른 심볼들까지의 거리는 항상 같다.

More from this blog

락프리 데이터 구조와 알고리즘

여기서는 락프리 데이터 구조를 설명한다. 락프리(lock-free) 란 배타락을 이용하지 않고 처리를 수행하는 데이터 구조 및 그에 대한 조작 알고리즘을 총칭한다. 왜 락프리인가? 전통적인 동시성 제어 방법인 뮤텍스나 세마포어는 여러 문제점을 가지고 있다: 성능 저하: 락 경합(lock contention)으로 인한 대기 시간 데드락: 여러 스레드가 서로의 락을 기다리는 상황 우선순위 역전: 낮은 우선순위 스레드가 높은 우선순위 스레드를 ...

Jul 27, 20257 min read126

소프트웨어 트랜잭셔널 메모리

소프트웨어 트랜잭셔널 메모리 동시성 프로그래밍에서 공유 자원에 대한 안전한 접근은 항상 중요한 과제다. 전통적으로 뮤텍스 락과 같은 비관적 락(Negative Lock) 방식을 사용해왔다. 이 방식은 크리티컬 섹션에 진입하기 전에 반드시 락을 획득해야 하며, 락을 얻지 못하면 코드 실행 자체가 블록된다. 하지만 이와는 다른 접근 방식이 있다. 바로 낙관적 락(Optimistic Lock) 방식인데, 이는 "일단 실행하고 나중에 검증하자"는 철학...

Jul 20, 202517 min read263

공평한 배타 제어

공평한 배타 제어 여기서는 공평한 배타 제어에 대해 설명한다. 먼저 컨텐션(contention) 이라는 개념을 이해할 필요가 있다. 컨텐션이란 여러 스레드가 동시에 같은 락을 획득하려고 경쟁하는 상황을 말한다. 컨텐션이 높을수록 스레드들이 락을 기다리는 시간이 길어지고 성능이 저하된다. 이러한 컨텐션 상황은 시스템 아키텍처에 따라 더욱 복잡해질 수 있다. 특히 비균일 메모리 접근(Non-Uniform Memory Access, NUMA) 와 같...

Jul 13, 20259 min read21

KernelSnitch[논문 리뷰]

Paper 1. Intro 이 글은 NDSS 2025에서 발표된 KernelSnitch 논문을 소개이다. 이 연구는 커널의 평범한 데이터 구조체들이 가진 본질적인 특성이 어떻게 심각한 보안 취약점이 되는지를 보여준다. 핵심은 이러하다: "데이터 구조체의 크기에 따른 접근 시간 차이를 이용해 커널의 비밀 정보를 유출할 수 있다" 여기서는 커널 힙 포인터 유출에 집중해서 설명한다. 이 공격이 성공하면 KASLR을 우회하고 더 심각한 커널 익스플로...

Jul 11, 20257 min read131

멀티태스크와 액터 모델

멀티태스크 협조적/비협조적 멀티태스크 선점: 프로세스와의 협조 없이 수행하는 컨택스트 스위칭이라고는 하나, 결국 뺏어오는 게 가능하냐의 문제다. 협조적 멀티태스크(비선점형, cooperative): 각각의 프로세스가 자발적으로 컨택스트 스위칭을 수행하는 멀티태스크 방식. 장점: 멀티태스크 매커니즘을 구현하기 쉽다. 단점: 프로세스가 자발적으로 컨텍스트 스위칭을 해야하는데, 만약 버그가 발생하여 프로세스가 무한 루프에 빠지거나 정지하게 되면 그 ...

Jul 6, 20252 min read25
M

MaxLog

35 posts