要运行这个php代码,得把basic文件夹代码复制到phpstudy的www目录下,然后创建网站才可以从网页看到执行结果。
创建和删除对象都会自动调用magic函数。
运行结果如下
序列化:把一个对象转化为一段字符串
反序列化:把字符串转化回对象
序列化的作用
把不同序列化成字符串的例子
由于网页运行结果比较杂乱,这里整理运行结果如下
当然也支持序列化成其他的格式
把对象序列化成json字符串和xml字符串的例子。
把网站的运行结果进行整理如下,由于xml在网页端查看不出来,所以无法根据结果进行xml格式化显示。
有的对象可能具有敏感的私有属性不能被序列化,那么可以筛选出不能序列化的部分,比如下面例子中的password属性
运行结果如下,可以发现password私有属性没有被序列化
反序列化的例子,首先先创建一个对象,然后序列化。
运行结果入下图所示
通过序列化的内容进行反序列化成一个对象obj2。可以执行反序列化对象的方法。
==注意:也可对序列化的内容进行修改,来获得修改后反序列化的对象内容。==
比如php版本7.4.0之前执行该段代码
运行结果可以发现是优先调用wakeup的函数。
修改成7.4.0以后的php版本。
常见的写数据或者执行命令的相关函数
将马士兵提供的base目录下的代码放入phpstudy的网站根目录下,然后创建网站。
比如创建了index.php文件。
首先先获取删除index.php文件对象的序列化结果。
复制序列化结果用于传参来删除index.php文件。
除了删除文件,还可以利用反序列化漏洞获取文件内容。
生成一个readfile对象,把文件名属性改成要读取的文件。获得序列化字符串结果
通过param传参序列化结果,readfile.php文件中反序列化成对象后会输出这个对象,自然会调用tostring函数。
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
产生了一句话木马文件。
攻防世界的反序列化靶场。
分析源码可知当反序列化之后执行wakeup函数就会停止执行。所以需要绕过wakeup函数,则可以利用前面的cve漏洞
先序列化得到结果,然后调整序列化中的数量大于对象属性个数,来绕过wakeup函数。
获取答案提交即可通关。
从资料中把该目录移动到phpstudy根目录,并创建网站。
在navicat新建数据库。
开始安装。
管理员密码设置为123456
部署typecho博客成功。
由于要找反序列化漏洞,分析源码中包含unserialize函数。点击进入ctrl查看get函数
继续分析install.php文件,发现必须使得finish参数有参数传递才可以触发后续的序列化操作。
当finish有参数后,会进入源码中的三个分支,其中else分支才是执行反序列化操作。可以看出,要想进入我们需要的else判断里面,我们首先要使得我们的传参或者cookie里有__typecho_config这个参数并且也包含config.inc.php这个文件,才能进入我们所需要的反序列化里面。
在else分支,继续审计看是否有magic函数可以利用,反序列化后的config变量,又被拿来new了一个Typecho_db对象,进一步审计这个Typecho_db类。
发现在构造函数中$adapertName这个变量的前面加上一些字符串,那么如果说,$adapterName是一个对象呢?意思是不是就是将对象转化为了一个字符串呢?如果满足这种情况,我们是不是就可以去利用__tostring()这个魔术方法去写最后的payload呢。
于是经过查找发现typecho_request类不仅没有screenname属性,而且也有设置__get函数,进一步审计里面get函数
进一步查看_applyFilter函数是干嘛的
于是在生成poc中,写入我们要执行的命令,比如phpinfo(),运行这个文件获得序列化的字符。
java序列化的函数
并且注意,如果一个类需要序列化,必须实现serializable接口。
在idea中打开提供的源码包serialize-vul,打开此项目。
由于要对person类进行序列化,先查看person文件的内容。
这段代码定义了一个名为 Person 的 Java 类,并且实现了 Serializable 接口,使得 Person 类的对象可以被序列化。
查看testjson这个java文件。
test1() 方法的目的是将一个 Person 对象转换为 JSON 格式并输出 JSON 对象中的内容。
test2() 方法展示了如何将一个 JSON 字符串解析为 JSONObject 和 Java 对象,并从中获取属性值。
具体运行结果如下
查看TestToFile文件
运行结果
利用思路
在java工程有如下两个类重写了readObject方法。
为了说明readObject重写方法所产生的漏洞,这里进行一个简单的演示。
查看unsafeClass.java文件
查看UnsafeClass文件
运行结果,可以发现计算器被打开了。
Apache Commons Collection反序列化漏洞
复现该反序列化漏洞所需的环境
为了保证实验顺利实现成功,先设置java编译版本为java7
下载jdk1.7,然后添加sdk
添加后选中jdk1.7
确保依赖也是用jdk1.7
在菜单中打开setting,确保编译器使用7版本。
确保commons-collection版本小于3.2.1
漏洞存在的两个问题。
java反射的作用,可以动态地根据代码的执行创建不同的类,而非代码写死创建某个类。
理解该漏洞所要了解的关键类
这是调用链路,接下来根据调用链路分析代码找到反序列化漏洞。
通过ctrl+N寻找InvokerTransformer类的源码
该类的构造函数由三个参数组成,分别对应方法名称,方法的参数类型以及方法的参数。
而该类的transform方法需要传入一个对象参数,通过传入一个对象参数,然后通过之前构造函数提供的方法和参数让这个对象去执行。最后依据执行的method方法的返回值类型进行return.
为了解释这段代码的含义,可以用下列代码样例运行理解,运行下面这段代码,会启动计算器。
但是上面的样例代码是自己手动调用transform方法才去打开计算器的,那么怎么在反序列化的过程中自动调用这个transform方法呢?
分析chainedTransformer类,构造该类的对象需要传入Transformer对象数组。
该类的transform方法,会把Transformer数组的所有对象元素都执行transform函数。
注意:每次执行transform方法可能会使得object发生变化。
分析ConstantTransformer类发现构造函数传入的对象,一直到该类执行transform函数返回的对象是一样的。
通过下面的样例代码理解上述代码的含义。
接下来问题在于,如何使得chainedTransformer类的方法transform被自动调用呢?
分析TransformedMap类的构造方法,其中需要传入map和transformer对象。
其中该类的checkSetValue方法,作用是让valueTransformer属性对象执行transform方法。
思路有了可以通过chechSetValue方法来传入chainedTransformer对象来执行,那又怎么执行自动执行checkSetValue方法呢?
在AbstractInputCheckedMapDecorator类的静态内部类MapEntry类中的setValue方法的执行会触发checkSetValue方法的执行。
那又如何使得setValue方法被触发呢?只需找一个对象,能在反序列化的时候给map进行元素赋值,那么就会调用setValue函数。
而MapEntry类继承的AbstractMapEntryDecorator类实现了Map的接口,而map内部又嵌套了Entry接口。从而实现了Entry类的setValue方法。
而AnnotationInvocationHandler类被反序列化会自动执行readObject方法,而里面也有setValue方法,也会自动执行。
poc构造
poc代码如下。
运行这个poc,会打开计算器程序。
调用流程总结
要实现这个反序列化漏洞,需要保证fastjson版本低于如下两个。
确保版本的环境符合。
关于序列化需要知道的一些知识,根据下面代码的运行结果可以得知,如果类中的私有成员变量没有get方法,那么序列化的时候将会忽略这个成员变量。
运行结果
user.java文件,定义了user类,其中address私有属性不具有setter方法。
分别由两种反序列化函数,parse函数不能指定反序列化后类的类型,默认返回Object类型,而parseObject能指定反序列化后的类型。
通过运行结果可得知,类中的私有成员变量在反序列化类的过程会自动调用setter方法,而公有成员变量则不会,如果私有成员变量没有setter方法,那么反序列化后该私有成员变量的属性值将会失效。
总结如下。
fastjson实现反序列化的两个方法,在反序列化的时候,第二个参数用于指定反序列化后的对象类型。若不指定需要使用Object对象类型进行接收。
如果@type自省,那么就可以不填第二个参数。并且反序列化为了保证类型不丢失也需要使用自省,之所以这么说原因如下。
当一个类只有一个接口的时候,将这个类的对象序列化的时候,就会将子类抹去(apple/iphone)只保留接口的类型(Fruit),最后导致反序列化时无法得到原始类型。本例中,将两个json再反序列化生成java对象的时候,无法区分原始类是apple还是iphone。
实现该漏洞的总体流程
首先根据参考资料,让CentoS替换成阿里的yum源,并且安装docker。
接下来开始在docker里面安装vulhub靶场
这条命令会从 GitHub 下载适合当前系统架构的 docker-compose 版本 1.29.2,并将下载的文件保存为 /usr/local/bin/docker-compose。
并且加上可执行权限。
查看docker-compose版本。
安装git。
创建该目录,并
启动vulhub中的fastjson靶场
如果启动fastjson拉取镜像失败,那么去网上找最新docker镜像加速器。
fastjson靶场部署完毕。
kali设置python默认是python3
在update-alternative添加python2和python3这两个替代方案,在系统中设置或更新 python 命令,使其指向 /usr/bin/python2(Python 2 版本)。
设置该版本的优先级为 100。如果将来你安装了另一个 Python 版本并设置了更高的优先级,那么系统在调用 python 命令时会选择优先级更高的版本。
由于下载两个python版本,这里切换成python3版本。
并且为了保证后续实验顺利进行,需要给kali和centos7安装jdk8的环境。
首先下载jdk1.8并传入到虚拟机指定的目录。
把linux的jdk进行解压
把这个jdk放到指定目录下,并修改名称。
给jdk目录下的所有文件设置可执行权限。
配置JRE版本环境变量
执行环境变量配置文件
能查看java版本信息,说明安装jdk环境成功。
同理kali也需要安装jdk环境。但有一小部分不一样,kali不需要source环境变量,把jdk放入指定路径,并在update-alternative管理工具添加新的jdk8版本。
于是java版本切换成功。
在idea打开恶意代码,这个可以实现RCE漏洞,主要作用是:fastjson容器靶场去执行创建文件的操作。
注意编译源码时要去除com.wuya的包名,因为反序列化的时候,靶场不一定有这个包名,所以不去掉会导致反序列化失败。
并且为了保证实验成功,需要将该文件复制到jdk8版本下进行编译。
编译完恶意代码,把其中的class文件,放入到kali自定义的目录下。
在当前目录下启动一个简单的 HTTP 服务器,监听端口 8089。
搭建http服务器成功。
把该jar包传入到kali指定目录。
启动 marshalsec 工具中的 RMIRefServer 类,它会启动一个 RMI 引用服务器。该服务器提供的引用对象可以通过 http://192.168.15.141:8089/#LinuxTouch 这个 URL 访问。RMI 服务监听在 9473 端口,等待远程客户端连接
使用bp抓包,修改Host请求的主机是fastjson,并且设置datSourceName属性为rmi引用服务器的地址和端口。点击send发送。
于是即可在容器中查找到创建的文件,这里没有复现成功。
接下来利用1.2.47版本复现第二个fastjson漏洞,首先监听9001端口
http仍然使用python打开,并且启动LDAP转发器
使用bp发送post请求给fastjson靶场。
实现shell反弹,当然这里也没复现成功。
源码分析利用流程
setAutoCommit函数调用了connect函数。
而connect函数会调用lookup函数来连接到LDAP/RMI服务器,从而转发恶意代码执行。
漏洞挖掘思路。判断fastjson可以故意输入非法格式,通过回显的结果判断是否fastjson。
修复该漏洞的方法。
apache shiro反序列化漏洞
shiro的介绍
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。
主要的难点。怎么知道aes的密钥,以及构造一个反序列化后的对象可以通过readObject方法去执行命令。
登录流程。
验证流程
首先下载源码包,并且从idea打开源码包对应的目录
在pom.xml需要在此处加版本号。
也需要加common-collections依赖。
让maven导入这些依赖。
配置tomcat服务器。
并且导入war包即可运行此项目。
项目启动成功。
JRMP是RMI底层的工作协议,而RMI是用于远程引用的。
这些常见的检测工具可以实现shiro反序列化漏洞的操作。
这里我们可以尝试先通过docker搭建shiro靶场。docker靶场比较容易复现漏洞。
搭建靶场成功。
使用shiro_tool.jar来探测靶场地址是否有shiro反序列化漏洞。首先探测出公钥。
并且发现很多漏洞能够使用。
接下来使用shiro_attack探测工具进行shiro反序列化漏洞复现.
java -jar加载该探测工具,并且爆破目标地址的密钥。
检测利用链,发现其中有构造链,于是可以尝试命令的执行。
于是实现远程代码执行。
同理也可拿来攻击idea部署的shiro靶场。
通过源码分析可以查看shiro框架所使用的是写死的密钥。与探测工具爆破的密钥是一样。
完整流程
首先kali作为攻击机先监听7777端口。
把反弹链接的恶意代码进行base64加密,当然这里需要使用特定的网站来生成base64编码的payload,复制这个编码后的payload用于给RMI转发给被攻击机恶意代码。
这段命令涉及使用 ysoserial 工具来启动一个 Java RMI 监听器 (JRMPListener),端口为8888,并利用 Java 反序列化漏洞执行一个远程命令。
首先安装加密模块
用python3去运行shiro.py,这个加密的长串字符串,主要就是让shiro靶场机根据rememberMe的cookie值,反序列化后,会以JRMP客户端去访问JRMP服务端。然后JRMP服务端会把恶意代码给到靶场,靶场执行后,就会把shell反弹连接给攻击机。
分析shiro.py源码文件
bp使用repeater模块,通过post请求发给靶场。
具体的payload如下
通过反弹连接,可以对靶机执行任何操作。
修复防御的手段。