freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

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

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安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录