在一次针对某系列教育管理平台的渗透测试中,我们获得了一份有效的“弱口令通杀”列表。然而,登录过程并非一帆风顺,这也引出了一系列连锁反应式的漏洞发现,涉及接口未授权、JWT鉴权绕过、SQL注入以及水平与跨平台越权。
登录按钮“失灵”与初步信息收集
尝试使用弱口令登录时,我们发现点击登录按钮后页面没有跳转至后台。通过浏览器开发者工具的网络面板观察,请求实际上被正常发出,并收到了包含JWT令牌的响应,只是前端未能正确处理这个成功的响应来进行页面重定向。
一个典型的登录请求失败界面如下:左侧是登录表单,包含预填的用户名“admin”、密码输入框以及“管理员”、“班主任”等角色选择按钮;右侧开发者工具控制台显示,向 /auth/login 端点发送的POST请求返回了500状态码,但响应体中携带了JWT令牌。
对返回的JWT令牌进行初步分析,使用在线解码工具查看其结构。该JWT的Payload部分通常包含以下字段:
iss: 签发者信息,有时包含User-Agent。
ita: 令牌签发时间戳。
exp: 令牌过期时间戳。
data: 一个嵌套对象,其中包含uid(用户ID)和email(用户邮箱)等关键身份信息。
尝试对JWT密钥进行爆破,但未能成功,意味着我们无法伪造有效的签名。
利用JWT绕过接口鉴权
既然登录后能拿到JWT,尽管前端交互有问题,但后端可能认可这个令牌。我们开始尝试收集系统可能的API接口路径,例如:
/api/admin/user/myInfo
/api/admin/user/xxxxxList?page=1
使用工具(如Yakit)对这些接口进行Fuzz测试。在不携带任何认证信息时发起请求,大多数接口返回状态码500,并提示“未登录,请重新登录”。但其中一个接口直接返回了大量敏感数据,存在明显的未授权访问漏洞。
在请求头中正确添加JWT令牌后(格式为 Authorization: Bearer eyJ...),情况发生了变化。通过审计前端JavaScript代码发现,系统会将获取到的JWT令牌拼接上“Bearer ”前缀后存入本地存储或用于请求。模仿这一行为后,多个原本返回“未登录”的接口开始返回状态码200和大量数据,例如包含学生或教师详细信息的列表,从而实现了通过无效JWT绕过鉴权的信息泄露。
深入挖掘:SQL注入漏洞
在成功登录到另一个功能正常的同类型平台后台后,我们开始对查询功能进行测试。在“年级选择”和“班级选择”等参数处,发现了明显的SQL注入点。
-
初步探测:在参数后添加单引号,系统返回了详细的数据库报错信息,确认存在注入,且闭合方式涉及括号。
[10501] PDOException in SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ) LIMIT '1' at line 1
-
闭合确认与时间盲注:为避免sleep()函数可能导致数据库服务过载(曾因此触发502错误),我们使用BENCHMARK函数进行时间盲注来确认闭合方式。
构造Payload如下,通过调整BENCHMARK的第一个参数(重复计算次数)来观察响应延迟:
390632)/*8a792*/AND/*9b883*/(SELECT/*7c774*/5777/*6d665*/FROM/*5e556*/(SELECT(BENCHMARK(9000000,MD5(1))))Jedm)/*4f447*/AND/*3g338*/(3145=3145
当次数为400万时,响应延迟约2.5秒;次数为900万时,延迟约5秒,成功证明漏洞存在。
-
报错注入利用:确定闭合方式后,使用updatexml函数进行报错注入,成功获取数据库名等信息。
最终Payload示例:
419740)/*KV1QiLCJh*/and/*cyLCJleHA*/updatexml(1,concat(0x7e,(select/*GFPi1TdejA*/database()),0x7e),1)--+
服务器返回错误:XPATH syntax error: ‘~database_name~‘。
水平越权漏洞
系统存在管理员、教师、学生等多种身份。我们以两个教师账号(账号A和账号B)进行测试。
- 登录账号A,进入修改密码功能并抓包。发现请求通过
id参数来指定要修改密码的用户(例如id=285)。
- 登录账号B,同样进入修改密码功能抓包,其
id参数为278。
- 在账号B的会话中,将修改密码请求中的
id参数值替换为账号A的ID(285),提交请求。
- 操作成功后,账号A的原密码失效,必须使用账号B在请求中设置的新密码才能登录,从而实现了账号间的水平越权。
跨平台越权攻击
在测试中,我们遇到一些站点,弱口令已失效,但分析之前获取的JWT结构时发现,其Payload中的data字段包含了用户ID(uid)和邮箱(email)。我们推测,在管理员修改密码功能中,后端可能只验证JWT中携带的用户身份信息,而未严格绑定到当前会话或域名。
- 选取一个仍能用弱口令登录的站点(假设为
目标A:8087)。
- 登录
目标A的管理员账户(通常uid=1),进入修改密码功能并抓包。
- 在抓到的修改密码请求中,将
Host头或其他标识服务器地址的部分,直接修改为另一个无法登录的站点地址(目标B:8092),但保留请求体中的用户ID等参数以及当前有效的JWT令牌。
- 发送这个被篡改的请求。
- 结果:
目标B站点的管理员密码被成功修改为我们指定的新密码,实现了跨不同学校/平台实例的越权攻击。
总结与思考
本次测试案例揭示了多个常见但危害严重的安全问题:
- 前端与后端安全脱节:前端登录逻辑故障(按钮“坏掉”)反而暴露了后端接口在无有效会话状态、仅凭请求头中JWT进行鉴权时的设计缺陷。即使JWT密钥无法破解,其本身作为身份凭证被直接使用,也可能导致未授权访问。
- SQL注入的持久性:在存在大量参数过滤的系统中,往往因开发人员疏忽导致个别接口防护缺失。坚持不懈地对所有输入点进行测试至关重要。
- 越权漏洞的多样性:水平越权(同角色用户间)和跨业务实例的越权都源于对请求参数中用户标识符的校验不严。在测试多租户、多平台系统时,应特别关注身份标识的传递与验证逻辑。
- 工具使用的注意事项:在进行SQL注入时间盲注测试时,谨慎使用
sleep()函数,避免因未限制执行次数而导致目标数据库服务拒绝服务。可以考虑使用BENCHMARK等函数作为替代。
渗透测试的本质是对系统逻辑的深度理解与验证。细心观察每一个数据包,大胆假设可能存在缺陷的逻辑环节,往往能在看似“无懈可击”或“功能异常”的地方找到突破口。
参考资料
[1] 一次有趣的通杀, 微信公众号:mp.weixin.qq.com/s/Ege9C3yxPzLoewQmr4p4UA
版权声明:本文由 云栈社区 整理发布,版权归原作者所有。
|