freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Z3专栏 | Java代码审计之命令执行的三种方式
2021-12-12 21:40:02
所属地 辽宁省

介绍

这是java代码审计学习系列的开篇
前几篇从基础开始
本篇主要讲解三种方式的命令执行,它们的区别、原理、用途等

命令执行的三种方式

java基本语法知识是java代码审计的基础
在学会了java基础语法后,还要对javaweb有了解,如什么是servlet、jsp,怎么用,什么原理?
了解了javaweb后,就是对框架、中间件的了解,如spring、spring boot、mybatis、shiro、fastjson、tomcat等等
了解了框架后,应该就能感觉到spring框架的设计模式了,它用的就是mvc设计模式。
学习java的设计模式,,有助于理解代码
java面向对象,如类、接口、抽象类,继承,上转型、下转型、泛型,什么叫创建对象、实例化对象等等

既然要审代码,当然是对java越了解越好(上面这些我也在学)

这系列教程暂时用的是java11,其它版本可能源码有些区别

java代码如何执行命令?

大概有下面三种方式

  1. java.lang.Runtime#exec()

  2. java.lang.ProcessBuilder#start()

  3. java.lang.ProcessImpl#start()

下面调试一下 java.lang.Runtime执行代码的过程
示例代码如下

public class Main {

    static void runtime() throws IOException {
        Process Runexec = Runtime.getRuntime().exec("cmd /c dir");
        InputStream in = Runexec.getInputStream();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024];
        int size;
        while((size = in.read(bytes)) > 0)
            bos.write(bytes,0,size);
        System.out.println(bos.toString());
    }
    public static void main(String []args) throws IOException {
        runtime();
    }
}

Runtime的exec又把参数传给了重载的exec

传入参数通过空格分割成数组


然后将数组形式的命令传给了ProcessBuilder
至此,前几个exec操作都是处理命令的,如果直接这样调用Runtime.getRuntime().exec(new String[]{"cmd","/c","dir"},null,null);就会直接到最后一个exec
下面进入ProcessBuilder的start函数

先看第一个红框,说明java在创建进程时会先进行一个安全检查(如果有配置安全策略的话)
但下一步,下一个红框可以看见,又将命令传给了ProcessImpl,那如果直接调用ProcessImpl是不是可以绕过安全策略?

然后是ProcessImpl的start方法,把命令信息传入,创建了ProcessImpl对象

这里又检查了jdk.lang.Process.allowAmbiguousCommands

如果为true,那就直接调用createCommandLine生成cmdstr

cmdstr = createCommandLine(
    //legacy mode doesn't worry about extended verification
    VERIFICATION_LEGACY,
    executablePath,
    cmd);

如果为false,就要对执行的程序进行分类,然后生成命令

boolean isShell = allowAmbiguousCommands ? isShellFile(executablePath)
        : !isExe(executablePath);
cmdstr = createCommandLine(
        // We need the extended verification procedures
        isShell ? VERIFICATION_CMD_BAT
                : (allowAmbiguousCommands ? VERIFICATION_WIN32 : VERIFICATION_WIN32_SAFE),
        quoteString(executablePath),
        cmd);


然后通过create函数创建进程

而create是一个native函数(就是其它语言写的函数,做成动态库,java在运行时就加载了,所以这里声明一下就可以直接调用)

private static synchronized native long create(String cmdstr,
                                      String envblock,
                                      String dir,
                                      long[] stdHandles,
                                      boolean redirectErrorStream)
        throws IOException;

至此回顾一下调用过程
runtime的exec方法内执行了:return new ProcessBuilder(命令信息),=》
ProcessBuilder执行了:return ProcessImpl.start(命令信息) =》
ProcessImpl.start执行了 return ProcessImpl(命令信息)
而ProcessImpl通过native函数create创建了进程,然后将创建进程的信息存到了对象的属性中

所以最终exec或得到的是ProcessImpl的对象,又因为ProcessImpl继承自Process,所以可以使用Process接收返回值

现在知道了
Runtime是调用的ProcessBuilder
而ProcessBuilder是调用的ProcessImpl
所以上面三种方式本质都是调用ProcessImpl创建进程,命令执行

刚才是通过Runtime命令执行的
再换ProcessBuilder试试
回想起刚才runtime的exec是这样调用ProcessBuilder的

可实际这样写的时候,发现调用environment报错

因为源码中environment方法没写作用域,默认default作用域,只能同包下的类可以调用,而runtime和他都是java.lang包下,所以runtime可以调用
把environment(null)去掉试试:Process p = new ProcessBuilder(new String[]{"cmd","/c","dir"}).directory(null).start();
命令执行成功

下面再试试ProcessImpl
再模仿ProcessBuilder调用的方式试试

ProcessImpl直接爆红,原因是ProcessImpl也是default作用域,,

那问题就出现了,如果一个对象或方法,作用域是default或private,当前代码不在作用域内
是不是真的就不能调用了?

# java漏洞 # 代码审计 # java反序列化 # Java代码审计 # JAVA安全
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录