首页 / 资讯中心 / 文章详情

从SQL注入到HQL注入:站群系统安全评估与WAF绕过的实战复盘

从SQL注入到HQL注入:站群系统安全评估与WAF绕过的实战复盘 ★ FEATURED ARTICLE
上个月做了一次站群系统的授权评估甲方是一家大型集团的数字化部门旗下几十个对外官网全部跑在同一套平台里。交付负责人挺自信交接会上拍着胸脯说我们这里套了双重WAF前面有云加速后面有硬件设备SQL注入肯定打不进来。结果第一轮测试我们就在后台一个搜索功能里确认了HQL注入一路拿下了数据库权限。这篇实录不教人怎么打真实系统就是想复盘一次完整评估的思路SQL/HQL注入为什么在站群平台里反复出现WAF规则到底该怎么验证以及我开发z0scan这个扫描器的初衷。适合做甲方安全、做渗透测试、做代码审计的朋友参考。1. 一次授权评估SQL注入凭什么还是第一突破口1.1 站群系统的三大历史包袱站群类平台最典型的特征就是一套模板套多站点代码统一、数据库统一、维护集中。听起来是提高效率的好事实际上是把几十个站点的风险压缩成了同一个风险点只要公共代码里有一个坑那就是几十个网站一起踩进去。这次客户的平台是2015年前后上线的Java技术栈打底底层数据库用SQL Server中间还混着几个老业务模块。这种混合架构在传统企业里非常常见Windows Server加SQL Server加Tomcat的组合我在老国企、老央企的机房里见了一大圈。这几年做评估我总结SQL注入的高发区域基本固定在那几块历史遗留的报表查询、后台列表页的排序字段、导入导出功能里的动态列名、移动端接口的模糊搜索。这些功能有个共同特点参数看着人畜无害却直接拼进了SQL语句。当年写代码的人没想过会被外部数据影响等系统轮转了七八年中间换了好几拨维护团队很多老代码已经没人敢动了只能靠外围防护硬扛。1.2 SQL注入的底层逻辑一场快递员式的多嘴要理解SQL注入为什么老是卷土重来得先把底层逻辑说清楚。我习惯把数据库查询比作快递配送系统把用户输入的内容填进一张快递单模板然后交给数据库去执行。正常情况是收件人填什么地址快递员就按地址送。如果填地址的时候悄悄在末尾多了一句顺路把隔壁楼的件也带过来而系统没去校验这句话快递员就会稀里糊涂一起执行。经典的 OR 11就是这种多嘴。它利用了SQL语句里的逻辑判断让原本只查一条记录的WHERE条件变成永真条件一次把全表捞出来。很多老教材把这条payload奉为万能钥匙实际上现在的新系统基本都做了参数化处理直接套这条路成功率已经很低了。真正的漏网之鱼往往藏在那些没法走参数化的位置动态列名、动态表名、存储过程里的动态SQL以及ORDER BY后面的字段。这几个位置用拼接是为了迁就业务灵活性但灵活性往往就是风险敞口。再加上MySQL和SQL Server的方言差异同一段注入语句在两个数据库上的表现完全不同也让不少靠背payload的人翻车。1.3 常规扫描器在站群场景为何频繁哑火我们这次评估先跑了一遍市面上常见的商业扫描器结果不太理想。倒不是工具厂商不用心而是站群系统的请求链路太复杂前端套着CDN中间有WAF网关后面还有统一认证。扫描器发出去的探测请求大多数被WAF的频率限制或特征拦截直接挡下导致结果根本没法收敛。更麻烦的是站群平台里不少接口要求先拿到特定Cookie或签名才能访问扫描器不登录就扫不进去登录态又往往分散在多个子域之间。还有个致命问题是误报。商业扫描器报了不少所谓漏洞人工复核下来大部分是业务自身的异常响应比如参数为空时报了500错误并没有真正的注入特征。这种情况下开发团队看到报告只会说没法复现然后就没有然后了。这次经历让我下定决心自己写一个能完整处理会话链路、能给出证据链的扫描器也就是后面会详细讲的z0scan。2. HQL注入Hibernate框架下的另一个战场2.1 一层翻译带来的安全幻觉如果团队只盯着SQL注入很容易把HQL注入漏掉。HQL是Hibernate框架提供的面向对象查询语言它不是直接发给数据库执行的而是先由Hibernate解析器分析再翻译成对应的SQL方言。举个例子HQL里写from User u where u.name 张三走MySQL数据库时会被翻译成select ... from user where name张三走SQL Server时又变成另一套T-SQL语法。这个过程相当于系统内部多了一层翻译官开发人员的注意力全放在对象和属性的映射上很少有人意识到翻译之前的那段HQL字符串也是可以被篡改的。问题就出在翻译这层带来的幻觉上。很多开发用Hibernate时觉得框架天然安全反正不是自己拼SQL。但老代码里大量存在字符串拼接构造HQL的写法比如String hql from User u where u.name userName ; Query query session.createQuery(hql);这种写法跟直接拼JDBC没有任何本质区别。只要userName被塞进恶意内容Hibernate照样解析、照样翻译、照样执行。而且HQL的语法和SQL不完全一样很多人拿SQL注入的经典payload去打HQL打不通就以为系统没问题其实是打偏了根本没碰到真正的解析路径。2.2 实战里HQL注入的高发位置这次评估我们在一处后台搜索功能里发现了问题。那个功能支持按员工姓名搜索代码里把搜索词直接拼进了HQL后面还带着一个排序参数。两个参数理论上都可以注入但排序参数更隐蔽因为是直接拼进ORDER BY子句的。这类排序字段拼接的问题在HQL和SQL里都特别常见原因是排序方向ASC/DESC没法走参数绑定开发图省事就把字段名也一起拼进去了。ORDER BY注入的直接危害通常不在数据增删改而是会改变返回结果集的排列方式。但如果系统没有对查询结果做二次权限过滤攻击者可以通过构造复杂的条件让结果按某些字段排序再结合页面展示差异间接把不该看的数据暴露出来。我们在这次项目里正是通过对比不同排序参数返回的数据顺序变化确认了这个注入点真实存在然后顺藤摸瓜发现了更多调用链上的问题。还有一种高发情况是动态条件。业务上需要用户可选多个筛选条件、每个条件都可选可不选开发很自然就会写一长串if/else去拼HQL。拼得多了总会有一个条件分支忘了过滤特殊字符这是代码审计时要重点盯的地方。2.3 验证与修复的标准动作确认HQL注入点我们用的是半替换法。做法是构造两类请求一类是正常参数另一类在参数里加入能影响HQL执行结果的片段然后对比返回数据集合的差异。注意这不是去爆破数据库内容而是通过响应差异确认注入点是否存在。只有响应集合出现了可预期的差异才判定为有效注入点。这样可以把误报压到很低。确认之后修复方案其实很标准。能用setParameter绑定参数的地方全部改成绑定比如Query query session.createQuery(from User u where u.name :name); query.setParameter(name, userName);排序字段必须走白名单映射前端传什么值后端只做翻译比如传a就映射成username传d就映射成createTime传不认识的直接拒绝。这套方案后来被我写进了z0scan的修复建议模块每次扫到排序类注入报告里自动带上这份白名单模板开发照着改就能修干净。3. WAF评估绕过的思路要用在加固上3.1 WAF的检测维度和盲区现在的WAF产品不管是商业的还是开源的核心检测维度基本就是三类已知攻击签名、协议违规范、行为异常。签名库大家都会持续维护知名的注入payload基本都在库里协议层会检查请求是否规范比如URL编码是否非法、Content-Type和请求体对不对得上行为层则看同一个IP是不是在短时间高频发起恶意请求、有没有明显的爬虫特征。检测维度常见手段容易漏掉的地方已知签名正则匹配payload特征HQL等中间语言、编码变形体协议规范检查URL、请求头、body规范性分块传输、multipart边界变体行为分析频率、并发、爬虫特征低频慢速请求、分布式来源现在常见的部署形式也分两种。一种是在Web服务器前面单独挂一层WAF设备或网关插件另一种是直接集成在云CDN里。开源方案里有人用宝塔WAF、有人用南墙这类基于OpenResty的模块各有拥趸。商业WAF的好处是规则更新和技术支持有保障开源WAF的好处是规则完全可控但需要有人持续维护否则新漏洞出现的头几天会非常被动。3.2 双重WAF为什么还是放行了HQL注入这次客户已经算防护意识不错了云加速加本地WAF设备两层都上了。结果我们还是找到了HQL注入。原因不复杂WAF的规则库是围绕SQL语法做特征匹配的而HQL这种中间语言的变形空间比纯SQL更大很多规则库里对HQL特征的覆盖并不全。再加上客户为了让业务跑得顺开了不少白名单路径那个后台搜索接口恰好就在白名单里等于把前门直接敞开了。这里我必须把话说清楚。所谓WAF绕过的研究在正经安全评估里的正确用途只有一个帮你判断防护规则的有效性和覆盖度。我自己在测试里确实会去构造各种形态的请求但我做这件事的目的从来不是教人怎么打穿某套WAF而是找出规则的盲区然后推动厂商和甲方把规则补齐。对着真实生产系统反复试探绕过手法本身就是很不职业的行为万一打挂了业务系统后果谁都不想承担。3.3 评估WAF覆盖度的正确姿势如果真想评估自家WAF的防护水平我建议在测试环境里专门搭一个靶心部署一套跟生产环境一致的WAF规则和中间件配置然后用一批已经确认的攻击样本逐条打过去统计每条样本是否被拦截。拦住了就说明规则覆盖到位没拦住就把样本交给规则维护方去补特征。这样得到的覆盖率报告才是加固依据而不是盲目在生产网段里做试探。实际操作时样本集里可以多放些历史真实攻击记录也可以从当前漏洞情报里取一些新鲜样本。规则维护方拿到报告后重点不是看拦了多少而是看哪些样本漏进来了、漏的共性是什么。如果是编码变形漏的就优化解码逻辑如果是HQL特征漏的就去补充Hibernate相关的规则。3.4 部署WAF的几条实操建议我在各类企业环境里观察下来对WAF部署有几点比较实在的建议。第一别把WAF当万能药。代码侧的输入校验和参数化查询永远不能省WAF只负责兜底。第二启用拦截模式之前一定要先跑一段时间观察模式把误报率降下来不然业务团队被误拦几次之后会天天来找你关规则。第三留意WAF日志里拦截后仍然有请求通过的记录先别急着怀疑绕过有可能是规则被某个操作降级了。定期做规则覆盖度验证这件事比选哪家产品重要得多。4. z0scan的硬核初心工具不该只是一堆PoC4.1 自研扫描器的三个直接原因前面提过商业扫描器在站群系统上经常哑火。直接原因有三个进不了需要登录态的接口、被WAF限流挡死、对HQL这类中间语言支持不足。但更让我难受的是商业扫描器的检测过程不透明。它报一个漏洞你根本不知道它怎么打的、凭什么判定存在报告拿到开发团队面前开发只会回一句我没法复现。所以我开发z0scan的初衷特别明确做一个以SQL/HQL注入检测为核心、能完整复现会话链路、输出带证据链报告的开源扫描器。我想让使用者知道每一个告警是怎么来的payload是什么打在哪个参数上响应特征是什么为什么判定它是漏洞。工具存在的意义是辅助人做判断而不是代替人下结论。这个理念听起来朴素但真正做过安全运营的人都知道一堆不可解释的高危告警还不如几条说得清来源的中危提示有价值。4.2 检测引擎的三大设计要点z0scan的检测引擎我主要围绕三块来设计。第一是指纹识别。先通过响应头、页面特征、错误页样式识别后端技术栈是Java还是.NET是SQL Server还是MySQL。指纹识别准了后续的检测样本才有针对性。同一段HQL注入测试语句放到SQL Server上和放到MySQL上可能需要不同的验证语法才能看到有效差异。第二是请求构造。围绕一个目标参数生成一系列扰动样例保留原始参数的合法值、在合法值后面追加注释符、追加逻辑判断、替换编码形式。每个样例只改动一个变量这样一旦出现异常能精确定位是哪类输入导致的。第三是置信度计算。注入检测最怕误报光看状态码变化很容易被系统自己的500错误骗过去。z0scan会综合对比响应体相似度、响应时间、状态码和数据库错误特征。只有当响应变化与构造输入的预期结果方向一致时才给出高置信度告警。置信度评分的大致权重如下特征类别判断依据权重响应体变化与原响应的相似度、差异位置40%数据库错误特征是否出现SQL语法或解析错误信息25%时间特征注入函数引起的响应延迟20%状态码变化200、500、302之间的异常转换15%4.3 靶场实测和边界拿这次评估的靶场环境做了一轮对比。某个商业扫描器一共报了17条告警人工复核只有3条是真的误报率接近八成。z0scan扫出6条其中5条被人工确认真实存在第6条的问题是老模块的返回页写得比较特殊置信度打分偏低属于漏报边界上的事后续我又调了响应体相似度的算法。z0scan的边界我也很清楚它不碰没有回显的盲注深度利用不会为了拿数据做提权操作刻意不支持对真实业务站点的破坏性测试。工具负责发现问题修复和加固还是要人来完成。5. 复盘从发现漏洞到修复闭环的完整链路5.1 漏洞报告怎么写才真正有人看参与安全评估这么多次我最大的感受是漏洞报告的价值取决于你会不会翻译。开发团队不关心你用了什么奇技淫巧他们需要知道三件事漏洞出在哪个功能、被利用后会有什么后果、怎么改代码才能修好。我写报告的习惯是每个漏洞先写业务影响一句话。比如这次HQL注入我就写任意员工可通过搜索接口获取后台管理员的账号列表然后才是技术细节、复现过程和修复建议。修复建议尽量给得具体直接贴修改前后的伪代码而不是只丢一句建议使用参数化查询。这次我们给开发团队提供了绑定参数改写模板和排序白名单映射表第三天对方就完成了修复效率比之前任何一次都高。5.2 修复验证里容易踩的坑修复不等于结束验证修复是否有效同样重要。我们会在修复后的版本上重新跑一遍同样的检测请求确认注入点消失同时还要回归正常业务功能避免出现漏洞修好了搜索功能也废了的尴尬。一个容易踩的坑是开发用ORM框架的setParameter做了绑定但系统里还有一条历史SQL走了createSQLQuery原生查询通道压根没走绑定逻辑。修复的时候只改了一处另一条链路照样开着。我们后来专门在z0scan里加了一个原生SQL通道的检测标记遇到这种情况会单独提醒防止修复不彻底。回归测试里还要注意脏数据的影响。测试环境里如果残留了之前注入用的样本数据可能导致功能返回异常容易让人误判是修复引入了Bug。所以每次跑回归前我都会先清理测试数据确保结果干净可解释。5.3 给想入行做安全测试的朋友几句实在话最后想分享几句个人体会。第一别急着学各种酷炫技巧先把SQL本身学扎实。要理解一条查询在数据库里是怎么被解析、怎么走执行计划的这是理解SQL注入的前提。连执行计划都看不懂的人写出来的注入验证报告大概率也是糊涂账。第二多去正规的漏洞靶场练手授权范围内的测试才是安全测试未经授权对着真实目标做探测出了事谁也兜不住。第三学会看日志和报告知道一条告警背后的完整链路是怎么回事比会背十个payload有用得多因为真正的攻防对抗拼的都是理解深度。z0scan这个项目现在还在持续迭代我会继续把审计中最常踩的点沉淀进去。也希望读到这里的朋友能把工具当成辅助把安全意识当成习惯这才是这个行业能长期走下去的根本。
阅读完成 · 觉得有帮助?
咨询建站