Java安全之命令执行(一)

前言

java安全里最关键的可以说就是命令执行了,无论是反序列化还是什么RCE漏洞,要说最终最能体现漏洞价值的话,那就是非命令执行莫属了。这次打算花点时间好好总结整理下java命令执行的几种方式,并做些浅面的分析。最近刚好看到了360BugCloud公众号的一篇java命令执行的调试分析文章,我也跟着调试了一遍,期间学到了不少,链接会在文末贴出来。

首先总的的来说,java命令执行可以分为4种方法,分别是 java.lang.Runtime#exec()、java.lang.ProcessBuilder#start()、java.lang.ProcessImpl#start()以及通过JNI的方式调用动态链接库,最后一种方式这篇文章暂不做分析,先看完前面比较常用的三种方法。

Runtime命令执行

在java反序列化中用到最多的就是Runtime类的exec方法来命令执行了,用法:

Runtime.getRuntime().exec("whoami")

实际上Runtime类的exec的重载方法有6个,如下:

例如本地运行命令ipconfig查看网络配置并返回信息:

InputStream ins = Runtime.getRuntime().exec("ipconfig").getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int size;
while(
	(size = ins.read(bytes)) > 0)bos.write(bytes,0,size);
    System.out.println(bos.toString()
     );

但是这里有个问题,在渗透的过程中如果要遇到要写入文件的话,这里使用"echo xxx>test.txt"等类似的命令就会爆出如下的错误:

跟入代码调试查看下具体原因

首先是跳到另外一个exec的重载方法,envp参数为null,file类型的dir参数也为null

接着将我们传入的command字符串带入到StringTokenizer类进行处理,跟入查看下

初始化参数后又调用了setMaxDelimCodePoint方法

跟入到setMaxDelimCodePoint方法后,查看代码

来看最后的处理结果

最后再重新调用了对应的exec重载方法

跟入到java.lang.ProcessBuilder#start,首先会先取出cmdarray[0]赋值给prog,这里值为“echo”

接着,后面又调用了ProcessImpl.start

继续跟入查看ProcessImpl#start

接着,后面调用了ProcessImpl的构造方法,再跟入构造方法查看下

跟进ProcessImpl的构建方法后,首先是对系统的配置及环境变量进行检查,比如检测是否允许调用本地进程等配置,接着以cmd[0]为参数创建了一个File对象,然后调用其getPath方法得到路径并赋值给executablePath变量

往下,接着调用needsEscaping()方法对executablePath进行判断,如果其中包含空格,则调用quoteString()方法进行处理;然后调用createCommandLine()把字符串数组拼成字符串,最终的cmdstr为“echo xxx>test.txt”

最后,再调用了create()方法创建进程,整个过程调试到这里好像也没发现问题所在,原因是最后关键的问题还在create方法创建进程中。

查看该create()方法的代码可看到,这是个native方法,后续是通过调用ProcessImpl_md.c的创建进程的方法来调用调用window系统的API接口,从而完成命令执行等操作。

那么我们的问题该怎么解决呢?还是得回到ProcessImpl_md.c的创建进程的方法中,这方法会对最后的我们传入的cmdstr进行以空格分割,也就是"echo xxx>test.txt",会被分割会"echo"和"xxx>test.txt",然后第一部分的"echo"会被当成启动的执行模块,然而在window的系统环境变量中是找不到这个启动模块的(可以在cmd中输入命令“where echo”进行测试),所以运行后才会抛出文章一开始的“系统找不到指定文件”错误。

知道了问题所在,解决办法的思路就比较清晰了,可以把cmd做为启动的指定模块,然后以运行批处理的方式来达到命令执行,要以这样的方式的话就必须启动命令解释器,就是在批处理的语句前面加上"/c",最终的命令应该为“cmd /c echo xxx>test.txt”。

我们通过IDEA的调试器来测试一遍:

OK,这回没有抛出其他错误了,在本地的项目位置在找到了新创建的test.txt文件

Reference

https://s.yam.com/ddxhQ

乐观的悲观主义者。
原文地址:https://www.cnblogs.com/v1ntlyn/p/13888547.html