• 加班月


    本月都在埋头加班,没搞什么有趣的东西,就分享一篇Cloudflare捅的篓子吧XD

    Google有个叫Project Zero的安全团队,成员Tavis Ormandy某天做一个语料库项目,突然发现Google缓存数据里出现了完整的https请求。排查发现这些请求都来自于Cloudflare。Cloudflare是业界闻名的CDN,通过DNS解释的原理工作,不需要修改页面的资源地址,而是将代理资源的域名,解释到靠近访客的Cloudflare服务器上,以此达到加速效果。Tavis怀疑这次事故跟Cloudflare的ScrapeShield产品有关。ScrapeShield是一个页面改写的产品,用来在网页里混入不可见的随机字符串,以跟踪爬虫的来源;对爬虫改写正文里的email,以防被批量爬Email地址发垃圾邮件。(参见Tavis Ormandy的报告

    Cloudflare接到报告后,用了47分钟关掉三个特性,7个小时后修复了漏洞。(参见Cloudflare的报告)报告非常长,我这里简单说一下。Cloudflare使用了Ragel来处理html改写的任务,Ragel通过一般的正则表达式描述有限状态机,并将其编译成C代码执行。因业务发展,Cloudflare决定用新的cf-html来取代Ragel。Ragel和cf-html最终都是生成nginx的C代码模块,编译到nginx里面去。基于Ragel的分词器就有这个bug,换cf-html潜在改变了buffer的用法,引发了泄漏。罪魁祸首是以下这段代码:

    /* generated code */
    if ( ++p == pe )
        goto _test_eof;

    因为在某些情形下,p有可能越过pe,导致了内存越界读取。生成这段C代码的Ragel代码如下:

    script_consume_attr := ((unquoted_attr_char)* :>> (space|'/'|'>'))
    >{ ddctx("script consume_attr"); }
    @{ fhold; fgoto script_tag_parse; }
    $lerr{ dd("script consume_attr failed");
           fgoto script_consume_attr; };

     如果匹配成功,执行@{}的内容,如果是残缺的<script标签,则会执行$lerr{}的内容。比较一下两个分支,错误代码的分支少了一个fhold(fhold会生成p--)。所以p会错误停留在导致出错的字符上。如果出错的标签处在缓存的尾部,那么p已经指向了pe+1,++p == pe 总是为假,导致了缓冲区溢出。(所以不要用C来搞web ;))

    有意思的是,Cloudflare在正文里提示用++p >= pe就可以避免这个错误,但强调这个不是Ragel的问题(即使这句代码是Ragel生成的)。Ragel的作者则在评论吐槽说,Cloudflare的报告毁掉了他的生意。而Google的大牛则花了整个周末帮忙清理Google的cache,验证Cloudflare的漏洞是否已堵上,顺便发现了一款流行密码管理器明文传输密码(1password已否认,剩下的Lastpass、Enpass不知道会不会表态呢?)随后,Ragel的作者写了一篇文章,解释了Ragel的正确用法。

    Ragel是一门描述状态机的语言,fgoto语句用于跳转到状态机中的其他子状态。Ragel允许用户定义Action,在各种情形下执行。Error Action是正常处理流程无法继续时执行的Action。它们一般在两种情形下执行:1. 错误发生在当前字符 2. 状态还没到达最终状态,而输入已经结束。如果错误发生在情形1,是否fhold来回退指针无关紧要。但是情形2里,因为输入已经结束,这时候必须回退。现有的Ragel代码需要检查自己对情形2的处理。

  • 相关阅读:
    class7-附
    class6-附
    class6
    class5-附
    class4-附
    class4
    class3-附【家庭资产配置】
    class2
    芒果绿的blog
    java网络爬虫基础学习(四)
  • 原文地址:https://www.cnblogs.com/Lifehacker/p/cloudflare_buffer_overflow_bug.html
Copyright © 2020-2023  润新知