Challenge
Complete a chain of small server-side validation steps.
完成一组逐步变化的服务端校验。
https://webhacking.kr/challenge/bonus-6/Analysis
每个阶段只在前一阶段条件成立时继续返回 next link。校验链如下:
- GET 参数
get=hehe。 - POST 参数
post=hehe、post2=hehe2。 myip等于服务端看到的客户端公网 IP。password等于当前 Unix 秒数的 MD5,需要在当前秒提交。- GET、POST 和 Cookie 三类输入同时非空。
- Cookie
test=md5(REMOTE_ADDR),POSTkk=md5(HTTP_USER_AGENT)。 - GET 参数名和值都使用去掉点号后的客户端公网 IP。
addr=127.0.0.1。ans=acegikmoqsuwy。- 33-10 根据页面 source 中的字符替换、截断和除法逻辑生成
answerip/27287674755_5457534951.php。
前六步及后续阶段依赖同一 session;IP、User-Agent 和当前秒数变化后,派生值也随之变化。
Solution
下面的完整脚本保留同一 requests.Session,按顺序发送前十步请求。CLIENT_IP 使用题目页面显示的当前客户端地址,session-cookie 从环境变量读取。每一步都检查响应中是否仍出现 Next;最终请求使用 answerip 路径。
#!/usr/bin/env python3import hashlibimport osimport time
import requests
BASE_URL = "https://webhacking.kr/challenge/bonus-6/"ANSWER_PATH = "answerip/27287674755_5457534951.php"
def require_next(response: requests.Response, stage: str) -> None: """Stop when a stage does not expose the next-stage response.""" response.raise_for_status() if "Next" not in response.text: raise RuntimeError(f"stage {stage} did not return Next") print(f"stage {stage}: {response.status_code} {len(response.content)} bytes")
def md5_text(value: str) -> str: return hashlib.md5(value.encode("utf-8")).hexdigest()
57 collapsed lines
def main() -> None: client_ip = os.environ.get("CLIENT_IP") if not client_ip: raise SystemExit("set CLIENT_IP to the address shown by the challenge")
session = requests.Session() user_agent = session.headers["User-Agent"]
require_next(session.get(BASE_URL, params={"get": "hehe"}, timeout=20), "33-1") require_next( session.post( BASE_URL, data={"post": "hehe", "post2": "hehe2"}, timeout=20, ), "33-2", ) require_next(session.get(BASE_URL, params={"myip": client_ip}, timeout=20), "33-3")
current_second = str(int(time.time())) require_next( session.get( BASE_URL, params={"password": md5_text(current_second)}, timeout=20, ), "33-4", )
session.cookies.set("imcookie", "1", domain="webhacking.kr", path="/") require_next(session.get(BASE_URL, params={"imget": "1"}, timeout=20), "33-5-get") require_next(session.post(BASE_URL, data={"impost": "1"}, timeout=20), "33-5-post")
session.cookies.set("test", md5_text(client_ip), domain="webhacking.kr", path="/") require_next( session.post( BASE_URL, data={"kk": md5_text(user_agent)}, timeout=20, ), "33-6", )
ip_without_dots = client_ip.replace(".", "") require_next( session.get( BASE_URL, params={ip_without_dots: ip_without_dots}, timeout=20, ), "33-7", ) require_next(session.get(BASE_URL, params={"addr": "127.0.0.1"}, timeout=20), "33-8") require_next( session.get(BASE_URL, params={"ans": "acegikmoqsuwy"}, timeout=20), "33-9", )
answer_url = BASE_URL + ANSWER_PATH response = session.get(answer_url, timeout=20) response.raise_for_status() print(response.text)
if __name__ == "__main__": main()对应的关键请求形态为:
GET /challenge/bonus-6/?get=hehePOST /challenge/bonus-6/ post=hehe&post2=hehe2GET /challenge/bonus-6/?myip=<client-ip>GET /challenge/bonus-6/?password=md5(<current-unix-second>)GET /challenge/bonus-6/?imget=1POST /challenge/bonus-6/ impost=1Cookie: imcookie=1POST /challenge/bonus-6/ kk=md5(<current-user-agent>)Cookie: test=md5(<client-ip>)GET /challenge/bonus-6/?<ip-without-dots>=<ip-without-dots>GET /challenge/bonus-6/?addr=127.0.0.1GET /challenge/bonus-6/?ans=acegikmoqsuwyGET /challenge/bonus-6/answerip/27287674755_5457534951.php同一 session 需要按顺序完成 33-1 到 33-10,最终请求进入题目完成分支。