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

参考:使用太阿(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;@Overridepublic void setSolver(Solver solver) {this.solver=solver;}@Overridepublic void onStart() {//add all hsd mapping annotation methods to entrypointList<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.mzph.cn/web/14817.shtml

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

相关文章

OpenHarmony轻松玩转GIF数据渲染

OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;提供了Image组件支持GIF动图的播放&#xff0c;但是缺乏扩展能力&#xff0c;不支持播放控制等。今天介绍一款三方库——ohos-gif-drawable三方组件&#xff0c;带大家一起玩转GIF的数据渲染&#xff0c;搞…

二手车经营效率罗盘,用经营效率罗盘玩转二手车生意

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89292198 更多资源下载&#xff1a;关注我。 带你了解不一样的二手车圈让二手车经营更高效 课程介绍 随着时代不断发展&#xff0c;二手车行业迎来了快速变革期。二手车有着一车一况、一车一价非标和价…

无人机+飞行服务:无人机飞防服务(打药+施肥+播种)技术详解

无人机飞防服务&#xff0c;结合了先进的无人机技术与农业实践&#xff0c;为现代农业提供了高效、精准的打药、施肥和播种解决方案。以下是对这些技术的详细解析&#xff1a; 一、无人机打药技术 无人机打药技术利用无人机搭载喷雾设备&#xff0c;对农田进行精准施药。通过…

【Crypto】看我回旋踢

文章目录 一、看我回旋踢二、知识点什么是ROT13&#xff1f;工作原理分析字符串格式 解题感悟 一、看我回旋踢 关键词回旋&#xff0c;盲猜ROT13 因为以 synt{ 开头&#xff0c;并以 } 结束&#xff0c;基本可以判断是ROT13 小小flag&#xff0c;拿下&#xff01; 二、知识点 …

抖音极速版:抖音轻量精简版本,新人享大福利

和快手一样&#xff0c;抖音也有自己的极速版&#xff0c;可视作抖音的轻量精简版&#xff0c;更专注于刷视频看广告赚钱&#xff0c;收益比抖音要高&#xff0c;可玩性更佳。 抖音极速版简介 抖音极速版是一个提供短视频创业和收益任务的平台&#xff0c;用户可以通过观看广…

【机器学习聚类算法实战-5】机器学习聚类算法之DBSCAN聚类、K均值聚类算法、分层聚类和不同度量的聚集聚类实例分析

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

保障餐饮场所安全:定期送检可燃气体报警器

在餐饮行业&#xff0c;火灾隐患一直备受关注。餐厅、茶饮店等场所常常使用燃气设备&#xff0c;而这些设备带来了潜在的安全隐患。 为了及时发现并预防可燃气体泄漏&#xff0c;可燃气体报警器的定期送检显得尤为重要。那么&#xff0c;为什么可燃气体报警器需要定期送检呢&a…

【Linux】从零开始认识进程间通信 —— 管道

送给大家一句话&#xff1a; 人要成长&#xff0c;必有原因&#xff0c;背后的努力与积累一定数倍于普通人。所以&#xff0c;关键还在于自己。 – 杨绛 从零开始认识进程间通信 1 为什么要进程间通信2 进程如何通信3 进程通信的常见方式4 管道4.1 什么是管道4.2 管道通信的系…

交叉编译程序,提示 incomplete type “struct sigaction“ is not allowed

问题描述 incomplete type "struct sigaction" is not allowed解决办法 在代码的最顶端添加如下代码即可 #define _XOPEN_SOURCE此定义不是简单的宏定义&#xff0c;是使程序符合系统环境的不可缺少的部分 _XOPEN_SOURCE为了实现XPG&#xff1a;The X/Open Porta…

零一万物Yi-1.5开源,34B/9B/6B多尺寸,34B超Qwen1.5-72B

前言 近年来&#xff0c;大型语言模型&#xff08;LLM&#xff09;在各个领域展现出惊人的能力&#xff0c;为人们的生活和工作带来了巨大的改变。然而&#xff0c;大多数开源 LLM 的性能仍然无法与闭源模型相媲美&#xff0c;这限制了 LLM 在科研和商业领域的进一步应用。为了…

element-plus表格的表单校验如何实现,重点在model和prop

文章目录 vue&#xff1a;3.x element-plus&#xff1a;2.7.3 重点&#xff1a; 1) tableData放到form对象里 2) form-item的prop要写成tableData.序号.属性 <!--table-表单校验--> <template><el-form ref"forms" :model"form"><e…

安装mpi4py与dlio_profiler_py的总结

安装mpi4py mpi4py是一个Python库&#xff0c;它提供了与MPI&#xff08;Message Passing Interface&#xff09;兼容的接口&#xff0c;使得Python程序能够利用MPI实现并行计算。mpi4py 的核心是基于MPI标准的C/C实现&#xff0c;它能够在高性能计算环境下进行高效的并行处理…

软考之信息系统管理知识点(3)

流水线&#xff1a;是指在程序执行时多条指令重叠进行操作的一种准并行处理实现技术。各种部件同时处理是针对不同指令而言的&#xff0c;它们可同时为多条指令的不同部分进行工作&#xff0c;以提高各部件的利用率和指令的平均执行速度。 编译得过程 关系数据库是表的集合 …

【全网最全】2024电工杯数学建模A题前两问完整解答+21页初步参考论文+py代码+保奖思路等(后续会更新成品论文)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片链接&#xff0c;那是获取资料的入口&#xff01; 【全网最全】2024电工杯数学建模A题前两问完整解答21页初步参考论文py代码保奖思路等&#xff08;后续会更新成品论文&#xff09;「首先来看看目前已有…

力扣:92. 反转链表 II(Java)

目录 题目描述&#xff1a;示例 1&#xff1a;示例 2&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的…

网络采集受限?如何解决指纹识别、IP封禁、验证码、账号多登等问题

网页采集是什么 网页采集&#xff0c;也常被称作网络采集、网络数据抓取&#xff0c;是一种通过自动化工具从网站上获取信息的技术。这些技术通过访问网页&#xff0c;解析页面上的内容&#xff0c;并提取出有价值的数据&#xff0c;如文本、图片、链接等。 网页采集通常用于…

Python 调整PDF文件的页面大小

在处理PDF文件时&#xff0c;我们可能会遇到这样的情况&#xff1a;原始PDF文档不符合我们的阅读习惯&#xff0c;或者需要适配不同显示设备等。这时&#xff0c;我们就需要及时调整PDF文档中的页面尺寸&#xff0c;以满足不同应用场景的需求。 利用Python语言的高效性和灵活性…

Linux--网络通信(一)概述

网络通信概述 网络通信本质上是一种进程间通信&#xff0c;是位于网络中不同主机上的进程之间的通信&#xff0c;属于 IPC 的一种&#xff0c; 通常称为 socket IPC。所以网络通信是为了解决在网络环境中&#xff0c;不同主机上的应用程序之间的通信问题。 大概可以分为三个层…

23种设计模式之一————外观模式详细介绍与讲解

外观模式详细讲解 一、概念二、 外观模式结构核心思想及解释模式的UML类图模式角色应用场景模式优点模式缺点 三、实例演示图示代码展示运行结果 一、概念 外观模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它提供了一个统一的接口&#xff0c…

【问题解决】Android Studio Jellyfish新建Kotlin项目后Gradle Sync及Maven下载很慢

创建新项目之后&#xff0c;Gradle Sync和Build都很慢&#xff0c;因为下载Gradle和Maven等工具。 代码默认配置 settings.gradle.kts pluginManagement {repositories {google {content {includeGroupByRegex("com\\.android.*")includeGroupByRegex("com\\.g…