This post aims to be a one-stop shop for all the things that an offensive security practitioner might want to know about using Secure Shell (SSH) tunnels and SOCKS proxies. The information provided here is not new, but it does aim to be a reference document that can be used during operations.
Secure Shell (SSH) Basics
SSH is a protocol that allows a user to remotely connect to a host and typically provides an interactive shell or command prompt that can further be leveraged to execute commands. Most Linux-based servers have a SSH server installed and both Windows and Linux have a built-in SSH client. The most common SSH client/server is the OpenSSH implementation and is the application used for all references in this post.
Firewall
Because SSH facilitates remote control of a host, the SSH server should always be configure with firewall rules that whitelist connection from a specific host. This is especially true if the SSH server is internet accessible. It would be a significant failure if offensive operations infrastructure was compromised or even accessible to adversaries.
Authentication
SSH connections can be established with only a username and password for authentication. In addition, SSH allows users to create a public and private key pair that can subsequently be used in place of a password. These keys offer strong configurable asymmetric encryption. Users should secure access to their generated private key just like it is a secret. The generated public key is added to the target host’s SSH authorized_keys file.
Just like a password, if a private key is recovered by an attacker, it can be used to access the server. Because of this, SSH keys should be encrypted with a password that acts as a second factor. The ssh-keygen utility can be used to create a 4096-bit RSA key pair with:
>$ ssh-keygen -t rsa -b 4096
By default, this will output a private key named id_rsa and public key file named id_rsa.pub. Be sure to enter a password when prompted to encrypt the key. The private key file permissions must be restricted so that only the user, and nobody else, can read the file. If file permissions allow others to read the file, the SSH client will ignore the identity file and display an error. On a Linux host, the permissions should be “600” so that the user can read and write the file, but the group and other users are not allowed access.
Each major section of this post will build on the previous section and also break down the commands into numbered parts to in an attempt to increase understanding. A visual image will be presented after each set of commands to illustrate the network connectivity and to identify which hosts commands should be executed on. To start, LINUX1 represents an operator’s Linux workstation and REDIR1 represents an internet accessible host that is part of the offensive operation’s infrastructure.
The following image illustrates using a SSH private key to connect to a SSH server on the host REDIR1 as the rastley user from LINUX1:

- Use the SSH client on LINUX1
- Use the identity file, the user’s private key at /root/priv.key
- Establish a SSH connection to REDIR1 and login as rastley
This image illustrates establishing a SSH connection from LINUX1 to REDIR1

Remote Port Forward Tunnel
GOAL: Connect to a port on a compromised host in the client network from a redirector
Let’s assume that during an assessment, an operator compromises a host, named PWNED1, that is running a SSH server. The operator now wants to SSH into the compromised host directly from the internet. One option is to create a remote port forward SSH tunnel, aka a reverse tunnel, from PWNED1 to the operator’s internet accessible server, REDIR1. Once the tunnel is setup, the operator can SSH directly into the compromised host from the redirector.
From the compromised host, use the SSH client -R flag to build a Remote port forward SSH tunnel. This flag takes an argument of [bind_address:]port:host:hostport . The bind_address is the interface IP address that tunnel should BIND to, or listen on, for the remote host. The bind_address should not be confused with the SSH server address that SSH client connects to for authentication. The OpenSSH default is to use the host’s loopback adapter IP address, 127.0.0.1. If you want to use a different interface IP, the GatewayPorts option must be enabled in the SSH server’s /etc/ssh/sshd_config file. When this option is enabled, and the bind_address is empty, 0.0.0.0, or *, then it will bind to ALL interfaces. This introduces a problem because now anyone on the internet could connect to tunnel because it would be listening on an interface with a public IP address and subsequently reach the internal compromised host. If the GatewayPorts options is NOT enabled, and the bind_address is blank, the tunnel will only bind to the loopback adapter. For this reason, it is recommended that operators always explicitly specify the bind_address as opposed to assuming the SSH server’s configuration.
The name “localhost” typically refers to the host’s loopback adapter; however, it could also refer to any arbitrary IP address by modifying the computer’s /etc/hosts file. For this reason, it is recommended that operators always explicitly use the interface IP address 127.0.0.1, instead of a DNS name that is assumed to resolve to the loopback adapter.
The port argument specifies the port that the tunnel will bind to on the bind_address interface of the remote server. This port must be free, and the user account must have adequate permissions to bind to a privileged port, anything less than 1024.
The host argument is the interface that the tunnel will bind to on the source host where the command is executed, and the connection is initiated from. The same rules apply as before, explicitly specifying the loopback adapter with 127.0.0.1 is best.
The last argument, hostport, is the port on the source host that the tunnel will send traffic to. The hostport should reference a service on the source host that an operator wants to connect to through the tunnel. Because we want to be reach the SSH server on the source host, port 22 should be used.
This command will build a SSH Reverse port forward tunnel that sends traffic from the remote host to the source host. The SSH client -R flag determines the direction traffic flows through the tunnel relative to where the command was executed. The command will be run on the source host, PWNED1, so traffic will flow from the remote host REDIR1 to the source host PWNED1.
SSH Remote Port Forward- Use the SSH client on PWNED1
- Create a Remote port forward tunnel from the remote host, REDIR1, to the source host, PWNED1
- On the remote host, REDIR1, listen on the bind_address, 127.0.0.1, on port 2222
- Forward the connection from the remote host, REDIR1, to the SSH service that is already listening on the source host, PWNED1, host loopback adapter 127.0.0.1 on hostport 22
- The SSH client should connect to REDIR1 and login as rastley
Once a SSH tunnel has been established between the compromised host to the internet host, an operator can now use it to connect to the SSH server on the source host, PWNED1. From REDIR1, SSH to bind port 2222 and the traffic will be sent through the tunnel to hostport on the host server. The operator must provide a valid username and password for the compromised host, PWNED1.

- Use the SSH client on REDIR1
- Establish a SSH connection to port 2222
- The SSH client should connect to the loopback adapter (127.0.0.1), which is forwarded through the previously created SSH tunnel to PWNED1, and login as the user hpotter, a valid user account on PWNED1
This image visualizes establishing a Remote port forward SSH tunnel between REDIR1 and PWNED1, and then starting a new SSH session through the tunnel from REDIR1 to PWNED1.

Web Applications
GOAL: Connect to a web application on the compromised host in the client network from a redirector
Remote port forward tunnels can be used to connect to any service listening on source host. One example might be connecting to a web application listening on PWNED1 port 8443. This could be something like a vulnerability scanner or virtual machine controller. This command creates a Remote port forward tunnel to access a web application listening on PWNED1.
SSH Remote Port Forward- Use the SSH client on PWNED1
- Create a Remote port forward tunnel from the remote host, REDIR1, to the source host, PWNED1
- On the remote host, REDIR1, listen on the bind_address, 127.0.0.1, on port 443
- Forward the connection from the remote host to the web server that is already listening on source, PWNED1, host loopback adapter 127.0.0.1 hostport 8443
- The SSH client should connect to REDIR1 and login as rastley
To use the tunnel, open a web browser on REDIR1 and navigate to https://127.0.0.1:443. Traffic will be forwarded through the tunnel to the web application listening on PWNED1 at port 8443.
SSH Client Optional Arguments
There are a few optional SSH client arguments that are useful when creating tunnels. When the Remote port forward tunnel command from the previous section was executed on PWNED1, it left an interactive SSH terminal prompt to REDIR1. Anyone with access to PWNED1 could now issue commands to REDIR1, depending on the command execution context. The -N flag removes the ability to execute commands on the remote host all together. When the -N flag is used, a SSH connection is established but the operator will not have a prompt to enter or execute commands. The -T flag prevents a pseudo terminal from being allocated for the connection; however, commands could still be issued if only the -T flag was used. The best way to prevent remote command execution is to use both the -N and -T flags together.
In most cases, an operator does not need to interact with a tunnel once it is setup. To keep the tunnel from blocking continued use of the current terminal, use the -f flag. This flag forks the SSH client process into the background right after authentication.
SSH Optional Arguments- Use the SSH client on PWNED1
- Use the following SSH arguments:
a. fork this SSH client process to the background after authentication
b. No command execution on the remote host
c. Do not allocate a pseudo-Terminal on the remote host
d. Create a Remote port forward tunnel from the remote host to the source host
3. On the remote host, REDIR1, listen on the bind_address, 127.0.0.1, on port 443
4. Forward the connection from the remote host to the web server that is already listening on source, PWNED1, host loopback adapter 127.0.0.1 hostport 8443
5. The SSH client should connect to REDIR1 and login as rastley
SOCKS Proxy
GOAL: Proxy tools and traffic from a Linux host into the client network
Accessing a single service listening on a compromised target is nice, but it would be better if operators could reach any host in the entire internal client network. SSH conveniently has a built-in way to act as a SOCKS server through its “Dynamic” application-level port forwarding feature. SOCKS is layer 5 protocol service that acts as a gateway and transparently relays network traffic to and from its listening port. This is commonly used to bypass network restrictions. Traffic appears to be coming from the host where the SOCKS server is listening and does not show the IP address of the host that initiated the request. To create a listening SOCKS proxy server, use the SSH -D [bind_address:]port argument.
SSH Dynamic Port- Use the SSH client on PWNED1
- Create a Dynamic port, a SOCKS server
- The SOCKS server will listen on the bind_address 127.0.0.1 port 9052
- The SSH client should connect to itself (PWNED1), via the loopback adapter, and login as hpotter
Once the SOCKS server is listening, it must be accessible to an operator so their tools cand be used to reach the internal target networks. Create a SSH Remote port forward tunnel to make the SOCKS server on PWNED1 available through REDIR1.
SSH Remote Port Forward- Use the SSH client on PWNED1
- Create a Remote port forward tunnel from the remote host, REDIR1, to the source host, PWNED1
- On the remote host, REDIR1, listen on the bind_address, 127.0.0.1, on port 9051
- Forward the connection from the remote host to the SOCKS service that is already listening on the source, PWNED1, host loopback adapter 127.0.0.1 hostport 9052
- The SSH client should connect to REDIR1 and login as rastley
A program, application, or offensive security tool must have explicit support for a proxy to natively use one. Some tools like Firefox and Burp Suite have an option to configure a proxy. However, there are many tools that do not have explicit proxy support.
proxychains-ng is a tool that can be used to send TCP traffic for any arbitrary program to a SOCKS proxy. It does not support ICMP or UDP. Once proxychains is installed, edit the /etc/proxychains4.conf so that the last line of the configuration file points to the interface and port where the SOCKS server is listening. In our example the last line would read: socks4 127.0.0.1 9051 .
With proxychains configured, an operator could run an arbitrary program and send its TCP traffic to the internal client network. If an operator wanted to use Nmap and do a TCP port scan for port 445, they would prefix their normal command with “proxychains”. For Nmap specifically, the proxychains proxy_dns setting must be disabled. Because proxychains does not support UDP or ICMP, the Nmap TCP SYN and connect scan types must be used. An example Nmap command using proxychains looks like:
$> proxychains nmap -sT -p 445 192.168.1.10SSH SOCKS Proxy Diagram
Reverse Dynamic Port Forward
GOAL: Proxy tools and traffic from a Linux host into the client network in a single ssh command
OpenSSH version 7.6 introduced a new feature dubbed “reverse dynamic forwarding” that leverages the extended syntax for the -R argument. This feature allows an operator to create a listening SOCKS 5 service on a remote host in a single command. The previous example in the Remote Port Forward section required two SSH commands; one with the -D argument to create the SOCKS 5 service and a second command with the -R argument to connect it with a tunnel. This feature is enabled client side and therefore it does not need to be configured on the server itself. The SSH documentation states:
if no explicit destination was specified, ssh will act as a SOCKS 4/5 proxy and forward connections to the destinations requested by the remote SOCKS client.
The extend syntax for the SSH -R argument is [bind_address:]port.
SSH Reverse Dynamic Forward- Use the SSH client on PWNED1
- Create a Reverse dynamic port forward tunnel from the remote host, REDIR1, to the source host, PWNED1
- On the remote host, REDIR1, create a SOCKS services that is listening on the bind_address, 127.0.0.1, on port 9051
- The SSH client should connect to REDIR1 and login as rastley
DNS
GOAL: Configure an operator’s private workstation to resolve hostnames while using a proxy
By default, proxychains nor the SOCKS4 protocol support DNS requests. A workaround is to add an entry for the target hostname into the /etc/hosts file where proxychains is used. This will allow the operating system to look up the target host’s IP address locally before it is sent through the tunnel. However, the SOCKS4a and SOCKS5 protocols are capable of handling DNS requests and do not require an entry in the /etc/hosts file. To enable DNS support for proxychains, edit the configuration file, /etc/proxychains4.conf , and uncomment the “proxy_dns” line:
# Proxy DNS requests — no leak for DNS data
proxy_dns
To enable transparent DNS support for some Linux tools, A DNS server on the internal target network must be identified to send the DNS requests to. Export the “PROXYRESOLV_DNS” environment variable with a value of the internal DNS server like:
$> export PROXYRESOLVE_DNS=192.168.1.1
Local Port Forward Tunnel
GOAL: Forward traffic from an operator’s private workstation to a SOCKS proxy server
An operator may not want to run tools from an internet accessible host like REDIR1. A more likely scenario is to use a Virtual Machine (VM), LINUX1, from the operator’s own internal network. To do this, use the same setup before to create a SSH SOCKS proxy and a Remote port forward tunnel. An additional tunnel needs to be created from the operator’s VM (LINUX1), to the redirector (REDIR1).
Use the SSH -L [bind_address:]port:host:hostport to setup a tunnel to forward traffic from the operator’s local VM (LINUX1) to the redirector (REDIR1). This creates a Local port forward tunnel from LINUX1 to REDIR1. This command is like using the -R flag, but the main difference is the direction in which traffic flows through the tunnel. The -L flag sends traffic from the source host to the remote host where the -R flag send traffic from the remote host to the source host. Source is determined based on where the SSH command is run.
SSH Local Port Forward- Use the SSH client on LINUX1
- Create a Local port forward tunnel from the source host, LINUX1, to the remote host, REDIR1
- On the local host, LINUX1, listen on the bind_address, 127.0.0.1, on port 9050
- Forward the connection from the local host to the port that is already listening on remote host, REDIR1, host loopback adapter 127.0.0.1 hostport 9051
- The SSH client should connect to REDIR1 and login as rastley
Once the tunnel has been setup from LINUX1, the operator can again use proxychains on their VM to execute tools. Update the proxychains configuration file at /etc/proxychains4.conf to use the SOCKS5 tunnel accessible on port 9050 (e.g., socks5 127.0.0.1 9050). The traffic from those tools will be routed from LINUX1 to REDIR1, and then from REDIR1 to PWNED1, and finally from PWNED1 to target host in the client internal network.
SSH Local Port Forward DiagramWeb Browser
GOAL: Configure an operator’s web browser to use a SOCKS proxy
Operators can also use other programs that have built-in SOCKS proxy support such as Firefox to reach web applications in a client network. The host running the web browser must be configured with a listening tunnel to a SOCKS proxy as documented in the previous section. To configure Firefox to use the SOCKS proxy on LINUX1, open the network Connection Setting menu and configure it to use the listening port on 9050. Once Firefox is configured, all traffic will be sent to hosts on the internal network. Do not forget that DNS will not work through a SOCKS4 tunnel. Operators can reach a host by its internal IP address or create an entry in the /etc/hosts file.
Firefox Connection SettingsBurp Suite
GOAL: Configure Burp Suite to use proxychains and a SOCKS proxy
Sometimes an operator might want to double up and use an HTTP proxy, such as Burp Suite, on their VM together with a SOCKS proxy. Burp Suite does have built-in support for a SOCKS server; however, experience has shown it does not work well when used with SOCKS servers from tools like Cobalt Strike. The best approach is to start Burp Suite with proxychains instead of explicitly configuring Burp Suite to use a SOCKS server. Follow the steps from the previous sections to configure a tuneel on the VM that connects to SOCKS port.
Command and Control (C2) Frameworks
GOAL: Proxy tools and traffic from an operator’s private workstation into the client network through a C2 framework
Some C2 frameworks utilize an agent that has a built-in SOCKS server such as Mythic’s Poseidon and Cobalt Strike’s Beacon. Using an agent’s built in SOCKS capabilities removes the need to setup multiple SSH tunnels.
From a Cobalt Strike Beacon command prompt, issue the socks 9051 command. This will create a listening SOCKS server port on the Cobalt Strike Team Server (REDIR1), not the host where the agent is running (PWNED1) nor on the host where the Cobalt Strike user-interface client is running (LINUX1). All traffic sent to the listening SOCKS port on the Team Server will subsequently be sent to the compromised host where the Beacon is running.
Once the SOCKS server port is listening on the Team Server, setup a Local port forward from the operator’s VM (LINUX1) to the Team Server (REDIR1). After the tunnel has been setup, tools such as proxychains and Firefox can be used to send traffic to the internal client network.
SSH Tunnel Through C2 Agent SOCKS ServerRemote Desktop Protocol (RDP)
GOAL: Use RDP through a SOCKS proxy from an operator’s private Linux workstation
Administrators commonly use RDP to remotely access a Windows host and leverage the Windows GUI to perform tasks. For this same reason, operators will often want to use RDP to access a target information system as well. SSH tunnels and SOCKS proxies can be used to transport an RDP client from an operator’s host into a target environment and remotely control a host. The Linux xfreerdp client is a commonly used program to remotely access a Windows host Linux. With a SOCKS server and tunnels already up and running, use proxychains to connect into the target environment with xfreerdp.
xfreerdp Command- Use the proxychains client on LINUX1
- Proxy in the xfreerdp client
- Connect to the remote server hostname 192.168.1.10
- Connect to the RDP service on the remote port 3389
- Login with the user Administrator
- Login with the password Passw0rd1
Other useful flags include /clipboard to share data from copy and paste commands between your host and the remote system. Additionally, the /drive:MyDrive,/tmp flag creates a file share named MyDrive on the Windows host that can be used to transfer files to and from the /tmp directory on the source host, LINUX1.
Windows
GOAL: Proxy tools and traffic from an operator’s private Windows workstation into the client network
Windows 10 has a built-in OpenSSH client that can be used to create a SSH Local port forward tunnel to proxy traffic into the target network. However, proxychains does not support Windows. An alternative is to use Proxifier. The host running Proxifier must be configured with a SSH tunnel to a listening SOCKS proxy port as documented in the previous sections. Once the tunnel is setup, open Proxifier and go to the Profile menu. Add a Proxy Server that points to the Local port forward SSH tunnel that was setup on the Windows host.
Proxifier SOCKS Proxy ServerProxifier can be configured to route all traffic from your Windows VM through the SOCKS proxy into the target network. Alternatively, Proxifier offers a more OPSEC safe choice to create rules that restrict traffic through the SOCKS proxy for specific programs. To add a rule, go to the Profile menu and select Proxification Rules and add applications that should be allowed to use the proxy. This image shows a rule that will only proxy traffic from cmd.exe, rubeus.exe, mstsc.exe, and powershell.exe.
Proxifier RulesProxifier Caveats
While Proxifier can enable an operator to use Windows tools and send traffic into a client network, there are several “gotchas” to keep in mind. The first is to consider the credentials associated with the logon session that is executing the tool. The local user account of an operator’s Windows host will not successfully authenticate to a client’s network resources. It is recommended to use the runas.exe program with the /netonly flag to create a new logon session with credentials for a valid user in the client’s domain. Other alternatives include patching LSASS with a NTLM hash or importing a Kerberos TGT into the current logon session with a tool like Rubeus.
Applications that implicitly use other Windows services, or the Windows NT Kernel, to connect to a remote host will not be able to communicate through the proxy. Some programs transparently use the other OS services (e.g., RPC, DCOM, or WMI) or the Windows kernel (e.g., SMB) under the hood. An example of this is when Windows Explorer is used to browse a SMB file share. The System process, which represents the Windows kernel, will attempt to connect the SMB share, but the connection will time out because the Kernel’s network connection is not going through the proxy. Similarly, when there is an attempt authenticate to a Domain Controller using Kerberos, lsass.exe makes the connection to the Domain Controllers, not the source application that initiated the request. This activity can also be seen when attempting to use the net view command or PowerView’s Get-Net* cmdlets as well because they use RPC network connections that primarily originate from the Kernel or the RPCSS service.
Additionally, some tools like BloodHound or Rubeus depend on certain Windows environment variables to identify the Active Directory domain and logon servers. Because an Operator’s private Windows workstation is not domain joined, those environment variables will not exist. A work around is to always specify the domain, domain controller, and explicit credentials depending on the tool and available arguments (e.g., Powerview’s Get-DomainUser cmdlet).
HTTP Tunnel
GOAL: Create a tunnel using the HTTP protocol
Operators can use the HTTP protocol to create a tunnel. This is useful when the SSH protocol cannot egress the target network on ports 22, 80, or 443. Almost every organization allows the HTTP protocol to egress the network as it is used for day-to-day activities and business operations. gTunnel is a multi-platform HTTP/2 “tunneling solution aimed to provide quick and easy network tunnels that are fast, efficient, and stealthy.” The tool has support for reverse and forward tunnels as well as the SOCKS 5 proxy protocol. The operator will need to place the gTunnel client on the compromised host, PWNED1, to create a connection. Visit the gTunnel documentation for details steps to get started.
Conclusion
SSH tunnels and SOCKS proxies are invaluable during offensive security operations but they can also be slightly confusing to setup. Hopefully the break out information for each part of a command, followed by a visual graphic, made things easier to understand. Leveraging the SSH client Remote and Local port forward tunnels allows an operator to bypass network security restrictions. Operators can combine SSH tunnels with a SOCKS proxy to execute their tools on a host they control where there are no endpoint monitoring applications. This can be used to bypass detections and allows flexible use of programs that may run on different operating systems. Hopefully, this post is used a reference point for demystifying the complexity of setting up SSH tunnels and provided verbose examples as a point-of-reference for future operations.
Offensive Security Guide to SSH Tunnels and Proxies was originally published in Posts By SpecterOps Team Members on Medium, where people are continuing the conversation by highlighting and responding to this story.
Article Link: https://posts.specterops.io/offensive-security-guide-to-ssh-tunnels-and-proxies-b525cbd4d4c6?source=rss----f05f8696e3cc---4