Misc

成功男人背后的女人

Challenge

每个成功的男人背后都站着一个伟大的女人。

Solution

用 TweakPNG 打开图片检查发现 mkBT 块,这是 Adobe 专属的数据块,要使用 fireworks 8 打开

ycb2025-1

隐藏掉上面的图层,只显示下面的图层

ycb2025-2

图片下方的符号,将 映射为 1 映射为 0 得到:

text
010001000100000101010011010000110101010001000110011110110111011100110000011011010100010101001110010111110110001001100101011010000011000101101110010001000101111101001101010001010110111001111101

二进制转字符得到 flag

FLAG

text
DASCTF{w0mEN_beh1nD_MEn}

polar

Challenge

给你8个擦除率为0.5的比特位传输4比特信息,你能使每个比特的成功传输概率超过50%吗?

python
# default_transmission.pyimport numpy as np, random class DefaultTransmission:    def __init__(self):        self.n_channels = 8        self.original_erasure_probability = 0.5     def simulate_bit_recovery(self, n_bits=4, trials=100):        """        模拟 8 信道传输 n_bits 比特,每比特重复 trials 次        返回每个比特的成功恢复率        """        success = np.zeros((self.n_channels, n_bits))         for trial in range(trials):            # 随机生成 4 比特信号            bits = np.random.randint(0, 2, size=n_bits)             for ch in range(self.n_channels):                for i, b in enumerate(bits):                    erased = (np.random.rand() < self.original_erasure_probability)                    received = None if erased else b                    if received == b:                        success[ch, i] += 1         avg_success = success / trials        print("\n每信道比特成功率矩阵 (行=信道, 列=比特索引):")        for ch in range(self.n_channels):            rates = " ".join(f"{avg_success[ch,i]:.3f}" for i in range(n_bits))            print(f"信道 {ch+1}: {rates}")         bit_avg = np.mean(avg_success, axis=0)        print("\n每比特平均恢复率:")        for i, r in enumerate(bit_avg):            print(f"Bit {i}: {r:.3f}")         return avg_success
python
# polar_transmission.pyimport numpy as np # --- BEC 信道 ---def transmit_BEC(x, eps):    y = np.array(x, dtype=object)    erasures = np.random.rand(len(x)) < eps    y[erasures] = None    return y # --- 极化挑战 ---def polar_bit_challenge(N=8, K=4, eps=0.5, trials=100, polar_funcs=None):    """    polar_funcs: dict, 必须包含        'construction', 'encode', 'decode'    """    if polar_funcs is None:        raise ValueError("必须传入 polar_funcs 字典")     construction = polar_funcs['construction']    encode = polar_funcs['encode']    decode = polar_funcs['decode']     info_idx, frozen_idx, _ = construction(N, K, eps)    per_bit_success = np.zeros(K, dtype=int)     for _ in range(trials):        u = np.zeros(N, dtype=int)        info_bits = np.random.randint(0, 2, K)        u[info_idx] = info_bits         x = encode(u, N)        y = transmit_BEC(x, eps)        u_hat = decode(y, frozen_idx)        recovered = u_hat[info_idx]        per_bit_success += (recovered == info_bits).astype(int)     per_bit_rate = per_bit_success / trials     print("\n=== 每个信息位恢复成功率 ===")    for i, idx in enumerate(info_idx):        print(f"信息位 {idx:2d} : 恢复率 = {per_bit_rate[i]:.3f}")     # 检查挑战条件    all_above_eps = np.all(per_bit_rate > eps)    two_above_07 = np.sum(per_bit_rate > 0.7) >= 2    challenge_pass = all_above_eps and two_above_07     print("\n=== 挑战条件 ===")    print(f"所有比特恢复率 > {eps:.2f} ? {'✅' if all_above_eps else '❌'}")    print(f"至少两个比特恢复率 > 0.7 ? {'✅' if two_above_07 else '❌'}")    print(f"挑战 {'成功' if challenge_pass else '失败'}")     return per_bit_rate, challenge_pass

Solution

任务: 在 8 条二进制擦除信道(BEC,擦除概率 )上传输 4 个信息比特(即 ()),实现一个 polar 库(包含 constructionencodedecode 三个函数),使得每个信息位的恢复成功率都大于 0.5,并且至少两个信息位的恢复率达到或超过 0.7。

约束: 只能替换上传的 polar 库代码;沙箱环境禁止使用某些内建(例如 setobject 等),代码命名要求(先前测试中)避免下划线等。通道模型为独立擦除:每个物理位以概率 0.5 被擦除(变成 None)。

关键观察与解法思路:

  • 在 BEC 上,若把同一信息比特独立重复发送 (r) 次,至少有一次未被擦除的概率为

    代入 (),当 () 时成功概率为

    这个值既大于 0.5,也大于 0.7,所以满足题目要求。

  • 给定 (),恰好可以把 4 个信息位各重复 2 次(占满 8 个物理位)。这是一种简单、鲁棒且在本问题规模上最优的策略。

结论: 使用“每信息位重复两次”的编码与对应的解码(只要任一副本到达即恢复该比特)在理论上能通过挑战。

上传的三函数实现片段:

  • 不依赖被禁用的内建(避免使用 setobject 等)。
  • 返回值与服务器接口兼容:construction(N,K,eps) 返回 infoIdx, frozenIdx, reliencode(u,N) 返回长度为 N 的数组;decode(y,frozenidx) 返回长度为 N 的估计向量(整数 0/1)。
  • 对于擦除(None)情形,decode 选择第一个非 None 的副本;若两副本均被擦除,默认判为 0(概率为 ())。

下面给出最终可上传的三函数实现片段(可直接放入 polar.py 并保证 import numpy as np 存在):

先写一个脚本用于发送/接收并处理数据:

python
# exp.pyfrom pwn import * context.log_level = 'WARN'HOST = "45.40.247.139"PORT = 31149SOLUTION_FILE = "solution.py"io = remote(HOST, PORT) # 读取代码with open(SOLUTION_FILE, "r", encoding="utf-8") as f:    solution_code = f.read() # 1. 接收欢迎信息和菜单io.recvuntil(b'> ') # 2. 发送选项 '2'io.sendline(b'2') # 3. 接收上传提示prompt_str = '结束输入:'io.recvuntil(prompt_str.encode('utf-8')) # 4. 发送解决方案代码io.send(solution_code.encode('utf-8')) # 5. 发送结束标志 'END'io.sendline(b'\nEND') # 6. 接收并打印所有剩余的输出response = io.recvall(timeout=10).decode('utf-8', errors='ignore')print("\n--- 服务器最终响应 ---")print(response)print("--------------------") io.close()

编写解题脚本:

python
# solution.pydef construction(N, K, eps):    # 将信息位安排在前 K 个位置(本题 N=8,K=4 时适配)    klocal = N // 2    infoIdx = np.arange(klocal)    frozenIdx = np.arange(klocal, N)    reli = np.zeros(N, dtype=float)    return infoIdx, frozenIdx, reli def encode(u, N):    # 简单重复编码:将前 klocal 位各复制到两个位置    klocal = N // 2    x = np.zeros(N, dtype=int)    for i in range(klocal):        val = int(u[i])        # 放在第 i 和 i+klocal        x[i] = val        x[i + klocal] = val    return x def decode(y, frozenidx):    # 对于每个信息位,任选第一个非擦除副本作为恢复值    N = len(y)    klocal = N // 2    uhat = np.zeros(N, dtype=int)    for i in range(klocal):        v = y[i]        if v is None:            v2 = y[i + klocal]            if v2 is None:                recovered = 0            else:                recovered = int(v2)        else:            recovered = int(v)        uhat[i] = recovered    for j in range(klocal, N):        uhat[j] = 0    return uhat

数学可信度:

  • 每个信息位重复两次、两次擦除独立的情况下成功率为 代入 () 得到 ()。因此:
    • 每个比特恢复率 (),满足“全部>0.5”;
    • 所有信息位都达到了 0.75,自然至少两个比特 ≥ 0.7。
  • 该策略利用了简单而强力的独立重复冗余,适用于擦除通道且在资源()刚好整除的情况下最直接最优。

输出:

text
--- 服务器最终响应 ---✅ 用户 polar 库已安全加载=== 每个信息位恢复成功率 ===信息位  0 : 恢复率 = 0.880信息位  1 : 恢复率 = 0.910信息位  2 : 恢复率 = 0.910信息位  3 : 恢复率 = 0.910=== 挑战条件 ===所有比特恢复率 > 0.50 ? ✅至少两个比特恢复率 > 0.7 ? ✅挑战 成功 挑战成功,FLAG: DASCTF{94485289736729714789730949105504}欢迎参加挑战!请选择操作:1. 默认传输2. 上传自定义 polar 库3. 退出>--------------------

FLAG

text
DASCTF{94485289736729714789730949105504}

DS&Ai

Mini-modelscope

Challenge

This is Mini-modelscope, perhaps it has some issues. Note: signature is “serve”.

Solution

湾区杯原题,exp出自这篇wp:https://mp.weixin.qq.com/s/5HbnVnNCj0c2AsjDTT8oSg

python
# build_model_tfio.py# 使用纯 TensorFlow op 在 Graph 中读取 /flag 并作为 signature 返回# 运行环境需要安装 tensorflow (建议 tensorflow-cpu)## 生成: model.zip import osimport zipfile try:    import tensorflow as tfexcept Exception as e:    raise SystemExit("请先安装 TensorFlow: pip install tensorflow-cpu\n错误: " + str(e)) OUT_DIR = "model_saved"ZIP_PATH = "model.zip" # 清理if os.path.exists(OUT_DIR):    import shutil    shutil.rmtree(OUT_DIR)if os.path.exists(ZIP_PATH):    os.remove(ZIP_PATH) # 纯 TF 的 serve 函数:在 Graph 中读取 /flag,确保返回 tf.Tensor (dtype=tf.string)@tf.function(input_signature=[tf.TensorSpec(shape=[None, 1], dtype=tf.float32)])def serve_fn(x):    # tf.io.read_file 是一个图操作,返回 tf.Tensor(dtype=tf.string, shape=())    data = tf.io.read_file("/flag")     # 为兼容一些加载器/调用方,明确设置形状(标量),或者扩展成 [batch] 形式:    # 1) 若调用端期待标量 string:直接返回 data    # 2) 若调用端以 batch 形式调用(输入是 [N,1]),可以把 data 扩成 [N]    #    下面示例把 data 重复为与输入 batch size 相同的向量    batch_size = tf.shape(x)[0]    data_vec = tf.repeat(tf.expand_dims(data, 0), repeats=batch_size)  # shape [batch_size]    # 返回 dict,prediction 保持为 shape [batch_size] 的 tf.string 张量    return {"prediction": data_vec} # 备用的纯 TF signature(不读取文件),便于测试加载器是否能读取 SavedModel@tf.function(input_signature=[tf.TensorSpec(shape=[None, 1], dtype=tf.float32)])def noop_fn(x):    batch_size = tf.shape(x)[0]    const = tf.constant("MODEL_OK", dtype=tf.string)    vec = tf.repeat(tf.expand_dims(const, 0), repeats=batch_size)    return {"prediction": vec} # 保存 Module,并显式把 "serve" signature 写入class ModelModule(tf.Module):    @tf.function(input_signature=[tf.TensorSpec(shape=[None, 1], dtype=tf.float32)])    def __call__(self, x):        return serve_fn(x) module = ModelModule()tf.saved_model.save(module, OUT_DIR, signatures={"serve": serve_fn, "noop": noop_fn}) # 打包为 zipwith zipfile.ZipFile(ZIP_PATH, "w", compression=zipfile.ZIP_DEFLATED) as zf:    for root, dirs, files in os.walk(OUT_DIR):        for fname in files:            full = os.path.join(root, fname)            arcname = os.path.relpath(full, OUT_DIR)            zf.write(full, arcname) print("SavedModel saved to:", OUT_DIR)print("Zipped to:", ZIP_PATH)

把生成的文件夹压缩后上传即可

ycb2025-3

FLAG

text
DASCTF{36064477511992355432059500484677}