创建你自己的MD5碰撞

步骤

首先获取HashClash的远程代码仓库

1
svn checkout https://svn.marc-stevens.nl/p/hashclash/trunk/ hashclash-read-only

安装编译所需依赖

此处可能不完全,还有问题可以找StackOverflow

1
2
sudo apt-get install libbz2-1.0 libbz2-dev libbz2-ocaml libbz2-ocaml-dev
sudo apt-get install build-essential

编译boost

sourceforge下载boost_1_57_0.tar.bz2放在hashclash-read-only/src目录下。

在src目录下

1
make boost

编译成功后会显示

1
2
3
4
5
6
7
8
9
The Boost C++ Libraries were successfully built!
The following directory should be added to compiler include paths:
/root/src/boost_1_57_0
The following directory should be added to linker library paths:
/root/src/boost_1_57_0/stage/lib

将boost的动态链接库加入到ld.so.conf文件中,然后运行ldconfig

1
2
3
4
5
6
7
8
9
root@aliyunserver:~/src/boost_1_57_0/stage/lib# pwd
/root/src/boost_1_57_0/stage/lib
root@aliyunserver:~/src/boost_1_57_0/stage/lib# echo "/root/src/boost_1_57_0/stage/lib" >> /etc/ld.so.conf
root@aliyunserver:~/src/boost_1_57_0/stage/lib# cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf
/root/src/boost_1_57_0/stage/lib
root@aliyunserver:~/src/boost_1_57_0/stage/lib# ldconfig

编译 HashClash

在src目录下

1
make

运行方法

在src/script目录下

1
(./cpc.sh fighter.jpg defcon.jpg &> demo.output &)

查看运行情况

1
tail -f demo.output

顺带一提,用 sublime text 查看也是可以的。

改良的cpc.sh

参考资料[1]中给出的改进版

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
#!/bin/bash
export BIRTHDAYSEARCH=../md5birthdaysearch
export HELPER=../md5diffpathhelper
export FORWARD=../md5diffpathforward
export BACKWARD=../md5diffpathbackward
export CONNECT=../md5diffpathconnect
export CPUS=8
export TTT=12
rm -r data 2>/dev/null
mkdir data || exit 1
file1=$1
file2=$2
if [ -f "$file1" -a -f "$file2" ]; then
echo "Chosen-prefix file 1: $file1"
echo "Chosen-prefix file 2: $file2"
else
echo "Usage $0 <file1> <file2> [<redonearcollstep>] [<nobirthday>]"
exit 1
fi
if [ "$3" = "" ] ; then
echo "birthday searching\r"
$BIRTHDAYSEARCH --inputfile1 "$file1" --inputfile2 "$file2" --hybridbits 0 --pathtyperange 2 --maxblocks 9 --maxmemory 100 --cuda_enabled --threads $CPUS
else
if [ "$4" != "" ]; then
cp $file1 file1.bin
cp $file2 file2.bin
fi
fi
function doforward {
echo "forward"
$FORWARD -w $1 -f $1/lowerpath.bin.gz --normalt01 -t 1 --trange $(($TTT-2)) || exit 1
}
function dobackward {
echo "backward"
$BACKWARD -w $1 -f $1/upperpath.bin.gz -t 34 --trange 4 -a 65536 -q 128 || exit 1
$BACKWARD -w $1 -t 29 --trange 8 || exit 1
$BACKWARD -w $1 -t 20 -a 16384 || exit 1
$BACKWARD -w $1 -t 19 --trange $((18-$TTT-4)) || exit 1
$BACKWARD -w $1 -t $(($TTT+4)) --maxweight 13 --minweight 10 || exit 1
}
function testcoll {
echo "testcoll"
for f in $1/coll1_*; do
if [ -e `echo $f | sed s/coll1/coll2/` ]; then return 0; fi
done
return 1
}
function doconnect {
echo "connect"
let c=0
CPIDS=""
while [ $c -lt $CPUS ]; do
mkdir $1/connect$c
$CONNECT -w $1/connect$c -t $TTT --inputfilelow $1/paths$(($TTT-1))_0of1.bin.gz --inputfilehigh $1/paths$(($TTT+4))_0of1.bin.gz -m $CPUS -i $c &
CPIDS="$CPIDS $!"
let c=c+1
done
let contime=0
while true; do
sleep 5
let contime=contime+$((5*$CPUS))
cstop=true;
for cp in $CPIDS; do
if ps -p $cp 2>/dev/null >/dev/null; then cstop=false; fi
done
if $cstop; then break; fi
if testcoll $1 ; then
sleep 5
kill $CPIDS
break;
fi
if [ $contime -gt 7200 ]; then
kill $CPIDS
break;
fi
done
}
function docollfind {
echo "collfind"
cf=""
for f in `ls -t $2/bestpath*.bin.gz`; do
$HELPER -w $1 --findcoll $f 2>&1 > $2/helper.log &
pid=$!
sleep 10
kill $pid
if [ `grep "^[.].*$" $2/helper.log | wc -l` -gt 0 ]; then cf=$f; break; fi
done
$HELPER -w $1 --findcoll $cf &
CPID=$!
while true; do
if testcoll $1 ; then break; fi
sleep 1
done
sleep 5
kill $CPID
}
cp file1.bin file1_0.bin
cp file2.bin file2_0.bin
let k=0$3
while true; do
rm -r workdir$k 2>/dev/null
mkdir workdir$k
$HELPER -w workdir$k --startnearcollision file1_$k.bin file2_$k.bin --pathtyperange 2 || exit
cp *.cfg workdir$k
if [ $CPUS -gt 1 ]; then
doforward workdir$k &
fpid=$!
dobackward workdir$k &
bpid=$!
while true; do
if ps -p $fpid 2>/dev/null >/dev/null; then continue; fi
if ps -p $bpid 2>/dev/null >/dev/null; then continue; fi
break;
done
else
doforward workdir$k
dobackward workdir$k
fi
doconnect workdir$k
for d in workdir$k/connect*; do
docollfind workdir$k $d &
done
while true; do
if testcoll workdir$k ; then break; fi
sleep 1
done
sleep 5
for f in workdir$k/coll1_*; do
if [ -e `echo $f | sed s/coll1/coll2/` ]; then
cat file1_$k.bin $f > file1_$(($k+1)).bin
cat file2_$k.bin `echo $f | sed s/coll1/coll2/` > file2_$(($k+1)).bin
cp file1_$(($k+1)).bin ${file1}.coll
cp file2_$(($k+1)).bin ${file2}.coll
break;
fi
done
let k=k+1
done

选择前缀攻击

如果你能找到一个碰撞,可以用两个不同的值然后给他们分别附加数据后可以计算出相同的值:

在原始的MD5碰撞攻击基础上,研究者计算两个消息M1和M2,有H(M1)= H(M2),其中函数H(x)是计算哈希值函数。Stevens扩展了该研究,并且找到了一种方法,使两个已知值P1和P2通过附加字节产生碰撞。

使用前缀P1和P2,他能够证明如何找到S1和S2,使得H(P1| S)= H(P2| S2)。

这就足以让攻击者创建两个具有相同的哈希值的证书。让我们来看看证书的大致结构:
序列号(Serial number)
有效期(Validity period)
域名(Domain name)
公钥(Public key)
扩展(X.509 extensions)
签名(Signature)

如果攻击者知道域名之前的每一个值,那么他们可以采取

P1 = Serial number | Validity period | real domain name

P2 = Serial number | Validity period | forged domain name

并应用该攻击以获得S1和S2中,用于公共密钥部分。而如果序列号和有效期可以预测,碰撞就可使用以下步骤中找到:

1.推测何时将签发的证书;
2.预测的序列号在该时间段内颁发的证书;
3.计算出相同的前缀数猜测覆盖预期的序列号和签发日期

攻击者首先对每一个取值预先进行选择前缀碰撞,直到碰撞出一个包含攻击者掌握的域名(如:attacker.com)与目标域名(如:google.com)相匹配的前缀。计算冲突比特位(collision bits)是因为我们伪造的域名跟原域名长度不一致从而导致前缀的末尾无法对齐,这是就会与公钥的初始几位比特值发现冲突。

计算出碰撞后,攻击者还得让CA在正确的时间颁发一个证书,该证书需带有已核查过域和公钥的正确的序列号的证书。如果幸运的话,这个CA返回的证书将与恶意证书具有相同的哈希,然后,他们可以将有效签名改成恶意证书,使其可信的。

碰撞域名是最简单的选择前缀碰撞攻击类型。另外,也可以将同样的碰撞方法应用于从所述可信证书与伪造证书的扩展的X.509的RSA公钥。这是更加危险的,因为它允许伪造证书设置“is CA”位。只要签名CA的路径长度约束足够大,这个伪造的CA将能够发放可信证书的任何域。

参考资料

[1] Create Your Own md5

[2] Hashclash

[3] HashClash

[4] 指定MD5值碰撞,指定MD5碰撞免杀,修改木马MD5值和系统文件一致

[5] 使用 MD5 碰撞算法伪装木马,躲过杀毒软件查杀,加入360白名单

[6] 如何碰撞两个功能不一样,但 MD5 值一样的程序的方法 + 源码公

[7] 添加共享链接库到系统搜索目录解决方法

[8] linux 后台运行

[9] 为什么伪造SHA-1证书比找SHA-1碰撞难?

[10] Python-rsa中的签名伪造

[11] MD5碰撞的演化之路

[12] Flame利用哈希碰撞攻击伪造微软CA证书