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.
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:
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:
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