哈希长度扩展攻击解析

起因

这是 ISCC 上的一道题目,抄 PCTF 的,并且给予了简化。在利用简化过的方式通过后,突然想起利用哈希长度扩展攻击来进行通关。哈希长度扩展攻击是一个很有意思的东西,利用了 md5、sha1 等加密算法的缺陷,可以在不知道原始密钥的情况下来进行计算出一个对应的 hash 值。
这里是 ISCC 中题目中的 admin.php 的算法:

$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)));
}

了解哈希长度扩展攻击

哈希长度扩展攻击适用于加密情况为:hash($SECRET, $message)的情况,其中 hash 最常见的就是 md5、hash1。我们可以在不知道$SECRET的情况下推算出另外一个匹配的值。如上例所给的 PHP 代码:

  • 我们知道md5($SECRET . strrev($_COOKIE["auth"]))的值
  • 我们知道$hsh的值
  • 我们可以算出另外一个 md5 值和另外一个 $hsh 的值,使得 $hsh == md5($SECRET . strrev($_COOKIE["auth"]))

这样即可通过验证。如果要理解哈希长度扩展攻击,我们要先理解消息摘要算法的实现。以下拿 md5 算法举例。

md5 算法实现

我们要实现对于字符串abc的 md5 的值计算。首先我们要把其转化为 16 进制。

补位

消息必须进行补位,即使得其长度在对 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。

补长度

补位过后,第 57 个字节储存的是补位之前的消息长度。abc是 3 个字母,也就是 3 个字节,24 bit。换算成 16 进制为 0x18。其后跟着 7 个字节的 0x00,把消息补满 64 字节。

计算消息摘要

计算消息摘要必须用补位已经补长度完成之后的消息来进行运算,拿出 512 bit的消息(即64字节)。 计算消息摘要的时候,有一个初始的链变量,用来参与第一轮的运算。MD5 的初始链变量为:

A=0x67452301
B=0xefcdab89
C=0x98badcfe
D=0x10325476

我们不需要关心计算细节,我们只需要知道经过一次消息摘要后,上面的链变量将会被新的值覆盖,而最后一轮产生的链变量经过高低位互换(如:aabbccdd -> ddccbbaa)后就是我们计算出来的 md5 值。

哈希长度扩展攻击的实现

问题就出在覆盖上。我们在不知道具体 $SECRET 的情况下,得知了其 hash 值,以及我们有一个可控的消息。而我们得到的 hash 值正是最后一轮摘要后的经过高地位互换的链变量。我们可以想像一下,在常规的摘要之后把我们的控制的信息进行下一轮摘要,只需要知道上一轮消息产生的链变量
有点难理解,因为我都看的头大。看起来我们把实现放在攻击场景里会更好。
仍然是如上的 PHP。因为其走了一点弯路(strrev、unserialize),所以我们修改一下。

$auth = "I_L0vE_L0li";
if (isset($_COOKIE["auth"])) {
    $hsh = $_COOKIE["hsh"];
    if ($hsh !== md5($SECRET . $_COOKIE["auth"])) {
        die("F4ck_U!");
    }
} else {
    setcookie("auth", $auth);
    setcookie("hsh", md5($SECRET . $auth));
    die("F4ck_U!");
}
die("I_aM_A_L0li_dA_Yo~");

在实际环境中,我不知道 $SECRET 的值(我胡乱打的QAQ),只知道长度为 12。首先我们访问一下看看。不出意外地被 f4ck 了。

Cookie 中的 auth 为I_L0vE_L0li,hsh 为 7a84f420f8abe642237409f9d4daa851。我们来进行哈希长度扩展攻击。

长度扩展

我们仍然要进行补位。因为 $SECRET 的长度是 12,我们用 12 个 x 来填补一下,紧跟着就是 auth 的值。然后我们把消息补到 448 bit。接着进行补长度。

然后后面跟着要附加的值,随意什么都可以。我这里是I_aM_L01i好了=v=。

然后去掉前面的假的 $SECRET,得到最终的 $auth。

I_L0vE_L0li\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\xB8\x00\x00\x00\x00\x00\x00\x00I_aM_L01i

urlencode之后为

I_L0vE_L0li%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%B8%00%00%00%00%00%00%00I_aM_L01i

计算哈希

我在网上找了一个 C 语言的 md5 实现。因为 Python 的实现不能改初始的链变量。我修改了初始的链变量为经过高低位逆转的 $hsh。
PS:原来的是7a84f420f8abe642237409f9d4daa851

A=0x20f4847a
B=0x42e6abf8
C=0xf9097423
D=0x51a8dad4


然后我们对附加的值进行 md5 加密。附加的值为I_aM_L01i。首先我们把前面 64 个字节改为 64 个A。这是为了使得除了 hash 本身以外其他的状态完全一样(原文:Then we take the MD5 of 64 'A's. We take the MD5 of a full (64-byte) block of 'A's to ensure that any internal values — other than the state of the hash itself — are set to what we expect)。实际上,前 64 个字节填充什么都无所谓。因为在进行我们的附加值的摘要之前,我们已经把链变量覆盖了。
然后我们编译并运行这个加密实现。
得到了一串密文,是1d00eac3f7da072d8365b0a7ae1fec42。我们用 Firefox 的 firebug 插件进行修改 Cookie。
刷新后发现已经通过验证。

总结

看起来很难理解,我本人也通宵了一晚上才搞定。当然因为我比较笨QAQ。总之,这是个很好玩的东西,大家可以去复现一下。
另外这个问题的解决方案为:hash($SECRET, hash($message))。这样就可以避免用户可控 message 了。

参考:https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks


GSM 嗅探笔记

在 5.21 这个充满了情欲的日子,重庆下起了暴雨——不过这看起来并不能阻挡情侣们的幽会了。
不过 Ricter 仍旧窝在寝室,开始折腾 GSM 嗅探。前几天 Cyrils 菊苣折腾了下,蛮有意思。总之就是在淘宝花了 80 元买齐了东西。

在昨天晚上,这一堆东西到货了。

折腾这个确实麻烦。编译软件环境就看 Cyrils 菊苣做的笔记,地址:http://cyrils.org/index.php/archives/20/。虽然折腾这个麻烦点,但是门槛确实低的可怕。像 Ricter 这样的渣渣都折腾好了..感谢 Cyrils 菊苣的鼎力帮助。
拿到手首先接线子。顺序大概是这样:

红 TX
白 RX
黄/黑 GN

总之我第一次接错了,然后没刷进去QAQ。
下面的图片供能看懂的人参考=A=。


然后把 USB 共享到虚拟机里。运行lsmod | grep usb后会有这个东西。

同时 GND 处的红灯会亮起来。
如果你的环境配置好了,紧接着就开始刷固件了。
运行:

./osmocon -m c123xor -p /dev/ttyUSB0 ../../target/firmware/board/compal_e88/layer1.compalram.bin

接着点一下开机键。如果成功了大概会这样:

如果没有任何输出,说明你线子插错了。如果有Received FTMTOOL from phone, ramloader has aborted的错误,多多尝试几次,包括但不限于:

  • 拔了线重新运行
  • 多次点击关机键

祝你成功!!QAQ!!!
如果成功了,多开几个新的终端,一个运行:

cd ~/osmocom-bb/src/host/layer23/src/misc
./cell_log -O

大概会出现这样的东西。

这里 ARFCN 使我们需要注意的。这里第一个是 58,中国移动。那就选它了。
接着运行:

./ccch_scan -i 127.0.0.1 -a 58

这里的 58 就是 ARFCN ID,然后回车。
最后一个终端运行:wireshark -k -i lo -f 'port 4729'来抓包。过滤器里填写gsm_sms
如果有短信发进来的话,大概会出现这种东西。

嘛,被分手了(((゚д゚)))
参考:http://cyrils.org/index.php/archives/20/http://drops.wooyun.org/tips/723


免费 WiFi 下看胖次的颜色几种姿势

0x00,前言和环境

前言

免费 Wi-Fi 下的安全性我们自然清楚,一不小心就会被看到胖次,无论是否是 https。当然,如果被攻击者很敏感的发现该是 https 的地方不是 https 就停止登录,那么自然无妨。不过令人遗憾的是大多数人没这个意识,免费无线网的诱惑远大于浏览器上爆红的恐惧,所以 GG。

环境

渗透环境是完全控制路由器,攻击者和被攻击者在同一个内网。实际上,当黑阔们完全控制路由器时,可以直接在路由器上抓包进行中间人攻击,这个反而比 ARP 欺骗之类的攻击手法方便很多。不过许多路由器压根没有命令接口,烂的很ow<

被攻击主机:

IP: 192.168.1.109  
OS: Windows 7

攻击者主机:

IP: 192.168.1.111
OS: CentOS 6.5

路由器:

IP: 192.168.1.253

网络拓扑图:

0x01,用 sslstrip+ARP 来看胖次

原理大概就是:

  1. ARP 欺骗受害主机,劫持受害主机流量;
  2. 用 sslstrip 将 https 的裤子脱下来,强迫受害者用 http 通信;
  3. 同时攻击者也会与服务器建立正常的 https 连接;
  4. 嗅探得到受害者的信息。

就像这样:

受害者  <== HTTP ==>  攻击者  <== HTTPS ==>  服务器

也就是 MITM 的加强版的样子ow<
攻击详细过程如下:

  1. 首先开启内核包转发,然后用 iptables 转发 80 端口到 10000 端口上,其中 10000 端口为 sslstrip 监听的端口;
    echo 1 > /proc/sys/net/ipv4/ip_forward
    iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-ports 10000

  2. 开启 sslstrip(脱裤子什么,让我来owo);
    sslstrip -a -k -f

  3. ARP 嗅探受害者主机(大骗子哼OAQ);
    arpspoof -i eth1 -t 192.168.1.109 192.168.1.253
  4. ettercap 来看胖次;
    ettap -u -T -q -i eth1
    我们先打开正常的 http 页面看一看能不能得到胖次,登录 v2ex 好了。
    输入账号 test,密码 test,嗯呜,喵呜,胖次被轻易的看到惹 QAQ..

接下来我们找一个 HTTPS 的网站来,就拿支付宝来说。我们访问后发现并不是 https,而是 http。但是大多数人并不会意识到这个的,他们会自然而然的登录。

紧接着我们进行嗅探,由于数据包比较乱,我把它输出为一个文件来看。

然后用 Wireshark 过滤一下,看数据包。虽然密码加密了,但是我们仍然清楚的看到已经发包成功。虽然得不到密码,有 Cookie 也会登录进去噢ow<

0x02,DNS 劫持直接看胖次

DNS 劫持是老生常谈的话题了,利用 DNS 直接钓鱼的效率极高。特别是在攻击者搭建的 Wi-Fi 环境下,DNS 想改成什么就改成什么,简直酸爽无比。

本来我想直接在路由器上复现 DNS 劫持的,但是搭建个 DNS 服务器感觉好麻烦,另外出租屋里其他四个人如果被我搞的上不了网那么我感觉会被群殴QAQ,所以我以另外一种方式简单复现一下。

  1. 首先我先把被攻击着的 DNS 服务器改为攻击者的 IP 地址,其实如果直接在路由器上改也是这个效果。

    然后我们在攻击者的机子上搭建一个简单的 DNS Server——无论请求什么地址,都返回攻击者的 IP;

    我们在被攻击着的机子上测试,发现成功。

  2. 我们在攻击者的机子上搭建一个和 QQ Mail 一模一样的网站——直接用浏览器保存就好了嘛,小学就学过;

  3. 稍微修改一些代码,使得可以接受被攻击着的用户名和密码。

由于一些原因保存的页面非常粗糙,不过如果是精心钓鱼的话,页面会和真正的页面一样,钓鱼成功几率极高ow<

0x03,胖次上的摄像头——JS 投毒

JS 投毒是在中间人攻击的基础上,在返回的 HTML 数据中插入恶意 JavaScript 代码。JS 投毒完全可以配合 DNS 劫持以及 sslstrip 来使用。
利用 closurether 轻松实现插入任意脚本,编译安装 node.js 以及用 npm 安装 closurether 就不赘述了。
(由于这是我第二天复现的,所以受害者 IP 变成了 192.168.1.104)

仍然是上面的环境,受害者的 DNS 服务器为攻击者的主机。我们修改 /usr/local/lib/node_modules/closurether/asset/inject 目录下的 extern.js 插入我们的测试代码,成功得到 Cookie。
而且通过 JS 投毒,可以进行许多事情,甚至可以配合 metasploit 进行对目标主机的渗透。具体看这个新的 framework,MITMf

0xFF,后话

所以说免费 Wi-Fi 能不用就不用,如果非要用,那就黑下来路由器然后改密码好了。
偶尔去大街上转悠的我随便就能进去的路由器也不少,大多数是弱口令,还有小部分的路由器 authenticate bypass 啦,抓包抓到密码啦什么的。
这些也不是什么新的东西,都是被玩烂了的。咱闲着无聊玩一下,好歹也算是折腾过。嘛,毕竟渣。
另外,不就是想看胖次吗,给你看就是了!hentai..OAQ!

参考资料:
针对SSL的中间人攻击演示和防范
利用sslstrip和ettercap突破ssl嗅探密码
渗透测试:内网DNS投毒技术劫持会话