【静态分析】在springboot使用太阿(Tai-e)01

news2025/1/4 19:39:55

参考:使用太阿(Tai-e)进行静态代码安全分析(spring-boot篇一) - 先知社区

----------------------------------------------------------------------

 由于spring-boot实现了控制反转与面向切面编程的设计思想,使得程序并非顺序执行,因此很难通过程序入口来顺序分析所有代码。本篇文章旨在从0开始,利用Tai-e来分析spring-boot程序,解决控制反转的问题。

1. 从0开始配置tai-e并加载java-sec-code

1.1 下载tai-e代码

创建文件夹并进入

git clone https://github.com/pascal-lab/Tai-e.git

git init

git submodule update --init --recursive

1.2 IDEA配置tai-e

其实就是按照官方文档进行配置IDEA
在打开tai-e程序后等待一会

Tai-e加载完之后

设置

File->Project Structure,将SDK与Language level设置为17

File->Settings

然后将Build and run using与Run tests using改为idea

1.3 编译java-sec-code

下载java-sec-code源码

git clone https://github.com/JoyChou93/java-sec-code.git

cd java-sec-code

mvn clean package -X

注意:若出现'mvn'不是内部或外部命令,也不是可运行的程序或批处理文件。

则首先确认自己是否安装了maven

若已经安装,则

  • 添加Maven到环境变量

    • 打开系统的环境变量设置,找到Path变量,确保Maven的bin目录路径已经添加到Path变量中。
  • 重新打开命令行执行

等待

看到Build success代表编译成功

1.4 配置tai-e加载java-sec-code

首先创建common目录和options和taint-config文件。作为我们自己的配置

options文件内容,注意需要修改appClassPath也就是我们刚才编译的java-sec-code的target。

optionsFile: null
printHelp: false
classPath: []
appClassPath:
  - D:\software\java-sec-code\java-sec-code\target\classes
mainClass:
inputClasses: []
javaVersion: 8
prependJVM: false
allowPhantom: true
worldBuilderClass: pascal.taie.frontend.soot.SootWorldBuilder
outputDir: output
preBuildIR: false
worldCacheMode: true
scope: REACHABLE
nativeModel: true
planFile: null
analyses:
  #  ir-dumper: ;
  pta: cs:ci;implicit-entries:true;distinguish-string-constants:null;reflection-inference:solar;merge-string-objects:false;merge-string-builders:false;merge-exception-objects:false;taint-config:config/common/taint-config.yml;
onlyGenPlan: false
keepResult:
  - $KEEP-ALL

taint-config文件内容,这个文件暂时不需要修改,就是加了一个sources和sinks点以及transfers。

sources:
#  - { kind: param, method: "<org.joychou.controller.SQLI: java.lang.String jdbc_sqli_sec(java.lang.String)>", index: 0}
  - { kind: param, method: "<org.joychou.controller.SQLI: java.lang.String jdbc_sqli_vul(java.lang.String)>", index: 0}
#  - { kind: param, method: "<org.joychou.controller.SpEL: void main(java.lang.String[])>", index: 0}
#  - {kind: param, method: "<org.joychou.controller.Rce: java.lang.String CommandExec(java.lang.String)>",index: 0}
sinks:
  ## SQLI
  - { vuln: "SQL Injection", level: 4, method: "<java.sql.Statement: java.sql.ResultSet executeQuery(java.lang.String)>", index: 0 }
  - { vuln: "SQL Injection", level: 4, method: "<java.sql.Connection: java.sql.PreparedStatement prepareStatement(java.lang.String)>", index: 0 }
transfers:
  - { method: "<java.lang.String: java.lang.String concat(java.lang.String)>", from: base, to: result }
  - { method: "<java.lang.String: java.lang.String concat(java.lang.String)>", from: 0, to: result }
  - { method: "<java.lang.String: char[] toCharArray()>", from: base, to: result }
  - { method: "<java.lang.String: void <init>(char[])>", from: 0, to: base }
  - { method: "<java.lang.String: void getChars(int,int,char[],int)>", from: base, to: 2 }
  - { method: "<java.lang.String: java.lang.String format(java.lang.String,java.lang.Object[])>", from: "1[*]", to: result }
  - { method: "<java.lang.StringBuffer: void <init>(java.lang.String)>", from: 0, to: base }
  - { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>", from: 0, to: base }
  - { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>", from: 0, to: result }
  - { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>", from: base, to: result }
  - { method: "<java.lang.StringBuffer: java.lang.String toString()>", from: base, to: result }
  - { method: "<java.lang.StringBuilder: void <init>(java.lang.String)>", from: 0, to: base }
  - { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: 0, to: base }
  - { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: 0, to: result }
  - { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: base, to: result }
  - { method: "<java.lang.StringBuilder: java.lang.String toString()>", from: base, to: result }

call-site-mode: true

配置Main函数参数

现在可以运行了,但是你会发现没有taint-flow的结果。
主要的问题是tai-e的taint-analysis是基于pointer-anysis的。而pointer-analysis的分析是基于worklist,这个worklist如果是java se程序初始化就是main函数,可以通过main函数进行分析,进行函数调用处理,从而reach到需要分析的函数。由于我们的是springboot程序包含依赖注入和控制反转等,所以从springboot的入口tai-e没办法分析到我们的controller函数,所以pointer-anlaysis的结果也就是空的,导致taint-anlaysis结果也是空的。

经过分析发现tai-e分析SpringBoot项目存在2个问题。
1. 缺少entrypoint,因为pointerAnalysis分析没办法reach到我们需要分析的controller方法。
2. 缺少source,我们暂时没有办法通过yml将所有Mapping注解的parameters加入sources,我们上边yml仅加入了一个source。
需要解决以上两个问题,才能进行基础的分析。

2. 关键源码分析及实现

2.1 PointerAnaysis分析及入口点添加

由于tai-e的插件是集成在PointerAnalysis,包括污点分析也是,也就是说PointerAnalysis就是这些分析的核心。所以在写如何编写插件前我们先大概分析一下PointerAnlaysis的执行流程。

2.1.1 PointerAnaysis执行流程

我们先看PointerAnalysisrunAnalysis方法,这个其实不算是入口点,analyze才是,但是analyze仅根据配置然后调用runAnalysis,所以我们直接分析这个就好。

DefalutSolver实现指针分析以及插件的调用,并返回指针分析的结果(core)。

Plugin即一个插件,可以通过编写插件的方式,在进行指针分析的过程中执行我们想要的操作(污点分析也是类似的操作)。

接下来看一下setPlugin方法,目前先不用修改它。

接下来分析DefaultSolver的solve方法。

solve方法会首先调用 initialize 方法,然后执行analyze()方法

initialize方法:
● 初始化调用图、指针流向图 、worklist(可以理解为指针分析的工作集)、reachableMethods(也就是分析能到达的方法)、initializedClasses(可以理解为内置的类,会进行指针分析,不需要我们添加)、stmtProcessor(这个类是指针分析处理语句的核心,根据访问者模式处理对行的语句(new、load、invoke、array等))
● 然后会执行插件的onStart()方法

analyze方法

  • 通过消费worklist中的Entry,对Entry进行指针分析的处理,然后调用Plugin的onNewPointsToSet方法(本篇文章用不到该方法,该方法在处理field和array source时会用到)
  • 在处理worklist后调用Plugin.onFinish()方法

2.1.2 分析EntryPointerHandler

通过对PointerAnalysis执行流程的分析应该能大概了解Plugin的执行顺序了。
看一下Plugin的interface ,有一系列plugin的方法。

  • 插件方法执行流程
    loadPlugin(pointerAnalysis加载插件)->setSolver->onStart->指针分析以及一些plugin方法->onFinish
  • 从上边我们可以理解编写Plugin的流程,选择我们需要的方法比如onStart 、onFinish
  • 在PointerAnalysis加入编写的插件。

看一下tai-e自带的EntryPointHandler它就是在onStart处判断加入口点。

2.1.3 添加EntryPoint

这样加入我们自己入口点的方法也有了,在onStart的时候把所有 有Mapping注解的方法都加入 入口点,这样pointeranalysis就能分析到我们的source点了,taintanalysis也会有结果。
下边的代码就是获取所有的class,然后获取class的method,若注解是
Mapping,则加入entrypoint

public class AddControllerEntryPointHandler implements Plugin {
    private  Solver solver;
    @Override
    public void setSolver(Solver solver) {
        this.solver=solver;
    }

    @Override
    public void onStart() {
        //add all hsd mapping annotation methods to entrypoint
        List<JClass> list = solver.getHierarchy().applicationClasses().toList();
        for (JClass jClass : list) {
           jClass.getDeclaredMethods().forEach(jMethod->{
               if (!jMethod.getAnnotations().stream().filter(
                       annotation -> annotation.getType().matches("org.springframework.web.bind.annotation.\\w+Mapping")
               ).toList().isEmpty()) {
                   solver.addEntryPoint(new EntryPoint(jMethod, EmptyParamProvider.get()));
               }
            });
        }
    }
}

然后在pointer Analysis直接加入该插件,然后执行程序

然后执行程序将dot转换为svg
dot -Tsvg -o taint-flow-graph.svg taint-flow-graph.dot

由于上边我们只加入了一个sources和sql的sink点,所以只能检测出来一个taintflow

2.2 SourceHandler 分析及springboot source 添加

污点分析的核心包括如下三个集成到指针分析的核心插件:
SourceHandler:添加代表污点的特殊堆抽象加入到符合source点规则的指针的指向集中
TransferHandler:将符合污点传播规则的边加入指针分析
SinkHandler:收集污点流,输出source->sink的污点流的集合

2.2.1 source类型

tai-e的污点分析支以下持三种source

sources:
  - { kind: call, method: "<javax.servlet.ServletRequestWrapper: java.lang.String getParameter(java.lang.String)>", index: result }
  - { kind: param, method: "<com.example.Controller: java.lang.String index(javax.servlet.http.HttpServletRequest)>", index: 0 }
  - { kind: field, field: "<SourceSink: java.lang.String info>" }

call sources
source点由调用点生成,若call site语句调用了特定方法,那么返回值就是source点。
paramater sources
对于特定方法,例如入口方法,没有特定的调用点。但其形参依然可能是source点。在spring-boot,通过注解来声明请求的处理入口,没有明确调用controller方法,在这种情况下,paramater sources非常有用。
field sources
对于字段类型的指针,其依然可能是source。

2.2.2 流程分析

从配置中获取source点的配置

当指针分析中产生新的语句或新的调用边时,若语句符合source的规则,则代表指针即为source点

当指针分析产生新的方法时,会处理其中的语句,也会在这个地方处理paramater sources

若我们想添加自定义的source点,便可以在handleParamSource、handleFieldSource、handleCallSource中添加。

2.2.3 添加source

我们的source仅加入了一个,但是一条一条加source也不太现实,没办法实现通用型。
通过分析找到2个比较适合加source点的地方,一个是处理source的开始,一个是处理source结束的地方。
1. SourceHandler处理ParamSource的地方

2. 在tai-e读取taint-config文件解析后添加source

我们这里就看第一种方法吧

代码如下

private void handleParamSource(CSMethod csMethod) {
        JMethod method = csMethod.getMethod();
        if (paramSources.containsKey(method)) {
            Context context = csMethod.getContext();
            IR ir = method.getIR();
            paramSources.get(method).forEach(source -> {
                IndexRef indexRef = source.indexRef();
                Var param = ir.getParam(indexRef.index());
                SourcePoint sourcePoint = new ParamSourcePoint(method, indexRef);
                Obj taint = manager.makeTaint(sourcePoint, source.type());
                switch (indexRef.kind()) {
                    case VAR -> solver.addVarPointsTo(context, param, taint);
                    case ARRAY, FIELD -> sourceInfos.put(
                            param, new SourceInfo(indexRef, taint));
                }
            });
        }else {
            if (!method.getAnnotations().stream().filter(
                    annotation -> annotation.getType().matches("org.springframework.web.bind.annotation.\\w+Mapping")
            ).toList().isEmpty()) {
                Context context = csMethod.getContext();
                IR ir = method.getIR();
                for (int i = 0; i < ir.getParams().size(); i++) {
                    Var param = ir.getParam(i);
                    SourcePoint sourcePoint = new ParamSourcePoint(method, new IndexRef(IndexRef.Kind.VAR, i, null));
                    Obj taint = manager.makeTaint(sourcePoint, param.getType());
                    solver.addVarPointsTo(context, param, taint);
                }
            }
        }
    }

然后执行程序,查看结果,可以看到source都添加成功了,并且新增了一个taintflow。

 3. 结果展示

lcark、keanu大佬实现更改的相关代码以及配置文件已经上传至github

具体食用方法如下:

1. 下载代码,并移动至spring-boot-1目录下

git clone https://github.com/lcark/Tai-e-demo
cd Tai-e-demo/spring-boot-1
git submodule update --init

2. 将java-sec-code文件夹移至与Tai-e-demo文件夹相同目录下

3. 将SpringBootHandler.java移动至Tai-e源码的src/main/java/pascal/taie/analysis/pta/plugin/taint/目录下,并重新编译打包

4. 使用如下命令运行tai-e便可以成功获取到扫描结果
java -cp xxx\tai-e-all-0.5.1-SNAPSHOT.jar pascal.taie.Main --options-file=options.yml

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1693208.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu 如何根据NVIDIA显卡型号确定对应的显卡驱动版本并安装

目录 一、查询推荐安装的驱动版本 二、安装推荐版本的驱动 1. 通过终端安装&#xff0c;只安装 nvidia 驱动&#xff08;亲测可用&#xff01;&#xff09; 2. 通过 software & Updates 安装&#xff0c;安装 nvidia 驱动。 三、查询能安装的最新的显卡驱动版本 1. 方…

微信好友这样打标签更高效!

为什么要做标签管理? ① 通过标签管理&#xff0c;可以清晰的知道每个私域好友的关系程度&#xff0c;如哪些是忠诚客户&#xff0c;哪些是意向客户&#xff0c;哪些是刚加上的客户等等。 这样就知道下一步要怎么操作&#xff0c;做到精细化运营。如忠诚客户跟进维护&#x…

逍遥模拟器安装xp时报错处理

在执行script.sh&#xff0c;无法执行程序&#xff0c;报错如下&#xff1a; Mounting /system and /vendor read-write /dev/block/sda6 is read-only 尝试了很多种的方法&#xff0c;都不行 经过研究发现是逍遥模拟器的设置问题&#xff1a; 出问题时&#xff0c;磁盘共享…

VBA批量合并带有图片、表格与文本框的Word

本文介绍基于VBA语言&#xff0c;对大量含有图片、文本框与表格的Word文档加以批量自动合并&#xff0c;并在每一次合并时添加分页符的方法。 在我们之前的文章基于Python中docx与docxcompose批量合并多个Word文档文件并逐一添加分页符&#xff08;https://blog.csdn.net/zhebu…

[读论文]精读Self-Attentive Sequential Recommendation

论文链接&#xff1a;https://arxiv.org/abs/1808.09781 其他解读文章&#xff1a;https://mp.weixin.qq.com/s/cRQi3FBi9OMdO7imK2Y4Ew 摘要 顺序动态是许多现代推荐系统的一个关键特征&#xff0c;这些系统试图根据用户最近执行的操作来捕获用户活动的“上下文”。为了捕捉…

【热门话题】CentOS 常见命令指南

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 CentOS 常见命令指南一、文件与目录操作1. 切换目录2. 查看当前目录3. 列出目录…

vue+iview tabs context-menu 弹出框怎么修改样式

今天遇到一个需求说页面顶部的菜单右键弹出框离得有点远 代码是这样 <Tabs type"card" closable class"main-tags-col-tabs" v-model"activeTab" on-click"handleClickTag" :before-remove"handleBeforeRemove" capt…

ArcGIS提取含有计曲线的等高线

喜欢就关注我们吧&#xff01; 今天我么来看看&#xff0c;如何利用DEM提取含有计曲线的等高线&#xff01; 常规的话我们利用DEM提取的等高线都是不带计曲线的&#xff0c;无法把计曲线标注出来&#xff0c;今天我们就来看下&#xff0c;如何处理一下哦&#xff01;提取带有计…

【狂神说Java】Redis笔记以及拓展

一、Redis 入门 Redis为什么单线程还这么快&#xff1f; 误区1&#xff1a;高性能的服务器一定是多线程的&#xff1f; 误区2&#xff1a;多线程&#xff08;CPU上下文会切换&#xff01;&#xff09;一定比单线程效率高&#xff01; 核心&#xff1a;Redis是将所有的数据放在内…

开视频号小店要花哪些钱?这些费用大家要知道

大家好&#xff0c;我是喷火龙。 目前&#xff0c;视频号小店从推出到现在已经快两年的时间了&#xff0c;视频号小店虽然门槛高&#xff0c;但是单价也高&#xff0c;利润也高&#xff0c;市场环境也好&#xff0c;算是一个不错的项目。 接下来给大家讲讲开视频号小店要花哪…

ensp-三层交换技术

交换机-三层交换 一.概述 单臂路由有明显的缺陷,单臂路由的链路使用率高,可能会造成网路拥塞,造成网络不可用 可以让多个交换机连接路由器的不同接口,但是路由器的接口毕竟有限,不像交换机一样有那么多接口 使用三层交换解决路由器接口不够用问题 二.三层交换 1.创建多个VLAN…

可重构柔性装配产线,为智能制造领域带来了新的革命性变革

随着科技的飞速发展&#xff0c;个性化需求逐渐成为市场的主导。在这个充满变革的时代&#xff0c;制造业正面临着前所未有的挑战和机遇。如何快速响应市场需求、提高生产效率、保证产品质量&#xff0c;成为每一家制造企业必须思考的问题。 在这样的背景下&#xff0c;富唯智…

【MATLAB源码-第214期】基于matlab的遗传算法GA最短路径路由优化算法仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 在现代网络通信和路径规划领域&#xff0c;最短路径路由优化算法是一项关键技术。它涉及在给定的网络拓扑中寻找从源点到目标点的最短或成本最低的路径。近年来&#xff0c;遗传算法&#xff08;GA&#xff09;因其出色的全局…

Python3 笔记:部分专有名词解释

1、python 英 /ˈpaɪθən/ 这个词在英文中的意思是蟒蛇。但据说Python的创始人Guido van Rossum&#xff08;吉多范罗苏姆&#xff09;选择Python这个名字的原因与蟒蛇毫无关系&#xff0c;只是因为他是“蒙提派森飞行马戏团&#xff08;Monty Python&#xff07;s Flying Ci…

2024.05.18学习记录

1、Vue3 Composition API Vite jsx 2、react 基本使用、高级用法 3、刷题&#xff1a;回溯部分剩下的题目

屎山代码SSM转换Springboot

SSM项目转Springboot项目 最近很多人可能是在网上买的那种屎山代码&#xff0c;数据库都是拼音的那种 比如项目如下所示&#xff1a; 这种屎山代码我改过太多了&#xff0c;很多人可能无从下手&#xff0c;因为代码结构太混乱了&#xff0c;但是我改过太多这种代码&#xff0…

用HAL库改写江科大的stm32入门例子-6-2 定时器外部时钟

实验目的&#xff1a; 熟悉外部时钟的应用。 实验步骤&#xff1a; 创建项目参照前面的文章&#xff0c;集成oled(没有oled,用uart串口传递也可以)选择外部时钟源时钟源参数设置编写代码&#xff1a; 5.1声明全局变量&#xff0c;如果发生定时器中断的时候&#xff0c;在回调…

从ZooKeeper切换到ClickHouse-Keeper,藏着怎样的秘密

本文字数&#xff1a;7772&#xff1b;估计阅读时间&#xff1a;20 分钟 作者&#xff1a;博睿数据 李骅宸&#xff08;太道&#xff09;& 小叮当 本文在公众号【ClickHouseInc】首发 本系列前两篇内容&#xff1a; 从ES到ClickHouse&#xff0c;Bonree ONE平台更轻更快&a…

操作抖音小店一直不出单怎么办?只需要做好这两点就可以了!

大家好&#xff0c;我是电商小V 最近很多新手小伙伴来咨询我说自己操作抖音小店&#xff0c;自己的店铺长时间不出单应该怎么办&#xff1f;今天咱们就来详细的说一下&#xff0c; 咱们要清楚的就是自己的店铺不出&#xff0c;只需要咱们做好这两点就可以了&#xff0c; 第一点…

七个很酷的GenAI LLM技术性面试问题

不同于互联网上随处可见的传统问题库&#xff0c;这些问题需要跳出常规思维。 大语言模型(LLM)在数据科学、生成式人工智能(GenAI)和人工智能领域越来越重要。这些复杂的算法提升了人类的技能&#xff0c;并在诸多行业中推动了效率和创新性的提升&#xff0c;成为企业保持竞争…