java 安全调用_Java安全之命令执行(一)

本文探讨了Java中命令执行的重要性,并详细分析了Runtime.exec()方法的使用,包括不同重载方法,以及在执行过程中可能出现的错误,如"系统找不到指定文件"。通过调试代码,揭示了问题根源在于命令被错误地分割,导致找不到执行模块。解决方案是通过添加"cmd /c"来启动命令解释器,确保命令正确执行。最后,通过IDEA调试器验证了修正后的命令成功创建了目标文件。
摘要由CSDN通过智能技术生成

前言

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个,如下:

6358f26156580041efb528aeda349e52.png

例如本地运行命令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"等类似的命令就会爆出如下的错误:

5cd9809496ce2b6a690ea897b264dc33.png

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

017311730654b9e10e073f962293af1c.png

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

9b80b53d4e601c26e97760287af2e04b.png

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

e074015645223fb3490ed38f8b284653.png

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

5dd2f847f73f81f035a7728b6b737d9b.png

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

2211ab00390a7a93f380993525d3a962.png

来看最后的处理结果

8f258f66451353fb6e164eba7cdedc57.png

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

9712b54ef2e072899da8aad46b94abd4.png

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

65df90608251366e89dd25803e1456b3.png

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

02377d52ea8dec151a1d36e4a28773df.png

继续跟入查看ProcessImpl#start

987484ea4b76c1167cd9a4e48e96e8b5.png

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

5b723d4dbf143215ee0b797bc916e3f3.png

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

15602d4a7244157fcceda6e4f1e4fe40.png

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

ad1d068a62c1741861752a17afc6621e.png

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

dc894396c2d7a0103b339a75860e0ce2.png

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

0300455b56fef071e1447159c23fa41f.png

那么我们的问题该怎么解决呢?还是得回到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的调试器来测试一遍:

aace9b9d93b803da816cd60fccb540d0.png

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

8686863da705fc537d645819dce6fea3.png

Reference

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值