首页
社区
课程
招聘
[原创]KCTF2023 第八题AI核心地带
2023-9-19 08:43 8860

[原创]KCTF2023 第八题AI核心地带

2023-9-19 08:43
8860
  1. IDA加载,发现如下字符串
1
2
3
4
5
6
7
//称为flag[10],看做10int
00402100  66 6C 61 67 7B 42 7A 63 5A 44 6E 66 4E 49 71 6D  flag{BzcZDnfNIqm
00402110  51 43 74 6B 54 47 6C 77 4C 79 44 59 65 69 48 49  QCtkTGlwLyDYeiHI
00402120  6A 78 53 58 77 6B 52 4B 7A 70 46 50 76 7D 00 00  jxSXwkRKzpFPv}..
//称为check[5],取20字节,看做5int
00402130  43 61 6E 20 79 6F 75 20 63 72 61 63 6B 20 6D 65  Can you crack me
00402140  3F 5E 6F 6C 6F 5E 00 00                          ?^olo^.
  1. 输入最多1000位
1
2
3
4
5
if ( input_len <= 0 || (input_len_1 = input_len - 1, input[input_len - 1] != '\n') )
{
  v3 = 1;                                     // 错误
  v27 = 1;
}
  1. 输入只能是数字
1
2
3
4
5
input_num = input[v8];
if ( (unsigned int)(input_num - '0') <= 9 )// 只能输入数字
  break;
v3 = 1;    //这个数不能等于1
v27 = 1;    //这个数也不能等于1
  1. 然后后面的代码很乱,看不懂在干啥,一度不想做这道题了
  2. 分析关键代码
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
//根据下标和对应的数字,转换出一个0-9的数
v12 = (input_num + (0xFFFEC610 >> input_id % 31)) % 0xA;
//奇数位,v12==0;偶数位,v12!=0
if ( (input_id & 1) != v12 < 1 )   
    v27 = 1;
//将check数组中5int做xor,得到4字节=v28
v28 = v38 ^ HIDWORD(v37) ^ v37 ^ HIDWORD(v36) ^ v36;
if ( v25 )    //如果是input最后一位,就进行校验
{
//v28的 (((bit0 ^ bit2) & 0x1F) == 0)
//v28的 (((bit1 ^ bit3) & 0x1F) == 0)
  if ( ((unsigned __int8)v28 ^ (unsigned __int8)(BYTE2(v38) ^ BYTE6(v37) ^ BYTE2(v37) ^ BYTE6(v36) ^ BYTE2(v36))) & 0x1F// 也就是说这个条件,必须成立
        || (HIBYTE(v28) ^ BYTE1(v28)) & 0x1F )
  {
        v13 = -1;    //v13等于-1是成功状态
  }
  goto LABEL_27;
}
//根据v12取出flag中的4字节,分别跟check的5个数xor
v17 = v31[v12];
LODWORD(v36) = v17 ^ v36;               // 20字节,跟选定的数字xor
HIDWORD(v36) ^= v17;
LODWORD(v37) = v17 ^ v37;
HIDWORD(v37) ^= v17;
//xor后的数据要满足
if ( v12 >= 6 )
{
    if ( ((unsigned __int8)v28 ^ BYTE2(v28)) & 0x1F )
    {
LABEL_26:
        v13 = 9;    //将当前v12重置为9
        goto LABEL_27;
    }
    v15 = 13 - v12;
}
else
{
    v15 = 8 - v12;
    v16 = v28;                                // =0
}
if ( (v16 << v15) + -128 == v14 << v15 )    //需要成立
    goto LABEL_27;
  1. 然后整理下大概逻辑:
    check数组有初始状态
    flag数组是始终不变的
    根据input的每一位,计算出一个下标id,选取flag[id]
    将check数组5个数,分别跟flag[id] xor,最终会进行一个校验

  2. 因为xor具有偶数次消除的性质,所以先编写脚本,跑出最终结果需要flag中的[0] [2] [4] [8]

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
//标记10层,每层选取的元素是谁
unsigned int id08[10] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1 };
 
//判断数组是否合规 1=合规 0=错误
int judge08(unsigned int *flag, unsigned int *check)
{
    unsigned int final[5] = { 0 };
    for (int i = 0; i < 5; i++)
        final[i] = check[i];
 
    for (int i = 0; i < 10; i++) //flag用10
    {
        if (id08[i] == -1//不跟这个数参与xor
            continue;
 
        for (int j = 0; j < 5; j++//check中是54位数
        {
            final[j] ^= flag[i];
        }
    }
 
    final[0] = final[0] ^ final[1] ^ final[2] ^ final[3] ^ final[4];
    int bit0 = (final[0] & 0xFF000000) >> 24;
    int bit1 = (final[0] & 0xFF0000) >> 16;
    int bit2 = (final[0] & 0xFF00) >> 8;
    int bit3 = final[0] & 0xFF;
    printf("%x\n", final[0]);
 
    int e = bit0 ^ bit2;
    int f = bit1 ^ bit3;
    printf("%x %x\n", e, f);
 
    if (((e & 0x1F) == 0) && ((f & 0x1F) == 0))
    {
        printf("%x %x\n", (e & 0x1F), (f & 0x1F));
        return 1;
    }
    return 0;
}
 
//depth表示当前层级,一共遍历39
void search08(unsigned int *flag, unsigned int *check, int depth)
{
    if (depth == 10)
    {
        //printArr(id08, 10);
 
        if (judge08(flag, check))
        {
            printf("heihei\n");
            printArr(id08, 10);
            exit(0);
        }
        return;
    }
 
    for (int i = 0; i < 2; i++)
    {
        if (i == 0)
            id08[depth] = 1;
        else
            id08[depth] = -1;
        search08(flag, check, depth + 1);
    }
    return;
}
  1. 但是结果只选4位,分别对应0248,那样又不行,因为还有其他限制
    8.1 奇数位,v12必须等于0;偶数位,v12不能等于0
    8.2 若成立,flag下标就设定位为9
    ((unsigned __int8)v28 ^ BYTE2(v28)) & 0x1F
    8.3 若成立,就能跳过flag设定为9
    if ( (v16 << v15) + -128 == v14 << v15 )
    goto LABEL_27;
  2. 又因为 0248必须是奇数个数,4个奇数=偶数。135679都是偶数,所以input是偶数位。
    然后就编码跑数据
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
int judge08_2(unsigned int *flag, unsigned int *check, int index)
{
    for (int j = 0; j < 5; j++//check中是54位数
    {
        check[j] ^= flag[index];
    }
 
    int num = check[0] ^ check[1] ^ check[2] ^ check[3] ^ check[4];
    int bit0 = (num & 0xFF000000) >> 24;
    int bit1 = (num & 0xFF0000) >> 16;
    int bit2 = (num & 0xFF00) >> 8;
    int bit3 = num & 0xFF;
    //printf("%x\n", num);
 
    int e = bit0 ^ bit2;
    int f = bit1 ^ bit3;
    //printf("%x %x\n", e, f);
 
    if (((e & 0x1F) == 0) && ((f & 0x1F) == 0))
    {
        //printf("%x %x\n", (e & 0x1F), (f & 0x1F));
        return 1;
    }
    return 0;
}
 
void ctf08()
{
    unsigned int check[] = { 0x43616e20, 0x796f7520, 0x63726163, 0x6b206d65, 0x3f5e6f6c };
    unsigned int flag[] = {
        0x427a635a,
        0x446e664e,
        0x49716d51,
        0x43746b54,
        0x476c774c,
        0x79445965,
        0x6948496a,
        0x78535877,
        0x6b524b7a,
        0x70465076
    };
 
    for (int i = 0; i < 1000; i++)
    {
        int j = 0;
        for (; j < 10; j++//0-9遍历
        {
            int v12 = ((0xFFFEC610 >> i % 31) + (0x30 + j)) % 0xA;
            if ((i & 1) != v12 < 1//过滤掉不符合的数字
                continue;
 
            if (v12 == 0)   //i是奇数位
            {
                for (int k = 0; k < 5; k++//check中是54位数
                {
                    check[k] ^= flag[v12];  //其实也就是0
                }
                printf("id=%d num=%d v12=%d\n", i, j, v12);
                break;
            }
 
            int num = check[0] ^ check[1] ^ check[2] ^ check[3] ^ check[4];
 
            //75 73 1D 00=0x001d7375
            int al = (num & 0xFF00) >> 8; //73
            int dl = (num & 0xFF000000) >> 24;
 
            int v15;
            if (v12 >= 6)
            {
                int e = al ^ dl;
                if ((e & 0x1F) != 0)
                    continue;
                 
                v15 = 13 - v12;
                al = num & 0xFF;
                dl = (num & 0xFF0000) >> 16;
            }
            else
                v15 = 8 - v12;
 
            int a = (al << v15) & 0xff;
            int b = (dl << v15) & 0xff;
            if (a == ((128 + b) & 0xff))
            {
                printf("id=%d num=%d v12=%d\n", i, j, v12);
 
                if (judge08_2(flag, check, v12))
                {
                    printf("hahaha");
                    exit(0);
                }
 
                break;
            }
        }
             
        if (j == 10)    //没找到数字的话
        {
            for (int k = 0; k < 5; k++//check中是54位数
            {
                check[j] ^= flag[9];    //其实也就是0
            }
            printf("id=%d [9]\n", i);
        }
    }
    //search08(flag, check, 0);
}
  1. 跑出来的数据,再手动根据0248原则筛选,最后发现从486位截断不要9就行了。。。
    582606981190746395118531851185249089744027265368693769576937697816165851808443150195011501950410798490871663488927792799277958360668112074539521851185318514909974002766535869476937695769681626582180144305010501950115040079949037162348792789277927995826067811907483951185218511853490897410272653986937694769376988161658318084433501950105019504207984904716634829277927892779584606681100745395418511852185149009740027365358697769376947696816365821809443050125019501050400790490371673487927

[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回