利用Relative Path Overwrite(RPO)构造xss的几种姿势

一年半过去了诶...夹点私货吧..也算是给自己一点交代了

讲道理我也不知道这一年多到底做了些什么...总之一晃就过去了...可以说是彻底褪变成了一条致癌咸鱼惹

一直摸鱼一直爽(并不

总之咸了很久叭..一部分原因是因为把精力放在了一些奇怪的地方吧...比如学校的课程..密码学这些还是值得好好学的,还有开发向的东西...碰了下Rust、Elixir这些...也就只是看完了The book,没有也不打算做什么项目的程度..还有就是也借着学校课程的机会看了不少mooc补全了一些奇怪的和之前的方向不怎么相关的基础吧...操作系统、体系结构、自动机理论这些 我全都要.jpg

嘛...之前的我大概对技术之外的任何东西都是不怎么提得起兴趣的样子..摸了一年现在变得有点 ((

咱目前姑且也算半个飞友和铁道宅了吧...甚至还有存在主义哲学、精神病学、医学心理学、天文学什么的都略懂一点 (错乱) ((w

要说为什么会变成这样的话大概最主要的原因还是因为一些自己个人的事情吧...感情上的事情..还有生病什么的...还有就是SNS什么的真的很浪费时间...本该用来提升自己的时间..嘛..都过去了就是。

总之对于我来说这一年多更像是失去的一年,希望现在能够重新开始、继续努力的话还来得及..

好了..私货到此为止吧..只是想随便找个地方把这些东西记录下来..又没有写日记的习惯干脆就记录在这里啦w


<!doctype html>  
<html>  
  <body>
    <h1>test.html here!</h1>
    <script src="main.js"></script>
  </body>
</html>  

test.html

alert("Welcome!");  

main.js

首先丢一个问题叭..像上面这样的东西是否可能构造xss呢...

直觉上觉得绝对不可能对吧...

首先介绍一下相对路径覆盖(RPO)这种攻击方式,使用这种方式可有效扩大xss的攻击面,这种攻击方式也不算新鲜了,网上的资料不是很多但也有不少师傅写得很详细了我这里就不过多赘述了..

这里就只简单讲一讲实现的原理和一些前置条件吧..

RPO的原理和产生的根本原因

浏览器在通过相对路径加载静态资源时(其实也不一定非要是静态资源啦..也可以劫持使用相对路径进行的跳转什么的,可能意义不大,看场景来),由于浏览器和部分webserver实现上的差异导致部分特殊构造的路径的语义出现歧义,攻击者可以通过构造特殊的url,来控制页面的base-path,让浏览器加载攻击者控制的任意静态资源。

浏览器和部分webserver实现上的差异

部分webserver(如nginx在遇到编码后的/(%2f)后会自动解码然后继续处理),就是说在url里构造/和%2f对于webserver来说没有差别,/foo/bar/foo%2fbar对于webserver来说是同一个东西,即foo目录下的bar文件

而对于大部分浏览器来说(我这边只实测了chrome和firefox,不知道ie什么情况),并不会自动解码%2f,唯一的用来作为目录分隔符的东西就是/本身(这里不考虑反斜杠\, 其实无所谓),所以对于浏览器来说/foo/barfoo目录下的bar文件,而/foo%2fbar是根目录下的foo%2fbar文件,浏览器以这两种方式中任意一种去请求webserver得到的结果都是同一个东西,但是微妙的区别就在于使用/foo/bar去请求的话浏览器会把/foo目录作为相对路径的base-url,而对于第二种/foo%2fbar,浏览器会认为相对路径的base-url是根目录/.

此时我们假设/foo/bar文件是一个html,通过相对路径加载src="main.js"。对于正常的请求/foo/bar,相对路径的base-url是/foo所以浏览器会加载/foo/main.js

相对的,如果通过/foo%2fbar去访问,虽然webserver返回的html是一样的,但是因为浏览器的实现不会decode %2f,而是把foo%2fbar整个当成了文件名,自然根目录/就被当成了相对路径的base-url,因此浏览器会去加载/main.js

进一步利用

在浏览器通过相对路径加载资源的过程中(base url+relative path),我们实际上可控的是base-url。 比如下面的payload/child/vul.php%3fvul=alert('RPO')%23/..%2ftest.html,这个在webserver看来和/child/n0t3x1st/../test.html是一样的,就是/child/test.html的内容。 而对于浏览器来说这是/child/vul.php%3fvul=alert('RPO')%23/目录下的..%2ftest.html文件,其要通过相对路径加载的js自然就是/child/vul.php%3fvul=alert('RPO')%23/main.js辣,而/child/vul.php?vul=alert('RPO')的响应就是我们反射回来的payload

注:#(%23)后的值只在浏览器本地有意义,在发送时会被丢弃。

RPO的利用条件

  • 目标页面使用相对路径加载资源(必须,且不能有base标签)
  • 同站存在可控的输入点(必须,无论是否有过滤,无论是储存的还是反射的)
  • webserver需能够把%2f解码为/并继续处理(Nginx默认配置下是可以的,Apache默认配置下 AllowEncodedSlashes的值是Off,会404哒)
  • webserver把uri视为路由而非一般文件系统的路径。这样一般(也不一定)可通过url获取参数,比如php里的pathinfo(非必须,这个多见于一些mvc的框架,之所以要加上这个条件是因为这样可以增加容错率。如果webserver把我们构造的uri当作文件系统的路径然后找不到对应的文件的话很可能直接404了,如果可以把部分无用的path当成无用的参数让webserver直接pass过去可以省很多事)
  • urldecode的小问题(非必须,这个影响到能不能用反射型的输入点来构造payload,最后说)

exploit!

注意:需修改webserver配置支持pathinfo
目录结构 目录结构
main.js和test.html就是一开始那两个

<?php  
  parse_str(parse_url(urldecode($_SERVER[REQUEST_URI]), PHP_URL_QUERY), $_GET);
  echo htmlentities($_GET['vul']);

vul.php

利用姿势1

Payload:
//localhost/father/child/vul.php%3fvul=alert('RPO')//..%2ftest.html Payload1 tricks: 使用//注释掉浏览器在后面拼接上的main.js

利用姿势2

Payload:
//localhost/father/child/vul.php%3fvul=alert('RPO')&/..%2ftest.html Payload2 tricks: 使用&把后面的/main.js变成query string里无用的参数

利用姿势3

//localhost/father/child/vul.php%3fvul=alert('RPO')%23/..%2ftest.html Payload3 tricks: 使用#(hashtag),浏览器应该加载的静态资源的uri是localhost/father/child/vul.php%3fvul=alert('RPO')#/main.js,因为#后面的main.js作为hash只在本地处理,不会被包含在浏览器发送的request里,实际发送的请求见上图。
讲道理我觉得这种姿势是最优雅的...

附录

为什么不能有base标签...
base标签已经指定了相对路径的默认base,那还构造个鬼...

  <head> 
    <base href="http://localhost/father/child/">
  </head>

比如这里,无论我们怎么构造,浏览器加载静态资源时的行为就是base指定的base-path(和我们在url里构造出来的无关)+relative path(比如这里的main.js),两者我们都不可控,且似乎没有覆盖base标签指定的这个默认值的方法,所以和绝对路径一样,gg。

urldecode的小问题
要用反射型的输入点来构造的话问号需编码,不然后面构造的东西都被webserver当成参数了而不是路径,且编码后的?(%3f)必须被decode,否则在未使用pathinfo的环境下编码后的%3f会被当成文件名的一部分然后直接404,在使用pathinfo的环境中这个似乎还会影响到webserver对传给php的QUERY_STRING的取值,还有很多很多其他的环境变量,最优解可能是在webserver层处理一下吧,不过这里我是在parse_str(parse_url(urldecode($_SERVER[REQUEST_URI]), PHP_URL_QUERY), $_GET);这里处理了一下,不是很优雅就是。使用path代替query string来传参的话似乎可以避开这个坑。

点击右边的按钮加载评论,如果无法加载那估计是被墙啦..你看着办w