RPO攻击是什么

RPO全称Relative Path Overwrite(相对路径覆盖),一种“欺骗”型攻击技术。其原理就是利用服务器与浏览器对URL解析的差异,将页面中使用相对路径引入的静态资源文件解析成其他资源文件或者我们可以控制内容的页面,从而导致XSS、任意文件读取、信息泄露等漏洞的产生。

利用条件

  • 配置错误的apache服务器(开启AllowEncodedSlashes选项),或者nginx服务器。
  • 存在相对路径的js或者css的引用

服务器解析差异

要利用该漏洞首先得了解不同服务器的解析差异,我们可以通过一个简单的测试来区分服务器对URL的解析差异,现在可以在rpotest目录下创建一个空的test文件夹和一个内容为“I'm rpotest”的test.php文件,当成功访问时会输出该内容。

现在分别请求apache和nginx服务器下该链接的访问情况:http://127.0.0.1/rpotest/xxxxx/..%2Ftest.php

  • apache:
  • nginx:

    从上图的访问情况可以看到对于同样的URL,apache和nginx服务器的处理方式是不同的,Apache服务器默认情况下是不解析%2F,也就是说服务器解析处理到xxxxx目录后认为..%2Ftest.php是一个文件,此时没有这个文件的所以返回404;而ngin服务器会先解码%2F为/,请求会变成/rpotest/xxxxx/../test.php,并且../会向前跳转到rpotest目录下,ci此时rpotest目录下有test.php文件,所以成功返回内容。

漏洞利用

在rpotest目录下创建一个test.php和一个空的rpo.js,test文件夹内创建另一个内容为alert('QAQ')的rpo.js。
test.php内容:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>rpo攻击测试</title>
    <script type="text/javascript" src="rpo.js"></script> //这里引用相对路径下的rpo.js
</head>
<body>
    <?php echo "RPO test"; ?>
</body>
</html>

先来看下正常访问test.php时,会加载根目录下的rpo.js(即rpotest目录下这个空的rpo.js) 。

但是当我们访问http://127.0.0.1/rpotest/test/..%2ftest.php时会发现该test.php竟然会调用test文件夹下rpo.js然后出现弹框现象?

我们来深度解析一下这一现象具体是怎么实现的:当我们访问http://127.0.0.1/rpotest/test/..%2ftest.php时向服务器发起请求,服务器首先解码%2F然后向上目录跳转即:http://127.0.0.1/rpotest/test.php, 然后返回test.php给浏览器,由浏览器开始处理test.php中的各css、js脚本;但是由于浏览器并不会解码%2F这个符号,所以浏览器加载的还是http://127.0.0.1/rpotest/test/..%2ftest.php这个url,同样的把会把..%2ftest.php当成一个文件,所以当前目录是/test,并且此时test.php引用的相对路径下的rpo.js文件是/test/rpo.js,因此造成弹框!

特性利用

如果是开启URL重写的伪静态网站的话(pathinfo模式),那么漏洞利用将会变得轻而易举了。什么是pathinfo模式?简单举例你一定一看就懂:

http://xxx.com/news/index.php?test=123
等同于
http://xxx.com/news/123.html
  • apache要开启pathinfo模式:
<Directory />
Options +Indexes +FollowSymLinks +ExecCGI
AllowOverride All
Order allow,deny
Allow from all
#AcceptPathInfo on   #增加这行配置
Require all granted
</Directory>
  • nginx增加pathinfo模式配置:
server {
        listen       80;
        server_name  localhost;
        root   "D:/good";
        location / {
           index  index.html index.htm index.php;
           autoindex  off;
           if (!-e $request_filename) {
              rewrite ^/(.*)  /index.php/$1 last;
            } 
        }
        
        location ~ \.php(.*)$  {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_split_path_info  ^((?U).+\.php)(/?.+)$;
            fastcgi_param  SCRIPT_FILENAME       $document_root$fastcgi_script_name;
            fastcgi_param  PATH_INFO  $fastcgi_path_info;
            fastcgi_param  PATH_TRANSLATED      $document_root$fastcgi_path_info;
            include        fastcgi_params;
        }
     }

这里我们配置好开启pathinfo模式的apache服务器,测试一下

创建一个test.php和一个内容alert('QAQ')的1.html文件

//test.php内容:
<html>
    <head>RPO attack test</head>
    <body>
        <script src="1.js"></script>
    </body>
</html>
<?php
error_reporting(E_ALL^E_NOTICE^E_WARNING);
if($_GET['page'])
{
    $a=$_GET['page'];
    Header('Location:http://127.0.0.1/rpotest/test/'."$a".'.html');
}
?>

然后访问:http://127.0.0.1/rpotest/test.php/page/1/..%2f..%2f..%2ftest.php

我们会发现竟然将1.html没有标签的纯文本内容当作javascript语句执行,这是因为和上述一样当浏览器解析到..%2f..%2f..%2ftest.php时把这一堆当作一个文件,然后加载是的http://127.0.0.1/rpotest/test.php/page/1/1.js,而且test.php里面写的请求是,所以返回的内容(1.html)会被当成js解析,所以造成了弹框现象。

漏洞防护

在页面中避免直接使用相对路径进行静态文件的加载。

总结

在实际场景中可能需要网站有可控的未声明的输出点,并且使用相对路径的js、css加载方式或者开启pathinfo模式;但如果细心的研究这些利用技巧的话,可能会有意想不到的收获!