首页
论坛
课程
招聘
[原创]KCTF2021秋 万事俱备 writeup
2021-12-7 21:11 15449

[原创]KCTF2021秋 万事俱备 writeup

2021-12-7 21:11
15449

e语言32位,ida拖进去看看,发现了zlib,随即运行起来看看进程,多了个python27.exe

 

把temp目录下面的 pub 整个目录copy出来, 发现可以正常验证通过,说明跟E语言没关系,就是一个python题

 

用官方版python27执行脚本check.py,发现报错,所以,python27.exe是个改版的。

 

python.dll拖进ida,提示pdb路径,知道python版本,2.7.18

 

下一个python源码,编译release,64位出来,跟python.dll对比,发现opcode改了

 

经过N......个小时,把opcode找对,下面的opcode.h,编译出来的python可以正常通过check.py的验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/* Instruction opcodes for compiled code */
 
 
#define SLICE        0
#define SLICE_1        1
#define SLICE_2        2
#define SLICE_3        3
#define STORE_SLICE    4
#define STORE_SLICE_1    5
#define STORE_SLICE_2    6
#define STORE_SLICE_3    7
#define DELETE_SLICE    8
#define DELETE_SLICE_1    9
#define DELETE_SLICE_2    10
#define DELETE_SLICE_3    11
#define DUP_TOP        13
#define BINARY_MODULO    14
#define PRINT_NEWLINE    15
#define ROT_FOUR    16
#define INPLACE_ADD    17
#define BINARY_AND    18
#define BINARY_RSHIFT    19
#define INPLACE_MULTIPLY    20
#define POP_BLOCK    22
#define DELETE_SUBSCR    23
#define PRINT_NEWLINE_TO 24
#define UNARY_INVERT    25
#define INPLACE_FLOOR_DIVIDE 26
#define IMPORT_STAR    28
#define INPLACE_OR    29
#define POP_TOP        30
#define END_FINALLY    31
#define UNARY_POSITIVE    32
#define INPLACE_POWER    33
#define INPLACE_XOR    37
#define PRINT_EXPR    39
#define BINARY_SUBSCR    40
#define INPLACE_LSHIFT    41
#define WITH_CLEANUP    42
#define INPLACE_AND    45
#define RETURN_VALUE    46
#define BINARY_TRUE_DIVIDE 48
#define BREAK_LOOP    50
#define EXEC_STMT    51
#define ROT_TWO        52
#define BINARY_SUBTRACT    53
#define BUILD_CLASS    54
#define ROT_THREE    56
#define UNARY_NOT    57
#define STOP_CODE    58
#define PRINT_ITEM    59
#define YIELD_VALUE    60
#define PRINT_ITEM_TO   62
#define INPLACE_TRUE_DIVIDE 63
#define STORE_MAP    64
#define NOP        65
#define BINARY_LSHIFT    66
#define INPLACE_MODULO    67
#define BINARY_MULTIPLY    69
#define GET_ITER    70
#define BINARY_DIVIDE    71
#define INPLACE_RSHIFT    72
#define STORE_SUBSCR    73
#define INPLACE_DIVIDE    74
#define BINARY_FLOOR_DIVIDE 76
#define BINARY_POWER    77
#define BINARY_ADD    81
#define LOAD_LOCALS    83
#define BINARY_OR    85
#define INPLACE_SUBTRACT    86
#define UNARY_CONVERT    87
#define BINARY_XOR    88
#define UNARY_NEGATIVE    89
#define CALL_FUNCTION    90
#define SETUP_LOOP    93
#define SETUP_EXCEPT    130
#define SETUP_FINALLY    111
#define LOAD_NAME    94
#define COMPARE_OP    95
#define STORE_GLOBAL 98
#define CALL_FUNCTION_VAR          99
#define CALL_FUNCTION_KW           100
#define CALL_FUNCTION_VAR_KW       101
#define STORE_ATTR    102
#define BUILD_SLICE  105
#define BUILD_TUPLE    106
#define UNPACK_SEQUENCE 107
#define FOR_ITER 108
#define LOAD_FAST 109
#define DUP_TOPX    110
#define STORE_NAME    112
#define CONTINUE_LOOP    113
#define DELETE_GLOBAL    114
#define JUMP_FORWARD    115   
#define BUILD_SET    116
#define JUMP_ABSOLUTE    118
#define STORE_FAST    119
#define STORE_DEREF     122
#define POP_JUMP_IF_FALSE     92
#define POP_JUMP_IF_TRUE     120
#define JUMP_IF_FALSE_OR_POP  128
#define JUMP_IF_TRUE_OR_POP 123
#define IMPORT_NAME    124
#define LOAD_DEREF      125
#define MAKE_FUNCTION    126
#define DELETE_NAME    127
#define LOAD_CONST    131
#define LOAD_CLOSURE    132
#define BUILD_LIST    133
#define RAISE_VARARGS    134
#define IMPORT_FROM    135
#define MAKE_CLOSURE    136
#define DELETE_ATTR    137
#define LOAD_GLOBAL    138
#define BUILD_MAP    139
#define LOAD_ATTR    140
#define LIST_APPEND    141
#define DELETE_FAST    142
#define SETUP_WITH 143
#define EXTENDED_ARG  145
#define SET_ADD         146
#define MAP_ADD         147
 
 
////////////////////////////////////////////////////
 
 
#define HAVE_ARGUMENT    90    /* Opcodes from here have an argument: */
 
enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE, PyCmp_GT=Py_GT, PyCmp_GE=Py_GE,
         PyCmp_IN, PyCmp_NOT_IN, PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD};
 
#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT)

用python反编译工具,替换opcode,运行直接崩溃
用python反汇编一看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[Disassembly]
     0       LOAD_CONST              0: 0x42DB1D32A8D2872F2A53EFD66A2L
     3       STORE_FAST              16: �������u���� ȩ�ੳ�G��Ǣ��L2$
     6       JUMP_ABSOLUTE           11
     9       <INVALID>              
     10      <INVALID>              
     11      NOP                    
     12      LOAD_FAST               16: �������u���� ȩ�ੳ�G��Ǣ��L2$
     15      LOAD_CONST              1: 0x3849DC465F5559FE912DC348448L
     18      COMPARE_OP              3 (!=)
     21      POP_JUMP_IF_FALSE       11110
     24      JUMP_ABSOLUTE           30
     27      <INVALID>              
     28      <INVALID>              
     29      <INVALID>              
     30      LOAD_FAST               16: �������u���� ȩ�ੳ�G��Ǣ��L2$
     33      LOAD_CONST              2: 0x28C3F2ACCDBCFA7333B7D99FA80L
     36      COMPARE_OP              3 (!=)
     39      POP_JUMP_IF_FALSE       8158
     42      JUMP_ABSOLUTE           46
     45      NOP                    
     46      LOAD_FAST               16: �������u���� ȩ�ੳ�G��Ǣ��L2$

全是垃圾指令,而且有INVALID,说明还有混淆,无奈放弃。

 

直接使用源码编译,单步调试

 

源码中有个lltrace,打开后,python崩溃。。
发现在输入完name/serial后打开,才能成功。
。。。。。。中间省略N小时。。。。。。。。。
最终,过滤掉垃圾代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (lltrace) {
    if (opcode == JUMP_ABSOLUTE || opcode == LOAD_FAST
        || opcode == LOAD_CONST
        || opcode == STORE_FAST
        || opcode == NOP
        || (opcode == COMPARE_OP && oparg == 3)
        || opcode == INPLACE_XOR
        ) {
        ;
        g_myDbgTrace = 0;
    }
    else {
        g_myDbgTrace = 1;
        if (HAS_ARG(opcode)) {
            printf("instr tracing: %d: opcode = %d[%s], oparg = %d\n",
                f->f_lasti, opcode, opcode_name[opcode], oparg);
        }
        else {
            printf("instr tracing: %d: opcode = %d[%s]\n",
                f->f_lasti, opcode, opcode_name[opcode]);
        }
    }
}

注意:COMPARE_OP ,oparg == 3的时候是垃圾,其他是正常的if语句

 

增加几个宏:

1
2
3
4
#define SHOW_TOP()           ((void)(lltrace && prtrace(TOP(), "top")), TOP())
#define SHOW_SECOND()           ((void)(lltrace && prtrace(SECOND(), "second")), SECOND())
#define SHOW_THIRD()           ((void)(lltrace && prtrace(THIRD(), "third")), THIRD())
#define SHOW_SET_TOP(v)        ((void)(lltrace && prtrace(v, "result")), SET_TOP(v))

把关键操作用show打印出调用前后堆栈信息,开始log,先通过COMPARE_OP,发现KCTF最终比较的是 'KCTF@021GoodLuck',这个字符串,16字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
push 'I\x97V\x92\x02\xdep\x83\x89\x19\xb1\x0e\xa0\x03P\x03'
instr tracing: 2734: opcode = 95[COMPARE_OP_A], oparg = 2
pop 'KCTF@021GoodLuck'
top 'I\x97V\x92\x02\xdep\x83\x89\x19\xb1\x0e\xa0\x03P\x03'
instr tracing: 2537: opcode = 92[POP_JUMP_IF_FALSE_A], oparg = 2351
pop False
instr tracing: 3515: opcode = 138[LOAD_GLOBAL_A], oparg = 0
push {577: 'helloctf_pediy_Archaia', 187: 'The serial number was entered incorrectly!', 421: 'decode', 72: 'append', 169: 'hashlib', 10: 'input serial:', 336: 'The serial number was entered incorrectly!', 232: 'The serial number was entered incorrectly!', 178: '@', 308: 'The serial number was entered incorrectly!', 757: 'input username:', 694: 'hex', 489: 'The serial number was entered incorrectly!', 116: 'append', 26: 'kctf2021GoodLuck', 507: 'md5', 220: '@', 446: 'upper', 447: 'Congratulations, serial number is correct  ^-^'}
instr tracing: 4071: opcode = 40[BINARY_SUBSCR]
pop 308
top {577: 'helloctf_pediy_Archaia', 187: 'The serial number was entered incorrectly!', 421: 'decode', 72: 'append', 169: 'hashlib', 10: 'input serial:', 336: 'The serial number was entered incorrectly!', 232: 'The serial number was entered incorrectly!', 178: '@', 308: 'The serial number was entered incorrectly!', 757: 'input username:', 694: 'hex', 489: 'The serial number was entered incorrectly!', 116: 'append', 26: 'kctf2021GoodLuck', 507: 'md5', 220: '@', 446: 'upper', 447: 'Congratulations, serial number is correct  ^-^'}
result 'The serial number was entered incorrectly!'
instr tracing: 4360: opcode = 59[PRINT_ITEM]
pop 'The serial number was entered incorrectly!'

然后看中间过程,又是漫长的N。。。。。小时
先md5字符串:helloctf_pediy_Archaia,结果
bfdc823fca7d85034d70f650df268108,全小写字符串
扩展5遍,得到256字节的数组
这个数组作为 rc4的key,rc4是变种的,rc4_setup_key不是标准的,rc4_crypt也不是标准的,等setup_key结束后,抓到rc4_table

1
2
3
4
pop None
ext_pop 'helloctf_pediy_Archaia'
ext_pop [98, 30, 106, 149, 9, 136, 33, 174, 26, 128, 32, 48, 116, 69, 244, 5, 122, 239, 79, 147, 197, 64, 16, 10, 53, 188, 135, 89, 120, 12, 73, 160, 34, 169, 96, 179, 54, 29, 191, 76, 215, 180, 70, 84, 181, 15, 211, 157, 59, 75, 218, 133, 132, 161, 242, 237, 99, 18, 199, 162, 83, 183, 102, 156, 137, 163, 61, 190, 110, 227, 3, 94, 142, 201, 158, 27, 184, 178, 39, 77, 74, 62, 115, 22, 126, 95, 234, 186, 38, 243, 103, 203, 107, 101, 219, 36, 41, 4, 196, 140, 165, 200, 93, 145, 45, 195, 141, 185, 249, 155, 8, 117, 113, 65, 224, 63, 170, 233, 60, 23, 131, 111, 35, 52, 80, 166, 212, 146, 204, 228, 100, 2, 238, 235, 88, 189, 58, 17, 114, 44, 47, 214, 171, 6, 209, 207, 121, 21, 182, 194, 91, 150, 192, 25, 139, 252, 7, 173, 86, 28, 104, 11, 223, 127, 125, 14, 251, 124, 40, 87, 231, 246, 50, 82, 20, 221, 130, 241, 172, 42, 97, 129, 205, 220, 144, 225, 49, 138, 57, 222, 250, 193, 206, 43, 13, 217, 164, 245, 148, 1, 202, 90, 109, 248, 253, 92, 187, 226, 19, 46, 67, 31, 105, 143, 153, 78, 123, 236, 56, 208, 66, 176, 118, 51, 175, 85, 255, 177, 254, 112, 213, 37, 210, 167, 72, 24, 232, 0, 168, 216, 230, 198, 134, 154, 240, 229, 152, 159, 151, 55, 81, 119, 108, 71, 247, 68]
ext_pop <function O00 at 0x0000015EAD5C2D78>

然后单步逆了crypt过程,处理了两遍,前一遍多异或一次161,后16轮多异或一次61,注意后一轮的i是从16开始的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void rc4_crypt_mod(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] ^ 161;        // ^ 161
    }
    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] ^ 61;        // ^ 61
    }
}

然后的算法,叫 “喵指令”,跟第三题 中娅之戒里面的算法很像,不过这是16位长度的,一共8个,有一个交换顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                   交换顺序
验证是这样映射       
                =============>
 
                dx[0] = di[3];
                dx[1] = di[4];
                dx[2] = di[1];
                dx[3] = di[2];
                dx[4] = di[7];
                dx[5] = di[0];
                dx[6] = di[5];
                dx[7] = di[6];
 
                <=============
keygen这样映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
喵指令:
// di,A,B,C,D,R,S,U,V是16bit
A=d0 ^ d1;
B=d2 + d3;
C=d4 - d5;
D=HW(d6 ^ d7);
 
S=(A & B) | ((~A) & C) ;
 
d6^=S;d7^=S;
d6<<<=D;d7<<<=D;
 
R=(uint16)(((uint32)A*S)>>D)+24;
d4+=R;d5+=R;
 
U=R^C;
d2+=U;d3-=U;
 
V=(R & S) | (S & U) | (U & R);
d0^=V;d1^=V;

最终keygen代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
void rc4_crypt_mod(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] ^ 161;        // ^ 161
    }
    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] ^ 61;        // ^ 61
    }
}
void miao() {
    unsigned char good[16] = { 0x4B, 0x43, 0x54, 0x46, 0x40, 0x30, 0x32, 0x31, 0x47, 0x6F, 0x6F, 0x64, 0x4C, 0x75, 0x63, 0x6B };
 
    uint16_t* di = (uint16_t*)good;
 
    uint16_t a, b, c, d, r, s, u, v;
 
    a = di[0] ^ di[1];
    b = di[2] + di[3];
    c = di[4] - di[5];
    d = __popcnt16(di[6] ^ di[7]);
 
    s = (a & b) | ((~a) & c);
    r = (uint16_t)(((uint32_t)a * s) >> d) + 24;
    u = r ^ c;
    v = (r & s) | (s & u) | (u & r);
 
    di[0] ^= v; di[1] ^= v;
    di[2] -= u; di[3] += u;
    di[4] -= r; di[5] -= r;
    di[6] = _rotr16(di[6], d); di[7] = _rotr16(di[7], d);
    di[6] ^= s; di[7] ^= s;
 
 
    // 下面是ok 的
    unsigned char ret[16] = { 0 };
 
    uint16_t* dx = (uint16_t*)ret;
 
    dx[0] = di[3];
    dx[1] = di[4];
    dx[2] = di[1];
    dx[3] = di[2];
    dx[4] = di[7];
    dx[5] = di[0];
    dx[6] = di[5];
    dx[7] = di[6];
 
    unsigned char ctx_good[256] = { 98, 30, 106, 149, 9, 136, 33, 174, 26, 128, 32, 48, 116, 69, 244, 5, 122, 239, 79, 147, 197, 64, 16, 10, 53, 188, 135, 89, 120, 12, 73, 160, 34, 169, 96, 179, 54, 29, 191, 76, 215, 180, 70, 84, 181, 15, 211, 157, 59, 75, 218, 133, 132, 161, 242, 237, 99, 18, 199, 162, 83, 183, 102, 156, 137, 163, 61, 190, 110, 227, 3, 94, 142, 201, 158, 27, 184, 178, 39, 77, 74, 62, 115, 22, 126, 95, 234, 186, 38, 243, 103, 203, 107, 101, 219, 36, 41, 4, 196, 140, 165, 200, 93, 145, 45, 195, 141, 185, 249, 155, 8, 117, 113, 65, 224, 63, 170, 233, 60, 23, 131, 111, 35, 52, 80, 166, 212, 146, 204, 228, 100, 2, 238, 235, 88, 189, 58, 17, 114, 44, 47, 214, 171, 6, 209, 207, 121, 21, 182, 194, 91, 150, 192, 25, 139, 252, 7, 173, 86, 28, 104, 11, 223, 127, 125, 14, 251, 124, 40, 87, 231, 246, 50, 82, 20, 221, 130, 241, 172, 42, 97, 129, 205, 220, 144, 225, 49, 138, 57, 222, 250, 193, 206, 43, 13, 217, 164, 245, 148, 1, 202, 90, 109, 248, 253, 92, 187, 226, 19, 46, 67, 31, 105, 143, 153, 78, 123, 236, 56, 208, 66, 176, 118, 51, 175, 85, 255, 177, 254, 112, 213, 37, 210, 167, 72, 24, 232, 0, 168, 216, 230, 198, 134, 154, 240, 229, 152, 159, 151, 55, 81, 119, 108, 71, 247, 68 };
 
    unsigned char ctx[256] = { 0 };
    memcpy(ctx, ctx_good, 256);
 
    rc4_crypt_mod(ctx, (unsigned char*)ret, 16);
    return;
}

ret里面打印成大写16进制即可

 

flag: AD0A1F8179ABE48ED3B073F840DA52A7


【公告】 [2022大礼包]《看雪论坛精华22期》发布!收录近1000余篇精华优秀文章!

最后于 2021-12-7 21:13 被海风月影编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回