2

深入浅出Joern(二)CPG与图数据库

 1 year ago
source link: https://lorexxar.cn/2023/08/22/joern2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

深入浅出Joern(二)CPG与图数据库



字数统计: 1.9k阅读时长: 7 min
 2023/08/22  71  Share

在上篇文章里,我们从Joern入手大致介绍了CPG(Code Property Graph)的设计理念和简单逻辑

但实际上来说,如果想要更深入的了解Joern,CPG和图数据库是绕不开的一个话题。CPG作为一种代码属性图,就必须寻找一种图数据库作为载体,就像我们常用的数据和SQL数据库的关系一样。

旧版本的Joern使用的Gremlin,但后来的开发中换成了OverflowDB,在joern中也完全支持使用OverflowDB的查询语法

但属性图本身没有什么特异性,比较常见的比如Neo4J,OrientDB或者JanesGraph都支持CPG的表现形式。

但,在这之前,我们首先需要知道,为什么是图?

为什么是图?

在上篇文章中,我在讲了CPG的设计思路时曾经提到过一些相关的内容。

如果说CFG(control flow graphs)相比AST来说最大的特点是带有明确数据流向的流向,在数据流分析可能更有优势。

那么CPG相比CFG来说有一个很大的特点就是信息量大,而图最大的特点也在于,就是可以容纳信息量巨大的内容

假设我们有这样一段代码

a = new A()
b = a.b
c.a = b.a
d.a = c
c.b = d.c

这里简单的几行代码,其实展示了相当复杂的依赖链,abcd几个变量中有着复杂的互相指向关系,如果用文字来表示abcd之间的关系我们可能需要拆分很多部分。

a -> A()
b -> A().b
c.a -> A().b.a
c.b -> ....

我甚至很难用文字的方式表达出他们之间的关系,而图在这样的场景下就变得很有优势。

当然这只是一个粗浅的例子,但已经很明显的能感觉出来图和文字之间的差距了,图关系可以很轻松的表达出文字很难表达出来的信息量。

Joern与图

Joern用了CPG来储存代码的所有节点关系和属性数据,由于CPG的信息量大,所以Joern甚至提供了官方的生成AST、CFG等其他结构的接口,对于C/C++甚至支持多种自定义的结构。

  • Abstract Syntax Trees (AST)
  • Control Flow Graphs (CFG)
  • Control Dependence Graphs (CDG)
  • Data Dependence Graphs (DDG)
  • Program Dependence graphs (PDG)
  • Code Property Graphs (CPG14)
  • Entire graph, i.e. convert to a different graph format (ALL)

在Joern的命令行你可以直接使用相应的命令生成对应的格式

cpg.method($name).dotAst.l // output AST in dot format
cpg.method($name).dotCfg.l // output CFG in dot format
...
cpg.method($name).dotCpg14.l // output CPG'14 in dot format

有个很有意思的是,如果你的电脑装了Graphviz,Joern还可以调用Graphviz来绘图,虽然生成的图很难看。

安装Graphviz之后我们可以通过命令来绘图

cpg.method($name).plotDotAst // plot AST
cpg.method($name).plotDotCfg // plot CFG
...
cpg.method($name).plotDotCpg14 // plot CPG'14

说实话,不太实用,但是很方便

Neo4J

相比Graphviz这种仅仅用来临时展示图的应用来说,Neo4J则是标准而且非常成熟的图数据库,不但性能强,而且还实用。

你可以在官网下载免费的neo4j,其中包括服务端和客户端版本,服务端版本启动后会默认跑到7474端口上。

Neo4j使用的查询语言叫做Cypher,这是一种声明式的图查询语言,我个人觉得Cypher其实算是比较反人类的一种语言,具体的语法可以看对应的文档。

简单来讲Cypher中对应SQL的语句关系有几个比较特别的,首先就是MATCH和where。

# SQL
select Person from user where born = 'beijing'
# Cypher
MATCH (a:Person)-[:BORN]->(b:Location {city:'beijing'}) RETURN a,b

MATCH和where在两种查询语句中是类似的功能,其中的区别就是MATCH匹配的是图中节点之间的关系。Cypher语法比较强调节点之间的关系,比如-就是无方向关系,->就是有方向关系。

match 
(node)-[relationship]->(node)
where
(node | relationship)
return
(node | relationship)

其他的比如创建节点、删除节点、创建关系、搜索匹配的节点以及关系等等就不赘述了,算是比较符合理解的语言逻辑。

而相对于普通的数据库来说,图数据库有着可能是一种优势的特性,就是可以直接通过Neo4j的浏览器直接操作图内容以及结构。

直接用鼠标点击各个节点查看对应的属性以及它们之间的关系,并且可以直接拖动他们。

点击节点下面的按钮,可以直接查看到节点连接到的其他节点,很方便也很直观。

Joern与Neo4J

前面说了,Joern使用了自己做的OverflowDBl来作为图数据库存储CPG,但CPG本身没有什么特异性,也就意味着他可以在任意一种图数据库上导入。

而Joern本身是自带了这个功能的,就是joern-export。它支持你导出Joern的CPG到neo4j , graphml, graphson 和 graphviz dot。

./joern-export --repr=all --format=neo4jcsv
./joern-export --repr=all --format=graphml
./joern-export --repr=all --format=graphson
./joern-export --repr=all --format=dot

要使用joern-export导出数据的话,需要指定CPG的位置,这个东西会存在Joern目录下的workspace当中,并且需要指定output,默认是./out。

然后我们可以想办法把这些csv文件导入到Neo4j当中。当然你可以用一些自己的方式导入,但joern的这个图还挺麻烦的,主要是neo4j导入复杂结构数据需要指定好各种csv文件的关联。

但joern当然也给出了导入的办法,在生成文件的时候会给出一个导入命令的范例,照着范例就可以搞定了。

首先joern导入数据是有限制的,只能导入import目录下的文件,这个import文件一般会在对应链接的server目录下面,如果你使用的是neo4j的desltop浏览器,那么你可以直接打开对应的import目录,并把文件复制过去。

除了文件以外,还有就是这个/bin/cypher-shell的位置,这个脚本就在对应链接目录的bin下

然后构造对应的find命令生成执行导入即可,其实它的原理也比较简单,就是依次执行*_cypher.csv文件中的命令,然后导入header和data。

最终导入的数据就是这样的

用cypher在Neo4J上查询漏洞

当我们把CPG导入到Neo4J上之后,理论上来说我们可以用cypher来完成我们在Joern中做的所有工作。

这里还是拿上篇文章中用到的RCE代码来举例子。

对应Joern的语句为

def source = cpg.method.where(_.annotation.name(".*Mapping")).parameter

def sink = cpg.call.name("exec")

首先匹配注解节点满足.*Mapping的

MATCH (n:ANNOTATION) where n.NAME=~".*Mapping" RETURN n LIMIT 25

然后找这些对应节点关联的方法

MATCH (m:METHOD)-[:AST]->(n:ANNOTATION) where n.NAME=~".*Mapping" RETURN n LIMIT 25

然后找一下对应调用exec方法的节点

MATCH (n:CALL) where n.NAME="exec" RETURN n LIMIT 25

然后我们把两个节点连接起来,并查找最短路径,这里的[*..10]表示最长不超过10个关系

MATCH (p1:METHOD)-[:AST]->(n:ANNOTATION),(p2:CALL),p=shortestpath((p1)-[*..10]-(p2)) where n.NAME=~".*Mapping" and p2.NAME="exec" RETURN p LIMIT 25

这里范例算是比较简单的,所以用这个还算比较简单的语句就可以查询到结果,正好对应漏洞利用链。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK