Kernel Write-What-Where in Qualcomm Driver == LPE

David Wells
Tenable TechBlog
Published in
5 min readSep 4, 2019

README

Recently, I found a Kernel Write-What-Where vulnerability in Qualcomm Atheros WLAN Driver Service (QcomWlanSrvx64.exe) version 12.0.0.825, that came default on the Dell XPS I ordered. The affected kernel-mode component was Qcamain10x64.sys. I discovered this on what I thought was the latest Qualcomm Atheros service at the time (after fully updating through latest Dell Updates and Device Manager). It turned out, however, that those utilities were not providing the latest Qcamain10x64.sys from Qualcomm, which happens to have the fix for the vulnerability I am going to be talking about. This means many of the Dell XPS laptops with Qualcomm Atheros WLAN are probably vulnerable to this issue. I notified Qualcomm about Dell not having their latest driver versions, and they said they are looking into updating that process. If by the time you read this and you can’t get Qcamain10x64.sys version 12.0.0.827 or higher (which has the fix for the vulnerability) through traditional driver update methods, you can look here for the latest driver versions.

When disclosing to Qualcomm, they mentioned the issue was fixed last year, however the CVE they provided (CVE-2019–10567) still seems to be in reserved status at the time of writing. I also have not seen any existing advisories/write ups on this particular vulnerability, so I figured I would go over this attack.

Qcamain10x64.sys Overview

This driver vulnerability starts off like many driver vulnerability stories: a device that is writable by EVERYONE. This means any low privileged process can interact with the device driver by sending IOCTL codes to device.

Figure 1 — WinObjEx showing permissions for Device

This device name is “ATH_WIFIDEV.00”. We can reverse engineer this driver to see what IOCTL codes it supports and interesting code paths we can hit with various IOCTLs and buffers.

Here shows Qcamain10x64.sys’ routine for Device I/O Control requests.

Figure 2 — Qcamain10x64.sys IOCTL handler

Part One: Setting the Buffer

Once we located the routine that handles Device I/O Requests, I searched for interesting code paths. The first interesting part I saw was the ability to write arbitrary data to PCI config space.

Figure 3 — Qcamain10x64.sys calling into NdisMSetBusData (PCI config space)

This can be triggered by sending an IOCTL 0xC3502406 to the “ATH_WIFIDEV.00” device (using DeviceIoControl API), which branches to a NdisMSetBusData call by the driver. The driver uses user-supplied data and user-supplied offset to write into PCI config space.

Initially, I had a hard time finding a safe offset to write arbitrary data to without crashing the OS (BSOD); however, after testing various offsets, I found we could safely write 4 bytes to offset 0x14 in PCI config space without adverse effects on the OS. This is step one for our Write-What-Where, and the image below shows the simple code for writing “0xDEADBEEF” to PCI config space from a low privileged process.

Figure 4 — Minimal PoC that writes data to PCI config space from user-mode

That’s neat. So far, all we have is the ability to write arbitrary data to some PCI space. It’s nothing too special; however, combining it with another driver routine we can trigger, things get much more interesting.

Part Two: Writing The Buffer

Now that we have our custom payload safely stored in PCI config space, we need to do something interesting with it. Well, this worked out beautifully, as I did not only find a driver call to NdisMGETBusData (which reads PCI space at given offset into a buffer), but it’s also a function we can subsequently trigger by sending the right IOCTL.

Figure 5 — Qcamain10x64.sys call to retrieve data from PCI config space

As if that wasn’t enough, the IOCTL for this routine is 0x220043, which judging by its lower 2 bits, tells us its transfer type is METHOD_NEITHER. This means the routine will operate directly on buffers passed from user mode, and no mapping/copying will be done before calling into driver routine (dangerous). Oh, and I’m not done yet, it gets better. There is no ProbeForWrite call to validate these buffers passed from user mode. This all means we can supply a Kernel Mode address for our output buffer, and have the routine read PCI data directly into this supplied output buffer (via the memcpy_s operation following the NdisMGetBusData call), essentially being a controlled kernel write. Combining this with our first step, which is writing arbitrary data to PCI config space, makes this a Kernel Write-What-Where. The following code for all this is very simple and seen below:

Figure 6 — Full PoC that triggers kernel write to kernel-mode address from user-mode

This writes our “AAAA” to kernel mode address 0xFFFFBE8A2BA00000.

Figure 7 — Windbg showing kernel memory we wrote to

Exploitation

I was able to successfully escalate my privileges to SYSTEM solely using the described Kernel Write-What-Where vulnerability on Windows 10 (x64) with all default mitigations enabled (SMEP, CFG, etc). I’m hoping someday, I can go more into this technique, but so far we can see it succeeded in LPE.

Figure 8 — Escalating to System…
Figure 9 — Escalated to SYSTEM

Showing that this is a real vulnerability with real consequences, I urge anyone who has Qualcomm Atheros WLAN Driver Service to update ASAP.

Qualcomm Patch

Looking at the latest Qualcomm patch, we can see they utilize ProbeForWrite checks on the buffers passed from user mode, essentially preventing this Write-What-Where vulnerability.

Figure 10 — Patch analysis of latest Qcamain10x64.sys patch that prevents this Write-What-Where

--

--