• 如果您想对本站表示支持,请随手点击一下广告即可~
  • 本站致力于提供原创、优秀的技术文章~
  • 有任何疑问或建议 均可以在站点右侧栏处 通过各种方式联系站长哦~
  • CTF – RootMe解题报告 [Web-Client : HTTP Response Splitting]

    渗透测试 EXP 1327阅读 0评论

    挑战入口:Root-Me(https://www.root-me.org/en/Challenges/Web-Client/HTTP-Response-Splitting)
      分类目录:Link to …(http://exp-blog.com/2019/01/02/pid-2597/11/)


    吐槽

    搞了 4 天,忍不住想喷死出题的,这题其实不是很难,就是题目的机制和 robot 的行为太恶心,而且这些行为还是隐藏的,要自己慢慢摸索。

    摸索都算了,这其实很正常,关键是摸索规则出来之后,发现这些规则跟学习 HRS(HTTP Response Splitting) 原理半毛线关系都没有,就是存心恶心人,让你就算做出了正确答案,也要靠运气才能蒙过去。

    要比喻的就是,考试明明所有题都做对了,老师就是故意不理你,不告诉你考了多少分,还误导你可能没全做对,让你再想想是不是做错了哪里,本来你就是刚开始学的,因为基础不牢固,结果越想越怀疑自己,越想越跑偏,这不本末倒置么。

    题目分析

    喷完了就开始分析这题的解题思路。

    首先关注题目描述有几个关键字:

    • HTTP Response Splitting : HTTP 响应头切割(下面简称 HRS)
    • reverse proxy cache : 网站启用了反向代理缓存
    • administrator often logs in:管理员 robot 会经常登录站点
    • IPv4 only:现在虽然都 2019 年了但 IPv4 还没到淘汰地步,所以不用管,跟解题没什么关系

    而我们要做的就是窃取管理员的 Cookie 登陆管理页面。

    考察技能

    这题主要考察关于 HRS反向代理 的原理,是这题的解题关键,必须彻底弄懂。

    可以参考这几篇文章,说得很清楚了,很有参考价值:


    而有 反向代理 必定有 正向代理,虽然 正向代理 与本题无关,但最好还是了解一下。

    我找了一张图,很清楚了画出了 反向代理 和 正向代理 的区别:

    简单来说,反向代理 是服务端搭建的,角色定位是防御,主要作用包括:隐藏服务端集群的具体机器、通过缓存加速客户端访问等,常见的反向代理如 Nginx。

    正向代理 是客户端搭建的,角色定位是攻击,主要作用包括:隐藏客户端身份发起攻击、翻墙等,常见的正向代理如 Shadowsocks。

    找到注入点

    有了前面的基础知识,就可以开始解题了,首先要找到注入点。

    整个挑战其实只有3个页面:

    有一点需要注意的是,开启挑战后,选择语言页面 只会出现一次,除非删除浏览器 cookie ,否则之后无论如何也不会再看见这个页面。

    这就导致有些同学很容易就把这个页面忽略了,一直在 主页管理页面 之间徘徊找注入点,浪费了大量时间而却只是无用功。


    其实真正的注入点恰恰就在 选择语言页面

    从页面源码可知,只有 lang=frlang=en 两种语言可供选择。

    但是使用 Burp Suite -> Repeater 工具构造 payload,可以发现修改请求参数 lang=any_value 为任意值,都会在响应头的 Cookie-Set 中回显。

    换言之这很可能就是注入点,而且从形式上看,应该就是 HRS 。

    为了验证是不是 HRS ,不妨尝试注入 lang=exp%0D%0Ahrs%0D%0A回车换行符 的 URL 编码,亦即 \r\n ,亦即 CRLF 。若不理解为什么要注入回车换行,先去学习下 HRS 的基本原理再继续往下阅读)

    可以发现注入成功,这样就具备攻击条件了。

    解题思路

    不要忘记题目有个信息:站点启用了 反向代理缓存

    换言之我们可以尝试使用 HRS + 反向代理 实现 缓存污染


    总结而言,攻击分五步:

    • (1) 通过 CRLF 构造适当的 HRS 请求在服务器构造两个响应页面(构造细节可参考 Github : CRLF – Write HTML,下面也会详述),其中第一个响应页对应本次请求,第二个响应页则由于还没有请求,导致在服务器挂起(下面称之为 payload 页面)。
    • (2) 在发动第 (1) 步攻击后,马上向服务器请求 /admin 页面,由于此时 payload 页面处于挂起状态,根据 HTTP 协议的特性会将其马上返回给 /admin 请求。在这一瞬间,会同时利用了服务器反向代理的特点(通过缓存机制加速访问),即 /admin 请求 与 payload 页面的映射关系会被自动绑定到了反向代理缓存。这样在缓存过期之前,无论谁发起 /admin 请求,都是从反向代理缓存获得 payload 页面,而不会从真正访问到 HTTP 服务器,亦即我们把缓存污染了。
    • (3) payload 页面是经过精心设计的,具备窃取当前访问用户的 Cookie 并发到 Hacker 服务器的功能。
    • (4) Hacker 等待 robot 管理员发起 /admin 页面请求,从而获得管理员 Cookie 。
    • (5) 在反向代理缓存过期后,Hacker 利用管理员 Cookie 向真正的 HTTP 服务器发起 /admin 页面请求,实现登录。

    梳理好攻击步骤后,明显本题的 解题关键在于如何构造 payload 页面

    构造 payload 页面

    首先需要知道,提交请求 http://challenge01.root-me.org:58002/user/param?lang=[注入点] 的响应是这样的:

    先不考虑 [注入点] 的 payload 怎么写,而是先看看我们最终期望的响应,应该是这样的:

    注:每行的行首都有一组 {CRLF} ,表示每行之间都有回车换行符号进行分隔

    逐行解释

    • [01][02] 是原本的响应内容,这部分的响应对我们没用,所以无需关心内容是什么
    • [03] 从这行开始就是注入内容,正因为我们不关心原本的第一个响应内容,所以用 Content- Length: 0 直接标记第一个响应结束(注意这里的每一个空格,不知为何很重要,少一个都不行
    • [04] 是一个空行,这很重要,用于分隔第一个和第二个响应内容
    • [05] 这里开始就是我们要构造的第二个响应内容
    • [06] 必要的响应头之一,标明我们构造的响应页面内容类型
    • [07] 非必要的响应头,但是以防万一,用于关闭页面的 XSS 保护,使得我们可以注入 XSS 脚本
    • [08] 必要的响应头之一,要使得反向代理服务器的缓存在过期前可以被持续污染,就需要把 Last-Modified 设置为一个未来值,这样代理服务器就以为缓存没有更新过,从而对我们的 payload 页面进行保持,而不会向 HTTP 服务器获取新的缓存进行覆盖。
    • [09]HTTP/1.1 版本之前是必要的响应头之一、这个版本之后则无关重要了。Content-Length 的值需要刚好就是 [11] 页面内容的长度(按ASCII字符计算,包括空字符),若过长会造成等待响应内容超时、过短会截断页面内容。
    • [10] 是一个空行,这很重要,用于分隔第二个响应的响应头和页面内容。
    • [11] 页面内容,这里我只构造了一个 JS 脚本,其功能是向预设的 RequestBin 服务器发送访问这个页面的用户的 Cookie ,这样当管理员浏览这个页面时,就会被窃取 Cookie 。

    回到前面的 [注入点] ,根据我们期望的响应内容,将其转换成 URL 编码,因此在 [注入点] 的位置构造 payload 应该是这样的:

    我们把每部分拼接起来,最终的 payload 请求 是这样的:

    现在尝试通过 Burp 把 payload 发送到服务器,看看效果如何:

    发起攻击

    既然拥有 payload 就可以发起攻击了,步骤其实很简单,前面也已经说过了:

    • (1) 提交前面的 payload 请求
    • (2) 马上访问 http://challenge01.root-me.org:58002/admin 页面,如果返回的是我们构造的 payload 响应页面,则污染缓存成功
    • (3) 登录 payload 响应页面中预设的 RequestBin 服务器等待管理员 Cookie

    攻击过程的细节

    事实上,攻击过程并没有我们想象中顺利,这是这题最恶心的地方,尤其是在不知道自己的 payload 是否正确的前提下,真的能调试到你怀疑人设。

    总结一下,我遇到的细节问题有以下这些:

    • 经测试无法使用 Burp 发起缓存污染攻击,原因不明。
    • 只有 IE 或 Edge 浏览器可以用来执行这个挑战,且要求 IE 或 Edge 关闭所有安全选项。
    • 发起 payload 请求后,服务器默认的第一个响应是一个 302 页面,跳转的 Location/home 页面。由于其跳转速度非常快,导致我们尝试使用 /admin 请求绑定我们构造的第二个 payload 响应前,就很可能被 /home 请求抢先一步绑定了 payload 响应(换言之可供我们操作的时间可能还不到 1 秒)。
    • Robot 管理员只会扫描 /admin 页面,如果被 /home 抢先一步绑定了第二个 payload 响应,那么我们构造的 payload 就无法窃取管理员 Cookie 。亦即第一个响应的 302 跳转目的之一是拦截我们的污染缓存攻击。
    • Robot 管理员的行为不可预见,亦即它在访问 /admin 页面时,会不会读取图片、有没有启用 JS 脚本等都是未知的,我们需要逐个 html 标签进行注入调试。
    • 由于这个挑战是运行在沙盒的,这个沙盒会对每个新的挑战 Cookie 分配一个 SessionId 。因此若某一次污染缓存失败或窃取 Cookie 失败,就需要重新领取一个新的 SessionId 进行调试。而在 Burp 无法使用的情况下,要获取新的 SessionId 只有一个方法,重置挑战环境参数:注销 rootme 的登陆,清除浏览器 Cookie 和缓存,然后重登 rootme 再重开挑战。
    • 反向代理缓存的过期时间是 15 分钟,亦即在绑定 /admin 与 payload 响应失败后,要么等 15 分钟再试,要么重置挑战环境参数(这个是真的烦,繁琐的重置步骤能重置到吐血)。
    • 由于这是一个沙盒,且 Robot 管理员是通过 SessionId 区分每个用户的挑战环境的,因此可以考虑通过代码并行操作来提高污染缓存的成功率。
    • Chrome 和 Firefox 浏览器因自身的安全机制,是无法某些渗透攻击的,包括这个挑战在内。因为这个挑战的 payload 会不断用到重定向,导致多试几次就会报错并拦截攻击:

    由此可知,这题除了题目表面的描述提示,其实还隐藏了很多解题要点,先了解的话可以少走很多弯路。

    这里要感谢 rootme 社区的讨论组,给了我不少启发:


    完成挑战

    经过孜孜不倦的重试,我最后是通过 IE 浏览器完成挑战的。

    我用 IE 浏览器预先打开两个标签,一个准备发送 payload ,一个准备请求 /admin 页面。

    然后就是和时间赛跑:在提交 payload 的一瞬间,马上发送 /admin 请求。

    重复 N 次后,终于使得 /admin 请求成功地与我们构造的 payload 页面绑定到一起:

    此时几乎同一时间,我就在 RequestBin 服务器收到 Robot 访问 payload 页面后被窃取的 admin_session

    利用 admin_session 访问 /admin 页面,被告知 admin_session 的值 946a0b2d-c590-46f9-86fd-f7e76062779d 就是 flag ,完成挑战。


    转载请注明:EXP 技术分享博客 » CTF – RootMe解题报告 [Web-Client : HTTP Response Splitting]

    喜欢 (1) 分享 (0)
    发表我的评论
    取消评论

    表情

    Hi,您需要填写昵称和邮箱!

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址