Tracking Windows Updates with Git and CI
When researching a system or program, it can be valuable to track and visualise changes made from version-to-version. When a new release comes out, being able to easily visualise the changes can make it easy to spot things like silently-patched bugs, new undocumented features, or even the introduction of new bugs. And this goes for not only single binaries, but for full systems, including entire Operating Systems.
This post will cover my approach to using Git and Continuous Integration to create automated change visualisations, and how I’m using it to track changes to Windows from week to week.
The Goal - Tracking ETW
Event Tracing for Windows provides an excellent look into what is occurring on a running machine. Currently, there are over 1000 different providers of data, with providers being added, renamed, and removed regularly in weekly Windows updates.
A new ETW provider is often a sign that a part of Windows has been upgraded or altered, and the log messages from the providers can often give new insights into the Windows’ inner workings.
My Goal was to be automatically notified when Windows is updated with new ETW Providers, and to be able to visualise what providers have been added, removed, or altered.
Additionally, I maintain an ETW Tracer called Sealighter, and so being able to check Sealighter still works with new Providers was also important to do.
Visualising with Git
Getting the list of ETW Providers is very easy, I simply ran this command from an elevated PowerShell:
logman query providers > providers.txt
Now, the contents of providers.txt
contained the list of ETW Providers for my current version of
Windows. The next time Windows updates, I can just run the command again to get the updated list.
In order to be able to track changes as Windows Updates, I used git and GitHub, in the same way I tracked code changes when I used to be a software dev. After creating a GitHub account and a new repository, I created the following script, to be run each time Windows updates. The Script produces new git commits with any changes to the provider list, that I can then view from either the commandline or the Github UI:
# Get the current Windows Version
$WINDOWS_VERSION = ([System.Environment]::OSVersion.Version).ToString()
# Get the list of Providers
logman query providers > providers.txt
# Add and commit the updated version
# If nothing has changed then these will do nothing
git add providers.txt
git commit -m "Updated to $WINDOWS_VERSION"
git push
In the GitHub UI, when a change occurred I’d see a something like this:
To view the same information from the commandline, inside the folder I could simply run:
git diff HEAD~1
And I’d get a similar colour output:
In both circumstances, when new providers are added, removed, or altered, they would be highlighted, so I’d know which ones to investigate.
Automating Data Collection
This was all fine if I wanted to manually run the script every update, but I wanted something to run automatically, and just it notifies me when there are changes to view.
There are a number of ways to accomplish this:
Option A - Scheduled Task
If I had a machine that was always running and being updated, the most basic solution would be to run a Scheduled Task on the host:
# Change directory to folder with the git repo
cd C:\path\to\repo
# Write out git code into a script file
$SCRIPT = @"
`$WINDOWS_VERSION = ([System.Environment]::OSVersion.Version).ToString()
# Get the list of Providers
logman query providers > providers.txt
# Add and commit the updated version
# If nothing has changed then these will do nothing
git add providers.txt
git commit -m "Updated to `$WINDOWS_VERSION"
git push
"@
Write-Output $SCRIPT | Out-File "run.ps1"
# Create Scheduled task action
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ep bypass -f $pwd\run.ps1" -WorkingDirectory $pwd
# Create a trigger to make scheduled task every times
# Windows starts, as once windows updates it'll have to restart
$trigger = New-ScheduledTaskTrigger -AtStartup
# Now actually register the Task
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "Git-Update-Checker" -Description "Checks for windows updates"
If using this method I recommend also adding the run.ps1
script to the git repo, so you don’t lose it.
Option B - GitHub Actions CI
Another option is to make use of GitHub Actions and their managed CI runners. These have the advantage of being updated weekly by GitHub, but the disadvantage of being Windows containers, which might be missing certain files or features that a regular desktop or server version of Windows have.
There’s a plugin to Github Actions called Add & Commit which makes it easy to define an Action to replace our git
scripts, with steps that will run automatically on the managed runners:
name: CI
on:
push:
branches: [ main ]
schedule:
- cron: '0 0 * * *' # every day at midnight
jobs:
run:
runs-on: windows-latest
steps:
# Checkout Code
- name: Checkout code
uses: actions/checkout@v2
with:
submodules: recursive
# Run command
- name: Run Script
run: logman query providers | Out-File -FilePath .\providers.txt -Encoding ascii
shell: powershell
# Set WINDOWS_VERSION environment variable
- name: Set WINDOWS_VERSION env variable
run: Echo ("WINDOWS_VERSION="+([System.Environment]::OSVersion.Version).ToString()) | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
shell: powershell
# If the script changed any files, this step will commit them and make a new tag
- name: Add new commit if needed
uses: EndBug/add-and-commit@v5
with:
add: "providers.txt --force"
message: "Updated to ${{ env.WINDOWS_VERSION }}"
tag: "${{ env.WINDOWS_VERSION }} --force"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
IMPORTANT SIDE NOTE
GitHub Managed runners are designed to be only used to assist in the continual development and deployment of software stored in GitHub. The Acceptable Use Policy of GitHub actions is here, which states:
Actions and any elements of the Action service may not be used in violation of the Agreement, the Acceptable Use Policy, or the GitHub Actions service limitations. Additionally, Actions should not be used for:
1. cryptomining;
2. using our servers to disrupt, or to gain or to attempt to gain unauthorized access to, any service, device, data, account or network (other than those authorized by the GitHub Bug Bounty program)
3. the provision of a stand-alone or integrated application or service offering Actions or any elements of Actions for commercial purposes;
4. any activity that places a burden on our servers, where that burden is disproportionate to the benefits provided to users (for example, don't use Actions as a content delivery network or as part of a serverless application, but a low benefit Action could be ok if it’s also low burden); or
5. any other activity unrelated to the production, testing, deployment, or publication of the software project associated with the repository where GitHub Actions are used.
So, IANAL, but if you make sure you are 100% not violating 4.
, you’re doing this for non-commercial reasons, and the results are used to assist development of a project also hosted in this repository, I think this type of work falls within the guidelines.
For me, having an updated list of ETW Providers is important to make sure Sealighter works with all ETW Providers and events.
Option C - Terraform and the cloud
A third way could be to use tools like Terraform and Ansible to automatically create new updated Virtual Machines in the cloud (e.g. in AWS or Azure), run our script, then shutdown and destroy them.
I plan to cover this in a future blog series as this takes a while to set up if you’re new to the tools, and this blog is getting too long already.
Automating Notifications
If using GitHub, the notification part is actually the easiest, as Github by default will email you when a new tag is created on a repository. So this was a simple change to the script to also create a new tag on changes:
# Get the current Windows Version
$WINDOWS_VERSION = ([System.Environment]::OSVersion.Version).ToString()
# Get the list of Providers
logman query providers > providers.txt
# Add and commit the updated version
git add providers.txt
git commit -m "Updated to $WINDOWS_VERSION"
# If we actually commited changes, also tag and then push up
# '$?' means 'was the last command successfull?'
if ($?) {
git tag $WINDOWS_VERSION
git push
git push --tags
}
Then whenever a Windows Update changes the ETW providers, git will create a new commit and new tag, which will then email my Github account’s email address, altering me to changes.
Results - UacScan
I created a project called ETW Watcher, which demonstrates using the managed Github Actions runner to provide daily updates to the list of ETW Providers, if there are any.
A recent automatic commit alerted me to the addition of a new ETW Provider: Microsoft-Antimalware-UacScan
.
This sounded very interesting, so I ran Sealighter with the following config, to get a sample of events from this new Provider:
{
"session_properties": {
"session_name": "Sealighter-Trace",
"output_format": "stdout"
},
"user_traces": [
{
"trace_name": "UACScan",
"provider_name": "Microsoft-Antimalware-UacScan"
}
]
}
In order to test the new Provider, I ran the ByeIntegrity2 UAC Bypass PoC, created by axagarampur.
This bypass works by talking to a COM interface on the auto-elevated COM Object Internet Explorer Add-on Installer
, which runs inside an auto-elevated IEInstal.exe
process. The Bypass convinces the ieinstal
to run an arbitrary binary as a “setup command”, which it creates retaining the elevated privileges.
Running the bypass alongside Sealighter produces the following log:
{
"header": {
"activity_id": "{00000000-0000-0000-0000-000000000000}",
"event_flags": 576,
"event_id": 1201,
"event_name": "",
"event_opcode": 0,
"event_version": 0,
"process_id": 7588,
"provider_name": "Microsoft-Antimalware-UacScan",
"thread_id": 7932,
"timestamp": "2020-11-24 06:20:11Z",
"trace_name": "myTrace"
},
"properties": {
"autoElevateRequest": "BOOLEAN",
"comClsid": "{BDB57FF2-79B9-4205-9447-F5FE85F37312}",
"comRequestor": "C:\\WINDOWS\\explorer.exe",
"comServerBinary": "C:\\Program Files\\Internet Explorer\\IEInstal.exe",
"exeApplicationName": "",
"exeCommandLine": "",
"exeDllParam": "",
"requestorProcessId": 0,
"uacRequestType": 1,
"uacTrustState": 0
}
}
So we can see from Sealighter that the new ETW Provider logs information about when UAC requests
are created by COM objects, DLLs, and executables, as well as when those requests are auto-approved. Looking at OLEView (and axagarampur’s notes),
we can confirm that the comClsid
is the vulnerable ‘Add-on installer’ COM Class:
For a defender, these events provide some indication of what programs are starting elevated programs, and what DLLs or COM objects they are using.
For bug hunters, these events look to be an excellent source of information when looking for auto-elevating COM Objects or DLLs.
Running a trace for a while on a system may reveal interesting COM Objects to RE, and using Sealighter’s stack_trace
option
could help quickly find how these objects are used, and if they can be manipulated to elevate privileges.
Conclusion
This blog walks through one of my processes to use open source tooling to automate and triage data for future research.
These techniques could be applied to anything interesting that you can represent in textual form, and that may change over time. Examples could be:
- Directory listings
- DLL exported functions
- Responses from HTTP endpoints
- Documentation scraped from Open Source
For more information, hit me up on Twitter, and see these Github Projects: