seccon 2023

# PREFACE: 本来人说和一块看,结果周末事情太多了只能自己做,赛中做了俩,后面只能再慢慢补题 qaq.
# 基本上算是比较少看国外赛,看了一下 seccon 感觉确实和平时做的差距挺大的,比如这个 re 很有些 misc 的感觉,难度还行不算很恶心,但是确实新,希望最近几天能把它复现出来
# 9-19: ok 勉强算搞完了,这个 nim 的 oi 还是挺恶心的…

# jumpout

签到题,写了个小混淆反静态看,大概意思是将所有的 jmp addr 改成类似 mov rax, addr; jmp rax 的操作,应该是写了个 llvm 小工具之类的

动调仔细看汇编即可,理论上可以自己修,但是没啥必要(而且也不太会,近期还得学学 angr,qaq),这里只是做了三次异或

需要对栈上和寄存器的数据跟的比较清晰,这里的 i 其实没跟明白但是可以简单猜一下,实在不行就每轮都跟完也能出(异或么反正,每轮都拿一个最后值就行其实~)

exp:

n
a = [  0xF6, 0xF5, 0x31, 0xC8, 0x81, 0x15, 0x14, 0x68, 0xF6, 0x35,
  0xE5, 0x3E, 0x82, 0x09, 0xCA, 0xF1, 0x8A, 0xA9, 0xDF, 0xDF,
  0x33, 0x2A, 0x6D, 0x81, 0xF5, 0xA6, 0x85, 0xDF, 0x17]
enc = [  0xF0, 0xE4, 0x25, 0xDD, 0x9F, 0x0B, 0x3C, 0x50, 0xDE, 0x04,
  0xCA, 0x3F, 0xAF, 0x30, 0xF3, 0xC7, 0xAA, 0xB2, 0xFD, 0xEF,
  0x17, 0x18, 0x57, 0xB4, 0xD0, 0x8F, 0xB8, 0xF4, 0x23]
for i in range(len(enc)):
    print(chr(i ^ 0x55 ^ enc[i] ^ a[i]),end='')
# SECCON{jump_table_everywhere}

# Sickle

第一次见这个 pickle 加载 payload 反序列化调用 shellcode 的操作,具体其实还是不太会搞,这题 pickletools.dis(payload) 得到的结果感觉用处不大,知道有个 xor 的信息,不知道怎么提取其实(等 wp 出来再复现复现)但是 re 么真不能太纠结过程吧,反正 fuzz 一下,嗯看一下 bytes,猜测是一个 rsa + xor + 倒序 的组合,参数很奇怪 n 是一个素数(开始以为要打,问了一下密码爷说不用直接可以解)解了第一段发现第二段参数不对,遂注意到 xor 参数换了,然后么就出了…

看这个 flag 内容感觉或许确实不太有动调工具,但是应该有方法起码把完整字节码反序列化拿到吧… 但是确实没搞出来,晚点看看 wp…

# 9-19: 原来可以直接改源码,增加 print,这样直接就有完整流程了,orz

exp:

n
check_values = [
    8215359690687096682,
    1862662588367509514,
    8350772864914849965,
    11616510986494699232,
    3711648467207374797,
    9722127090168848805,
    16780197523811627561,
    18138828537077112905,
]
import gmpy2
from Crypto.Util.number import long_to_bytes
xor = 1244422970072434993
for c in check_values:
    e = 65537
    n = 18446744073709551557
    phi = (18446744073709551557 - 1)
    d = gmpy2.invert(e, phi)
    m = pow(c,d,n)
    print(long_to_bytes(m ^ xor).decode()[::-1],end='')
    xor = c
# SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecode??}

# prefect-flu

# 9-17, 没出还,很急,感觉确实有点点 misc 来的

也是第一次见,dvd iso,没搞清楚怎么提取逻辑

感觉可以参考是这篇:蓝光文件解析 - 知乎 (zhihu.com),但是里面的工具编译不出来,它的文件路径感觉是乱的,还没解决…

用 vlc 可以运行,点击很多 button,会触发 check 逻辑一段一段播放视频,作为回显,逻辑应该是在 BDMV/MovieObject.bdmv 里面的(JAR 里面明明确实没找到东西…)

BDedit 可以打开这个 bdmv,里面有一定的运行逻辑,但是里面是缺少 check flag 的逻辑的,目前是还没找到 check 到底在哪,急急急…

image-20230918105138109

# 9-18, 看了别的师傅的 wp,出了,一步之遥…

导入 STREAM 中的 M2TS 到这里的 Menu 里面,可以看每个 button 的具体逻辑…

image-20230918160853438

这个 Call Object 不同的既是正确的 flag,猜一下可以得到这个图(偷来的图)

image-20230918161010536

SECCON{

26 11 10 25 38 4 7 12 28 38 10 11 13 28 38 32 24 21 11 38 16 23 13 17 38 31 16 15 2 38 15 25 27 27 38 27 23

34 33 39

a = '1234567890QWERTYUIOPASDFGHJKL{ZXCVBNM_-}'
b = [26,11,10,25,38,4,7,12,28,38,10,11,13,28,38,32,24,21,11,38,16,23,13,17,38,31,16,15,2,38,15,25,27,27,38,27,23,34,33,39]
flag = 'SECCON{'
for i in b:
    flag += a[i]
print(flag)
# SECCON{JWQH-58EL-QWRL-CGSW-UFRI-XUY3-YHKK-KFBV} 没交过,感觉可能错了一两位?但是确实不想debug了先就这样

# xuyao

一开始以为混淆的是 check 部分逻辑,因为确实有点点奇怪

结果混淆的是加密,将 SM4 的盒换成 AES 的 sbox 了(属于看 wp 看的,确实识别加密这块不熟悉 qaq)

回来动调硬看一下可以发现这里 Feistel 的轮数都不对

image-20230919154214378

这里旋转的位数改了:

image-20230919160104814

然后抄一个 r3 的 exp 吧,把表换一下,改天得再重新学学 sm4 算法了:

p
#include <stdio.h>
#include <string.h>
#include <time.h>
#define SM4_ENCRYPT 1
#define SM4_DECRYPT 0
typedef struct {
	int mode; /*!< encrypt/decrypt */
	unsigned long sk[32]; /*!< SM4 subkeys */
} sm4_context;
void sm4_setkey_enc(sm4_context *ctx, unsigned char key[16]);
void sm4_setkey_dec(sm4_context *ctx, unsigned char key[16]);
void sm4_crypt_ecb(sm4_context *ctx, int mode, int length, unsigned char *input,
	unsigned char *output);
void sm4_crypt_cbc(sm4_context *ctx, int mode, int length, unsigned char iv[16],
	unsigned char *input, unsigned char *output);
#ifndef GET_ULONG_BE
#define GET_ULONG_BE(n, b, i) \
{ \
(n) = ((unsigned long)(b)[(i)] << 24) | \
((unsigned long)(b)[(i) + 1] << 16) | \
((unsigned long)(b)[(i) + 2] << 8) | ((unsigned long)(b)[(i) + 3]); \
}
#endif
#ifndef PUT_ULONG_BE
#define PUT_ULONG_BE(n, b, i) \
{ \
(b)[(i)] = (unsigned char)((n) >> 24); \
(b)[(i) + 1] = (unsigned char)((n) >> 16); \
(b)[(i) + 2] = (unsigned char)((n) >> 8); \
(b)[(i) + 3] = (unsigned char)((n)); \
}
#endif
#define SHL(x, n) (((x)&0xFFFFFFFF) << n)
#define ROTL(x, n) (SHL((x), n) | ((x) >> (32 - n)))
#define SWAP(a, b) \
{ \
unsigned long t = a; \
a = b; \
b = t; \
t = 0; \
}
static const unsigned char SboxTable[16][16] = {
	0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,
	0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
	0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26,
	0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
	0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
	0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
	0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED,
	0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
	0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F,
	0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
	0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC,
	0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
	0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14,
	0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
	0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
	0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
	0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F,
	0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
	0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11,
	0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
	0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F,
	0xB0, 0x54, 0xBB, 0x16};
static const unsigned long FK[4] = {0xFF324600, 0x4F9A25B8, 0x3CC7477C,
	0x0C0B9ECD};
static const unsigned long CK[32] = {
	0xEC656287, 0xD9A22031, 0x01C7BCA8, 0xABE7033B, 0x313FE5DC, 0x940FFAD0,
	0x176EDEB8, 0x7C61B20E, 0x9EAD452F, 0x80E2C15B, 0xBA500D7B, 0xA2C0449F,
	0xBC0E774F, 0x3E393763, 0x43D46B3F, 0x2ADEF404, 0xCA884B87, 0x3C953C45,
	0x7CDBDE63, 0x6E995945, 0xB6CF3655, 0x8D60396A, 0x9A496B38, 0x9D87D81B,
	0x36FEDBC9, 0x79882953, 0x10611E15, 0x0030AB3E, 0x12503487, 0x187E21FF,
	0x6D85127E, 0xDF42C76C,
};
static unsigned char sm4Sbox(unsigned char inch) {
	unsigned char *pTable = (unsigned char *)SboxTable;
	unsigned char retVal = (unsigned char)(pTable[inch]);
	return retVal;
}
static unsigned long sm4Lt(unsigned long ka) {
	unsigned long bb = 0;
	unsigned long c = 0;
	unsigned char a[4];
	unsigned char b[4];
	PUT_ULONG_BE(ka, a, 0)
	b[0] = sm4Sbox(a[0]);
	b[1] = sm4Sbox(a[1]);
	b[2] = sm4Sbox(a[2]);
	b[3] = sm4Sbox(a[3]);
	GET_ULONG_BE(bb, b, 0)
	c = bb ^ (ROTL(bb, 3)) ^ (ROTL(bb, 14)) ^ (ROTL(bb, 15)) ^ (ROTL(bb, 9));
	return c;
}
static unsigned long sm4F(unsigned long x0, unsigned long x1, unsigned long x2,
	unsigned long x3, unsigned long rk) {
	return (x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk));
}
static unsigned long sm4CalciRK(unsigned long ka) {
	unsigned long bb = 0;
	unsigned long rk = 0;
	unsigned char a[4];
	unsigned char b[4];
	PUT_ULONG_BE(ka, a, 0)
	b[0] = sm4Sbox(a[0]);
	b[1] = sm4Sbox(a[1]);
	b[2] = sm4Sbox(a[2]);
	b[3] = sm4Sbox(a[3]);
	GET_ULONG_BE(bb, b, 0)
	rk = bb ^ (ROTL(bb, 11)) ^ (ROTL(bb, 25));
	return rk;
}
static void sm4_setkey(unsigned long SK[32], unsigned char key[16]) {
	unsigned long MK[4];
	unsigned long k[36];
	unsigned long i = 0;
	GET_ULONG_BE(MK[0], key, 0);
	GET_ULONG_BE(MK[1], key, 4);
	GET_ULONG_BE(MK[2], key, 8);
	GET_ULONG_BE(MK[3], key, 12);
	k[0] = MK[0] ^ FK[0];
	k[1] = MK[1] ^ FK[1];
	k[2] = MK[2] ^ FK[2];
	k[3] = MK[3] ^ FK[3];
	for (; i < 32; i++) {
		k[i + 4] = k[i] ^ (sm4CalciRK(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ CK[i]));
		SK[i] = k[i + 4];
	}
}
static void sm4_one_round(unsigned long sk[32], unsigned char input[16],
	unsigned char output[16]) {
	unsigned long i = 0;
	unsigned long ulbuf[36];
	memset(ulbuf, 0, sizeof(ulbuf));
	GET_ULONG_BE(ulbuf[0], input, 0)
	GET_ULONG_BE(ulbuf[1], input, 4)
	GET_ULONG_BE(ulbuf[2], input, 8)
	GET_ULONG_BE(ulbuf[3], input, 12)
	while (i < 32) {
		ulbuf[i + 4] =
		sm4F(ulbuf[i], ulbuf[i + 1], ulbuf[i + 2], ulbuf[i + 3], sk[i]);
		i++;
	}
	PUT_ULONG_BE(ulbuf[35], output, 0);
	PUT_ULONG_BE(ulbuf[34], output, 4);
	PUT_ULONG_BE(ulbuf[33], output, 8);
	PUT_ULONG_BE(ulbuf[32], output, 12);
}
void sm4_setkey_enc(sm4_context *ctx, unsigned char key[16]) {
	ctx->mode = SM4_ENCRYPT;
	sm4_setkey(ctx->sk, key);
}
void sm4_setkey_dec(sm4_context *ctx, unsigned char key[16]) {
	int i;
	ctx->mode = SM4_DECRYPT;
	sm4_setkey(ctx->sk, key);
	for (i = 0; i < 16; i++) {
		SWAP(ctx->sk[i], ctx->sk[31 - i]);
	}
}
void sm4_crypt_ecb(sm4_context *ctx, int mode, int length, unsigned char *input,
	unsigned char *output) {
	while (length > 0) {
		sm4_one_round(ctx->sk, input, output);
		input += 16;
		output += 16;
		length -= 16;
	}
}
void sm4_crypt_cbc(sm4_context *ctx, int mode, int length, unsigned char iv[16],
	unsigned char *input, unsigned char *output) {
	int i;
	unsigned char temp[16];
	if (mode == SM4_ENCRYPT) {
		while (length > 0) {
			for (i = 0; i < 16; i++)
				output[i] = (unsigned char)(input[i] ^ iv[i]);
			sm4_one_round(ctx->sk, output, output);
			memcpy(iv, output, 16);
			input += 16;
			output += 16;
			length -= 16;
		}
	} else /* SM4_DECRYPT */
	{
		while (length > 0) {
			memcpy(temp, input, 16);
			sm4_one_round(ctx->sk, input, output);
			for (i = 0; i < 16; i++)
				output[i] = (unsigned char)(output[i] ^ iv[i]);
			memcpy(iv, temp, 16);
			input += 16;
			output += 16;
			length -= 16;
		}
	}
}
int main() {
	unsigned char key[17] = "SECCON CTF 2023!";
	unsigned char input[112] = {
		0xFE, 0x60, 0xA8, 0xC0, 0x3B, 0xFE, 0xBC, 0x66, 0xFC, 0x9A, 0x9B, 0x31,
		0x9A, 0xD8, 0x03, 0xBB, 0xA9, 0xE1, 0x56, 0xFC, 0xFC, 0x11, 0x9F, 0x89,
		0x5F, 0x4D, 0x9F, 0xE0, 0x9F, 0xAE, 0x2A, 0xCF, 0x5E, 0x73, 0xCB, 0xEC,
		0x3F, 0xFF, 0xB9, 0xD1, 0x99, 0x44, 0x1B, 0x9A, 0x79, 0x79, 0xEC, 0xD1,
		0xB4, 0xFD, 0xEA, 0x2B, 0xE2, 0xF1, 0x1A, 0x70, 0x76, 0x3C, 0x2E, 0x7F,
		0x3F, 0x3B, 0x7B, 0x66, 0xA3, 0x4B, 0x1B, 0x5C, 0x0F, 0xBE, 0xDD, 0x98,
		0x5A, 0x5B, 0xD0, 0x0A, 0x3D, 0x7E, 0x2C, 0x10, 0x56, 0x2A, 0x10, 0x87,
		0x5D, 0xD9, 0xB9, 0x7F, 0x3E, 0x2E, 0x86, 0xB7, 0x17, 0x04, 0xDF, 0xB1,
		0x27, 0xC4, 0x47, 0xE2, 0xD9, 0x7A, 0x9A, 0x48, 0x7C, 0xDB, 0xC6, 0x1D,
		0x3C, 0x00, 0xA3, 0x21};
	unsigned char output[112];
	sm4_context ctx;
	unsigned long i;
	// sm4_setkey_enc(&ctx, key);
	// sm4_crypt_ecb(&ctx, 1, 64, input, output);
	// for (i = 0; i < 64; i++)
	// printf("%02x ", output[i]);
	// printf("\n");
	sm4_setkey_dec(&ctx, key);
	sm4_crypt_ecb(&ctx, 0, 112, input, output);
	for (i = 0; i < 112; i++)
		printf("%c", output[i]);
	printf("\n");
	return 0;
}

# optinimize

第一次见的 nim 编译产物,抽象是有点抽象的,尤其这个符号表(有点当年第一次见 rust 的美感)

主动态来看,发现它输出 flag 的几位后就卡住了,本来想着是不是要考模拟执行,然后发现应该就是手动把整个流程复现出来,让他跑就完了

调一下发现主要就是在 Q_main 里面的 P_main 卡住的

然后不会了,只得看 wp…555555555555…

这是模拟的代码,一样是跑不出来的(注意 dump 的时候都是 QWORD 格式,可以自己处理一下)

def P(i: int):
    num1 = 3
    num2 = 0
    num3 = 2
    num4 = 0
    if i == num4:
        return num1
    else:
        num5 = 1
        if i == num5:
            return num2
        else:
            num6 = 2
            if i == num6:
                return num3
            else:
                num7 = 2
                if num7 < i:
                    j = i
                    num8 = 2
                    while num8 < j:
                        v98 = num1 + num2
                        num1 = num2
                        num2 = num3
                        num3 = v98
                        j -= 1
                    return num3
def Q(n: int):
    i = num = 0
    while i < n:
        num += 1
        v63 = P(num) % num
        if v63 == 0:
            i += 1
    return num
ns = [0x000000000000004A, 0x0000000000000055, 0x000000000000006F, 0x0000000000000079, 0x0000000000000080, 0x0000000000000095, 0x00000000000000AE, 0x00000000000000BF, 0x00000000000000C7, 0x00000000000000D5, 0x0000000000000306, 0x0000000000001AC8, 0x00000000000024BA, 0x0000000000003D00, 0x0000000000004301, 0x0000000000005626, 0x0000000000006AD9, 0x0000000000007103, 0x000000000000901B, 0x0000000000009E03, 0x00000000001E5FB6, 0x000000000026F764, 0x000000000030BD9E, 0x0000000000407678, 0x00000000005B173B, 0x00000000006FE3B1, 0x000000000078EF25, 0x0000000000858E5F, 0x000000000098C639, 0x0000000000AD6AF6, 0x0000000001080096, 0x00000000018E08CD, 0x0000000001BB6107, 0x0000000001F50FF1, 0x00000000025C6327, 0x0000000002A971B6, 0x0000000002D68493, 0x000000000362F0C0, 0x0000000003788EAD, 0x0000000003CAA8ED]
cs = [0x3C,0xF4,0x1A,0xD0,0x8A,0x17,0x7C,0x4C,0xDF,0x21,0xDF,0xB0,0x12,0xB8,0x4E,0xFA,0xD9,0x2D,0x66,0xFA,0xD4,0x95,0xF0,0x66,0x6D,0xCE,0x69,0x00,0x7D,0x95,0xEA,0xD9,0x0A,0xEB,0x27,0x63,0x75,0x11,0x37,0xD4,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
for i in range(len(cs)):
    print(chr((Q(ns[i]) & 0xff) ^ cs[i]), end='')

唉然后是优化,oi 题来了… 没太看懂,贴个 wp 吧…(r3 大哥确实太高手了)

image-20230919171428466