反序列化渗透与防御

php magic函数

image-20240820150445188
image-20240820151133690

要运行这个php代码,得把basic文件夹代码复制到phpstudy的www目录下,然后创建网站才可以从网页看到执行结果。

创建和删除对象都会自动调用magic函数。

运行结果如下

image-20240820155403210

php序列化和反序列化

序列化:把一个对象转化为一段字符串

反序列化:把字符串转化回对象

image-20240820155910134

序列化的作用

image-20240820170738080

把不同序列化成字符串的例子

由于网页运行结果比较杂乱,这里整理运行结果如下

当然也支持序列化成其他的格式

image-20240820161050296

把对象序列化成json字符串和xml字符串的例子。

把网站的运行结果进行整理如下,由于xml在网页端查看不出来,所以无法根据结果进行xml格式化显示。

有的对象可能具有敏感的私有属性不能被序列化,那么可以筛选出不能序列化的部分,比如下面例子中的password属性

运行结果如下,可以发现password私有属性没有被序列化

反序列化的例子,首先先创建一个对象,然后序列化。

运行结果入下图所示

通过序列化的内容进行反序列化成一个对象obj2。可以执行反序列化对象的方法。

==注意:也可对序列化的内容进行修改,来获得修改后反序列化的对象内容。==

image-20240820161629404

比如php版本7.4.0之前执行该段代码

运行结果可以发现是优先调用wakeup的函数。

image-20240820173047916

修改成7.4.0以后的php版本。

image-20240820173146554

反序列化漏洞的出现

image-20240820174957841

常见的写数据或者执行命令的相关函数

image-20240820175113804

将马士兵提供的base目录下的代码放入phpstudy的网站根目录下,然后创建网站。

image-20240820175936024
image-20240820180353316
image-20240820180548271

比如创建了index.php文件。

image-20240820181011582

首先先获取删除index.php文件对象的序列化结果。

image-20240820181052127

复制序列化结果用于传参来删除index.php文件。

image-20240820181230776

除了删除文件,还可以利用反序列化漏洞获取文件内容。

image-20240820192928653

生成一个readfile对象,把文件名属性改成要读取的文件。获得序列化字符串结果

image-20240820193154114
image-20240820193238832

通过param传参序列化结果,readfile.php文件中反序列化成对象后会输出这个对象,自然会调用tostring函数。

image-20240820193339919

ctf题目分析

CVE-2016-7124

CVE-2016-7124漏洞源于PHP在反序列化过程中未能正确处理序列化字符串中表示对象属性个数的值。在正常情况下,如果类中存在__wakeup()方法,当使用unserialize()函数反序列化对象时,会首先调用该__wakeup()方法。然而,当序列化字符串中指定的对象属性个数大于实际对象属性个数时,PHP会跳过__wakeup()方法的执行,这可能导致安全漏洞。

存在该cve漏洞的php版本,PHP 5.x系列:小于5.6.25的版本,PHP 7.x系列:小于7.0.10的版本

接下来演示该cve漏洞,环境需求:把提供的cve目录放置phpstudy根目录,并创建网站。

分析cve7124.php源码

分析poc.php文件源码

访问这个payload

image-20240820200910743

产生了一句话木马文件。

image-20240820200926157

ctf题目

攻防世界的反序列化靶场。

image-20240820201555217

分析源码可知当反序列化之后执行wakeup函数就会停止执行。所以需要绕过wakeup函数,则可以利用前面的cve漏洞

image-20240820201621244

先序列化得到结果,然后调整序列化中的数量大于对象属性个数,来绕过wakeup函数。

image-20240820201752115

获取答案提交即可通关。

image-20240820202023231

typecho反序列化漏洞

从资料中把该目录移动到phpstudy根目录,并创建网站。

image-20240821134620614

在navicat新建数据库。

image-20240821134947507

开始安装。

image-20240821135033421

管理员密码设置为123456

image-20240821135115590

部署typecho博客成功。

image-20240821135226063

由于要找反序列化漏洞,分析源码中包含unserialize函数。点击进入ctrl查看get函数

image-20240821135628984
image-20240821140032423

继续分析install.php文件,发现必须使得finish参数有参数传递才可以触发后续的序列化操作。

image-20240821141013147

当finish有参数后,会进入源码中的三个分支,其中else分支才是执行反序列化操作。可以看出,要想进入我们需要的else判断里面,我们首先要使得我们的传参或者cookie里有__typecho_config这个参数并且也包含config.inc.php这个文件,才能进入我们所需要的反序列化里面。

在else分支,继续审计看是否有magic函数可以利用,反序列化后的config变量,又被拿来new了一个Typecho_db对象,进一步审计这个Typecho_db类。

image-20240821142212327

发现在构造函数中$adapertName这个变量的前面加上一些字符串,那么如果说,$adapterName是一个对象呢?意思是不是就是将对象转化为了一个字符串呢?如果满足这种情况,我们是不是就可以去利用__tostring()这个魔术方法去写最后的payload呢。

image-20240821150545351
image-20240821151429324

于是经过查找发现typecho_request类不仅没有screenname属性,而且也有设置__get函数,进一步审计里面get函数

image-20240821151632414

进一步查看_applyFilter函数是干嘛的

image-20240821151742091
image-20240821151853546

于是在生成poc中,写入我们要执行的命令,比如phpinfo(),运行这个文件获得序列化的字符。

image-20240821152958825
image-20240821153122953
image-20240821153350585

Java序列化和反序列化

image-20240821161545326
image-20240821163310640
image-20240821163515804
image-20240821164042125

java序列化的函数

image-20240821193536483

并且注意,如果一个类需要序列化,必须实现serializable接口。

image-20240826165716852

在idea中打开提供的源码包serialize-vul,打开此项目。

image-20240826165804172

由于要对person类进行序列化,先查看person文件的内容。

这段代码定义了一个名为 Person 的 Java 类,并且实现了 Serializable 接口,使得 Person 类的对象可以被序列化。

json字符串反序列化

查看testjson这个java文件。

test1() 方法的目的是将一个 Person 对象转换为 JSON 格式并输出 JSON 对象中的内容。

test2() 方法展示了如何将一个 JSON 字符串解析为 JSONObject 和 Java 对象,并从中获取属性值。

具体运行结果如下

image-20240826171319939

读写文件反序列化

查看TestToFile文件

运行结果

image-20240826172346667
image-20240821194737911

利用思路

image-20240821194914008

在java工程有如下两个类重写了readObject方法。

image-20240821194956160

为了说明readObject重写方法所产生的漏洞,这里进行一个简单的演示。

查看unsafeClass.java文件

查看UnsafeClass文件

运行结果,可以发现计算器被打开了。

image-20240826175346136

Apache Commons Collection反序列化漏洞

image-20240826230454774

复现该反序列化漏洞所需的环境

image-20240826230947212

为了保证实验顺利实现成功,先设置java编译版本为java7

image-20240827155652631

下载jdk1.7,然后添加sdk

image-20240827162823443

添加后选中jdk1.7

image-20240827162924952

确保依赖也是用jdk1.7

image-20240827162957927

在菜单中打开setting,确保编译器使用7版本。

image-20240827155850862

确保commons-collection版本小于3.2.1

image-20240827155929010

漏洞存在的两个问题。

image-20240826230758170
image-20240826231046345

反射例子

java反射的作用,可以动态地根据代码的执行创建不同的类,而非代码写死创建某个类。

image-20240826231604418
image-20240826234207047

理解该漏洞所要了解的关键类

image-20240826234552857

这是调用链路,接下来根据调用链路分析代码找到反序列化漏洞。

image-20240826235106512

通过ctrl+N寻找InvokerTransformer类的源码

该类的构造函数由三个参数组成,分别对应方法名称,方法的参数类型以及方法的参数。

image-20240827165758890

而该类的transform方法需要传入一个对象参数,通过传入一个对象参数,然后通过之前构造函数提供的方法和参数让这个对象去执行。最后依据执行的method方法的返回值类型进行return.

image-20240827170208919

为了解释这段代码的含义,可以用下列代码样例运行理解,运行下面这段代码,会启动计算器。

但是上面的样例代码是自己手动调用transform方法才去打开计算器的,那么怎么在反序列化的过程中自动调用这个transform方法呢?

分析chainedTransformer类,构造该类的对象需要传入Transformer对象数组。

image-20240827171022116

该类的transform方法,会把Transformer数组的所有对象元素都执行transform函数。

注意:每次执行transform方法可能会使得object发生变化。

image-20240827171138111

分析ConstantTransformer类发现构造函数传入的对象,一直到该类执行transform函数返回的对象是一样的。

image-20240827171338194

通过下面的样例代码理解上述代码的含义。

接下来问题在于,如何使得chainedTransformer类的方法transform被自动调用呢?

分析TransformedMap类的构造方法,其中需要传入map和transformer对象。

image-20240827180952754

其中该类的checkSetValue方法,作用是让valueTransformer属性对象执行transform方法。

image-20240827181316882

思路有了可以通过chechSetValue方法来传入chainedTransformer对象来执行,那又怎么执行自动执行checkSetValue方法呢?

在AbstractInputCheckedMapDecorator类的静态内部类MapEntry类中的setValue方法的执行会触发checkSetValue方法的执行。

image-20240827182612941

那又如何使得setValue方法被触发呢?只需找一个对象,能在反序列化的时候给map进行元素赋值,那么就会调用setValue函数。

而MapEntry类继承的AbstractMapEntryDecorator类实现了Map的接口,而map内部又嵌套了Entry接口。从而实现了Entry类的setValue方法。

image-20240827183905551

而AnnotationInvocationHandler类被反序列化会自动执行readObject方法,而里面也有setValue方法,也会自动执行。

image-20240827184931240

poc构造

image-20240826235546375

poc代码如下。

运行这个poc,会打开计算器程序。

image-20240827190213632

调用流程总结

image-20240827153552496

Fastjson反序列化漏洞

image-20240827233516207

要实现这个反序列化漏洞,需要保证fastjson版本低于如下两个。

image-20240827233528560

确保版本的环境符合。

image-20240828152248981

关于序列化需要知道的一些知识,根据下面代码的运行结果可以得知,如果类中的私有成员变量没有get方法,那么序列化的时候将会忽略这个成员变量。

运行结果

image-20240828155159228

user.java文件,定义了user类,其中address私有属性不具有setter方法。

分别由两种反序列化函数,parse函数不能指定反序列化后类的类型,默认返回Object类型,而parseObject能指定反序列化后的类型。

通过运行结果可得知,类中的私有成员变量在反序列化类的过程会自动调用setter方法,而公有成员变量则不会,如果私有成员变量没有setter方法,那么反序列化后该私有成员变量的属性值将会失效。

总结如下。

image-20240827234036876

fastjson实现反序列化的两个方法,在反序列化的时候,第二个参数用于指定反序列化后的对象类型。若不指定需要使用Object对象类型进行接收。

image-20240827234255245

如果@type自省,那么就可以不填第二个参数。并且反序列化为了保证类型不丢失也需要使用自省,之所以这么说原因如下。

当一个类只有一个接口的时候,将这个类的对象序列化的时候,就会将子类抹去(apple/iphone)只保留接口的类型(Fruit),最后导致反序列化时无法得到原始类型。本例中,将两个json再反序列化生成java对象的时候,无法区分原始类是apple还是iphone。

image-20240827234548045

fastjson小总结

实现该漏洞的总体流程

image-20240827235302369
image-20240828000103151

首先根据参考资料,让CentoS替换成阿里的yum源,并且安装docker。

接下来开始在docker里面安装vulhub靶场

这条命令会从 GitHub 下载适合当前系统架构的 docker-compose 版本 1.29.2,并将下载的文件保存为 /usr/local/bin/docker-compose

image-20240828171732155

并且加上可执行权限。

image-20240828173832596

查看docker-compose版本。

image-20240828173955100

安装git。

image-20240828174145083

创建该目录,并

image-20240831104738133

启动vulhub中的fastjson靶场

如果启动fastjson拉取镜像失败,那么去网上找最新docker镜像加速器。

image-20240831105209991

fastjson靶场部署完毕。

image-20240901120647583

kali设置python默认是python3

在update-alternative添加python2和python3这两个替代方案,在系统中设置或更新 python 命令,使其指向 /usr/bin/python2(Python 2 版本)。

设置该版本的优先级为 100。如果将来你安装了另一个 Python 版本并设置了更高的优先级,那么系统在调用 python 命令时会选择优先级更高的版本。

image-20240901124312339

由于下载两个python版本,这里切换成python3版本。

image-20240901124458856

并且为了保证后续实验顺利进行,需要给kali和centos7安装jdk8的环境。

首先下载jdk1.8并传入到虚拟机指定的目录。

image-20240901125708917

把linux的jdk进行解压

image-20240901133517618

把这个jdk放到指定目录下,并修改名称。

image-20240901133706345

给jdk目录下的所有文件设置可执行权限。

image-20240901130730095

配置JRE版本环境变量

image-20240901125525549
image-20240901125514975

执行环境变量配置文件

image-20240901125927001

能查看java版本信息,说明安装jdk环境成功。

image-20240901133831236

同理kali也需要安装jdk环境。但有一小部分不一样,kali不需要source环境变量,把jdk放入指定路径,并在update-alternative管理工具添加新的jdk8版本。

image-20240901135348401

于是java版本切换成功。

image-20240901135459846

在idea打开恶意代码,这个可以实现RCE漏洞,主要作用是:fastjson容器靶场去执行创建文件的操作。

image-20240901123545272

注意编译源码时要去除com.wuya的包名,因为反序列化的时候,靶场不一定有这个包名,所以不去掉会导致反序列化失败。

并且为了保证实验成功,需要将该文件复制到jdk8版本下进行编译。

image-20240901140137798

编译完恶意代码,把其中的class文件,放入到kali自定义的目录下。

image-20240901140331778
image-20240901140809132

在当前目录下启动一个简单的 HTTP 服务器,监听端口 8089

image-20240901140844318

搭建http服务器成功。

image-20240901141112944

把该jar包传入到kali指定目录。

image-20240901141229811

启动 marshalsec 工具中的 RMIRefServer 类,它会启动一个 RMI 引用服务器。该服务器提供的引用对象可以通过 http://192.168.15.141:8089/#LinuxTouch 这个 URL 访问。RMI 服务监听在 9473 端口,等待远程客户端连接

image-20240901141658505

使用bp抓包,修改Host请求的主机是fastjson,并且设置datSourceName属性为rmi引用服务器的地址和端口。点击send发送。

image-20240901150926360

于是即可在容器中查找到创建的文件,这里没有复现成功。

image-20240901154531637

接下来利用1.2.47版本复现第二个fastjson漏洞,首先监听9001端口

image-20240901154956009

http仍然使用python打开,并且启动LDAP转发器

image-20240901155429636

使用bp发送post请求给fastjson靶场。

image-20240901155400944

实现shell反弹,当然这里也没复现成功。

image-20240901155605848

源码分析利用流程

image-20240901155837594

setAutoCommit函数调用了connect函数。

image-20240901160248592

而connect函数会调用lookup函数来连接到LDAP/RMI服务器,从而转发恶意代码执行。

image-20240901160339247

漏洞挖掘思路。判断fastjson可以故意输入非法格式,通过回显的结果判断是否fastjson。

image-20240901160548192

修复该漏洞的方法。

image-20240901161015027
image-20240901161039843

apache shiro反序列化漏洞

image-20240901161536802

shiro的介绍

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。

image-20240901161638640
image-20240901163810740

漏洞原因分析

image-20240901184538876

主要的难点。怎么知道aes的密钥,以及构造一个反序列化后的对象可以通过readObject方法去执行命令。

image-20240901190155345

登录流程。

image-20240901190359398

验证流程

image-20240901190537425

漏洞环境搭建

首先下载源码包,并且从idea打开源码包对应的目录

image-20240901190951817

在pom.xml需要在此处加版本号。

image-20240901194534532

也需要加common-collections依赖。

image-20240901194623610

让maven导入这些依赖。

image-20240901194712701

配置tomcat服务器。

image-20240901194751107

并且导入war包即可运行此项目。

image-20240901194809731

项目启动成功。

image-20240901194924259

利用工具和方式

JRMP是RMI底层的工作协议,而RMI是用于远程引用的。

image-20240901195958898
image-20240901203352227
image-20240901203709064
image-20240901203824878
image-20240901204625162

利用实现1

image-20240901204826221

这些常见的检测工具可以实现shiro反序列化漏洞的操作。

image-20240901204900679

这里我们可以尝试先通过docker搭建shiro靶场。docker靶场比较容易复现漏洞。

image-20240901220615211

搭建靶场成功。

image-20240901220852072

使用shiro_tool.jar来探测靶场地址是否有shiro反序列化漏洞。首先探测出公钥。

image-20240901222654097

并且发现很多漏洞能够使用。

image-20240901222753988

接下来使用shiro_attack探测工具进行shiro反序列化漏洞复现.

java -jar加载该探测工具,并且爆破目标地址的密钥。

image-20240901223450660

检测利用链,发现其中有构造链,于是可以尝试命令的执行。

image-20240901223704038

于是实现远程代码执行。

image-20240901223741100

同理也可拿来攻击idea部署的shiro靶场。

image-20240901223941519

通过源码分析可以查看shiro框架所使用的是写死的密钥。与探测工具爆破的密钥是一样。

image-20240901224917833

利用实现2

完整流程

image-20240901205818507

首先kali作为攻击机先监听7777端口。

image-20240901225153277

把反弹链接的恶意代码进行base64加密,当然这里需要使用特定的网站来生成base64编码的payload,复制这个编码后的payload用于给RMI转发给被攻击机恶意代码。

image-20240901225654345

这段命令涉及使用 ysoserial 工具来启动一个 Java RMI 监听器 (JRMPListener),端口为8888,并利用 Java 反序列化漏洞执行一个远程命令。

image-20240901230506016

首先安装加密模块

image-20240901231241883

用python3去运行shiro.py,这个加密的长串字符串,主要就是让shiro靶场机根据rememberMe的cookie值,反序列化后,会以JRMP客户端去访问JRMP服务端。然后JRMP服务端会把恶意代码给到靶场,靶场执行后,就会把shell反弹连接给攻击机。

image-20240901231258281

分析shiro.py源码文件

bp使用repeater模块,通过post请求发给靶场。

image-20240901232319726

具体的payload如下

通过反弹连接,可以对靶机执行任何操作。

image-20240901232418653

shiro小总结

修复防御的手段。

image-20240902195838143

Last updated