[EX007] How playing CS: GO helped you bypass security products

Many of us love to play games, and as offensive security engineers, we also want to learn about how game studios are dealing with cheaters. We have observed that cheaters have used vulnerable graphic drivers to bypass anti-cheat mechanisms from several gaming cheating forums. In some cases, the cheaters tried to install vulnerable driver versions onto their computers, then exploited the vulnerability to read and write the game process's memory with the kernel privileges.

Instantly, we thought, why don't we apply this to bypass the security products or endpoint security solutions? So, we have started to research and present what we have learned in this article.

In this research, our goal is to read LSASS.exe memory from userland.  We experimented against Kaspersky Total Security (KTS), one of the top security products currently. KTS has its self-defense mechanism, and it also has a strict behavior and memory monitoring mode. This article doesn't demonstrate any vulnerabilities in any Kaspersky products or imply anything to Kaspersky. Researchers can apply this technique to disable most AV products today. In essence, this is not really a weakness of AVs. This is a common weakness. We only used Kaspersky as an example to highlight that common weakness.

Typically you are blocked by KTS if you use ReadProcessMemory to read LSASS.exe memory in userland. Our idea is to find a vulnerable driver and then install it. Through the vulnerability of the installed driver, we can read and write to physical memory. Then, we develop a shellcode to read any process memory in kernel context and make it available to user context.

In detail, the shellcode calls two undocumented APIs, including MmCopyVirtualMemory and PsLookupProcessByProcessId, to read the process memory with kernel privilege. And, we overwrite the existing NtShutdownSystem function with our shellcode.

Therefore, we can read any process memory (lsass.exe included) by call overwrote NtShutdownSystem API from userland. For better understanding, we describe this process in the diagram below:

Technical Detail

1.     Find the vulnerable driver

Firstly, we need to find a driver with an EoP vulnerability for reading and writing physical memory. You can easily see many such drivers, and below are two of them.

a.      CVE-2015-2291

IQVW32.sys before and IQVW64.sys before in the Intel Ethernet diagnostics driver for Windows allows local users to cause a denial of service or possibly execute arbitrary code with kernel privileges via a crafted 0x80862013, 0x8086200B, 0x8086200F, or 0x80862007 IOCTL call.

Exploit: Use three 0x80862007 IOCTL calls to MapIO, CopyMemory, and UnmapIO for reading and writing physical memory.

b.      CVE-2019-16098

The driver in Micro-Star MSI Afterburner (aka RTCore64.sys and RTCore32.sys) allows any authenticated user to read and write to arbitrary memory, I/O ports, and MSRs. This can be exploited for privilege escalation, code execution under high privileges, and information disclosure. These signed drivers can also be used to bypass the Microsoft driver-signing policy to deploy malicious code.

Exploit: https://github.com/Barakat/CVE-2019-16098

2.     Overwrite existing NtShutdownSystem WinAPI function

To overwrite NtShutdownSystem API in the memory, we will need:

  • Load image to memory and get 32 bytes from function virtual address: GetProcAddress(LoadLibraryW(L"ntoskrnl.exe"), "NtShutdownSystem")
  • Use it as signature bytes to find nPhysicalAddress from 0 physical address.
  • If and physical address is found, I check by overwriting a simple shellcode to return 0xABABABAB.

Simple shellcode (x64 version) to return 0xABABABAB:

3.     Develop Shellcodes to call needed APIs

With each API, we do the following steps:

  • Prepare shellcode by replacing parameters.
  • Get the physical address of NtShutdownSystem API in ntoskrnl.exe for overwriting.
  • Get and store the virtual address of NtShutdownSystem API in ntdll.dll in variable pOverwriteFunc.
  • Get and store the physical address of needed API in variable pExportFuncPhysicalAddress.
  • Read and store original bytes of NtShutdownSystem API for backup.
  • Write shellcode to the physical address of NtShutdownSystem API.
  • Execute: static_cast<FUNC_CAST>(pOverwriteFunc)(pExportFuncPhysicalAddress)
  • Write original bytes to the physical address of NtShutdownSystem API.

32-bit shellcode to call PsLookupProcessByProcessId:

64-bit shellcode to call MmCopyVirtualMemory:

Follow the official document, by default, the x64 calling convention passes the first four arguments in registers, while the remaining ones get pushed on the stack in right-to-left order.

4.     Read process memory from userland

By combining needed kernel APIs, we can read any process memory from userland:

  • Get the pointer to EPROCESS struct of the targeted process through PsLookupProcessByProcessId.
  • Know the virtual address of the targeted process.
  • Get the pointer to EPROCESS struct of the current process through PsLookupProcessByProcessId.
  • Allocate the buffer.
  • Use MmCopyVirtualMemory to copy the targeted process memory to the buffer.

5.     Modify Mimikatz code

The only reason for this is to reuse Mimikatz code for parsing lsass.exe memory.


We successfully bypassed the security product and read the credentials in lsass.exe memory.

Here is our demo clip: 


This technique is not new and unique. Many similar ideas have been published:

  • Physmeme: systematic exploitation of physical read/write to map unsigned code into the kernel.
  • PSKDM: Map a driver into specific processes only, with zero allocations in the kernel.
  • KDMapper: Exploit Intel driver to map non-signed drivers in memory manually.
  • VDM - Vulnerable Driver Manipulation: Overwrite NtShutdownSystem to call any kernel function.
  • Some of our team members will also publish a more in-depth study on this topic as individual research at FDSE 2021.

During our research, we also discovered that Kaspersky's development team might have anticipated this issue as well. An example of that is Kaspersky and some other AV manufacturers that have equipped the feature to find outdated software and drivers on the computer and then warn the user to update them. However, many limitations make it difficult for AV manufacturers to build a mechanism for their AV to actively take more drastic measures (For example, automatically updating outdated drivers without any user action). One of them includes that AVs will have to claim more privileges and powers. In addition, they can also cause instability in the computer system.

The unique point is applying the idea of abusing vulnerable drivers in red team activities (adversary simulation). In VinCSS's MSSP projects, our Threat Hunting team regularly deploys adversarial simulation campaigns aimed at customers' IT networks. These campaigns help our clients always to get used to being vulnerable to a sophisticated targeted attack at any time. Therefore, researching and exploiting the weaknesses in this report is very important for a red team.

VinCSS Threat Hunting Team

No comments:

Post a Comment