某省会城市护网实战复盘(二)

本文转载自微信公众号「星宇Sec」

原文标题:某省会城市护网实战复盘(二)

内容版权归原作者及原公众号所有,转载仅为传播资讯;若有侵权请联系管理员删除。

一、护网背景

本次复盘来自某省省会城市政府护网实战。材料覆盖9个有报告的业务站点,共整理出26份漏洞报告,报告自评包含13个高危和13个中危问题。

这些站点覆盖教育、文旅、工会、妇幼、市场监管、文件流转、停车和媒体业务,后台连接微信登录、会员信息、活动报名、内容审核、文件附件、登录日志和业务表单。匿名接口、客户端会话和路径处理一旦缺少权限校验,就可能直接触达真实业务数据或后台功能。

验证时先确认站点用途、登录入口和业务对象,再比较正常请求、无认证请求和参数变形后的响应,最后用最小记录或合成账号确认读取、导出、写入和越权能力。涉及个人信息和业务数据的结果均只保留最小样本。

二、总体情况

归档的26份报告中,部分记录针对同一接口的不同测试结果,另有同一系统跨目录保存的材料。按业务入口和利用链归并后,可以整理为28个独立问题。

从风险类型看,主要有四类。第一类是Druid、Swagger和调试页等运维组件直接开放。第二类是业务接口只检查参数、Cookie或JWT是否存在,没有继续校验角色和数据范围。第三类是路径解析差异,通过分号参数绕过过滤器。第四类是客户端身份和页面输出处理不严,造成会话伪造、脚本执行或敏感信息泄露。

这些问题集中在少数站点,实际影响差异较大。部分目标已经确认个人信息批量读取和后台导出,部分完成了合成账号登录或受控写入,还有部分只证明接口可达。下面先按站点列出全部报告,再给出各问题的风险说明和测试过程。

三、发现成果

wXk.fXXXu.cn 教育管理平台

  • 未授权微信接口曾回显AppID、AppSecret、第三方请求URL和.NET异常堆栈。
  • 后续复测只返回通用授权失败,未记录密钥轮换和历史调用审计。

fXXXh.org.cn 工会业务平台

  • 工会树接口未授权返回22个下级工会。
  • 会员服务卡接口可匿名查询并导出会员身份证号、手机号和工会卡号。
  • 自注册低权限账号使用固定验证码创建,随后可读取15万会员分页数据。

fXg.cXXXd.net 文旅服务平台

  • tpl 参数可穿越模板目录,查询参数可造成反射型XSS。
  • 后台 sessionKey 存放于 localStorage ,同源脚本具备读取条件。
  • 报名记录和活动流水未授权返回姓名、手机号和报名信息。
  • 评论接口未授权返回评论内容,未审核评论可直接显示。
  • 短信接口存在手机号注册状态枚举,前端JS硬编码签名材料。

fXu.aXi.zXXXd.cn 监管业务平台

  • /<接口路径> 调试页泄露ThinkPHP源码和签名算法。
  • 短信验证码使用PHP宽松比较,布尔值 true 可绕过。
  • 合成手机号完成验证码登录、密码重置、新密码登录和旧密码失效验证。
  • 公开 appid 与时间戳MD5可伪造签名并触发短信发送接口。

nXc.hXh.cXn.fXXXt.net 妇幼健康业务系统

  • 保存可逆序列化会话,修改 user_id 可进入控制台。
  • 会话中的 org_id 直接进入SQL,造成时间盲注。

fXr.bXXXp.com 文件管理系统

  • Druid监控和Swagger接口在测试时可匿名访问。
  • 业务入口通过 ..;/ 路径跳过认证过滤器,读取文件详情和附件。

yXn.jXXXe.com.cn 停车管理平台

  • 在 /<接口路径>/** 路径后附加 ;download 可绕过登录令牌过滤器。
  • 登录日志接口返回1,816万条总量和真实记录。
  • 图片上传控制器可被同一方式匿名触达并写入对象存储。

wXw.fXXXd.gov.cn 政务网站

  • 图片验证码可自动识别,群众来信接口接受匿名提交。
  • 管理端生产脚本把留言内容直接送入 innerHTML ,存在存储型XSS条件。

wXw.iXXXb.com 媒体网站

  • 微信登录回调可使用外部 re_url 并透传授权码。
  • 举报表单可匿名写入,记录详情未校验表单和用户归属。
  • 历史数字报异步接口返回完整正文,可绕过付费限制。
  • 强制改密页存在反射型XSS,匿名改密接口可进入业务校验。

四、漏洞详情

wXk.fXXXu.cn 教育管理平台

资产概况

该站点为教育管理平台的互联网入口,域名为 wXk.fXXXu.cn 。系统前端承担微信登录、账号绑定和移动端业务接入,后端使用.NET应用处理微信用户信息、授权回调和业务账号数据。

未授权微信接口回显凭据

漏洞情况

/<接口路径> 在微信用户信息获取失败时,把服务端拼接的第三方请求URL完整写入响应,其中包含公众号AppID和AppSecret。另一个授权接口 /<接口路径> 在 code 参数异常时返回完整.NET异常堆栈和服务器绝对路径。两个接口均可匿名访问,错误处理没有区分前端提示信息和后端运行信息。

code 由客户端直接传入,接口在没有业务会话的情况下就调用微信OAuth接口。微信返回错误后,通用异常处理又把 第三方微信SDK 的请求地址、异常对象和调用堆栈序列化到JSON,导致凭据和代码路径同时出现在失败响应中。 <接口路径> 使用了相近的异常输出方式,因此两个入口都能为后续接口排查提供服务器目录、控制器名称和依赖组件信息。

危害

AppID和AppSecret属于微信公众号的核心调用凭据。持有这组信息的人可以获取公众号 access_token ,进而读取用户资料、发送模板或客服消息、操作菜单与素材。异常堆栈还会暴露项目目录、控制器文件和请求处理逻辑,方便攻击者继续查找其他接口。10月27日复核时敏感字段已不再返回,但历史凭据仍需按已泄露处理。

测试过程

1.先不携带Cookie和Token请求微信用户信息接口。响应未返回登录失败,继续进入微信用户信息获取逻辑,说明该入口至少允许匿名到达业务层,需要继续检查返回内容是否包含服务端运行信息。

GET /<接口路径>?code=x HTTP/1.1
Host:wXk.fXXXu.cn

2.当时的响应体返回微信 access_token 请求的完整URL,其中直接出现AppID与AppSecret。这个结果已经超出普通错误提示的范围,因此继续查看同一JSON中的其他字段,判断异常对象是否也被完整序列化。

3.响应同时带有 <微信授权错误响应> ,说明请求已经到达微信接口并完成凭据拼接。为了确认泄露不是某个错误码偶然触发,改用带目录穿越字符的 code 请求授权接口,观察另一条异常处理路径。

GET /<接口路径>?code=..%2F&state=STATE HTTP/1.1
Host:wXk.fXXXu.cn

4.授权接口返回.NET异常堆栈、服务器绝对路径以及 Url 、 StackTrace 、 Source 等运行字段。两个不同接口都能在匿名状态下回显内部信息,可以排除单一接口的定制提示,问题落在统一异常处理和微信调用封装上。

5.10月27日再次发送上述两条请求,响应只保留通用授权失败,敏感字段已经不再返回。为了排除只是字段改名,继续比较响应体长度和JSON键名,确认历史运行字段确实消失。

6.复核结果只能证明当前入口已经收口,不能证明10月22日返回的AppSecret没有被读取。由于公众号凭据一旦泄露就可以在服务端继续使用,后续处理仍需轮换密钥并检查该时间段的微信接口调用记录。

fXXXh.org.cn 工会业务平台

资产概况

该站点为工会业务平台,域名为 fXXXh.org.cn 。系统包含工会组织树、会员服务卡、会员管理、APP注册和后台账号体系,业务数据覆盖辖区基层工会及工会会员。

会员服务卡接口未授权查询与导出

漏洞情况

/<接口路径> 、 /<接口路径> 和 /<接口路径> 均未校验登录态与数据权限。工会树接口先返回各基层工会的 deptId ,会员列表和导出接口随后接受任意 deptId 查询会员姓名、完整身份证号、完整手机号和工会卡号。

同一网关下的其他业务接口在缺少Token时会返回401,这三个接口却直接进入查询和导出逻辑,说明问题集中在接口级鉴权遗漏。 deptId 没有和当前账号所属机构绑定,分页参数也没有限制上限,攻击者可以先枚举工会树,再按机构循环调用列表接口整理会员名册。导出接口进一步绕过了页面展示层的脱敏处理,部分记录还包含银行卡号等字段,实际影响高于单页查询。

危害

这三个接口组成了一条完整的匿名取数路径。攻击者可以先枚举22个下级工会,再逐项查询或下载会员信息。实测一个工会即可导出3741条记录,平台会员总量为150064。姓名、身份证号和手机号同时泄露,可被用于精准诈骗、身份冒用、撞库和线下骚扰,属于大规模公民个人信息泄露。

测试过程

1.先不登录请求工会树接口。响应直接返回组织名称和编号,没有出现Token缺失或权限不足提示,说明组织数据可能由公开控制器处理,但还需要确认这些编号能否继续用于会员业务接口。

GET /<接口路径>?deptId=&getChild=1 HTTP/1.1
Host:fXXXh.org.cn

2.响应返回22个下级工会及各自 deptId 。挑选其中一个编号请求会员列表,观察组织树接口和会员数据接口是否共用同一套匿名访问规则。

GET /<接口路径>?deptId=<工会ID>&page=1&limit=10 HTTP/1.1
Host:fXXXh.org.cn

3.会员列表没有跳转登录,直接返回姓名、完整身份证号、手机号和工会服务卡号。把 limit 保持为10是为了先确认字段和数据结构,此时继续请求导出接口,判断页面查询限制是否也会在文件导出中生效。

GET /<接口路径>?deptId=<工会ID>&exportMode=1 HTTP/1.1
Host:fXXXh.org.cn

4.服务端返回Excel文件,单个工会实际导出3,741条会员记录。列表和导出都能匿名读取,下一步需要找到同网关的正常受保护接口做对照,避免把整个网关都允许匿名访问误判为接口级问题。

5.选取同一 /<接口路径> 网关下的其他业务接口,不携带Token时返回401,而组织树、会员列表和导出仍返回正常数据。这个差异排除了网关整体匿名开放,也把范围缩小到三个具体接口的鉴权配置。

6.由于组织树已经提供了22个可枚举编号,后续风险不止是单页数据泄露。测试在保留一个工会的10条列表和一个导出文件后停止,避免扩大真实会员数据的影响。

低权限账号越权读取15万会员信息

漏洞情况

新注册的低权限账号可以携带普通JWT调用 /<接口路径> ,接口没有根据登录账号所属机构限制 deptId ,也没有校验账号是否具备会员管理权限。服务端直接按客户端传入的机构编号返回会员分页数据,响应同时包含掩码字段和未脱敏字段。

注册账号调用 /<接口路径> 、 /<接口路径> 和 /<接口路径> 时,返回结果分别为 memberId=null 、 isMember=false 和 adminFlag=null 。这些字段说明账号既不是会员也没有管理身份,但同一JWT仍可读取全量会员页。接口只完成了JWT有效性校验,机构范围、角色和数据字段权限没有进入业务查询条件。

危害

攻击者通过固定验证码问题取得低权限账号后,可以把权限提升为全平台会员数据读取能力。实测分页总量为150064,单条记录包含完整身份证号、手机号和机构信息。该问题使一个普通注册用户能够遍历全部会员数据,影响范围不限于本人所属工会。

测试过程

1.先利用APP注册接口取得一个低权限账号,再调用登录接口取得JWT。随后分别查询账号信息、会员身份和管理员身份,响应为 memberId=null 、 isMember=false 和 adminFlag=null ,确认测试账号没有业务身份,也没有会员管理权限。

2.携带这个普通JWT请求会员分页接口,只取一条记录,观察接口是否会把账号所属机构和角色带入查询条件。

GET /<接口路径>?page=1&limit=1&deptId= HTTP/1.1
Authorization:Bearer
Host:fXXXh.org.cn

3.响应返回 total=150064 ,单条记录同时包含掩码字段和完整身份证号、手机号字段,以及出生日期、户籍地、工作单位和所属工会。账号没有会员身份却能读取全量总数,说明接口可能只校验了JWT是否有效。

4.删除 Authorization 头重新发送同一请求,服务端返回401。这个对照证明接口确实要求登录,但登录后的角色和机构范围没有生效,因此需要继续确认 deptId 是否可以换成任意组织编号。

5.把 deptId 换成其他工会编号并用同一JWT重放,响应仍返回对应机构的会员数据,总数和字段没有变化。请求差异确认问题位于对象级授权,普通注册账号能够越过自身机构读取其他工会的数据。

6.为控制影响,测试只读取第一页的一条记录,没有利用15万总量循环翻页,也没有修改会员资料。现有结果确认低权限账号可以扩大为全平台会员读取权限。

APP注册固定验证码

漏洞情况

APP注册接口 /<接口路径> 只把 mobileCode 与固定字符串 <固定验证码> 进行比较,没有校验验证码是否由短信服务生成、是否属于当前手机号以及是否仍在有效期。发送验证码接口只承担前置调用,注册流程不依赖用户实际收到短信。

接口同时接收账号、手机号和口令,验证码校验只比较请求体中的一个字符串。服务端既没有把验证码与手机号写入同一缓存键,也没有消费记录和过期时间判断。攻击者只要提交固定值即可创建新账号,注册完成后马上登录取得JWT,账号还可以继续进入其他仅检查登录态的接口。

危害

攻击者可以批量注册账号并抢占他人手机号,也可以把注册得到的基础账号作为会员越权接口的入口。错误验证码 <固定验证码> 、字符串 true 和数字 0 均被拒绝,只有 <固定验证码> 能够完成注册,说明缺陷来自服务端固定值校验,猜测验证码的可能性可以排除。

测试过程

1.先选择未注册测试手机号请求发送验证码,确认接口能够正常调用,记录响应中的业务码和提示语。

POST /<接口路径>?mobile=<测试手机号> HTTP/1.1
Host:fXXXh.org.cn

2.注册时不使用收到的验证码,先把 mobileCode 固定为 <固定验证码> 。这个值来自对APP前端和测试响应的交叉比对,提交前同时保持账号、手机号和口令固定,方便后续只改变验证码做对照。

POST /<接口路径> HTTP/1.1
Host:fXXXh.org.cn
Content-Type:application/json

{"account":"<测试手机号>","mobileCode":"<固定验证码>","password":"测试口令"}

3.服务端返回注册成功,说明固定值可以替代真实短信验证码。为排除<固定验证码>只是测试号码的预设验证码,分别把 mobileCode 改为 <固定验证码> 、 true 和 0 重新提交,服务端均返回验证码错误。

4.保持其他字段不变,只把 mobileCode 改回 <固定验证码> ,响应长度缩短为注册成功提示。相同请求只有验证码值不同,说明服务端确实按固定字符串判断,而不是校验短信缓存中的真实值。

5.使用注册账号和设置的口令调用登录接口,响应返回有效JWT。为了确认账号不是前端假成功,继续用该JWT访问会员管理接口,业务请求能够正常进入并返回数据。

6.换用另一个未注册测试手机号重复注册和登录,同样取得可登录账号,排除单个号码被预先加入白名单。该结果也与后面的低权限越权问题连成一条完整路径,账号注册和会员数据读取不需要真实短信参与。

fXg.cXXXd.net 文旅服务平台

资产概况

该站点为文旅主管部门运营的文旅服务平台,域名为 fXg.cXXXd.net 。系统面向公众提供资讯、场馆预约、活动报名、培训报名、评论和后台内容管理,前台页面与后台管理接口同源。

模板路径穿越与查询参数XSS

漏洞情况

/<接口文件>.jspx 的 tpl 参数允许使用 ../ 选择模板目录外文件, q 参数又被当前模板直接输出到HTML正文和页面属性中,没有进行对应上下文的编码。管理端登录后把 sessionKey 写入 localStorage ,所有后台API请求都从该位置读取并携带这个值。

自定义模板接口 动作把 tpl 拼入 <接口路径>/{tpl}.html , 线索搜索接口 动作把参数拼入 <接口路径>/{tpl}.html ,模板路径由客户端直接控制。查询参数还会整体写入FreeMarker模型,模板中的标题、链接和属性没有统一转义。两个问题叠加后,匿名请求既可以跨目录渲染模板,也可以在页面中反射脚本。管理端令牌没有放在HttpOnlyCookie中,同源脚本读取 localStorage 就能获得后台API凭据。

危害

攻击者可以读取主题目录外的模板或页面文件,也可以构造带脚本的链接诱导用户访问。脚本在 fXg.cXXXd.net 源下执行后,可以读取同源 localStorage 中的 sessionKey ,再调用后台API或修改页面内容。若已登录管理员打开链接,攻击者能够取得管理端会话凭据并接管后台。

测试过程

1.先请求正常搜索模板,记录页面长度、标题和链接结构。页面能够正常渲染搜索结果,后续请求以这组响应作为基线,便于判断路径变化是否只影响模板选择。

GET /<接口文件>.jspx?tpl=<路径>&q=test HTTP/1.1
Host:fXg.cXXXd.net

2.把 tpl 改为跨目录路径,其余参数保持不变。响应仍返回HTTP200并且正文进入搜索模板,说明 ../ 没有在服务端被规范化,模板位置可以由请求参数改变。

GET /<接口文件>.jspx?tpl=<路径>&q=test HTTP/1.1
Host:fXg.cXXXd.net

3.路径穿越成立后,保持页面结构不变,把 q 替换成图片事件载荷,观察搜索词在HTML中的输出位置。这样可以把模板路径问题和参数编码问题分开确认。

GET /<接口文件>.jspx?tpl=<路径>&q=%3Cimg%20src=x%20onerror=alert(884)%3E HTTP/1.1
Host:fXg.cXXXd.net

4.响应HTML直接包含未转义的 ,浏览器加载后执行。为了确认不是某个标签被允许,继续把载荷换成 ,浏览器加载后执行。由于正文可能单独转义,继续改用属性逃逸载荷,检查 href 等属性的输出上下文。

3.属性逃逸载荷 "><<接口路径>(4242)> 也被原样输出并执行,说明问题不止一个文本节点。为确认请求方法不是影响因素,再用POST提交相同参数。

4.POST响应与GET一致,搜索词仍进入页面。随后对普通搜索接口提交同一载荷,响应会把 < 转义为 < 并截断,两个模板的处理差异由此得到确认。

5.最后在真实浏览器中加载测试URL,观察 alert 执行,并检查响应头中没有CSP。浏览器结果与HTTP响应一致,可以确认反射型XSS不是只靠字符串匹配得出的判断。

6.验证结束后关闭页面,没有读取真实用户存储,也没有把反射链接发送给其他用户。

前端硬编码签名密钥与AES密钥

漏洞情况

管理端打包脚本在未登录状态下可以直接下载,JS中硬编码了API签名参数 signKey 、AES密钥和encryptionIV。签名算法把请求参数按名称排序后拼接,再追加 signKey 计算MD5,服务端接受按该算法生成的签名。

签名材料被打入公开静态资源,任何访问者都能读取并离线复现。AES密钥还用于处理登录密码字段,客户端能够解密抓包中的 passwordCipher 。签名只能证明请求由掌握算法的一端生成,无法证明调用者已经登录。该问题与短信枚举、账号口令尝试和XSS会话读取组合后,会降低后台接口的防护强度。

危害

攻击者可以复现前端签名,为任意前台或后台API生成可用签名,也可以解密截获的 passwordCipher 。结合登录接口的手机号枚举和无频率限制,攻击者能够在线尝试账号口令。虽然该密钥单独不能绕过登录态,但会使签名机制失去防护作用,并为爆破、重放和会话利用提供条件。

测试过程

1.先不登录下载管理端JS文件。静态资源返回完整脚本,没有因为缺少Cookie进入登录页,说明前端打包文件可以直接分析。

GET /<接口路径>/<接口文件>.js HTTP/1.1
Host:fXg.cXXXd.net

2.从JS中提取 signKey 、AES密钥和IV,并定位签名函数和密码加密函数。密钥出现在公开资源中,下一步需要确认算法是否与服务端实际校验一致,不能只凭变量名判断。

3.在浏览器开发者工具和本地脚本中各计算一次相同参数的签名,两种实现得到相同MD5值。随后按参数名称排序拼接请求参数,再追加 signKey 生成测试接口签名。

4.把生成结果放入请求调用测试接口,服务端正常处理,说明签名算法可以从公开前端完整复现。为了区分签名和登录态,继续使用随机 sessionKey 调用后台用户接口。

5.随机 sessionKey 请求返回未登录状态,说明合法签名不能替代用户登录。该对照把风险范围限定为签名失效,而不是直接认证绕过。

6.使用提取的AES密钥解密自建测试密文, passwordCipher 等字段能够被还原。结合前面的签名结果可以确认,公开静态资源已经包含请求防伪和密码字段保护所需材料。

短信接口手机号枚举与频率限制缺失

漏洞情况

/<接口路径>/<接口文件>.jspx 无需登录、图形验证码或签名即可访问。 smsType=1 对已注册和未注册手机号返回不同业务码, smsType=2 也会返回“用户未找到”等差异化结果。历史验证中同一手机号在2秒内连续发送两次均返回成功。

接口使用GET传递手机号和业务类型,没有图形验证码、短信令牌或请求签名。注册状态通过业务码和提示语直接返回,响应差异可以在不登录的情况下批量收集。当前只观察到同一号码的短冷却,跨号码、IP和会话的频率限制尚未验证。

危害

攻击者可以批量判断手机号是否属于平台会员,形成可继续用于撞库和精准诈骗的名单。发送接口只确认了号码级短冷却,跨号码批量调用可能造成短信骚扰和费用消耗,其他频率限制仍需继续验证。

测试过程

1.先准备一个未注册手机号和已有账号使用的测试手机号,分别请求注册短信接口,只改变号码,保持 smsType=1 和其他参数不变。

GET /<接口路径>/<接口文件>.jspx?mobilePhone=<未注册手机号>&smsType=1 HTTP/1.1
Host:fXg.cXXXd.net

2.未注册号码返回“成功”,已注册号码返回“手机号已注册”,两者业务码不同。响应已经能区分注册状态,因此继续把 smsType 改为2,检查第二种业务场景是否也返回可比较的提示。

3.把 smsType 改为2并提交未注册号码,响应返回“用户未找到”。保持号码不变再切回类型1,提示恢复为可发送状态,说明差异来自业务类型而不是号码格式错误。

4.为了确认接口是否具备频率限制,在短时间内对同一测试号码重复发送,历史响应均提示成功或验证码已发送,短时间窗口内没有出现阻断。

5.由于响应差异稳定且没有验证码门槛,攻击者可以批量判断号码是否属于平台会员。测试只使用未注册测试号码观察提示,没有向真实用户批量发送短信,也没有继续扩大请求频率。

fXu.aXi.zXXXd.cn 监管业务平台

资产概况

该站点为监管业务平台业务接口,域名为 fXu.aXi.zXXXd.cn 。前端小程序和后台共用 /<接口路径> 、 /<接口路径> 等模块,短信登录、密码重置和业务申报请求统一经过API签名中间件处理。

ThinkPHP调试页源码泄露与API签名失效

漏洞情况

生产环境未关闭ThinkPHP调试模式,匿名访问 /<接口路径> 就会触发异常页并回显 <接口路径>/<接口文件>.php 第10至28行源码。源码包含API签名算法、公开 appid 和时间戳计算方式,同时显示时间戳超时校验已经被注释。

调试页由框架异常处理器直接输出,请求不需要账号或令牌,也不会因为异常而返回统一错误页。 认证文件 中的注释和实际代码同时暴露,攻击者可以确认签名参数、拼接顺序和MD5输入范围。时间戳超时校验被注释后,签名不再具备短时有效性,公开 appid 又能从页面或小程序包中取得,网关签名可以被离线重放。

危害

攻击者不需要登录即可取得完整签名算法和公开 appid ,随后可以为任意 /<接口路径> 请求生成合法签名。超时校验失效后,历史时间戳也可以重复使用,API签名机制无法起到防伪造和防重放作用。该问题还会暴露框架版本、服务器路径和PHP环境信息,为后续接口攻击提供条件。

测试过程

1.直接访问 /<接口路径> ,不附加Cookie、Token或特殊参数,先确认生产入口在普通请求下是否返回正常业务页面。

GET /<接口路径> HTTP/1.1
Host:fXu.aXi.zXXXd.cn

2.响应返回ThinkPHP异常页,正文包含 认证文件 源码和签名算法。页面直接展示文件片段,说明框架调试模式在生产环境处于开启状态,需要继续查看源码中的校验逻辑。

3.源码显示时间戳超时校验被注释,签名由公开 appid 和毫秒时间戳拼接后计算MD5。为了确认这只是页面展示还是实际运行逻辑,先记录框架版本和应用路径,再用同一算法生成两个不同时间戳的签名。

4.分别使用当前时间和较早时间生成签名,调用同一个只读业务接口。两个请求都能进入业务处理,旧时间戳没有被拒绝,说明注释掉的超时校验确实没有生效。

布尔值绕过短信验证码

漏洞情况

短信登录和密码重置接口从JSON请求体读取 code 后,使用PHP宽松比较判断验证码。攻击者为目标手机号生成验证码缓存后,把 code 提交为布尔值 true ,服务端会把它与任意非空验证码字符串判定为相等,从而通过校验。接口没有限制 code 的JSON类型。

验证码登录接口 和密码重置接口共用了短信验证码校验逻辑,传入值没有先转换成字符串,也没有检查JSON类型。PHP的松散比较会把布尔值 true 与任意非空字符串视为相等,因此只要目标手机号存在验证码缓存,验证码内容本身就不需要知道。登录成功后返回的 token 可用于读取账号资料,密码重置则会改变真实口令状态。

危害

攻击者无需接收目标手机短信即可登录任意已注册手机号账号。同一缺陷可以用于密码重置,重置成功后还能使用新密码登录并使旧密码失效。账号内的企业信息、申报记录和办件数据都会暴露,属于可直接接管用户账号的高风险问题。

测试过程

1.先使用合成手机号请求登录短信,使服务端为该号码建立验证码缓存。请求返回发送成功,后续不读取真实验证码,只改变登录请求中的 code 类型。

POST /<接口路径>?appid=×tamp=
Host:fXu.aXi.zXXXd.cn

2.提交登录请求,把 code 设为JSON布尔值 true 。如果服务端严格按字符串比较,这个值应该失败,因此先观察是否返回验证码错误。

POST /<接口路径>?appid=×tamp=
Host:fXu.aXi.zXXXd.cn
Content-Type:application/json

{"mobile":"<测试手机号>","code":true}

3.响应直接返回有效 token ,没有提示验证码错误,说明 true 与非空验证码字符串在宽松比较中被当成相等。为排除验证码被跳过,再把 code 改为普通错误字符串重新登录。

4.错误字符串返回验证码错误,说明接口确实执行了比较,差异来自JSON类型。继续把布尔值 true 用于同一手机号的密码重置接口,检查两处是否共用同一校验函数。

5.密码重置请求通过,随后使用新密码登录成功,旧密码登录失败。账号状态发生变化,可以确认问题不只影响登录响应,还能改变真实口令。

6.再把 code 改为数字 1 、字符串 "1" 和布尔值 false ,观察类型处理。只有特定布尔值触发异常放行,其他类型仍按验证码错误处理,进一步缩小到PHP宽松比较。

7.登录和改密只使用合成手机号,没有访问真实用户业务数据。测试结束后保留新密码登录和旧密码失效的结果,不继续尝试其他账号。

签名伪造与未授权短信下发

漏洞情况

短信发送接口 /<接口路径> 只校验由公开参数生成的 sign ,没有继续校验登录态、图形验证码或短信业务场景。 appid 可以从公开前端取得,签名算法已经通过调试页泄露,攻击者能够自行计算时间戳和MD5。

签名中间件验证的是请求参数能否被复算,无法识别调用者身份,也没有把手机号、业务场景和一次性随机数绑定。 短信发送接口 本身没有登录态检查,进入控制器后直接按手机号执行发送。服务端只对同一号码设置很短冷却时间,重复请求会返回已发送状态,但不同号码和不同来源之间没有统一频控。

危害

攻击者可以匿名调用短信发送接口,对不同手机号批量下发验证码或通知短信,造成短信费用消耗、用户骚扰和业务通知可信度下降。该接口也可以作为短信验证码弱类型绕过的前置调用,为账号登录和密码重置提供验证码缓存。

测试过程

1.先另选一个需要签名的业务接口作为对照。不带签名请求返回401,按已泄露算法生成签名后能够进入业务处理,说明签名中间件接受客户端计算的参数。

2.根据 认证文件 的算法计算 appid 与时间戳的MD5,并把结果用于短信发送接口。这个接口不需要登录态,因此请求面对的主要门槛就是签名和业务频控。

POST /<接口路径>?appid=×tamp=
Host:fXu.aXi.zXXXd.cn
Content-Type:application/json

{"mobile":"<测试手机号>"}

3.首次响应提示发送成功。短时间重复同一请求时,业务码变为402并提示验证码已发送,说明服务端只对同一号码设置了很短的发送间隔。

4.把时间戳改成较早的值,重新计算签名后再次发送,服务端仍未因时间过旧而拒绝。该结果与调试页中注释掉超时校验一致,历史签名可以重复使用。

5.由于签名材料和算法都可复现,而接口没有登录态和图形验证码,攻击者可以换用多个号码继续调用。测试全程只使用合成手机号并触发两次发送,没有更换号码批量请求。

nXc.hXh.cXn.fXXXt.net 妇幼健康业务系统

资产概况

该站点为妇幼健康业务系统,域名为 nXc.hXh.cXn.fXXXt.net 。系统入口位于 /<接口路径> ,业务覆盖托幼机构、工作人员、儿童健康档案和组织管理,登录会话保存在客户端 Cookie中。

客户端会话伪造导致认证绕过

漏洞情况

保存完整的PHP序列化会话,服务端直接信任其中的 user_id 。Cookie只经过base64编码和固定20字节掩码XOR处理,没有服务端会话存储、HMAC或数字签名,客户端可以自行还原并重新构造会话内容。

会话中还包含机构、角色和用户展示信息,服务端进入业务页面时直接读取这些字段。XOR使用的是客户端可推导的固定掩码,编码后没有附带校验值,修改序列化内容不会破坏Cookie格式。攻击者可以从空白会话或自有账号会话中还原结构,再把 user_id 替换为1、2等小整数,不需要知道账号口令或短信验证码。

危害

攻击者无需账号即可把 user_id 改为其他用户编号并进入管理控制台。伪造 user_id=1 后,系统返回完整后台框架、菜单和用户信息页面,后续托幼机构、工作人员和儿童健康档案功能均可能继续访问。该问题绕过整套登录认证,会使系统内受保护业务数据暴露。

测试过程

1.未登录访问控制台页面,响应为“未经授权的访问”。这个结果说明入口存在鉴权检查,随后检查浏览器保存的 结构,判断会话信息是否完全由客户端保存。

GET /<接口路径> HTTP/1.1
Host:nXc.hXh.cXn.fXXXt.net

2.将 修改为 user_id=1 ,保留原有可逆序列化结构。此时只改变用户编号,不修改Cookie长度和序列化字段,便于判断服务端是否校验会话完整性。

3.携带修改后的Cookie再次请求控制台。如果仍未登录,只需要继续寻找动态校验值,如果直接进入后台,则说明Cookie内容可以被完整信任。

GET /<接口路径> HTTP/1.1
Host:nXc.hXh.cXn.fXXXt.net
Cookie:=<伪造会话>

4.响应变成控制台frameset,菜单和用户信息页面可访问。为确认服务端只按编号建立身份,分别把 user_id 改为2和<固定验证码>,重新请求控制台、菜单和用户信息页。

5.不同编号都按Cookie中的值加载对应身份信息,没有出现签名错误或会话失效。再次更换全新Cookie容器重放同一伪造值,响应仍进入控制台,说明会话没有和浏览器指纹或服务端随机标识绑定。

6.测试只读取控制台框架、菜单和用户信息,没有进入具体业务列表,也没有修改健康档案。认证绕过的结果已经成立,不再继续访问儿童健康数据。

会话org_id时间盲SQL注入

漏洞情况

会话中的 org_id 由客户端控制,服务端没有重新校验其格式和权限,并把它直接拼接进 /<接口路径> 的SQL条件中。参数没有使用预处理或类型转换,攻击者可以在会话Cookie内写入MySQL表达式。

org_id 从可伪造的 中取出后直接进入查询条件,既没有用引号包裹,也没有强制转换为整数。页面加载一次会多次执行相关查询,因此 SLEEP 造成的延迟会叠加。响应页面尺寸保持稳定,只有处理时间随真条件和假条件变化,适合用时间差判断数据库表达式结果。

危害

攻击者可以构造真条件和假条件观察响应时间,并通过 SLEEP 参数逐位读取数据库内容。当前数据库账号权限内的数据存在被逐位读取的条件。现有测试只确认表达式执行和真假延迟,没有提取表名、账号或业务数据,具体表范围仍需继续核验。

测试过程

1.先使用正常会话请求组织页面,连续记录几次耗时和页面大小,建立基线。随后把Cookie中的 org_id 改为假条件 IF(1=2,SLEEP(2),0) ,其余字段保持不变。

GET /<接口路径> HTTP/1.1
Host:nXc.hXh.cXn.fXXXt.net

2.假条件请求没有明显延迟,页面仍能正常返回。再把 org_id 改为真条件 IF(1=1,SLEEP(2),0) ,观察同一页面是否只在条件成立时变慢。

3.真条件请求出现明显延迟,页面大小基本不变。由于服务端返回体没有变化,下一步把 SLEEP 参数从0增加到1和3,检查延迟是否按指定秒数增长。

4.延迟时间随 SLEEP 数值增加,说明Cookie中的表达式进入了数据库执行。为避免网络抖动造成误判,连续执行多次基线、假条件和真条件请求。

5.多轮结果保持相同差异,假条件接近基线,真条件稳定延迟。再将条件改为可比较的字符串表达式,确认响应时间能够根据真假值变化,从而具备逐字符读取数据库的条件。

6.测试只使用 IF 和 SLEEP 判断表达式能否执行,没有提取表名、账号或业务数据,也没有写文件、删表或修改数据库。

fXr.bXXXp.com 文件管理系统

资产概况

该站点为文件管理系统,域名为 fXr.bXXXp.com ,后台入口位于 /<接口路径> 。系统提供文件流转、附件管理、文件详情和下载功能,同域还部署了Druid监控与Swagger接口文档。

Druid监控台与Swagger未授权访问

漏洞情况

/<接口路径>/<接口文件>.html 、 /<接口路径>/<接口文件>.json 和 /<接口路径> 均可在未登录状态下访问。Druid基础信息暴露Java、Tomcat、安装路径和启动时间,数据源信息包含JDBC连接串、数据库类型、 数据库管理账号 账号和连接池状态,SQL统计接口还返回业务查询语句。Swagger文档暴露87个后台接口。

Druid页面和JSON接口没有配置登录页或来源IP限制,数据源连接串直接指向本地MySQL,应用还使用 数据库管理账号 账号并启用多语句参数。SQL统计中可以看到系统表、售后附件和定时任务相关语句,接口文档则给出后台路径、请求方法和参数模型。两类信息放在同一域名下,攻击者可以从数据库结构和接口清单两个方向寻找可写入口。

危害

攻击者可以利用Druid信息确认数据库地址、账号、框架版本和业务SQL,再通过Swagger了解文件接口、参数和权限结构。数据库 数据库管理账号 账号与业务SQL会显著降低后续攻击门槛,接口文档还可以帮助发现未授权接口、越权参数和文件操作入口。

测试过程

1.先不登录访问Druid首页和数据源接口。Druid首页能够正常打开,说明监控组件没有在入口处要求登录,随后请求JSON接口,确认页面是否存在实际数据。

GET /<接口路径>/<接口文件>.html HTTP/1.1
Host:fXr.bXXXp.com
GET /<接口路径>/<接口文件>.json HTTP/1.1
Host:fXr.bXXXp.com

2.响应返回数据库连接、用户名、系统路径和Java组件信息。由于基础接口已经可用,继续请求 基础信息接口 和 SQL统计接口 ,比较不同接口返回的组件版本、安装路径、连接池状态和SQL数量。

3.统计数据中包含业务查询语句,说明监控接口不只是展示健康状态。退出Druid页面后直接请求Swagger文档,检查同域是否还暴露接口清单。

GET /<接口路径> HTTP/1.1
Host:fXr.bXXXp.com

4.响应约为73KB,包含87个业务接口。从文档中抽查文件查询、附件上传和用户查询接口,参数模型和后台控制器名称均可见。

5.抽查结果与Druid中的业务SQL相互对应,可以确认两类信息都来自同一后台环境。测试只读取监控和文档接口,没有使用泄露的数据库账号连接数据库,也没有调用文档中的写接口。

路径归一化读取后台文件

漏洞情况

业务入口鉴权过滤器按原始URI前缀判断匿名白名单,把 /<接口路径>/** 视为无需Token的路径。Tomcat在路由前会把字面 ..; 路径段归一化为 .. ,因此 /<接口路径>/..;/<接口路径> 既命中匿名白名单,又会到达受保护的文件详情控制器。

过滤器使用的URI和Spring最终映射的URI不一致,是这条路径能够通过的原因。分号让中间件把 ..; 当作普通路径参数,Tomcat随后又把它解析成上级目录,结果 /<接口路径>/..;/<接口路径> 在业务路由中被还原为 /<接口路径> 。文件详情控制器没有再次读取登录态,匿名请求可以直接取得元数据和附件地址。

危害

攻击者可以匿名读取后台文件详情、文件名、文件描述和附件直链。对附件URL发送HEAD请求返回HTTP200,说明文件对象本身也不需要登录即可访问。若文件包含申报材料、身份证明或业务附件,会造成敏感文件泄露,并可被继续枚举。

测试过程

1.先正常请求文件详情接口,记录HTTP状态、响应码和错误提示。接口返回 鉴权失败业务码 和 code=401 ,说明正常路径存在Token校验,需要继续比较过滤器看到的URI与业务路由实际匹配的URI。

POST /<接口路径>?fid= HTTP/1.1
Host:fXr.bXXXp.com

2.保持请求方法、 fid 和请求体不变,只在 业务入口 后插入 <接口路径>/..; 。如果过滤器按 /<接口路径>/** 放行而Tomcat又把 ..; 归一化为上级目录,这条路径应当同时命中匿名白名单和原业务控制器。

POST /<接口路径>/..;/<接口路径>?fid= HTTP/1.1
Host:fXr.bXXXp.com

3.变形请求返回 code=0 ,并带有文件名、文件描述和附件直链。接口从401变为正常业务响应,说明路径归一化已经让请求越过Token过滤器。

4.对响应中的附件URL发送HEAD请求,返回HTTP200,未登录也可以访问对象存储中的附件。该结果表明确实存在文件元数据和附件地址泄露,附件对象本身也不需要登录即可访问。

5.为判断影响范围,选择用户、机构、文件策略等30个受保护只读端点做相同对照。正常路径返回401,变形路径进入业务层并出现 code=500 等处理结果,说明绕过方式不局限于文件详情接口。

6.分别测试 ..%3b 、 %2e%2e%3b 和末尾追加斜杠等变形,只有Tomcat能够识别的字面 ..; 进入业务层。不同编码的结果进一步确认问题来自URI解析差异,而不是某个控制器被单独配置为匿名。

7.读取 fid= 对应的文件名、描述和附件地址后停止,没有下载附件内容,也没有调用删除、修改或上传接口。现有结果已经足够确认后台文件元数据和附件访问控制被绕过。

yXn.jXXXe.com.cn 停车管理平台

资产概况

该站点为停车管理平台,域名为 yXn.jXXXe.com.cn ,业务入口位于 /<接口路径> 。系统提供登录审计日志、组织用户信息和图片上传功能,相关控制器统一受 登录令牌 过滤器保护。

矩阵参数绕过鉴权读取登录日志

漏洞情况

/<接口路径>/** 下的接口依赖 登录令牌 过滤器校验登录态,但过滤器按原始URI匹配,业务路由会忽略路径末段的矩阵参数。攻击者在受保护路径后追加 ;download 后,请求绕过Token校验并继续到达Spring控制器。登录日志接口直接返回分页数据,没有额外检查调用者身份。

Tomcat会把分号后的内容作为路径参数处理,防火墙和过滤器看到的路径与Spring控制器匹配的路径不同。过滤器把 ;download 当成路径的一部分,控制器仍按 登录日志查询接口 执行。日志查询方法没有从会话读取用户身份,分页参数也不受角色限制,因此绕过过滤器后直接返回审计数据。

危害

实测日志总量为18167748条,单条包含姓名、手机号、登录IP、所属组织、登录来源和操作时间。攻击者可以分页收集员工身份、组织关系和登录习惯,用于账号撞库、精准钓鱼和内网信息搜集。数据规模已经达到千万级,属于大规模个人信息和审计数据泄露。

测试过程

1.先直接请求登录日志接口,不附加 登录令牌 。响应体约73字节,业务码为 鉴权失败业务码 ,提示未登录,说明正常路径确实经过过滤器。

POST /<接口路径> HTTP/1.1
Host:yXn.jXXXe.com.cn

2.保持请求方法和请求体不变,在路径后追加 ;download 。这样过滤器会把分号内容当成路径的一部分,而Spring控制器仍可能匹配到 登录日志查询接口 ,因此重点观察业务码是否从失败变为成功。

POST /<接口路径>/<接口路径>;<矩阵参数> HTTP/1.1
Host:yXn.jXXXe.com.cn

3.响应业务码变为 鉴权成功业务码 ,返回登录日志和 totalCount=18167748 ,单条包含姓名、手机号、IP和组织。路径变化直接消除了鉴权失败,可以确认矩阵参数导致过滤器和控制器看到了不同路径。

4.把 pageSize 设为1,只读取一条记录,避免批量导出。随后在相同路径上测试 ;export 等其他矩阵参数,响应仍能进入业务处理,说明差异不只对 download 这一个后缀生效。

5.测试停留在单条日志和少量后缀,没有利用1800多万条总量翻页,也没有导出完整审计数据。

矩阵参数绕过鉴权调用上传接口

漏洞情况

同一路径解析差异也影响 /<接口路径> 。直接请求上传控制器会返回未登录提示,在路径后追加 ;download 后,请求可以绕过 登录令牌 过滤器并进入文件处理逻辑。控制器接受 multipart/form-data 文件并直接调用对象存储上传。

上传控制器与日志接口暴露在同一个 /<接口路径> 前缀下,过滤器规则只覆盖固定URI模式。绕过鉴权后,控制器会读取文件名、内容和扩展名,再调用对象存储SDK写入业务对象存储桶。响应包含对象地址和带安全令牌的签名参数,说明操作已经进入云存储写入阶段。

危害

攻击者可以匿名向官方对象存储桶写入文件,并取得上传后的图片地址和STS临时凭据。批量写入会占用存储资源,攻击者还可能上传违规内容或利用可控文件名干扰业务展示。若上传类型和对象权限校验不足,后续还可能扩展到文件覆盖或内容投放。

测试过程

1.先按正常路径提交上传请求。响应约73字节,业务码为 鉴权失败业务码 并提示未登录,说明上传控制器受到 登录令牌 过滤器保护。

2.使用带 ;download 的路径提交一个70字节透明PNG。文件内容保持无害,只改变路径,观察请求能否越过过滤器进入控制器。

POST /<接口路径>/<接口路径>;<矩阵参数> HTTP/1.1
Host:yXn.jXXXe.com.cn
Content-Type:multipart/form-data;boundary=----probe

------probe
Content-Disposition:form-data;name="file";filename="proof.png"
Content-Type:image/png

<70字节PNG二进制>
------probe--

3.首次响应返回上传控制器业务码 上传失败业务码 ,不再是 登录令牌 未登录提示。该变化说明请求已经进入上传控制器,只是当前请求还缺少控制器要求的字段。

4.补齐控制器要求后再次提交同一无害文件,响应返回 imagePath 和带 x-对象存储-security-token 的对象存储签名地址。响应结果确认匿名请求已经完成对象存储写入。

5.对返回的对象地址只检查可访问性,没有上传脚本、HTML或覆盖任何已有文件。测试使用透明PNG并控制文件大小,避免对存储桶造成额外影响。

wXw.fXXXd.gov.cn 政务网站

资产概况

该站点为政务网站,域名为 wXw.fXXXd.gov.cn 。公开侧提供新闻、人大业务和群众来信入口,管理端与公开站同源,留言详情和打印页面由单独的后台脚本渲染。

群众来信验证码可识别且允许匿名批量提交

漏洞情况

群众来信接口 /<接口路径> 允许未登录提交留言,图片验证码只做简单字符校验,没有绑定会话、来源IP或提交频率。验证码图片能够被OCR自动识别,留言正文也没有长度、格式和频率限制。

危害

攻击者可以批量伪造群众来信,污染信访数据并消耗人工审核资源。大量重复或恶意内容可能触发错误的信访流转,影响正常群众诉求处理。匿名接口还会掩盖真实提交来源,增加后续追溯和处置难度。

测试过程

1.先访问群众来信页面并请求验证码图片,记录页面字段、验证码URL和请求所需的Referer。页面可以匿名打开,验证码接口也正常返回图片。

GET /<接口路径>?t=<时间戳> HTTP/1.1
Host:wXw.fXXXd.gov.cn
Referer:http://wXw.fXXXd.gov.cn/<接口路径>
Cookie:<群众来信会话Cookie>

2.使用OCR识别验证码字符。识别结果能够稳定读出,说明验证码没有使用复杂扭曲或交互校验,下一步把这个结果带回留言表单。

3.提交带无害标记的留言。提交时保持页面要求的字段和Referer,只观察服务端是否验证当前会话与验证码的绑定关系。

POST /<接口路径> HTTP/1.1
Host:wXw.fXXXd.gov.cn
Content-Type:application/x-www-form-urlencoded
Cookie:<群众来信会话Cookie>

content=<无害标记>&captcha=<识别结果>

4.响应返回留言成功,说明匿名请求可以完成验证码识别并写入。结合接口未设置频率限制,攻击者可以重复获取、识别和提交,现有结果确认批量写入条件成立。

管理端留言内容innerHTML渲染风险

漏洞情况

管理端生产脚本 业务脚本 把 detail.content 和 replyContent 直接赋给 innerHTML ,没有使用转义、可靠清洗或安全DOM接口。群众来信接口可以匿名写入,管理端与该接口同源。当前已验证匿名写入成功和危险渲染函数存在,服务端是否原样落库以及管理员打开后的脚本执行尚未完成端到端验证,因此按条件性存储型XSS描述。

危害

如果服务端保存留言时不进行HTML实体编码,攻击者可以提交带事件处理器或脚本标签的正文。管理员打开详情或打印页面后,脚本会在 wXw.fXXXd.gov.cn 源下执行,可能读取管理端 localStorage 中的 access-token 、以管理员身份调用后台接口并修改页面内容。

测试过程

1.先下载管理端生产脚本,确认页面渲染逻辑是否对留言正文进行编码。脚本能够匿名读取,文件中的 detail.content 和 replyContent 直接赋给 innerHTML 。

GET /<接口路径>/<接口文件>.js HTTP/1.1
Host:wXw.fXXXd.gov.cn

2.危险渲染函数已经出现,但还不能据此认定存储型XSS。继续请求后台详情接口,观察服务端是否把原始留言直接交给该脚本。

3.直接请求后台详情接口时返回401,公开侧也无法回读刚提交的留言,因此没有取得管理员查看详情时的完整响应。当前只能确认匿名写入入口和危险渲染函数同时存在。

4.由于缺少管理员详情响应,文章把该问题按条件性存储型XSS记录。要确认实际执行,还需要在授权环境中检查服务端是否原样保存HTML,以及管理员页面是否在浏览器中执行该内容。

wXw.iXXXb.com 媒体网站

资产概况

该站点为媒体网站,域名为 wXw.iXXXb.com 。系统包含新闻内容、历史数字报、微信登录、举报表单和后台管理,前台用户与后台账号共用同一主域,部分身份Cookie没有设置 HttpOnly 。

微信授权回调开放重定向与state校验缺失

漏洞情况

微信授权中转接口 微信授权中转接口 接收 re_url 后没有进行域名白名单校验,会把微信回调携带的 code 拼接到任意目标地址并返回302。登录接口 微信登录接口 在独立会话中直接使用 code 换取微信用户信息,没有校验 state 与发起授权的会话是否匹配。

危害

攻击者可以构造微信授权链接,诱导用户授权后把新的OAuth授权码发送到外部服务器。授权码没有与用户会话绑定,具备在另一会话中重放的条件。当前使用假 code ,只确认外跳、参数透传和 state 缺失,没有完成真实授权码换取或账号接管。

测试过程

1.先构造外部 re_url 并携带假 code ,观察中转接口是否限制目标域名。请求没有携带真实微信授权码,只检查重定向和参数拼接。

GET /<接口文件>.php?mod=<动作>&a=<动作>&wx_product_id=&re_url=https%3A%2F%2FeXXXe.com%2F&code=测试授权码&state=0 HTTP/1.1
Host:wXw.iXXXb.com

2.响应302到外部域名,并把 code 拼到目标URL。由于绝对地址可能受到单独规则限制,继续改用协议相对地址,检查域名检查是否只匹配了部分URL格式。

GET /<接口文件>.php?mod=<动作>&a=<动作>&wx_product_id=&re_url=%2F%2FeXXXe.org%2F<接口路径>&code=测试授权码&state=0 HTTP/1.1
Host:wXw.iXXXb.com

3.协议相对地址同样发生外跳,说明 re_url 没有经过可靠的域名白名单。接着在两个独立无Cookie会话中请求 微信登录接口 ,观察 state 是否要求与原始授权会话一致。

4.两个独立会话都会尝试用假 code 换取微信用户信息,没有因为 state 缺失或会话不匹配而提前终止。结合前面的外跳结果,授权码具备跨会话重放条件,真实账号接管尚未验证。

举报表单匿名写入与IDOR

漏洞情况

formModule 模块的 记录创建接口 接口允许匿名提交表单,创建成功后直接返回自增记录ID。 记录详情接口 接口按 formRecordId 读取详情,没有校验表单归属、提交人或当前登录用户。记录ID虽然稀疏,但整体递增,攻击者可以通过新建探针记录取得一个接近当前值的ID。

危害

攻击者可以匿名批量写入举报数据,干扰正常受理流程。读取接口还会返回举报人姓名、手机号和举报正文,替换为其他记录ID后可以读取其他表单及提交人的信息。举报内容通常包含真实身份、联系方式、纠纷经过等敏感信息,泄露后容易引发骚扰和报复风险。

测试过程

1.匿名提交一条自建记录,使用测试标记、测试号码和无害正文,先确认写接口是否要求登录。提交前记录 form_id ,便于后面对比读取范围。

POST /<接口文件>.php?mod=<动作>&a=<动作> HTTP/1.1
Host:wXw.iXXXb.com
Content-Type:application/x-www-form-urlencoded

form_id=&name=<测试标记>&phone=<测试号码>&content=<测试内容>

2.响应返回自增记录ID,说明匿名写入成功。随后使用新的无Cookie请求读取该ID,检查详情接口是否延续了写入接口的匿名访问状态。

GET /<接口文件>.php?mod=<动作>&a=<动作>&formRecordId=<记录ID> HTTP/1.1
Host:wXw.iXXXb.com

3.响应返回姓名、手机号和留言正文,说明记录详情没有校验提交人或登录态。把记录ID替换为其他自增编号,接口还返回其他表单的数据。

4.其他编号不属于本次创建的 form_id= ,但接口仍返回记录内容,说明表单归属和用户归属都没有进入查询条件。测试只读取少量记录,没有枚举完整编号区间。

历史数字报付费墙绕过

漏洞情况

历史数字报详情页在 详情接口 中只返回截断正文,但同一数据的异步接口 记录详情接口 没有检查登录状态、购买记录或订阅权限,直接返回完整文章正文。页面入口和异步接口共用文章编号,攻击者可以从版面列表提取 articleId 后逐个请求。

危害

攻击者可以免费获取应付费阅读的历史数字报全文,并可批量转载、二次分发或建立镜像。该问题直接损害报纸的数字内容收入和版权控制,也会使付费订阅机制失去约束作用。

测试过程

1.先打开正常文章详情,记录页面实际显示的正文长度。页面只显示截断内容,说明付费限制至少在模板层存在。

GET /<接口文件>.php?mod=<动作>&a=<动作>&articleId= HTTP/1.1
Host:wXw.iXXXb.com

2.保持文章编号不变,改为请求页面使用的异步接口。同一条数据由模板和异步接口分别返回,可以直接比较两种响应是否执行了相同的购买状态检查。

GET /<接口文件>.php?mod=<动作>&a=<动作>&articleId= HTTP/1.1
Host:wXw.iXXXb.com

3.响应直接返回完整文章正文,没有购买状态、登录校验或截断字段。响应结果确认付费限制只作用于详情页模板,异步接口能够绕过同一篇内容的访问控制。

强制改密页反射型XSS与会话劫持

漏洞情况

/<接口文件>.php?mod=<动作>&a=<动作> 把请求体 targetUserId 原值写入隐藏输入框的 value 属性,没有进行HTML实体编码。双引号可以闭合属性并注入任意标签,接口允许匿名提交,没有CSRFtoken、验证码或来源校验,响应也没有CSP等缓解头。

危害

攻击者可以构造自动提交表单,让已登录管理员在 wXw.iXXXb.com 源下执行脚本。后台身份Cookie 会话Cookie 、 用户编号Cookie 、 用户名Cookie 等没有设置 HttpOnly ,脚本能够读取Cookie并外传,也可以代替管理员调用后台接口。真实浏览器验证已经观察到脚本执行和身份Cookie可读。

测试过程

1.向强制改密接口提交可控的 targetUserId 。载荷用于闭合隐藏输入框的 value 属性,先观察服务端返回的HTML是否保留双引号和标签。

POST /<接口文件>.php?mod=<动作>&a=<动作> HTTP/1.1
Host:wXw.iXXXb.com
Content-Type:application/x-www-form-urlencoded

targetUserId=">

2.响应把参数原样写入 value 属性,双引号闭合后形成脚本标签。页面源码已经具备执行条件,随后检查响应头中的CSP和身份Cookie的 HttpOnly 属性。

3.响应没有CSP,身份Cookie也没有 HttpOnly ,脚本可以读取普通Cookie。在真实浏览器中加载响应后, alert 执行并且身份Cookie能够被同源脚本读取,确认反射型XSS和会话材料暴露同时成立。

后台强制改密接口未授权访问

漏洞情况

<接口路径>/<接口文件>.php?mod=<动作>&a=<动作> 均无需登录。匿名GET会返回完整改密表单,匿名POST携带不存在的 targetUserId 和新旧密码后,服务端进入旧密码校验与修改逻辑,并把客户端提交的用户ID回显为目标账号。接口没有从登录会话中读取当前用户,也没有在进入业务逻辑前验证身份。

危害

攻击者可以匿名对任意用户编号发起改密请求,绕过登录侧验证码和账号锁定。如果旧密码校验没有绑定当前会话,攻击者取得某账号旧密码后就具备修改该账号口令的条件。当前使用不存在的测试编号,没有完成任意账号改密,也没有继续批量提交用户ID。

测试过程

1.先匿名GET强制改密页面,响应直接返回完整表单,没有登录跳转或权限提示,说明页面入口本身允许匿名访问。

2.随后提交不存在的用户ID <测试用户编号> 和新旧密码,观察接口是返回通用参数错误,还是继续进入业务校验。

POST /<接口文件>.php?mod=<动作>&a=<动作> HTTP/1.1
Host:wXw.iXXXb.com
Content-Type:application/x-www-form-urlencoded

targetUserId=<测试用户编号>&oldpwd=x&newpwd=y

3.响应进入旧密码与新密码比较逻辑,并回显客户端提交的用户ID,说明改密控制器没有从登录会话中读取当前用户。测试使用不存在的编号,没有完成真实账号改密,也没有继续提交其他用户编号。

五、修复建议

wXk.fXXXu.cn 教育管理平台

轮换AppSecret,审计历史 access_token 调用、素材、菜单和模板消息记录。接口只返回通用错误,禁止把第三方请求URL、密钥和异常堆栈写入响应。

fXXXh.org.cn 工会业务平台

给工会树、会员列表和导出接口增加登录和机构数据权限。固定验证码改为一次性短信码并限制发送频率,会员接口从服务端身份读取机构范围,不接受任意 deptId 。

fXg.cXXXd.net 文旅服务平台

tpl 改为固定模板白名单,搜索和页面属性统一编码。报名、评论和活动流水接口增加登录及数据权限。后台会话改为服务端短期令牌,不再使用 localStorage 作为唯一鉴权值。短信接口增加验证码、手机号和IP限流,前端密钥全部轮换。

fXu.aXi.zXXXd.cn 监管业务平台

关闭生产调试模式,移除源码回显。短信验证码改为严格类型比较,签名加入服务端密钥和一次性随机数。短信发送接口增加登录、图形验证和频率限制。

nXc.hXh.cXn.fXXXt.net 妇幼健康业务系统

会话改由服务端保存,客户端只保留随机标识,并对Cookie签名。所有从会话取值进入SQL的位置改为参数化查询, user_id 和 org_id 增加类型及白名单校验。

fXr.bXXXp.com 文件管理系统

关闭或限制Druid和Swagger公网访问,轮换数据库口令。业务入口白名单改为精确端点,过滤器使用规范化路径,Tomcat和网关同步拦截 ..; 、编码和矩阵参数。

yXn.jXXXe.com.cn 停车管理平台

拒绝未预期的分号和矩阵参数,网关、过滤器和业务处理器使用同一套路径规范化结果。登录日志限制字段和批量导出,上传接口要求登录、类型校验和归属校验,缩短对象存储临时凭据有效期。

wXw.fXXXd.gov.cn 政务网站

群众来信增加有效人机校验和频率限制。管理端对 content 和 replyContent 统一编码或可靠清洗,后台令牌不要放在脚本可读的 localStorage ,并增加CSP和异常查看告警。

wXw.iXXXb.com 媒体网站

微信回调只允许固定域名,强制 state 与发起会话绑定,并保证授权码只消费一次。举报表单增加登录、验证码和CSRF校验,详情接口校验表单与用户归属。付费墙同时覆盖详情、异步接口和PDF下载。改密页对 targetUserId 做HTML编码,增加CSRF和身份校验,身份Cookie设置 HttpOnly 。

六、结语

这组政务站点的排查过程大多不依赖复杂payload。一个空认证请求、一个替换的ID或者一个附加在路径后的分号,就可能进入真实业务。

修复时需要同时检查网关、过滤器、框架路由和业务接口。只改页面或只拦截一个已知字符串,往往不能关闭同一入口的其他变体。


— 阅读原文请访问微信公众号「星宇Sec」 · 原文链接 —

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容