天创培训:您身边的信息安全培训专家!
技术中心
从黑客角度深入剖析跨站请求伪造

在对Web应用程序进行测试的过程中,我们使用的自动化工具有时候会漏掉一些非常严重的安全漏洞。跨站请求伪造便是其中之一。因此,对于Web应用测试来说,通过人工方式分析代码是非常重要的一种测试手段。
在近期的Web应用测试过程中,我利用手工方式发现了若干个CSRF漏洞,如果将其串联使用,就能演变成更加严重的安全漏洞。下面,本文将详细介绍这些漏洞的发现过程,并演示如何利用它们来控制受害者的账户。需要注意的是,为了保护客户信息,部分内容已经做了模糊处理。
0×01 艰难的发现之旅
为了展开跨站请求伪造攻击,首先要进行一些侦查工作。为此,我根据客户提供的低权限账户登录了需要测试的Web应用。在这个网站上,大部分页面都要求预先知道帐户名、产品序列号等信息。由于我对这些一无所知,所以只好转而考察账户简介页面。这个页面允许用户更新他们的帐号信息,例如姓名、电子邮件地址、城市、省份,等等。
对于浏览器,我通常选择Firefox,这是因为可以使用TamperData插件来方便地操作表单的字段,来观察是否发生有趣的事情。比如,我经常在引号或星号之间插入一些奇怪的数据,来探索各种注入攻击。就本例而言,我发现了一个会随其他账户信息一同传输的隐藏表单字段,这个字段的名称为_RequestVerificationToken。我不断摆弄这个字段,但是就我观察而言,并没有找到注入漏洞。但是,我却注意到了一个重要的事情,那就是这个站点好像并不在意我对这个字段所做的各种修改。

这对我来说是非常奇怪的。根据这个字段的名称来看,它好像是用来验证每个提交的请求是否来自该网站实际用户的一项安全措施。这通常是用来防止跨站请求伪造(CSRF)攻击的,但是,这个页面看上去并没有验证这个令牌,这就意味着这个信息更新表单容易受到CSRF的影响。
在默默记住这一点之后,我开始继续测试该网站的其他部分。我想,如果一个页面有这种漏洞,那么其他页面也很可能有这种漏洞。不幸的是,事实并非如此。对于所有其他页面,只要修改这个令牌就会导致服务器错误,并且表单也无法进行处理。也就是说,除了个人信息页面之外,其他地方的大门都被堵上了。接下来,我们就要想办法利用它来进行更加邪恶的事情。
0×02 忘记密码
就像某些网站一样,这个站点也提供了一个“忘记密码”功能。这个功能的初衷,当然是为哪些忘掉密码的用户提供帮助的。这个自动化系统非常简单,用户将其用户名输入到相应的表单字段,然后点击提交按钮即可。如果用户名是有效的,系统就会给该用户的账户生成一个新的随机密码,并将其发送至该用户账户绑定的电子邮箱中。这个功能不仅对于用户来说非常简单,同时,也为黑客提供了极大的方便。
我意识到,如果我可以利用CSRF漏洞把用户的电子邮件地址更新为自己的邮箱地址,那么,我就能够利用忘记密码功能令系统把用户的密码发给我,而不是用户。不幸的是,我很快就遇到了一个难题。
客户(这里指example.com)提供给我的测试帐户所绑定的电子邮件地址中含有客户的域名,如bob@example.com。虽然系统允许将这个电子邮件地址改为我想要的地址,但是这里有一个问题,当让系统向我的电子邮箱发送新密码时,实际收到的却是一个服务器错误。不知道什么原因,它似乎只喜欢类似bob@example.com这样的邮箱。
经过进一步的手工测试后,我发现它可以接受任何用户名,只要是以example.com结尾即可。我不知道到底为何会发生这种情况,因为在系统中的其他帐户包含了来自不同域的电子邮件地址。我觉得,这背后肯定还进行了某些额外的安全验证,比如根据白名单对比电子邮件地址的域名等。但是,我的个人电子邮件地址或域名肯定是不在这个表中的。因此,我们需要借助于下面介绍的技巧。
0×03 Gmail来解围
我有一种预感,即电子邮件地址的白名单匹配过程是可以破解的,后来事实证明,我的感觉还是很准的:我发现,任何电子邮件地址都是可以绕过白名单的,只要该地址的邮箱名称部分含有“example.com”字样即可。举例来说,example.com@otherdomain.com就完全可以达到我们的目的。因此,我们只需要建立一个邮箱名部分含有example.com的电子邮件账户就行了。
现在,该Gmail上场了。之所以选择Gmail,是因为它允许Gmail用户名之后、@符号之前使用加号,这个特性对我们来说非常有用。举例来说,如果你的电子邮件地址是rick@gmail.com,那么你也可以使用rick+test@gmail.com来接收邮件,实际上邮件还是会发送到rick@gmail.com邮箱中,因为Gmail会忽略加号和@符号之间的所有内容。
考虑到这一点,我又登录到Web应用程序进行测试,并把帐户的电子邮件地址更新为“rick+example.com@gmail.com”。果然,这种方法确实是可行的,密码重置功能给我发来了一个新的密码。既然如此,下面就该从poc验证这个CSRF漏洞的可利用性了。
0×04 一个简单的Web表单
由于这个有漏洞的应用使用POST方法来提交数据,所以我必须构建自己的表单来达成目标。由于这里只是一个poc,所以不要求太过精致。我建立的这个表单含有一些隐藏的文本字段,当个人简介更新时,所有的表单字段都会被传送给这个Web应用。这其中包括一个CSRF令牌,不过由于这个应用没能有效对其进行验证,所以该令牌的值实际上是无关紧要的。这里最为重要的部分是EmailAddress字段,虽然这个字段是隐藏的,但是却包含一个重要的缺省值,即rick+example.com@gmail.com。
 action="https://www.example.com/account/EditProfile/" id="EditProfile" method="post" name="EditProfile">     name="__RequestVerificationToken" type="hidden" value="ANYTHING CAN GO HERE" />
      id="PasswordChgRequired" name="PasswordChgRequired" type="hidden" value="No" />

  id="UsersName" name="UsersName" type="hidden" value="ANITIAN" />
      id="returnURL" type="hidden" />
      class="userName text-box" id="FirstName" maxlength="25" name="FirstName" style="width:160px" type="hidden" value="Anitian" />
      class="userName text-box" id="LastName" maxlength="25" name="LastName" style="width:160px" type="hidden" value="Anitian" />
      id="Phone" name="Phone" style="width:160px" type="hidden" value="" />
      id="Phone" name="Phone" type="hidden" value="" />
      id="PhoneExt" maxlength="5" name="PhoneExt" style="width:160px" title="1 to 5 numbers. " type="hidden" value="     " />
      id="PhoneExt" name="PhoneExt" type="hidden" value="     " />
      class="text-box" id="City" maxlength="20" name="City" placeholder="Office Location" style="width:160px" title="Enter the city of your office location." type="hidden" value="Winterville" />
      id="EmailAddress" maxlength="50" name="EmailAddress" style="width:394px" title="Email is limited to 50 characters." type="hidden" value="rick+test.com@gmail.com" />
      class="jobFunc" id="JobFunction2" name="JobFunction2" type="hidden" value="Vice President" />
      class="jobFunc" id="JubFunction" name="JobFunction" type="hidden" value="ACCOUNT HACKED">
     Click this awesome button!
      id="submit" type="submit" value="Save" />
在这个表单中,唯一可见的部分就是一个提交按钮。为了进行poc,我将这个新的网页加载到了自己本地的计算机上。对于实际的攻击者来说,他们可以将其托管到互联网上的某个Web服务器上面,并且很可能是他们已经成功入侵的系统上所搭建的服务器。一旦加载之后,受害者将会看到如下所示的页面:

当然,这个页面看起来有些简陋,但是对于poc来说已经足够了。接下来,我们必须自己来扮演受害者了。为此,我首先打开了浏览器,并使用正常账号登录上这个Web应用,这一切跟正常用户没有什么区别。然后,我将前面的恶意表单页面上载到了另一个标签中。攻击者很可能会利用电子邮件将这个链接发给我,然后,我单击了这个按钮,象期望的那样,攻击成功了。

这个恶意网页会把所有信息递交给了目标站点,并按照要求更新了电子邮件地址字段。需要引起注意的是,即使目标页面已经在受害者浏览器中被关闭的情况下,这次攻击依然能够得手,因为只要用户的会话依然是打开的,那就足够了。许多时候,一个会话可以在很长一段时间保持打开状态,即使网页关闭之后亦是如此。现在,攻击者唯一需要去做的就是,利用密码重置功能通过电子邮件获得受害者(这里就是我们自己)的新密码了。

这样一来,受害者的账号就完全处于我们的控制之下了。
0×05 进一步改进攻击手法
需要注意的是,该攻击手法具有两个缺点,一是要求受害者参与其中, 因为受害者至少需要访问一个恶意页面,这样该手法才能成功。就本例来说,受害者还必须按下一个按钮,不过,利用Javascript可以实现在页面加载时自动完成表单的提交动作。
另一个问题是,你必须知道受害者的用户名。为了利用忘记密码功能,我们必须知道受害者的用户名才行。幸运的是,我发现了另外一个办法,可以巧妙地绕开这个问题。
0×06 隐秘的表单字段
我注意到,个人简介的更新页面还会提交一个用于UsersName的字段。这引起了我的兴趣,因为页面本身是无法修改这个用户名字段的。这是一个静态的条目,即总是保持不变。出于好奇,我决定再次尝试通过Tamper Data来对这个变量进行修改。让我惊讶的是,它居然奏效了。实际上,该网站不仅接受了我改过的用户名,而且连该帐户的用户名也改过来了。当然,我们无法将用户名更新为业已存在的那些用户名,但是我们可以更新为随机选择的、不常用的用户名,这是很容易成功的。

为了利用这一点,我们只需更新自己的恶意页面中的表单,让其username字段包含一个新的、当前未曾使用的值即可。现在,当受害者点击了该提交按钮之后,不仅他们的电子邮件地址会被更新,而且就连他们的用户名也会变成我们之前设定的值。这就意味着,这个链接可以发送给任何已经登录该网站的人,他们的账户都可能会失窃。再进一步,我们甚至可以将这个过程脚本化,这样就可以成批地把不同的表单发送给不同的人员。只要他们轻轻点击一下,就会全部中招。
0×07 如何保护自己
那么,我们如何保护自己免受此类攻击的威胁呢? 实际上,这种攻击之所以会发生,主要是由于两个核心漏洞导致的,下面我们就介绍如何处理这个问题。
在这里,最严重的问题就是CSRF攻击,如果没有它,就不会发生后面的事情了。为了防御CSRF攻击,OWASP组织已经提供了多种不同的方法,不过最为安全的方法就是在隐藏字段中使用一个唯一的令牌。这也是我们这个示例网站所试图去做的。如果能够彻底实施的话,CSRF攻击的得手机会就会大大降低。但是,只要有一个页面没有彻查该令牌的话,攻击者仍然有机可乘。这是因为对于他们来说,只要有一个漏洞就足够了,并且不要忘了,那些心怀不轨的家伙们可有的是时间。
另一个问题与用户输入验证有关。尽管这个系统看上去会利用某种白名单来检查电子邮件地址,但是这种方法是无效的。它只是检查电子邮件地址中是否存在白名单中的域名而已,只要有就行,不管出现在什么地方。实际上,它应该重点检查电子邮件地址的最后部分,或者对整个电子邮件地址进行更加严格的检查。此外,更为安全的做法是,当用户更新个人简介信息时,应该要求他们重新输入自己当前的密码。当然,在重新输入密码的时候,还可以加入CAPTCHA验证,因为恶意Web表单是无法自动识别CAPTCHA的。
最终的漏洞还涉及到用户输入内容的验证。根据个人信息更新页面的设计来看,这个页面好像并没有打算让用户更新他们的用户名。在原始的表单中,用户名字段是不允许编辑的。如果果真如此的话,那么这个表单根本就不应该将用户名作为一个字段进行提交。即使由于某种原因,必须提交这个字段的话,那么该页面也应该进行重写,以禁止编辑用户名。
0×08 小结
CSRF攻击是攻击者用来访问你的数据的方法之一。不过,只扫描站点漏洞是远远不够的。这需要仔细审查该网站运作方式,以检测更细微的瑕疵。本文通过实例说明,我们不仅能够找出这种缺陷的,并且还能帮助客户防御此类攻击,从而避免出现灾难性的数据破坏。