- Welcome to our talk, 2021,
our journey back to the future of Windows vulnerabilities
and the 0-days we brought back with us.
My name is Tomer Bar.
I've been in the cybersecurity field for more than 15 years,
currently leading SafeBreach Labs
as director of security research.
My main focus is on vulnerability research
and nation state APT research.
My name is Eran Segal.
I've been in the cyber security field
for more than seven years.
Currently, I'm working as a security researcher
in SafeBreach Labs for the last two years.
My experience involves research on windows
and the better devices.
In memory of my dad, David,
who bought me my first personal computer
and he's the reason I'm standing here.
In 2020, security researchers reported
a record number of 1000 Windows vulnerabilities.
We were curious, what superpowers will we get
from researching this huge number of vulnerabilities?
Can we leverage our findings to discover 0-days?
We decided to go back in time to 2016,
to search for patterns and automatically classify
all the public vulnerabilities since then.
We believe that only by connecting the dots
to a bigger picture,
we will be able to come back to 2021
with the success of achieving our goal.
We will start with describing the research goals,
assumptions, motivations and approach,
then we will explain the challenge
and our chosen solution, process and infrastructure.
We will detail the process step-by-step,
provide a detailed end to end example
from 0 to 0-day.
We will present the six vulnerabilities
and past exportation techniques we discovered.
We'll end our talk with a new proposed idea
for discovering vulnerable Windows machines
and open a Q&A session.
We define three main goals.
First, dive in and understand the root cause
of each vulnerability and how Microsoft chose to address it.
Second, automate and provide tools
to ease the process from root cause to POC.
Last but most important, discover new 0-days
based on a semi-automated process.
Every research start with assumptions.
Let's describe them.
The first assumption is that although Microsoft
is a huge enterprise with thousands of developers,
we believe that similar vulnerabilities will be mitigated
by similar patching techniques.
The second assumption is that Microsoft will try
to fix each vulnerability with as minority change
as possible in order to avoid backward compatibility issues.
This approach might be prone to patch bypasses.
Last, it will be a good idea to search for 0-days
near a code that's already been known as vulnerable.
Probably the first thing you will do
is to start checking if your assumptions are correct.
We encountered a recent public research
on just one ento scalar function,
which included five different vulnerabilities
from various categories, in a short block of code.
We won't dive into the specific details
of each vulnerability,
but you can get the impression
that our assumptions were fulfilled.
Vulnerable code is a good place to search
for new vulnerabilities.
The vulnerabilities have different categories,
but all are due to wrong bound checks
or wrong entering of memory allocations.
Now that we are more confident in the research idea,
it was time to think about our research approach.
Until now, patch diffing was done manually
by comparing the differences
of a vulnerable executable to its new code version
after the fix.
Furthermore, the main goal of this process
was focused on understanding the root cause
of a single vulnerability and building 1-day
to exploit it.
We aimed higher,
we wanted to find 0-days in the end.
But is it possible to jump from root cost to 0-days?
We understood that in order to achieve it,
we will need to build an automated process
that will gather the insights
from all the patches in a single searchable DB.
Let's dive into our infrastructure implementation,
moving over to Eran.
- We were required to take four steps
in order to achieve our goal.
The first step is to understand each one of the patches
Microsoft released and infer the vulnerability root cause.
Then we want to integrate the context
of each vulnerability by correlating
publicly available information about the vulnerability
with each patch.
The second step is to find a way
to trigger the vulnerability.
In other words, which actions lead to the vulnerable code.
Finally, after gaining this valuable knowledge,
we had to find ways to harness this knowledge
for hunting 0-days.
Let's begin with step one.
The automated process includes five sub-steps.
First, downloading all Windows 8
security only updates since 2016.
the reason we chose to download security updates
of Windows 8 and not Windows 10,
is because Windows 8 is a security only updates
while Windows 10 doesn't.
It is possible to discover all the vulnerabilities
Microsoft ever fixed in Windows 8 since 2016,
using the security updates.
After we downloaded all Windows 8 security only updates,
the automated process, extract the KBs
and compare the extracted files
versus the older version of the same files
using BinDiff tool.
Now the fun part begins.
The changes are classified into groups
and inserted into a database
so we will be able to search for patterns.
Each group represent a specific type of change
and we will talk about these features in the next slides.
To get some perspective on the scale we are talking about,
here are a few numbers.
You can see, we have more than 100,000 unique functions,
each one of them may be a part of a patch
so it is clear that we need to understand
which changes are interesting and which aren't.
Inside the KB, some of the files are interesting
while others aren't because APE can get indirectly updated
such as a change in one of its libraries.
It have huge effect.
For example, GDI 32 is one of the most vulnerable
PEs in Windows.
It has more than 26 statically linked libraries.
Some of these libraries are statically linked
to another libraries and so on.
In addition, every PE in Windows 8 is part of a package
and every time one of the PEs is affected,
has to be updated.
The entire package is really compiled together
and all the files are included in the KB.
In order to detect which changes are interesting
and which aren't, first, we need to understand
which changes the compiler can do.
It is not simple to distinguish between changes made
by the developer and the changes made by the compiler
because of free compilations.
If you compile the same code twice,
the compiler sometimes is compiled
with different alignments of the code.
We all do some instructions
and all other optimizations.
We had to find ways to reduce the compiler noise.
We chose to reduce the compiler noise using features.
The features group similar changes together
and allow us to look for vulnerability patterns.
We developed 33 features,
most of them are in the function level,
but other are in the executable level.
Each feature is optimized differently.
Some are optimized to have the lowest amount
of false positives,
while others give us the overview insights on the patch.
There are two types of features,
patch-related features and vulnerability-related features.
Patch-related features group patches
by the type of the change made by Microsoft,
such as add a new function call,
while vulnerability-related features group patches
by vulnerability category such as off by one,
use after free and so on.
The beauty of the features lie in their simplicity,
we'll provide some examples in the next slides.
2019, here we come.
The first example of patch-related features
is the number of Xrefs.
It is not directly linked
to a specific vulnerability category.
This feature compares the number of function calls
between each patched function versus the unpatched version.
In this example, ReadPROPVARIANT added one Xref
to IStream_Read.
Let's look into the overview of ReadPROP variant function.
It is very easy to spot the change block.
Let's zoom in to understand the root cause.
As we can see, one call and slim move operations were added.
The root cause of this vulnerability is type confusion
due to the fact that ReadPROP variant,
reads a decimal from the file
without resetting VT to VT_DECIMAL
This means that we can control
the type of ReadPROP variant object.
The patch added a call to item read
and set VT to VT_DECIMAL to avoid type confusion.
Let's jump back to 2018.
An additional patch-related feature is number of conditions.
It compares the patched and unpatched version
of the file and looks for different amounts of conditions
in those functions.
In this example, the patch added five new conditions
to the function NTFS find files owned by SID,
changing the number of conditions between 27 and 32.
We automatically correlation between finding in our DB
and public exploits.
Going into the patch, we can see the five added code blocks
in the patched files on the right.
Their goal is to verify that the user has admin permissions
by calling SE token is admin and allowing the listing,
only if the user have listing permissions.
Let's move to vulnerability-specific features, Tomer.
- Thank you, Eran.
Until this point, we introduced patch-related features.
Now we will represent how these patch-related features
can be used to understand the vulnerability root cause.
We will go over a few examples.
The professor told us not to,
but we are bound to break rules.
Following our guidelines of learning from the past,
we look at high-profile integer overflow vulnerability,
the SMB GHOST, the patch usage function RtlULong
in order to verify that our location size
will not overflow before calling the allocation function.
Is this a unique fix
or should we expect Microsoft to use this fixed method
in other integer overflow vulnerabilities?
Let's go back in time to the earliest patches
we identified two binary defic.
We went back in time to 2016, Las Vegas DEFCON 25.
It seems that there is a searchable pattern
for the integer overflow vulnerability category.
The fix added Xref to the several ulong arithmetic function
before calling the vulnerable function.
In this example, it calls the ulong multiply function
in order to verify that allocation size will not overflow
before calling the allocation function.
We constructed a list of ulong functions
and added in-safe feature.
Our integer overflow feature,
we tell with more than 200 results.
We selected the vulnerability,
but as far as we know, has not been publicly analyzed yet
and see if the feature provide us
with automated root cause analysis.
We chose the NTDLL pitch from April, 2020.
The only function that was really changed
was LDRP search resource section
and the change included adding both RtlULong multiply
and add functions.
Indeed, the patch was implemented
by verifying that allocated mapping size will not overflow.
This was done using the same ulong function
as the vulnerability we saw in the previous example.
As far as we know, this patch pattern used since 2016,
so now we have a working integer overflow feature.
Sometimes features are very simple,
but are still very effective,
searching for all added functions
that include integrity level, expand the category
and provide several additional vulnerabilities.
There are additional features that we have researched
and found interesting.
One feature is vice-conditioned vulnerabilities,
which are usually fixed by adding log functions.
Another feature is direct traversal,
which are mitigated by detecting the ./ in strings,
we'll speak about it later.
And finally, symbolic link vulnerabilities,
which are very common and are usually mitigated
by adding a check for the final path.
Let's describe the second step.
Patch Tuesday occurs on the second Tuesday of each month
and includes a report for each patch vulnerability.
The report includes important data,
such as name, CVE number the affected OS,
vulnerability category and et cetera.
The cue is to have a description until mid 2020.
The information can help us focus on the relevant features
and the related executables.
We have created an automated process that uses the API
for downloading of all published CVE data since mid 2016.
Please note that Microsoft recently changed the API
and released the new one.
The coalition process consists of four steps.
First, take the previously extracted patches files,
then query all CVE data using Microsoft's API.
Then, extract the vulnerable component name,
we will refer it as VCN
from vulnerability CVE name or description.
And finally, call it the CVEs to the patch files
based on four different coalition methods.
First, we check if the VCN extracted for Microsoft's API
is a Windows service.
The list of Windows service names is available
in the registry.
As we can see, connected user experiences
and telemetry is the name of diagtrack service.
If the VCN is not a service name,
we will check if the VCN is included in the file description
of any of the system files.
As we can see, compatibility appraiser,
VCN, is the description of the appraiser executable.
The third method of correlation was built manually.
We classify those VCN
based on our Windows internal knowledge.
The last correlation is based on get all statistics.
Past association help in understanding the correlation
between VCNs and the vulnerable executable.
In this example, arrow reporting was the VCN,
which was found in three Patch Tuesday during 2020.
If we search for files which were patched
during these months and were not patched
in other 22 months,
we've found eight possible patched files.
Print spooler was the VCN in four monthly the patches
and was associated with 12 possible patch files.
Next time, we will see this VCN
as part of a future vulnerability report.
We will be able to correlate them to those files.
The four methods automatically produced correlation
of 90% of the CVEs.
Now that we know more about the context of each patch,
we can prioritize which changes are more interesting
and which are less.
Moving over to Eran.
- So we found the patch linked to the corresponding CVE
and found which type of vulnerability it was.
Now, how do you find which PE can trigger that code?
Who is calling that PE?
And which functions are called?
Our goal in step three is finding out
how to trigger the vulnerable function
and generate the code that will take it.
There are two sub steps for this step.
First, find which executables
can trigger the vulnerable code and the second,
generate the code with the correct input
to trigger the vulnerability.
We added new features that will extract
all the function calls from all the PEs in Windows
and for all the versions in order to generate a call graph
from or to any function in Windows.
We are talking about a very large scale,
we are talking about more than 26 million function calls.
Our graph is just like the graphs in IDA
but across binaries and huge.
With the data we collected, now we can create a call graph
across the entire Windows system.
In the picture you can see a visual representation
of the call graph we created in order to find
which functions are called to unwrap XML invitation.
Later on, we will demonstrate
how we are going to use this function
in past exploitation scenario.
We support multiple ways to detect a function call
from a function to another.
Load-time linking is the common way to call
for an external function.
Runtime linking such as gettalkaddress
and COM servers and clients.
We generate this class for Windows 8
and Windows 10 because after all,
we want to find 0-days in Windows 10.
In addition to creating the graph,
we collected information we need to choose
which function are the most interesting.
We do that by looking for function,
which are close in terms of distance
to the vulnerable function,
verifying whether or not the function is exported
and events scraping the internet.
We collected information and call examples
about each function for multiple sources,
such as MSDN documentation and scraping project in GitHub.
User existing examples from the internet
is much quicker than writing them ourselves,
especially when we are talking
about non-documented functions.
But we found that even that wasn't enough.
We were still missing lots of functions
so we had to find additional ways to generate PLCs.
So we want to have the option to trigger
all RPC servers quickly.
RPC is a common interprocess communication.
It can be over network or locally.
RPC calls use IDL, interface definition language,
to set up the communication
between the client and the servers.
In order to communicate with an RPC server,
we have to include the corresponding IDL file.
Therefore, we would like to extract from Windows
as many ideas as possible.
Since the original version of RPC view
support only extraction of a single interface IDL,
we use a modified RPC view tool to extract all IDLs
with all the interfaces.
The extracted IDL file
include dozens of sparks and unions.
Most of them raise compilation and all
due to order of definition
so we fixed that as well.
RPC view works on the running RPC servers,
so we had to start all of them.
After running a script
to automatically start Windows services,
we got 150 running services.
After we extracted the IDL from the opposite view,
we used the automated process.
For each interface, the automation created a new project,
setting the relevant parameters for the code
and compile it using MSBuild.
In the end, we successfully automatically generated
127 working projects.
We'll provide a specific example of how easy it is
to exploit the task scheduler ALPC vulnerability,
using our semi-automated process,
calling some of the functions
that the IDL exposes.
We generate and compile the template project.
The project includes two fives,
the IDL and the short generic template for settings
that needed opposite parameters for composed RPC binding.
In this example, the provider DUID, the protocol,
the RPC endpoint name,
are relevant to the task scheduler opposite.
This template allows us to call each RPC function
that we would like to take trigger.
After the automation created our project
with all the dependencies, comes the manual part,
writing the exploit itself.
All we need to do is call the correct functions
with the correct input and we'll get a working project,
we will get privilege escalation.
In order to exploit this vulnerability,
we created and had a link and did two RPC calls.
The first RPC call creates a folder
in the task scheduler for them
and the second RPC call sets the permissions.
This would trigger the task scheduler
to set the permissions of our executable,
resulting previous escalation to anti-system.
Moving on to Tomer.
- Until this point, we covered how we can understand
the root cause and trigger a 1-day in scale.
But how do we find 0-days?
We will use the XXE vulnerability as an end to end example,
to demonstrate the four-step process from 0 to 0-day.
We enrich our DB with the vulnerability category,
name CWE, for each vulnerability.
Then when we queried our DB
for the different CWE categories,
we noticed the 611 category improper restriction
of XML entity resonance
with six relevant vulnerabilities.
Let's query to see more details about them.
We eventually found eight patched vulnerabilities,
six of them with full details in our DB.
So we were curious, what is this vulnerability category?
As described by CWE, using XML with an external entity,
allows remote file read.
Hmm, sounds very interesting.
Let's dig deeper in order to understand how XXE happens.
XXE vulnerabilities occur in XML parsers.
When the XML contains a reference to an external entity,
such an HTTP server, this external entity
can get the content of local files.
XXE vulnerabilities contain multiple parts.
The upper entity is the input to the XML parser.
The XML parser reads the content of the file system, INI
and then sends a request to the attacker server
to get the rest of the XML.
The second entity, the lower one,
is the response of the server.
When the XML parser parses this response,
it generates an additional HTTP GET request
with the content of the file system INI.
This is according to the standard of XML.
And in order to prevent this feature in the XML parser,
the DTD, which stands for document type definition,
must be disabled.
Let's analyze the patches and understand the root cause
of these XXE vulnerabilities.
The patch for CVE-2018-0878, added four conditions
to a function called LoadRATicket in the MSRA executable.
We know that the MSRA executable is used for Windows
or remote systems where a basic user
can send an invitation ticket to advanced user
to log into his agent and remotely assist him.
This function probably loads the invitation ticket.
The LoadRATicket creates the XML DOM document COM object
and uses it to load and pass the XML invitation file.
In the patch version of this function,
in order to disable the support for external XML entities,
four properties were added,
first thing document, XS script, externals and prohibitDTD.
The rest, stay the same,
calling put a sync and load functions.
We also followed the Xref to the user COM CLSIDs.
We will get to it later.
We wonder, either a pattern for XXE patching,
so we develop a feature to search for all added
prohibitDTD patches and found 20 past patches.
Let's verify the first patch in the UPNP host.
We can see that HR load documents
now cause the restrict DOM documents.
Let's see who also calls this patch function.
The ISAPI extension executable was patched
by calling restrict DOM document before a call to load XML.
This is a different function than the load function
used by remote assistance.
So there are probably other vulnerable function
used by the same COM object.
Digging deeper, we found a third vulnerable function,
set XML.
At this point, we understood the root cause
and define the conditions
for exploiting the vulnerable code.
Let's dig into the first condition.
We develop a code to trigger COM object.
First, we queried all CLSIDs
from all the files in Windows 10.
Then for each CLSID,
we enumerated all functions and interfaces
then we generated source code,
which created each one of the COM objects.
And finally, we called all these function
with an XXE XML file as input.
We automatically discovered the vulnerable COM objects
to the filtration data by adding the vulnerable CLSID to it.
As we can see, we found four common interfaces
and 16 vulnerable CLSIDs.
We developed a new feature to detect all the locations
that seems to be vulnerable to XXE.
This feature is different from other feature,
it does not compare between unpatched and patched version.
Instead, it searches for XXE condition
in all Windows 10 executables.
Eventually, we found 52 candidates,
25 of them are marked as possibly vulnerable.
They will be vulnerable if one last condition is met,
if we are able to control the input of the past XML.
For example, we can see
the MSRA patch function LoadRATicket which is patched,
while at the same time,
the LoadandSOTARAInvitationsHistory function
might still be vulnerable.
Let's see if we can control the input XML.
At the bottom, we can see the patched XML DOM object
that was used in the original vulnerability
and was called from LoadRATicket.
To our surprise, there is a second instance
of the exact same vulnerability XML DOM object,
just a few commands above used by several functions,
including our possibly vulnerable function
LoadandSOTARAInvitationsHistory.
LoadandSOTARAInvitationsHistory, indeed,
uses the vulnerable load function.
We can see that the file to be loaded
is the parameter A2.
The GetInvitationManagerLoaded function
calls our vulnerable function with the XML file
RA contact history from UB data.
This file is controlled by the user
with no need for admin privileges.
This is the history data read
from a legitimate RA contact history XML file
that we changed to contain the address, 1111
as the previous IP requested remote assistance.
Now we know how to trigger the vulnerability.
So let's begin with the fun stuff.
On the bottom left, we see the MSRA
that was run with offer RA as a parameter.
It can be reached also with double-click on MSRA.
In Pokemon, you can see the reduction of the RA export
is the refile and it's also it's content,
which includes the exploit code.
On the right, we can say the first DTD GetRequest
for XXE XML, which reads remote file
from the Windows 10 machine
and ex filtrate it to the C2 server
using a second GetRequest.
Microsoft fixes this MSRA vulnerability we found
and assign it the ID, CVE-2021-34507.
In total, we discovered six vulnerabilities.
All were reported to Microsoft.
We will go over them one by one.
The most critical vulnerability we found
was the vulnerability in the Windows Help.
We sent a spear phishing mail with a compressed attachment
of an HTML file.
We named the HTML file to include .chn twice.
This forced the vulnerable process HH executable to pass it,
although it's plain HTML and not a CHM file.
We can choose almost any extension to this file
and it will still be passed.
We tested the attack on default
fully-patched Windows 10 machine
and got blocked by the mark of the web mechanism.
But when we compress the attachment,
even without encryption,
it bypassed the sandbox and ex filtration was successful.
The third liability we found in MMC,
include several snappings.
The link to a web address snapping, expects an HTML file,
but if we will provide an XXE XML exploit instead,
it will be passed by MSHTML. DLL.
We can see on the top right
that it will read the remote file
and actually fight it to the C2 server.
It will provide an error message
which includes the content of the ex filtrated file.
To trigger the Windows Media Player remote file
read vulnerability, you will only need to open
the option screen and select the network top.
This will trigger vulnerable parsing of the XML file,
which we can control.
On the right, you can see the structures.
Please pay attention to the calling
of the vulnerable load function from MSXML3 DLL.
Moving over to Eran.
- In addition to finding vulnerabilities in native code,
we want them to discover 0-days in managed code.
So we added a feature that compiles
all .Net executables in windows 10 using ILSpy.
ILSpy de-compiles each executable
to a visual studio solution with Cshop files.
The files look very similar to the XL code,
even the parameter names and function names are kept.
We sell inside all the .Net files
for non-classes and functions
that are vulnerable or configured to use DTD.
All the XML parses with DTD processing enabled,
are vulnerable to XXE, such as XMLTextReader.
We found two XXE vulnerabilities
in official Windows SDK executables.
We found vulnerabilities in XXD,
the utility to generate disclaimer
or a class from a given source
and another in XSLTC.
In order to exploit a vulnerability in XXD,
it is required to execute it with a path to XML file,
which contains the XXE.
Unlike XSD, XSLTC requires multiple parameter
so it is a little bit more complex to trigger.
In addition to the vulnerabilities we found,
we found two XXE past exploitation techniques.
We can use them to ex filtrate text files using DLL
signed by Microsoft instead of using
suspicious network APIs.
This increases the odds for bypassing security controls.
The first one is P2P, PeerGroupParseInvitation.
It is documented function found in MSDN
and the root cause of the XXE
found in internal function named unwrap XML invitation.
We traced back to find which function
it is called from, using our call graph.
We found multiple options,
but we could not find any attack vector
that can trigger these functions
without calling one of them ourself.
The second past exploitation technique
is found in set XML in PLA DLL.
In order to trigger that function,
we used one of the examples provided by MSDN,
so we had to change only a few lines of code.
In June, 2020, Microsoft released a fix to CVE-2020-1300.
It is a directory of XL vulnerability.
You can see that they declared to fix it
on Windows 8 among other Windows versions.
Based on our correlation capability
between the CVE report and the relevant patch files,
we understood that the patch provided partial coverage
so we dug deeper.
Using our directorate of our XL feature,
we searched for any function with ../ or ..\
as an argument.
We found that on June, 2020,
Microsoft indeed patched two files,
localspl and win32spl
but the patch to the PrintBRM engine
was done only in August.
We compared the pitch to the paths of Windows 10
and found that on Windows 10,
all three files were patched in June.
Does it means that Windows 8 was vulnerable
to a 1-day for two months?
The PrintBRM engine executable was patched only in August
and with the same patch logic used in Windows 10 in June
and in two WCSDR function for mitigating ../ attacks.
The PrintBRM engine was not patched in June, 2020.
We verified that in Windows 8, in June,
it was vulnerable to 1-day attack of CVE-2020-1300.
The PrintBRM engine allows importing of a CAB file.
If the CAB file contains a file name
that contains ../charles,
it will extract the file to an attacker-controlled location,
such as System32 wbem,
which is prone to DLL hijacking.
We proved that it was possible to have RCE
on fully patched Windows 8
between June and August, 2020 with 1-day.
We also found an additional unpatched issue in 2020.
This time the vulnerability provided
arbiter delete capabilities.
Normal search is needed in order to determine
whether this is a new opportunity
for finding low-hanging force
or just a coincidence.
Microsoft response.
Regarding the MSI vulnerability,
the bug was fixed as part of July Patch Tuesday.
Regarding the other vulnerabilities,
no fix is currently planned
since they don't meet Microsoft service bottom.
We are encouraging you all to use and expand our tools
that will be published today.
We think that the vulnerabilities we found
are just the tip of the iceberg.
We would like to credit multiple researchers.
We based our research on top of their previous results.
Thanks for joining, moving on to the Q&A session.