Pivoting - ssh Local Port forwarding



In part one of the SSH pivoting guide, I’ll provide a detailed (pedantic) guide of the easiest form of pivoting which is depicted by the green arrow in the diagram bellow.

slp

Pivoting is the act of tunneling traffic between two networks, through a computer under our control. Generally, pivoting is used to bypass a lack of routing between the networks, or to bypass a firewall which prevents routing between the two networks. There are various tools that can be used to pivot, including ssh client, sshuttle, meterpreter, iptables, python, a custom tool etc.

Due to the proliferation of secure shell (SSH) it makes sense to first detail how ssh can be used to pivot. The two commands to pivot through 10.123.1.210 to 10.2.1.22:2222 using SSH local port forwarding are:

ssh -L 127.0.0.1:9922:10.2.1.22:2222 [email protected]
ssh 127.0.0.1 -p 9922

We will go into detail of what exactly happens ‘under the hood’ when we enter those two commands.

The best place to learn about the SSH protocol is the RFC document that describes it. There are a few RFCs that make up the description of the complete SSH protocol and the RFC that describes the SSH connection protocol is available at https://www.ietf.org/rfc/rfc4254.txt

Section “7. TCP/IP Port Forwarding” describes the component we will use to pivot, specifically:

7.2.  TCP/IP Forwarding Channels
   When a connection comes to a locally forwarded TCP/IP port, the following packet is sent to the other side.  Note that these messages MAY also be sent for ports for which no forwarding has been explicitly requested. The receiving side must decide whether to allow the forwarding.
      byte      SSH_MSG_CHANNEL_OPEN
      string    "direct-tcpip"
      uint32    sender channel
      uint32    initial window size
      uint32    maximum packet size
      string    host to connect
      uint32    port to connect
      string    originator IP address
      uint32    originator port
   The 'host to connect' and 'port to connect' specify the TCP/IP host and port where the recipient should connect the channel.  The 'host to connect' may be either a domain name or a numeric IP address.
   The 'originator IP address' is the numeric IP address of the machine from where the connection request originates, and the 'originator port' is the port on the host from where the connection originated.
   Forwarded TCP/IP channels are independent of any sessions, and closing a session channel does not in any way imply that forwarded connections should be closed.

We can demonstrate with an example how SSH local port forwarding works.

The following command creates a locally forwarded TCP/IP port on “kali” with some useful options.

ssh -i ~/id_rsa -q -f -N -p 22 [email protected] -L 127.0.0.1:9922:10.2.1.22:2222

The command above can be broken down as follows:

ssh                     = /usr/bin/ssh (the linux ssh client application)
-i ~/id_rsa             = login using public/private keys
-q                      = suppresses most warnings and errors
-f                      = run the ssh client in a background process
-N                      = do not open a shell
-p 22 [email protected] = login to server 10.123.1.210 on port 22 with user "frog"
-L 127.0.0.1:9922       = open a local listener on interface 127.0.0.1 port 9922
:10.2.1.22:2222         = instruct remote server to send traffic to 10.2.1.22:2222

When we enter the command above, the following happens:

  • the ssh client starts up and sends itself to the background
  • the ssh client on “kali” connects to “pivot-host-1” via a new session we’ll refer to as process-1
  • the ssh server on “pivot-host-1” accepts connection from “kali”
  • the ssh client on “kali” logs in with user “frog” and matching private key “id_rsa”
  • the ssh server on “pivot-host-1” logs in user based on match with a key in authorized_keys
  • the ssh client on “kali” creates a listener on port 9922 and waits for new connections

This is the relevant netstat output on “kali” after connecting:

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:9922          0.0.0.0:*               LISTEN      10100/ssh
tcp        0      0 10.123.1.199:50108      10.123.1.210:22         ESTABLISHED 10100/ssh

And this is the relevant netstat output on “pivot-host-1” after connecting:

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 10.123.1.210:22         10.123.1.199:50108      ESTABLISHED 4734/sshd: jol

process-1 can be visually represented as follows:

slp

Now that we have created a local ssh port-forwarding session on “kali”, we can connect to the listening port 9922 to see what happens. The following command connects to local port 9922 on “kali” which was configured as an ssh local port-forward:

ssh -p 9922 127.0.0.1

When we enter the command above, the following happens:

  • the ssh client on “kali” connects to its own port tcp/9922 as new process we’ll refer to as process-2
  • backgrounded port-forward process process-1 accepts the connection
  • process-1 sends a “direct-tcpip” request to remote ssh server “pivot-host-1” with following contents
ssh request: "direct-tcpip"
host to connect: 10.2.1.22
port to connect: 2222
originator IP address: 127.0.0.1 (process-2 connection source IP)
originator port: 51210 (process-2 connection source port)
  • ssh server on “pivot-host-1” reads the “direct-tcpip” request
  • “pivot-host-1” looks up the routing table for the IP 10.2.1.22 and confirms source interface 10.2.1.1
  • ssh server on “pivot-host-1” creates a connection to 10.2.1.22:2222 from interface 10.2.1.1 port 33000
  • ssh server on “pivot-host-1” maps ssh connection from process-2 to the new connection to 10.2.1.22:2222
  • 10.2.1.22 port 2222 runs an SSH server, so we will be asked to login to the ssh server on “target-1”

This is the relevant netstat on “kali” after connecting to port 9922 on “kali”:

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:9922          0.0.0.0:*               LISTEN      10100/ssh
tcp        0      0 10.123.1.199:50108      10.123.1.210:22         ESTABLISHED 10100/ssh
tcp        0      0 127.0.0.1:9922          127.0.0.1:51210         ESTABLISHED 10100/ssh
tcp        0      0 127.0.0.1:51210         127.0.0.1:9922          ESTABLISHED 10121/ssh

And this is the relevant netstat on “pivot-host-1” after connecting to port 9922 on “kali”:

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 10.2.1.1:33000          10.2.1.22:2222          ESTABLISHED 4734/sshd: jollyfro
tcp        0      0 10.123.1.210:22         10.123.1.199:50108      ESTABLISHED 4734/sshd: jollyfro

process-2 can be visually represented as follows:

slp

When process-2 connects to port 9922 on “kali”, process-1 on “kali” tunnels the request via its existing ssh (port-forward) connection with “pivot-host-1”. Host “pivot-host-1” connects the new connection to 10.2.1.22:2222 via its interface “10.2.1.1”.

Pivoting into an unreachable network via SSH local port-forwarding can be useful when there are known ports on known hosts in the remote network. When hosts are not known, or ports are not known, setting up a large number of single port forwards is not practical. This is where ProxyChains takes over, and it will be covered in Part 2 of the detailed guide to pivoting.

Guide from JollyFrogs

KSEC Labs
comments powered by Disqus