Monday, 30 March 2026

Blog 3 - Windows Vulnerability Research - Certificates and Asn.1

Disclaimer: This blog series won't be very technical. It is intended to be easy to understand and follow. Like most of my blogs, I have no reason to be peacocking with technical jargon like all the infosec losers.

Overview and donation information: https://weirdquadratic.blogspot.com/p/blog-overview.html

To the reader: Feel free to share anything I write with those who need it. And be sure to make local copies as I am sure it is a matter of time until this blog will be taken down as well.

Introduction

In the previous blog we found an integer overflow by providing custom user mapping data via the Supplemental Data handshake message. Which allowed us to overflow an unsigned short length field which was passed to a memcpy() call and subsequently set a field in a structure that is then passed as an argument for a LsaLogonUser() call. However, to get this deep into the code, we had to use a valid client certificate. The authenticated attack surface is still interesting, as it could allow us to perform a domain level privilege escalation attack. However, the true doomsday-type bugs are found in the fully unauthenticated part. Lets begin by digging deeper into certificates and what we can do with them.

Enabling pageheap and application verifier

For any target you are researching, at a minimum you should have pageheap enabled.

Both application verifier and pageheap can be enabled with the gflags application.

https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/gflags

You can download gflags as part of the windows debugging tools suite, which comes bundled with either the Windows SDK or WDK.

https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools

Pageheap has all different sorts of tricks to detect heap corruptions. And application verifier can help to find a whole range of other issues. You can find plenty of information about this online, hence I won't cover it here.

One thing to keep in mind is that Pageheap adds a "fill pattern" (for example 0xc0c0c0c0) in different places. One being uninitialized heap memory. This means that if you spot code using a page heap fill pattern in your debugger, it likely means it copied uninitialized data (or read outside a bound) somewhere. Hence it is important to keep an eye out for these.

Install gflags on your Windows Server VM and then launch gflags and enable pageheap and application verifier for lsass.exe using the GUI (you can use command line too):

Next be sure to restart your VM.

A closer look at certificates

If we run the proof of concept from the last blog (https://github.com/BigPolarBear1/Blog/blob/main/Blog%202/tls_integer_truncation.py)
and capture the result with Wireshark we will see the following TLS handshake messages:

The main code that routes all these handshake messages to the correct function and manages the overall state for TLS 1.2 can be found in the following function:

CSsl3TlsServerContext::ProcessHandshake

You can read up more about the TLS handshake online. I won't spent too much time on it. In the above Wireshark capture you see two "Certificate" handshake messages. First we have one send by the server after the Server Hello message. This is simply the certificate associated with the website. A browser can use this to verify it is talking to the correct website and traffic isn't being intercepted and/or modified.

Further down after the Supplemental Data message we see another Certificate message. This is the certificate send by the client for client authentication. While TLS mutual authentication (the server requesting a client certificate) is usually only used to protect access to resources, as seen commonly in corporate environments, its use is still widespread enough to be of interest to an attacker. Especially since protecting a resource with mutual authentication usually implies that the resource being protected has some importance and thus value to an attacker.

The initial parsing of a user supplied certificate is done in CSsl3TlsContext::DigestRemoteCertificate

Opening schannel.dll in Binary Ninja and navigating to CSsl3TlsServerContext::ProcessHandshake we can see where this function is called:

Lets put a breakpoint on CSsl3TlsContext::DigestRemoteCertificate, run the PoC from previous blog and create a function trace.

First place a breakpoint in your debugger (see blog 1 and 2 on setting up a debugger using either windbg or binja):

bp CSsl3TlsContext::DigestRemoteCertificate

Run the PoC from previous blog from your Windows Enterprise machine and make sure the IP at the bottom of the PoC is correct:

python tls_integer_truncation.py

After the breakpoint hits, run the following command to create a function trace:

wt -i ntdll -i KERNELBASE -i CRYPT32 -i verifier

This should yield a function trace similar to the one below:

If you omit i -CRYPT32 this trace ends up being much larger and we will also see all the ASN.1 parsing related function calls:

ASN.1 is a standard for encoding and decoding data. I highly recommend the following introduction on ASN.1: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/

Looking at the above function traces, we can somewhat make sense of what is happening by just looking at the symbolic names. It is basically going to perform ASN.1 decoding of all the different certificate fields and copy the decoded information over to an internal object in memory. Which then gets used later in DoCertificateMapping() and all the functions we saw in the previous blog. Knowing where all these fields are populated is important, hence why I quickly mentioned it.

Signature Algorithms

Looking at the earlier Wireshark capture again. After the Certificate message send by the client, the client also sends a Certificate Verify message.

A beautiful property of asymmetric cryptography is the fact that having just a public key, using mathematical properties, one can verify, if a signature is indeed created by someone who possesses the matching private key, without the verifier having knowledge of this private key.

This is what Certificate Verify does. As defined by the following RFC: https://datatracker.ietf.org/doc/html/rfc5246#page-62

Looking at the client certificate being sent in the Certificate message in Wireshark, we see it is comprised of 4 sub fields:

The signedCertificate field, is also commonly known as the TBSCertificate field (TBS = to be signed).

Next we see the algorithmIdentifier field, this is basically information about the algorithm used to create the signature.

Ignore the padding field, this is not commonly used I believe. This does have the interesting property that it does not affect the signature. And seems to be a common mechanism for hiding exploit payloads.

And at the bottom we have the encrypted field. Which is the signature itself.

The way a signature is created is very simple. It creates a hash of all the fields inside SignedCertificate, using the hashing algorithm indicated by the algorithmIdentifier field.
Next it encrypts the generated hash using the private key (which must be the private key matching the public key listed inside SignedCertificate) and this result ends up being the encrypted field.

This encrypted field is then used during the Certificate Verify step of the TLS handshake to make sure that the certificate is signed using the correct private key and in addition it is also used to guarantee integrity of all the data inside "signedCertificate" to make sure no modifications have occurred. The actual math behind digital signature algorithms is very cool, but a little out of scope for these blogs. But perhaps in the future it could be a cool topic to write some python and do a deep dive into all this math (in a way that makes sense to non-math folks or amateur math enthusiasts such as myself).

Next we will write some code that lets us create a custom "signedCertificate" at the byte level and use this blob of bytes to generate a signature for the final certificate. The problem with many public APIs like OpenSsl is that it is very difficult to create signed certificates using malformed data, but as a security researcher, this capability is a must have. Luckily the python cryptography module exposes all the functionality we need.

By signing our custom certificates correctly, it should allow us to get past the certificate verification step and call into DoCertificateMapping(), while being able to modify all the individual fields of a certificate at the byte level.

Asn.1 and generating custom certificates

I here present a script, to create certificates at the byte level and then sign them:

https://github.com/BigPolarBear1/Blog/blob/main/Blog%203/buildcert.py

The code that represents the certificate as a tree data structure is some old code I had laying around from long gone glory days.. which I quickly repurposed for this blog.
For using the cryptography python module to then self sign the generated certificate, surprisingly the free version of Claude was able to spit out the correct code for this.
While trivial to just google it myself, that did surprise me and it is probably the first time in my career AI turned out to be of any use at all. But again, when I then tried to have it create math related code, it completely missed the mark. It appears AI is very strong when there are plenty of examples online, and you can even see exactly which code it scraped from the internet is being used.
However when it comes to a truly niche topic with very little public code samples.. it just produces hallucinated garbage. Which was quite a relief to see actually.
Yes, AI can save time if you use it as a glorified search engine.. but aside from that, I don't see much utility in it and I honestly hate it and will never use it aside from quickly looking up very basic things when I'm too lazy to google it myself.

Lets quickly go over the code an explain what happens:

The very first function call is to buildcert().

sng_cert=Asnc("sgnd_cert") is the parent node of the tree data structure. Then it defines all the other nodes in the tree and defines their parent node.

Scrolling down in buildcert() we then see calls to set_data. This sets the data for each node and also defines the asn.1 type. One important thing to note here are the following lines:

This reads the public key from a certificate encoded as a pem file and returns a byte object to be embedded into the certificate we are generating with our code. We do this because the public key here must match with the private key it is signed with else the verification step will fail and we'll never be able to hit DoCertificateMapping().

The naming conventions used are not great as when I wrote this, I had not intended to ever share it. But shortly after I wrote it my entire life collapsed and I never really finished doing what I had intended to do with this project. But we are slowly working toward that now, and eventually you will see what the plan here was :).

Then next we have a call to calculatelength() and build().
Calculatelength() will basically propagate all the individual length fields of each node in the tree and adjust the length fields of all the parent nodes. Then in build() we flatten everything into a byte object. It is very simple.

What this also allows us to do is fuzz for example at both the node and data level. This was my initial motivation to represent it like this. But now I am sharing it, so feel free to cause mayhem. Anything related to asn.1 parsing is usually an interesting target to fuzz.

Lastly, we will sign it using our private key, this adds the Encrypted field and AlgorithmIdentifier field, discussed earlier, to the certificate and we save the completed certificate to disk.

Bringing everything together

To build a certificate and use it with our PoC from the previous blog, follow these steps:

Step 1:

In buildcert.py change the following fields:

certificatepath="certificate.pem"
privitekeypath="private.key"
outputname="blah.der"

I have used the same key and certificate from the client certificate we generated in the previous blog. You can use an arbitrary key and certificate to self-sign, but do keep in mind that certificate chain building will fail then.

Step 2:

run: python buildcert.py

Modify the proof of concept from the previous blog (https://github.com/BigPolarBear1/Blog/blob/main/Blog%203/tls_integer_truncation.py)
And make sure we point it to the certificate we just generated:

t = TLSClientAutomaton(server="172.26.178.47", dport=443, version="tls12",server_name="bear.com",mycert="blah.der",mykey="private.key")

In addition, make sure the IP is set to the IP of the windows server VM.

Step 3:

run: python tls_integer_truncation.py

If all goes well, the TLS handshake should succeed, meaning we succesfully performed client authentication. When recording the traffic with Wireshark, we will see our bogus issuer appear which we have set in buildcert.py (if not something went wrong):

The next blog

We'll stop here. The main purpose of this blog was showing how we can modify and send custom self-signed certificates that we can modify at the byte level. In the next blog, we will use this as a tool to find some bugs in certificate chain building related code. 

Blog 2 - Windows Vulnerability Research – Digging deeper into Secure Channel

 Edit: Oops, I was tired yesterday and initially called the bug at the bottom of this blog an integer truncation bug, but to be technically correct it is an unsigned integer overflow bug. Was tired and making some weird abstractions in my head.

Disclaimer: This blog series won't be very technical. It is intended to be easy to understand and follow. Like most of my blogs, I have no reason to be peacocking with technical jargon like all the infosec losers.

Overview and donation information: https://weirdquadratic.blogspot.com/p/blog-overview.html

To the reader: Feel free to share anything I write with those who need it. And be sure to make local copies as I am sure it is a matter of time until this blog will be taken down as well.

A high level strategy for finding software vulnerabilities

In the previous blog post (Blog 1) we set up debugging and triggered a top-level function in Secure Channel.

Generating a trigger PoC for a top-level function (an initial entry point for an attacker) in a software component is nearly always my first step. This can be the most frustrating part, especially if you are trying to craft a trigger PoC for something very complicated or poorly documented. Once you have a trigger PoC however, the next step is to begin exploring!

The code exploration step is usually the most time consuming but at the same time, the most relaxing once you possess a baseline of code reading and/or reverse engineering skills.

Software issues usually occur in areas of high complexity. This can be a lot of different threads interacting on the same objects. Or very complicated parsing of user input. Another area of interest is the interaction between different components. For example in the case of IIS, where Secure Channel returns to http.sys. Because http.sys and Secure Channel are likely written by different developers, so they may not have made correct assumptions about the format of data being returned and visa versa.

Digging deeper into Secure Channel

There is a feature in Secure Channel called “Mutual Authentication”. What this means is that both the client and the server must authenticate themselves. This is useful to restrict access to certain resources while also leveraging existing PKI-infrastructure.

The easiest way to enable Mutual Authentication is with the “Internet Information Services (IIS) Manager” GUI.

Forcing a certificate request

In the “Internet Information Services (IIS) Manager” GUI's left-hand pane go to <computer name> → sites → default Web Site. First Select “Ssl Settings” from the middle pane and make sure “Require Ssl” is enabled and “Client Certificates” is set to “require”.

Enabling Activate Directory Certificate Services

Next rather than using a self-signed certificate, let us use a proper domain certificate, while not needed for this tutorial, it will be useful in the future.

First we should enable “Active Directory Certificate Services”.

To do this go to the “Server Manager” and in the middle pane selected “Add roles and features”.

Then select as Installation Type “Role-based or feature-based installation” and under Server Roles select “Activate Directory Certificate Services”. Then go ahead and install.

Once that is done, a button should appear to configure the Certificate Services

Under Role Services select “Certification Authority”.
Under Setup Type select “Enterprise CA”.
Under CA Type select “Root CA”.
Under Private Key select “Create a new private key”.

Then just use the defaults for the rest and finish configuring.

Generating a domain certificate

From the “Internet Information Services (IIS) Manager” window go to (if it was already open before installing the certificate services, you may need to re-open it) <computer name> on the left-hand pane and in the middle pane double click “server certificates”.

Next on the right-hand pane click “Create Domain Certificate”

Make sure to set the common name (CN) to your domain (I.e bear.com) and on the next screen as Online Certification Authority, you should be able to select the one we just installed. Then finish.

Next on the left-hand pane go to <computer name>-> sites → Default Web Site

Select “Bindings” and click edit on the “https” binding we created in the previous blog and change the self-signed certificate to the domain certificate we just created. In addition make sure we enable “Negotiate Client certificate” and for simplicity also disable the use of TLS 1.3 for now, since TLS 1.3 is a little bit more complicated and harder to debug.

Next in the windows search bar, search for “Manage computer Certificates” and just go to personal → certificates and double click on the certificate we just created. Then go to the details tab and find “thumbprint” and copy this.

Open a terminal and run the following curl command:

curl --cert "LocalMachine\\MY\\02d8ff631752419efd659a4bf5f0cf0c91c62700" -v https://bear.com:443

Replacing the thumbprint part in --cert with the one you just copied and the site url with your domain name.

If all goes well it should throw an error about an invalid client certificate:

<h3>HTTP Error 403.16 - Forbidden</h3>

<h4>Your client certificate is either not trusted or is invalid.</h4>

(and so on)

This is not important for now.

Next open Wireshark and record on the loopback interface (or ethernet interface if running curl from a domain joined machine) and rerun the curl command.

You will see a "certificate request" handshake message coming from the IIS server. This only happens when mutual authentication is enabled and basically indicates to the client it is expecting the client to provide a certificate. Next in Wireshark you will then see the client providing a certificate (albeit a wrong one).

Let us trace this handshake with a debugger.

But first, we must download binary ninja and have a quick look at the decompilation so that we know where to put breakpoints.

Download Binary Ninja from here: https://binary.ninja/free/

Once Binary Ninja is installed, on your Windows Server Virtual Machine go to c:\windows\system32 and copy past schannel.dll to your host machine.

Launch Binary Ninja and Open schannel.dll.

Find the following function: Cssl3TlsServerContext::ProcessHandshake in Binary Ninja

This function is responsible for routing all the handshake messages to the correct function.

If we scroll down in this function we will see a call to Cssl3TlsContext::DigestRemoteCertificate.

This function is responsible for parsing incoming certificates. Be it from the server or client context.

Further down you will see another function called DoCertificateMapping.

This function will do the actual client authentication.

Launch a debug server on the Windows Server Virtual Machine using the following command (see previous blog for detailed instructions):

dbgsrvX64.exe -t tcp:port=1234

To make things a little easier, we can also connect to a debug server with binary ninja.
In “ debugger” select “Connect to debug server” and enter the port and ip.
Then press accept. Then go again to “debugger” at the top and this time select “Attach to process”. Then just press accept again and a small windows listing all the process running on your Windows Server VM should appear, find and select lsass.

Type bp schannel!DoCertificateMapping into the debugger window to place a breakpoint.

Let the debugger run again and rerun the curl command. If everything is setup correctly, this should trigger the breakpoint.

Now we can step through pseudo code using F7 and F8, which makes things a little easier. However, I do have a bug where the function names are not showing up (I'm using binja for the first time myself, I'm normally an IDA user, but it is too expensive), so I have to open up a second copy to see the function names in the pseudo code. If anyone knows how to properly load function names while debugging, please let me know.

With the debugger broken into at DoCertificateMapping there is a very easy trick to quickly make sense of all the code it ends up calling from this function onward. We can create a function trace.

https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/wt--trace-and-watch-data-

This is elite level windbg magic, so please do not share with the plebs.

wt -i ntdll -i KERNELBASE -i CRYPT32

(this will ignore ntdll, KERNELBASE and CRYPT32 from the trace, as they can take up quite a bit of time otherwise)

The following trace will be created:

If we actually scroll through DoCertificateMapping in Binary Ninja, we will see a call to SslMapCredential. This does not appear in our trace.
So somewhere in one of the certificate chain building functions, we are failing. Lets see if we can get SslMapCredential to hit by configuring client certificate authentication.

Enabling IIS Client Certificate Mapping Authentication

In the server manager, we can install “IIS Client Certificate Mapping Authentication”. There is also an option to use “Client Certificate Mapping Authentication” and utilize Active Directory but we will explore that later as it is a little bit more involved to setup.

In the Internet Information Services (IIS) Manager UI go to <computer name> → sites → default web site and open the configuration editor. Navigate to system.webServer/security/authentication/iisClientCertificateMappingAuthentication

and change “enabled” to “True”.

Rerunning the curl command again and SslMapCredential should hit!

Creating a certificate to use for client authentication

Let us also create a valid client certificate we can use. Just the see what the normal execution flow is supposed to be.

Ideally we should use Active Directory Certificate Services to create a certificate for client authentication, but setting all of this up correctly can be a pain and we will focus on this in a later blog post.

We can use the instructions here to quickly generate a client certifcate: https://learn.microsoft.com/en-us/previous-versions/msp-n-p/ff650751(v=pandp.10)?redirectedfrom=MSDN

If you run the command at the bottom of that tutorial, it will install a client certificate in your user certificate store. You can then export it as a .pfx file and install it on your Windows Server virtual machine into the Current User -> Personal certificate store. Once you do that, grab the thumbprint and rerun curl.

curl --cert "CurrentUser\\MY\\9ef0e3693d025806aed6028567e7f08446c0e737" -v https://bear.com:443

When the breakpoint hits on SslMapCredential, we create a new trace using:

wt -i ntdll -i KERNELBASE -i CRYPT32.


We now see that we end up calling into LsaLogonUser. Which is the main api for authentication.

https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsalogonuser

For the purposes of this tutorial, we have burrowed deep enough into the code now. Lets see if we can find a bug.

The case of the unsigned integer overflow bug

Prior to DoCertificateMapping we call into Cssl3TlsServerContext::DigestCertVerify. Which basically verifies the digest of a certificate. So to construct a PoC (rather then using curl) to get past this is a little more elaborate then simply sending raw bytes over a socket.

Luckily there are TLS clients we can repurpose.

Installing Scapy

Scapy comes with a TLS client which is easy to modify. So let us go ahead and install Scapy.

In the previous blog we installed python on the Windows Enterprise (client) VM. So let us run our PoC from there. Install scapy on the Windows Enterprise VM by following the instructions here: https://scapy.readthedocs.io/en/latest/installation.html#windows

Note: If internet is not working, just remove the dns setting (in adapter settings, see blog 0) so we arn't pointing to our own dns server anymore.

In addition also make sure the cryptography module is installed by running:

pip install cryptography

Next download our proof of concept: https://github.com/BigPolarBear1/Blog/blob/main/Blog%202/tls_integer_truncation.py

edit: Actually this is an usigned integer overflow bug. Not truncation. Was just mentally tired yesterday and making some weird abstractions in my head.

This is simply a modified version of the scapy TLS client: scapy/scapy/layers/tls/automaton_cli.py at master · secdev/scapy

We will need the client certificate and private key which we generated earlier. If you go to the certificate user store where it was installed, you can extract it as .pfx and use OpenSsl to convert that to a seperate key and certificate file which can be used with the Scapy script, using the following commands:

For the private key:

openssl pkcs12 -in clientcert.pfx -out private.key -nocerts -nodes -passin pass:YourPassword

For the certificate:

openssl pkcs12 -in clientcert.pfx -out certificate.pem -nokeys -passin pass:YourPassword

The proof of concept will trigger an unsigned integer overflow bug. This works by providing a supplemental data message: https://www.rfc-editor.org/rfc/rfc4680.html

At the very bottom of the PoC modify the following line (line 1502):

t = TLSClientAutomaton(server="172.20.136.114",dport=443,version="tls12",server_name="bear.com",mycert="certificate.pem",mykey="private.key")

Make sure to change server to the IP address if your Windows Server VM and the server_name to that of your domain. Also provide the client certificate and key.

The first modification I have made in the scapy TLS code is adding a new extension for the TLS ClientHello handshake message.

If we run the proof of concept and record with wireshark we will see these same bytes in our ClientHello:

This is the TLS User Mapping Extension, as defined by the following RFC: ietf.org/rfc/rfc4681
This extension will allow us to send a Supplemental Data handshake Message and provide custom User Mapping Data. Which is normally included in the client certificate, but can also be defined with the Supplemental Data handshake Message instead.

I have modified the scapy code and added the following lines to send a TLSSupplementalData message:

What this does is send a TLS Supplemental Data handshake message after receiving the certificate request by the server. Which we can observe in the above WireShark capture.

The CSsl3TlsServerContext::DigestSupplementalDataMsg function is responsible for parsing this handshake message.

By setting the size of the user mapping data to 0x7fff, we end up with a unsigned integer overflow bug in SslTryS4U2Self. Let us observe what happens.

First we have a do while() loop. This iterates the provided user mapping data until a null-terminator is reach. After this loop rax_5 will be set to 0x7fff. Next we increment this value and then multiply by 2, but since we are dealing with the ushort data-type, we now end up with an unsigned integer overflow. This overflowed value is then saved to the r13 register. It does further down check if AuthenticationInfromationLength_1 is smaller then the maximum positive value for a signed int32, however, that does not stop the overflow bug.

And we then see this overflowed size being used in a call to memcpy:

What ends up happening now is memcpy just returning an empty array. Since we passed the overflowed 0-size to memcpy. So we basically end up with an empty field in the structure that ends up getting passed to LsaLogonUser. I have not been able to come up with a way to exploit this. Perhaps if an endpoint assumes that this field should be populated and doesn't perform a size check, then yes, maybe this could result in a bug. There is a handful of integer overflow bugs in Secure Channel which I never ended up reporting, because I believe the minimum bar to report a bug should be a crash PoC (Of-course don't report bugs to Microsoft either way, just drop them). However, I feel like this is a nice bug to start this blog series with. In the next blog post we will start digging much much deeper and attempt to find some bugs we can actually exploit.

Edit 2: I was thinking a little bit deeper about this bug last night, there may also be a path to exploit it if it is utilized during chain building in Secure Channel. Because then we may have an incongruence between what was used in Secure Channel and what ended up getting passed into LsaLogonUser. Definitely an angle worth exploring, but I will leave it to the reader as an exercise. Anytime you are able to perform any behavior at all, which wasn't intended by the developer, the next step is to explore every angle for abuse. Be it from a memory corruption perspective or logic perspective. Sometimes this yields nothing (most of the time), but sometimes you strike gold.

Blog 1 - Windows Vulnerability Research – Debugging, intro Secure Channel

Disclaimer: This blog series won't be very technical. It is intended to be easy to understand and follow. Like most of my blogs, I have no reason to be peacocking with technical jargon like all the infosec losers.

Overview and donation information: https://weirdquadratic.blogspot.com/p/blog-overview.html

To the reader: Feel free to share anything I write with those who need it. And be sure to make local copies as I am sure it is a matter of time until this blog will be taken down as well.

In the previous blog, we set up two Virtual Machines in Hyper-V and created a domain. Next, we need introspection. We need to be able to see what happens inside these machines.

A large majority of cryptographic components is handled in lsass.exe (Local Authority Subsystem Service). These components also tend to be full of complexity and are especially interesting to an attacker.

The lsass.exe process runs at SYSTEM-level privileges. Meaning any take-over of this process results in the take-over of the machine, and possibly even the entire domain depending on the target.

There is two ways we can debug lsass.exe, one is through a kernel debugger, and another one is through a remote process server.

Disabling exploit mitigations

For debugging userland code, a userland debugger is almost always preferred. However, lsass.exe has a lot of exploit protections as it is a very popular target for privilege escalation which can make attaching a userland debugger troublesome.

To get a userland debugger attached, we must first disable all of the exploit mitigations for lsass.exe. We can enable them later when developing an exploit.

In the windows search bar, type “exploit protection” and open the exploit protection menu. Next go to “program settings” and select “Add program to customize” and then “Add program by name” and enter “lsass.exe”.

Once that is done, enable “override system settings” for each exploit protection and make sure they are indicated as “off”.

Once that is done, reboot the machine.

Downloading a debugger

Download Windbg on your host and on the Windows Server virtual machine.

https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/

Alternatively from an elevated terminal run:

winget install Microsoft.WinDbg

Launching a process server and connecting

From an elevated terminal in the Windows Server virtual machine (after installing Windbg)

Navigate to the following folder:

C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps\Microsoft.WinDbg_8wekyb3d8bbwe

Then run the following command:

dbgsrvX64.exe -t tcp:port=1234

On your host computer open Windbg and go to “connect to process server” and enter the following connection string: tcp:server=<ip>,port=1234 (where the ip is the ip of your Windows Server virtual machine)

If that doesn't work you may have to disable firewall.

After connecting you should see the following:

Next select “Attach to process” and go ahead and attach to lsass.exe. The PID (Process ID) should match the PID of lass.exe on your Windows Server virtual machine if the above steps completed succesfully.

If all went well we will be greeted by the following:

Cheers, we now have achieved introspection into lass.exe.

Press the "Go" button or type g to continue execution.

Intro to Secure Channel

Secure Channel, located in schannel.dll, is Microsoft's implementation for the TLS protocol. While Microsoft Edge (browser) uses a modified version of OpenSsl to handle TLS connections from the client side, a lot of enterprise features use Secure Channel instead.

Of-course, this is a lot of fancy words, but what does it do?

There are two types of cryptography, symmetric cryptography and asymmetric cryptography.

Symmetric cryptography is by far the fastest. However it relies on the fact that both parties communicating with each other have knowledge of the same secret key to encrypt and decrypt traffic.

To make sure both parties have the same secret key, asymmetric cryptography was invented. As we cannot simply share a secret key over plain text since anything that gets transmitted over a network, or the internet, can be intercepted by an adversary.

Asymmetric cryptography basically establishes a “secure channel” over which we can transmit symmetric keys safely.

Encrypting and decrypting using symmetric keys is usually referred to as “bulk encryption/decryption”, and this is the preferred method as it is by far the fastest. Hence to achieve this, we must always leverage some form of asymmetric cryptography first to distribute shared keys. And that is what schannel.dll (literally called Secure Channel) is for.

While the actual details are far more complex, you should always make simple abstractions mentally. Details you can always read up about later when needed, as long as you understand things abstractly at a high-level. Abstractions are the mind's greatest tool.

Setting up IIS with TLS support

A simply way to gain access to the Secure Channel component is by installing IIS and enabling TLS support.

From an elevated powershell terminal enter:

Install-WindowsFeature -name Web-Server -IncludeManagementTools

Next in search bar type “internet information services” and open the IIS manager.

On the left-hand pane go to <computer name> and select Server Certificates. Then on the right-hand pane, select “Create Self-signed certificate”

Follow the instructions to create a self-signed certificate and select “web hosting” as the certificate store.

On the left-hand pane go to <computer name>->sites ->Default Web Site and then in the right-hand pane select “bindings”.

Next select “Add...” and as type select “https” and set the SSL certificate to the one we just created.

Installing wireshark

To debug network traffic, wireshark is a great tool. If we end up handcrafting packets this lets us verify easily if they are crafted correctly and are correctly arriving at the intended target.

Download and install Wireshark on the Windows Server Virtual Machine.

https://www.wireshark.org/download.html

Triggering Secure Channel from an attacker machine

On the windows 10 Enterprise machine which we domain joined in Blog 0. Login to the domain (Note: Not required, will work without domain joining as well) by preceding the username by your domain name, in my case: BEAR\Administrator.

Download the following python script: https://github.com/BigPolarBear1/Blog/blob/main/Blog%201/tls_test.py

And change the ip at the top of the script to the ip of the Windows Server Virtual Machine.

In addition, grab python v3 from the Microsoft store (I'm using v3.13) so that we can actually execute the script.

Next on the Windows Server machine open Wireshark and begin recording traffic on your main network adapter and filter by src ip using the following filter: ip.src == 172.29.229.175, where the source ip is the ip of the windows enterprise machine (client) on which we just downloaded the python script

Run the python script on the Windows Enterprise machine:

python tls_test.py

In wireshark you should see a bunch of “TLS client hellos” appearing.

Placing a breakpoint in Secure Channel

In future blogs we will try to use the free version of Binary Ninja to figure entry points in our code.

First break into the debugger and type “ld *” to make sure we load all the symbols.

Next type “lm” this will show all the available modules in lsass.exe. This is very useful for piecing together what code we may be able to attack in a certain process. Find "Schannel" in the list.

Alternatively type “lmDvmschannel”

Once you found schannel, select "functions" to display all available functions for which we have public symbols. Or use the following command “x /D /f schannel!*”

One of the function names will be: schannel!CSsl3TlsServerContext::ProcessHandshake

For TLS 1.2 traffic, this will be responsible for routing TLS 1.2 handshake messages to the correct functions, depending on the stage we are at in the handshake process.

Place a breakpoint:

bp schannel!CSsl3TlsServerContext::ProcessHandshake

If we then re-run our python script and all is set-up correctly up until this point, we should get a hit on the breakpoint.

Together with blog 0, this covers all the basics. In the next blog we will begin to focus on finding bugs and exploring all the code we can reach in lsass.exe.

Blog 0 - Windows Vulnerability Research - Picking up the pieces

Disclaimer: This blog series won't be very technical. It is intended to be easy to understand and follow. Like most of my blogs, I have no reason to be peacocking with technical jargon like all the infosec losers.

Overview and donation information: https://weirdquadratic.blogspot.com/p/blog-overview.html

To the reader: Feel free to share anything I write with those who need it. And be sure to make local copies as I am sure it is a matter of time until this blog will be taken down as well. 


Introduction

These last couple of years have been extremely difficult. During my last months of employment at Microsoft, I started learning math and exploring the factorization problem as I needed a new challenge. A whole lot of traumatic events happened soon after, and I think, as a traumatic reaction, I kept working on factorization for the next couple of years. I am sure a psychologist could explain to me why trauma caused a complete obsession with the factorization problem. I learned a lot about math, especially since I dropped out of high school and had no math education beyond that. I still believe I am closing in quickly on a breakthrough, but some days, the pressure of it is too much.

Last night I found myself thinking back to those days I had lived in Vancouver, Canada, roller-skating with my teamlead around the seawall, drinking beers on Sunset beach and talking about life. And thinking back to all the cool people I had gotten to know there. I was just in absolute torment, being unable to go back to those places and people, stuck in social isolation and unemployment. I had decided I was going to end my life.

It is very difficult a lot of days, and what happened last night is not a rarity. Today, with the hopes and perspectives of a new day ahead, I decided I must act, perhaps what will give me purpose again, is simply writing about something I used to enjoy before my life came crashing down.

Today is day 0, a new beginning. 3 years ago, when my employment ended, I was in the process of a multi-year audit of all the cryptographic components. Well, the software side at least, as learning the mathematics behind it is still an ongoing process.

Since I cannot ever again submit my work to Microsoft, after how they treated my friend and former manager for defending me, I will simply start documenting everything and sharing it here, including anything I find. I am a bear and my pride and loyalty to my friends is much more important than any money in the world.

My hope is to find some creative vulnerabilities, something AI wouldn't easily be able to find. Not just to keep relevance, but also to keep research fun. There is few things as soul draining as looking for buffer overruns all day long. There is a subset in the Offensive Security community who thinks memory corruption vulnerabilities are the l33t3st thing in the world. They are not, and 99.99% of them follow very classical patterns. This is also the reason why those types of bugs won't stay immune to a pattern recognition machine like AI.

Be aware it is 3 years ago now, since I've last done any bug hunting. So I am beginning from the very basics, not just for the reader but also to ease myself back into the process. As this series progresses the topics will become more complex and technical.

Setting up a research station

If we want to audit cryptographic components, ideally we should set up a domain locally so that we can have access to all the enterprise features. Follow the steps below to setup up your research infrastructure.

Step 1: Install Hyper-V

Install Hyper-V https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/get-started/install-hyper-v

We should always use a virtual machine. The benefit being that we can much more easily audit kernel code or processes which would otherwise not be accessible. But the biggest advantage by far is that we can always reset to a clean state and don't accidentally ruin our host computer by doing obscure vulnerability research. (veterans will know what I'm talking about, we've all been there , done that)

Step 2: Download windows Enterprise

If you sign up for the Windows insider program you can easily download Windows enterprise .iso's: https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewiso

I generally prefer using Windows 10 over Windows 11 for debugging purposes, as windows 11 can be a pain sometimes. Hence I will download “Windows 10 Insider Preview Enterprise (Release Preview Channel)”

Step 3: Install Windows Enterprise

Install the .iso we just downloaded in Hyper-V. Instructions: https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/get-started/create-a-virtual-machine-in-hyper-v

Optional: I usually install virtual machines on external SSDs. Because they can start taking up a lot of disk space very quickly, especially with automatic checkpoints enabled. You also should probably get into the habit of disabling automatic checkpoints so you don't suddenly find yourself running out of disk space.

Make sure to leave the VM disconnected from the network until after your installation so that you can use an offline account. (But do remember to reconnect it later, otherwise you won't be able to domain join)

Step 4: Download and install Windows Server.

The .iso we just installed, you can think of it as the windows installation that the typical corporate user will be using. Of-course, to set-up a domain, we also need a second virtual machine running Windows Server.

Download “Windows Server Vnext Preview ISO (Canary)” from here: https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewserver

Ideally I would download older builds as these latest builds are often a little bit more difficult to debug, especially when it comes to processes like LSASS. But let us see how it goes and problem solve as needed.

Make sure to select “Windows server 2025 Standard (Desktop Experience)” during installation, so we have a graphical user interface.

Setup 5: Install AD DS

Instructions: Install Active Directory Domain Services on Windows Server | Microsoft Learn

If you prefer the GUI scroll down to “Install AD DS by using Server Manager”. Here are the important steps summarized:

  1. In the Server Manager window click “Add roles and features”

  2. As “Installation Type” select Role-Based or feature-based installation

  3. As “Server Roles” Select “Active Directory Domain Services” and “DNS Server”

  4. Install the roles. We can add more roles or features later if we must.

Once done, you should see the following option appear in the Server Manager:

Click “Promote this server to a Domain Controller”

  1. At “Deployment configuration” select “Add a new forest” and enter a name. I.e “bear.com”

  2. At “Domain controller options” Enter an easy to remember DSRM password

  3. Install and wait for the installation to complete

Step 6: Joining the domain

Next go to your Windows 10 Enterprise VM and go to adapter settings and change the DNS server to be the IP of the Windows Server machine we just promoted to Domain Controller.

Next type “Access Work or School” in the search bar. Then press connect and select “Join this device to a local Active Directory domain” and enter “bear.com” or whatever the name is you have given your domain.

I had to disable firewall for it to work. We can enable firewall later and add all the rules if we must. It's just for local testing anyway.

Login with “Administrator” and whatever your password is.

Congrats. You have just created a domain and domain joined another VM locally.

In the future we will be creating seperate standard user accounts with limited privileges for testing purposes.

In the next part in this series I will setup debugging and IIS and we will try to debug some code in Secure Channel. I may come back and edit things here later if debugging doesn't work out of the box. (I.e I forgot if using generation 1 or 2 VMs mattered).

If there are any questions leave a comment