如需转载请注明出处,侵权必究。
论文题目:Scaling JavaScript Abstract Interpretation to Detect and Exploit Node.js Taint-style Vulnerability
发表会议:S&P 23
本文第一作者是来自Johns Hopkins University的Mingqing Kang,师从Yinzhi Cao教授。课题组主要研究如何使用程序分析技术解决Web、智能手机以及机器学习领域的安全和隐私问题。
概述
贡献
1. 提出一种自底向上和自顶向下两阶段的抽象解释方法,以解决漏洞检测精度和规模化难以平衡的问题。
2. 实现开源静态分析工具原型 FAST,以检测污点类型漏洞。
3. 实验表明FAST在减少漏报方面显著优于现有工具。
背景
污点类型漏洞
污点类型漏洞是一类比较常见的漏洞,此类漏洞一般是由于外部控制的输入数据在未经过滤的情况下到达敏感函数导致的。这类漏洞最终会造成服务劫持、信息泄露等安全问题。常见的污点类型漏洞包括:
-
外部输入注入OS命令造成的命令注入漏洞。
-
外部输入注入的未授权资源路径造成的路径穿越漏洞。
-
外部输入注入能够执行的代码造成的任意代码执行漏洞。
JavaScript 等语言的动态特性
对于污点类型漏洞的自动化检测,一个直接的想法是使用传统的静态分析方法,去构建控制流图与数据流图,但是由于JavaScript是一种基于原型(prototype)的脚本语言,具有大量的动态特性。这些动态特性使得如果不提供运行时上下文信息,那么大量的调用边是无法解析的。也就是说,仅使用语法导向的静态分析会使得构建出来的调用图不准确,因此需要针对JavaScript等语言的动态特性定制解决方案。
现有工作的局限性
为了解决语法导向的静态分析精度不高的问题,相关工作提出了抽象解释的思路,即以图或者矩阵的形式存储分析所需的上下文信息,用于调用边的解析。理论上这是能够解决调用边的解析问题,但是由于目前工作在分析时会尝试探索所有可能的程序状态,这也带来了路径爆炸的问题。
挑战
Insight
工作流程
此阶段的目的是找到sink点和source点之间的路径。具体来说,方法会忽略函数的调用序列,而仅关注函数的作用域(scope),也就是只构建函数内的控制流图。例如对于如下函数compress,其控制流示意图如下所示:
然后,方法构建Function Dependency Graph(FDG)以标注所有函数的依赖关系,此步骤是为了解决JS的函数指针和动态特性导致的调用链缺失问题。例如对于下图中promisify函数的参数fn,在静态分析时只能确定其函数名,而无法确定具体调用的是什么函数(需要等待execProcess函数里childProcess[method]部分解析完成后才能够确定)。因此为了不丢失函数之间的调用关系,需要先在函数依赖图中标记promisify和fn之间存在的依赖关系,以便于后续fn函数的具体指向解析完成后构建完整的控制流图。
作者定义函数依赖图包含的四种依赖关系:Lookup Dependency(由闭包或外部作用域中的函数指针查找引起的依赖关系)、Callback Dependency(由回调函数引起的依赖关系)、Return Dependency(由另一个函数返回的函数调用引起的依赖关系)、Promise Dependency(由Promise引起的依赖关系)。下图是上图代码片段中针对Promise函数生成的函数依赖图:
接下来,在函数依赖图的基础上构建函数间的控制流图。控制流图的构建主要考虑三种类型的表达式:1. 函数调用:直接可解析函数调用;无法直接解析的函数调用(需要借助FDG);Return相关的函数调用和回调相关的函数调用。2. 函数定义:回调、返回函数和函数表达式类型的函数定义。3. Promise相关语句。最终根据控制流图生成source到sink的控制流路径。整体流程图如下:
阶段二:数据流路径生成
此阶段的目的是构建出数据流图,并验证漏洞存在。
此处首先采用过程内后向切片技术,来为每个函数生成函数内的数据流图。具体来说,根据上一阶段得到的函数间控制流关系,就可以从到sink点的中间函数出发,反向找出仅与sink点相关的语句,对其他无关语句剪枝,然后在数据流图上构建数据边,如下所示:
然后,FAST基于控制流路径和程序后向切片的结果,对裁剪后的程序语句子集进行抽象解释,即自顶向下的抽象解释。此过程包括两个步骤,首先是object级别的数据流生成,FAST在object之间创建数据流,为数据流图构造数据边。第二步是路径敏感信息的收集,当FAST对某个分支的代码进行抽象解释时,会传入当前的路径敏感信息到当前的分支栈中,当抽象解释完成时,再从分支栈里pop出相应的路径敏感对象。
最后进行数据流搜索与漏洞检测。首先FAST会通过模式匹配和搜索所有预定义的函数来找到source点。其次,FAST采用深度优先搜索来找到source到sink的数据流路径,并限制每个语句的访问次数以避免循环。
程序内数据流图的例子如下所示:
阶段三:利用代码生成
实验评估
针对给定100k Node.js软件包的数据集,FAST-det以最低的FP和FN优于所有SOTA方法。其中,FAST-det在规模化方面的提升使得它优于现有的抽象解释方法(即ODGen)。此外,由于抽象解释能够解决JavaScript动态特性难以处理的问题,因此FAST-det优于现有的语法导向方法(例如CodeQL)。
FAST漏报的主要原因是存在一些未建模的source与sink,导致数据流丢失。相比之下,ODGen的漏报主要是由于代码覆盖率较低,即在分析期间可能无法到达某些漏洞点。CodeQL的漏报原因则是无法处理动态JavaScript功能,例如与括号语法相关的函数调用。而FAST-det误报主要原因是许多应用程序有控制流和数据流的sanitizations,这使得部分检测的漏洞实际上是无法利用的。
而另一方面,FAST-exp的误报率为零,但漏报率相对较高。这是因为Z3求解器在有限时间内无法求解约束条件。但是作者提出这部分复杂的漏洞利用代码可以由人工构造。误报与漏报的实验结果如下所示:
RQ3:Scalability
对于大规模项目(>10K LoC)的实验测试数据集,FAST能够检测14个漏洞中的10个,甚至在拥有近200K行代码的strapi中也能够执行分析检测。相比之下,ODGen在分析漏洞一天后依然无法检测到漏洞(甚至会崩溃)。但是作者发现FAST漏报了其中三个漏洞,而漏报的原因是某些source和sink建模失败。
RQ4:Call Edges构建的FN与FP
在这个实验中,作者比较FAST和现有方法(即抽象解释方法ODGen和语法导向方法JS Call Graph的开源实现)生成调用边的情况。
在漏洞数据集中,作者运行以上三种工具,来生成调用边,然后比较三种方法生成的结果,并手动检查三种方法生成调用边的正确性。检查所有调用边大约需要一个研究生230个小时。下面列出FAST调用边误报和漏报的原因:
FN:无法为未知对象(例如函数参数没有标准定义)构造调用边,以及建模嵌入式三元运算符的时候存在某些工程错误。
FP:无法识别回调函数是同步还是异步的,FAST会默认认为是同步的,导致调用图构建不精确。
调用图构建误报和漏报的实验结果如下所示:
总结
本文提出了一种新颖的两阶段抽象解释方法FAST,用于检测和利用Node.js污点类型漏洞。FAST第一阶段(自下而上的抽象解释)生成source和sink之间的控制流路径。然后,第二阶段(自上而下的抽象解释)沿着控制流路径分析只与sink具有控制或者数据依赖关系的语句。与现有的抽象解释方法相比,此种剪枝分析显著缓解状态爆炸问题,并且提升了抽象分析方法的规模化能力。在两个阶段之后,FAST遍历sink相关的控制流路径,收集和求解数据和控制流约束,自动生成漏洞利用exploit。实验评估结果显示,FAST在减少误报和检测漏洞方面优于所有现有的方法。
文案:WHT、LFY
审核:边顾、HG
排版:边顾
戳“阅读原文”即可查看论文原文哦~
复旦白泽战队
一个有情怀的安全团队
还没有关注复旦白泽战队?
公众号、知乎、微博搜索:复旦白泽战队也能找到我们哦~
原文始发于微信公众号(复旦白泽战队):白泽带你读论文|FAST
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论