How-To Assess System Images: Overview

Hey folks, happy new year. Today I wanted to go over how I do assessments on system images. I've been interested in this topic for a little over a year, and most of that experience has been on Windows 10. So this won't cover other operating systems, but the concepts should still apply. I'm going start this series off with a barebones overview, and in future posts we'll delve into the gory details with workflow examples and case-studies (maybe walkthrough some CVEs).

So why do system image assessments? They can be important for OEMs and enterprises in identifying "built-in" vulnerabilities that could have a large blast radius affecting many workstations. The main goals are usually to find remote code execution, local privilege elevation, and other vectors that could be used to compromise a base install.

In my last methodology post, I wrote that the vulnerability discovery process can be reduced to three steps:

  1. Enumerate entry points (i.e. ways of interacting with the app).
  2. Think about insecure states (i.e. vulnerabilities) that an adversary would like to manifest.
  3. Manipulate the app using the identified entry points to reach the insecure states.

We can simplify it further to The Three E's: Enumerate, Examine, and Exploit. System image assessments follow this pattern, just at a larger scale. Below is a general outline of what I do. Think of it as a funnel where everything installed on the system passes through and out comes a collection of superpowers. Some will be stronger than others, but certain others can be chained to become more powerful.

Enumerate

  1. Identify components on the system and customizations made to the stock OS image (e.g. bloatware, utilities, services).
  2. Sort them from least scrutinized to most (e.g. from obscure third-party apps to Windows internals; it's subjective).
  3. Prioritize components where high-privileged code executes (e.g. services, kernel-mode drivers).
  4. Identify IPC entrypoints and protocols used in components (e.g. listening ports, RPC, COM, DeviceIoControl).
  5. Prioritize components with entrypoints that can be communicated remotely (for use toward RCE).
  6. Prioritize components with entrypoints to those where low-privileged code can interact (for use toward LPE).

Examine

  1. Look for patch management issues first on candidate components (to avoid finding already known vulnerabilities).
  2. Look for misconfigurations next (e.g. ACLs, service paths, using tools like SharpUp or PowerUp).
  3. Look for security bugs in code or design.
    • From the identified entrypoints, examine the handler functions of the high-privileged code and look for bug classes.
    • Start from least complex bug classes to most (e.g. from blatant logical bugs to non-trivial memory corruption).
    • Look for opportunities where side-effects of one component can impact other (unrelated) components.

Exploit

  1. Develop a test harness to first ensure that that the vulnerable code path can be reached.
  2. Craft a payload and ensure that it can be reliably triggered.
  3. Simplify the payload by trimming out anything that is irrelevant or extraneous.
  4. Write the exploit code in a modular way where the primitive is fungible and is easy to replace.
  5. Present it in a way where its impact is immediately understandable to the target audience (e.g. shell as SYSTEM).

Thanks for reading and stay tuned! Feel free to ping me if you have any questions or comments.

An excellent source of inspiration was James Forshaw's Introduction to Logical Privilege Escalation on Windows workshop.

@Jackson_T