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'))
相关推荐
缘友一世3 小时前
深入理解Shell与反弹Shell:从原理到实战
linux·网络安全·shell
Whoami!4 小时前
⸢ 捌-Ⅰ⸥⤳ 可信纵深防御应用实践:0Day漏洞 & 钓鱼攻击
网络安全·信息安全·纵深防御·案例实践
white-persist5 小时前
CSRF 漏洞全解析:从原理到实战
网络·python·安全·web安全·网络安全·系统安全·csrf
北京耐用通信8 小时前
电力自动化新突破:Modbus如何变身Profinet?智能仪表连接的终极解决方案
人工智能·物联网·网络安全·自动化·信息与通信
汤愈韬8 小时前
NTFS权限基础、权限累加规则、权限继承规则
windows·网络安全
lingggggaaaa11 小时前
小迪安全v2023学习笔记(一百三十四讲)—— Windows权限提升篇&数据库篇&MySQL&MSSQL&Oracle&自动化项目
java·数据库·windows·笔记·学习·安全·网络安全
Bruce_Liuxiaowei16 小时前
Win7虚拟机加入域错误排查指南:解决无法启动服务问题
运维·网络·windows·安全·网络安全
mjhcsp1 天前
博客安全攻防演练:从实战视角构建全方位防护体系
网络安全·博客
2401_865382501 天前
工信部发布→《云计算综合标准化体系建设指南(2025版)》
网络安全·云计算·信息安全技术