首页
社区
课程
招聘
[原创] KCTF 2023 第十一题 wp - 98k
2023-9-27 22:46 9526

[原创] KCTF 2023 第十一题 wp - 98k

2023-9-27 22:46
9526

逆向部分没什么好说的,有个 check 算法,要在 1,000,000,000 到 1<<32 之间找一个数 k ,满足 !check(k) && check(k - 1) , check 函数大致如下:

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
def check(value, try_count):
    buf1 = [0] * 10000
    buf2 = [0] * 10000
    buf3 = [0] * 20000
    buf4 = [0] * 20000
    for _ in range(try_count):
        print(_, end=' \r')
        v = [2, 4, 5, 6, 7, 8, 13]
        for i in range(10000):
            _v = v[rand() % len(v)]
            buf1[i] = _v
            if i == 0:
                buf2[i] = rand() % (_v - 1) + 1
            else:
                buf2[i] = rand() % _v
        for i in  range(20000):
            buf3[i] = v[rand() % len(v)]
        i12 = 0
        i34 = 0
        tmpv = 0
        while i12 < 10000 or tmpv > 0:
            if i34 >= 20000:
                # assert False, 1
                return True
            tmpv_bak = tmpv
            if i12 >= 10000:
                tmpv = tmpv // buf3[i34]
                buf4[i34] = tmpv_bak % buf3[i34]
                i34 += 1
            else:
                tmpv = buf1[i12] * tmpv + buf2[i12]
                if tmpv_bak // buf3[i34] * tmpv >= value:
                    tmpv = tmpv_bak // buf3[i34]
                    buf4[i34] = tmpv_bak % buf3[i34]
                    i34 += 1
                else:
                    i12 += 1
        i12 = 10000 - 1
        i34 -= 1
        tmpv = 0
        while i34 >= 0 or tmpv > 0:
            if i12 < 0:
                # assert False, 2
                return True
            tmpv_bak = tmpv
            if i34 < 0:
                tmpv = tmpv // buf1[i12]
                if tmpv_bak % buf1[i12] != buf2[i12]:
                    # assert False, 3
                    return True
                i12 -= 1
            else:
                tmpv = buf3[i34] * tmpv + buf4[i34]
                if tmpv_bak // buf1[i12] * tmpv >= value:
                    tmpv = tmpv_bak // buf1[i12]
                    if tmpv_bak % buf1[i12] != buf2[i12]:
                        # assert False, 4
                        return True
                    i12 -= 1
                else:
                    i34 -= 1
    return False

程序使用了多线程,总共的 try_count 为 10000 。

直接看这算法完全不懂,于是来找规律。先固定 buf1 和 buf3 数组的值,当输入的 value 比较小的时候 (range(1, 512)) 发现返回值非常稳定,也就是 try_count 为 1 都能直接得到稳定结果,于是使用小 value 调整 buf1 buf3 的取值找规律,部分输出如下:

1
2
3
4
5
6
$ python3 ./script.py
[(28, True), (51, False), (107, True), (153, False), 238, True)]
$ python3./script.py
[(54, True), (103, False), (211, True)]
$ python3 ./script.py
[(28, True), (51, False), (54, True), (103, False), (107, True), (153, False), (211, True)]

上面的三次运行结果,是取 value 为 range(1, 256) , buf3 全为 13 , buf1 分别为 ① 全 2 ② 全 4 ③ 随机 2 和 4 这三种情况下的输出,输出的格式是返回值在某个值处发生了变化,变为了 True/False ,如第一次的输出含义是在 value = 28 时返回值开始为 True , value = 51 时返回值开始为 False ,以此类推。这些输出就得到了指定条件下能返回 True/False 的 value 值的区间。并且这个结果是非常稳定的,只需要一次就能得到这个正确的结果。

这三个综合起来就可以看到,第三次返回 False 的 value 区间就是第一次返回 False 的 value 区间与第二次的交集。

如果将 value 的范围改为 range(1, 512) ,设置 buf1 全 2 , buf3 全 13 ,可以得到如下结果:

1
[(28, True), (51, False), (107, True), (153, False), 238, True), (307, False), (421, True)]

可以看到 True/False 的区间长度是二次递增的(相邻 True 区间或者相邻 False 区间的长度差是一个等差数列),总之就是我们可以通过前几个区间值递推到我们想要的区间的 True/False 分布。

所以我们只要找到某组 buf1 和 buf3 的固定取值下 value 在 range(1, 256) 里的 True/False 分布(必须是稳定的),我们就能找到这些取值下 value 在 1,000,000,000 到 1<<32 之间的 True/False 分布,再结合第一条规律,不同 buf1 和 buf3 的取值下得到的返回 False 的取值区间的交就一定包含目标区间,这样就能大大缩小需要暴破的空间。

后面测试发现, buf1 与 buf3 的固定取值存在倍数关系时得到的结果不稳定,所以排除这一部分数据。最后共有 18 组组合,将暴破区间从 3294967296 减少到 87551 。

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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#!/usr/bin/env python3
 
'''
seed = 0
def rand():
    global seed
    seed += 1
    return seed
'''
 
def rand(): # simulate rand (may be any random function)
    pass
 
def check(value, try_count): # check
    pass
 
def check_1(value, buf1_v, buf3_v):
    print(value, end=' \r')
    buf1 = [buf1_v] * 10000
    buf2 = [rand() % (buf1_v - 1) + 1]
    for i in range(1, 10000):
        buf2.append(rand() % buf1_v)
    buf3 = [buf3_v] * 20000
    buf4 = [0] * 20000
    i12 = 0
    i34 = 0
    tmpv = 0
    while i12 < 10000 or tmpv > 0:
        if i34 >= 20000:
            return True
        tmpv_bak = tmpv
        if i12 >= 10000:
            tmpv = tmpv // buf3[i34]
            buf4[i34] = tmpv_bak % buf3[i34]
            i34 += 1
        else:
            tmpv = buf1[i12] * tmpv + buf2[i12]
            if tmpv_bak // buf3[i34] * tmpv >= value:
                tmpv = tmpv_bak // buf3[i34]
                buf4[i34] = tmpv_bak % buf3[i34]
                i34 += 1
            else:
                i12 += 1
    i12 = 10000 - 1
    i34 -= 1
    tmpv = 0
    while i34 >= 0 or tmpv > 0:
        if i12 < 0:
            return True
        tmpv_bak = tmpv
        if i34 < 0:
            tmpv = tmpv // buf1[i12]
            if tmpv_bak % buf1[i12] != buf2[i12]:
                return True
            i12 -= 1
        else:
            tmpv = buf3[i34] * tmpv + buf4[i34]
            if tmpv_bak // buf1[i12] * tmpv >= value:
                tmpv = tmpv_bak // buf1[i12]
                if tmpv_bak % buf1[i12] != buf2[i12]:
                    return True
                i12 -= 1
            else:
                i34 -= 1
    return False
 
 
try_count = 150
 
'''
low = 1000000000
v_low = check(low, try_count)
print(low, v_low)
 
high = 0x100000000
v_high = check(high, try_count)
print(high, v_high)
 
while low < high:
    mid = (low + high) // 2
    v_mid = check(mid, try_count)
    print(mid, v_mid)
    if v_mid == v_low:
        low = mid + 1
        v_low = check(low, try_count)
        print(low, v_low)
    else:
        high = mid - 1
        v_high = check(high, try_count)
        print(high, v_high)
'''
 
def interval_and(l1, l2):
    l = []
    i1 = 0
    i2 = 0
    while i1 < len(l1) and i2 < len(l2):
        l1a, l1b = l1[i1]
        l2a, l2b = l2[i2]
        if l1a >= l2b:
            i2 += 1
            continue
        elif l2a >= l1b:
            i1 += 1
        elif l1b >= l2b:
            l.append((max(l1a, l2a), l2b))
            i2 += 1
            if l1b == l2b:
                i1 += 1
        else:
            l.append((max(l1a, l2a), l1b))
            i1 += 1
    return l
 
def get_f_intervals(t, f, tdiv, fdiv, pub_div):
    assert t < f
    t = t + tdiv
    tdiv += pub_div
    assert t > f
    while t < 1000000000:
        f += fdiv
        fdiv += pub_div
        t += tdiv
        tdiv += pub_div
    fl = []
    while f < 0x100000000:
        fl.append((f, t))
        f += fdiv
        fdiv += pub_div
        t += tdiv
        tdiv += pub_div
    return fl
 
def number_count(l):
    s = 0
    for a, b in l:
        s += b - a
    return s
 
'''
last_r = False
a = []
for i in range(1, 1024):
    r = check_1(i, 2, 13)
    if last_r != r:
        a.append((i, r))
        if len(a) >= 8: break
        last_r = r
        # print(a)
 
print(a)
print(a[2][0] - a[0][0], a[4][0] - a[2][0], a[6][0] - a[4][0])
 
x=a[0][0]
y=a[2][0]
z=a[4][0]
x1=a[1][0]
y1=a[3][0]
print(x,x1,y-x,y1-x1,(z-y)-(y-x))
'''
 
 
s='''
28 51 79 102 52
54 103 157 206 104
67 129 196 258 130
80 155 235 310 156
93 181 274 362 182
106 207 313 414 208
 
16 27 43 54 28
30 55 85 110 56
37 69 106 138 70
44 83 127 166 84
58 111 169 222 112
 
12 19 31 38 20
22 39 61 78 40
32 59 91 118 60
42 79 121 158 80
 
14 23 37 46 24
26 47 73 94 48
50 95 145 190 96
'''
 
fl = [(1000000000, 0x100000000)]
n = number_count(fl)
 
for i in s.strip().split('\n'):
    if not i.strip(): continue
    fl = interval_and(fl, get_f_intervals(*(int(j) for j in i.split(' '))))
    last_n = n
    n = number_count(fl)
    print('{} -> {} (-{}%)'.format(last_n, n, 100 * (last_n - n) / last_n))
 
for i in fl:
    print(i, i[1] - i[0])

筛选后得到的区间:

1
2
3
4
(1124320915, 1124327425) 6510
(1898762303, 1898787472) 25169
(2133213325, 2133231523) 18198
(2793543721, 2793581395) 37674

这个空间就可以开始暴了,暴的过程中发现第 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
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
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <unistd.h>
 
#define ullong unsigned long long
 
int check(ullong value, int try_count) {
    int buf1[10000];
    int buf2[10000];
    int buf3[20000];
    int buf4[20000];
    // printf("%d, %d, %d, %d\n", buf1[0], buf2[0], buf3[0], buf4[0]);
    unsigned int v[] = {2, 4, 5, 6, 7, 8, 13};
    for (int _ = 0; _ < try_count; _++) {
        for (int i = 0; i < 10000; i++) {
            int _v = v[rand() % 7];
            buf1[i] = _v;
            if (i) {
                buf2[i] = rand() % _v;
            } else {
                buf2[i] = rand() % (_v - 1) + 1;
            }
        }
 
        for (int i = 0; i < 20000; i++) {
            buf3[i] = v[rand() % 7];
        }
        int i12 = 0;
        int i34 = 0;
        ullong tmpv = 0;
        while (i12 < 10000 || tmpv > 0) {
            if (i34 >= 20000) {
                assert(0 || "1");
                return 1;
            }
            ullong tmpv_bak = tmpv;
            if (i12 >= 10000) {
                tmpv = tmpv / buf3[i34];
                buf4[i34] = tmpv_bak % buf3[i34];
                i34 += 1;
            } else {
                tmpv = buf1[i12] * tmpv + buf2[i12];
                if (tmpv_bak / buf3[i34] * tmpv >= value) {
                    tmpv = tmpv_bak / buf3[i34];
                    buf4[i34] = tmpv_bak % buf3[i34];
                    i34 += 1;
                } else {
                    i12 += 1;
                }
            }
        }
        i12 = 10000 - 1;
        i34 -= 1;
        tmpv = 0;
        while (i34 >= 0 || tmpv > 0) {
            if (i12 < 0) {
                assert(0 || "2");
                return 1;
            }
            ullong tmpv_bak = tmpv;
            if (i34 < 0) {
                tmpv = tmpv / buf1[i12];
                if (tmpv_bak % buf1[i12] != buf2[i12]) {
                    return 1;
                }
                i12 -= 1;
            } else {
                tmpv = buf3[i34] * tmpv + buf4[i34];
                if (tmpv_bak / buf1[i12] * tmpv >= value) {
                    tmpv = tmpv_bak / buf1[i12];
                    if (tmpv_bak % buf1[i12] != buf2[i12]) {
                        return 1;
                    }
                    i12 -= 1;
                } else {
                    i34 -= 1;
                }
            }
        }
    }
    return 0;
}
 
int main() {
    setbuf(stdout, NULL);
 
    int try_count = 100;
    srand(time(NULL));
 
    #define PROCESS_COUNT 16
    #define INIT_VALUE (1898762303l)
    #define FINI_VALUE (1898787472l)
    #define TASK_COUNT ((FINI_VALUE - INIT_VALUE) / PROCESS_COUNT)
 
    #define LOG_TOTAL (100)
    #define COUNT_TO_LOG (TASK_COUNT / LOG_TOTAL)
 
    for (int j = 0; j < PROCESS_COUNT; j++) {
        if (!fork()) { // child
            int logged_count = 0;
            int counting = COUNT_TO_LOG;
            printf("process %d started (0x%lx - 0x%lx).\n", j, INIT_VALUE + j * TASK_COUNT, INIT_VALUE + j * TASK_COUNT + TASK_COUNT);
            for (ullong i = INIT_VALUE + j * TASK_COUNT; i < INIT_VALUE + j * TASK_COUNT + TASK_COUNT; i++) {
                if (!check(i, try_count) && !check(i, 10000)) {
                    printf("found: %lld\n", i);
                } else {
                    // printf("\r%lld", i);
                }
                counting--;
                if (counting <= 0) {
                    counting = COUNT_TO_LOG;
                    logged_count += 1;
                    printf("process %d: %d / %d\n", j, logged_count, LOG_TOTAL);
                }
            }
            printf("process %d done.\n", j);
            exit(0);
        }
    }
 
    getchar();
    return 0;
}

跑了可能有半小时,输出里找了下很容易看到有一段连续好几百个值,这就是目标区间。当然里面还有一些误报的值, 2 万多个数误报了 20 多个,但是显然这些值是没法重复稳定通过的,只有这个区间里的值能稳定过 !check 。最后回到问题,满足 !check(k) && check(k - 1) 的值就是这个区间里的最小值 1898766093 。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

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