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:
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:
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 到底在哪,急急急…
# 9-18, 看了别的师傅的 wp,出了,一步之遥…
导入 STREAM 中的 M2TS 到这里的 Menu 里面,可以看每个 button 的具体逻辑…
这个 Call Object
不同的既是正确的 flag,猜一下可以得到这个图(偷来的图)
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
的轮数都不对
这里旋转的位数改了:
然后抄一个 r3 的 exp 吧,把表换一下,改天得再重新学学 sm4 算法了:
#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 大哥确实太高手了)