【课后有同学提出了很好的问题,就是文中给出的两个例子的应用场景不够合理;譬如如果已经知道了root密码,就可以直接Login;下载文件的时候怎么能保证一定能找到/etc/passwd。这里使用这个两个例子主要是用于演示扩展长度攻击的过程和原理,可以说是为了攻击而攻击。如果对实际的场景感兴趣,可以阅读Flickr真实场景的长度攻击。】
在密码学之哈希一文中介绍了SHA256。希望大家对这种哈希算法在流程上比较熟悉。接下来,我们结合 CTF题目来看一下针对基于Merkle-Damgard构造的哈希算法的一种攻击形式,扩展长度攻击。
在介绍扩展长度攻击之前,首先澄清一个概念,hash和MAC(message authentication code)的区别:
主要区别在于概念:虽然哈希用于保证数据的完整性,但MAC保证了完整性和身份验证。
怎么理解呢?哈希码是在没有任何外部输入的情况下从消息中生成的,得到的是可用于检查消息在其传输期间是否有任何更改的内容。
而MAC在生成时会使用一个密钥附加在消息之前:这确保不仅消息未被修改,而且发送者也是我们所期望的:否则攻击者无法知道用于生成的code的密钥。【考虑发送方和接收方共享一个密钥,在hash时附上密钥一起进行哈希】
根据维基百科:虽然MAC功能类似于加密哈希函数,但它们具有不同的安全性要求。MAC必须能够抵御指定明文攻击【??】。
首先,介绍攻击场景。攻击场景在CTF比赛中比较常见:
web系统的密码验证逻辑为:
$auth = false;
if (isset($_COOKIE["auth"])) {
$auth = unserialize($_COOKIE["auth"]);
$hsh = $_COOKIE["hsh"];
if ($hsh !== md5($SECRET . strrev($_COOKIE["auth"]))) { //$SECRET is a 8-bit salt
$auth = false;
}
} else {
$auth = false;
$s = serialize($auth);
setcookie("auth", $s);
setcookie("hsh", md5($SECRET . strrev($s)));
}
访问页面会返回一个cookie
Cookie: auth=b%3A0%3B; hsh=32efdc967fcaebc6853b75cacfb80c5f
为了验证方便,改写成以下的python代码:
import hashlib
import sys
from urllib import unquote
def login(password, hash_val):
m = hashlib.md5()
secret_key = "message" # secret_key is a salt
m.update(secret_key + password)
if(m.hexdigest() == hash_val):
print "Login Successful!"
else:
print "Login Failed"
if __name__ == "__main__":
password = unquote(sys.argv[1])
hash_val = unquote(sys.argv[2])
login(password, hash_val)
以上代码需要用户提供密码和一个哈希值,这个哈希值本身是服务端通过将一个密钥放在密码之前生成的。也即,网站需要确认的是,第一,用户确实知道密码;第二,用户确实是之前访问过网站并且获得了使用了密钥的哈希值的用户。作为攻击者,如果要想成功Login,那么就需要提供一个密码,同时还需要提供跟这个密码相配对的哈希值。而且这个哈希值还得是使用了服务端的密钥的哈希值。
已知系统中用户user1的密码为root,对应的salt为message,md5(salt || password)=md5(messageroot)=f3c36e01c874865bc081e4ae7af037ea。
第一次login是正常的login,成功;
$ python example.py root f3c36e01c874865bc081e4ae7af037ea
Login Successful!
第二次是攻击者构造的。利用“哈希长度扩展攻击”,我们可以算出一个密码和相应hash值,它们可以通过上面的密码验证逻辑,测试如下:
$ python example.py %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
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%38%00%00%00%00%00%00%00admin
e53a681a30ff99e3f6522270ca7db244
Login Successful!
尝试一下:
【上面的字符串的输入中不能有换行。尝试的同学请看参考9】
【上面的例子有问题。等讲完之后请大家讨论。】
通过上面的输出可知,攻击者使用伪造的密码和hash值进入了系统。
另一个例子是:
假设有一个网站,在用户下载文件之前需验证下载权限。这个网站会用如下的算法产生一个关于文件名的MAC:
def create_mac(key, fileName)
return Digest::SHA1.hexdigest(key + fileName)
End
</