模拟漏洞
2017年春天,模拟漏洞我盯着一段不到四十行的模拟漏洞Python脚本,后背发凉。模拟漏洞那是模拟漏洞一个测试环境的登录接口,参数校验写得一塌糊涂——用户名字段直接拼进SQL查询,模拟漏洞连最基本的模拟漏洞转义都没有。我随手敲了个单引号,模拟漏洞页面回显了数据库报错。模拟漏洞再补一个OR '1'='1,模拟漏洞管理员权限到手。模拟漏洞整个过程不到三分钟。模拟漏洞
这就是模拟漏洞模拟漏洞的日常。它不像缓冲区溢出那样需要精密计算偏移量,模拟漏洞也不像反序列化利用链那样环环相扣。模拟漏洞模拟漏洞的模拟漏洞核心逻辑简单到近乎粗暴:系统把用户输入当成了可信指令,而攻击者只需要伪造一个“合法身份”就能绕过所有防线。
我后来在多个项目里复现过类似场景。某次内部演练,目标是一个企业OA系统的密码重置功能。请求包里有个user_id参数,正常流程是用户先验证手机验证码,再提交新密码。但后端只校验了验证码是否正确,没校验这个验证码到底属于哪个user_id。我用自己的手机号收到验证码,把请求里的user_id改成目标管理员的ID,密码直接重置成功。系统全程“认为”我就是那个管理员。
这种漏洞的本质是身份模拟。攻击者不需要破解加密,不需要窃取凭证,只需要找到一个系统信任的输入点,然后把自己“模拟”成另一个人。很多开发人员觉得参数校验是小事,前端已经限制了输入格式,后端就懒得再查一遍。可前端限制是给正常用户看的,攻击者直接发原始HTTP请求,前端那层壳根本不存在。
更隐蔽的模拟漏洞出现在API网关层。某次代码审计,我发现一个内部服务通过HTTP头X-User-Id来识别调用者身份。网关负责鉴权,鉴权通过后把用户ID塞进这个头,转发给后端。问题在于,后端服务直接监听内网端口,没有额外的身份校验。任何能访问内网的人,手动构造一个X-User-Id: admin的请求,就能以管理员身份调用所有接口。网关的鉴权形同虚设——它只防外网,不防内网。
这类漏洞的修复方案往往很简单。参数化查询、服务端二次校验、内网服务间加签名、敏感操作强制二次认证。但简单不代表容易落地。我见过一个团队,安全规范写了几十页,上线前扫描工具跑一遍,报告全是“低风险”。结果渗透测试时,一个Content-Type: application/json改成text/xml,XML解析器直接加载了外部实体,服务器文件被读了个遍。扫描工具没报,因为它的规则库里没这条。
模拟漏洞的可怕之处在于它太“自然”了。开发人员写代码时,脑子里想的是正常用户会怎么操作,而不是攻击者会怎么伪造。一个删除订单的接口,参数是order_id,后端查一下这个订单属不属于当前用户,属于就删。逻辑没问题。但如果后端只查了订单存在,没查归属呢?攻击者遍历order_id,就能删掉别人的订单。系统“模拟”了合法删除操作,因为请求本身看起来完全合法。
我现在的习惯是,每写一个接收外部输入的接口,先问自己三个问题:这个输入能不能改变我的业务逻辑?我有没有在服务端重新验证它的每一个假设?如果我把这个输入替换成任意值,最坏会发生什么?大部分模拟漏洞,在这三个问题面前都撑不过第一轮。
安全不是加一层WAF就能解决的。WAF能挡SQL注入的常见payload,但挡不住逻辑漏洞。一个精心构造的请求,参数完全合法,格式完全正确,WAF放行,后端也放行,因为后端信任了不该信任的东西。模拟漏洞利用的就是这种信任。
(责任编辑:休闲)
