Return Oriented Programming(ROP) -x86
- ROP( Return-oriented programming )란? 공격자가 실행 공간 보호(NXbit) 및 코드 서명(Code signing)과 같은 보안 방어가있는 상태에서 코드를 실행할 수있게 해주는 기술이다.
- RTL + Gadgets
- 이 기법에서 공격자는 프로그램의 흐름을 변경하기 위해 Stack Overflow 취약성이 필요하고, "가젯(Gadgets)"이라고 하는 해당 프로그램이 사용하는 메모리에 이미 있는 기계 명령어가 필요하다.
Gadgets - POP; POP; POP; RET
ROP는 기본적으로 RTL 기법을 이용하며, 공격자는 RTL과 Gadgets을 이용해 공격에 필요한 코드를 프로그래밍 하는 것이다.
- 01.RTL(Return to Libc) - x86 페이지에서 system() 함수만 호출하였다.
- 하지만 프로그램 및 운영체제,등 다양한 상황에 따라 여러 개의 함수 호출이 필요할 수 있다.
여러 개의 함수를 호출하기 위해 사용되는 것이 Gadgets이며, 기본적으로 다음과 같은 Gadgets 이 사용된다.
- 호출 하는 함수의 인자가 3개 일 경우 : "pop; pop; pop; ret"
- 호출 하는 함수의 인자가 2개 일 경우 : "pop; pop; ret"
- 호출 하는 함수의 인자가 1개 일 경우 : "pop; ret"
- 호출 하는 함수의 인자가 없을 경우 : "ret"
해당 Gadgets들의 역할은 ESP 레지스터의 값을 증가시키는 것이다.
- RTL에 의해 호출되는 함수에 전달되는 인자 값이 저장된 영역을 지나 다음 함수가 호출될 수 있도록 하는 것.
다음과 같은 방법으로 여러 개의 함수를 연속해서 실행할 수 있다.
- RTL에서 호출할 함수(주소 값이 저장된)의 다음 영역은 해당 함수가 종료된 후 이동할 Return Address 영역이다.
- 해당 영역에 Gadgets의 주소를 저장함으로써 연속해서 다음 함수가 호출될 수 있다.
- 아래 예제는 read() 함수 호출 후 System() 함수를 호출하게 된다.
PLT & GOT
프로시저 링키지 테이블(PLT, Procedure linkage table)에는 동적 링커가 공유 라이브러리의 함수를 호출하기 위한 코드가 저장되어 있다.
- 해당 정보들은 ".plt" 섹션에 저장되어 있다.
- 전역 오프셋 테이블(GOT, Global offset table)에는 동적 링커에 의해 공유 라이브러리에서 호출할 함수의 주소가 저장된다.
- 이 정보들은 ".got.plt" 섹션에 저장된다.
- 이 섹션은 공격자들의 공격 대상이 되며, 주로 힙, ".bss" Exploit에 의해 포인터 값을 변조 한다.
- ROP에서는 해당 정보들을 유용하게 활용할 수 있다.
Overflow
다음과 같이 빌드한다.
lazenca0x0@ubuntu:~/Exploit/ROP$ gcc -m32 -fno-stack-protector -o rop rop.c
다음과 같이 Breakpoints를 설정한다.
- 0x0804843b : vuln 함수 코드 첫부분
- 0x0804844f : read() 함수 호출 전
다음과 같이 Overflow를 확인할 수 있다.
- Return address(0xffffd00c) - buf 변수의 시작 주소 (0xffffcfce) = 62
- 즉, 62개 이상의 문자를 입력함으로써 Return address 영역을 덮어 쓸 수 있다.
Exploit method
ROP 기법을 이용한 Exploit의 순서는 다음과 같다.
- read 함수를 이용해 "/bin/sh" 명령을 쓰기 가능한 메모리 영역에 저장
- write 함수를 이용해 read 함수의 .got 영역에 저장된 값을 출력
- read 함수를 이용해 read 함수의 .got 영역에 system 함수의 주소로 덮어씀
- read 함수 호출 - read .got 영역에 system 함수의 주소가 저장되어 있기 때문에 system 함수가 호출됨
이를 코드로 표현하면 다음과 같다.
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를 바탕으로 공격을 위해 알아내어야 할 정보는 다음과 같다.
- "/bin/sh"명령을 저장할 수 있는 쓰기 가능한 메모리 공간
- read(), write() 함수의 plt, got
- system() 함수의 주소
- pop,pop,pop,ret 가젯의 위치
이제 위에서 언급한 필요한 것들을 모두 구해보자!
Find a writable memory space
해당 바이너리의 0x0804a000 ~ 0x0804b000 영역에 쓰기권한이 부여되어 있다.
Find gadget
rp-lin-x86 -f ./rop -r 4 | grep "pop"
Find plt, got address - read, write
read_plt : 0x8058300
write_plt : 0x8048320
read_got : 0x804a00c
wirte_got : 0x804a014
Find the address of the system() function
read_system_offset = 0x9ae50
Exploit
위의 논리대로 payload를 작성해보았다!
성공적으로 익스한 모습을 볼 수 있다.
참고
www.lazenca.net/display/TEC/01.ROP%28Return+Oriented+Programming%29-x86
'Hacking > Pwnable' 카테고리의 다른 글
[Pwnable] python으로 인자 및 stdin 전달 (0) | 2021.04.01 |
---|---|
[Pwnable] Lazenca - ROP(x64) 정리 (0) | 2021.03.30 |
[Pwnable] PLT, GOT (0) | 2021.03.21 |
[Pwnable] Lazenca - RTL(x64) 정리 (0) | 2021.03.21 |
[Pwnable] Lazenca - RTL(x86) 정리 (0) | 2021.03.20 |