Return to Library

공격 기법과 보호 기법 정리

  • Return Address Overwrite: 반환 주소를 악성 함수의 주소로 덮어서 셸 획득

  • Stack Canary: 스택 프레임의 반환 주소 전에 랜덤한 카나리를 주입하여 반환 주소를 덮기 어렵게 함

  • Return to Shellcode: 카나리를 우회하고, 셸 코드를 주입한 버퍼의 주소로 반환 주소를 덮어서 셸 획득

  • ASLR: 임의 버퍼의 주소를 알기 어렵게 함

  • NX: 각 세그먼트에 불필요한 실행권한을 제거함으로써 공격자가 임의 버퍼에 주입한 코드를 실행하기 어렵게함

Return to Library

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

  • Return to Libc : 라이브러리에 구현되어 있는 함수들로 NX를 우회하고 쉘을 획득하는 공격 기법

분석

$ checksec rtl #최신 리눅스 커널에서 ASLR은 기본으로 적용되며 특별이 언급되지 않는다면 ASLR은 적용된 것
[*] '/home/dreamhack/rtl'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    
    ---------------------------
    
    // 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"; #ASLR이 적용돼도 PIE가 적용되지 않으면 코드 세그먼트와 데이터 세그먼트의 주소는 고정되므로 /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'"); 
  #plt에 system 추가, plt에 어떤 라이브러리 함수가 등록되어있다면, 그 함수의 PLT 엔트리를 실행함으로써 함수를 실행 가능 
  #ASLR이 걸려있어도 PIE가 적용되어있지 않다면 PLT의 주소는 고정되므로 이 방법으로 라이브러리 함수 실행 가능(Return to 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의 주소는 고정되어 있음

  • 버퍼오버플로우

익스플로잇

1. 카나리 우회

Exploit Tech: Return To Shellcode 에서와 마찬가지로, 첫 번째 입력에서 적절한 길이의 데이터를 입력하면 카나리를 구할 수 있습니다.( 0x38)

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

카나리를 구했으면, 이제 두 번째 입력으로 반환 주소를 덮을 수 있습니다. 그러나 NX로 인해 지난 강의에서와 같이 buf에 셸 코드를 주입하고 이를 실행할 수는 없습니다. 이 강의에서는 다른 방법으로 셸을 획득해야 합니다.

공격을 위해 알고 있는 정보를 정리해보면 다음과 같습니다.

  • “/bin/sh”의 주소를 안다.

  • system 함수의 PLT 주소를 안다. ==> system 함수를 호출할 수 있다.

Exploit Tech: Return Address Overwrite에서 system(“/bin/sh”)을 호출하면 셸을 획득할 수 있음을 배웠습니다. x86-64의 호출 규약에 따르면 이는 rdi=”/bin/sh” 주소인 상태에서 system 함수를 호출한 것과 같습니다.

이 예제에서는 “/bin/sh”의 주소를 알고, system 함수를 호출할 수 있으므로 “/bin/sh”의 주소를 rdi의 값으로 설정할 수 있다면 system(“/bin/sh”)를 실행할 수 있습니다. 이를 위해선 리턴 가젯을 활용해야 합니다.

리턴 가젯 ⛓

리턴 가젯(Return gadget)은 다음과 같이 ret 명령어로 끝나는 어셈블리 코드 조각을 의미합니다. pwntools 설치 시 함께 설치되는 ROPgadget 명령어를 사용해서 다음과 같이 가젯을 구할 수 있습니다.

이제까지의 강의에서는 어떤 함수의 주소 또는 셸 코드의 주소로 반환 주소를 덮어서 한 번에 셸을 획득했습니다. 그러나 NX로 인해 셸 코드를 실행할 수 없는 상황에서, 단 한 번의 함수 실행으로 셸을 획득하는 것은 일반적으로 불가능합니다.

리턴 가젯은 반환 주소를 덮는 공격의 유연성을 높여서 익스플로잇에 필요한 조건을 만족할 수 있도록 돕습니다. 예를 들어 이 예제에서는 rdi의 값을 “/bin/sh”의 주소로 설정하고, system 함수를 호출해야 합니다. 리턴 가젯을 사용하여 반환 주소와 이후의 버퍼를 다음과 같이 덮으면, pop rdirdi를 “/bin/sh”의 주소로 설정하고, 이어지는 retsystem 함수를 호출할 수 있습니다.

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

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

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

대부분의 함수는 ret로 종료되므로, 함수들도 리턴 가젯으로 사용될 수 있습니다. 리턴 가젯을 사용한 더욱 복잡한 공격은 다음 강의인 Exploit Tech: Return-Oriented Programming에서 소개하겠습니다.

카나리를 우회하고, system(“/bin/sh”)를 호출할 계획을 세웠으므로 이를 코드로 구현하겠습니다. 반드시 직접 구현해보면서 이해하기 바랍니다.

익스플로잇 구현

카나리 우회

리턴 가젯 찾기

익스플로잇

  • system 함수로 rip가 이동할 때 스택은 반드시 0x10단위로 정렬되어 있어야함 why? system 함수 내부에 있는 movaps 명령어때문 -> 이 명령어는 스택이 0x10단위로 정렬되어 있지 않으면 segmentation fault를 발생시킴

    • 만약 system 함수를 이용할 때 익스플로잇이 제대로 작성됬을때도 segmentation Fault가 발생한다면 system 함수의 가젯을 8바이트 뒤로 미루기(no-opgadget을 system 함수 전에 추가하여 미루기)

전체 익스플로잇

마치며

Last updated