1. Finding the Base of the Windows Kernel

    Recently-ish (~2020), Microsoft changed the way the kernel image is mapped and also some implementation details of hal.dll. The kernel changes have caused existing methods of finding the base of the kernel via shellcode or a leak and arbitrary read to crash. This obviously isn't great, so I decided to figure out a way around the issue to support some code I've been writing in my free time (maybe more on that later).

    Our discussion is going to start at Windows 10 1903 and then move up through Windows 10 21H2. These changes are also still present in Windows 11.

    What's the point(er)?

    Finding the base of the kernel is important for kernel exploits and kernel shellcode. If you can find the base of the kernel you can look up functions inside of it via the export table in its PE header. Various functions inside of the kernel allow you to allocate memory, start threads, and resolve other kernel module bases via the PsLoadedModuleList. Without being able to utilize kernel routines and symbols, you're pretty limited in what you can do if you're executing in kernel. Hopefully this clarifies why this post is even necessary.


    Check out the full post for more details!
  2. Using kd.exe from VSCode Remote

    I wanted to do a small post here, just because the answer to this issue was sort of scattered on the internet. Bigger post coming soon on some kernel exploit technique stuff.

    It turns out that when running kd.exe for command line kernel debugging from VSCode remote, symbol resolution breaks completely. Why? Looks like when running from a service symsrv.dll uses WINHTTP for making requests instead of WININET. You can replicate this behavior in a normal shell by setting $env:DBGHELP_WINHTTP=1 in a powershell window and then running kd.exe. For some reason, WINHTTP tries to always use a proxy server, so you have to tell it not to via the following key in the registry:

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Symbol Server -> NoInternetProxy -> DWORD = 1

    You should also set it in HKLM\SOFTWARE\WOW6432Node\Microsoft\Symbol Server too, in case you are using a 32-bit debugger.

    This issue will happen with cdb.exe and kd.exe, so I hope this solution helps someone.

  3. Windows 10 KVAS and Software SMEP

    Kernel Virtual Address Shadow (KVAS) is the Windows implementation of Kernel Page Table Isolation (KPTI). It was introduced to mitigate the Meltdown vulnerability, which allowed an attacker that could execute code in user mode to leak out data from the kernel by abusing a side channel. While there are plenty of papers and blog posts on Meltdown and KVAS, there isn't much info on an interesting feature that KVAS enables: software SMEP. Unfortunately or fortunately, depending on your interest level in this post and Windows internals, understanding how software SMEP works requires knowledge of x86_64 paging, regular SMEP, and KVAS, so I'll be getting into those topics enough to give you an understanding of the underlying technology. Near the end I'll be running some experiments to show the internals of what I covered in the technical sections prior.

    x64 Paging on Windows

    First, I'm going to dive into a short introduction to x86_64 (4-level) paging, the structures involved, and WinDbg commands to interact with the page hierarchy, just so the experiments later on are more understandable; plus a lot of this information is almost never presented together, so I think collecting it in a here's what you need to know format is useful. If you want more info consult the Intel manuals or check out Connor McGarr's blog. Connor does a great job of explaining the basics, so you may want to read his post over before continuing here if you don't already have at least a vague understanding of multi-level paging.


    Check out the full post for more details!
  4. Extracting and Diffing Windows Patches in 2020

    It's been a while since I've posted anything here! After all, what are personal blogs for but ignoring for years at a time ;)
    Anyhow, I've been running through this demo when teaching SANS SEC760 and I thought I'd write it up so that researchers can come back to it later when they need it. It's also useful to document all of this stuff in one place, since the information about it seems scattered throughout the internet, as many Windows topics are.

    So why should you care about extracting and analyzing Windows patches? Doesn't the patch mean the bugs being fixed are now useless?


    Check out the full post for more details!
  5. Autoruns Bypasses

    Autoruns is a tool that is part of the Microsoft Sysinternals suite. It comes in permutations of console/GUI and 32/64 bit versions. Its main purpose is to detect programs, scripts, and other items that run either periodically or at login. It's a fantastic tool for blue teams to find persistent execution, but it is not perfect! By default, autoruns hides entries that are considered "Windows" entries (Options menu -> Hide Windows Entries). There is a checkbox to unhide them, but it introduces a lot of noise. In my preparations to red team for the Information Security Talent Search (ISTS) at RIT and the Mid-Atlantic Collegiate Cyber Defense Comptition (MACCDC) this year I found a few ways to hide myself among the Windows entries reported in Autoruns.

    For some prior work done in this area check out Huntress Labs's research and Conscious Hacker's research.


    Check out the full post for more details!
  6. Uberconference Hidden Hangup Button

    I was on an uberconference call the other day and the leader of the conference mentioned how they had the ability to disconnect anyone on the call with a "Hangup" button next to the mute and profile buttons. Looking at the interface a caller with the icons expanded looks like this:

    caller interface

    Now let's inspect... Going down to where the profile and mute buttons are located it looks like there's one more, hidden button available:

    hangup hidden html

    Removing the style="display: none;" attribute from the div causes the button to show...

    hangup enabled

    It's funny because it actually works. If you click it the person gets booted from the call, even if you aren't an admin/call leader. Web is hard.
    Thanks for reading.

  7. Hack Fortress 2019 - helloworld2.apk

    Final Score

    Another great year of Hack Fortress at Shmoocon!
    I wanted to do a post on this challenge in particular becuase it was one of two 300 point challenges on the board. I always get inside my own head about these challenges but I remind myself: they are not normal CTF challenges. These challenges are meant to be solved in just a few minutes, since the board is pretty big and the length of the competition is pretty short (30 min for prelims, 45 min for finals).

    I always focus on the Data Exploitation challenges because they usually have high point values and consist of android application reversing, basic binary reversing, macOS and image forensics (thanks Sarah), obscure encoding, crypto (sometimes), and hardware, among other things. It's a very diverse but fun category. I solved three challenges totalling 525 points in this category in the finals. This particular challenge was the majority of those points, but actually took the least time since I've had experience with android application reverse engineering before.

    The challenge details were:

    Name: HelloWorld2
    Location: helloworld2.apk
    Points: 300
    Desc: Find the encryption key

    Whenever I get an APK I do two things:

    Unfortunately my version of dex2jar was out of date so I had some issues with my automated decompilation tools. I ended up downloading the newest version of dextools, running dex2jar, loading the jar into JD-GUI, and exporting the sources.
    Android apps always start with MainActivity, which was in the class path fortress/hack/helloworld2. The decompiled code is below.

    package fortress.hack.helloworld2;
    import android.os.Bundle;
    import android.util.Base64;
    import android.view.View;
    import android.widget.EditText;
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    public class MainActivity
      extends AppCompatActivity
      public static String encrypt(String paramString1, String paramString2, String paramString3)
          IvParameterSpec localIvParameterSpec = new javax/crypto/spec/IvParameterSpec;
          paramString2 = new javax/crypto/spec/SecretKeySpec;
          paramString2.(paramString1.getBytes("UTF-8"), "AES");
          paramString1 = Cipher.getInstance("AES/CBC/PKCS5PADDING");
          paramString1.init(1, paramString2, localIvParameterSpec);
          paramString1 = Base64.encodeToString(paramString1.doFinal(paramString3.getBytes()), 0);
          return paramString1;
        catch (Exception paramString1)
        return null;
      public void enceyptData(View paramView)
        paramView = (EditText)findViewById(2131165238);
        ((EditText)findViewById(2131165239)).setText(encrypt(keyFromJNI(), getString(2131427370), paramView.getText().toString()));
      public native String keyFromJNI();
      protected void onCreate(Bundle paramBundle)

    We are looking for the encryption key. In the encrypt function the first paramter passed is the key. We know this because the first parameter to the init function of javax.crypto.spec.SecretKeySpec is the key as bytes. Encrypt is called from MainActivity.enceyptData (sic) and the first parameter is keyFromJNI(). The function keyFromJNI has the prototype public native String keyFromJNI(); which means that there is a native library in the application that will provide the key back to the java app.
    Native libraries for an android application can be found in the lib directory of the APK. The unpacked apk shows four different architectures in the lib directory: arm64-v8a, armeabi-v7a, x86, and x86_64. I chose to look at the x86 version of, since Hopper is better at x86 than other architectures (in my opinion).
    Since I have reverse engineered java native libraries before I know to look for the function name and/or class name in the function list. Pictured below is both the search and the decompiled function.


    Looks like the classic "build a string as integers" trick. I'm assuming sub_61a0 is some kind of memory allocation function, and arg0 is always the JNIEnv pointer, which contains a bunch of useful functions to convert C types into java types to return. I'm guessing the arg0+0x29c is either NewString or NewStringUTF. Moving forward I just took all of the hex bytes from the four integers that get put into the key buffer and unhexlified them.

    In [26]: from binascii import unhexlify as unhex
    In [27]: unhex("212b2b636f74746f47756f596563694e")
    Out[27]: b'!++cottoGuoYeciN'

    Looks promising, but backwards...

    In [28]: unhex("212b2b636f74746f47756f596563694e")[::-1]
    Out[28]: b'NiceYouGottoc++!

    And there's the flag!

  8. sqlalchemy Magic

    I was writing a plugin for CTFd and I was faced with an interesting problem: how the hell do I add a column (attribue) to a parent table without modifying that table (or model object)???
    I was trying to assign an extra attribute to the Teams model; a one-to-many relationship between bracket and team so I could have Teams.chal_bracket and Bracket.teams, but again without modifying the Teams model.
    I had actually tried overriding the Teams model and also adding a row on the fly, but neither of those worked. I ended up with the solution below: ...

    Check out the full post for more details!
  9. Server Side Google Analytics

    I stitched together a bunch of posts from different sites to get a working setup for server-side google analytics with unique user tracking. This allows you to have a completely static (javascript-free) site and still get useful analytics data.

    server {
        # all of your other config...
            userid         on;
            userid_name    uid;
            userid_domain  <<the domain you are using this on>>;
            userid_path    /;
            userid_expires 365d;
            userid_p3p     'policyref="/w3c/p3p.xml", CP="CUR ADM OUR NOR STA NID"';
            location / {
                    try_files $uri $uri/;
                    index index.html;
                    post_action @analytics;
            location @analytics {
                    set $ipaddr $remote_addr;
                    resolver ipv6=off;
                    proxy_pass<<your analytics UA- tag>>&cid=$uid_got&t=pageview&dh=$host&dp=$uri&dr=$http_referer&uip=$remote_addr;

    Of course replace the <<the domain you are using this on>> and <<your analytics UA- tag>> with the appropriate data.
    This will result in the server sending out a GET request with the client's info to the tracking URL for each page visit. It increases bandwidth used by your server but is a neat trick regardless.

  10. Upgrading an Amazon EC2 Instance from Ubuntu Trusty to Xenial

    I had a bad time.
    I ran a do-release-upgrade on one of my Amazon EC2 instances to try and upgrade it from 14.04 (Trusty) to 16.04 (Xenial). After the update and a reboot the box refused to come back up. When I detached the drive and attached it to another to check syslog I found this:

    /sbin/dhclient -1 -v -pf /run/ -lf /var/lib/dhcp/dhclient.eth0.leases -I -df /var/lib/dhcp/dhclient6.eth0.leases eth0
    Usage: dhclient [-4|-6] [-SNTP1dvrx] [-nw] [-p <port>] [-D LL|LLT]
                 [-s server-addr] [-cf config-file] [-lf lease-file]
                 [-pf pid-file] [--no-pid] [-e VAR=val]
                 [-sf script-file] [interface]
    Failed to bring up eth0.

    Oh good, it forgot how to eth0.
    I spent about four hours figuring out how to fix it:

    apt update
    apt -y upgrade
    cat  << EOF > /etc/update-manager/release-upgrades.d/unauth.cfg
    apt install -y network-manager
    apt update
    apt -y upgrade
    systemctl enable systemd-networkd
    systemctl enable systemd-resolved
    dpkg-reconfigure resolvconf
    apt-get -y autoremove
    rm /etc/update-manager/release-upgrades.d/unauth.cfg
    1. Make sure you are up to date first.
    2. Some packages (python3) complain that they are unauthenticated. Feel free to skip this if you want.
    3. Install the network-manager
    4. Leap of faith... do the upgrade
    5. Finish the upgrade by installing the rest of the packages.
    6. Enable the systemd network daemon and resolver daemon
    7. Reconfigure resolvconf so you can dns
    8. Get rid of the unauth.cfg file you created
    9. Reboot and pray.

    Thanks to these three links for the solutions (I just put them together):