前言#
以前一直没有把XSS当做一个很重要的漏洞去学习,也许是国内一直不大重视XSS(犹记得初中高中那段时间的src基本上都不收)至少在我的印象里是这样子,但是作为一个CTFer,国际赛上更多出现的是nodejs,xss等前端问题的题目,于是为了能适应国外xss的赛题,又或者是想精修一下自己的前端,于是系统的学习一下。
一些简单的叙述#
一个小小的XSS#
对于XSS的简介,我就不详细概述了,我也不会想再捋一遍XSS的前世(毕竟我历史不好bushi) 那么我们来举一个最最简单的XSS的例子
<?php
echo "Hello". $_GET['name'];
?>
如果正常的去传参,浏览器也只会解析为
Hello Delete
传入<scrpit>alert(hacked by D05TL3)</script>
自然而然的就拼接为
Hello <scrpit>alert(hacked by D05TL3)</script>
payload角度#
对于这个地方,引用huli师傅的看法,我们需要考虑到两个角度
Angle1-内容是如何被放到页面上的#
这里huli师傅给出了另外一个例子
<div>
HEllo,<span id = "name"></span>
</div>
<script>
const qs = new URLSearchParams(window.location.search)
const name = qs.get("name")
document.querySelector('#name').innerHTML = name
</script>
同样的这里一眼就能看出来:name=<scrpit>alert(hacked by D05TL3)</script>
即可,但是这里的script不会在innerHTML插入中被调用。
所以就是我们在研究xss的时候也要看一下xss是如何触发,是怎么放上去又是怎么被解析的。
Angle2-Payload有没有被储存#
顾名思义,我们前面的例子都是没有被存储下来的,这一点其实我认为不用太过于纠结有没有存储到,因为在CTF中,大部分很少会给你存储型的XSS,除了环境确实是这么出的,但是我也不否认在真实的攻击场景中需要这种可持续性的漏洞存在,也可以说是worm XSS,这里不过多赘述
起步,从第一个XSS开始#
常见的触发方式#
JS event handler#
其实这个地方也可以说是关于对JS event handler的使用 对于前面的一个例子
<div>
HEllo,<span id = "name"></span>
</div>
<script>
const qs = new URLSearchParams(window.location.search)
const name = qs.get("name")
document.querySelector('#name').innerHTML = name
</script>
这里没办法用<script>
来xss,所以这个地方就可以用到我们的event handler,举个例子<img src=x onerror="alert(1)">
。
所以这里可以列举一些常见的event handler
- onerror
- onload
- onfocus
- onblur
- onanimationend
- onclick
- onmouseenter 然后如果想了解更多payload: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet
然后这里huli师傅也提到了一点,就是可以不需要用到'
和"
,就像这样
<svg/onload=alert(0)>
~~自问自答环节
but这个地方如果用不了event handler呢? solved:我们可以使用iframe+srcdoc(犹记得似乎在n1junior2025出现过)
属性注入(EZ bypass)#
demo
<div id="content"></div>
<script>
const qs = new URLSearchParams(window.location.search)
const clazz = qs.get('clazz')
document.querySelector('#content').innerHTML = `
<div class="${clazz}">
Demo
</div>
`
</script>
这个时候我们就可以闭合掉这个尖括号
"><img src=x onerror=alert(1)
方式二:" tabindex=1 onfocus="alert(1)" x="
,我们可以选择不闭合,直接往里面加入更多的属性,并且包含这个event handler
javascript:伪协议#
使用场景#
- a标签
<a href=javascript:alert(1)>Link</a>
- iframe标签
<iframe src=javascript:alert(1)></iframe>
- form标签
<form action = javascript:alert(1)>
<button>submit</button>
</form>
<form id=f2>
</form>
<button form=f2 formaction=javascript:alert(2)>submit</button>
一个小小的demo#
<iframe src="<?= $youtube_url ?>" width="500" height="300"></iframe>
可以看到这里有一个地方可以插入网址,但是这里可以把javascript:alert(1)当做网址输入,同样的写法在react和Vue里都可以实现 react:
import React from 'react';
export function App(props) {
// 假設底下的資料是來自於使用者
const href = 'javascript:alert(1)'
return (
<a href={href}>click me</a>
);
}
Vue:
<script setup>
import { ref } from 'vue'
const link = ref('javascript:alert(1)')
</script>
<template>
<a :href="link">click me</a>
</template>
除了简单的iframe中,我们实现页面跳转也可以使用这个伪协议,类似于这样
const searchParams = new URLSearchParams(location.search)
window.location = searchParams.get('redirect')
问题在于window.location他的值也可以是伪协议
在waf上面#
在huli师傅的叙述中,似乎很难对伪协议做一个过滤,因为他的使用范围非常的广,比如你去正则JavaScript:,但是html可以用实体编码绕过,Like n1junior的display一样,总的来说,如果要防御伪协议的出现,
建议是加上target="_blank"
这个属性,或者用上一些library
最后#
huli师傅留下了一个题目,先思考一下
// 這是一個可以在 profile 頁面嵌入自己 YouTube 影片的功能
const url = 'value from user'
// 確保是 YouTube 影片網址的開頭
if (url.startsWith('https://www.youtube.com/watch')) {
document.querySelector('iframe').src = url
}
这里有什么安全问题?