tabby的安装及简单使用

Posted by Azeril on November 24, 2023
这个东西大概我去年就在安装只不过因为种种苦难最后放弃了现在想想其实当时已经成功了唉
java这东西真的学一段时间往后一看蛮有趣的

E:\Tabby\tt\tabby-master\build\libs

E:\Tabby\tabby1.2.0\tabby 二个不同的版本

  1. java -Xmx6g -jar .\tabby-1.1.1.RELEASE.jar
    
这是我tabby的安装位置然后只需要更换jars文件中的jar包即可

image-20231124195048917

` ` 运行命令即可,代码运行完运行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专业版不可用,需要用社区版

image-20231124200936587

然后重新构建即可,然后把生成的jar包拖进jars目录即可

image-20231124200950049

命令

: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中有两个重要的节点类型:MethodClass

查询match (source:Method)return *返回全图类的调用,但会很卡(无用)

查询所有方法名为excute的语句:match(source:Method {NAME:"execute"}) return *

image-20231124202605021

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安装的插件apocapoc.algo.allSimplePaths语法是:

apoc.algo.allSimplePaths(startNode :: NODE?, endNode :: NODE?, relationshipTypesAndDirections :: STRING?, maxNodes :: INTEGER?) :: (path :: PATH?)

该函数的主要作用是返回startNodeendNode之间的关系路径。该函数有四个输入参数:

  • startNode - 开始节点,也就是Source
  • endNode - 结束节点,也就是Sink
  • relationshipTypesAndDirections - Neo4j的关系边,类型是字符串,可以设置为多个关系。查询时会按照这个关系进行路径图的查找
  • maxNodes - 搜索的最大节点数,也就是路径深度。注意千万不能设太大,尤其是节点很多的时候。

同时还有一个输出参数:

  • path - 没看到文档有特别说明233,看到文档中给的都是YIELD path, weight。拿就照着文档来吧

了解完每个语句的功能后,再看回模板可以知道:该模板将方法名为execute的节点作为Source;将调用方法名为getInstance、且在/rules/knowledges.json设置过规则的节点调用者设置为Sink;最后调用allSimplePaths搜索关系边为CALLALIAS的调用路径,返回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

发现只有五个节点,简单分析一波

image-20231124230042489

好神奇,SealedObject#getObject方法里面就有readObject

image-20231124230246649

sun.awt.datatransfer#TransferableProxy

image-20231124230701106

image-20231124232255539

看了一下左边是右边接口的实现类,[:CALL]怪不得用CALL,因为图中就是这样写的

match (source:Method)-[:CALL|ALIAS*..3]->(sink:Method)

如果这个链子不知道有多长,可以用*..3这种方式大概限制一下链的最大长度。

再分析一下ALIAS进行一个分析,证实一下上面的想法,证实成功,是接口指向了实现类

image-20231124232650396

清库命令

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

image-20231206115003901

无参数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


Creative Commons License
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。