软件源码安全攻防之道(中)
点击关注上方蓝字,关注我们噢
上次提到了“内存战场”的攻防博弈,攻击者通过“夺”“绕”“借”“猜”四种战法以获取Shellcode代码的执行权;而防御者由点防往面防发展,由修改代码缺陷往修改整体设计变化,进行对应的阻挡防御。除此之外,还有其他几种典型的攻击,但是战法思想基本和“内存战场”时代如出一辙。
源码漏洞类型和常见攻击手段
01数据变代码,内容变指令
通过前面缓冲区溢出的几个例子,我们可以看到,当内存内容中的shellcode被PC指针指向时,原本的二进制内容就变成机器指令开始执行。其实在很多注入攻击中,攻击者也是尝试让传入的数据具备命令的语义,若程序逻辑存在缺陷,很有可能就将数据当做代码来执行,程序实际上也就成为了攻击者“借”用的“编译器”。
以大家熟知的XSS(Cross-SiteScripting,跨站点脚本攻击)漏洞为例,攻击者传入的数据突破缺陷程序,按照预期变成了可以执行的Javascript 代码,将浏览器当成了“编译器”。
再比如反序列化漏洞中,攻击者传入的载荷实际上就是序列化数据,但缺陷程序限制被突破,导致载荷中的二进制内容转成了Java字节码,将JRE当成了“编译器”。
在漏洞利用中,命令注入作为典型的“数据变代码”类型的攻击,常常能够从载荷中找出攻击者“加料的内容”。一般来说,命令注入可以根据注入逻辑的不同,分为两种。
第一种,命令条件变更,即利用原命令拼接加入新的条件。例如:
原语句:SELECT * FROM items WHERE owner = 'xxxxxx' AND itemname = 'yyyyyy';
注入后:SELECT * FROM items WHERE owner = 'wiley' AND itemname = 'name' OR 'a'='a';
我们并未改变SELECT语句,只是改变了命令条件,得到了想要的结果,本质仍是“借”了SELECT的意图。
第二种,引入新的命令,即命令闭合后另起炉灶。例如:
原语句:SELECT * FROM items WHERE owner = 'xxxxxx' AND itemname = 'yyyyyy';
注入后:SELECT * FROM items WHERE owner = 'wiley' AND itemname = 'name';DROP TABLE userList;—'
我们闭合了第一个SELECT语句,不关心执行结果,不去“借”SELECT。而是“绕”过限制,新加了一个DROP语句进行表删除。
相比之下,大家可以看到,第二种注入的自由度相对比较大,也有可能造成更严重的破坏。
02识破双刃剑,突破解析器和解释器
前面提到了“编译器”,将数据转成代码进行执行,推而广之,我们其实可以看到,在我们的程序中,有很多这一性质的工具存在着,也就是解析器和解释器。刚刚提到的SQL注入案例中,你也可以把数据库执行SQL语句的功能看成是SQL语句的解析。
针对解析器和解释器的攻击是很多的,基本上很少有解析器没有中过招。解析器对格式化的数据进行解析,逻辑是十分复杂的,我们大多数开发者往往是直接拿现成的,很少有自己去开发的。关于主流解析器的攻击类型如下:
解析器/解释器 | 引入的攻击枚举 |
XML解析器 | XML注入攻击 XML外部实体解析(XXE) XML实体扩展攻击(XEE) |
SQL解释器 | SQL注入攻击 |
JSON解析器 | JSON递归DOS攻击 JSON反序列化攻击 |
Bash/WindowsCmd | OS命令注入攻击 |
Struts2 OGNL | OGNL远程命令执行攻击 |
Xpath解析器 | Xpath注入攻击 |
HTML/JS/webview解析器 | XSS攻击 Webview JS接口攻击 |
图片/视频/文档解析器 | 溢出攻击执行恶意代码 |
协议解析器 | 溢出攻击执行恶意代码 DOS攻击 |
时至今日,TCP/IP协议栈处理增加各种安全防护,以前的攻击代码可能已经失效,但攻击的思路并未过时。从近年来很多应用层安全攻击的案例中,我们仍能找到当年TCP/IP网络层安全攻击的影子。
攻击者针对解析器漏洞的发现也逐渐开始自动化,模糊测试技术帮了大忙,攻击者只需要将攻击数据结构构造好,剩下的交由模糊测试工具反复去跑就可以了。设置好开头数据的输入(source),及时关注异常结果的输出(sink),再人工结合分析异常堆栈的细节,就可能找出一个0day。
循环构造的过程很重要,如果本身的数据不符合解析器的格式要求,可能都进不到内部逻辑,因此也需要攻击者对协议足够了解,加速内部解析逻辑的“探查”,才能让模糊测试工具“听话”。千万不要指望工具能帮你彻底分析,你总得学会自己掌舵。即使自动化挖洞的时代会到来,你也要学会站在更高的层面驾驭它,否则总会有失控的风险。
03捕获高权限的时空间隙
利用时间和空间的gap(间隙),是很多提权漏洞利用的常见手段。比如TOCTOU(timeof check time of use)竞争条件利用,就是其中的一种典型手法。
这里的提权,本质是“借”权,攻击者的权限完全来自于缺陷程序。越高权限的程序被“借”权,产生的影响也就可能会越大,因此对于高权限程序的时间和空间限制需要更多的安全考量,避免出现gap,被“狐假虎威”。
缺陷程序没有对要操作的目标文件进行严格校验,导致恶意文件或恶意行为被批准执行。
典型的一个案例,如DirtyCOW脏牛漏洞,攻击者就是利用了高权限复制内存的间隙,将特定内存内容进行覆写,导致提权成为root。漏洞利用的时间窗如下图所示:
04
程序数据的泄露利用
近年来攻击者的攻击方向越来越聚焦于针对数据的攻击,因此程序对于数据处理的安全性受到一定挑战。我们的数据如何在程序中流转,哪些数据在内部处理,哪些数据与外部交互,都是需要做安全性设计考量的。
更严格的情况下,数据甚至在内存中存在的形式和时间都有限制,一旦超出限制或时间,可能会造成机密数据被探取或导出。另外一些报错的系统信息数据(比如堆栈),也有可能会给统计者可乘之机,导致引发进一步的渗透和攻击。
05
脆弱的加密适得其反
对于数据的保护,有很多人可能第一时间想到要进行加密,可是不当的加密反而会适得其反,反而引起攻击者的注意并对其进行破解。
针对脆弱的加密,可采取以下手段:
1、针对不安全的随机算法输出值,进行预测
2、通过反编译获取密钥和加解密算法,对密文数据进行解密
3、针对密码算法本身的破解(统计分析、侧信道、算法漏洞等)
4、对使用有已知漏洞的库(不限于安全的库)进行攻击,例如 OpenSSL:心脏滴血...
加密算法中对于加密模式也有要求,例如ECB模式(ElectronicCodebook,电子密码本模式)相比CBC模式(CipherBlock Chaining,密码分组链接模式)就存在更明显的弱点。
除了加密模式,密钥如何安全存放也是一个难题,很多开发者选择直接将它写死到程序中,简单粗暴,很容易被逆向分析然后进行解密。如果密钥再加密,那么针对密钥加密的密钥(此处不得已而套娃),也得要考虑,总而言之,最终总有一个密钥你要进行保护。
密码学是很专业的安全技术,需要在对的场景使用对的加密算法,实际需要考虑的多方平衡问题也很多,限于篇幅,这里就不再继续讨论了。
06
过多的缺陷引发组合攻击
前文也有提到,如果程序的缺陷越多,那么可能形成漏洞的可能性就越大。因此一个没有遵循安全编码规范的程序和一个遵循安全编码规范的程序相比,出现安全漏洞的可能性是非常大的。没有控制好缺陷密度,就可能使得整个软件产品漏洞重重。
做好必要的代码安全审计工作是十分必要的,如果你没有对自己的代码做好安全审计并解决缺陷,那么攻击者可能就会利用代码安全审计技术将你的缺陷逐一挖掘,再串成一个漏洞利用给你一个“惊喜”。
攻防之道,讲完了“攻”,后续就开始讲如何去“防”,敬请期待。
—END—