危險的文件夾上傳框


分享一篇老文章。前些年在 ASRC 上的看了一篇文章「Html5新功能上的ui-redressing」后,寫的一些改進方案。


文件對話框

文件上傳對話框是一直以來就存在的網頁控件。

到了 HTML5 時代,增加了更多的功能,例如支持文件多選。Chrome 甚至還支持「上傳文件夾」這一私有特征:

<input type="file" webkitdirectory />

在給用戶方便的同時,其安全隱患也逐漸出現。用戶平時在下載時,理所當然的彈出的是保存對話框,因此常常不仔細看就做出了選擇。

這極有可能被攻擊者所利用。一些惡意網站在用戶點擊下載時,故意彈出一個上傳對話框。只要用戶一疏忽,就把選中的文件夾給上傳了!

下載對話框

上傳對話框

當然,僅僅依靠默認的上傳,這種攻擊方式仍有較大難度。因為整個文件夾可能非常大,上傳需要很久的時間。

如果用戶等了半天也沒看見下載進度,或許就會刷新重試,甚至放棄了。

選擇即授權

然而,HTML5 帶來了一個新的規范 —— File API,允許腳本訪問文件。

但由於沙箱限制,腳本無法訪問任何一個本地文件,除非用戶主動授權。如何授權?最常見的,就是「上傳對話框」了。

事實上,如今的上傳對話框,早已不是從前「選擇哪個文件」的功能,而是「允許腳本訪問哪個文件」的權限申請!只不過界面上沒有提示罷了。

例如,一個傳統的文件上傳控件。當用戶選中文件后,即可通過 File API 讀取文件內容:

<input id="dialog" type="file" />
<script>
    dialog.onchange = function(e) {
        var reader = new FileReader();
        reader.onload = function() {
            console.log(this.result);
        };
        reader.readAsText(this.files[0]);
    };
</script>

或許你已注意到,File 位於files[]而不是file,這正是給文件夾預留的!

在 Chrome 里,上傳控件只要加上 webkitdirectory 屬性,就變成文件夾選擇框。這時一旦用戶選中某個文件夾,瞬間就賜予腳本訪問整個文件夾的權限!

<input id="dialog" type="file" webkitdirectory />
<script>
    dialog.onchange = function(e) {
        var files = this.files;
        var table = {};

        for (var i = 0; i < files.length; i++) {
            var f = files[i];
            var dt = new Date(f.lastModified);

            table[i] = {
                path: f.webkitRelativePath,
                size: f.size,
                modified: dt.toLocaleString()
            };
        }

        console.table(table);
    };
</script>

演示

於是,用戶本想將文件保存在桌面上,結果卻將桌面上的所有資料,被攻擊者的腳本拿到!

優化上傳

一旦腳本可主動訪問,我們可以用更靈活的方式處理這些文件,無需再用傳統落后的方式上傳。我們可以直接在前端分析出「有價值」的文件,例如:

  • 備注文件、腳本、批處理、電子表格等,很可能存有一些敏感信息,而且體積小價值大,優先將其上傳;

  • 圖片則可通過 canvas 縮放,先傳較小的縮略圖。當接收端發現有意義時,再傳輸原文件。

  • 對於一些體積較大但意義不大的文件,則可以直接忽略。

由於 HTTP 上傳是沒有壓縮的,因此在傳輸文本文件時效率很低。我們可以借助 Flash 內置的LZMA壓縮算法,極大提升傳輸效率。如果不支持 Flash,也可以使用asm.js版的 LZMA 壓縮器,配合Worker線程在后台壓縮和傳輸。

同時,將多個小文件合並后再壓縮,可進一步提高壓縮率。再多開幾個連接,上傳速度即可大幅提升。

續點上傳

不過即使再優化,仍有傳不完的可能。因此,我們得將沒傳完的內容儲存起來,當用戶再次回來時,繼續傳輸。

得益於 HTML5 的 Storage API,這不難實現。

事實上,當用戶授權了某個文件夾時,我們首先要做的不是發送,而是讀出文件夾內容,立即備份到 Storage 里。畢竟,文件的讀取需要用戶主動配合,機會是非常珍貴的;而 Storage 的訪問則無需任何條件。

當備份完成后,再從 Storage 里一塊一塊的讀取、發送、刪除。這樣,即使中途頁面刷新或關閉了,下次回來時,仍能從 Storage 中繼續。

考慮到每個域的 Storage 容量有限,我們可以使用iframe嵌入多個不同域的頁面,然后通過postMessage進行數據的分發和匯總,這樣就不受容量限制了。

當然,能不能無限容量還得看瀏覽器策略,不然硬盤會被撐滿

將數據存放在 Storage 里還有另一個好處,即使用戶永不回來,但數據仍持久保存着。只要以后一旦進入其他的站點,只要是我們可控的,仍有機會繼續上傳。(例如將用戶引到我們布置了 XSS 的站點上)

延長上傳

在之前《延長 XSS 生命期》 中介紹過,可以使用各種黑魔法來提升腳本有效期。

利用這個原理,即使當前頁面關閉,其他關聯的頁面也能繼續上傳。事實上,除了文中提到的方法,如今還有一個新的 API —— SharedWorker,它可以讓 Worker 共享於多個頁面,只要有一個存在,線程就不會停止。可以讓續點時丟失的數據更少。

視覺欺騙

由於上傳控件有着獨特的界面,怎樣才能讓用戶不小心點到呢?萬能的方法是點擊劫持(Clickjacking)。

不過本場景無需這么麻煩,只需簡單的調用控件的click方法就可以了。

<a href="ed2k://|file|xxx.avi" id="download">高速下載</a>
<script>
    var uploader = document.createElement('input');
    uploader.type = 'file';
    uploader.webkitdirectory = true;

    download.onclick = function(e) {
        uploader.click();       // 彈出上傳對話框
        e.preventDefault();     // 屏蔽下載對話框
    };
</script>

演示

我們屏蔽超鏈接的默認行為,用上傳控件的點擊事件取而代之,即可召喚出「文件夾授權」對話框了!

同時,為了不讓眼亮的人發現對話框上的破綻,我們使用一些第三方的下載方式,例如電驢、迅雷等等,讓人們誤以為就是這樣的。

當然,這只是一個小例子。只要頁面做的真實,下載內容引人入勝,用戶自然就會中招。

后記

本以為 webkitdirectory 的這個私有屬性很快就會放棄,至少是更強的安全提示。不過至少現在也沒更新,因此上網時還是要多加留心,看清楚了再做決定。

當然,這篇只是最早的「交互欺騙」探索。事實上,深入挖掘會發現可利用點遠不僅此。

如今的瀏覽器在視覺、音頻上的體驗已經非常完善,攻擊者甚至可以在網頁里,高度模擬一個本地應用的交互效果。讓用戶誤以為是瀏覽器之外的程序彈出的界面,從而進行釣魚。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM