深入探讨跨站脚本攻击





5.00/5 (1投票)
跨站脚本攻击及其潜入我们程序的方式,以及不同跨站脚本输入字符串的样式
引言
这是关于跨站脚本(XSS)系列文章的第二篇。在这篇文章中,我将探讨跨站脚本以及它如何悄悄地潜入我们的程序,以及不同类型的跨站脚本输入字符串是什么样子的。一旦我们对XSS有了深入的理解,我将在下一篇文章中向你展示如何利用XSS漏洞,最后将介绍如何规避输入过滤器以及如何修复跨站脚本漏洞。你可以在这里查看本系列的第一篇文章。
我提供了一个教程Web应用程序,以便你可以跟着这篇文章进行操作,这个功能齐全且可用的Web应用程序可以在这里找到。
跨站脚本
我们主要有5个输入区域可能引入跨站脚本漏洞。它们是:
- 表单上的隐藏字段
- 会话存储
- Cookie存储
- 注入文件
- 注入Cookie
其中一些区域由于各种原因会更加严重和具有破坏性,我将在后续探讨。一个有趣的旁注是,如果你注意下面的截图,或者自己尝试一下。当我将ZAP攻击代理指向http://exploits.howellsonline.ca/xss/moderate/default.aspx时,ZAP攻击代理没有检测到任何跨站脚本漏洞,如下图所示。因此,QA或开发人员仅仅使用攻击代理并期望通过这种方式找到所有漏洞是不够的,攻击代理只是工具箱中的一个工具,不应该取代实际测试。此演示的示例已在Firefox中测试并运行。在其他浏览器中效果可能会有所不同。请注意,WordPress似乎在JavaScript攻击字符串正确渲染方面存在一些困难。你可以在这里找到本教程的攻击字符串。
隐藏输入字段
HTML表单上的隐藏输入字段用于存储对用户隐藏的输入。本质上,演示中的第一个示例是设置一个隐藏输入并回发,在回发时,读取隐藏表单的值并显示给用户。正确使用时,一切都按预期工作,如下图所示。
我们将用于演示此漏洞的攻击字符串是
<script type="text/javascript">// <![CDATA[
alert("XSS")//<
// ]]></script>
然而,当此输入用于恶意目的并发现跨站脚本漏洞时,请看下面图像中的结果。要自己尝试下面的示例,请在文本框中输入以下string
。
现在让我们检查导致此漏洞的代码。
客户端
JavaScript
<script type="text/javascript">// <![CDATA[
function populateData()
{ form1.elements["m_hdnInpt"].value = document.getElementById("m_txtHiddenField").value;
}
// ]]></script>
服务器
if (Page.IsPostBack)
{
Response.Output.WriteLine("Your input from example was: " + m_hdnInpt.Value);
}
问题所在显而易见,我们在客户端用用户输入的值填充一个隐藏输入,然后明目张胆地将该值写回服务器端的原始HTML中以重新显示给客户端。请注意,ZAP未将其检测为跨站脚本漏洞。服务器不应该盲目信任来自客户端的输入。
会话存储
在这个例子中,如果你愿意,可以首先在演示应用程序中测试正常输入是否按预期工作,我知道它确实如此,所以我将直接进入漏洞部分。第一个例子和这个例子之间的区别在于,第一个例子中表单回发到*Default.aspx*,而这个例子中表单重定向到*SessionInpt.aspx*,这个例子和其余的例子与本系列的第一篇文章类似,都是数据在两个页面之间传递。
我将用于此示例的攻击字符串
是
<<script type="text/javascript">// <![CDATA[
alert("XSS");//<
// ]]></script>
从下图中可以看出,跨站脚本漏洞已成功被利用。
现在我们来检查一下代码。
服务器端
Session["SessionInpt"] = m_sessionInpt.Text;
Response.Redirect("/xss/moderate/SessionInpt.aspx");
重定向服务器
Response.Output.WriteLine("Your input for example 2 was: " + Session["SessionInpt"]);
正如你所看到的,*Default.aspx* 盲目地将用户输入的数据扔进一个会话变量,而 *SessionInpt.aspx* 欣然接受,相信会话中的一切都是安全的。如果我作为领导职位审查这段代码,我会对 *Default.aspx* 没有进行任何输入验证感到不满,我甚至会更不满 *SessionInpt.aspx* 没有进行任何输入验证并且盲目信任输入。这种情况可能发生在两个开发人员分别处理这些页面时,他们各自信任对方来处理安全问题。这也是进行安全审查的绝佳理由。在两个页面上都进行验证是最好的,并且建立在深度防御的概念之上。
Cookie存储
这个例子是我最喜欢的例子之一,原因在于它能以多种方式真正影响用户。在此之前,前两个例子和上一篇文章本质上都是一次性跨站脚本攻击。在这个例子中,我们实际上要将攻击存储在一个 cookie 中。
我们将用于此攻击的攻击字符串是
<<script type="text/javascript">// <![CDATA[
alert("XSS Attack")//<
// ]]></script>
如下图所示,漏洞利用成功。
更令人不安的趋势是,因为这个漏洞利用被保存在 cookie 中,所以每次用户访问该页面时,他们都会重新看到这个漏洞利用。如果你正在演示应用程序中操作,我建议你在 *Default.aspx* 上输入攻击字符串。当你被重定向后,复制你被重定向到的 URL,在你的 URL 栏中导航到另一个 URL,比如 Google。一旦 Google 加载,将你复制的重定向 URL 粘贴回你的浏览器。注意漏洞利用是如何立即重新出现并会持续重新出现,直到你清除你的 cookie。如果你想清除你的 cookie 并再次尝试,请选择删除 cookie 按钮。
让我们来检查一下代码
服务器端
Response.Cookies["UserInpt"]["Data"] = m_txtCookieInpt.Text;
Response.Redirect("/xss/moderate/CookieInpt.aspx");
重定向页面
String txt = Request.Cookies["UserInpt"]["Data"];
Response.Output.WriteLine("Your input for example 3 was: " + txt);
如您所见,*Default.aspx* 在单击按钮时会放置一个 cookie,*CookieInput.aspx* 会接收该 cookie,并不断从 exploits.howellsonline.ca 的 cookie 中读取漏洞利用信息,直到 cookie 被清除,因此用户会不断看到此漏洞利用,这可能会让他们远离您的网站。
注入文件
这是跨站脚本攻击的一种更高级形式,攻击者试图通过注入自己的JavaScript文件来在你的网站上执行一些恶意代码。
对于这个例子,我们的攻击字符串将是
请看下面显示的输出
这和你输入到文本框中的内容完全不同,是吗?攻击者是如何做到这一点的?让我们看看代码:服务器端。
Session["Injected"] = m_txtBoxInjected.Text;
Response.Redirect("/xss/moderate/InjectedXss.aspx");
重定向页面
String txt = (String)Session["Injected"];
Response.Output.WriteLine("Your input for example 4 was: " + txt);
我们还需要查看一个更重要的文件,那就是我们注入的脚本,可以在这里找到。
注入的脚本
document.writeln("Hello site you have an injected suffered an XSS attack");
这次攻击非常简单,本质上我成功地让我的注入脚本运行起来。之所以能够实现,是因为重定向页面盲目地接受了会话中的输入,并且未经验证就立即将该输入写入了HTML。当它这样做并且页面渲染时,页面去发出了一个HTTP GET
请求,获取了注入的URL,一旦收到请求的脚本,脚本就执行并破坏了我的网页。有趣的是,到目前为止,我们看到的所有其他攻击都显示了一个消息框,然而这个脚本没有显示消息框,如果不是因为输出,用户根本不会知道这个脚本已经执行了。
注入Cookie
下一个示例演示了我们已经看到的几种攻击向量的组合,在这个示例中,我们将一个脚本注入到 cookie 中,每次页面加载时,页面都会像上一个示例一样下载该脚本。
我们将用于此攻击的攻击字符串是
如你所见,攻击成功了。
到目前为止,你可能已经注意到了一个模式,所有之前的例子都使用了这种模式
Response.Output.WriteLine(Some OUTPUT)
那么,只要你不使用这种模式,你就相当安全,这是理所当然的。直到这个例子,你都是对的。
让我们看看代码
服务器端
Response.Cookies["UserInpt"]["Deface"] = TextBox1.Text;
Response.Redirect("/xss/moderate/InjectedDefaced.aspx");
注入篡改
<script type="text/javascript">// <![CDATA[
function handleCookies() {
var cookie = document.cookie;
var cookieValue = cookie.substr(cookie.lastIndexOf("Deface=") + 7, cookie.length - 7);
document.write("Welcome back: " + cookieValue);
}
// ]]></script>
注意到这里的不同了吗?区别在于当HTML正文加载时,cookie是从客户端读取的。所以漏洞实际上发生在客户端,而不是在后端服务器端代码中,因此请求不必返回服务器再返回客户端。
最后
第五个例子最真实的危险在于,一旦攻击者将他们的 JavaScript URL 存储在某个地方,比如一个 cookie 中,他们就可以修改 JavaScript 来对毫无戒心的用户做任何他们想做的事情。所以,也许我的 JavaScript 文件可能有一个月什么都不做。然后我让它开始窃取用户在你的网站表单中输入的数据。因为我的攻击向量的 URL 存储在你的 cookie 中,所以我几乎拥有完全的控制权,可以为所欲为。
我希望这个故事的寓意是不要相信未经验证的用户输入。我演示了用户输入可能来自的5个来源,如你所见,所有这些输入在显示给用户之前都应该经过适当的验证。我甚至建议在输入时和向用户显示之前都进行验证,但至少在写入HTML之前应该进行验证。
在我的下一篇文章中,我将向你展示如何利用这些漏洞,然后是如何修复它们,以及潜在攻击者将如何尝试绕过你的过滤以及你能为此做些什么。