Introduction
当客户端和服务器通过互联网进行通信时,它们就可能会受到 MITM 攻击。攻击者可以拦截来自客户端的请求,修改数据并将修改后的请求发送到服务器。在这种情况下,服务器需要验证接收到的请求的完整性。验证请求完整性的标准方法是在请求上附加一个称为 MAC 的标记。
MAC 是由密钥和一条消息计算出来的,计算 MAC 的一个简单方法是对密钥(密钥只有客户端和服务端知道)和消息拼接后的字符串做单向哈希运算。这种方法会受到哈希长度拓展攻击,该攻击允许攻击者在不知道密钥的情况下修改消息,同时仍然能够基于修改后的消息生成有效的 MAC。
Lab Environment
在这个实验中有一个服务器,客户端可以向服务器发送一系列命令,每个命令都需要附加一个基于密钥和命令计算的 MAC。服务器只有在 MAC 被成功验证后才会执行请求中的命令。
首先,我们需要在 /etc/hosts 文件中添加以下条目,来绑定 IP 地址和服务器域名。
10.9.0.80 www.seedlab-hashlen.com |
服务器代码在 Labsetup/image_flask/app 文件夹中,它有两个目录。www 目录包含服务器代码,LabHome 目录包含一个秘密文件和用于计算 MAC 的密钥。
服务器接受以下命令:
- lstcmd:列出 LabHome 文件夹中的所有文件
- download:从 LabHome 目录返回指定文件的内容
客户端发送到服务器的典型请求如下所示。服务器需要传递一个 uid 参数。它使用 uid 从 LabHome/key.txt 获取 MAC 密钥。然后使用密钥和命令生成一个哈希值,就是后面附着的 MAC 值。
http://www.seedlab-hashlen.com/?myname=JohnDoe&uid=1001&lstcmd=1&mac=dc8788905dbcbceffcdd5578887717c12691b3cf1dac6b2f2bcfabc14a6a7f11 |
Task 1: Send Request to List Files
我们发送的请求如下:
http://www.seedlab-hashlen.com/?myname=ceyewan&uid=1001&lstcmd=1&mac=8fc661f39b5215d0801daaaad73dfccc1bb723fad30a8abc2c0952018f300ad3 |
其中 MAC 字段是使用如下命令生成的,其中 123456 是 uid=1001 对应的密钥,然后将其与消息拼接,最后计算其哈希值。
echo -n "123456:myname=ceyewan&uid=1001&lstcmd=1" | sha256sum |
发送结果如下:
发送下载命令:
http://www.seedlab-hashlen.com/?myname=ceyewan&uid=1001&lstcmd=1&download=secret.txt&mac=12b691e6f439e1e18e70dc35767543cef5ba0e34320c414c9d33f1d6c35504ae |
这里我是使用 SSH 连接远程服务器的,那么也就说明我们的服务器没办法使用浏览器。执行
ssh -L 8888:10.9.0.80:80 seed@192.168.2.3
表示启动端口转发,将所有传入 8888 端口的连接转发到远程服务器的 IP 地址为 10.9.0.80,端口为 80 的目标主机。
Task 2: Create Padding
SHA-256 的块大小是 64 字节,所以在哈希计算过程中,一条消息 m 将被填充为 64 字节的倍数。填充的第一个字节固定为 \x80,最后 8 个字节是 m 的长度(bit 长度),如果一个消息有 10 个字节,那么长度就是 80,并且使用大端序保存。
因此,我们原始的 message 是 “123456:myname=ceyewan&uid=1001&lstcmd=1”,我们构造 payload 如下:
with open("file.bin", "r+b") as file: |
这个脚本会将整个 64 字节的块存入 file.bin
并输出我们参数为 123456:myname=ceyewan&uid=1001&lstcmd=1%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%01%38
,然后我们使用 sha256sum file.bin
得到这个块的哈希值 262b9696f81c50290adc39360cbc4a282e313debee93b1330125a37ca4cab897
。最后我们构造的 URL 如下:
// URL 中需要将 \x80 转码为 %80 |
运行结果如下,提示说 MAC 是正确的,但是没有执行命令:
Task 3: The Length Extension Attack
在这个任务中,我们将在不知道 MAC 密钥的情况下为 URL 生成一个有效的 MAC。假设我们知道一个有效请求 message 的 MAC,也知道 MAC 密钥的大小。我们的工作是在 message 的基础上伪造一个新的请求,并且计算一个有效的 MAC。
首先,我们使用 Task 1 中最开始那个 MAC,是 key:message 的哈希值。这个值的计算是 key:message 扩展后的大小为 64 字节的块的哈希值。然后我们将这个值填入如下的代码中:
|
这份代码在原有的哈希计算链后面加了一个块,就是 additional message,我们不需要知道 key 是什么,我们只需要知道前一个块的 MAC 值,拿来计算当前块的 MAC 值,结果就是最终的 MAC 块。
这样,message 就不是最后一个块了。我们需要将 message 块使用 payload 填充,这样 additional message 才会和 message 不在同一块,而是下一块。填充不需要知道 key 的内容,只需要知道 key:message 的长度,然后相应填充 64 - length 个字节即可。我们的这个填充不会改变 message 块计算出来的哈希值,因为使用的是和哈希算法相同的哈希值。
执行代码,最终的 MAC 值为 edd78a79d181d70f3b11be16bd27c6d21622a2e533bcfcd778915e4efcbe4f54,我们构造 URL 如下:
http://localhost:8888/?myname=ceyewan&uid=1001&lstcmd=1%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%01%38&download=secret.txt&mac=edd78a79d181d70f3b11be16bd27c6d21622a2e533bcfcd778915e4efcbe4f54 |
执行结果如下:
Task 4: Attack Mitigation using HMAC
将服务端 MAC 计算由下面方法一换成方法二,然后重新启动容器,输入 Task1 的命令,没有结果输出了。
# 方法一,拼接后直接使用 sha256 算法。key 只会在第一个块参与 |
这样,就无法进行攻击了,如下,使用的不是这种算法了。
#!/bin/env python3 |