首页
论坛
课程
招聘
[原创]2021 KCTF 秋季赛 第二题 迷失丛林
2021-11-17 11:53 6006

[原创]2021 KCTF 秋季赛 第二题 迷失丛林

2021-11-17 11:53
6006

基本流程

windows 程序通过查找 GetDlgItemTextA 引用找到关键入口
流程如下:

  1. 获取输入的 32 个字符,转换成 16字节 ipt(大写HEX,注意和输入的内容有 swap8 的差异)
  2. ipt 分半为 ipt1ipt2
  3. ipt1 拷贝进内置的 tb(256表)前 8 字节
  4. 第一个算法 验证 tb
  5. 通过 第二个算法 变换 tb
  6. 通过 第三个算法 运算 ipt2tb 得到的结果 对比 GoodJob~

切入点

通过 第二个算法 有对换的特点,和 原始tb 唯一数字的特点

 

可以看出 tb 应该要满足 256 数字唯一

解题流程

  1. 找到丢失的 8 个字节 loss
  2. 枚举所有 pad 可能,找到能够过 第一个算法 的排列 part1
  3. 随便输入 part1 + 16 字符,dump 第二个算法 之后的 tb
  4. 直接爆破 第三个算法 得到 part2

代码

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
199
200
201
202
203
204
205
206
207
208
package main
 
import (
    "fmt"
)
 
// tb 404000
var t2tb = []byte{
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x9B,
    0xF4, 0xDF, 0xAC, 0x7C, 0xA1, 0xC6, 0x16, 0xD0, 0x0F, 0xDD,
    0xDC, 0x73, 0xC5, 0x6B, 0xD1, 0x96, 0x47, 0xC2, 0x26, 0x67,
    0x4E, 0x41, 0x82, 0x20, 0x56, 0x9A, 0x6E, 0x33, 0x92, 0x88,
    0x29, 0xB5, 0xB4, 0x71, 0xA9, 0xCE, 0xC3, 0x34, 0x50, 0x59,
    0xBF, 0x2D, 0x57, 0x22, 0xA6, 0x30, 0x04, 0xB2, 0xCD, 0x36,
    0xD5, 0x68, 0x4D, 0x5B, 0x45, 0x9E, 0x85, 0xCF, 0x9D, 0xCC,
    0x61, 0x78, 0x32, 0x76, 0x31, 0xE3, 0x80, 0xAD, 0x39, 0x4F,
    0xFA, 0x72, 0x83, 0x4C, 0x86, 0x60, 0xB7, 0xD7, 0x63, 0x0C,
    0x44, 0x35, 0xB3, 0x7B, 0x19, 0xD4, 0x69, 0x08, 0x0B, 0x1F,
    0x3D, 0x11, 0x79, 0xD3, 0xEE, 0x93, 0x42, 0xDE, 0x23, 0x3B,
    0x5D, 0x8D, 0xA5, 0x77, 0x5F, 0x58, 0xDB, 0x97, 0xF6, 0x7A,
    0x18, 0x52, 0x15, 0x74, 0x25, 0x62, 0x2C, 0x05, 0xE8, 0x0D,
    0x98, 0x2A, 0x43, 0xE2, 0xEF, 0x48, 0x87, 0x49, 0x1C, 0xCA,
    0x2B, 0xA7, 0x8A, 0x09, 0x81, 0xE7, 0x53, 0xAA, 0xFF, 0x6F,
    0x8E, 0x91, 0xF1, 0xF0, 0xA4, 0x46, 0x3A, 0x7D, 0x54, 0xEB,
    0x2F, 0xC1, 0xC0, 0x0E, 0xBD, 0xE1, 0x6C, 0x64, 0xBE, 0xE4,
    0x02, 0x3C, 0x5A, 0xA8, 0x9F, 0x37, 0xAF, 0xA0, 0x13, 0xED,
    0x1B, 0xEC, 0x8B, 0x3E, 0x7E, 0x27, 0x99, 0x75, 0xAB, 0xFE,
    0xD9, 0x3F, 0xF3, 0xEA, 0x70, 0xF7, 0x95, 0xBA, 0x1D, 0x40,
    0xB0, 0xF9, 0xE5, 0xF8, 0x06, 0xBC, 0xB6, 0x03, 0xC9, 0x10,
    0x9C, 0x2E, 0x89, 0x5C, 0x7F, 0xB1, 0x1A, 0xD6, 0x90, 0xAE,
    0xDA, 0xE6, 0x5E, 0xB9, 0x84, 0xE9, 0x55, 0xBB, 0xC7, 0x0A,
    0xE0, 0x66, 0xF2, 0xD8, 0xCB, 0x00, 0x12, 0xB8, 0x17, 0x94,
    0x6A, 0x4A, 0x01, 0x24, 0x14, 0x51, 0x07, 0x65, 0x21, 0xC8,
    0x38, 0xFD, 0x8F, 0xC4, 0xF5, 0xFC,
}
 
func getloss() (ret []byte) {
    m := make(map[byte]bool)
    for i := 8; i < 256; i++ {
        m[t2tb[i]] = true
    }
 
    for i := 0; i < 256; i++ {
        if !m[byte(i)] {
            ret = append(ret, byte(i))
        }
    }
    return
}
 
// 排列算法
func permutations(arr []byte) [][]byte {
    var helper func([]byte, int)
    res := [][]byte{}
 
    helper = func(arr []byte, n int) {
        if n == 1 {
            tmp := make([]byte, len(arr))
            copy(tmp, arr)
            res = append(res, tmp)
        } else {
            for i := 0; i < n; i++ {
                helper(arr, n-1)
                if n%2 == 1 {
                    tmp := arr[i]
                    arr[i] = arr[n-1]
                    arr[n-1] = tmp
                } else {
                    tmp := arr[0]
                    arr[0] = arr[n-1]
                    arr[n-1] = tmp
                }
            }
        }
    }
    helper(arr, len(arr))
    return res
}
 
// 第一个校验算法
func checksk(c [256]byte) bool {
    var sss [256 * 2]byte
    var kkk [256][256]byte
    maxn := []int{2, 4, 8, 0x10, 0x20, 0x40, 0x80}
    for i := 0; i < 256; i++ {
        sss[0] = c[i]
        sss[1] = byte(i + 1)
        var ps int
        k := maxn[0]
        for _, n := range maxn {
            for j := 0; j < n; j++ {
                sss[k] = c[sss[ps]]
                sss[k+1] = sss[ps] + 1
                k += 2
                ps++
            }
        }
 
        for j := 0; j < 256; j++ {
            kkk[i][sss[ps]]++
            ps++
        }
    }
 
    var r1, r2, r3, r4 int
    for i := 0; i < 256; i++ {
        if kkk[i][0] != 0 {
            r1++
        }
        if kkk[i][40-26] != 0 {
            r2++
        }
        if kkk[i][40] != 0 {
            r3++
        }
        if kkk[i][40+39] != 0 {
            r4++
        }
    }
 
    return r1 == 169 && r2 == 172 && r3 == 167 && r4 > 200
}
 
// 要反一下打印
func printHex(p []byte) {
    for _, v := range p {
        c := ((v & 0xf) << 4) | ((v & 0xf0) >> 4)
        fmt.Printf("%X", c)
    }
    fmt.Println()
}
 
// 求解第一部分
func t2p1() {
    // 获取 tb 中缺失的 8 个数
    loss := getloss()
    // 排列所有可能 8! = 40320
    for _, pad := range permutations(loss) {
        var t [256]byte
        copy(t[:8], pad)
        copy(t[8:], t2tb[8:])
        // 求出满足第一个校验的 pad
        if checksk(t) {
            printHex(pad)
        }
    }
}
 
// 输入 前半部分 结果后
// 直接 dump 内存获取 tb变换 的结果
var t2tb2 = []byte{
    0xC1, 0x9B, 0x7F, 0x58, 0x64, 0xD5, 0x77, 0x21, 0x74, 0xEB, 0x14, 0xBF, 0xDF, 0x25, 0x5A, 0x37,
    0x85, 0x2C, 0xAF, 0x8C, 0xDA, 0x26, 0xE2, 0x7A, 0x87, 0x4C, 0x60, 0x99, 0x54, 0x3C, 0x95, 0xC0,
    0xB9, 0x0C, 0xBC, 0x0E, 0xE7, 0x2D, 0x86, 0xBE, 0x67, 0xD3, 0xD8, 0xFC, 0x30, 0xB6, 0xC8, 0x57,
    0x1E, 0x62, 0x3E, 0xCE, 0xA0, 0xCD, 0xF5, 0xEE, 0xA7, 0xCF, 0x45, 0xFE, 0xD0, 0x80, 0x05, 0xAD,
    0x13, 0xF3, 0xB7, 0x6B, 0x22, 0x2B, 0xBD, 0x69, 0x42, 0x4B, 0xA5, 0xEA, 0xA6, 0xD2, 0x6F, 0x4F,
    0x4E, 0x07, 0xE1, 0x36, 0x01, 0xB5, 0xAA, 0xB1, 0x94, 0x0B, 0x35, 0x3A, 0xC7, 0x49, 0x53, 0x82,
    0xC3, 0x7B, 0x32, 0xFF, 0x19, 0xC4, 0xF1, 0xC9, 0xE8, 0xF7, 0x56, 0x15, 0xA3, 0x46, 0x89, 0x43,
    0x9D, 0x8F, 0x20, 0xEF, 0xBB, 0x2A, 0xCB, 0x09, 0x93, 0x4A, 0x1C, 0xE3, 0x33, 0xD1, 0xE0, 0x1D,
    0x72, 0x7C, 0x27, 0xE9, 0x17, 0x28, 0x6D, 0x6A, 0xD9, 0x00, 0x9A, 0xE5, 0x63, 0xDE, 0x23, 0x9F,
    0x0D, 0x47, 0x3B, 0x65, 0x08, 0x84, 0x6C, 0x1A, 0x88, 0x12, 0xA1, 0xA4, 0xB3, 0x18, 0x24, 0x1B,
    0xD7, 0x44, 0xDB, 0xAC, 0x6E, 0x7D, 0x51, 0x5E, 0xED, 0x50, 0xD6, 0x11, 0x5B, 0x9C, 0xB4, 0x68,
    0x3D, 0x2F, 0x03, 0x40, 0xBA, 0x2E, 0xCA, 0x02, 0xE6, 0xA8, 0xEC, 0x83, 0x06, 0x5D, 0xB8, 0x4D,
    0x97, 0x66, 0xF0, 0xFB, 0x8A, 0x55, 0xAB, 0xB2, 0x04, 0xFA, 0x0A, 0x31, 0x71, 0xCC, 0x8B, 0x73,
    0xA9, 0x48, 0x5C, 0xF9, 0x98, 0xE4, 0xC6, 0x34, 0xC5, 0x7E, 0x81, 0x75, 0x90, 0x1F, 0x92, 0x3F,
    0x9E, 0x10, 0x29, 0x52, 0x39, 0xF4, 0x41, 0x78, 0x5F, 0x16, 0x79, 0xC2, 0xB0, 0xDD, 0xF2, 0x61,
    0x0F, 0x70, 0xD4, 0x91, 0xDC, 0xF6, 0xF8, 0xFD, 0x59, 0x38, 0x8D, 0x96, 0xAE, 0x8E, 0x76, 0xA2,
}
 
// 枚举 256 就能得出源 byte
func dec(cmp byte, idx int) byte {
    // 第三部分算法
    enc := func(x byte) byte {
        c := t2tb2[idx]
        for i := 0; i < 8; i++ {
            if x&1 != 0 {
                c = c + 1
            } else {
                c = t2tb2[c]
            }
            x = x >> 1
        }
        if idx == 0 || idx == 7 {
            c--
        }
        return c
    }
    for i := 0; i < 256; i++ {
        if enc(byte(i)) == cmp {
            return byte(i)
        }
    }
    return 0
}
 
// 求解第二部分
func t2p2() {
    var arr []byte
    for i, c := range []byte("GoodJob~") {
        arr = append(arr, dec(c, i))
    }
    printHex(arr)
}
 
func main() {
    t2p1()
    t2p2()
}

[注意] 欢迎加入看雪团队!base上海,招聘CTF安全工程师,将兴趣和工作融合在一起!看雪20年安全圈的口碑,助你快速成长!

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