哈希扩展长度攻击
哈希扩展长度攻击
0x00 起于一道CTF
这是一道好题!学了一种新的攻击方法,顺带知道了hash的实现。只不过现在这种攻击方法也只活在ctf中了
1 | flag = “XXXXXXXXXXXXXXXXXXXXXXX”; |
要显示出flag就得要通过这个逻辑:$COOKIE[“getmein”] === md5($secret . urldecode($username . $password)
问题在于不知道secret的值是多少,爆破几乎是不可能的,这里就要用到哈希扩展长度攻击
0x01什么是哈希扩展长度攻击
已知:salt的MD5值,但是不知道str1本身的值
我们可以知道salt+padding+任意字符 的md5值
因为md5的实现中64轮变换每次都是用的上次变换的结果,通过md5可以反向算出最后一次变换的四个值,之后加入到变换函数中,在后面添加任意数据。这样在不知道salt本身值的情况下也能算出salt+padding+任意数据的md5值了
我们需要知道md5的实现过程才能理解这种攻击方式
hash的实现
首先对消息进行分组,每组64个字节,不足64个字节的部分用padding补齐,其中,第一个补充的字节是0x80,之后的都是0x00,padding的最后8个字节用来表示需要哈希的消息长度
defpadding(self,n,sz=None):
ifszisNone:
sz=n
pre=64–8
sz=struct.pack(“Q”,sz*8)
pad=‘\x80‘
n+=1
ifn%64<=pre:
pad+=‘\x00‘*(pre–n%64)
pad+=sz
else:
pad+=‘\x00‘*(pre+64–n%64)
pad+=sz
returnpad
#最后八个字节为数据长度
defpad(self,msg):
returnmsg+self.padding(len(msg))
。
之后开始对每组消息进行压缩,经过64轮的数学变换(位运算的与或非异或、按位左移右移、按位无符号左移右移、进制转换、sin、和0xFF、0xFFFFFFF做与运算保证位数不变等等)每一次变换压缩的结果被当成下一轮变换压缩的初始值。最开始的初始值是由文档规定好的。最后的结果拼在一起就是128位的md5值
四个定义好的变换函数:
F(X,Y,Z) =(X&Y)|((~X)&Z)
G(X,Y,Z) =(X&Z)|(Y&(~Z))
H(X,Y,Z) =X^Y^Z
I(X,Y,Z)=Y^(X|(~Z))
(&是与,|是或,~是非,^是异或)
四个定义好的初始值:
A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。
变换函数
defXX(func,a,b,c,d,x,s,ac):
res=0L
res=res+a+func(b,c,d)
res=res+x
res=res+ac
res=res&0xffffffffL
res=_rotateLeft(res,s)
res=res&0xffffffffL
res=res+b
returnres&0xffffffffL
defmd5_compress(self,buf):
iflen(buf)!=64:
raiseValueError,“Invalidbufferoflength%d:%s”%(len(buf),repr(buf))
inp=struct.unpack(“I”*16,buf)
a,b,c,d=self.A,self.B,self.C,self.D
#开始进行64轮数学变换,四大轮,每轮16次
#Round1.
S11,S12,S13,S14=7,12,17,22
a=XX(F,a,b,c,d,inp[0],S11,0xD76AA478L)#1
d=XX(F,d,a,b,c,inp[1],S12,0xE8C7B756L)#2
c=XX(F,c,d,a,b,inp[2],S13,0x242070DBL)#3
b=XX(F,b,c,d,a,inp[3],S14,0xC1BDCEEEL)#4
a=XX(F,a,b,c,d,inp[4],S11,0xF57C0FAFL)#5
d=XX(F,d,a,b,c,inp[5],S12,0x4787C62AL)#6
c=XX(F,c,d,a,b,inp[6],S13,0xA8304613L)#7
b=XX(F,b,c,d,a,inp[7],S14,0xFD469501L)#8
a=XX(F,a,b,c,d,inp[8],S11,0x698098D8L)#9
d=XX(F,d,a,b,c,inp[9],S12,0x8B44F7AFL)#10
c=XX(F,c,d,a,b,inp[10],S13,0xFFFF5BB1L)#11
b=XX(F,b,c,d,a,inp[11],S14,0x895CD7BEL)#12
a=XX(F,a,b,c,d,inp[12],S11,0x6B901122L)#13
d=XX(F,d,a,b,c,inp[13],S12,0xFD987193L)#14
c=XX(F,c,d,a,b,inp[14],S13,0xA679438EL)#15
b=XX(F,b,c,d,a,inp[15],S14,0x49B40821L)#16
#Round2.
S21,S22,S23,S24=5,9,14,20
a=XX(G,a,b,c,d,inp[1],S21,0xF61E2562L)#17
d=XX(G,d,a,b,c,inp[6],S22,0xC040B340L)#18
c=XX(G,c,d,a,b,inp[11],S23,0x265E5A51L)#19
b=XX(G,b,c,d,a,inp[0],S24,0xE9B6C7AAL)#20
a=XX(G,a,b,c,d,inp[5],S21,0xD62F105DL)#21
d=XX(G,d,a,b,c,inp[10],S22,0x02441453L)#22
c=XX(G,c,d,a,b,inp[15],S23,0xD8A1E681L)#23
b=XX(G,b,c,d,a,inp[4],S24,0xE7D3FBC8L)#24
a=XX(G,a,b,c,d,inp[9],S21,0x21E1CDE6L)#25
d=XX(G,d,a,b,c,inp[14],S22,0xC33707D6L)#26
c=XX(G,c,d,a,b,inp[3],S23,0xF4D50D87L)#27
b=XX(G,b,c,d,a,inp[8],S24,0x455A14EDL)#28
a=XX(G,a,b,c,d,inp[13],S21,0xA9E3E905L)#29
d=XX(G,d,a,b,c,inp[2],S22,0xFCEFA3F8L)#30
c=XX(G,c,d,a,b,inp[7],S23,0x676F02D9L)#31
b=XX(G,b,c,d,a,inp[12],S24,0x8D2A4C8AL)#32
#Round3.
S31,S32,S33,S34=4,11,16,23
a=XX(H,a,b,c,d,inp[5],S31,0xFFFA3942L)#33
d=XX(H,d,a,b,c,inp[8],S32,0x8771F681L)#34
c=XX(H,c,d,a,b,inp[11],S33,0x6D9D6122L)#35
b=XX(H,b,c,d,a,inp[14],S34,0xFDE5380CL)#36
a=XX(H,a,b,c,d,inp[1],S31,0xA4BEEA44L)#37
d=XX(H,d,a,b,c,inp[4],S32,0x4BDECFA9L)#38
c=XX(H,c,d,a,b,inp[7],S33,0xF6BB4B60L)#39
b=XX(H,b,c,d,a,inp[10],S34,0xBEBFBC70L)#40
a=XX(H,a,b,c,d,inp[13],S31,0x289B7EC6L)#41
d=XX(H,d,a,b,c,inp[0],S32,0xEAA127FAL)#42
c=XX(H,c,d,a,b,inp[3],S33,0xD4EF3085L)#43
b=XX(H,b,c,d,a,inp[6],S34,0x04881D05L)#44
a=XX(H,a,b,c,d,inp[9],S31,0xD9D4D039L)#45
d=XX(H,d,a,b,c,inp[12],S32,0xE6DB99E5L)#46
c=XX(H,c,d,a,b,inp[15],S33,0x1FA27CF8L)#47
b=XX(H,b,c,d,a,inp[2],S34,0xC4AC5665L)#48
#Round4.
S41,S42,S43,S44=6,10,15,21
a=XX(I,a,b,c,d,inp[0],S41,0xF4292244L)#49
d=XX(I,d,a,b,c,inp[7],S42,0x432AFF97L)#50
c=XX(I,c,d,a,b,inp[14],S43,0xAB9423A7L)#51
b=XX(I,b,c,d,a,inp[5],S44,0xFC93A039L)#52
a=XX(I,a,b,c,d,inp[12],S41,0x655B59C3L)#53
d=XX(I,d,a,b,c,inp[3],S42,0x8F0CCC92L)#54
c=XX(I,c,d,a,b,inp[10],S43,0xFFEFF47DL)#55
b=XX(I,b,c,d,a,inp[1],S44,0x85845DD1L)#56
a=XX(I,a,b,c,d,inp[8],S41,0x6FA87E4FL)#57
d=XX(I,d,a,b,c,inp[15],S42,0xFE2CE6E0L)#58
c=XX(I,c,d,a,b,inp[6],S43,0xA3014314L)#59
b=XX(I,b,c,d,a,inp[13],S44,0x4E0811A1L)#60
a=XX(I,a,b,c,d,inp[4],S41,0xF7537E82L)#61
d=XX(I,d,a,b,c,inp[11],S42,0xBD3AF235L)#62
c=XX(I,c,d,a,b,inp[2],S43,0x2AD7D2BBL)#63
b=XX(I,b,c,d,a,inp[9],S44,0xEB86D391L)#64
self.A=(self.A+a)&0xffffffffL
self.B=(self.B+b)&0xffffffffL
self.C=(self.C+c)&0xffffffffL
self.D=(self.D+d)&0xffffffffL
0x02回到题目
这个时候我们通过脚本构造payload,padding的值得是64位
脚本如下
1 | # coding:utf-8 |
根据源代码,username必须是admin,这样的话password必须是admin+padding+附加值
这样的话post上去的就是
username=admin&password=admin%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%c8%00%00%00%00%00%00%00asd
在数据包里设置cookies:getmein=bf3b234ff0410ee4ec09b470e314d8c9
就成功绕过验证了
这里面直接给出的salt的MD5值其实是secret+adminadmin的MD5,所以salt的长度得是25,最后补位的时候要注意,实在不行自己手动构造padding。但是salt+padding+附加值的MD5没问题
0x03后记
中途也是踩了好多坑
首先是MD5的实现过程和漏洞原理,看了好一阵才明白,还好
之后就是脚本的实现,hashpumpy到最后都没装上,只能自己写。MD5的python实现是从网上找的,也花了不少时间改。
最后是payload的问题,忘了题目给的MD5是secret+adminadmin的md5,结果脚本跑的是secret本身,和它的不一致,有歧义。当时太晚了神志不太清醒还以为是脚本写错了,卡了好长时间,最后手动padding才知道不是自己的问题