简介

由于在CC链的学习过程中,是使用的IO操作,模拟网络传输操作,在这里难免会不太理解如果是在web传输过程中,如何实现序列化数据在网络上传输,并且在网络传输过程中,序列化数据(二进制流)与源文件的hash值不等,以及文件特征丢失等等很奇怪的问题,目前找到原因就是编码问题,但是二进制流的会被篡改的问题还没有解决,但是在测试反序列化,就能知道有哪些需要注意的事项了

环境

jdk 8u181
maven 3.8.5
springmvc 4.3.18.RELEASE
tomcat 8.5.79

搭建SpringMVC

web选取SpringMVC,关于SpringMVC的搭建过程,请参考SpringMVC内存马#创建SpringMVC项目,Tomcat版本为8.5.79,目录结构如下所示
image.png
然后整体思路是,通过文件上传,上传已经序列化好的文件(可以通过ysoserial项目创建,也可以自己下载对应的Commons Collections包,进行序列化成文件)
为什么会用文件上传的形式?这个在后面提及,当然还有更好的方式,但是只是为了验证网络传输,所以也就选择了最简单的方式
但是文件上传需要用到中间件,依赖如下,添加到pom.xml中

  1. <!--文件上传依赖包-->
  2. <dependency>
  3. <groupId>commons-fileupload</groupId>
  4. <artifactId>commons-fileupload</artifactId>
  5. <version>1.3.1</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>commons-io</groupId>
  9. <artifactId>commons-io</artifactId>
  10. <version>2.4</version>
  11. </dependency>

并且在dispatcher-servlet.xml里添加bean,配置上传文件的最大值,以及默认的编码方式

  1. <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  2. <property name="maxUploadSize" value="10485760"/>
  3. <property name="defaultEncoding" value="UTF-8" />
  4. </bean>

HelloController

/hi接口是返回前端的文件上传页面,/ysoserial接口则是处理传入的序列化的=数据,然后进行反序列化操作

  1. package com.spring.Controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RequestParam;
  5. import org.springframework.web.multipart.MultipartFile;
  6. import java.io.*;
  7. @Controller
  8. public class HelloController {
  9. @RequestMapping(value = "/hi")
  10. public String ReturnJsp(){
  11. return "hello";
  12. }
  13. @RequestMapping(value = "/ysoserial")
  14. public String SerialTest(@RequestParam("file") MultipartFile f1) throws Exception{
  15. ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(f1.getBytes());
  16. ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
  17. objectInputStream.readObject();
  18. return "test";
  19. }
  20. }

启动起来,效果如下:
image.png
这里上传对应的已经序列化过后的文件(比如CC7.ser),即可执行命令
image.png
image.png
然后将它运行起来之后,就可以看到正常的网络传输过程中,在web中如何反序列化
这个靶场其实很简单,但是在其中的问题很多,所以才会有这一篇文章
在针对上传文件属实有点low了,这里将文件输出成base64编码,然后网络传输传递base64编码,然后再序列化

  1. // Base64EncodeTest
  2. package com.myproject;
  3. import sun.misc.BASE64Decoder;
  4. import sun.misc.BASE64Encoder;
  5. import java.io.File;
  6. import java.io.FileInputStream;
  7. import java.io.FileOutputStream;
  8. public class Base64EncodeTest {
  9. public static String encodeBase64File(String path) throws Exception{
  10. File file = new File(path);
  11. FileInputStream fileInputStream = new FileInputStream(file);
  12. byte[] buffer = new byte[(int) file.length()];
  13. fileInputStream.read(buffer);
  14. fileInputStream.close();
  15. return new BASE64Encoder().encode(buffer);
  16. }
  17. public static void decodeBase64File(String base64Encode,String targetPath) throws Exception{
  18. byte[] buffer = new BASE64Decoder().decodeBuffer(base64Encode);
  19. FileOutputStream outputStream = new FileOutputStream(targetPath);
  20. outputStream.write(buffer);
  21. outputStream.close();
  22. }
  23. public static void main(String[] args) throws Exception {
  24. String base64Code = encodeBase64File("cc7.ser");
  25. System.out.println(base64Code);
  26. }
  27. }

image.png
然后我们对base64编码的二进制文件,base64解码还原来比较一下sha256

  1. // Base64EncodeTest
  2. //...
  3. public static void main(String[] args) throws Exception {
  4. String base64Code = encodeBase64File("cc7.ser");
  5. decodeBase64File(base64Code,"cc7base64Decode.ser");
  6. }

image.png
可以发现是hash之后是相等的,那么最后需要更改的就是web接收的参数,为base64字符串,这个字符串当解码之后就是二进制流,需要存放在一个byte数组中,然后再存放在ByteArrayInputStream中,然后使用ObjectInputStream来读取,最后再readObject进行反序列化

  1. byte[] buff = new BASE64Decoder().decodeBuffer(request.getParameter("data"));
  2. ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buff);
  3. ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
  4. objectInputStream.readObject();

完整的Spring web Controller 如下,对应的接口为/ysoserialBase64

  1. package com.spring.Controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RequestParam;
  5. import org.springframework.web.multipart.MultipartFile;
  6. import sun.misc.BASE64Decoder;
  7. import java.io.*;
  8. import javax.servlet.http.HttpServletRequest;
  9. @Controller
  10. public class HelloController {
  11. @RequestMapping(value = "/hi")
  12. public String ReturnJsp(){
  13. return "hello";
  14. }
  15. @RequestMapping(value = "/ysoserial")
  16. public String SerialTest(@RequestParam("file") MultipartFile f1) throws Exception{
  17. ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(f1.getBytes());
  18. ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
  19. objectInputStream.readObject();
  20. return "test";
  21. }
  22. @RequestMapping(value = "/ysoserialBase64")
  23. public String SerialBase64Test(HttpServletRequest request) throws IOException, ClassNotFoundException {
  24. byte[] buff = new BASE64Decoder().decodeBuffer(request.getParameter("data"));
  25. ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buff);
  26. ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
  27. objectInputStream.readObject();
  28. return "test";
  29. }
  30. }

在进行传输base64编码的时候,一定要注意,因为base64编码存在+,在web进行传输的时候+会被解码成空格则会导致解码失败,详情参考Base64编码/解码原理在这里需要将base64编码再进行一次URL编码放在burp里传输,如下所示
image.png
image.png

相关问题

0x1 接收POST Body

最开始想如Adobe ColdFusion AMF Deserialization Remote Command Execution (CVE-2017-3066)的漏洞利用这样,将反序列化的数据保存到文本文件,然后直接pasted from burp,将文本文件的二进制流传递给web服务器,但是在我使用node.js的时候发现无法接收到来自POST的请求体,因为在express里要接收对应的POST Body,都是由中间件解析完成,然而其实并不想太麻烦,所以最后使用上传文件的方式来将二进制文件流传入给服务端,然后立马就有了编码问题

0x2 编码问题

我在反复尝试后,将经过网络传输的文件保存下来,再利用模拟的IO操作进行反序列化,发现一直无法反序列化成功,因为在模拟IO操作的地方,我发现有crash,CC2本身在反序列化cc2.ser的时候程序就会crash,然后一直没去调试反序列化的过程,在这里浪费了许多时间
排查思路,需要拿到对应的文件进行判断,是否正常
我在这里选择使用node.js,因为轻便(使用java也行,只是我用node比较快),简单来看一下问题

  1. npm install express --save
  2. npm install multer --save
  1. const express = require('express')
  2. const app = new express()
  3. const fs = require('fs')
  4. const multer = require('multer')
  5. let multerConfig = multer({
  6. dest: './static/upload',
  7. })
  8. app.get('/',(req,res)=>{
  9. res.send('hahaha')
  10. })
  11. app.post('/',multerConfig.single('file'),(req,res)=>{
  12. console.log(req.file)
  13. res.send('get Post')
  14. })
  15. app.listen(8081,()=>{
  16. console.log('running!')
  17. })

此时通过burp构造一个multipart/form-data,然后对应将cc2.ser 粘贴到burp里
image.png
image.png
对其进行sha256,与源文件进行对比,发现并不能对上
image.png
并且可以看到如下对比,通过pasted from burp 上传到的文件,已经不符合java 序列化文件的类型了
image.png
image.png
那到底为什么会出现这种情况?我在Adobe ColdFusion AMF Deserialization Remote Command Execution (CVE-2017-3066)复现过程遇到过,以前是用低版本burp能复现成功,最开始的时候没有探讨原因,后面在北京的时候问过大佬,说是因为编码问题,只要更改为utf-8就可以了,但是我把所有的编码方式都修改了,发现还是不行,包括还是修改对应的16进制码(只修改了一部分),还是不行
然后写了一个静态页面,上传页面,注意后端需要修改同源策略,允许跨域访问

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <title></title>
  7. </head>
  8. <body>
  9. <form action="http://127.0.0.1:8081/" method="post" enctype="multipart/form-data">
  10. <input type="file" name="file">
  11. <input type="submit" name="submit">
  12. </form>
  13. </body>
  14. </html>
  1. const express = require('express')
  2. const app = new express()
  3. const fs = require('fs')
  4. const multer = require('multer')
  5. let multerConfig = multer({
  6. dest: './static/upload',
  7. })
  8. app.all('*', function(req, res, next) {
  9. res.header('Access-Control-Allow-Origin', req.headers.origin) //需要显示设置来源
  10. // 'Access-Control-Allow-Headers', 'Content-Type'
  11. res.header(
  12. 'Access-Control-Allow-Headers',
  13. 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
  14. )
  15. res.header('Access-Control-Allow-Methods', 'POST,GET,DELETE,OPTIONS')
  16. res.header('Access-Control-Allow-Credentials', true) //带cookies7
  17. res.header('Content-Type', 'application/json;charset=utf-8')
  18. if (req.method == 'OPTIONS') {
  19. res.sendStatus(200)
  20. } else {
  21. next()
  22. }
  23. })
  24. app.get('/',(req,res)=>{
  25. res.send('hahaha')
  26. })
  27. app.post('/',multerConfig.single('file'),(req,res)=>{
  28. console.log(req.file)
  29. res.send('get Post')
  30. })
  31. app.listen(8081,()=>{
  32. console.log('running!')
  33. })

在此处上传文件发现,通过前端上传的文件,和源文件能相对应上
image.png
然后将两个包进行比较,发现序列化数据发生改变了,暂时还不知道如何解决,就是编码问题,从文件里获取的时候,文件内容其实就已经发生了变化
image.png

0x3 依赖

在CC链中,我们知道需要特定版本的Commons Collections才能触发反序列化的点,比如Commons Collections 1 需要配合使用JDK1.7 和 Commons Collections 3.1-3.2.1,然而当我直接把本地的jar包放上去之后,调试的时候竟然发现找不到对应的类,这个问题也是高了好久才发现,一直以为是哪个环节出了问题,最后在pom.xml中引入Commons Collections 组件解决问题

  1. <dependency>
  2. <groupId>org.apache.commons</groupId>
  3. <artifactId>commons-collections4</artifactId>
  4. <version>4.0</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>commons-collections</groupId>
  8. <artifactId>commons-collections</artifactId>
  9. <version>3.1</version>
  10. </dependency>

参考链接

https://zhuanlan.zhihu.com/p/386631799
Adobe ColdFusion AMF Deserialization Remote Command Execution (CVE-2017-3066)