前言
这个场景发生时间是去年了,那时候得到授权对某系统进行渗透测试,测试过程中已经发现存在接口未授权访问漏洞了,同时对于其登录页,联想到伪造其正确的请求返回包,可不可以结合接口未授权访问漏洞绕过前端登录逻辑从而达到进入系统后台呢?
接下来便开始实际操作,遂记录此文。
分析前端代码
对于该系统,经分析为前后端分离的系统,其前端页面是 Vue 框架所开发,使用的是 Webpack 打包器。于是在其登录相关的 js 代码处找到登录验证逻辑,然后分析相关代码流程,由于代码经过了混淆,故理解起来稍微有些困难,不过所幸代码逻辑并不是很难,其代码如下:
接口 l
代码:
const l = (t, e) =>c['a'].request({
url: 'http://xxx.xxx.xxx.xxx/api/xxx/xxx/SPLogin',
method: 'post',
data: {
account: t,
password: e
}
})
接口 p
代码:
p = t=>c['a'].request({
url: 'http://xxx.xxx.xxx.xxx/api/xxx/xxx/Test',
method: 'post',
headers: {
Authorization: t
}
})
登录过程代码如下:
login() {
if (!this.isLoading) {
if (!this.username) return this.$message.info('请输入用户名!');
if (!this.password) return this.$message.info('请输入密码!');
if (!this.yzcode) return this.$message.info('请输入验证码!');
if (this.yzcode != this.identifyCode) return this.refreshCode(),
this.$message.info('验证码错误!');
this.isLoading = !0,
l(this.dataEncrypt(this.username), this.dataEncrypt(this.password)).then(t=>{
const e = t.data;
if (e.access_token) {
Object(u['b']) (e.access_token);
var r = 'Bearer ' + e.access_token;
p(r).then(t=>{
200 == t.status && 'test' == t.data ? (this.$message({
message: '登录成功!',
type: 'success'
}), localStorage.setItem('lastTimer', Date.now()), this.$router.push('/')) : this.$message.error('登录失败 ' + err.message)
})
} else this.$message({
message: '登录失败!',
type: 'error'
})
}).catch(t=>{
this.$message.error('登录失败 ' + t.message)
}).finally(() =>{
this.isLoading = !1
})
}
}
上述代码在登录过程中会对接口 l
发起一个请求,然后解析请求 l
返回包数据里的 access_token
字段值,在得到 access_token
字段值后,其值再与 Bearer
进行拼接得到新字符串并赋值给变量 r
,其变量 r
再作为另一个接口 p
请求中 Header 的 Authorization 参数值并发起请求。对于最后的验证情况就是接口 p
返回的请求中,其 HTTP 状态码为 200 并且返回包内容为 test
即可验证通过(感情第一个接口稍微有点打酱油了……)。
下面开始实战测试验证!
验证测试
打开网站,开启 Burpsuite ,在 Burpsuite 中开启请求拦截,然后在网站的登录处随意输入登录用户名和密码,然后点击登录,此时 Burpsuite 将请求响应包拦截,如下图:
修改返回包里的的内容:
对返回包里新增一处 access_token
字段值,内容随意即可,然后放包,在对下一个接口的请求响应包拦截,如下:
此时可以看到在该请求的 Header 处多了一个 Authorization
字段头,其内容就是上述分析的那样,将上个请求的 access_token
值与 Bearer
拼接后生成的新字符串交于该接口进行验证。此时我们修改接口返回包的内容,如下:
上述的原始响应包与修改后的返回包已放一起对比,主要修改返回的 HTTP 状态码并增加返回包的数据字符串 test
,然后放包,发现并未进入对应的后台页面……
此时一脸疑惑的我打开浏览器的控制台查看缘由,在控制台处发现给出了解释:
原因是验证的接口与网页不是同源的(接口是另一个端口验证的),且网站由于设置了同源策略,故被浏览器拦截了跨域请求。此时浏览器也给出了相关解释,其原因是:CORS 头缺少 Access-Control-Allow-Origin
。
于是乎,开始着手绕过 CORS 跨域。
关于 CORS 跨域资源共享,后期会单独出一文详细的介绍一下这玩意儿。
绕过 CORS 跨域
CORS 跨域,其主要是验证服务器端返回包的 Header 头中的相关字段值。由于上述我们可以伪造返回的数据包内容,以此欺骗浏览器对数据的处理,那么我们也同样可以伪造服务器端返回包的相关 CORS 头的字段值内容,再次欺骗浏览器,使得浏览器以为服务器端允许我们可以这样跨域访问。
此时我们可以通过在返回包中 Header 头处添加伪造一个 Access-Control-Allow-Origin: *
字段 ,*
则表明该资源可以被任意外源访问。然后再次模拟一遍上述流程,此时我们再对比一下原返回包的内容与修改的内容:
放行该包,成功绕过登录逻辑验证与 CORS 跨域,浏览器也成功跳转到了后台界面,其功能也都可正常点击访问:
小结
造成本次的前端登录验证绕过,主要缘由还是接口未授权访问漏洞引起的,因为加载后台界面中的相关的资源无鉴权,不需要身份的验证即可访问。而登录逻辑处的 js 代码分析与绕过,起到一个引子作用,使其绕过了前端的登录逻辑。若接口存在身份鉴权,即使前端登录的逻辑绕过了,由于没有正确的身份,其接口也不会返回数据,那么其结果是要么卡在登录页、要么停留在什么都没有的后台空白页或再次重定向到登录页,当然此处也是看后端的处理逻辑的写法。瑞思拜!
暂无评论
大佬,像那种网页上登陆才能查看的会员文档,或者是那种要关注公众号验证才能查看的文档,这种一般用什么思路破解?