用MD5实现hash长度扩展攻击 By Assassin
2017-04-28 21:48
246 查看
今天心血来潮,看到了扩展攻击,猛然想到了之前看的扩展攻击的原理还没有弄懂,这里补充一下,hash长度扩展攻击到底是怎么实现的。
然后观察代码我们首先发现一些规律:
1.首先$secret变量我们不知道,但是我们知道它的长度
2.从下面setcookie函数我们可以通过sample-hash这个变量拿到md5($secret+”admin”+”admin”)的md5值
3.我们登陆成功的条件,username==admin&&password!=admin 并且我们要cookie getmein的值等于md5($secret+username+password)
下面讲一下何为扩展攻击
1.MD5加密过程中512比特(64字节)为一组,属于分组加密,而且在运算的过程中,将512比特分为32bit*16块,分块运算
2.我们关键利用的是MD5的填充,对加密的字符串进行填充(比特第一位为1其余比特为0),使之(二进制)补到448模512同余,即长度为512的倍数减64,最后的64位在补充为原来字符串的长度,这样刚好补满512位的倍数,如果当前明文正好是512bit倍数则再加上一个512bit的一组。
3.MD5不管怎么加密,每一块加密得到的密文作为下一次加密的初始向量IV,这一点很关键!!!
有点绕,下面讲一个例子讲一下如何填充
比如计算字符串“admin”
十六进制0x61646d696e
这里与448模512不同余,补位后的数据如下
注意!下图中8是怎么来的!比特第一位补位1,其余为0,那么admin后面(二进制补位1000…000)
注意!下图中框框中是啥!是全部要加密明文的长度!这里长度不包含填充的长度!而且注意是比特数!而且我们需要注意一点
MD5中存储的都是小端方式!
MD5中存储的都是小端方式!
MD5中存储的都是小端方式!
重要的事情说三遍,举个例子:假如我们这一块值为0x12345678
那么在MD5运算时候存储的顺序是 0x78563412
这也是之所以后8字节为长度,而第1字节先有数据的原因
admin长度为40比特,所以长度为0x28=40bits
在讲如何利用填充扩展攻击?首先直观看一下上面讲的第三点
假如我们知道了,md5(“secret”)我们不需要知道secret是什么,只要知道长度,人为将secret填充完,在新加一块假设为add,之前得到的MD5值作为最后一块加密的初始向量IV,最后加密得到的结果和MD5(secret+add)结果是一样的!重点看下面的题目分析吧!
我们通过sample-hash得到$secret+”admin”+”admin”的加密值
,假如我们构造第一块如
这个时候是xxxxxxxxxxxxxxxadminadmin加密填充后的内容,假设得到的MD5值为m,假设$a=xxxxxxxxxxxxxxxadminadmin,我们构造$b=admn
n让m作为加密$b时候的IV初始向量,那么就能得到的MD5($a+$b)的!!
是注意一点,刚刚说的小端存储,所以IV值设置的时候需要注意!
比如我们得到key值为e2c25a7f 7fd42f0f 03194d72 58fbcb6
那么我们构造的初始IV应为
A=0x7f5ac2e2
B=0x0f2fd47f
C=0x724d1903
D=0xb6c8fb58
利用脚本可以得到我们需要的cookie值
而且我们可以发现我们构造IV得出的结果和直接MD5加密的结果是一样的!这时候我们只需要将username和password转换成url格式的提交、同时构造getmein的cookie变量提交即可!
讲的不是很清楚,如果还有不太明白的,请参考如下博文
MD5长度扩展攻击
不愧是鸡哥,在这里再次补充一个神乎其神得工具hash_extender
https://github.com/iagox86/hash_extender
利用方法也相当得方便
利用代码如下
我们就只要修改find_hash得值就可以了!
题目
首先从实验吧上面见到的经典的题目,下面给出源码<?php $flag = "flag{flag is here}"; $secret = "aaaaabbbbbccccc"; // This secret is 15 characters long for security! @$username = $_POST["username"]; @$password = $_POST["password"]; if (!empty($_COOKIE["getmein"])) { if (urldecode($username) === "admin" && urldecode($password) != "admin") { if ($_COOKIE["getmein"] === md5($secret . urldecode($username . $password))) { echo "Congratulations! You are a registered user.\n"; die ("The flag is ". $flag); } else { die ("Your cookies don't match up! STOP HACKING THIS SITE."); } } else { die ("You are not an admin! LEAVE."); } } setcookie("sample-hash", md5($secret . urldecode("admin" . "admin")), time() + (60 * 60 * 24 * 7)); if (empty($_COOKIE["source"])) { setcookie("source", 0, time() + (60 * 60 * 24 * 7)); } else { if ($_COOKIE["source"] != 0) { echo ""; // This source code is outputted here } } ?>
然后观察代码我们首先发现一些规律:
1.首先$secret变量我们不知道,但是我们知道它的长度
2.从下面setcookie函数我们可以通过sample-hash这个变量拿到md5($secret+”admin”+”admin”)的md5值
3.我们登陆成功的条件,username==admin&&password!=admin 并且我们要cookie getmein的值等于md5($secret+username+password)
下面讲一下何为扩展攻击
MD5扩展攻击介绍
要说MD5的扩展攻击,首先要了解一定MD5加密算法的过程,其中我们不需要了解太多,我们需要知道如下几点1.MD5加密过程中512比特(64字节)为一组,属于分组加密,而且在运算的过程中,将512比特分为32bit*16块,分块运算
2.我们关键利用的是MD5的填充,对加密的字符串进行填充(比特第一位为1其余比特为0),使之(二进制)补到448模512同余,即长度为512的倍数减64,最后的64位在补充为原来字符串的长度,这样刚好补满512位的倍数,如果当前明文正好是512bit倍数则再加上一个512bit的一组。
3.MD5不管怎么加密,每一块加密得到的密文作为下一次加密的初始向量IV,这一点很关键!!!
有点绕,下面讲一个例子讲一下如何填充
比如计算字符串“admin”
十六进制0x61646d696e
这里与448模512不同余,补位后的数据如下
0x61646d696e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002800000000000000
注意!下图中8是怎么来的!比特第一位补位1,其余为0,那么admin后面(二进制补位1000…000)
注意!下图中框框中是啥!是全部要加密明文的长度!这里长度不包含填充的长度!而且注意是比特数!而且我们需要注意一点
MD5中存储的都是小端方式!
MD5中存储的都是小端方式!
MD5中存储的都是小端方式!
重要的事情说三遍,举个例子:假如我们这一块值为0x12345678
那么在MD5运算时候存储的顺序是 0x78563412
这也是之所以后8字节为长度,而第1字节先有数据的原因
admin长度为40比特,所以长度为0x28=40bits
在讲如何利用填充扩展攻击?首先直观看一下上面讲的第三点
假如我们知道了,md5(“secret”)我们不需要知道secret是什么,只要知道长度,人为将secret填充完,在新加一块假设为add,之前得到的MD5值作为最后一块加密的初始向量IV,最后加密得到的结果和MD5(secret+add)结果是一样的!重点看下面的题目分析吧!
MD5扩展攻击实现
那么再分析从题目中得到的条件,虽然我们不知道$secret是什么,但是我们是否已经知道md5($secret+”admin”+”admin”)的值了?(将其MD5值假设为key)这个时候假设我们主动将$secret+”admin”+”admin”+第一组MD5填充 作为第一组明文,第二组明文自己设定的某值,同时我们利用已经知道的key作为我们构造的明文最后一块加密的初始向量IV,那么加密出来的结果应该和$secret+”admin”+”admin”+第一组MD5填充+第二块铭文的MD5值一样吧。我们通过sample-hash得到$secret+”admin”+”admin”的加密值
,假如我们构造第一块如
xxxxxxxxxxxxxxxadminadmin+'\x80'+'\x00'*30+'\xc8'+'\x00'*7
这个时候是xxxxxxxxxxxxxxxadminadmin加密填充后的内容,假设得到的MD5值为m,假设$a=xxxxxxxxxxxxxxxadminadmin,我们构造$b=admn
n让m作为加密$b时候的IV初始向量,那么就能得到的MD5($a+$b)的!!
是注意一点,刚刚说的小端存储,所以IV值设置的时候需要注意!
比如我们得到key值为e2c25a7f 7fd42f0f 03194d72 58fbcb6
那么我们构造的初始IV应为
A=0x7f5ac2e2
B=0x0f2fd47f
C=0x724d1903
D=0xb6c8fb58
利用脚本可以得到我们需要的cookie值
MD5加密一块函数的实现,可以构造IV向量 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author:DshtAnger # theory reference: # blog: # http://blog.csdn.net/adidala/article/details/28677393 # http://blog.csdn.net/forgotaboutgirl/article/details/7258109 # http://blog.sina.com.cn/s/blog_6fe0eb1901014cpl.html # RFC1321: # https://www.rfc-editor.org/rfc/pdfrfc/rfc1321.txt.pdf ############################################################################## import sys def genMsgLengthDescriptor(msg_bitsLenth): ''' ---args: msg_bitsLenth : the bits length of raw message --return: 16 hex-encoded string , i.e.64bits,8bytes which used to describe the bits length of raw message added after padding ''' return __import__("struct").pack(">Q",msg_bitsLenth).encode("hex") def reverse_hex_8bytes(hex_str): ''' --args: hex_str: a hex-encoded string with length 16 , i.e.8bytes --return: transform raw message descriptor to little-endian ''' hex_str = "%016x"%int(hex_str,16) assert len(hex_str)==16 return __import__("struct").pack("<Q",int(hex_str,16)).encode("hex") def reverse_hex_4bytes(hex_str): ''' --args: hex_str: a hex-encoded string with length 8 , i.e.4bytes --return: transform 4 bytes message block to little-endian ''' hex_str = "%08x"%int(hex_str,16) assert len(hex_str)==8 return __import__("struct").pack("<L",int(hex_str,16)).encode("hex") def deal_rawInputMsg(input_msg): ''' --args: input_msg : inputed a ascii-encoded string --return: a hex-encoded string which can be inputed to mathematical transformation function. ''' ascii_list = [x.encode("hex") for x in input_msg] length_msg_bytes = len(ascii_list) length_msg_bits = len(ascii_list)*8 #padding ascii_list.append('80') while (len(ascii_list)*8+64)%512 != 0: ascii_list.append('00') #add Descriptor ascii_list.append(reverse_hex_8bytes(genMsgLengthDescriptor(length_msg_bits))) return "".join(ascii_list) def getM16(hex_str,operatingBlockNum): ''' --args: hex_str : a hex-encoded string with length in integral multiple of 512bits operatingBlockNum : message block number which is being operated , greater than 1 --return: M : result of splited 64bytes into 4*16 message blocks with little-endian ''' M = [int(reverse_hex_4bytes(hex_str[i:(i+8)]),16) for i in xrange(128*(operatingBlockNum-1),128*operatingBlockNum,8)] return M #定义函数,用来产生常数T[i],常数有可能超过32位,同样需要&0xffffffff操作。注意返回的是十进制的数 def T(i): result = (int(4294967296*abs(__import__("math").sin(i))))&0xffffffff return result #定义每轮中用到的函数 #RL为循环左移,注意左移之后可能会超过32位,所以要和0xffffffff做与运算,确保结果为32位 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 = L = 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 show_md5(A,B,C,D): return "".join( [ "".join(__import__("re").findall(r"..","%08x"%i)[::-1]) for i in (A,B,C,D) ] ) def run_md5(A=0x67452301,B=0xefcdab89,C=0x98badcfe,D=0x10325476,readyMsg=""): a = A b = B c = C d = D for i in xrange(0,len(readyMsg)/128): M = getM16(readyMsg,i+1) for i in xrange(16): exec "M"+str(i)+"=M["+str(i)+"]" #First round a=FF(a,b,c,d,M0,7,0xd76aa478L) d=FF(d,a,b,c,M1,12,0xe8c7b756L) c=FF(c,d,a,b,M2,17,0x242070dbL) b=FF(b,c,d,a,M3,22,0xc1bdceeeL) a=FF(a,b,c,d,M4,7,0xf57c0fafL) d=FF(d,a,b,c,M5,12,0x4787c62aL) c=FF(c,d,a,b,M6,17,0xa8304613L) b=FF(b,c,d,a,M7,22,0xfd469501L) a=FF(a,b,c,d,M8,7,0x698098d8L) d=FF(d,a,b,c,M9,12,0x8b44f7afL) c=FF(c,d,a,b,M10,17,0xffff5bb1L) b=FF(b,c,d,a,M11,22,0x895cd7beL) a=FF(a,b,c,d,M12,7,0x6b901122L) d=FF(d,a,b,c,M13,12,0xfd987193L) c=FF(c,d,a,b,M14,17,0xa679438eL) b=FF(b,c,d,a,M15,22,0x49b40821L) #Second round a=GG(a,b,c,d,M1,5,0xf61e2562L) d=GG(d,a,b,c,M6,9,0xc040b340L) c=GG(c,d,a,b,M11,14,0x265e5a51L) b=GG(b,c,d,a,M0,20,0xe9b6c7aaL) a=GG(a,b,c,d,M5,5,0xd62f105dL) d=GG(d,a,b,c,M10,9,0x02441453L) c=GG(c,d,a,b,M15,14,0xd8a1e681L) b=GG(b,c,d,a,M4,20,0xe7d3fbc8L) a=GG(a,b,c,d,M9,5,0x21e1cde6L) d=GG(d,a,b,c,M14,9,0xc33707d6L) c=GG(c,d,a,b,M3,14,0xf4d50d87L) b=GG(b,c,d,a,M8,20,0x455a14edL) a=GG(a,b,c,d,M13,5,0xa9e3e905L) d=GG(d,a,b,c,M2,9,0xfcefa3f8L) c=GG(c,d,a,b,M7,14,0x676f02d9L) b=GG(b,c,d,a,M12,20,0x8d2a4c8aL) #Third round a=HH(a,b,c,d,M5,4,0xfffa3942L) d=HH(d,a,b,c,M8,11,0x8771f681L) c=HH(c,d,a,b,M11,16,0x6d9d6122L) b=HH(b,c,d,a,M14,23,0xfde5380c) a=HH(a,b,c,d,M1,4,0xa4beea44L) d=HH(d,a,b,c,M4,11,0x4bdecfa9L) c=HH(c,d,a,b,M7,16,0xf6bb4b60L) b=HH(b,c,d,a,M10,23,0xbebfbc70L) a=HH(a,b,c,d,M13,4,0x289b7ec6L) d=HH(d,a,b,c,M0,11,0xeaa127faL) c=HH(c,d,a,b,M3,16,0xd4ef3085L) b=HH(b,c,d,a,M6,23,0x04881d05L) a=HH(a,b,c,d,M9,4,0xd9d4d039L) d=HH(d,a,b,c,M12,11,0xe6db99e5L) c=HH(c,d,a,b,M15,16,0x1fa27cf8L) b=HH(b,c,d,a,M2,23,0xc4ac5665L) #Fourth round a=II(a,b,c,d,M0,6,0xf4292244L) d=II(d,a,b,c,M7,10,0x432aff97L) c=II(c,d,a,b,M14,15,0xab9423a7L) b=II(b,c,d,a,M5,21,0xfc93a039L) a=II(a,b,c,d,M12,6,0x655b59c3L) d=II(d,a,b,c,M3,10,0x8f0ccc92L) c=II(c,d,a,b,M10,15,0xffeff47dL) b=II(b,c,d,a,M1,21,0x85845dd1L) a=II(a,b,c,d,M8,6,0x6fa87e4fL) d=II(d,a,b,c,M15,10,0xfe2ce6e0L) c=II(c,d,a,b,M6,15,0xa3014314L) b=II(b,c,d,a,M13,21,0x4e0811a1L) a=II(a,b,c,d,M4,6,0xf7537e82L) d=II(d,a,b,c,M11,10,0xbd3af235L) c=II(c,d,a,b,M2,15,0x2ad7d2bbL) b=II(b,c,d,a,M9,21,0xeb86d391L) 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 show_md5(a,b,c,d)
利用代码 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author:DshtAnger import my_md5 import hashlib import urllib #reference: # http://www.freebuf.com/articles/web/69264.html #problem link: # http://ctf4.shiyanbar.com/web/kzhan.php samplehash="e2c25a7f 7fd42f0f 03194d72 58fbcdb6" #将哈希值分为四段,并反转该四字节为小端序,作为64第二次循环的输入幻书 s1=0x7f5ac2e2 s2=0x0f2fd47f s3=0x724d1903 s4=0xb6cdfb58 #exp secret = "a"*15 secret_admin="aaaaabbbbbcccccadminadmin"+'\x80'+'\x00'*30+'\xc8'+'\x00'*7+"admin" r = my_md5.deal_rawInputMsg(secret_admin) inp = r[len(r)/2:] #我们需要截断的地方,也是我们需要控制的地方 #print r #print inp print "getmein:"+my_md5.run_md5(s1,s2,s3,s4,inp) print "getmein:"+hashlib.md5(secret_admin).hexdigest()
而且我们可以发现我们构造IV得出的结果和直接MD5加密的结果是一样的!这时候我们只需要将username和password转换成url格式的提交、同时构造getmein的cookie变量提交即可!
讲的不是很清楚,如果还有不太明白的,请参考如下博文
MD5长度扩展攻击
不愧是鸡哥,在这里再次补充一个神乎其神得工具hash_extender
https://github.com/iagox86/hash_extender
利用方法也相当得方便
-d 被扩展的明文 -a 附加的到原来hash的padding -l 盐的长度 -f 加密方式 -s 带盐加密的hash值 --out-data-format 输出格式 --quiet 仅输出必要的值
利用代码如下
# -*- coding:utf-8 -*- from urlparse import urlparse from httplib import HTTPConnection from urllib import urlencode import json import time import os import urllib def gao(x, y): #print x #print y url = "http://120.26.131.152:32778/" cookie = "role=" + x + "; hsh=" + y #print cookie build_header = { 'Cookie': cookie, 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:44.0) Gecko/20100101 Firefox/44.0', 'Host': 'web.phrack.top:32785', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', } urlparts = urlparse(url) conn = HTTPConnection(urlparts.hostname, urlparts.port or 80) conn.request("GET", urlparts.path, '', build_header) resp = conn.getresponse() body = resp.read() return body for i in xrange(1000): print i #secret len = ??? find_hash = "../hash_extender/hash_extender -d ';\"tseug\":5:s' -s 3a4727d57463f122833d9e732f94e4e0 -f md5 -a ';\"nimda\":5:s' --out-data-format=html -l " + str(i) + " --quiet" #print find_hash calc_res = os.popen(find_hash).readlines() print calc_res hash_value = calc_res[0][:32] attack_padding = calc_res[0][32:] attack_padding = urllib.quote(urllib.unquote(attack_padding)[::-1]) ret = gao(attack_padding, hash_value) if "Welcome" in ret: print ret break
我们就只要修改find_hash得值就可以了!
相关文章推荐
- 商务综合管理-day2~5 分页实现,shiro(验证授权,MD5Hash加密),struts2全局异常处理
- C# 实现MD5 Hash 计算
- Silverlight,Windows Phone : MD5算法实现 Silverlight 2: MD5 hash string provider implementation[by Jeff Wilcox]
- MD5 Hash + Salt的密码存储方式实现
- MD5的Java Bean实现
- 一个实现MD5的简洁的java类
- Java的MD5实现
- SQL Server中的MD5实现方法
- 一个实现MD5的简洁的java类
- 用C++ Builder的MD5控件实现软件防护
- C# 中使用 MD5 算法计算 hash (哈希)值的四种方法
- Java的MD5实现
- 用SHA1或MD5 算法加密数据(示例:对用户身份验证的简单实现)
- 在Java中使用MessageDigest实现MD5,sha1等加密
- 用SHA1或MD5 算法加密数据(示例:对用户身份验证的简单实现)
- HMAC-MD5的C#实现
- C#的MD5实现
- MD5 算法的java实现
- Java的MD5实现
- 用SHA1或MD5 算法加密数据(示例:对用户身份验证的简单实现)