题目描述

我们得到了一串神秘字符串:TASC?O3RJMV?WDJKX?ZM,问号部分是未知大写字母,为了确定这个神秘字符串,我们通过了其他途径获得了这个字串的32位MD5码。但是我们获得它的32位MD5码也是残缺不全,E903???4DAB????08?????51?80??8A?,请猜出神秘字符串的原本模样,并且提交这个字串的32位MD5码作为答案。 注意:得到的 flag 请包上 flag{} 提交

WriteUp

先穷举,共有26^3^种可能的字符串,然后进行MD5编码,将得到的MD5码和已知的残缺码比对,匹配成功就输出当前字符串及其MD5码。

具体步骤:

  1. 将模板字符串 TASC?O3RJMV?WDJKX?ZM 中的 ? 记录下来(共 3 个)。
  2. 枚举这 3 个位置的所有大写字母组合(263=17576263=17576 种)。
  3. 对每个候选字符串计算 MD5(128 bit → 32 位十六进制)。
  4. 用正则把已知的 MD5 片段 E903???4DAB????08?????51?80??8A? 匹配进去。
    匹配成功即得到答案。

使用GO编写穷举比对代码如下:

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
package main

import (
"crypto/md5"
"encoding/hex"
"fmt"
"regexp"
)

func main() {

//找出?的位置
str := []byte("TASC?O3RJMV?WDJKX?ZM")
var qIdx []int
for i, letter := range str {
if letter == '?' {
qIdx = append(qIdx, i)
}
}

if len(qIdx) != 3 {
panic("?不是3个")
}

//残缺的MD5编码转化为16进制,方便后边相匹配
md5Pattern := `(?i)^E903[0-9a-f]{3}4DAB[0-9a-f]{4}08[0-9a-f]{5}51[0-9a-f]{1}80[0-9a-f]{2}8A[0-9a-f]{1}$`
re := regexp.MustCompile(md5Pattern)

//26个字母穷举,替换?
allLetter := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
for _, letter1 := range allLetter {
str[qIdx[0]] = letter1
for _, letter2 := range allLetter {
str[qIdx[1]] = letter2
for _, letter3 := range allLetter {
str[qIdx[2]] = letter3

//穷举的结果转化为字符串,然后MD5,转化为string
cand := string(str)
sum := md5.Sum([]byte(cand))
hash := hex.EncodeToString(sum[:])

md5Upper := fmt.Sprintf("%X\n", sum)

if re.MatchString(hash) {
fmt.Println("MD5:", hash)
fmt.Println("大写:", md5Upper)
fmt.Println("原字符串:", cand)
return
}
}
}
}
fmt.Println("未匹配到符合条件的字符串")
}


/*输出
MD5: e9032994dabac08080091151380478a2
大写: E9032994DABAC08080091151380478A2

原字符串: TASCJO3RJMVKWDJKXLZM

进程 已完成,退出代码为 0*/

得到flag{E9032994DABAC08080091151380478A2}