NewStarCTF2025-WEEK2

OhNativeEnc

根据提示直接分析so文件

发现是XXTEA加密

密钥:

密文:

解密如下

python 复制代码
from struct import pack, unpack

# --- 配置区: 若需要请按实际内存填入(32 字节 hex, 空格分隔或逗号分隔均可) ---
CIPHERTEXT_HEX = "B6 53 6E 4D 77 5D 08 D2 FB 2C 63 1E BB 7B 01 9B F5 04 6A F4 0E 84 27 47 64 A1 E4 D9 EF 12 44 37"
KEY_BYTES = b"ThisIsAXXteaKey\x00"   # 确认为 16 字节(你已经确认末尾为 0)

# ------------------ 下面为实现(无需修改) ------------------
mask32 = 0xFFFFFFFF

def hextobytes(s):
    s = s.replace("0x"," ").replace(",", " ").strip()
    parts = [p for p in s.split() if p]
    return bytes(int(p,16) for p in parts)

# parse ciphertext
ct = hextobytes(CIPHERTEXT_HEX)
if len(ct) != 32:
    raise SystemExit("CIPHERTEXT must be exactly 32 bytes (32 hex bytes).")

# interpret as 8 uint32 little-endian words (mapping matches binary: dest[0..3] -> W0 etc.)
W = list(unpack("<8I", ct))

# parse key into 4 uint32 little-endian words
if len(KEY_BYTES) != 16:
    raise SystemExit("KEY_BYTES must be exactly 16 bytes.")
KEY = list(unpack("<4I", KEY_BYTES))

# helper F function (matches forward update used in binary)
def F(a, b, i_val, kx):
    # (((a >> 5) ^ (4 * b)) + ((b >> 3) ^ (16 * a))) ^ ((i ^ b) + (kx ^ a))
    part1 = ((a >> 5) ^ ((4 * b) & mask32)) & mask32
    part2 = ((b >> 3) ^ ((16 * a) & mask32)) & mask32
    sum12 = (part1 + part2) & mask32
    part3 = ((i_val ^ b) + ((kx ^ a) & mask32)) & mask32
    return (sum12 ^ part3) & mask32

# Build reverse deltas: forward loop used i = 114514 * k for k=1..12.
deltas = [114514 * k for k in range(12, 0, -1)]  # 12..1

# Make a local copy to operate on
v = W[:]  # v[0]..v[7]

for i in deltas:
    v13 = (i >> 2) & 3
    v14 = KEY[v13]
    # reverse updates in exact reverse order of forward encryption
    # forward order (from asm snippet): v5(v0) += ..., v6(v1) += ..., v11(v2) += ..., v7(v3) += ...,
    # v8(v4) += ..., v9(v5) += ..., v10(v6) += ..., v4(v7) += ...
    # reverse subtract in opposite order:
    # reverse v7:
    tmp = F(v[6], v[0], i, KEY[v13 ^ 3])
    v[7] = (v[7] - tmp) & mask32
    # reverse v6:
    tmp = F(v[5], v[7], i, KEY[v13 ^ 2])
    v[6] = (v[6] - tmp) & mask32
    # reverse v5:
    tmp = F(v[4], v[6], i, KEY[v13 ^ 1])
    v[5] = (v[5] - tmp) & mask32
    # reverse v4:
    tmp = F(v[3], v[5], i, v14)
    v[4] = (v[4] - tmp) & mask32
    # reverse v3:
    tmp = F(v[2], v[4], i, KEY[v13 ^ 3])
    v[3] = (v[3] - tmp) & mask32
    # reverse v2:
    tmp = F(v[1], v[3], i, KEY[v13 ^ 2])
    v[2] = (v[2] - tmp) & mask32
    # reverse v1:
    tmp = F(v[0], v[2], i, KEY[v13 ^ 1])
    v[1] = (v[1] - tmp) & mask32
    # reverse v0:
    tmp = F(v[7], v[1], i, v14)
    v[0] = (v[0] - tmp) & mask32

# pack back to bytes
plain = pack("<8I", *v)

# print results
print("Recovered plaintext (hex):", plain.hex())
# printable ASCII or escaped
def printable_ascii(b):
    try:
        s = b.decode('ascii')
    except:
        return None
    # check for printable range 0x20..0x7e or newline/tab allowed
    if all((0x20 <= ord(ch) <= 0x7e) or ch in "\r\n\t" for ch in s):
        return s
    return None

s = printable_ascii(plain)
if s is not None:
    print("Recovered plaintext (ASCII, printable):")
    print(s)
else:
    # show escaped bytes and printable-chars-with-escapes
    print("Recovered plaintext contains non-printable bytes. Escaped form:")
    print(''.join('\\x{:02x}'.format(b) for b in plain))
    # also show a mixed printable representation
    mixed = ''.join((chr(b) if 0x20 <= b <= 0x7e else '\\x{:02x}'.format(b)) for b in plain)
    print("Mixed view:", mixed)

尤皮·埃克斯历险记(1)

查壳发现有upx壳,无魔改,用UPX工具直接脱壳即可,对脱壳后文件分析

主要逻辑如下,其中v19是我们的输入,v21是密文,长度为34

分析可知,先对我们的输入经过一个encrypt函数加密,然后进行调试检测,如果没有检到debug,就将encrypt的处理结果异或上0x3c

跟进分析encrypt,发现是对输入进行一系列变换

对大小写字符(-69-v24)

对数字(105-v24)

不对符号进行变换,写脚本解密即可

python 复制代码
flag=""
enc=[0x69, 0x73, 0x66, 0x68, 0x47, 0x4A, 0x09, 0x74, 0x7E, 0x63, 0x55, 0x0A, 0x79, 0x0A, 0x75, 0x54,0x6A, 0x63, 0x6A, 0x09, 0x54, 0x7E, 0x63, 0x6A, 0x51, 0x64, 0x75, 0x7E, 0x77, 0x7B, 0x04, 0x05,0x71, 0x41]
print(len(enc))
for x in enc:
    flag+=chr(x^0x3c)
for c in flag:
    if(ord(c)>0x39):
        if((c>="a" and c<="z")or(c>="A" and c<="Z")):
            print(chr((-ord(c)-69)&0xff),end='')
        else:
            print(c,end='')
    else:
        print(chr((105-ord(c))&0xff),end='')

flag{E4sy_R3v3rSe_e4Sy_eNcrypt10n}

Look at me carefully

可以发现是对输入经过函数sub_4016E0变换,然后和v7字符串比较

打断点查看加密后的结果,结果多次调试,可以发现本质上是进行索引重排

输入

加密结果

找到序列即可

patch修改我们的输入如下

![[Pasted image 20251008172040.png]]

运行到加密结束

提取到下标列表,重排解密即可

python 复制代码
flag=""
index1=[   0x1C, 0x06, 0x07, 0x0A, 0x1D, 0x13, 0x21, 0x1E, 0x05, 0x0C, 
  0x10, 0x12, 0x17, 0x09, 0x23, 0x11, 0x14, 0x08, 0x1B, 0x24, 
  0x03, 0x0F, 0x16, 0x01, 0x02, 0x1A, 0x0E, 0x18, 0x15, 0x1F, 
  0x22, 0x0B, 0x04, 0x0D, 0x19, 0x20]
enc="cH4_1elo{ookte?0dv_}alafle___5yygume"
for i in range(36):
    c=index1.index(i+1)
    flag+=enc[c]
print(flag)

flag{H4ve_you_lo0ked_at_me_c1o5ely?}

采一朵花,送给艾达(1)

手动去除花指令

去除完main函数的花指令后,查看伪代码

发现是RC4加密,先看一下rc4_init,去除花指令后发现有魔改

再看rc4_crypt,发现也魔改了

key和密文在main中已给出,修改脚本直接解密即可

c 复制代码
#include<stdio.h>
#include<string.h>
typedef unsigned longULONG;

//初始化函数
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len)
{
	int i = 0, j = 0;
	char k[256] = { 0 };
	unsigned char tmp = 0;
	for (i = 0; i < 256; i++)
	{
		s[i] = -i;
		k[i] = key[i % Len];
	}
	for (i = 0; i < 256; i++)
	{
		j = (j + s[i] + k[i]) % 256;
		tmp = s[i];
		s[i] = s[j]; // 交换s[i]和s[j]
		s[j] = tmp;
	}
}

//加解密
void rc4_crypt(unsigned char* s, unsigned char* Data, unsigned long Len)
{
	int i = 0, j = 0, t = 0;
	unsigned long k = 0;
	unsigned char tmp;
	for (k = 0; k < Len; k++)
	{
		i = (i + 1) % 256;
		j = (j + s[i]) % 256;
		tmp = s[i];
		s[i] = s[j]; // 交换s[x]和s[y]
		s[j] = tmp;
		t = (s[i] + s[j]) % 256;
		Data[k] -= s[t];
	}
}

int main()
{
	unsigned char s[256] = { 0 }, s2[256] = { 0 }; // S-box
	char key[256] = { "EasyJunkCodes" };
	char pData[] = { 0xC7, 0x7F, 0xC1, 0x43, 0x03, 0x64, 0x75, 0x11,0x88, 0xB8, 0x8C, 0x55, 0xF6, 0xC0, 0x23, 0xDF,
	0x4D, 0x0F, 0x2E, 0x9E, 0xF6, 0x82, 0xF0, 0xF2 ,0xBC, 0x51, 0x6B, 0x08, 0x29, 0x83, 0x27, 0xE1 ,0xCB, 0xBD, 0xC6, 0x88, 0xB1, 0x80, 0x4F, 0x4E };
	unsigned long len = strlen(pData);
	int i;

	printf("pData=%s\n", pData);
	printf("key=%s,length=%d\n\n", key, strlen(key));

	rc4_init(s, (unsigned char*)key, strlen(key)); // 已经完成了初始化
	printf("\n\n");
	for (i = 0; i < 256; i++) // 用s2[i]暂时保留经过初始化的s[i],很重要的!!!
	{
		s2[i] = s[i];
	}

	//可以看到,加解密函数都是相同的
	printf("已经加密,现在解密:\n\n");
	rc4_crypt(s2, (unsigned char*)pData, len); // 解密
	printf("pData=%s\n\n", pData);
	//如果需要得到16进制数组

	//printf("解密后pData(16进制): ");
	//for (i = 0; i < len; i++) {
	 //   printf("%02X ", pData[i]);
	//} 
	return 0;
}

flag{Junk_C0d3s_4Re_345y_t0_rEc0gn1Ze!!}

Forgotten_Code

汇编,可以手撕,也可以直接丢AI

里面有个比较容易错的地方,就是密钥每次都会异或上17,所以第一三块的解密密钥和二四块的解密密钥不同

python 复制代码
#!/usr/bin/env python3
# verify_chal.py
# 验证脚本:严格模拟汇编行为(ng 每次调用 fn 前 ^=17;fn 为 TEA 加密 32 轮)
# 用法:
#   python verify_chal.py "flag{...}"
# 或直接运行:
#   python verify_chal.py
# 会用内置 candidate 测试

import sys
import struct

# 原始 ng 数据(和汇编中完全一致)
NG_INITIAL = bytearray(b"sp\x7fvuctp|xeb|hv~")  # 16 bytes, includes 0x7F

# ezgm 常量(按汇编给出)。这里转换为 32-bit 无符号整数数组
EZGM = [
    1210405119, 710975774,
    -90350153, -1958008304,
    -745722482, 67707510,
    -86515270, -1728462407
]
EZGM_U = [x & 0xFFFFFFFF for x in EZGM]

# TEA constants
DELTA = 0x9E3779B9
ROUNDS = 32
MASK32 = 0xFFFFFFFF

def ng_xor17(ng):
    """就地对 ng 的 16 字节逐字节 ^= 17(与汇编一致)"""
    for i in range(16):
        ng[i] ^= 17

def tea_encrypt_block(v0, v1, key_words):
    """TEA 加密(32 轮),返回 (v0', v1')。与汇编中 v0+=... v1+=... 顺序一致。"""
    v0 &= MASK32
    v1 &= MASK32
    sum_ = 0
    k0, k1, k2, k3 = key_words
    for _ in range(ROUNDS):
        sum_ = (sum_ + DELTA) & MASK32
        # v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1)
        t1 = ( (v1 << 4) + k0 ) & MASK32
        t2 = (v1 + sum_) & MASK32
        t3 = ( (v1 >> 5) + k1 ) & MASK32
        v0 = (v0 + ((t1 ^ t2) ^ t3)) & MASK32
        # v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3)
        u1 = ( (v0 << 4) + k2 ) & MASK32
        u2 = (v0 + sum_) & MASK32
        u3 = ( (v0 >> 5) + k3 ) & MASK32
        v1 = (v1 + ((u1 ^ u2) ^ u3)) & MASK32
    return v0, v1

def tea_decrypt_block(v0, v1, key_words):
    """TEA 解密(逆运算),用于从 ezgm 反推原始明文块"""
    v0 &= MASK32
    v1 &= MASK32
    sum_ = (DELTA * ROUNDS) & MASK32
    k0, k1, k2, k3 = key_words
    for _ in range(ROUNDS):
        v1 = (v1 - (((v0 << 4) + k2) ^ (v0 + sum_) ^ ((v0 >> 5) + k3))) & MASK32
        v0 = (v0 - (((v1 << 4) + k0) ^ (v1 + sum_) ^ ((v1 >> 5) + k1))) & MASK32
        sum_ = (sum_ - DELTA) & MASK32
    return v0, v1

def get_key_words_from_ng(ng):
    """从 ng 当前状态按小端读出 4 个 uint32 作为 TEA key"""
    return [int.from_bytes(bytes(ng[i:i+4]), 'little') for i in range(0, 16, 4)]

def fn_simulate_encrypt(ng, block_bytes):
    """
    模拟 _Z2fnPj 的行为(加密流程):
      - 函数一开始对 ng 做 ^=17(就地)
      - 读取 key(4 * uint32 小端)
      - 将 block_bytes(8字节)拆为 v0,v1(小端 uint32)
      - 用 TEA 加密得到 v0',v1'
      - 返回 (v0', v1')
    """
    # toggle ng first (汇编每次进入 fn 都执行该操作)
    ng_xor17(ng)
    key_words = get_key_words_from_ng(ng)
    v0 = int.from_bytes(block_bytes[0:4], 'little')
    v1 = int.from_bytes(block_bytes[4:8], 'little')
    return tea_encrypt_block(v0, v1, key_words)

def verify_flag(candidate):
    """验证 candidate 是否为正确 flag(按照汇编逻辑)"""
    if not candidate.startswith("flag{") or not candidate.endswith("}"):
        print("Invalid flag format!")
        return False
    inner = candidate[5:-1]
    if len(inner) != 32:
        print("Wrong length! inner length =", len(inner))
        return False

    # working copy of ng (mutable)
    ng = bytearray(NG_INITIAL)

    # for each 8-byte block, simulate fn (encrypt) and compare to ezgm
    for i in range(4):
        offset = i * 8
        block = inner.encode('latin1')[offset:offset+8]  # latin1 preserves raw bytes 0-255
        if len(block) != 8:
            print("Block", i, "too short")
            return False
        out_v0, out_v1 = fn_simulate_encrypt(ng, block)
        expected_v0 = EZGM_U[i*2]
        expected_v1 = EZGM_U[i*2+1]
        if out_v0 != expected_v0 or out_v1 != expected_v1:
            print(f"Block {i} mismatch:")
            print(f"  simulated -> v0={hex(out_v0)}, v1={hex(out_v1)}")
            print(f"  expected  -> v0={hex(expected_v0)}, v1={hex(expected_v1)}")
            return False

    print("Right! flag accepted")
    return True

def reconstruct_inner_from_ezgm():
    """
    从 ezgm 反推出 inner 32 字节(按汇编交替 key)
    注意:汇编中每次调用 fn 时都会先对 ng ^=17,因此在加密时 key 是交替的。
    当我们解密 ezgm (密文)时,需要按相同的顺序交替使用 ng 状态来解密。
    """
    ng = bytearray(NG_INITIAL)
    recovered = bytearray()
    for i in range(4):
        # 在程序中,fn 在加密时会在每次进入时调用 ng ^=17,得到用于该次加密的 key。
        # 要解密 ezgm[i],我们需要用对应时刻的 key(即调用前执行了 ng ^=17 的 ng)。
        ng_xor17(ng)  # toggle to get the key used when encryption ran for this block
        key_words = get_key_words_from_ng(ng)
        ev0 = EZGM_U[i*2]
        ev1 = EZGM_U[i*2+1]
        dv0, dv1 = tea_decrypt_block(ev0, ev1, key_words)
        recovered += dv0.to_bytes(4, 'little') + dv1.to_bytes(4, 'little')
    return bytes(recovered)

if __name__ == "__main__":
    if len(sys.argv) > 1:
        candidate = sys.argv[1]
    else:
        # 默认 candidate(可替换为你想测试的字符串)
        candidate = "flag{4553m81y_5_s0o0o0_345y_jD5yQ5mD9}"
        print("No candidate provided; testing default:", candidate)
    ok = verify_flag(candidate)
    if not ok:
        # If verification fails, print expected inner 32 bytes (hex / latin1)
        expected_inner = reconstruct_inner_from_ezgm()
        print("\n-- Expected inner 32 bytes (hex):", expected_inner.hex())
        print("-- Expected inner 32 bytes (latin1 view):", expected_inner.decode('latin1'))
相关推荐
大方子4 小时前
【PolarCTF】rce1
网络安全·polarctf
枷锁—sha6 小时前
Burp Suite 抓包全流程与 Xray 联动自动挖洞指南
网络·安全·网络安全
聚铭网络7 小时前
聚铭网络再度入选2026年度扬州市网络和数据安全服务资源池单位
网络安全
darkb1rd9 小时前
八、PHP SAPI与运行环境差异
开发语言·网络安全·php·webshell
世界尽头与你13 小时前
(修复方案)基础目录枚举漏洞
安全·网络安全·渗透测试
枷锁—sha1 天前
【SRC】SQL注入快速判定与应对策略(一)
网络·数据库·sql·安全·网络安全·系统安全
liann1191 天前
3.1_网络——基础
网络·安全·web安全·http·网络安全
ESBK20252 天前
第四届移动互联网、云计算与信息安全国际会议(MICCIS 2026)二轮征稿启动,诚邀全球学者共赴学术盛宴
大数据·网络·物联网·网络安全·云计算·密码学·信息与通信
旺仔Sec2 天前
一文带你看懂免费开源 WAF 天花板!雷池 (SafeLine) 部署与实战全解析
web安全·网络安全·开源·waf
七牛云行业应用2 天前
Moltbook一夜崩盘:150万密钥泄露背后的架构“死穴”与重构实战
网络安全·postgresql·架构·高并发·七牛云