前提说明:5月26日,该漏洞已被修复。环境的功能,当你点击时,它能够实现一键化配置和部署第三方组件、函数库和应用。比如说你可以在如下页面中看到这个标志https://dashboard.heroku.com/new?button-url=https%3A%2F%2Fgithub.com%2FParsePlatform%2Fparse-server-example&template=https%3A%2F%2Fgithub.com%2FParsePlatform%2Fparse-server-example
每个开发者都可以创建像上面那样的“一键部署”按钮,你可以从下面这个链接中看到更多说明。
我创建了一个“一键部署”按钮,它只包含一个文件app.json,Heroku会从这个文件中读取到该按钮的相关信息。
一开始,我尝试在app.json中插入HTML标签和属性来实现XSS,结果发现JavaScript会将特殊字符进行转义。后来我发现某个URL中的“logo”可以被设置成任何值,然后当页面被加载时,该URL会被设置成img标签中的data-src的值。
当img标签加载时,浏览器会向服务器发起获取图片的请求,这时候会带上Referer的值,这时候只需要在服务器监听该请求,即可获取该Referer的值。
有关HTTP导致的信息泄露,可以参考cure53的搜集。漏洞
到目前为止,当我对下面这个进行加载时https://dashboard.heroku.com/new?template=https%3A%2F%2Fgithub.com%2FParsePlatform%2Fparse-server-example&other_parameter=with_value
如下的img标签会被建立
https://avatars0.githubusercontent.com/u/1294580?v=3&s=200
这时候HTTP头会带上Referer的值,如下:
你也可以看到这里并没有什么有用的数据。
接下来我去测试了一下Heroku的OAuth认证过程,当我将redirect_uri设置成如下时:https://dashboard.heroku.com/new?template=https%3A%2F%2Fgithub.com%2FParsePlatform%2Fparse-server-example&other_parameter=with_value
本以为可以成功从Referer获取到有用的数据,但是却没有成功。
后来不知道什么时候,我注意到在身份验证成功后[path_requested]会带上一个参数code,这个参数的值在发送的时候并没有防护CSRF攻击,然后会以JSON的格式返回一个权限为“global”的“access_token”,这个“global”权限允许你通过API执行所有操作!但是我却不能修改账号的密码或者绑定的邮箱…攻击
只有当用户登录后攻击才会生效。
1、先让用户访问如下链接:
state=https%3A%2F%2Fdashboard.heroku.com%2Fnew%3Ftemplate%3Dhttps%253A%252F%252Fgithub.com%252Fesevece%252Fheroku_test
2、用户会被重定向到如下地址:https://id.heroku.com/oauth/autorize?client_id=....
3、用户再次被重定向到:https://longboard.heroku.com/auth/heroku/callback?code=....
4、最后,用户被重定向到https://dashboard.heroku.com/new?template=https%3A%2F%2Fgithub.com%2Fesevece%2Fheroku_test&code=...
5、一旦页面被加载,应用就会自动向Github的API发起获取app.json的请求:https://api.github.com/repos/esevece/heroku_test/contents/app.json?ref=master
6、这时候如下payload被创建,然后发起获取图片的请求,Referer中会包含code。
7、我服务器上的文件poc.php 会截获该img请求,获取Referer中的code的值,然后发起如下POST请求:
来获取token。将password参数的值设置为code的值
8、poc.php 获取到7中得到的“access_token”,将头部Authorization:设置成Bearer [获取到的token],然后发起如下请求:
9、poc.php 获取到上一步返回的“email”的值,然后自动发送一封提醒邮件给我。
上面说到我不能修改用户的绑定邮箱,但是最后面我发现通过将密码设置成access_token,居然就成功了!