长亭百川云 - 文章详情

抽丝剥茧代码属性图CPG-第五弹:CPG中的函数摘要

CodeAnalyzer Ultra

56

2024-07-13

一、引言

CPG在进行数据流分析时,具备一定的过程间(interprocedurally)分析能力。但是,无论是哪款静态分析工具,其是无法对用户代码做预判的,也就是说,分析引擎在分析过程中很可能会遇到无法分析的方法(如:第三方库方法等)。若在数据流分析过程中,从source点(污染源)到sink点(爆发点)的传播路径中遇到了无法分析/识别的方法,那么数据流就会中断,从而造成漏报(False Nagitive)。因此,分析引擎为了避免这种情况的发生,就需要提供用户自定义分析的能力,这样就可以很大程度上解决这种漏报问题。

CPG在进行数据流分析时,也提供了这种自定义数据流分析的能力,也就是函数摘要(Function Summaries),用户可以通过配置文件的方式去自定义一些函数,使得数据流可以完整且连续。

读后有收获的同学可以点个赞哦,要是能关注一下公众号就更好啦大家的鼓励是笔者硬肝技术的动力!非常感谢大家

文中有错误的地方欢迎大家私信/评论区指正。同时也欢迎大家将自己的想法发布在评论区,希望大家能够畅所欲言,共同进步~

二、函数摘要配置方法

CPG提供了两种配置文件格式,JSON文件和YAML文件。

每一个函数摘要包含两个字段:functionDeclaration(函数声明)与dataFlows(对应的数据流)。

functionDeclaration字段

1.language

函数对应语言的全限定名(FNQ),注意是CPG项目内对该语言定义类的全限定名

可以对照上面的示例查看

2.methodName

函数的全限定名,注意别忘记添加类名

3.signature

方法参数列表(可选字段),该字段是为了处理方法重载的情况,如果不指定该字段,那么默认根据methodName匹配所有的方法

tips: 方法重载是指在同一个类中,方法名相同,但是方法的参数列表不同(参数类型/个数不同)

目前来看,CPG不支持方法重写的Function Summary,还是不太方便的,希望可以早日支持吧。

dataFlows字段

是一个数据流声明列表,可以对一个函数声明多条数据流

1.from

表示数据流(DFG)边的起点,其包含以下两个属性:

  • paramX: 参数位置,X从0开始

    如:param0表示第一个参数

  • base: 方法接收者(即调用方法的对象)

2.to

数据流的流向target,其包含以下四个属性:

  • paramX 参数位置,X从0开始

  • base 方法接收者(即调用方法的对象)

  • return方法返回值

  • returnX 返回值位置(针对多返回值的情况,Java不支持

3.dfgType

CPG提供的一个扩展字段,可以通过这个字段给DFG的边添加一些自定义的信息。目前CPG没有启用这个字段,后续会支持。

样例

  • JSON方式
`[     {       "functionDeclaration": {         "language": "de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage",         "methodName": "java.util.List.addAll",         "signature": ["int", "java.util.Object"]       },       "dataFlows": [         {           "from": "param1",           "to": "base",           "dfgType": "full"         }       ]     },     {       "functionDeclaration": {         "language": "de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage",         "methodName": "java.util.List.addAll",         "signature": ["java.util.Object"]       },       "dataFlows": [         {           "from": "param0",           "to": "base",           "dfgType": "full"         }       ]     },     {       "functionDeclaration": {         "language": "de.fraunhofer.aisec.cpg.frontends.cxx.CLanguage",         "methodName": "memcpy"       },       "dataFlows": [         {           "from": "param1",           "to": "param0",           "dfgType": "full"         }       ]     }   ]   `
  • YAML方式
`- functionDeclaration:       language: de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage       methodName: java.util.List.addAll       signature:         - int         - java.util.Object       dataFlows:         - from: param1           to: base           dfgType: full      - functionDeclaration:       language: de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage       methodName: java.util.List.addAll       signature:         - java.util.Object       dataFlows:         - from: param0           to: base           dfgType: full      - functionDeclaration:       language: de.fraunhofer.aisec.cpg.frontends.cxx.CLanguage       methodName: memcpy       dataFlows:         - from: param1           to: param0           dfgType: full   `

解释

JSON和YAML表示的是相同的实现,只是格式不同。

配置文件中配置了以下信息:

  • Java方法java.util.List.addAll(int, java.util.Object),数据流从第二个参数流向方法调用者(base)

  • Java方法java.util.List.addAll(java.util.Object),数据流从第一个参数流向方法调用者(base)

  • C方法 memcpy (也就是C++中的std::memcpy方法),数据流从第二个参数流向第一个参数

OK,目前为止,我们已经能看懂这种函数摘要配置文件了,那么问题来了,我们在开发引擎的过程中,**什么时候需要用到这种函数摘要配置呢?**以下将给出实际的代码示例,便于大家理解。

tips: 以下示例不一定是真实代码场景,但是可以说明现在的问题就OK

代码场景示例

  • 示例一

Java后端代码中可能有如下场景(有SQL注入风险):

`@Override   protected void doGet(HttpServletRequest req, HttpServletResponse res) {       // 用户请求中的数据认为是有风险的,所以被认为是污染源       String username = req.getParameter("name"); // tainted source       // 污染源被拼接到sql语句中       String sql = "select * from users where username = " + username; // add username to sql       // 模拟sql语句执行,被污染的sql被执行了,所以是被认为是爆发点       executeSql(sql); // sink   }   `

上面的示例很简单,有点web基础的应该都能读懂,正常的数据流分析也是可以分析出这种简单的代码场景的。但是假如说有以下代码:

  • 示例二

为了避免混淆,假设已经配置了Arrays.asList()list.get()方法的数据流摘要

`@Override   protected void doGet(HttpServletRequest req, HttpServletResponse res) {       List<Stirng> list = new ArrayList<>();       String username = req.getParameter("name"); // tainted source       List<String> list2 = Arrays.asList(username); // 已配置Arrays.asList()方法的摘要       list.addAll(list2);       String sql = "select * from users where username = " + list.get(0); // 此处涉及域敏感(大家先忽略哈)       executeSql(sql); // sink   }   `

显然,示例二也是有SQL注入风险的,如果我们现在的分析引擎可以分析出示例一的SQL注入,但是分析不出示例二的SQL注入,那么我们就可以通过配置函数摘要的方式去解决这种case(这只是一种解决方式,也可以通过对容器建模来解决)。

上面的配置文件中配置了java.util.List.addAll(java.util.Object)方法的第一个参数到方法调用者的一条数据流,也就是说,当被污染的数据username流到list.addAll(list2)这条语句时,数据流可以从list2流到list,从而使得 source --> sink 的整条数据流完整,也就避免了漏报的发生。

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2