整合營銷服務商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          Firefox 中一個 Cross-mmap 溢出的利用

          文首發(fā)于看雪論壇:http://bbs.pediy.com/thread-216592.htm

          這篇文章將會探索一下CVE-2016-9066,這是 Firefox 中一個簡單卻有趣(從實際操作的角度來看)的漏洞,可以利用該漏洞來獲取代碼執(zhí)行權(quán)限。

          一段負責加載腳本標簽的代碼中的一個整數(shù)溢出導致了對 mmap 結(jié)束塊的越界寫操作。一種利用方法是在緩沖區(qū)后放置一個 JavaScript 堆以便便溢出到它的元數(shù)據(jù)中來創(chuàng)建一個假的未使用堆單元。然后可以將一個ArrayBuffer實例放置在另一個ArrayBuffer 的內(nèi)聯(lián)數(shù)據(jù)中。ArrayBuffer 對象內(nèi)部可以被任意修改,就產(chǎn)生了任意讀/寫原語。至此,實現(xiàn)代碼執(zhí)行就變的非常簡單了。完整的 exploit針對 macOS 10.11.6 平臺上的 Firefox 48.0.1 進行了測試。

          漏洞

          以下代碼用來加載外部腳本標簽:

          result

          當服務器中的新數(shù)據(jù)到達時這段代碼將被 OnIncrementalData 調(diào)用。這是一個簡單的整數(shù)溢出 Bug,當服務器發(fā)送超過 4GB 數(shù)據(jù)的時候即可發(fā)生。數(shù)據(jù)超過4GB的情況下,capacity 將會環(huán)回(wrap around),接下來的對 mBuffer.reserve 函數(shù)的調(diào)用并不會修改緩沖區(qū)。接下來 mDecode->Convert 函數(shù)將會把數(shù)據(jù)寫入到8GB緩沖區(qū)的尾部(數(shù)據(jù)在瀏覽器中以 char16_t 的形式存儲),這部分內(nèi)存將會通過 mmap 塊備份(對于一個非常大的塊這是通用做法)。

          修補也是相當簡單:

          int32_t haveRead = mBuffer.length();

          這個漏洞第一眼看上不并沒有什么搞頭。它有一個必要條件,需要發(fā)送和申請多大幾個GB的數(shù)據(jù)。正如我們即將看到的,該漏洞在我的 2015 MacBook Pro 上相當可靠的被利用,完成整個利用代碼只需打開頁面用時不到1分鐘。我們接下來先來探索一下此漏洞為什么會在 macOS 上被利用并彈出一個計算器,然后我們來改進一下利用代碼讓它變的更可靠一些,并且占用更低的帶寬(劇透:我們將會使用 HTTP 壓縮)

          操作

          當超過 mmap 區(qū)的溢出發(fā)生時,我們首先關(guān)注的是有沒有可能在溢出的內(nèi)存之后可靠地申請一塊空間。與一些堆分配器相反,mmap(可以看作是內(nèi)核提供的內(nèi)存分配器)是非常具有確定性的:如果沒有合適的內(nèi)存塊,調(diào)用 mmap 兩次將會導致兩次連續(xù)的內(nèi)存映射。你可以用下邊的代碼來嘗試一下。注意,實驗的結(jié)果的異同取決于代碼是運行在Linux系統(tǒng)還是 macOS 系統(tǒng)上。mmap 內(nèi)存區(qū)相較于 macOS 系統(tǒng)上由低向高增長,在 Linux 系統(tǒng)上,是由高向低增長。在本篇文章接下來的部分,我們將會專注于 macOS。Linux 或者 Windows 上應該也可能存在相似的利用代碼。

          #include #include const size_t MAP_SIZE = 0x100000; // 1 MB

          上邊的程序向我們展示了通過簡單的映射所有的內(nèi)存頁直到所有已經(jīng)存在的分頁被填滿,然后通過 mmap 再申請一塊內(nèi)存塊。為了驗證這個過程,我們接下來將會這樣做:

          1. 加載一個包含一段腳本(將會觸發(fā)溢出的payload.js)的 HTML 文檔并且異步執(zhí)行一些 JavaScript 代碼(code.js,實現(xiàn)第3步和第5步)。

          2. 當瀏覽器請求 payload.js 時,使服務器返回一個 Content-Length 為 0x100000001 但是只發(fā)送 0xffffffff 字節(jié)的數(shù)據(jù)。

          3. 接下來,讓 JavaScript 代碼申請多個巨大(1GB)的 ArrayBuffers(在緩沖區(qū)實際寫入前,內(nèi)存不一定被使用)。

          4. 讓服務器發(fā)送 payload.js 剩下的 2 個字節(jié)。

          5. 檢查所有 ArrayBuffer 對象的前幾個字節(jié),其中的某一個應該會包含服務器發(fā)送的數(shù)據(jù)。

          為了實現(xiàn)這個過程,我們需要一些瀏覽器中的 JavaScript 代碼和服務器之間的同步原語。為此,我在 python 的 asyncio 庫智商寫了一個小小的 webserver,它包含一個方便的 Event 對象,用來和協(xié)同程序同步。創(chuàng)建兩個全局事件可使客戶端代碼完成當前任務等待 webserver 進行下一步的操作時通知服務器。/sync 的處理例程如下所示:

          async def sync(request, response):

          客戶端中我是用了同步 XMLHttpRequests 來阻塞腳本的執(zhí)行,直到服務器完成相關(guān)工作:

          function synchronize() {

          這樣,我們就可以實現(xiàn)上邊的場景并且將會看到實際上有一個 ArrayBuffer 對象的開始處包含了我們的 payload 字節(jié)。不過還有一個小小的限制條件:我們只能通過有效的 UTF-16 來進行溢出,因為這是 Firefox 內(nèi)部使用的。我們必須記住這一點。現(xiàn)在剩下的就是找到一些更有趣的事情,用內(nèi)存分配來取代對 ArrayBuffer 的溢出。

          尋找目標對象

          因為 malloc(同樣的 C++ 中的 new 操作)將在某些時候使用 mmap 請求更多的內(nèi)存,所以像這些操作分配的內(nèi)存可能是我們代碼所感興趣的。我走了一條不同的路線。最初我想檢測一下是否有可能溢出到 JavaScript 對象中,比如說使數(shù)組的或者其他類似對象的長度腐敗。為此,我開始圍繞著 JavaScript 分配器深入發(fā)掘,來看 JSObject 被存儲在哪里。Spidermonkey(Firefox 中的 JavaScript 引擎)把 JSObjet 存儲在兩個獨立的區(qū)域中:

          1. 永久堆(The tenured heap)。長生命周期的對象,同時少數(shù)被選擇的對象類型在此處分配。這是一個相當經(jīng)典的堆,跟蹤自由內(nèi)存然后在未來重復使用。

          2. 托管區(qū)。這是一塊包含短暫生命周期對象的內(nèi)存區(qū)域。大多數(shù)的JSObject最初都被分配在此處,然后如果在下一個垃圾回收周期( 包括更新所有指向他們的指針,因此要求牢記收集器知道其對象的所有指針)中依然存在則被移動到永久堆中。托管區(qū)不需要釋放列表或者相似的結(jié)構(gòu):在一個垃圾回收循環(huán)之后,托管區(qū)只是在所有的存活對象被移出之后簡單的聲明一下自己是可用的。

          永久堆中存儲對象的容器叫做 Arenas:

          /*

          注釋已經(jīng)給出了非常好的總結(jié):Arenas只是簡單容器對象,其中分配了相同大小的 JavaScript 對象。它們位于容器對象內(nèi),這個塊結(jié)構(gòu)本身就是直接使用 mmap 來分配的。在Arena類中有趣的部分是它的 firstFreeSpan 成員:它是 Arena 對象(因此處于一個映射區(qū)的開始處)的第一個成員,并且本質(zhì)上它指明了 Arena 中第一個未使用區(qū)塊的索引。下邊就是FreeSpan的大致結(jié)構(gòu):

          class FreeSpan

          其中的 first 和 last 在 Arena 中都是按字節(jié)索引,用來指明未使用區(qū)塊鏈的頭部。那么這就開辟了一條有趣的道路來利用這個漏洞:通過對 Arena 中 firstFreeSpan 成員對象的溢出,我們有可能在另一個對象中分配一個對象,最好是在某些可訪問的內(nèi)聯(lián)數(shù)據(jù)中分配。那么接下來我們就能任意的修改內(nèi)部分配的對象。

          這個技巧有一下幾點好處:

          • 我們將會看到能在 Arena 中使用指定的偏移分配一個 JavaScript 對象,這就產(chǎn)生了一條內(nèi)存讀寫原語。

          • 我們只需溢出接下來的區(qū)塊的4字節(jié),因此不會破壞任何的指針或是其他的敏感數(shù)據(jù)。

          • Arenas/Chunks 可以僅僅通過申請大量的 JavaScript 對象來可靠的產(chǎn)生。

          事實證明,ArrayBuffer 對象中高達96字節(jié)的數(shù)組會被內(nèi)聯(lián)存儲在該對象頭部之后后。這將會跳過托管過程并且因此它將會在 Arena 中被分配。這使得它們成為我們漏洞利用的理想選擇。我們會這樣做:

          1. 申請大量存儲 96 字節(jié)的 ArrayBuffer 對象。

          2. 溢出,并且在我們的緩沖區(qū)之后,在 Arena 內(nèi)部創(chuàng)建一個假的未使用內(nèi)存塊。

          3. 申請更多的同樣大小的 ArrayBuffer 對象,看是否其中的某一個會被放置在另一個 ArrayBuffer 的數(shù)據(jù)中(通過掃描所有先前的 ArrayBuffer 中的不為空的內(nèi)容即可)

          垃圾回收的要求

          不幸的是,這并不是那樣簡單:為了讓 Spidermonkey 在我們的目標 Arena(被破壞)中申請一個對象,那么這個 Arena 就必須在之前就被(部分)標記為可使用。這意味著,我們需要釋放所有的 Arena 中至少一個存儲塊。我們可以通過刪除每第 25個 ArrayBuffer(每個 Arena 有 25 個)來實現(xiàn),然后強制進行垃圾回收。

          Spidermonkey 因為各種各樣的原因而觸發(fā)垃圾回收。諸多方法中似乎使用TOO_MUCH_MALLOC 來觸發(fā)是最簡單的一種:只要通過 malloc 分配了一定數(shù)量的字節(jié),它就會被簡單的觸發(fā)。因此,下邊的代碼足以用來觸發(fā)垃圾回收:

          function gc() {

          在此之后,我們的目標 Arena 將會被放置在一個未使用標記鏈表中,隨后的覆蓋將會破壞該鏈表。下一次從被破壞的 Arena 中產(chǎn)生的分配將會返回一個假的處于一個 ArrayBuffer 對象的內(nèi)聯(lián)數(shù)據(jù)中內(nèi)存塊。

          (可選閱讀)壓縮GC

          實際上,這有點復雜了。存在一種叫做壓縮 GC 模式的垃圾回收,這種模式將會把多個部分填充的 Arena移動去填滿另一個 Arena。此舉減少了內(nèi)部碎片,并且協(xié)助釋放整個內(nèi)存區(qū)域以便系統(tǒng)回收。不管怎樣,對于我們來講,壓縮 GC 著實是個麻煩,因為,它有可能填充了我們之前創(chuàng)建的目標Arena。以下的代碼用來決定是否應該運行一個壓縮 GC:

          bool

          查看一下代碼,應該有方法來阻止壓縮 GC 來運行(比如說,展示一些動畫)。看來我們很幸運:上文中提到的gc函數(shù)(譯注:用來產(chǎn)生 ArrayBuffer 的 JS 代碼片段)將會在 Spidermonkey 中觸發(fā)下邊的代碼流程,因此,阻止壓縮 GC 的調(diào)用形式將會從 GC_SHRINK 變?yōu)?GC_NORMAL。

          bool

          編寫 Exploit

          此刻,我們已經(jīng)拼接了所有的碎片,可以實際動手寫利用代碼了。一旦我們創(chuàng)建了一個假的自由存儲塊并在其中創(chuàng)建一個 ArrayBuffer 對象,就能看見其中一個之前申請的 ArrayBuffer 中包含了我們的數(shù)據(jù)。ArrayBuffer 對象的結(jié)構(gòu)大致如下:

          // From JSObject

          常量 XXX_SLOT 確定對象的起始位置相應值的偏移量。這樣一來,數(shù)據(jù)指針(DATA_SLOT)將會被存儲在 addrof(ArrayBuffer) + sizeof(ArrayBuffer)。

          現(xiàn)在,我們就可以構(gòu)建以下的代碼利用原語:

          • 讀取絕對內(nèi)存地址:設置 DATA_SLOT 為所需的地址,然后從 ArrayBuffer 對象中讀取。

          • 寫入絕對內(nèi)存地址:和上邊一樣,不過此刻變成了寫操作。

          • 獲取 JavaScript 對象的地址:為此,需要設置需要獲取地址的對象為 ArrayBuffer 的內(nèi)部屬性,然后通過之前的讀內(nèi)存原語從 slots_指針處讀取地址。

          進程持續(xù)

          為了避免瀏覽器進程在下一次垃圾回收中崩潰,我們必須修復一下幾點:

          • 在我們的利用代碼中的 ArrayBuffer 之外的 ArrayBuffer,將會被代碼內(nèi)的 ArrayBuffer 數(shù)據(jù)破壞。可以通過簡單的拷貝另一個 ArrayBuffer 對象來覆蓋被破壞的數(shù)據(jù)即可修復。

          • 我們原本在 Arena 中釋放的區(qū)塊此刻看起來應該是一個正在被使用的狀態(tài),同樣回收器也是這樣認為,這將會導致崩潰,因為這塊數(shù)據(jù)已經(jīng)被其他的數(shù)據(jù)(比如,F(xiàn)reeSpan 實例)覆蓋了。我們可以通過重新載入我們 Arena 中原始 firstFreeSpan 域的值來標記該塊為可使用狀態(tài)。

          以上幾點就足以保證瀏覽器在跑完利用代碼之后仍然存活了。

          概要

          1. 插入一個腳本標簽加載 payload,最終觸發(fā)漏洞

          2. 等待服務器發(fā)送 2GB+1 字節(jié)的數(shù)據(jù)。瀏覽器此刻應該已經(jīng)申請了我們最終要溢出的內(nèi)存塊。像我們最初的最簡單的 POC 那樣嘗試使用 ArrayBuffer 對象來填充 mmap 中的區(qū)塊。

          3. 申請包含大小為 96 字節(jié)(數(shù)據(jù)存儲空間分配在對象之后的最大大小)的JavaScript Arenas(內(nèi)存區(qū)域),接下來期待其中之一被放置在了我們將要溢出的緩沖區(qū)之后。由于 mmap 分配連續(xù)的區(qū)域,所以如果我們不申請足夠的空間或者有什么其它的東西已經(jīng)被分配在此處則可能會導致失敗。

          4. 讓服務器盡情的發(fā)送 0xffffffff 字節(jié)的數(shù)據(jù),完全填充當前塊。

          5. 釋放所有的 Arena 中的一個 ArrayBuffer,然后嘗試觸發(fā)垃圾回收,使 Arena 被插入到未使用塊鏈表中。

          6. 使服務器發(fā)送剩下的數(shù)據(jù)。這將會觸發(fā)溢出并破壞一個 Arenas 內(nèi)部的一個未使用內(nèi)存塊鏈表。鏈表被改寫使得其中之一的 ArrayBuffer 中第一個包含內(nèi)聯(lián)數(shù)據(jù)的自由塊被包含在 Arena 中。

          7. 申請更多的 ArrayBuffer。如果一切正常,其中之一的 ArrayBuffer 將會被分配在另一個 ArrayBuffer 內(nèi)聯(lián)數(shù)據(jù)內(nèi)部。找到那個 ArrayBuffer!。

          8. 如果找到了,構(gòu)造一個任意內(nèi)存讀寫原語。現(xiàn)在我們就可以修改 ArrayBuffer 內(nèi)部的數(shù)據(jù)指針,所以這是非常容易做到的。

          9. 修復被破壞的對象,以便使我們的利用代碼跑完之后瀏覽器進程依然存活。

          彈出計算器

          剩下的工作就是以某種方式彈出一個計算器。

          一個簡單的跑自己代碼的方式是嘿咻 JIT了,但是,這個技術(shù)(部分)在Firefox 中被削弱了。考慮到我們開發(fā)的原語,可以繞過被削弱的部分(比如,使用 ROP 來轉(zhuǎn)移控制),但對于簡單的 PoC 來說,似乎有點復雜。

          有一些其他的 Firefox 相關(guān)的技巧來通過濫用特權(quán)的 JavaScript 獲取代碼執(zhí)行權(quán)限,但是這些需要對瀏覽器狀態(tài)進行不必要的修改(比如,關(guān)閉所有安全性,以便病毒可以接管這臺電腦)。

          我最終使用了一些標準的 CTF 技巧來完成利用代碼:尋找對 libc 中第一個參數(shù)為字符串的函數(shù)的交叉引用(此例中,選用strcmp),我發(fā)現(xiàn)Date.toLocalFormat的執(zhí)行并且注意到了該函數(shù)將第一個參數(shù)從JSString轉(zhuǎn)換為C-String,它的第一個參數(shù)被用來做strcmp。因此,我們可以簡單的 strcmp 的 GOT 換成 system,然后執(zhí)行data_obj.toLocaleFormat("open -a /Applications/Calculator.app");。搞定:).

          改進Exploit

          這時,基本的利用代碼已經(jīng)完成了, 接下來我們將會描述如何使它變的更可靠一些以及占用更少的帶寬。

          增強魯棒性

          此時此刻我們的利用代碼知識申請了一些非常大的 ArrayBuffer 實例(每個1GB)用來填充 mmap 空間,然后再分配一大堆 js :: Arena 實例用來溢出。因此,該操作假設瀏覽器的堆操作在利用期間多多少少是確定的。既然這不一定是這樣,我們希望讓我們的漏洞更加強大一些。

          快速瀏覽一下 mozilla::Vector 類(用來保留腳本緩沖區(qū))接下來的操作向我們展示了它使用了 realloc 在其需要的時候來倍增自己的空間。由于 jemalloc 直接使用 mmap 來申請較大的區(qū)塊,這就給了我們以下的分配模式:

          • mmap 1MB

          • mmap 2MB, munmap previous chunk

          • mmap 4MB, munmap previous chunk

          • mmap 8MB, munmap previous chunk

          • mmap 8GB, munmap previous chunk

          因為當前區(qū)塊的大小總是會大于之前所有區(qū)塊的大小,這將導致我們的最終緩沖區(qū)之前存在大量的可用空間。理論上,我們可以計算空閑空間之和,然后申請一個大的ArrayBuffer。實際上,這行不通,因為當服務器發(fā)送數(shù)據(jù)到來時,在瀏覽器完成解壓縮最后一塊數(shù)據(jù)完成之前有一些其他的申請空間的操作。此外 jemalloc 還保留了一部分被釋放的內(nèi)存以供以后使用。相反,我們會盡快在瀏覽器中申請被釋放的空間,理由如下:

          1. JavaScript代碼使用sync來等待服務器

          2. 服務器發(fā)送到下一個 2 的冪(MB)的所有數(shù)據(jù)從而在最后觸發(fā)一次對 realloc 的調(diào)用。瀏覽器此時將會釋放一個大小已知的空間。

          3. 服務器設置 server_done_event 信號,使 JavaScript 代碼繼續(xù)執(zhí)行。

          4. JavaScript 代碼申請一個與之前空間大小相同的 ArrayBuffer 實例來填充該空間。

          5. 重復上述步驟直到我們發(fā)送完 0x80000001 字節(jié)數(shù)據(jù)(強制申請最后一塊空間)。

          這個簡單的算法服務器端代碼在這里,客戶端第一步的代碼在這里。使用這個算法,我們可以通過僅噴射幾兆的 ArrayBuffer 實例而不是多個千兆字節(jié)來相當可靠地獲得分配在目標緩沖區(qū)之后的空間。

          減少網(wǎng)絡負載

          目前,我們的利用代碼需要通過服務器發(fā)送4GB的數(shù)據(jù)。可以簡單的通過下述方法來改進:使用HTTP壓縮。zlib有一個好處,支持流式壓縮,它可以逐步壓縮 payload。有了這個,我們只需將 payload 的每個部分添加到在 zlib 流中,然后調(diào)用 flush 來獲取 payload 的下一個壓縮塊,并將其發(fā)送到服務器(譯注:服務器發(fā)送到瀏覽器,筆誤?)。服務器(譯注:瀏覽器,筆誤?)在收到該塊后解壓縮該文件,并執(zhí)行所需要的操作(比如,執(zhí)行一次 realloc 操作)。

          poc.py中的 construct_payload 方法執(zhí)行了該過程,并且將 payload 的大小減少到大約18MB。

          資源使用

          至少在理論上,exploit 需要非常大量的內(nèi)存:

          • 一個保存我們 payload 腳本的 8GB的緩沖區(qū)。實際上,更像是12GB,因為在最后的 realloc 期間,有 4GB空間必須得拷貝到一個新的 8GB 空間中。

          • 需要 JavaScript 申請大量的(大約6GB)緩沖區(qū)來填充由 realloc 創(chuàng)建的內(nèi)存塊。

          • 大約 256 MB的 ArrayBuffers。

          不管怎么說,因為許多的緩沖區(qū)并沒有被寫入,所以不一定得消耗如此多的物理空間。更多的,在最終 realloc 中,只有 4GB新的緩沖區(qū)會被寫入之前釋放的舊的緩沖區(qū)中,因此真正需要的僅僅是 8GB 而已。

          不過這還是非常的占內(nèi)存。然而,如果物理內(nèi)存降低后,還是有一些技術(shù)來幫助減少內(nèi)存占用:

          • 內(nèi)存壓縮(macOS):大量的內(nèi)存區(qū)域可以被壓縮然后交換至交換分區(qū)。這對于我們的案例來講是完美的,因為8GB的緩沖區(qū)將完全被0填充。這個效果可以在Activity Monitor.app中觀察到,在利用代碼運行期間的某些時候會顯示超過6GB的內(nèi)存為“壓縮”。

          • 頁重復數(shù)據(jù)刪除(Windows,Linux):具有相同內(nèi)容的的內(nèi)存頁會被映射為具有寫時復制(COW,copy-on-write)屬性的相同物理頁面(基本上將內(nèi)存使用減少到4KB)。

          CPU 使用率在峰值期間(解壓縮數(shù)據(jù))也是相當?shù)母摺H欢梢酝ㄟ^延遲發(fā)送較小的塊之間的時間(這顯然會增加漏洞利用的時間)來進一步降低 CPU 的壓力。這也將給 OS 更多的時間來壓縮和或刪除大的重復數(shù)據(jù)內(nèi)存緩沖區(qū)。

          進一步可能的改進

          目前的漏洞利用有幾個不可靠的因素,主要是處理時機:

          • 在發(fā)送 payload 數(shù)據(jù)期間,如果 JavaScript 在瀏覽器完全處理下一個塊之前運行分配,則分配是不同步的。這可能導致利用失敗。理想情況下,JavaScript 將在接收和處理下一個塊后立即執(zhí)行分配,這個或許可以通過觀察 CPU 使用情況來確定。

          • 如果垃圾回收在我們已經(jīng)破壞 FreeSpan 之后但在我們修復之前運行,崩潰!

          • 如果在我們釋放了一些數(shù)組緩沖區(qū)之后,在觸發(fā)了溢出之前,如果一個壓縮的垃圾回收循環(huán)運行,那么漏洞利用就會失敗,因為 Arena 將再次被填滿。

          • 如果假的單元塊恰好放在釋放的 ArrayBuffer 的單元塊內(nèi),那么我們的漏洞利用將失敗,瀏覽器會在下一個垃圾回收周期中崩潰。每個 Arena 有 25 個單元塊,則在理論上有 1/25 失敗的幾率。然而,在我的實驗中,未使用單元塊總是位于相同的偏移處(Arena 的 1216 字節(jié)處),表明在開發(fā)階段引擎的狀態(tài)是相當確定的(至少關(guān)于 Arena 所持有的 160 字節(jié)的對象是來說是這樣的)。

          從我的經(jīng)驗來看,如果瀏覽器沒有大量的處理任務,這個漏洞利用率非常可靠(>95%)。如果 10 個以上的其他選項卡是打開的,則漏洞利用仍然有效,但如果大型 Web 應用程序正在加載,則可能會失敗。

          結(jié)論

          雖然從攻擊者的角度來看,這個漏洞并不理想,但它仍然可以相當可靠地在低帶寬下利用。過程中有趣的是使用的各種技術(shù)(壓縮,頁重復刪除。。。)來使得這個漏洞更加簡單的被利用。

          考慮到如何防止這種錯誤的可利用性,有幾點是我想說明的。一個相當通用的緩解措施是使用保護頁面(無論用什么來訪問保護頁面都會產(chǎn)生段錯誤)。保護頁面必須在每個mmap分配區(qū)域之前或之后分配,并且此舉將防止對這種線性溢出的利用。但是,它們不會防止非線性溢出,比如說這個漏洞。另一種可能性是引入內(nèi)部mmap隨機化來分散整個地址空間中的分配區(qū)域(可能僅在64位系統(tǒng)上有效)。這最好由內(nèi)核執(zhí)行,當然也可以在用戶空間中完成。

          本文由 看雪翻譯小組 zplusplus 編譯,來源root@saelo

          看雪論壇:http://bbs.pediy.com/

          微信公眾號 ID:ikanxue

          微博:看雪安全

          投稿、合作:www.kanxue.com

          們先來看個例子,這個容器里有兩段文字和一張圖片,由于容器的高度小于包含內(nèi)容的總高度,不能顯示全部的內(nèi)容。此時,我們可以通過鼠標滾輪使容器的內(nèi)容上下滾動,來查看全貌。

          這個效果是如何實現(xiàn)的呢?你一定想到的是 iframe!實際上,通過CSS的溢出樣式也能實現(xiàn)這個功能。

          CSS 的 overflow 屬性,指定了當一個元素的內(nèi)容太大,無法容納在指定區(qū)域時,是否要裁剪內(nèi)容或添加滾動條。overflow 屬性有以下值:

          visible - 默認。溢出的內(nèi)容沒有被剪掉。內(nèi)容會在元素的框外呈現(xiàn)。

          hidden - 溢出的內(nèi)容被剪切,其余的內(nèi)容將不可見。

          scroll - 溢出的內(nèi)容被剪掉了,并且增加了一個滾動條來查看其余的內(nèi)容。

          auto - 類似于 scroll,但它只在必要時添加滾動條。

          這是我已經(jīng)制作好的一個頁面。div 容器里包含三個段落,分別放置的是兩段文本和一張圖片。容器設置了背景顏色、內(nèi)填充和邊框。

          在瀏覽器里預覽,內(nèi)容撐滿整個容器,全部顯示出來了。

          接下來給外部的 div 容器添加 width: 50%、height: 300px 的樣式。

          HTML
          <div id="overflowTest">
              <p>
                國外某公司呢開發(fā)了一款自動找bug的AI程序,但是這個程序“悟道”了!AI“悟道”后解決bug的終極方案就是:沒有代碼就沒有bug,全刪!這是一個發(fā)生在美國的真實事件,美國版的大眾點評,本來想訓練AI來消除bug,結(jié)果它把所有的內(nèi)容全部刪除了。來看看 軟件的更新介紹:
              </p>
              <p>
                <img src="./image.jpeg" alt="" width="600">
              </p>
              <p>
                大概意思就是:我們?yōu)楸局苁褂迷揳pp遇到問題的用戶致歉,我們訓練了一個神經(jīng)網(wǎng)絡,來消愁該app的漏洞,但它刪除了一切。還好這事發(fā)生在國外,要是在國內(nèi)的互聯(lián)網(wǎng)公司,這么大的安全事故,又要有一個工程師“祭天”了。
              </p>
            </div>
          CSS
          #overflowTest {
            background: #4CAF50;
            padding: 15px;
            border: 1px solid #ccc;
          }

          我們看,容器的高度雖然小于內(nèi)容的總體高度,但是容器里的內(nèi)容還是全部顯示了。這說明瀏覽器在默認情況下, 對容器溢出的部分是完全顯示的。

          回到 css 代碼,給 div 容器再添加樣式 overflow: visible。

          效果是一樣的,說明 visible 是 overflow 屬性的默認值。

          如果希望把溢出的部分隱藏,需要將 overflow 的值設置為 hidden(注意這個單詞讀 [?h?dn])。

          果然,溢出的部分隱藏了,此時是無法看到隱藏的內(nèi)容的。

          如果希望通過滾動條查看隱藏的內(nèi)容,需要將 overflow 的值設置為 scroll。

          這樣,就可以通過滾動條來查看全部內(nèi)容了!

          當然,也可以把 overflow 的值設置為 auto,此時和 scroll 效果一樣。

          回到 css 代碼,我們將 div 容器的 width 修改為 30%。

          看效果,水平和垂直方向都可以滾動了。

          如果只希望某一個方向滾動,可以采用 overflow-x 和 overflow-y 兩個樣式屬性,它們指定是否只在水平方向或垂直方向上滾動。

          比如,注釋掉 overflow: scroll ,添加樣式 overflow-y: scroll。

          我們看,此時垂直方向可以滾動,可是水平方向也可以滾動,說明 overflow-y 只能約束垂直方向。

          再添加樣式 overflow-x: hidden。

          這回,水平方向就不能滾動了,只有垂直方向可以滾動!

          有時,文字標題或文字內(nèi)容所在的容器空間有限,不能顯示全部內(nèi)容的時候,會出現(xiàn)一個省略號,示意讀者:內(nèi)容顯示不全,可以點擊查看詳細內(nèi)容。這個效果如何實現(xiàn)呢?

          在 body 里添加一個 h1 元素,填入一些內(nèi)容。

          在 css 文件里,定義選擇器 h1,聲明樣式 width: 310px,overflow: hidden,white-space: nowrap。讓文本在一行上顯示,并且溢出的部分隱藏。

          很顯然,此時的效果用戶體驗不好——文字被剪切了。

          回到 css 代碼,給 h1 再添加一個 text-overflow: ellipsis 樣式。ei 累鋪賽司

          我們再看,效果實現(xiàn)了!

          篇文章主要給大家介紹一下在html頁面中如何讓單行文本以及多行文本溢出顯示省略號(…)。

          1)單行文本溢出顯示省略號

          當我們在編寫網(wǎng)頁代碼的時候,肯定會遇到過文字列表中的文字太多超出了我們所寫的寬度,導致文本換行或者文本超出了界限,這時有人就會說了,讓后臺限制一下調(diào)用的文字個數(shù)不就行了嗎,但是我們在做響應式的時候由于是百分比布局,無法計算一行會顯示多少個文字,所以這并不是一個好的解決方案,我們使用css3就可以輕松的實現(xiàn),而且簡單好用。

          核心css語句:

          1、overflow:hidden; (顧名思義超出限定的寬度就隱藏內(nèi)容)

          2、white-space: nowrap; (設置文字在一行顯示不能換行)

          3、text-overflow: ellipsis;(規(guī)定當文本溢出時顯示省略符號來代表被修剪的文本)

          我們具體的代碼效果演示就如下圖所示:(設置ul寬度為300,超出的文字內(nèi)容讓其自動隱藏并顯示...)

          2)多行文本溢出顯示省略號

          我們在編寫網(wǎng)頁代碼時,有時候新聞列表頁中新聞簡介可能有一行或者多行,我們要如何處理這種問題,讓其超出多行后還能顯示省略號呢,不要慌,我們css還是很強大的,已經(jīng)給我們提供了方法來處理這種問題了。

          核心css語句:

          1、-webkit-line-clamp:2; (用來限制在一個塊元素顯示的文本的行數(shù),2表示最多顯示2行。 為了實現(xiàn)該效果,它需要組合其他的WebKit屬性)

          2、display: -webkit-box; (和1結(jié)合使用,將對象作為彈性伸縮盒子模型顯示 )

          3、-webkit-box-orient:vertical;( 和1結(jié)合使用 ,設置或檢索伸縮盒對象的子元素的排列方式 。)

          4、overflow:hidden; (顧名思義超出限定的寬度就隱藏內(nèi)容)

          5、text-overflow: ellipsis;(規(guī)定當文本溢出時顯示省略符號來代表被修剪的文本)

          我們具體的代碼效果演示就如下圖所示:(設置段落p寬度為300,超出2行的文字內(nèi)容讓其自動隱藏并顯示...)

          好了,本篇文章就給大家說到這里,大家自己下來可以自己找例子寫一下試一試到底能不能實現(xiàn)我們所說的效果,以后在寫頁面的的遇到這種問題的時候直接復制使用即可。

          每日金句:必須從過去的錯誤學習教訓而非依賴過去的成功。喜歡我的文章的小伙伴記得關(guān)注一下哦,每天將為你更新最新知識。


          主站蜘蛛池模板: 成人精品一区二区电影| 精品欧洲AV无码一区二区男男| 精品一区二区三区水蜜桃| 美女免费视频一区二区| 国产91精品一区二区麻豆网站 | 日本一区二区高清不卡| 人妻少妇精品视频三区二区一区| 亚洲综合色自拍一区| 无码精品人妻一区| 精品无码综合一区| 一区 二区 三区 中文字幕| 天天视频一区二区三区| 精品一区二区三区东京热| 日本精品一区二区三区在线观看| 亚洲大尺度无码无码专线一区| 78成人精品电影在线播放日韩精品电影一区亚洲| 国产一区二区不卡老阿姨| 冲田杏梨AV一区二区三区| 清纯唯美经典一区二区| 久久综合精品国产一区二区三区| 精品国产一区二区三区无码| 亚洲影视一区二区| 老熟妇仑乱一区二区视頻| 午夜DV内射一区二区| 亚洲国产精品乱码一区二区 | 在线播放精品一区二区啪视频| 久久久精品人妻一区二区三区蜜桃 | 国产乱码精品一区三上| 国产一区二区三精品久久久无广告 | 无码人妻精品一区二区三区99性| 国产福利电影一区二区三区久久老子无码午夜伦不| 亚洲午夜电影一区二区三区 | 性盈盈影院免费视频观看在线一区 | 精品国产AV一区二区三区| 亚洲乱码av中文一区二区| 国产成人一区二区动漫精品| 亚洲日本一区二区三区在线| 亚洲片国产一区一级在线观看| 久久婷婷久久一区二区三区| 国产大秀视频在线一区二区 | 国产精品福利区一区二区三区四区 |