这个东西大概我去年就在安装只不过因为种种苦难最后放弃了,现在想想其实当时已经成功了唉。
java这东西真的学一段时间往后一看蛮有趣的
E:\Tabby\tt\tabby-master\build\libs
E:\Tabby\tabby1.2.0\tabby
二个不同的版本
-
java -Xmx6g -jar .\tabby-1.1.1.RELEASE.jar
这是我tabby的安装位置,然后只需要更换jars文件中的jar包即可
` ` 运行命令即可,代码运行完运行neo4j查找即可
https://github.com/wh1t3p1g/tabby/blob/master/doc/Tabby%20%E9%A3%9F%E7%94%A8%E6%8C%87%E5%8C%97.md
在项目中如何导出jar包(通过构建工件)
本地是idea专业版不可用,需要用社区版
然后重新构建即可,然后把生成的jar包拖进jars目录即可
命令
:sysinfo,将能看到数据库的存储信息
:schema
match (source:Method {NAME:"execute"})
match (sink:Method {IS_SINK:true, NAME:"getInstance"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 20) yield path
return * limit 20
match()
表示匹配哪些节点,类似SQL中的select。
在 match (source:Method {NAME:"execute"})
这一句中,source
是别名,Method
是节点类型,{NAME:"execute"}
是匹配的节点内容。这些数据来自于Tabby生成的csv文件。
节点数据一般都是类JSON格式的。在Tabby中有两个重要的节点类型:Method
和Class
。
查询match (source:Method)return *
返回全图类的调用,但会很卡(无用)
查询所有方法名为excute
的语句:match(source:Method {NAME:"execute"}) return *
。
Neo4j图数据库还有一个可查询的数据:关系边,也就是上文模板中match (sink:Method {IS_SINK:true, NAME:"getInstance"})<-[:CALL]-(m1:Method)
这一句。同样是match()
方法,只不过后面多了<-[:CALL]-(m1:Method)
。这个是Neo4j中关系查询的语法。
Neo4j中有三种指向:() - [] -> ()
、() <- [] - ()
和() - [] - ()
。顾名思义,前两种就是看箭头方向表示对应的单项关系;最后一种表示双向关系。
左右两边表示查询的节点,在该例中,左边(sink:Method {IS_SINK:true, NAME:"getInstance"})
就是把方法名为getInstance
、并且预设规则匹配是SINK的节点查出来;右边(m1:Method)
表示查询所有的方法;中间表示关系。这里的关系是[:CALL]
调用关系;箭头方向是() <- [] - ()
,表示节点的指向是从右到左;全部结合起来看就是:在所有方法中,筛选getInstance
方法的调用者。
模板第三句是call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 20) yield path
。这是调用了之前给Neo4j安装的插件apoc
。apoc.algo.allSimplePaths
语法是:
apoc.algo.allSimplePaths(startNode :: NODE?, endNode :: NODE?, relationshipTypesAndDirections :: STRING?, maxNodes :: INTEGER?) :: (path :: PATH?)
该函数的主要作用是返回startNode
到endNode
之间的关系路径。该函数有四个输入参数:
startNode
- 开始节点,也就是SourceendNode
- 结束节点,也就是SinkrelationshipTypesAndDirections
- Neo4j的关系边,类型是字符串,可以设置为多个关系。查询时会按照这个关系进行路径图的查找maxNodes
- 搜索的最大节点数,也就是路径深度。注意千万不能设太大,尤其是节点很多的时候。
同时还有一个输出参数:
了解完每个语句的功能后,再看回模板可以知道:该模板将方法名为execute
的节点作为Source;将调用方法名为getInstance
、且在/rules/knowledges.json
设置过规则的节点调用者设置为Sink;最后调用allSimplePaths
搜索关系边为CALL
和ALIAS
的调用路径,返回20个结果。
match (source:Method {NAME:"execute"})
match (sink:Method {IS_SINK:true, NAME:"getInstance"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 20) yield path
return * limit 20
但这个查询语句在我们的这个程序里是查不到东西的,因为没有这两个方法的通路。我们可以构造这样的查询语句:查看Tomcat中doFilter()
到service()
的通路情况。
match (source:Method {NAME:"doFilter"})
match (sink:Method {NAME:"service"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 20) yield path
return * limit 20
match (source:Method {NAME:"readObject"})
match (sink:Method {NAME:"toString"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(m1, source, "<CALL", 20) yield path
return * limit 20
入门的demo(纯新手入门,可以跳过🤫🤫)
match(source:Method) where source.NAME ="readObject" return source limit 10;
match (source:Method {NAME:"readObject"}) return source limit 10;
上面的效果是一样的,但是上面只是单纯的找指定的方法,并没有一个通路
match path=(source:Method)-[:CALL]->(sink:Method)
where source.NAME=~"get.+" and sink.NAME="readObject"
return path
看着像是getter调用到readObject的一个通路查找,
match path=(source:Method)-[:CALL]->(sink:Method)
where source.NAME =~ "get.+" and sink.NAME="readObject"
return path
发现只有五个节点,简单分析一波
好神奇,SealedObject#getObject方法里面就有readObject
sun.awt.datatransfer#TransferableProxy
看了一下左边是右边接口的实现类,[:CALL]怪不得用CALL,因为图中就是这样写的
match (source:Method)-[:CALL|ALIAS*..3]->(sink:Method)
如果这个链子不知道有多长,可以用*..3
这种方式大概限制一下链的最大长度。
再分析一下ALIAS进行一个分析,证实一下上面的想法,证实成功,是接口指向了实现类
清库命令
MATCH(n) DETACH DELETE n
tabby子类或接口
match (source:Method {NAME:"nextElement"})
<-[:HAS]-(cls:Class)-[:INTERFACE|EXTENDS*]
->(cls1:Class {NAME:"java.util.Enumeration"})
match (source)-[:CALL]->(m1:Method)
source的函数是nextElement并且实现了 java.util.Enumeration接口
tabby实践学习
从java.util.HashMap#readObject————->toString方法的链子,下面是黑名单
match (source:Method {NAME:"readObject",CLASSNAME:"java.util.HashMap"})
match (sink:Method {NAME:"toString"})
with source,collect(sink) as sinks
call tabby.algo.findJavaGadget(source,sinks,12,false,false/true) yield path
where none(n in nodes(path) where n.CLASSNAME in ["javax.management.BadAttributeValueExpException","com.sun.jmx.snmp.SnmpEngineId","com.sun.xml.internal.ws.api.BindingID","javax.swing.text.html.HTML$UnknownTag"])
return path limit 1
规定了起始源点,污点是invoke方法
match (source:Method) where source.NAME in ["equals","hashCode","compareTo"]
with collect(source) as sources
match (sink:Method {IS_SINK:true}) where sink.NAME =~ "invoke" and sink.VUL =~ "CODE" and sink.CLASSNAME =~ "java.lang.reflect.Method"
with sources, collect(sink) as sinks
call tabby.algo.allSimplePaths(sinks,sources, 15, false) yield path
where none(n in nodes(path) where (n.CLASSNAME =~ "java.util.Iterator" or n.CLASSNAME =~ "java.util.Enumeration" or n.CLASSNAME =~ "java.util.Map" or n.CLASSNAME =~ "java.util.List" or n.CLASSNAME=~"jdk.nashorn.internal.ir.UnaryNode" or n.CLASSNAME=~"com.sun.jndi.ldap.ClientId" or n.CLASSNAME=~"org.apache.catalina.webresources.TrackedInputStream"))
return path limit 1
由于版本的差异,上面的需要转换成下面这样的格式
match (source:Method {NAME:"readObject"})
match (sink:Method {NAME:"toString"})<-[:CALL]-(m1:Method)
call apoc.algo.allSimplePaths(m1, source, "<CALL", 20) yield path
return * limit 20
1.目标方法需要为public 和 static的 2.sink可以为代码执行,写文件,远程类加载等
match (m1.Method) where m1.VUL="CODE" and m1.IS_STATIC=true and m1.IS_PUBLIC=true
return m1 limit 50
VUL就相当于漏洞的意思,此处时代码执行漏洞,就是CODE
无参数getter方法调用lookup
match path=(m1:Method)-[:CALL*..10]->(m2:Method {IS_SINK:true}) where m1.NAME =~ "get.*" and m1.PARAMETER_SIZE=0 and m2.VUL="JNDI" and m2.NAME="lookup"
return path limit 2
match path=(m1:Method)-[:CALL*..10]->(m2:Method) where m2.CLASSNAME="java.sql.DriverManager" and m2.NAME="getConnection" return path limit 2
Method
CLASSNAME: java.lang.Class
HAS_DEFAULT_CONSTRUCTOR: false 有无默认构造方法
HAS_PARAMETERS: true 构造方法中有参数
IS_ABSTRACT: false 是否是抽象 类
IS_ACTION_CONTAINS_SWAP: false
IS_GETTER: false 是否是getter方法
IS_PUBLIC: true
IS_SETTER: false
IS_SINK: true
IS_SOURCE: false
NAME: invoke
NAME0: sun.reflect.misc.MethodUtil.invoke
PARAMETER_SIZE: 3
SIGNATURE: <java.lang.Class: java.lang.Class forName(java.lang.String,boolean,java.lang.ClassLoader)>
SUB_SIGNATURE: java.lang.Class forName(java.lang.String,boolean,java.lang.ClassLoader)
上面就是调用方法的参数类型那些东西
VUL: CODE 漏洞类型,比如jndi就有JNDI这种
CALL
INVOKER_TYPE: InterfaceInvoke 没啥用,因为我看CALL的都是这个
REAL_CALL_TYPE: com.alibaba.fastjson2.reader.ObjectReader 被调用类的类型,比如 getter-->readobject那么这就是readobject的类型
通过上面二种规则,我们不就可以想调用什么就调用什么
参考文献:https://mp.weixin.qq.com/s/u7RuSmBHy76R7_PqL8WJww
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。