freeBuf
主站

分类

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

特色

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

点我创作

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

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

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

FreeBuf+小程序

FreeBuf+小程序

从Java源码来看Native命令执行方法
2023-04-19 15:40:22
所属地 四川省

概述

在RASP等安全产品防护严密的现在,普通的寻找Runtime.getRuntime().exec(cmds)的调用已经成为了一件不现实的事情。

同样的,在Java中盛行的反序列化漏洞中,如果将RCE的功能简单的通过Runtime.getRuntime().exec(cmds)这种结构来进行实现可能大概率也不能达到我们的目的,所以探索一下Runtime的底层实现,使用更加底层且复杂的调用来进行RCE功能的实现相对来说更加的可行

这里主要是对Java中多种命令执行的方式跟踪源码进行原理分析、构造利用代码、集成自研工具。

前置

首先需要对Java中的反射机制有着基本的掌握

通过反射的方式,我们可以获取到任何类的构造方法,类方法,成员变量,且能够获取对应类对象进行对应方法的调用等等目的

  1. 获取Class类对象

    对于类对象的获取,主要可以通过Class.forName / loadClass的方式来获取,值得注意的是,在调用Class.forName进行类的加载的时候,将会调用static方法

    Class.forName("java.lang.Runtime")
    

    image-20230418202156861.png

  2. 获取对应类的构造方法

    对于获取类的构造方法,主要可以通过getConstructor或者getDeclaredConstructor这两种方法来进行实现

    image-20230418202545305.png

    image-20230418202554295.png

    两者的区别主要是前者不能够反射获取private修饰的构造方法,而后者能够获取

    所以通常使用后者进行构造函数的获取,传入的参数就是对应构造方法的参数类

    clazz.getDeclaredConstructor(type.class)
    clazz.getConstructor(type.class)
    
  3. 反射获取成员变量

    和构造方法类似的,存在有getFieldgetDeclaredField两个不同的获取方法,区别和构造函数类似

    clazz.getField(name)
    clazz.getDeclaredField(name)
    
  4. 反射获取类方法

    同样具有getMethodgetDeclaredMethod两种

  5. ...............

一个普通的命令执行是

Runtime.getRuntime().exec("calc");

如果使用反射机制

可以是

Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(runtime, "calc");

或者是其他的一些使用反射机制的变形

command

跟踪Runtime

首先我们跟踪Runtime执行命令的过程

image-20230418203905152.png

在这里接收一个String类型的参数,调用exec的另一个重在方法对参数进行处理,将其通过分隔符,将其封装成了数组对象(这里就是一个字符串)

image-20230418204046203.png

之后通过参数是String[]类型的另一个重载方法,通过调用ProcessBuilder类的方法进行执行

image-20230418204233337.png

ProcessBuilder#start方法中,将命令传递给了ProcessImpl#start方法进行处理

image-20230418204613461.png

windows

在windows中主要是在ProcessImpl的构造方法中调用了create方法

image-20230418210009655.png

这个create方法是通过win32的方式创建了一个进程

image-20230418210027743.png

linux

在linux下,在ProcessImpl#start的调用中将会创建一个UNIXProcess对象并返回

image-20230418213534618.png

image-20230418213547533.png

UNIXProcess类的构造方法中,调用了forkAndExec这个native方法

image-20230418213735497.png

创建了一个一个进程,并返回了对应进程的pid

构造命令执行

ProcessBuilder#start

在上面的流程分析中,知道了在Runtime.getRuntime().exec()方法调用的下一层就是使用ProcessBuilder#start方法

如果hook掉了我们可以通过使用ProcessBuilder类来进行命令执行的构造

new ProcessBuilder("calc").start();

或者使用反射的思路构造

//method_1
        Class pro = Class.forName("java.lang.ProcessBuilder");
        ((ProcessBuilder) pro.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();

        //method_2
        Class pro = Class.forName("java.lang.ProcessBuilder");
        pro.getMethod("start").invoke(pro.getConstructor(List.class).newInstance(Arrays.asList("calc.exe")));

        //method_3
        Class pro = Class.forName("java.lang.ProcessBuilder");
        ((ProcessBuilder) pro.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})).start();

        //method_4
        Class pro = Class.forName("java.lang.ProcessBuilder");
        pro.getMethod("start").invoke(pro.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}}));

ProcessImpl

从上面的分析可以知道,在windows环境下的JDK

ProcessImpl类的构造方法将会调用create方法执行native方法进行命令执行

image-20230418215221068.png

所以我们只需要反射获取ProcessImpl类的构造方法并实例化就会执行我们的恶意逻辑

UNIXProcess

上面是针对windows的方式

针对linux,在前面的分析中知道主要是在其start方法中调用了UNIXProcess类的构造方法

image-20230419094058481.png

执行forkAndExec这个native方法进行命令执行

other

甚至于,我们知道最后主要是在create方法(windows)、forkAndExec方法(linux)中执行命令,我们同样可以通过反射这两个方法进行命令执行

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