2017年,漏洞实例Equifax泄露了1.47亿人的漏洞实例信用数据。原因?漏洞实例一个没打补丁的Apache Struts漏洞,CVE-2017-5638。漏洞实例攻击者构造了一个畸形的漏洞实例Content-Type头,服务器就乖乖执行了远程代码。漏洞实例这事被捅出来的漏洞实例时候,补丁已经发布两个月了。漏洞实例两个月的漏洞实例窗口期,够干什么?漏洞实例够把一个国家的信用体系掀个底朝天。
我后来翻过Struts那段漏洞代码。漏洞实例问题出在Jakarta Multipart解析器上,漏洞实例它对Content-Type的漏洞实例校验几乎等于没有。你可以塞进OGNL表达式,漏洞实例服务器会当合法指令跑。漏洞实例说白了就是——门锁着,但钥匙插在锁孔上,还贴了张纸条写着“随便转”。
写代码的人有个通病:默认输入是干净的。这个假设害死人。我见过一个内部系统,登录接口的username字段直接拼进SQL,连预编译都懒得用。开发跟我说“内网嘛,谁会攻击”。结果呢?一个离职员工用sqlmap跑了十分钟,把全公司的薪资表拖走了。漏洞这东西不挑环境,内网外网对它来说没区别。
Buffer overflow算是最古典的漏洞类型了。C语言不检查数组边界,你往一个10字节的栈缓冲区里塞100字节,多出来的90字节就覆盖到相邻内存。如果覆盖的是函数返回地址,攻击者就能控制程序跳转。1988年的Morris蠕虫用的就是这个路子,fingerd服务里的gets()调用。三十多年过去了,这类漏洞还在CVE列表里刷存在感。为什么?因为性能。安全边界检查要花CPU周期,有些场景下开发者觉得“不值”。
换到Web层面,XSS和CSRF像是孪生兄弟。XSS是往页面里注入脚本,CSRF是借用户的身份发请求。前者偷Cookie,后者改密码。我测试过一个电商网站,搜索框输入<script>alert(1)</script>,弹窗直接跳出来。他们的过滤逻辑只替换了小写<script>,大写就绕过了。这种半吊子防御比不防御更危险——给你一种虚假的安全感。
再说个真实的。某次代码审计,我发现一个文件上传接口只检查了Content-Type头。把shell.php的Content-Type改成image/jpeg,服务器照单全收。上传目录还在Web根目录下,直接访问就能执行。我问开发为什么不做后缀白名单,他说“前端已经限制了”。前端限制?Burp Suite改个包的事。
漏洞的本质是预期和现实之间的裂缝。开发者预期用户会按规矩来,攻击者偏不。你预期输入是字符串,他传个数组;你预期文件是图片,他塞个木马;你预期请求来自自己的页面,他伪造Referer。每一条预期都是一扇没关严的窗。
修复漏洞有时候很简单——一个参数化查询、一次转义、一个白名单。难的是意识到那扇窗开着。代码写多了会产生盲区,自己看自己的东西总觉得没问题。这时候需要外人来踹一脚。渗透测试、代码审计、漏洞赏金,本质上都是花钱找人踹门。
我桌上贴了张便签,写着“所有输入都是恶意”。这话绝对了点,但管用。