前言
首先来介绍一下这个伪协议,JavaScript伪协议最重要的,其实就是可以用来执行js的代码,哪些地方可以用呢,
a标签的href里面,iframe标签的src里面,甚至form标签的action和button的formaction也是可以的
代码语言:javascript复制<a href=javascript:alert(1)>LLLL</a>
代码语言:javascript复制<iframe src=javascript:alert(1)></iframe>
代码语言:javascript复制<form action=javascript:alert(1)>
<button>submit</button>
</form>
<form id=f2>
</form>
<button form=f2 formaction=javascript:alert(2)>submit</button>
危害方式
这里可能会产生这种危害的地方,比如一个网站需要发布一个文章,发文的时候在文中会自动填入一个网址进行嵌入,然后这个功能可能没什么过滤,那么就可以尝试插入javascript伪协议进行xss
代码语言:javascript复制<iframe src="<?= $youtube_url ?>" width="500" height="300"></iframe>
当然也有可能会进行网址内是否包含正常网站的检查,我们也可以绕过,这里比如会对网址内是否包含youtube.com进行检测,就可以使用javascript:alert(1);console.log('youtube.com')绕过。
正确的方式是检测网址是否为上网址的格式,并且确保是https开头
这里有一个后端存在这种漏洞案列
代码语言:javascript复制<a href="<?php echo htmlspecialchars($data) ?>">link</a>`
这里虽然将<>";做了编码,但是没办法新增标签,也没办法跳脱引号新增属性,但是攻击者可以插入javascript伪协议
vue中案例:
代码语言:javascript复制<script setup>
import { ref } from 'vue'
const link = ref('javascript:alert(1)')
</script>
<template>
<a :href="link">click me</a>
</template>
如果是跳转登录的话,也会产生这种类型的漏洞
页面重定向一般来说使用
代码语言:javascript复制const searchParams = new URLSearchParams(location.search)
window.location = searchParams.get('redirect')
问题在于window.location的值也可以是javascript伪协议
这里举一个案例:
这是一个登录页面,
点击登入之后,会出现一个redirectToTarget 的 function ,而这个的源代码是这样
代码语言:javascript复制export const redirectToTarget = ({
fallback = 'current',
}: {
fallback?: 'homepage' | 'current'
} = {}) => {
const fallbackTarget =
fallback === 'homepage'
? `/` // FIXME: to purge cache
: window.location.href
const target = getTarget() || fallbackTarget
window.location.href = decodeURIComponent(target)
}
简单阅读下,就是使用了window.location.href机型重定向,如果登录的网址是https://xxxx/login?target=javascript:alert(1)
那么攻击者就会触发xss,这样如果攻击者抓取input的值,也就是账号密码就会泄露。
防御手法
针对这种类型的攻击,仅仅是将javascript过滤是不行的,因为href的内容是可以进行编码的
比如:
代码语言:javascript复制<a href="javascript:alert(1)">click me</a>
比较好的判断方式就是只允许http和https开头的字段,而且利用JavaScript去解析url,比如:
代码语言:javascript复制console.log(new URL('javascript:alert(1)'))
/*
{
// ...
href: "javascript:alert(1)",
origin: "null",
pathname: "alert(1)",
protocol: "javascript:",
}
*/
这样就可以根据protocol普安段是否合法
这里也会有一个错误的写法
代码语言:javascript复制console.log(new URL('javascript:alert(1)'))
/*
{
// ...
hostname: "",
host: "",
origin: null
}
*/
当hostname或者host为空,就代表不合法,但是我们可以利用//在JavaScript中注解方式,搭配一个元素看起来像网址,比如:
代码语言:javascript复制console.log(new URL('javascript://huli.tw/
alert(1)'))
这个在谷歌上没有问题,但是有一些浏览器就会存在问题。
上述这些问题,其实加一个target="_blank"就可以解决大部分问题,只需要重启一个新页面,浏览器会处理好很多问题。
实际案例
这里是一个23年6月telegram的漏洞,网页版中,有一个ensureProtocol函数,负责确认url有没有://,没有的话就加上,
代码语言:javascript复制export function ensureProtocol(url?: string) {
if (!url) {
return undefined;
}
return url.includes('://') ? url : `http://${url}`;
}
要绕过就很简单,我们只要加上javascript:alert('://')
但是这里浏览器解析也会分析url是不是合法的网址,而url本来最前面就可以带上账号和木马,中间就是使用:进行分割。像这样:
https://username:password@www.example.com/
因此攻击者发现可以用这样的字符串来绕过
代码语言:javascript复制javascript:alert@github.com/#://
这里javascript伪装成账号,而alert是密码,后面的hostname是github,后面://进行绕过,虽然前面没有http或者https,但是仍认为合法.
最后搭配url,进行编码,产生出一个密码只有合法的网址
代码语言:javascript复制javascript:alert('Slonser was here!');//@github.com#;alert(10);://eow5kas78d0wlv0.m.pipedream.net'
解码之后就是
代码语言:javascript复制javascript:alert('Slonser was here!');//@github.com#;alert(10);://eow5kas78d0wlv0.m.pipedream.net'
这里字符串会被服务器判断为一个链接类型,同时://也逃过检测,攻击者点击就会触发。