中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

網(wǎng)站做關(guān)鍵詞搜索要好多錢潮州seo建站

網(wǎng)站做關(guān)鍵詞搜索要好多錢,潮州seo建站,wordpress代言寶插件,中國制造網(wǎng)官網(wǎng)登錄譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 MIT 6.858 計算機系統(tǒng)安全筆記 2014 秋季 2014 年由Nickolai Zeldovich 教授和James Mickens 教授教授授課的 6.858 講座筆記。這些講座筆記略有修改,與 6.858 課程網(wǎng)站上發(fā)布的內(nèi)容略有不同。 第1講&#x…

譯者:飛龍

協(xié)議:CC BY-NC-SA 4.0

MIT 6.858 計算機系統(tǒng)安全筆記 2014 秋季

2014 年由Nickolai Zeldovich 教授和James Mickens 教授教授授課的 6.858 講座筆記。這些講座筆記略有修改,與 6.858 課程網(wǎng)站上發(fā)布的內(nèi)容略有不同。

  • 1講:介紹:什么是安全,意義何在,沒有完美的安全,策略,威脅模型,假設(shè),機制,緩沖區(qū)溢出

  • 2講:控制劫持攻擊:緩沖區(qū)溢出,棧金絲雀,邊界檢查,電子圍欄,胖指針,影子數(shù)據(jù)結(jié)構(gòu),Jones & Kelly,松松邊界檢查

  • 3講:更多松松邊界和返回導(dǎo)向編程:邊界檢查的成本,不可執(zhí)行內(nèi)存,地址空間布局隨機化(ASLR),返回導(dǎo)向編程(ROP),堆棧讀取,盲目 ROP,小工具

  • 4講:OKWS:特權(quán)分離,Linux 自主訪問控制(DAC),UIDs,GIDs,setuid/setgid,文件描述符,進程,Apache web 服務(wù)器,chroot 監(jiān)獄,遠程過程調(diào)用(RPC)

  • 5講:滲透測試 嘉賓講座 由 Paul Youn,iSEC Partners

  • 6講:Capsicum:混淆副手問題,環(huán)境權(quán)限,能力,沙盒,自主訪問控制(DAC),強制訪問控制(MAC),Capsicum

  • 7講:Native Client (NaCl):沙盒化 x86 本機代碼,軟件故障隔離,可靠的反匯編,x86 分段

  • 8講:Web 安全,第一部分:現(xiàn)代網(wǎng)絡(luò)瀏覽器,同源策略,框架,DOM 節(jié)點,cookies,跨站請求偽造(CSRF)攻擊,DNS 重綁定攻擊,瀏覽器插件

  • 9講:Web 安全,第二部分:跨站腳本(XSS)攻擊,XSS 防御,SQL 注入攻擊,Django,會話管理,cookies,HTML5 本地存儲,HTTP 協(xié)議的歧義,隱蔽通道

  • 10講:符號執(zhí)行 嘉賓講座 由 Armando Solar-Lezama 教授,MIT CSAIL

  • 11講:Ur/Web 嘉賓講座 由 Adam Chlipala 教授,MIT,CSAIL

  • 12講:TCP/IP 安全:威脅模型,序列號和攻擊,連接劫持攻擊,SYN 洪水攻擊,帶寬放大攻擊,路由

  • 13講:Kerberos:Kerberos 架構(gòu)和信任模型,票證,認證者,票證授予服務(wù)器,更改密碼,復(fù)制,網(wǎng)絡(luò)攻擊,前向保密性

  • 14講:ForceHTTPS:證書,HTTPS,在線證書狀態(tài)協(xié)議(OCSP),ForceHTTPS

  • 15講:醫(yī)療軟件 嘉賓講座 由 Kevin Fu 教授,密歇根大學(xué)

  • 16講:時序攻擊:側(cè)信道攻擊,RSA 加密,RSA 實現(xiàn),模指數(shù)運算,賽里斯剩余定理(CRT),重復(fù)平方,蒙哥馬利表示,卡拉茨巴乘法,RSA 蒙蔽,其他時序攻擊

  • 17講:用戶認證:你擁有什么,你知道什么,你是誰,密碼,挑戰(zhàn)-響應(yīng),可用性,部署性,安全性,生物特征,多因素認證(MFA),萬事達卡的 CAP 讀卡器

  • 18講:私密瀏覽:私密瀏覽模式,本地和網(wǎng)絡(luò)攻擊者,虛擬機級隱私,操作系統(tǒng)級隱私,瀏覽器實現(xiàn)了什么,瀏覽器擴展

  • 19講:Tor 客座講座,由 Nick Mathewson 主講,Tor 項目

    • 2012 年的 6.858 課程筆記關(guān)于匿名通信:洋蔥路由,Tor 設(shè)計,Tor 電路,Tor 流,Tor 隱藏服務(wù),阻止 Tor,用餐密碼學(xué)家網(wǎng)絡(luò)(DC-nets)
  • 20講:手機安全:Android 應(yīng)用程序,活動,服務(wù),內(nèi)容提供者,廣播接收器,意圖,權(quán)限,標簽,參考監(jiān)視器,廣播意圖

  • 21講:信息流跟蹤:TaintDroid,Android 數(shù)據(jù)泄漏,信息流控制,污點跟蹤,污點標志,隱式流,x86 污點跟蹤,TightLip

  • 22講:麻省理工學(xué)院信息服務(wù)與技術(shù)部 客座講座,由 Mark Silis 和 David LaPorte 主講,麻省理工學(xué)院信息服務(wù)與技術(shù)部

  • 23講:安全經(jīng)濟學(xué):網(wǎng)絡(luò)攻擊的經(jīng)濟學(xué),垃圾郵件價值鏈,廣告,點擊支持,實現(xiàn),CAPTCHA,僵尸網(wǎng)絡(luò),支付協(xié)議,道德

2015 年的新筆記

  • 8講:英特爾軟件保護擴展(SGX):隔離,Iago 攻擊,飛地,證明,Haven

論文

我們閱讀的論文列表(papers/):

  • 松松垮垮的邊界檢查

  • 盲目黑客

  • OKWS

  • 困惑的代理(或者為什么可能發(fā)明了能力)

  • 辣椒 (capabilities)

  • 本地客戶端(沙箱化 x86 代碼)

  • OWASP 前十名,最關(guān)鍵的 Web 應(yīng)用程序安全風(fēng)險

  • KLEE(符號執(zhí)行)

  • Ur/Web(面向 Web 的函數(shù)式編程)

  • 回顧“TCP/IP 協(xié)議套件中的安全問題”

  • 凱伯羅斯:用于開放網(wǎng)絡(luò)系統(tǒng)的認證服務(wù)

  • ForceHTTPs

  • 值得信賴的醫(yī)療設(shè)備軟件

  • 遠程時序攻擊是可行的

  • 替代密碼的探索

  • 私密瀏覽模式

  • Tor:第二代洋蔥路由器

  • 了解 Android 安全

  • TaintDroid:一種用于智能手機實時隱私監(jiān)控的信息流跟蹤系統(tǒng)

  • 點擊軌跡:垃圾郵件價值鏈的端到端分析

“新” 論文

  • Iago Attacks: 為什么系統(tǒng)調(diào)用 API 是一個糟糕的不受信任的 RPC 接口

其他論文

  • 賽里斯防火墻安全政策

介紹

**注意:**這些講座筆記略有修改,來源于 2014 年 6.858 課程網(wǎng)站上發(fā)布的內(nèi)容課程網(wǎng)站。

什么是安全性?

  • 在對手存在的情況下實現(xiàn)某個目標。

    • 許多系統(tǒng)連接到互聯(lián)網(wǎng),而互聯(lián)網(wǎng)上存在對手。

      • 因此,許多系統(tǒng)的設(shè)計可能需要考慮安全性。

      • 即,在有對手的情況下系統(tǒng)是否能正常工作?

  • 高層次的安全性思考計劃:

    • **政策:**您想要實現(xiàn)的目標。

      • 例如,只有艾麗絲應(yīng)該讀取文件F。

      • *常見目標:*保密性,完整性,可用性。

    • **威脅模型:**對攻擊者可能做什么的假設(shè)。

      • 例如可以猜測密碼,但無法物理抓取文件服務(wù)器。

      • 最好假設(shè)攻擊者可以做某事。

    • **機制:**系統(tǒng)提供的旋鈕,幫助維護政策。

      • 例如,用戶帳戶,密碼,文件權(quán)限,加密。
    • **最終目標:**在威脅模型內(nèi)部,對手無法違反政策。

      • 請注意,目標與機制無關(guān)。
  • 為什么安全性很難?這是一個負面目標。

    • 對比:檢查是否實現(xiàn)了積極目標很容易,例如,艾麗絲實際上可以讀取文件F。更難的是檢查沒有可能的方式讓艾麗絲讀取文件F。

      • 你甚至如何開始列舉艾麗絲可能讀取文件的所有可能方式?艾麗絲可以利用多少層次的漏洞來獲取文件F的訪問權(quán)限。
    • 需要保證政策,假設(shè)威脅模型。

    • 很難想象攻擊者可能如何入侵的所有可能方式。

    • 現(xiàn)實的威脅模型是開放式的(幾乎是負面模型)。

    • 最弱的環(huán)節(jié)很重要。

    • 迭代過程:設(shè)計,根據(jù)需要更新威脅模型等。

如果我們無法實現(xiàn)完美的安全性,那有什么意義呢?

  • 在這門課程中,我們將推動每個系統(tǒng)的邊界,看看它何時會崩潰。

    • 每個系統(tǒng)可能都會有一些導(dǎo)致妥協(xié)的破壞點。

    • 這并不一定意味著系統(tǒng)沒有用:這取決于上下文。

    • 重要的是了解系統(tǒng)能做什么,以及系統(tǒng)不能做什么。

  • 實際上,必須管理安全風(fēng)險與收益。

    • 更安全的系統(tǒng)意味著某些妥協(xié)的風(fēng)險(或后果)更小。

    • 不安全的系統(tǒng)可能需要手動審計以檢查攻擊等。

    • 攻擊成本越高,將有更多的對手被阻止。

  • 更好的安全性通常使新功能變得實用和安全。

    • 假設(shè)你想在系統(tǒng)上運行某些應(yīng)用程序。

    • 大公司有時會禁止用戶在其臺式機上安裝未經(jīng)批準的軟件,部分原因是出于安全考慮。

    • 瀏覽器中的 Javascript 是隔離的,這使得運行新代碼/應(yīng)用程序而無需手動檢查/批準變得可以接受(或虛擬機,或本地客戶端,或更好的操作系統(tǒng)隔離機制)。

    • 同樣,VPN 使得減輕允許員工從互聯(lián)網(wǎng)的任何地方連接到公司網(wǎng)絡(luò)的風(fēng)險變得實際可行。

出現(xiàn)問題的原因之一:政策問題

  • 例子: 薩拉·佩林的電子郵件賬戶。

    • 雅虎電子郵件賬戶有用戶名、密碼和安全問題。

    • 用戶可以通過提供用戶名和密碼登錄。

    • 如果用戶忘記密碼,可以通過回答安全問題來重置。

    • 安全問題有時比密碼更容易猜到。

    • 一些對手猜到了薩拉·佩林的高中、生日等信息。

    • 政策總結(jié)為:可以使用密碼或安全問題登錄。

      • (無法強制執(zhí)行“只有用戶忘記密碼,然后…”)
  • 例子: 馬特·霍南在亞馬遜、蘋果、谷歌等處的賬戶

    • Gmail 密碼重置:向備用電子郵件地址發(fā)送驗證鏈接。

      • 谷歌貼心地打印了備用電子郵件地址的一部分。

      • 馬特·霍南的備用地址是他的蘋果@me.com賬戶。

    • 蘋果密碼重置:需要賬單地址,信用卡的最后 4 位數(shù)字。

      • 地址可能很容易獲取,但如何獲取用戶信用卡號的 4 位數(shù)字?
    • 亞馬遜:可以向賬戶添加信用卡,無需密碼。

    • 亞馬遜密碼重置:提供用戶的任意一張信用卡號。

      • 哈哈。
    • 亞馬遜:不會打印信用卡號… 但會打印最后 4 位數(shù)字!

  • 例子: Twitter 的@N賬戶劫持。

    • 對于合法用戶來說,有時很難證明他們擁有一個賬戶!
  • 如何解決?

    • 仔細思考政策聲明的含義。

    • 一些政策檢查工具可以幫助,但需要一種指定不良內(nèi)容的方法。

    • 在分布式系統(tǒng)中很困難:不知道每個人在做什么。

出了什么問題 #2:威脅模型/假設(shè)問題

  • 例子: 未考慮人為因素。

    • 釣魚攻擊。

    • 用戶收到一封要求續(xù)訂電子郵件賬戶、轉(zhuǎn)賬或…的電子郵件。

    • 技術(shù)支持接到一個聲音很像用戶的電話要求重置密碼。

      • “橡皮管解密”
  • 例子: 計算假設(shè)隨時間變化。

    • 麻省理工學(xué)院的 Kerberos 系統(tǒng)使用 56 位 DES 密鑰,自 20 世紀 80 年代中期以來。

    • 當(dāng)時,似乎可以假設(shè)對手無法檢查所有 2?? 個密鑰。

    • 不再合理:現(xiàn)在大約需要 100 美元。

      • 幾年前,6.858 期末項目表明可以在一天內(nèi)獲取任何密鑰。
  • 例子: 所有 SSL 證書 CA 都是完全受信任的。

    • 要連接到啟用 SSL 的網(wǎng)站,Web 瀏覽器會驗證證書。

    • 證書是服務(wù)器主機名和加密密鑰的組合,

      • 由某些受信任的證書頒發(fā)機構(gòu)(CA)簽名。
    • 瀏覽器信任的證書頒發(fā)機構(gòu)(CA)長列表(數(shù)百個)。

    • 如果任何 CA 受到損害,對手可以攔截 SSL 連接。

      • 為任何服務(wù)器主機名創(chuàng)建一個“偽造”證書。
    • 2011 年,兩個 CA 受到損害,為許多域(谷歌、雅虎、Tor 等)簽發(fā)了偽造證書,顯然在伊朗使用。

      • en.wikipedia.org/wiki/DigiNotar

      • en.wikipedia.org/wiki/Comodo_Group

    • 2012 年,一家 CA 無意中簽發(fā)了一個適用于任何域的根證書。

      • www.h-online.com/security/news/item/Trustwave-issued-a-man-in-the-middle-certificate-1429982.html
  • *例子:*假設(shè)你的硬件是可信的。

    • 如果 NSA 是你的對手,結(jié)果可能并不是一個好的假設(shè)。

      • www.schneier.com/blog/archives/2013/12/more_about_the.html
  • *例子:*假設(shè)密碼學(xué)中有良好的隨機性。

    • 需要高質(zhì)量的隨機性來生成無法被猜測的密鑰。

    • 問題:嵌入式設(shè)備、虛擬機可能沒有太多的隨機性。

    • 結(jié)果,許多密鑰相似或容易被猜測攻擊。

      • 挖掘你的 P 和 Q:檢測網(wǎng)絡(luò)設(shè)備中普遍存在的弱密鑰
  • *例子:*顛覆軍事操作系統(tǒng)安全。

    • 在 80 年代,軍方鼓勵研究安全操作系統(tǒng)。

    • 操作系統(tǒng)被破壞的一個意想不到的方式:

      • 對手獲取了開發(fā)系統(tǒng)的訪問權(quán)限,修改了操作系統(tǒng)代碼。
  • *例子:*顛覆防火墻。

    • 對手可以連接到防火墻后面的不安全無線網(wǎng)絡(luò)。

    • 對手可以欺騙防火墻后面的用戶來禁用防火墻。

      • 可能只需點擊鏈接http://firewall/?action=disable就足夠了。

      • 或者也許在 CNN.com 購買廣告,指向那個 URL(有效)?

  • *例子:*斷開與互聯(lián)網(wǎng)連接的機器安全嗎?

    • Stuxnet 蠕蟲通過 USB 驅(qū)動器上的特制文件傳播。
  • 如何解決?

    • 更明確的威脅模型,以了解可能存在的弱點。

    • 更簡單、更通用的威脅模型。

    • 更好的設(shè)計可能會消除/減少對某些假設(shè)的依賴。

      • 例如,不依賴完全信任 CA 的替代信任模型。

      • 例如,不容易受到釣魚攻擊的身份驗證機制。

問題出在哪里 #3:機制問題–漏洞

  • 安全機制中的漏洞(例如,操作系統(tǒng)內(nèi)核)會導(dǎo)致漏洞。

  • 如果應(yīng)用程序正在執(zhí)行安全性,應(yīng)用程序級別的錯誤會導(dǎo)致漏洞。

  • 例子:蘋果的 iCloud 密碼猜測速率限制。

    • 人們經(jīng)常選擇弱密碼;通常可以在幾次嘗試(1K-1M)后猜中。

    • 大多數(shù)服務(wù),包括蘋果的 iCloud,會對登錄嘗試進行速率限制。

    • 蘋果的 iCloud 服務(wù)有許多 API。

    • 一個 API(“查找我的 iPhone”服務(wù))忘記實現(xiàn)速率限制。

    • 對手可以多次嘗試猜測密碼。

      • 可能和他們發(fā)送數(shù)據(jù)包的速度一樣快:>> M/day.
  • 例子:花旗集團信用卡網(wǎng)站缺失訪問控制檢查。

    • 花旗銀行允許信用卡用戶在線訪問他們的賬戶。

    • 登錄頁面要求輸入用戶名和密碼。

    • 如果用戶名和密碼正確,將重定向到賬戶信息頁面。

    • 賬戶信息頁面的 URL 包含一些數(shù)字。

    • 結(jié)果這些數(shù)字與用戶的賬號相關(guān)。

    • 更糟糕的是,服務(wù)器沒有檢查您是否已登錄到該帳戶。

    • 對手嘗試不同的數(shù)字,獲取不同人的賬戶信息。

    • 可能是錯誤的威脅模型:與現(xiàn)實世界不匹配?

      • 如果對手通過瀏覽器瀏覽網(wǎng)站,系統(tǒng)是安全的。

      • 如果對手自己合成新的 URL,系統(tǒng)就不安全了。

    • 很難說開發(fā)人員是否有錯誤的威脅模型,或者有錯誤的機制。

  • 例子: 安卓的 Java SecureRandom弱點導(dǎo)致比特幣被盜。

    • 擁有所有者私鑰的任何人都可以花費比特幣。

    • 許多安卓上的比特幣錢包應(yīng)用使用了 Java 的SecureRandom API。

    • 結(jié)果系統(tǒng)有時忘記了給 PRNG 種子!

    • 結(jié)果,一些比特幣密鑰很容易被猜到。

    • 對手搜索可猜測的密鑰,花費任何相應(yīng)的比特幣。

  • 例子: 沙箱中的漏洞(NaCl,Javascript,Java 運行時)。

    • 允許對手逃離隔離,執(zhí)行他們本不應(yīng)執(zhí)行的操作。
  • 例子: Moxie 的 SSL 證書名稱檢查漏洞

    • 空字節(jié)與長度編碼。
  • 例子: 緩沖區(qū)溢出(見下文)。

案例研究:緩沖區(qū)溢出

  • 考慮一個網(wǎng)絡(luò)服務(wù)器。

    • 通常情況下,網(wǎng)絡(luò)服務(wù)器的代碼負責(zé)安全性。

    • 例如,檢查可以訪問哪些 URL,檢查 SSL 客戶端證書,…

    • 因此,服務(wù)器代碼中的漏洞可能導(dǎo)致安全妥協(xié)。

  • 威脅模型是什么,策略是什么?

    • 假設(shè)對手可以連接到網(wǎng)絡(luò)服務(wù)器,提供任何輸入。

    • 策略有點模糊:只執(zhí)行程序員意圖的操作?

    • 例如,不希望對手竊取數(shù)據(jù),繞過檢查,安裝后門。

  • 考慮來自某個網(wǎng)絡(luò)服務(wù)器的簡化示例代碼:

webserver.c:

 int read_req(void) {char buf[128];int i;gets(buf);i = atoi(buf);return i;} 
  • 編譯器在內(nèi)存布局方面生成了什么?

    • x86 棧:

      • 棧向下增長。

      • %esp指向棧上最后(最底部)有效的內(nèi)容。

      • %ebp指向調(diào)用者的%esp值。

read_req() stack layout:

 +------------------+entry %ebp ----> | .. prev frame .. ||                  ||                  |+------------------+entry %esp ----> |  return address  |+------------------+new %ebp ------> |    saved %ebp    |+------------------+|     buf[127]     ||       ...        ||      buf[0]      |+------------------+|        i         |new %esp ------> +------------------+|       ...        |+------------------+ 
  • 調(diào)用者的代碼(比如,main()):

    • 調(diào)用read_req()

read_req()的匯編代碼:

 push    %ebpmov     %esp -> %ebpsub     168, %esp        # stack vars, etc...mov     %ebp -> %esppop     %ebpret 
  • 對手如何利用這段代碼?

    • 提供長輸入,覆蓋超出緩沖區(qū)的棧上數(shù)據(jù)。

    • 有趣的數(shù)據(jù):返回地址,被ret使用。

    • 可以將返回地址設(shè)置為緩沖區(qū)本身,在其中包含一些代碼。

  • 對手如何知道緩沖區(qū)的地址?

    • 如果一臺機器的內(nèi)存是另一臺的兩倍會發(fā)生什么?

    • 幸運的是對手,虛擬內(nèi)存使事情更加確定。

    • 對于給定的操作系統(tǒng)和程序,地址通常是相同的。

  • 如果棧向上增長,而不是向下,會發(fā)生什么?

    • 查看gets()的棧幀。
  • 一旦對手執(zhí)行代碼,他們可以做什么?

    • 使用進程的任何權(quán)限。

    • 經(jīng)常利用溢出來更容易地進入系統(tǒng)。

      • 最初在 Unix 上,運行 shell /bin/sh(因此稱為“shell 代碼”)。
    • 如果進程以 rootAdministrator 運行,可以做任何事情。

    • 即使不是,仍然可以發(fā)送垃圾郵件,讀取文件(Web 服務(wù)器,數(shù)據(jù)庫),…

    • 可以攻擊防火墻后面的其他機器。

  • 為什么程序員會寫出這樣的代碼?

    • 舊代碼,未暴露在互聯(lián)網(wǎng)上。

    • 程序員沒有考慮安全性。

    • 許多標準函數(shù)曾經(jīng)是不安全的(strcpygetssprintf)。

    • 即使是安全版本也有陷阱(strncpy 不會在末尾加上空字符)。

  • 更一般地說,任何內(nèi)存錯誤都可能轉(zhuǎn)化為漏洞。

    • 在釋放后繼續(xù)使用內(nèi)存(釋放后使用)。

      • 如果寫入,覆蓋新的數(shù)據(jù)結(jié)構(gòu),例如函數(shù)指針。

      • 如果讀取,可能會調(diào)用一個已損壞的函數(shù)指針。

    • 兩次釋放相同的內(nèi)存(雙重釋放)。

      • 可能會導(dǎo)致 malloc() 之后再次返回相同的內(nèi)存。
    • 將棧指針遞減到棧的末尾之外,進入其他內(nèi)存。

      • 利用運行在 Linux 上的 Xorg 服務(wù)器中的大內(nèi)存管理漏洞
    • 一個字節(jié)的錯誤寫入可能導(dǎo)致受損。

      • glibc __gconv_translit_find() 漏洞
    • 甚至可能不需要覆蓋返回地址或函數(shù)指針。

      • 可以足以讀取敏感數(shù)據(jù),如加密密鑰。

      • 可以通過改變一些位來滿足需求(例如int isLoggedInint isRoot)。

如何避免機制問題?

  • 減少安全關(guān)鍵代碼的數(shù)量。

    • 不要依賴整個應(yīng)用程序來執(zhí)行安全性。

    • 將在實驗 2 中進行。

  • 避免安全關(guān)鍵代碼中的錯誤。

    • 例如,不要使用 gets(),而是使用 fgets() 可以限制緩沖區(qū)長度。

    • 使用常見、經(jīng)過充分測試的安全機制(“機制的經(jīng)濟性”)。

    • 審計這些常見的安全機制(有很多動力這樣做)。

    • 避免開發(fā)可能存在錯誤的新的一次性機制。

    • 良好的機制支持多種用途、策略(更有動力進行審計)。

  • 常見機制的示例:

    • 操作系統(tǒng)級別的訪問控制(但是,通??梢愿?#xff09;。

    • 網(wǎng)絡(luò)防火墻(但是,通??梢愿?#xff09;。

    • 加密,加密協(xié)議。

緩沖區(qū)溢出,松散的邊界

注意: 這些講座筆記略有修改,來自 2014 年 6.858 課程網(wǎng)站。

緩沖區(qū)溢出攻擊回顧

在上一講中,我們看了執(zhí)行緩沖區(qū)溢出攻擊的基礎(chǔ)知識。該攻擊利用了幾個觀察結(jié)果:

  • 系統(tǒng)軟件通常用 C 編寫(操作系統(tǒng)、文件系統(tǒng)、數(shù)據(jù)庫、編譯器、網(wǎng)絡(luò)服務(wù)器、命令外殼和控制臺實用程序)

  • C 本質(zhì)上是高級匯編語言,所以…

    • 暴露原始指針到內(nèi)存

    • 不對數(shù)組執(zhí)行邊界檢查(因為硬件不這樣做,而 C 希望讓你盡可能接近硬件)

  • 攻擊還利用了關(guān)于 x86 代碼如何工作的架構(gòu)知識:

    • 棧增長的方向

    • 棧變量的布局(尤其是數(shù)組和函數(shù)的返回地址)

read_req.c:

 void read_req() {char buf[128];int i;gets(buf);//. . . do stuff w/buf . . .} 

編譯器在內(nèi)存布局方面生成了什么?x86 ??雌饋硐襁@樣:

 %esp points to the last (bottom-most) valid thing onthe stack.%ebp points to the caller's %esp value.+------------------+entry %ebp ----> | .. prev frame .. ||                  |  ||                  |  | stack grows down+------------------+  |entry %esp ----> |  return address  |  v+------------------+new %ebp ------> |    saved %ebp    |+------------------+|     buf[127]     ||       ...        ||      buf[0]      |+------------------+new %esp ------> |        i         |+------------------+ 

對手如何利用這段代碼?

  • 提供長輸入,覆蓋緩沖區(qū)后的棧數(shù)據(jù)。

  • 關(guān)鍵觀察 1: 攻擊者可以覆蓋返回地址,使程序跳轉(zhuǎn)到攻擊者選擇的位置!

  • 關(guān)鍵觀察 2: 攻擊者可以將返回地址設(shè)置為緩沖區(qū)本身,在其中包含一些 x86 代碼!

攻擊者在執(zhí)行代碼后可以做什么?

  • 利用進程的任何權(quán)限!如果進程以 root 或管理員身份運行,它可以在系統(tǒng)上做任何想做的事情。即使進程不以 root 身份運行,它也可以發(fā)送垃圾郵件、讀取文件,有趣的是,攻擊或破壞防火墻后面的其他機器。

  • 嗯,但為什么操作系統(tǒng)沒有注意到緩沖區(qū)已經(jīng)溢出?

    • 就操作系統(tǒng)而言,沒有發(fā)生任何奇怪的事情!請記住,粗略地說,操作系統(tǒng)只在 Web 服務(wù)器進行 IO 或 IPC 時才被調(diào)用。除此之外,操作系統(tǒng)基本上只是坐下來讓程序執(zhí)行,依靠硬件頁表防止進程篡改彼此的內(nèi)存。然而,頁表保護無法防止進程“針對自身”發(fā)起的緩沖區(qū)溢出,因為溢出的緩沖區(qū)、返回地址和所有相關(guān)內(nèi)容都在進程的有效地址空間內(nèi)。

    • 在本講座的后面,我們將討論操作系統(tǒng)可以采取的措施使緩沖區(qū)溢出更加困難。

修復(fù)緩沖區(qū)溢出

方法 #1: 避免 C 代碼中的錯誤。

  • 難以或不可能實現(xiàn)。

  • 程序員應(yīng)仔細檢查緩沖區(qū)、字符串、數(shù)組等的大小。特別是,程序員應(yīng)使用考慮到緩沖區(qū)大小的標準庫函數(shù)(strncpy() 而不是 strcpy()fgets() 而不是 gets() 等)。

  • 現(xiàn)代版本的 gcc 和 Visual Studio 在程序使用不安全函數(shù)(如 gets())時會發(fā)出警告。一般來說,你不應(yīng)該忽略編譯器警告。 將警告視為錯誤!

  • 好處: 首先避免問題!

  • 壞處: 很難確保代碼是無錯誤的,特別是如果代碼庫很大。此外,應(yīng)用程序本身可能定義不使用fgets()strcpy()作為基本操作的緩沖區(qū)操作函數(shù)。

方法 2: 構(gòu)建工具來幫助程序員找到錯誤。

  • 例如,我們可以使用靜態(tài)分析在編譯之前找到源代碼中的問題。想象一下,如果你有這樣一個函數(shù):

foo.c:

 void foo(int *p) {int offset;int *z = p + offset;if(offset > 7){bar(offset);}} 
  • 通過靜態(tài)分析控制流,我們可以知道 offset 在未初始化的情況下被使用。

  • if語句還限制了我們可能傳播到 bar 的偏移量。

  • 我們將在后續(xù)講座中更多地討論靜態(tài)分析。

  • 提供隨機輸入的“模糊器”可以有效地發(fā)現(xiàn)錯誤。請注意,模糊化可以與靜態(tài)分析結(jié)合以最大化代碼覆蓋率!

  • 壞處: 很難證明完全沒有錯誤,尤其是對于像 C 這樣的不安全代碼。

  • 好處: 即使是部分分析也是有用的,因為程序應(yīng)該變得更少有錯誤。例如,松散的邊界檢查可能無法捕捉所有內(nèi)存錯誤,但它可以檢測到許多重要類型。

方法 3: 使用內(nèi)存安全語言(JavaScript,C#,Python)。

  • 好處: 通過不向程序員暴露原始內(nèi)存地址,并通過自動處理垃圾回收來防止內(nèi)存損壞錯誤。

  • 壞處: 低級運行時代碼確實使用原始內(nèi)存地址。因此,運行時核心仍然需要正確。例如,堆噴射攻擊

    • NOZZLE:防御堆噴射代碼注入攻擊

    • 撰寫利用教程第 11 部分:堆噴射揭秘

  • 壞處: 仍然有很多使用不安全語言(FORTRAN 和 COBOL)的遺留代碼。

  • 壞處: 也許你需要訪問低級硬件功能(例如,你正在編寫設(shè)備驅(qū)動程序)

  • 壞處: 性能比調(diào)優(yōu)良好的 C 應(yīng)用程序差?

    • 過去是一個更大的問題,但硬件和高級語言變得更好了。

      • JIT 編譯萬歲!

      • asm.js的性能接近本機 C++性能的 2 倍!

      • 使用謹慎的編碼來避免關(guān)鍵路徑中的垃圾回收抖動。

      • 也許你是一個不懂得如何選擇合適工具的壞人/ 語言沙文主義者。如果你的任務(wù)是 I/O 綁定的,原始計算速度就不那么重要了。另外,不要成為那個用 C 語言編寫文本處理程序的笨蛋。

上述 3 種方法都是有效且廣泛使用的,但在實踐中緩沖區(qū)溢出仍然是一個問題。

  • 大量/復(fù)雜的用 C 語言編寫的遺留代碼非常普遍。

  • 即使是用 C/C++編寫的新代碼也可能存在內(nèi)存錯誤。

盡管存在有缺陷的代碼,我們?nèi)绾螠p輕緩沖區(qū)溢出?

  • 在“傳統(tǒng)”緩沖區(qū)溢出中發(fā)生了兩件事:

    • 對手控制執(zhí)行(程序計數(shù)器)。

    • 對手執(zhí)行一些惡意代碼。

  • 這兩個步驟存在哪些困難?

    • 需要覆蓋一個代碼指針(稍后被調(diào)用)。常見目標是使用堆棧上的緩沖區(qū)的返回地址。在實踐中,任何內(nèi)存錯誤都可能起作用。函數(shù)指針,C++ vtables,異常處理程序等。

    • 需要一些有趣的代碼在進程的內(nèi)存中。這通常比#1 更容易,因為:

      • 在緩沖區(qū)中放置代碼很容易,因此

      • 進程中已經(jīng)包含了許多可能被利用的代碼。

      • 然而,攻擊者需要將這段代碼放在可預(yù)測的位置,以便攻擊者可以將代碼指針設(shè)置為指向惡意代碼!

緩解方法 1:金絲雀(例如,StackGuard,gcc 的 SSP)

  • 理念:覆蓋代碼指針是可以接受的,只要我們在調(diào)用之前捕捉到它。

  • 較早的一個系統(tǒng):StackGuard

    • 在進入時在堆棧上放置一個金絲雀,在返回前檢查金絲雀值。

    • 通常需要源代碼;編譯器插入金絲雀檢查。

    • Q: 堆棧圖中的金絲雀在哪里? A: 金絲雀必須放在堆棧上返回地址的“前面”,這樣任何溢出重寫返回地址也將重寫金絲雀。

堆棧布局:

 |                  |+------------------+entry %esp ----> |  return address  |    ^+------------------+    |new %ebp ------> |    saved %ebp    |    |+------------------+    ||     CANARY       |    | Overflow goes+------------------+    | this way.|     buf[127]     |    ||       ...        |    ||      buf[0]      |    |+------------------+|                  | 
  • Q: 假設(shè)編譯器總是將金絲雀設(shè)為 4 個字節(jié)的'a'字符。這有什么問題嗎?

  • A: 對手可以在緩沖區(qū)溢出中包含適當(dāng)?shù)慕鸾z雀值!

  • 因此,金絲雀必須要么難以猜測,要么可以容易猜測但仍然能夠抵御緩沖區(qū)溢出。以下是這些方法的示例。

    • “終結(jié)符金絲雀”:四個字節(jié)(0,CR,LF,-1)

    • 理念:許多 C 函數(shù)將這些字符視為終結(jié)符(例如,gets()sprintf())。因此,如果金絲雀與這些終結(jié)符之一匹配,那么進一步的寫入將不會發(fā)生。

    • 在程序初始化時生成隨機金絲雀:今天更常見(但是,你需要良好的隨機性!)。

堆棧金絲雀不會捕捉到哪些類型的漏洞?

  • 在金絲雀之前覆蓋函數(shù)指針變量。

  • 攻擊者可以覆蓋數(shù)據(jù)指針,然后利用它進行任意內(nèi)存寫入。

數(shù)據(jù)指針示例:

 int *ptr = ...;char buf[128];gets(buf);  // Buffer is overflowed, and overwrites ptr.*ptr = 5;   // Writes to an attacker-controlled address!// Canaries can't stop this kind of thing. 
  • 堆對象溢出(函數(shù)指針,C++ vtables)。

  • malloc/free溢出

malloc 示例:

 int main(int argc, char **argv) {char *p, *q;p = malloc(1024);q = malloc(1024);if(argc >= 2)strcpy(p, argv[1]);free(q);free(p);return 0;} 
  • 假設(shè)屬于pq的兩個內(nèi)存塊在內(nèi)存中是相鄰/附近的。

  • 假設(shè)mallocfree表示內(nèi)存塊如下:

malloc 內(nèi)存塊:

 +----------------+  |                |     |   App data     |     |                |      Allocated memory block+----------------+     |   size         |     +----------------+  +----------------+|   size         |+----------------+|  ...empty...   |+----------------+  |   bkwd ptr     |     +----------------+          |   fwd ptr      |      Free memory block+----------------+     |   size         |     +----------------+ 
  • 因此,在p的緩沖區(qū)溢出將覆蓋q內(nèi)存塊中的大小值!為什么這是一個問題?

    • 當(dāng)free()合并兩個相鄰的空閑塊時,需要操作bkwdfwd指針…

    • …并且指針計算使用大小來確定空閑內(nèi)存塊結(jié)構(gòu)的位置!

free()內(nèi)部:

 p = get_free_block_struct(size);bck = p->bk;fwd = p->fd;fwd->bk = bck;  // Writes memory!bck->fd = fwd;  // Writes memory! 
  • 空閑內(nèi)存塊表示為 C struct;通過破壞大小值,攻擊者可以強制free()在位于攻擊者控制的內(nèi)存中的偽造struct上操作,并具有攻擊者控制的值用于前向和后向指針。

  • 如果攻擊者知道free()如何更新指針,他可以使用該更新代碼將任意值寫入任意位置。例如,攻擊者可以覆蓋返回地址。

    • 實際細節(jié)更加復(fù)雜;如果你對血腥細節(jié)感興趣,請訪問這里

    • 高層次的觀點是,棧金絲雀不會阻止這種攻擊,因為攻擊者正在“越過”金絲雀并直接寫入返回地址!

緩解方法 2: 邊界檢查

  • 總體目標: 通過檢查指針是否在范圍內(nèi)來防止指針誤用。

  • 挑戰(zhàn): 在 C 語言中,很難區(qū)分有效指針和無效指針。例如,假設(shè)一個程序分配了一個字符數(shù)組… char x[1024];

  • … 以及該數(shù)組中的某個位置的指針,例如,char *y = &x[107];

  • 增加y以訪問后續(xù)元素是否可以?

    • 如果x代表一個字符串緩沖區(qū),也許是。

    • 如果x代表一個網(wǎng)絡(luò)消息,也許不。

    • 如果程序使用聯(lián)合體,情況會變得更加復(fù)雜。

聯(lián)合體示例:

 union u{int i;struct s{int j;int k;};};int *ptr = &(u.s.k); // Does this point to valid data? 
  • 問題 是,在 C 語言中,指針不會編碼關(guān)于該指針的預(yù)期使用語義的信息

  • 因此,很多工具不會嘗試猜測這些語義。相反,這些工具的目標并不像“完全正確”的指針語義那樣高遠:這些工具只是強制執(zhí)行堆對象和棧對象的內(nèi)存邊界。在高層次上,這是目標:

    • 對于從p派生出的指針p'p'只能被解引用以訪問屬于p的有效內(nèi)存區(qū)域。
  • 強制執(zhí)行內(nèi)存邊界是一個比強制“完全正確”指針語義的目標。程序仍然可以通過惡意方式踐踏其內(nèi)存而自掘墳?zāi)?#xff08;例如,在聯(lián)合體示例中,應(yīng)用程序可能會寫入指針,盡管未定義)。

  • 然而,邊界檢查仍然很有用,因為它可以防止任意內(nèi)存覆寫。程序只能踐踏其內(nèi)存,如果該內(nèi)存實際上已分配!這在 C 語言世界中被認為是進步。

  • 邊界檢查的一個缺點是通常需要對編譯器進行更改,并且必須使用新編譯器重新編譯程序。如果只能訪問二進制文件,則這是一個問題。

有哪些實現(xiàn)邊界檢查的方法?

邊界檢查方法 #1: 電子圍欄

  • 這是一個舊方法,其優(yōu)點在于簡單。

  • 思路: 將每個堆對象與一個守衛(wèi)頁對齊,并使用頁表確保對守衛(wèi)頁的訪問導(dǎo)致故障。

電子圍欄:

 +---------+| Guard   ||         |  ^+---------+  | Overflows cause a page exception|  Heap   |  ||  obj    |  |+---------+ 
  • 這是一種方便的調(diào)試技術(shù),因為堆溢出會立即導(dǎo)致崩潰,而不是悄無聲息地破壞堆并在未來某個不確定的時間導(dǎo)致失敗。

  • 重要優(yōu)勢:無需源代碼即可運行:無需更改編譯器或重新編譯程序!

    • 確實需要重新鏈接它們,以便它們使用實現(xiàn)電子圍欄的新版本的malloc。
  • 主要缺點:巨大的開銷!每頁只有一個對象,并且您有一個未用于“真實”數(shù)據(jù)的虛擬頁面的開銷。

  • 摘要:電子圍欄可以作為調(diào)試技術(shù)很有用,并且可以防止堆對象的一些緩沖區(qū)溢出。然而,電子圍欄無法保護堆棧,并且內(nèi)存開銷太高,無法在生產(chǎn)系統(tǒng)中使用。

**邊界檢查方法#2:**胖指針

  • **想法:**修改指針表示以包含邊界信息?,F(xiàn)在,指針包括關(guān)于生存在該內(nèi)存區(qū)域中的對象的邊界信息。

示例:

 Regular 32-bit pointer  +-----------------+| 4-byte address  |+-----------------+Fat pointer (96 bits)+-----------------+----------------+---------------------+| 4-byte obj_base | 4-byte obj_end | 4-byte curr_address |+-----------------+----------------+---------------------+ 
  • 您需要修改編譯器并重新編譯程序以使用胖指針。編譯器生成的代碼會在它解引用地址超出自己的base ... end范圍的指針時中止程序。

示例:

 int *ptr = malloc(sizeof(int) * 2);while(1){*ptr = 42;       <----------|ptr++;                      |}                               |______________________________||This line checks the current address of the pointer andensures that it's in-bounds. Thus, this line will failduring the third iteration of the loop. 
  • **問題#1:**檢查所有指針解引用可能很昂貴。C 社區(qū)討厭昂貴的東西,因為 C 就是關(guān)于速度速度速度。

  • **問題#2:**胖指針與許多現(xiàn)有軟件不兼容。

    • 你不能將胖指針傳遞給未修改的庫。

    • 你不能在固定大小的數(shù)據(jù)結(jié)構(gòu)中使用胖指針。例如,sizeof(that_struct)將會改變!

    • 對胖指針的更新不是原子的,因為它們跨越多個字。一些程序假設(shè)指針寫入是原子的。

邊界檢查方法#3:影子數(shù)據(jù)結(jié)構(gòu)

**想法:**使用影子數(shù)據(jù)結(jié)構(gòu)來跟蹤邊界信息(Jones and Kelly,Baggy bounds)。

  • 對于每個分配的對象,存儲對象的大小。例如:

    • 記錄傳遞給 malloc 的值:

      • char *p = malloc(mem_size);
    • 對于靜態(tài)變量,值由編譯器確定:

      • char p[256];
    • 對于每個指針,我們需要對兩個操作進行干預(yù):

      1. 指針算術(shù):char *q = p + 256;

      2. 指針解引用:char ch = *q;

    • Q: 為什么我們需要對解引用進行干預(yù)?不能只進行算術(shù)嗎?

    • A: 無效指針并不總是一個錯誤!例如,數(shù)組最后一個元素之外的一個元素的指針可能被用作循環(huán)中的停止測試。應(yīng)用程序還可以執(zhí)行一些愚蠢的操作,如:

      • 模擬從 1 開始的數(shù)組

      • 計算 p+(a-b)為(p+a)-b

      • 生成稍后檢查有效性的 OOB 指針

    • 因此,僅僅創(chuàng)建無效指針不應(yīng)該導(dǎo)致程序失敗。

    • Q: 為什么我們需要對算術(shù)進行干預(yù)?不能只進行解引用嗎?

    • A: 干預(yù)算術(shù)是允許我們跟蹤指針的來源并設(shè)置 OOB 位的原因。沒有 OOB,我們將無法確定派生指針何時超出其基本對象的邊界。

  • 挑戰(zhàn) 1: 我們?nèi)绾握业匠R?guī)指針(即在邊界內(nèi)的指針)的邊界信息?

    • 天真: 使用哈希表或區(qū)間樹將地址映射到邊界。

      • 好: 空間高效(僅存儲正在使用的指針的信息,而不是所有可能的地址)。

      • 不好: 查找慢(每次查找多次內(nèi)存訪問)。

    • 天真: 使用數(shù)組存儲每個內(nèi)存地址的邊界信息。

      • 好: 快速!

      • 不好: 內(nèi)存開銷很高。

  • 挑戰(zhàn) 2: 我們?nèi)绾螐娭圃浇缰羔樈庖檬?#xff1f;

    • 天真: 對每個指針解引用進行檢測。

      • 好: 哦,它有效。

      • 不好: 昂貴。我們必須為每次解引用執(zhí)行額外的代碼!

Baggy bounds 方法:5 個技巧
  • Trick 1: 將每個分配向上舍入為 2 的冪,并將分配的起始對齊到該 2 的冪。

  • Trick 2: 將每個范圍限制表示為log_2(alloc_size)。

    • 對于 32 位指針,只需要 5 位來表示可能的范圍:我們只能分配 32 種不同大小的對象:21 = 2 字節(jié),22 = 4 字節(jié)...,231 字節(jié)或 232 字節(jié),并且我們存儲分配大小的以 2 為底的對數(shù),這是一個介于 1 和 32 之間的數(shù)字,因此我們只需要 5 位來表示它。
  • Trick 3: 在線性數(shù)組中存儲限制信息:每個條目一個字節(jié)的快速查找。此外,我們可以使用虛擬內(nèi)存按需分配數(shù)組!

  • Trick 4: 以插槽粒度(例如,16 字節(jié))分配內(nèi)存:更少的數(shù)組條目。

    • 這意味著最小分配大小為 16 字節(jié)

    • …而且,由于指針將對齊到其分配大小邊界,這意味著指針的最后 4 位都是零

示例:

 slot_size = 16p = malloc(16);  -->  table[p/slot_size] = 4;p = malloc(32);  -->  table[p/slot_size] = 5;\->  table[(p/slot_size) + 1] = 5; 
  • Trick 4 (續(xù)):

    • 現(xiàn)在,給定一個已知的好指針p,和一個派生指針p',我們可以通過檢查這兩個指針的地址位中是否有相同的前綴,并且它們只在它們的e個最低有效位上有所不同,其中e等于分配大小的對數(shù),來測試p'是否有效。

示例:

 C code------ p' = p + i;Bounds check------------size = 1 << table[p >> log_of_slot_size];base = p & ~(size - 1);(p' >= base) && ((p' - base) < size)Optimized bounds check----------------------(p^p') >> table[p >> log_of_slot_size] == 0 
  • Trick 5: 使用虛擬內(nèi)存系統(tǒng)來防止越界解引用:在 OOB 指針中設(shè)置最高有效位,然后將地址空間上半部分的頁面標記為不可訪問。這樣,我們就不必對指針解引用進行檢測以防止錯誤的內(nèi)存訪問!

示例代碼(假設(shè)slot_size=16

 char *p = malloc(44);     //Note that the nearest power of 2 (i.e.,//64 bytes) are allocated. So, there are//64/(slot_size) = 4 bounds table entries//that are set to log_2(64) = 6.char *q = p + 60;         //This access is ok: It's past p's object//size of 44, but still within the baggy//bounds of 64.char *r = q + 16;         //r is now at an offset of 60+16=76 from//p. This means that r is (76-64)=12 bytes//beyond the end of p. This is more than//half a slot away, so baggy bounds will//raise an error.char *s = q + 8;          //s is now at an offset of 60+8=68 from p.//So, s is only 4 bytes beyond the baggy//bounds, which is les than half a slot//away. No error is raised, but the OOB//high-order bit is set in s, so that s//cannot be dereferenced.char *t = s - 32;         //t is now back inside the bounds, so//the OOB bit is cleared. 

對于 OOB 指針,高位被設(shè)置(如果 OOB 在半個插槽內(nèi))。- 通常,操作系統(tǒng)內(nèi)核位于上半部分,通過分頁硬件保護自身。- 問: 為什么越界是半個插槽?

那么作業(yè)問題的答案是什么?

 char *p = malloc(256);char *q = p + 256;char ch = *q;  // Does this raise an exception?// Hint: How big is the baggy bound for p? 
Baggy bounds 論文勘誤

Baggy bounds 論文中的一些錯誤:

  • 圖 3,顯式邊界檢查應(yīng)該生成如下大小:

    • size = 1 << table[p >> log_of_slot_size]
  • 圖 3,優(yōu)化的邊界檢查應(yīng)該是:

    • (p^p') >> table[p >> log_of_slot_size] == 0
  • 圖 5 和 18,指針算術(shù)代碼應(yīng)該是以下之一:

    • char *p = &buf[i];

    • char *p = buf + i;

Baggy bounds(續(xù))

注意: 這些講座筆記是從 2014 年 6.858 課程網(wǎng)站上發(fā)布的筆記中稍作修改而來。

示例代碼:(假設(shè)slot_size = 16

 char *p = malloc(44); // Note that the nearest power of 2 (i.e.,// 64 bytes) are allocated. So, there are// 64/(slot_size) = 4 bounds table entries// that are set to log_2(64) = 6.char *q = p + 60;     // This access is ok: It's past p's object// size of 44, but still within the baggy// bounds of 64.char *r = q + 16;     // ERROR: r is now at an offset of 60+16=76// from p. This means that r is (76-64)=12// beyond the end of p. This is more than// half a slot away, so baggy bounds will// raise an error.char *s = q + 8;      // s is now at an offset of 60+8=68 from p.// So, s is only 4 bytes beyond the baggy// bounds, which is less than half a slot// away. No error is raised, but the OOB// high-order bit is set in s, so that s// cannot be derefernced.char *t = s - 32;     // t is now back inside the bounds, so// the OOB bit is cleared. 

對于越界指針,高位被設(shè)置(如果在半個插槽內(nèi)越界)。

  • 通常,操作系統(tǒng)內(nèi)核位于上半部分,通過分頁硬件保護自身。

  • Q: 為什么要為越界分配半個插槽?

那么作業(yè)問題的答案是什么?

 char *p = malloc(255);char *q = p + 256;char ch = *q;  // Does this raise an exception?// Hint: How big is the baggy bound for p? 

寬松邊界檢查是否必須檢測每個內(nèi)存地址計算和訪問?

  • ,靜態(tài)分析可以證明某些地址始終是安全的。但是,某些地址計算是“不安全”的,因為無法靜態(tài)確定其值的邊界。這些不安全的變量需要檢查。

處理函數(shù)調(diào)用參數(shù)有點棘手,因為 x86 調(diào)用約定是固定的,即硬件期望棧上的某些內(nèi)容放在特定位置。

  • 但是,我們可以將不安全的參數(shù)復(fù)制到一個單獨的區(qū)域,并確保復(fù)制的參數(shù)對齊和受保護。

  • Q: 我們是否必須在函數(shù)返回時用復(fù)制的值覆蓋原始參數(shù)?

  • A: 不,因為在 C 語言中一切都是按值傳遞的!

寬松邊界檢查如何確保與現(xiàn)有庫的二進制兼容性?

特別是,寬松邊界代碼如何與由未經(jīng)檢測的代碼分配的內(nèi)存指針交互?

  • 解決方案: 邊界表中的每個條目都初始化為值 31,這意味著相應(yīng)的指針具有 231 的內(nèi)存邊界(這是所有可尋址內(nèi)存)。

    • 經(jīng)過檢測的代碼中進行內(nèi)存分配時,邊界條目如前所述設(shè)置,并在釋放內(nèi)存時重置為 31。

    • 分配給未經(jīng)檢測的代碼的內(nèi)存永遠不會改變邊界表條目的默認值 31;因此,當(dāng)經(jīng)過檢測的代碼與這些指針交互時,邊界錯誤永遠不會發(fā)生

例子:

 Contiguous range ofmemory used for theheap+-------------------+|                   ||                   || Heap allocated by ||   uninstrumented  |---+|       code        |    \      Bounds table|                   |     \+-------------------+      \   +-----------+|                   |       +->|           ||                   |          | Always 31 || Heap allocated by |          |           || instrumented code |          +-----------+|                   |          | Set using ||                   |--------->| baggy bnds|+-------------------+          +-----------+ 
  • 這一切意味著什么?

    • 無法檢測在未經(jīng)檢測的代碼中生成的越界指針。

    • 無法檢測傳遞給庫的越界指針何時再次進入邊界內(nèi)。

      • Q: 為什么?

      • A: 因為未經(jīng)檢測的代碼中沒有指針檢查可以清除高位越界位!

      • Q: 為什么要檢測strcpy()memcpy()

      • A: 否則,這些函數(shù)就是未經(jīng)檢測的代碼,并且會遇到我們剛剛討論過的相同問題。例如,現(xiàn)成的strcpy()不能確保目標有足夠的空間來存儲源!

寬松位如何利用 64 位地址空間?

可以擺脫存儲邊界信息的表,并將其放入指針中。

 Regular pointer       +---------------+-------+------------------------+|      zero     |  size |   supported addr space |+---------------+-------+------------------------+21          5             38OOB pointer+--------+------+-------+------------------------+| offset | size |  zero |   supported addr space |+--------+------+-------+------------------------+13      5       8             38 

這類似于一個胖指針,但具有以下優(yōu)點……

  1. 標記指針與常規(guī)指針大小相同

  2. 對它們的寫入是原子的

……以便不破壞程序員的期望,并且數(shù)據(jù)布局保持不變。

還要注意,使用標記指針,我們現(xiàn)在可以跟蹤遠離基本指針多得多的越界指針。這是因為現(xiàn)在我們可以使用偏移量標記指針,指示它們距離基本指針有多遠。在 32 位世界中,如果沒有額外的數(shù)據(jù)結(jié)構(gòu),我們無法跟蹤越界偏移量!

在 baggy bounds 系統(tǒng)中仍然可以發(fā)動緩沖區(qū)溢出攻擊嗎?

是的,因為這個世界充滿了悲傷。

  • 可能會利用未經(jīng)檢測的庫中的漏洞。

  • 可能會利用時間漏洞(使用后釋放)。

  • 混合緩沖區(qū)和代碼指針

例子:

 struct {char buf[256];void (*f) (void);} my_type; 

請注意*f不是分配的類型,因此在調(diào)用期間與其解引用相關(guān)聯(lián)的邊界檢查不存在。因此,如果s.buf溢出(例如,由未經(jīng)檢測的庫中的錯誤引起),并且s.f被損壞,那么對f的調(diào)用不會導(dǎo)致邊界錯誤!

重新排列 f 和 buf 會有幫助嗎?

  • 可能會破壞依賴結(jié)構(gòu)布局的應(yīng)用程序。

  • 如果這是一個(struct my_type)數(shù)組,可能不會有幫助。

一般來說,邊界檢查的成本是什么?

  • 邊界信息的空間開銷(胖指針或 baggy bounds 表)。

  • Baggy bounds 還會為 buddy 分配器使用的額外填充內(nèi)存增加空間開銷(盡管所有流行的動態(tài)內(nèi)存分配算法都會有一定程度的開銷)。

  • 指針算術(shù)和解引用的 CPU 開銷。

  • 虛警!

    • 未使用的越界指針。

    • 臨時超出邊界指針超過slot_size/2

    • 指針到整數(shù)的轉(zhuǎn)換和反向轉(zhuǎn)換。

    • 將越界指針傳遞給未經(jīng)檢查的代碼(高地址位被設(shè)置,因此如果未經(jīng)檢查的代碼使用該指針進行算術(shù)運算,可能會導(dǎo)致混亂)。

  • 需要大量編譯器支持。

因此,baggy bounds 檢查是一種減輕有缺陷代碼中緩沖區(qū)溢出的方法。

實現(xiàn)邊界檢查的更多方法

方法 4:非可執(zhí)行內(nèi)存(AMD 的 NX 位,Windows DEP,W^X 等)

  • 現(xiàn)代硬件允許為內(nèi)存指定讀取、寫入和執(zhí)行權(quán)限。(R、W 權(quán)限很久以前就有了;執(zhí)行權(quán)限是最近才有的。)

  • 可以將堆棧標記為不可執(zhí)行,這樣對手就無法運行他們的代碼。

  • 更一般地,一些系統(tǒng)執(zhí)行“W^X”,意味著所有內(nèi)存要么可寫,要么可執(zhí)行,但不能同時。 (當(dāng)然,既不可寫也不可執(zhí)行也是可以的。)

    • 優(yōu)勢: 可能無需進行任何應(yīng)用程序更改即可運行。

    • 優(yōu)勢: 硬件一直在監(jiān)視你,不像操作系統(tǒng)。

    • 缺點: 動態(tài)生成代碼更困難(尤其是使用 W^X)。

      • 像 Java 運行時、Javascript 引擎這樣的 JIT 會即時生成 x86 代碼。

      • 可以通過先寫入,然后更改為可執(zhí)行來解決問題。

方法 5:隨機化內(nèi)存地址(ASLR,堆棧隨機化等)

  • 觀察:許多攻擊在 shellcode 中使用硬編碼地址![攻擊者抓取一個二進制文件并使用 gdb 來找出東西的位置。]

  • 因此,我們可以使攻擊者難以猜測有效的代碼指針。

    • 堆棧隨機化:將堆棧移動到隨機位置,并/或在堆棧變量之間放置填充。這使得攻擊者更難確定:

      • 當(dāng)前幀的返回地址位于何處

      • 攻擊者的 shellcode 緩沖區(qū)將位于何處

    • 隨機化整個地址空間(地址空間布局隨機化):隨機化堆棧、堆、DLL 的位置等。

      • 依賴于很多代碼是可重定位的這一事實。

      • 動態(tài)加載器可以為每個庫、程序選擇隨機地址。

      • 對手不知道 system()等函數(shù)的地址。

    • 這仍然可以被利用嗎?

      • 對手可能猜測隨機性。特別是在 32 位機器上,沒有太多的隨機位(例如,1 位屬于內(nèi)核/用戶模式劃分,12 位不能被隨機化,因為內(nèi)存映射頁面需要與頁面邊界對齊等)。

      • 例如,攻擊者可能進行緩沖區(qū)溢出并嘗試用usleep(16)的地址覆蓋返回地址,然后查看連接是否在 16 秒后掛起,或者是否崩潰(在這種情況下,服務(wù)器會使用相同的 ASLR 偏移量 fork 一個新的 ASLR 進程)。 usleep()可能在 21? 或 22? 個地方之一。更多細節(jié)。

      • ASLR 在 64 位機器上更實用(很容易有 32 位的隨機性)。

  • 對手可能提取隨機性。

    • 程序可能生成包含指針的堆棧跟蹤或錯誤消息。

    • 如果對手可以運行一些代碼,他們可能能夠提取真實地址(JIT 編譯的代碼?)。

    • Flash 的字典(哈希表)中的可愛地址泄漏:

      • 讓受害者訪問您的 Flash 啟用頁面(例如,購買廣告)。

      • 哈希表在內(nèi)部計算鍵的哈希值。

      • 整數(shù)的哈希值是整數(shù)本身。

      • 對象的哈希值是其內(nèi)存地址。

      • 遍歷哈希表是從最低哈希鍵到最高哈希鍵進行的。

      • 因此,攻擊者創(chuàng)建一個字典,插入一個包含 shellcode 的字符串對象,然后向字典中插入一堆數(shù)字。

      • 通過遍歷字典,攻擊者可以確定字符串對象位于何處,看看對象引用落在哪些整數(shù)之間!

      • 現(xiàn)在,用 shellcode 地址覆蓋代碼指針并繞過 ASLR!

  • 對手可能不關(guān)心確切要跳轉(zhuǎn)到哪里。

    • 例如:“堆噴灑”:填充內(nèi)存以便隨機跳轉(zhuǎn)是可以的!
  • 對手可能利用一些未隨機化的代碼(如果存在這樣的代碼)。

  • 隨機化的一些其他有趣用途:

    • 系統(tǒng)調(diào)用隨機化(每個進程有自己的系統(tǒng)調(diào)用號碼)。

    • 指令集隨機化,以便攻擊者不能輕易確定特定程序?qū)嵗?#34;shellcode"是什么樣子。

      • 例子: 想象一下,處理器有一個特殊的寄存器來保存“解碼密鑰”。每個特定應(yīng)用程序的安裝都與一個隨機密鑰相關(guān)聯(lián)。應(yīng)用程序中的每條機器指令都與該密鑰進行異或運算。當(dāng)操作系統(tǒng)啟動進程時,它設(shè)置解碼密鑰寄存器,處理器使用此密鑰解碼指令后再執(zhí)行它們。

實際上使用了哪些緩沖區(qū)溢出防御措施?

  • gcc 和 MSVC 默認啟用棧保護。

  • Linux 和 Windows 默認包含 ASLR 和 NX。

  • 由于:

    • 性能開銷

    • 需要重新編譯程序

    • 誤報:安全工具中的常見主題:誤報阻止了工具的采用!通常,一些遺漏但沒有誤報比零遺漏但有誤報更好。

返回導(dǎo)向編程(ROP)

ASLR 和 DEP 是非常強大的防御技術(shù)。

  • DEP 防止攻擊者執(zhí)行自己選擇的棧代碼。

  • ASLR 可以防止攻擊者確定 shellcode 或返回地址的位置。

  • 但是,如果攻擊者能夠找到位于已知位置的具有已知功能的預(yù)先存在的代碼呢?那么,攻擊者可以調(diào)用該代碼來做壞事。

    • 當(dāng)然,預(yù)先存在的代碼并不是故意惡意,因為它是應(yīng)用程序的正常部分。

    • 但是,攻擊者可以傳遞意外的參數(shù)給該代碼,或者跳轉(zhuǎn)到代碼的中間并僅執(zhí)行該代碼的所需部分。

這種攻擊稱為返回導(dǎo)向編程,或ROP。為了理解 ROP 的工作原理,讓我們看一個具有安全漏洞的簡單 C 程序。示例改編自此處。

 void run_shell(){system("/bin/bash");}void process_msg(){char buf[128];gets(buf);} 

假設(shè)系統(tǒng)不使用 ASLR 或棧保護,但使用了 DEP。process_msg()存在明顯的緩沖區(qū)溢出,但攻擊者無法利用此溢出在buf中執(zhí)行 shellcode,因為 DEP 使棧不可執(zhí)行。然而,run_shell()函數(shù)看起來很誘人… 攻擊者如何執(zhí)行它?

  • 攻擊者反匯編程序并找出run_shell()的起始地址在哪里。

  • 攻擊者發(fā)起緩沖區(qū)溢出,并用run_shell()的地址覆蓋process_msg()的返回地址。砰!攻擊者現(xiàn)在可以訪問以應(yīng)用程序權(quán)限運行的 shell。

例子:

 +------------------+entry %ebp ----> | .. prev frame .. ||                  |  |                  |+------------------+entry %esp ----> |  return address  | ^    <--Gets overwritten +------------------+ |       with address ofnew %ebp ------> |    saved %ebp    | |       run_shell()+------------------+ ||     buf[127]     | ||       ...        | ||      buf[0]      | |new %esp ------> +------------------+ 

這是我們已經(jīng)看過的緩沖區(qū)溢出的直接擴展。但是我們?nèi)绾蜗蛭覀円D(zhuǎn)到的函數(shù)傳遞參數(shù)呢?

 char *bash_path = "/bin/bash";void run_cmd(){system("/something/boring");}void process_msg(){char buf[128];gets(buf);} 

在這種情況下,我們要傳遞的參數(shù)已經(jīng)位于程序代碼中。程序中還存在一個對system()的調(diào)用,但該調(diào)用并未傳遞我們想要的參數(shù)。

我們知道system()必須與我們的程序鏈接。因此,使用我們可靠的朋友 gdb,我們可以找到system()函數(shù)的位置以及 bash_path 的位置。

要使用bash_path參數(shù)調(diào)用system(),我們必須設(shè)置堆棧,以便在跳轉(zhuǎn)到它時,system()期望堆棧上有這些內(nèi)容:

 |        ...       |+------------------+|     argument     |  The system() argument.+------------------+%esp ---->   |    return addr   |  Where system() should +------------------+  ret after it hasfinished. 

因此,緩沖區(qū)溢出需要設(shè)置一個看起來像這樣的堆棧:

 +------------------+entry %ebp ----> | .. prev frame .. ||                  ||                  || *  - - - - - - - | ^|                  | | Address of bash_path + *  - - - - - - - | ||                  | | Junk return addr for system()+------------------+ |entry %esp ----> |  return address  | | Address of system()+------------------+ | new %ebp ------> |    saved %ebp    | | Junk+------------------+ ||     buf[127]     | ||       ...        | | Junk|      buf[0]      | |new %esp ------> +------------------+ | 

本質(zhì)上,我們所做的是為system()調(diào)用設(shè)置了一個虛假的調(diào)用幀!換句話說,我們模擬了編譯器如果真的想要設(shè)置一個對system()的調(diào)用會做什么。

如果字符串"/bin/bash"不在程序中怎么辦?

  • 我們可以將該字符串包含在緩沖區(qū)溢出中,然后使system()的參數(shù)指向該字符串。

     |    h\0           | ^| *  - - - - - - - | ||    /bas          | || *  - - - - - - - | ||    /bin          | |  <--------------------+| *  - - - - - - - | |                       ||                  | | Address of bash_path--++ *  - - - - - - - | ||                  | | Junk return addr from system()+------------------+ |
    entry %esp ----> |  return address  | | Address of system()+------------------+ | 
    new %ebp ------> |    saved %ebp    | | Junk+------------------+ ||     buf[127]     | ||       ...        | | Junk|      buf[0]      | |
    new %esp ------> +------------------+ | 
    

請注意,在這些示例中,我一直假設(shè)攻擊者使用了來自system()的無用返回地址。然而,攻擊者也可以將其設(shè)置為有用的內(nèi)容。

實際上,通過將其設(shè)置為有用的內(nèi)容,攻擊者可以鏈接調(diào)用在一起!

**目標:**我們想要多次調(diào)用system("/bin/bash")。假設(shè)我們找到了三個地址:

  • system()的地址

  • 字符串"/bin/bash"的地址

  • 這些 x86 操作碼的地址:

     pop %eax    //Pops the top-of-stack and puts it in %eaxret         //Pops the top-of-stack and puts it in %eip 
    

這些操作碼是“小工具”的一個示例。小工具是預(yù)先存在的指令序列,可以串聯(lián)在一起創(chuàng)建一個利用。請注意,有一些用戶友好的工具可以幫助您從現(xiàn)有二進制文件中提取小工具(例如,msfelfscan)。

 |                  | ^+ *  - - - - - - - + ||                  | | Address of bash_path -+ Fake calling+ *  - - - - - - - + |                       | frame for(4)         |                  | | Address of pop/ret  * + system()+ *  - - - - - - - + | (3)         |                  | | Address of system()+ *  - - - - - - - + |(2)         |                  | | Address of bash_path -+ Fake calling+ *  - - - - - - - + |                       | frame for(1)         |                  | | Address of pop/ret  * + system()+------------------+ |entry %esp ----> |  return address  | | Address of system()+------------------+ | new %ebp ------> |    saved %ebp    | | Junk+------------------+ ||     buf[127]     | ||       ...        | | Junknew %esp ------> |      buf[0]      | |+------------------+ | 

那么,這是如何工作的呢?記住,返回指令彈出棧頂并將其放入%eip。

  • 溢出的函數(shù)通過發(fā)出ret來終止。ret彈出棧頂(system()的地址)并將%eip設(shè)置為它。system()開始執(zhí)行,%esp現(xiàn)在在(1),并指向pop/ret小工具。

  • system()執(zhí)行完畢并調(diào)用ret。%esp從(1)->(2),因為ret指令彈出棧頂并將其分配給%eip。%eip現(xiàn)在是pop/ret小工具的開始。

  • pop/ret小工具中的 pop 指令從棧中丟棄bash_path變量。%esp現(xiàn)在在(3)。我們?nèi)匀辉?code>pop/ret小工具中!

  • pop/ret小工具中的ret指令彈出棧頂并將其放入%eip?,F(xiàn)在我們再次在system()中,并且%esp在(4)。

等等。

基本上,我們創(chuàng)建了一種新類型的機器,它由堆棧指針驅(qū)動,而不是常規(guī)指令指針!隨著堆棧指針沿著堆棧移動,它執(zhí)行的小工具的代碼來自預(yù)先存在的程序代碼,數(shù)據(jù)來自緩沖區(qū)溢出創(chuàng)建的堆棧數(shù)據(jù)。

這種攻擊規(guī)避了 DEP 保護–我們沒有生成任何新代碼,只是調(diào)用了現(xiàn)有的代碼!

棧讀取:打敗金絲雀

假設(shè):

  • 遠程服務(wù)器存在緩沖區(qū)溢出漏洞。

  • 服務(wù)器崩潰并重新啟動,如果金絲雀值設(shè)置為不正確的值。

  • 當(dāng)服務(wù)器重新啟動時,canary 不會重新隨機化,ASLR 也不會重新隨機化,例如,因為服務(wù)器使用 Linux 的 PIE 機制,并且使用 fork() 來創(chuàng)建新的工作進程而不是 execve()。

因此,要確定一個 8 字節(jié)的 canary 值:

 char canary[8];for(int i = 1; i <= 8; i++){  //For each canary byte . . .for(char c = 0; c < 256; c++){  //. . . guess the value.canary[i-1] = c;server_crashed = try_i_byte_overflow(i, canary);if(!server_crashed){//We've discovered i-th byte of the//the canary!break;}}} 

此時我們已經(jīng)有了 canary,但請記住,該攻擊假設(shè)服務(wù)器在崩潰后使用相同的 canary。

猜測一個字節(jié)的正確值平均需要 128 次猜測,因此在 32 位系統(tǒng)上,我們只需要 4*128=512 次猜測來確定 canary(在 64 位系統(tǒng)上,我們需要 8*128=1024 次)。

  • 比在 canary 上進行暴力破解攻擊要快得多(在具有 16/28 位 ASLR 隨機性的 32/64 位系統(tǒng)上,預(yù)期猜測次數(shù)為 21?22?)。

  • 暴力破解攻擊可以使用我們之前討論過的 usleep(16) 探測。

  • Canary 讀取可以擴展到讀取緩沖區(qū)溢出可以覆蓋的任意值!

因此,我們已經(jīng)討論了如果服務(wù)器在重新生成時不更改 canaries,我們?nèi)绾文軌驌魯‰S機化的 canaries。我們還展示了如何使用 gdb 和 gadgets 來執(zhí)行程序中預(yù)先存在的函數(shù),使用攻擊者控制的參數(shù)。但是如果服務(wù)器使用 ASLR 呢?這將阻止您使用離線分析來找到預(yù)先存在的函數(shù)的位置?

這就是今天講座論文討論的內(nèi)容。該論文假設(shè)我們使用的是 64 位機器,所以從現(xiàn)在開始,在本講座中我們也將假設(shè)如此。在這次討論中,主要的變化是函數(shù)參數(shù)現(xiàn)在是通過寄存器傳遞而不是通過棧傳遞。

盲目返回導(dǎo)向編程

步驟 1:找到一個 stop gadget

  • stop gadget 是指指向會掛起程序但不會崩潰的代碼的返回地址。

  • 一旦攻擊者能夠擊敗 canaries,他就可以覆蓋溢出函數(shù)的返回地址并開始猜測 stop gadget 的位置。如果客戶端網(wǎng)絡(luò)連接突然關(guān)閉,猜測的地址不是 stop gadget。如果連接保持打開,那么該 gadget 就是 stop gadget。

步驟 2:找到彈出棧中條目的 gadgets

  • 一旦你有了一個 stop gadget,你可以用它來找到其他將棧中條目彈出并存入寄存器的 gadgets。

  • 定位棧彈出 gadgets 的三個構(gòu)建塊:

    • probe: 潛在的棧彈出 gadget 的地址

    • stop: stop gadget 的地址

    • crash: 非可執(zhí)行代碼的地址(0x0)

示例: 找到一個彈出棧中一個元素的 gadget。

 sleep(10)^       ^+--- pop rax         /         \|    ret            /           \|     \--->[stop]  0x5....     0x5....|          [trap]  0x0         0x0    <-----------------++----------[probe] 0x4...8     0x4...c -->xor rax, rax  | Crash!ret           |\__________| 

當(dāng)你這樣做很多次后,你將擁有一系列彈出棧中一個元素然后返回的 gadgets。然而,你不會知道這些 gadgets 將彈出的值存儲在哪個 寄存器 中。

  • 你需要知道哪些寄存器用于存儲數(shù)據(jù),以便您可以發(fā)出系統(tǒng)調(diào)用。每個系統(tǒng)調(diào)用都期望其參數(shù)在一組特定的寄存器中。

  • 請注意,我們也不知道 syscall() 庫函數(shù)的位置。

步驟 3:找到 syscall() 并確定 pop gadgets 使用哪些寄存器

  • pause()是一個不帶參數(shù)的系統(tǒng)調(diào)用(因此忽略寄存器中的所有內(nèi)容)。

  • 要找到pause(),攻擊者在棧上鏈接所有的"pop x; ret"小工具,將pause()的系統(tǒng)調(diào)用號作為每個小工具的"參數(shù)"推送進去。在鏈的底部,攻擊者放置了syscall()的猜測地址。

     |                  | ^+ *  - - - - - - - + ||                  | | Guessed addr of syscall() + *  - - - - - - - + | |                  | | ...+ *  - - - - - - - + | |                  | | Sys call # for pause+ *  - - - - - - - + ||                  | | Address of pop rsi; ret //Gadget 2+ *  - - - - - - - + | |                  | | Sys call # for pause+------------------+ |
    entry %esp ----> |  return address  | | Address of pop rdi; ret //Gadget 1+------------------+ | 
    new %ebp ------> |    saved %ebp    | | Junk+------------------+ ||     buf[127]     | ||       ...        | | Junk
    new %esp ------> |      buf[0]      | |+------------------+ | 
    

因此,在這個鏈的末端,彈出小工具已經(jīng)將pause()的系統(tǒng)調(diào)用號放入了一堆寄存器中,希望包括rax,這是syscall()查找系統(tǒng)調(diào)用號的寄存器。

一旦這個超級小工具引發(fā)了暫停,我們就知道已確定了syscall()的位置?,F(xiàn)在我們需要確定哪個小工具將棧頂彈出到rax中。攻擊者可以通過逐步嘗試一個小工具并查看是否可以調(diào)用pause()來弄清楚這一點。

要識別任意的"pop x; ret"小工具,可以使用與您試圖找到的x寄存器相關(guān)的其他系統(tǒng)調(diào)用的技巧。

因此,這個階段的結(jié)果是知道"pop x; ret"小工具,syscall()的位置。

第 4 步:調(diào)用 write()

現(xiàn)在我們想要在服務(wù)器與攻擊者客戶端之間的網(wǎng)絡(luò)套接字上調(diào)用寫入調(diào)用。我們需要以下小工具:

 pop rdi; ret (socket)pop rsi; ret (buffer)pop rdx; ret (length)pop rax; ret (write syscall number)syscall 

我們必須猜測套接字的值,但這在實踐中相當(dāng)容易,因為 Linux 將進程限制為同時打開 1024 個文件描述符,并且新文件描述符必須是可用的最低文件描述符(因此猜測一個小文件描述符在實踐中效果很好)。

要測試我們是否猜對了文件描述符,只需嘗試寫入并查看是否收到任何內(nèi)容!

一旦我們有了套接字號碼,我們發(fā)出一個寫入請求,發(fā)送的數(shù)據(jù)是指向程序的.text段的指針!這使得攻擊者可以讀取程序的代碼(雖然已隨機化,但現(xiàn)在完全為攻擊者所知!)?,F(xiàn)在攻擊者可以直接找到更強大的小工具,并利用這些小工具打開一個 shell。

防御 BROP 攻擊

  • 每次崩潰后重新隨機化 canaries 和地址空間!

    • 使用exec()代替fork()來創(chuàng)建進程,因為fork()會將父進程的地址空間復(fù)制給子進程。

    • 有趣的是,Windows 不容易受到 BROP 攻擊的影響,因為 Windows 沒有fork()的等效功能。

  • 崩潰后休眠?

    • 現(xiàn)在 BROP 攻擊是一種拒絕服務(wù)攻擊!
  • 邊界檢查?

    • 高達 2 倍的性能開銷…

有關(guān) ROP 和 x86 調(diào)用約定的更多信息

  • x86 調(diào)用約定簡介

  • 返回導(dǎo)向編程簡介

  • 深入了解 ROP:返回導(dǎo)向編程的快速介紹

  • 返回導(dǎo)向編程:系統(tǒng)、語言和應(yīng)用

OKWS

注意: 這些講座筆記略有修改,來自 2014 年 6.858 課程網(wǎng)站上發(fā)布的筆記。

今天的講座:如何在 Unix 上構(gòu)建一個安全的 Web 服務(wù)器。我們實驗室 Web 服務(wù)器 zookws 的設(shè)計靈感來自于 OKWS。

特權(quán)分離

  • 大型安全理念

  • 將系統(tǒng)分割成各自具有特權(quán)的模塊

    • 想法: 如果一個模塊被破壞,那么其他模塊就不會被破壞
  • 經(jīng)常使用:

    • 虛擬機(例如,在自己的虛擬機中運行網(wǎng)站)

    • SSH(分離 sshd、agent)

  • 挑戰(zhàn):

    • 模塊需要共享

    • 需要操作系統(tǒng)支持

    • 需要仔細使用操作系統(tǒng)正確設(shè)置事物

    • 性能

OKWS

  • 特權(quán)分離的有趣案例研究

    • 服務(wù)之間有很多共享

      • 嚴格的分區(qū)不起作用
    • 大量的代碼

  • 在 OKcupid 之外并不廣泛使用

    • 許多網(wǎng)站都有他們的特權(quán)分離計劃

    • 但沒有描述他們計劃的論文

背景:Unix 中的安全和保護

  • 典型的主體:用戶 ID、組 ID(32 位整數(shù))。

    • 每個進程都有一個用戶 ID(uid)和一組組 ID(gid + grouplist)。

    • 出于大多是歷史原因,一個進程有一個 gid +額外的組列表。

    • 超級用戶主體(root)由uid=0表示,繞過大多數(shù)檢查。

  • Unix 中的對象+操作是什么,操作系統(tǒng)如何進行訪問控制?

    • 文件、目錄。

      • 文件操作:讀、寫、執(zhí)行、更改權(quán)限,…

      • 目錄操作:查找、創(chuàng)建、刪除、重命名、更改權(quán)限,…

      • 每個 inode 都有一個所有者用戶和組。

      • 每個 inode 對于用戶、組、其他人都有讀、寫、執(zhí)行權(quán)限。

      • 通常表示為寫入基數(shù) 8(八進制)的位向量;

        • 八進制很好用,因為每個數(shù)字是 3 位(讀、寫、執(zhí)行)。
      • 誰可以更改文件的權(quán)限?

        • 只有用戶所有者(進程 UID)。
      • 對文件進行硬鏈接:需要對文件有寫權(quán)限。

        • 可能的理由:配額。

        • 可能的理由:防止將/etc/passwd硬鏈接到具有全局可寫/var/mail/var/mail/root。

      • 目錄的執(zhí)行意味著能夠查找名稱(但不能 ls)。

      • 檢查進程打開文件/etc/passwd

        • 必須能夠在/中查找'etc',在/etc中查找'passwd'

        • 必須能夠打開/etc/passwd(讀或讀寫)。

      • 假設(shè)你想要文件對 group1 和 group2 的交集可讀。

        • 在 Unix 中是否可能實現(xiàn)這一點?
    • 文件描述符。

      • 文件打開時執(zhí)行的文件訪問控制檢查。

      • 一旦進程有一個打開的文件描述符,就可以繼續(xù)訪問。

      • 進程可以傳遞文件描述符(通過 Unix 域套接字)。

    • 進程。

      • 你可以對一個進程做什么?

        • 調(diào)試(ptrace),發(fā)送信號,等待退出并獲取狀態(tài),
      • 調(diào)試,發(fā)送信號:必須具有相同的 UID(幾乎)。

        • 各種例外,在實踐中這變得棘手。
      • 等待/獲取退出狀態(tài):必須是該進程的父進程。

    • 內(nèi)存。

      • 一個進程通常不能命名另一個進程的內(nèi)存。

      • 例外:調(diào)試機制。

      • 例外:內(nèi)存映射文件。

    • 網(wǎng)絡(luò)。

      • 操作。

        • 綁定到一個端口。

        • 連接到某個地址。

        • 讀/寫連接。

        • 發(fā)送/接收原始數(shù)據(jù)包。

      • 規(guī)則:

        • 只有 root(UID 0)可以綁定到低于 1024 的端口;

        • (例如,任意用戶不能在端口 80 上運行 Web 服務(wù)器。)

        • 只有 root 可以發(fā)送/接收原始數(shù)據(jù)包。

        • 任何進程都可以連接到任何地址。

        • 只能讀取/寫入進程具有文件描述符的連接上的數(shù)據(jù)。

      • 此外,防火墻施加自己的檢查,與進程無關(guān)。

  • 進程的主體是如何設(shè)置的?

    • 系統(tǒng)調(diào)用:setuid()、setgid()、setgroups()

    • 只有 root(UID 0)可以調(diào)用這些系統(tǒng)調(diào)用(粗略估計)。

  • 用戶 ID、組 ID 列表從哪里獲取?

    • 在典型的 Unix 系統(tǒng)上,登錄程序以 root(UID 0)身份運行。

    • 檢查提供的用戶密碼是否與/etc/shadow中的匹配。

    • 根據(jù)/etc/passwd找到用戶的 UID。

    • 根據(jù)/etc/group找到用戶的組。

    • 在運行用戶的 shell 之前調(diào)用setuid()、setgid()、setgroups()。

  • 在切換到非 root 用戶后如何重新獲得權(quán)限?

    • 可以使用文件描述符傳遞(但必須編寫專門的代碼)

    • 內(nèi)核機制:setuid/setgid 二進制文件。

      • 當(dāng)執(zhí)行二進制文件時,將進程 UID 或 GID 設(shè)置為二進制文件所有者。

      • 通過文件權(quán)限中的特殊位指定。

      • 例如,su / sudo 二進制文件通常是 setuid root。

      • 即使您的 shell 不是 root,也可以運行"su otheruser"

      • su 進程會檢查密碼,如果正確則以otheruser身份運行 shell。

      • Unix 上有許多這樣的程序,因為通常需要 root 權(quán)限。

    • 為什么 setuid 二進制文件在安全方面可能是個壞主意?

      • 對手(二進制調(diào)用者)操縱進程的許多方法。

      • 在 Unix 中,執(zhí)行的進程會繼承環(huán)境變量、文件描述符等。

      • setuid 程序可能使用的庫不夠謹慎

      • 歷史上存在許多漏洞(例如傳遞$LD_PRELOAD,…)

  • 如何防止惡意程序利用 setuid-root 二進制文件?

    • 內(nèi)核機制:chroot

      • 通過路徑名打開文件時更改/的含義。

      • 不能在 chroot 樹之外命名文件(例如 setuid 二進制文件)。

    • 例如,OKWS 使用 chroot 將程序限制在/var/okws/run中,…

    • 內(nèi)核還確保/../不允許從 chroot 中逃脫。

    • 為什么只允許 root 使用 chroot?

      • setuid 二進制文件(如su)可能會混淆/etc/passwd的內(nèi)容。

      • 許多內(nèi)核實現(xiàn)(無意中?)允許遞歸調(diào)用chroot()以從 chroot 監(jiān)獄中逃脫,因此 chroot 對于以 root 身份運行的進程來說不是一種有效的安全機制。

    • 為什么 chroot 沒有被修復(fù)以限制根進程在該目錄中?

      • Root 可以寫入內(nèi)核內(nèi)存,加載內(nèi)核模塊,訪問磁盤扇區(qū),…

背景:傳統(tǒng)的 Web 服務(wù)器架構(gòu)(Apache)

  • Apache 運行N個相同的進程,處理 HTTP 請求。

  • 所有進程都以用戶'www'身份運行。

  • 應(yīng)用程序代碼(例如 PHP)通常在每個N個 Apache 進程中運行。

  • 任何對操作系統(tǒng)狀態(tài)(文件、進程等)的訪問都由www的 UID 執(zhí)行。

  • 存儲:SQL 數(shù)據(jù)庫,通常一個連接具有對整個數(shù)據(jù)庫的完全訪問權(quán)限。

    • 數(shù)據(jù)庫主體是整個應(yīng)用程序。
  • 問題:如果任何組件被攻破,對手將獲得所有數(shù)據(jù)。

  • Web 應(yīng)用可能會發(fā)生哪種攻擊?

    • 無意中的數(shù)據(jù)泄露(獲取頁面源代碼,隱藏文件,…)

    • 遠程代碼執(zhí)行(例如,Apache 中的緩沖區(qū)溢出)

    • 有 bug 的應(yīng)用程序代碼(難以編寫安全的 PHP 代碼),例如 SQL 注入

    • 對 Web 瀏覽器的攻擊(跨站腳本攻擊)

回到 OKWS:他們的應(yīng)用/動機是什么?

  • 約會網(wǎng)站:擔(dān)心數(shù)據(jù)保密性。

  • 不太擔(dān)心對手闖入并發(fā)送垃圾郵件。

  • 大量的服務(wù)器端代碼執(zhí)行:匹配,配置文件更新,…

  • 必須在用戶之間共享(例如匹配)-- 不能只是分區(qū)。

  • 對整體計劃的良好總結(jié):

    • “最容易受攻擊的方面對攻擊者最無用”

為什么這么難?

  • Unix 使降低權(quán)限變得棘手(chroot,UID,…)

  • 應(yīng)用程序需要以復(fù)雜的方式共享狀態(tài)。

  • Unix 和 SQL 數(shù)據(jù)庫沒有細粒度的共享控制機制。

OKWS 如何分割 Web 服務(wù)器?

  • 論文中的圖 1。

  • 這個 Web 服務(wù)器中的請求是如何流動的?

    • okd -> oklogd

      • -> pubd

      • -> svc -> dbproxy

      • -> oklogd

  • 這種設(shè)計如何映射到物理機器?

    • 可能有許多前端機器(okldokdpubdoklogdsvc

    • 幾臺 DB 機器(dbproxy,DB)

這些組件如何互動?

  • okld為每個服務(wù)設(shè)置socketpairs(雙向管道)。

    • 一個用于控制 RPC 請求的套接字對(例如,“獲取新的日志套接字對”)。

    • 用于日志記錄的一個套接字對(okld首先通過 RPC 從oklogd獲取它)。

    • 對于 HTTP 服務(wù):一個用于轉(zhuǎn)發(fā) HTTP 連接的套接字對。

    • 對于okd:HTTP 服務(wù)的套接字對的服務(wù)器端 FD(HTTP+RPC)。

  • okd監(jiān)聽一個單獨的套接字以接收控制請求(repubrelaunch)。

    • 在圖 1 中似乎是端口 11277,但在 OKWS 代碼中是 Unix 域套接字。

    • 對于repubokdpubd通信以生成新模板,

      • 然后通過 RPC 控制通道將生成的模板發(fā)送給每個服務(wù)。
  • 服務(wù)通過 TCP 與 DB 代理通信(通過端口號連接)。

OKWS 如何在圖 1 中的組件之間強制隔離?

  • 每個服務(wù)作為單獨的 UID 和 GID 運行。

  • chroot 用于將每個進程限制在單獨的目錄中(幾乎)。

  • 組件通過管道(或者說 Unix 域套接字對)進行通信。

  • 用于傳遞 HTTP 連接的文件描述符傳遞。

  • okld的目的是什么?

  • 為什么okld不同于okd

  • 為什么okld需要以 root 身份運行?(端口 80,chroot/setuid。)

  • okld啟動服務(wù)需要什么?

    • 創(chuàng)建套接字對

    • 獲取新的oklogd套接字

    • forksetuid/setgidexec服務(wù)

    • 將控制套接字傳遞給okd

  • oklogd的目的是什么?

  • pubd的目的是什么?

  • 為什么我們需要數(shù)據(jù)庫代理?

    • 確保每個服務(wù)在受損時無法獲取其他數(shù)據(jù)。

      • DB 代理協(xié)議由應(yīng)用程序開發(fā)人員定義,取決于應(yīng)用程序的要求。

      • 一個可能常見的代理類型是模板化的 SQL 查詢。

      • 代理強制執(zhí)行整體查詢結(jié)構(gòu)(選擇、更新),

        • 但允許客戶端填寫查詢參數(shù)。
    • 20 字節(jié)令牌是從哪里來的?

      • 作為服務(wù)的參數(shù)傳遞。
    • 誰檢查令牌?

      • DB 代理有令牌列表(和允許的查詢?)。
    • 誰生成令牌?

      • 不清楚;系統(tǒng)管理員手動?
    • 令牌泄露會怎樣?

      • 受損組件可能會發(fā)出查詢。
  • 表 1:為什么所有服務(wù)和okld都在同一個 chroot 中?

    • 這是一個問題嗎?

    • 我們?nèi)绾螞Q定?

      • 那里有哪些可讀寫文件?
    • 可讀性:包含服務(wù)代碼的共享庫。

    • 可寫:每個服務(wù)都可以寫入自己的/cores/<uid>

    • 配置文件在哪里?/etc/okws_config,由okld保存在內(nèi)存中。

    • oklogdpubd有單獨的 chroots,因為它們具有重要狀態(tài):

      • oklogd的 chroot 包含日志文件,希望確保它沒有被修改。

      • pubd的 chroot 包含模板,希望避免泄露它們(?)。

  • 為什么 OKWS 需要為每個服務(wù)單獨的 GID?

    • 需要執(zhí)行二進制文件,但文件所有權(quán)允許 chmod。

    • 解決方案:二進制文件由 root 所有,服務(wù)是組所有者,模式 0410。

    • 為什么是 0410(用戶讀取,組執(zhí)行),而不是 0510(用戶讀取和執(zhí)行)?

  • 為什么不按用戶處理?

    • 每個用戶是否嚴格更好?

    • 用戶 X 服務(wù)?

    • 對于 okcupid 來說,每個服務(wù)的隔離可能是有道理的。

      • (即,也許他們需要在用戶之間進行大量共享?)
    • 每個用戶隔離需要為每個用戶分配 UID,使okld變得復(fù)雜。

      • 并降低性能(盡管對于某些用例可能仍然可以接受)。

OKWS 是否實現(xiàn)了其目標?

  • OKWS 解決了典型 Web 攻擊列表中的哪些攻擊,以及如何解決?

    • 除了 XSS 之外的大多數(shù)問題都已解決。

    • 通過使用專門的模板例程,XSS 在某種程度上得到解決。

  • 每個組件被損壞的影響是什么,以及“攻擊面”是什么?

    • okld:對 Web 服務(wù)器機器的根訪問權(quán)限,但也許沒有對數(shù)據(jù)庫的訪問權(quán)限。

      • 攻擊面:很小(除了 svc 退出之外沒有用戶輸入)。
    • okd:攔截/修改所有用戶 HTTP 請求/響應(yīng),竊取密碼。

      • 攻擊面:解析 HTTP 請求的第一行;控制請求。
    • pubd:損壞模板,利用可能利用某些服務(wù)中的錯誤?

      • 攻擊面:從 okd 獲取模板的請求。
    • oklogd:損壞/忽略/刪除/偽造日志條目。

      • 攻擊面:來自 okd、okld、svcs 的日志消息。
    • service:向用戶發(fā)送垃圾,訪問 svc 的數(shù)據(jù)(模塊化 dbproxy)。

      • 攻擊面:來自用戶的 HTTP 請求(+來自 okd 的控制消息)。
    • dbproxy:訪問/更改其所連接的數(shù)據(jù)庫中的所有用戶數(shù)據(jù)。

      • 攻擊面:來自授權(quán)服務(wù)的請求。

        • 未經(jīng)授權(quán)服務(wù)的請求(易于丟棄)。
  • 一旦單個服務(wù)被損壞,操作系統(tǒng)內(nèi)核就成為攻擊面的一部分。

    • Linux 內(nèi)核漏洞很少見,但每年仍然會出現(xiàn)幾次。
  • OKWS 假設(shè)開發(fā)人員在設(shè)計層面做正確的事情(也許在實現(xiàn)層面不是):

    • 將 Web 應(yīng)用程序拆分為單獨的服務(wù)(而不是全部放在一個服務(wù)中)。

    • 為 DB 代理定義精確的協(xié)議(否則任何服務(wù)都可以獲取任何數(shù)據(jù))。

  • 性能?

    • 似乎比大多數(shù)替代方案更好。

    • 在負載下性能更好(因此在一定程度上抵抗 DoS 攻擊)

  • OKWS 與 Apache 相比如何?

    • 總體而言,更好的設(shè)計。

    • okld以 root 身份運行,與 Apache 中沒有任何東西相比,但可能不重要。

    • 兩者都沒有很好的解決客戶端漏洞(XSS 等)

  • 對手如何試圖破壞類似 OKWS 系統(tǒng)?

    • 利用 C++代碼中的緩沖區(qū)溢出或其他漏洞。

    • 在某個dbproxy中找到 SQL 注入攻擊。

    • 在服務(wù)代碼中找到邏輯錯誤。

    • 發(fā)現(xiàn)跨站腳本漏洞。

OKWS 有多成功?

  • 論文中描述的問題仍然相當(dāng)普遍。

  • okcupid.com 仍在運行 OKWS,但似乎沒有被其他網(wǎng)站使用。

  • C++可能不是編寫 Web 應(yīng)用程序的好選擇。

    • 對于許多 Web 應(yīng)用程序,獲得 C++性能可能并不關(guān)鍵。

    • 設(shè)計應(yīng)該適用于其他語言(Python 等)。

    • 實際上,6.858 實驗室中的zookws受 OKWS 啟發(fā),運行 Python 代碼。

  • 對于典型的 Web 應(yīng)用程序,DB 代理的想法并沒有起飛。

    • 但是 DB 代理對于限制服務(wù)可以訪問的數(shù)據(jù)至關(guān)重要。

    • 為什么?

      • 需要開發(fā)人員定義這些 API:額外的工作,會妨礙。
    • 很難提前精確定義允許的 DB 查詢。

      • (盡管如果很困難,可能是安全策略模糊的標志。)
  • Apache 的特權(quán)分離工作(盡管仍然難以使用)。

    • Unix 使非根用戶難以操作用戶 ID。

    • 性能是一個問題(為每個請求運行一個單獨的進程)。

  • scripts.mit.edu有類似的設(shè)計,以不同的 UID 運行腳本。

    • 主要擔(dān)心將用戶相互隔離。

    • 偏執(zhí)的 Web 應(yīng)用程序開發(fā)人員可以為每個組件創(chuàng)建單獨的鎖。

  • 敏感系統(tǒng)在更粗粒度上進行分區(qū)。

    • 信用卡處理公司將信用卡數(shù)據(jù)與其他所有數(shù)據(jù)分開。

    • 使用虛擬機或物理機器隔離來分割應(yīng)用程序、數(shù)據(jù)庫等。

你如何將現(xiàn)代 Web 應(yīng)用程序框架與 OKWS 集成?

  • 需要幫助 okd 找出如何將請求路由到服務(wù)。

  • 需要實現(xiàn) DB 代理,或其變體,以保護數(shù)據(jù)。

    • 取決于應(yīng)用代碼對靜態(tài)分析的適應(yīng)性。

    • 或者需要要求程序員為服務(wù)注釋可以運行的查詢。

  • 需要確保應(yīng)用代碼可以在單獨的進程中運行(可能沒問題)。

參考資料

  • css.csail.mit.edu/6.858/2014/readings/setuid.pdf

  • httpd.apache.org/docs/trunk/suexec.html

能力和其他保護機制

注意: 這些講座筆記是從 2014 年 6.858 課程網(wǎng)站 上發(fā)布的筆記稍作修改而來。

混淆的副手問題

"混淆的副手"的作者遇到了什么問題?

  • 他們的系統(tǒng)有一個 Fortran 編譯器,/sysx/fort(Unix 文件名語法)

  • 他們希望 Fortran 編譯器記錄使用統(tǒng)計信息,但在哪里?

    • 創(chuàng)建了一個特殊的統(tǒng)計文件,/sysx/stat

    • 給了/sysx/fort“家庭文件許可證”(類似于關(guān)于/sysx 的 setuid)

  • 出了什么問題?

    • 用戶可以調(diào)用編譯器,要求將輸出寫入/sysx/stat。

      • 例如/sysx/fort /my/code.f -o /sysx/stat
    • 編譯器打開提供的路徑名,并成功,因為它的許可證。

    • 用戶本身不能寫入那個/sysx/stat文件。

  • 為什么/sysx/fort只是編譯器中的一個錯誤?

    • 原則上,可以通過在各個地方添加檢查來解決這個問題。

    • 問題:需要在幾乎所有打開文件的地方添加檢查。

    • 完全正確的代碼一旦成為 setuid 二進制文件的一部分就會變得有 bug。

  • 那么什么是“混淆的副手”?

    • 編譯器代表兩個主體運行:

      • 用戶主體(用于打開用戶的文件)

      • 編譯器主體(用于打開編譯器的文件)

    • 不清楚在任何給定時間應(yīng)該使用主體權(quán)限。

我們能在 Unix 中解決這個混淆的副手問題嗎?

  • 假設(shè) gcc 想要在/etc/gcc.stats中保留統(tǒng)計信息

  • 可以有一個特殊的 setuid 程序,只能寫入該文件

    • 不太方便:不能像打開其他文件那樣簡單地打開文件。
  • 如果我們讓 gcc 成為某個非根用戶(統(tǒng)計文件所有者)的 setuid,會怎樣?

    • 難以訪問用戶的原始文件。
  • 如果 gcc 是 setuid-root?(壞主意,但讓我們弄清楚為什么…)

    • 大量潛在的緩沖區(qū)溢出可能導(dǎo)致 root 訪問權(quán)限。

    • 需要在 gcc 可能打開文件的每個地方進行檢測。

  • 當(dāng) gcc 打開文件時,我們應(yīng)該執(zhí)行什么檢查?

    • 如果是“內(nèi)部”文件(例如/etc/gcc.stats),也許不需要檢查。

    • 如果是用戶提供的文件,需要確保用戶可以訪問它。

    • 可以查看相關(guān)文件的權(quán)限。

    • 還需要檢查導(dǎo)致該文件的目錄的權(quán)限。

  • 潛在問題:競爭條件。

    • 如果文件在我們檢查和使用之間發(fā)生更改會怎么樣?

    • 常見的漏洞:攻擊者用符號鏈接替換合法文件

    • 符號鏈接可能指向,比如/etc/gcc.stats,或/etc/passwd,或…

    • 被稱為“檢查時間到使用時間”的錯誤(TOCTTOU)。

對這個問題有幾種可能的思考方式:

  1. 環(huán)境權(quán)限: 進程自動使用的權(quán)限是問題所在。任何權(quán)限都不應(yīng)該自動使用。對象的名稱也應(yīng)該是訪問它的權(quán)限。

  2. 復(fù)雜的權(quán)限檢查: 特權(quán)應(yīng)用程序難以復(fù)制。通過簡化的檢查,特權(quán)應(yīng)用程序可能能夠正確檢查另一個用戶是否應(yīng)該訪問某個對象。

什么是環(huán)境權(quán)限的例子?

  • Unix 用戶 ID,組 ID。

  • 防火墻(IP 地址與訪問權(quán)限)

  • HTTP cookies(例如,訪問 http://gmail.com 這樣的 URL)

通過能力給對象命名有什么幫助?

  • 傳遞文件描述符而不是傳遞文件名。

  • 除非調(diào)用者被授權(quán)打開該文件,否則無法傳遞有效的 FD。

我們能否使用文件描述符解決通過 setuid gcc 設(shè)置的問題?

  • 類似:可以使編譯器僅通過 FD 傳遞接受文件。

  • 或者,可以創(chuàng)建一個 setuid 輔助程序,打開/etc/gcc.stats文件,將一個打開的文件描述符傳遞回我們的編譯器進程。

  • 然后,可以繼續(xù)像處理任何其他文件一樣使用這個打開的文件。

  • 如何確保只有 gcc 可以運行這個輔助程序?

    • 使 gcc 設(shè)置為某個特殊組的 setgid。

    • 使輔助程序僅對該特殊組可執(zhí)行。

    • 確保該組沒有其他授予的特權(quán)。

Capsicum 作者試圖通過能力解決什么問題?

  • 在各種應(yīng)用程序中降低不可信代碼的特權(quán)。

  • 總體計劃:

    • 將應(yīng)用程序分解為較小的組件。

    • 減少最容易受攻擊的組件的特權(quán)。

    • 仔細設(shè)計接口,以便一個組件無法危害另一個組件。

  • 為什么這么困難?

    • 在傳統(tǒng)的 Unix 系統(tǒng)中難以降低代碼的特權(quán)(“沙盒”)。

    • 難以為沙盒化代碼提供有限的訪問權(quán)限(對文件、網(wǎng)絡(luò)等)。

什么樣的應(yīng)用程序可能會使用沙盒化?

OKWS

  • 處理網(wǎng)絡(luò)輸入的程序:

    • 將輸入處理代碼放入沙盒中。
  • 復(fù)雜操作數(shù)據(jù)的程序:(gzip,Chromium,媒體編解碼器,瀏覽器插件,…)

    • 將復(fù)雜(且可能有錯誤)的部分放入沙盒中。
  • 從互聯(lián)網(wǎng)下載的任意程序怎么樣?

    • 稍微不同的問題:需要隔離未修改的應(yīng)用程序代碼。

    • 一個選擇:程序員編寫他們的應(yīng)用程序以在沙盒中運行。

      • 在某些情況下有效:Javascript,Java,Native Client,…

      • 需要在沙盒代碼上制定一個環(huán)境標準。

    • 另一個選擇:對現(xiàn)有代碼施加新的安全策略。

      • 可能需要保留程序員正在使用的所有 API。

      • 需要對現(xiàn)有 API 施加檢查,在那種情況下。

      • 不清楚訪問文件、網(wǎng)絡(luò)等的策略應(yīng)該是什么。

  • 希望避免被欺騙誤用特權(quán)的應(yīng)用程序?

    • 假設(shè)兩個 Unix 用戶,Alice 和 Bob,正在某個項目上工作。

    • 兩者都在某個組G中,并且項目dir允許該組訪問。

    • 假設(shè) Alice 從項目目錄向某人發(fā)送一個文件。

    • 風(fēng)險:Bob 可能用符號鏈接替換文件為 Alice 的私人文件。

    • Alice 的進程將隱式使用 Alice 的環(huán)境特權(quán)來打開。

    • 可以將這看作對單個文件操作進行沙盒化。

有哪些沙盒化計劃(機制)存在(優(yōu)勢,限制)?

  • 操作系統(tǒng)通常提供某種安全機制(“原語”)。

    • 例如,在 Unix 中的用戶/組 ID,正如我們在上一堂課中看到的。
  • 今天,我們將研究操作系統(tǒng)級別的安全原語/機制。

    • 當(dāng)您關(guān)心保護操作系統(tǒng)管理的資源時通常是一個很好的選擇。

    • 例如,文件,進程,粗粒度內(nèi)存,網(wǎng)絡(luò)接口等。

  • 許多操作系統(tǒng)級別的沙箱機制在進程級別工作。

    • 適用于可以作為一個單元進行隔離的整個進程。

    • 可能需要重新設(shè)計應(yīng)用程序以創(chuàng)建用于隔離的進程。

  • 其他技術(shù)可以提供更細粒度的隔離(例如,在 proc 中的線程)。

    • 語言級別的隔離(例如,Javascript)。

    • 二進制儀器化(例如,Native Client)。

    • 為什么我們需要這些其他的沙箱技術(shù)?

      • 更容易控制對非操作系統(tǒng)/更細粒度對象的訪問。

      • 或者也許可以以與操作系統(tǒng)無關(guān)的方式進行沙箱化。操作系統(tǒng)級別的隔離通常與更細粒度的隔離結(jié)合使用。

      • 更細粒度的隔離通常很難做到正確(Javascript,NaCl)。例如,Native Client 同時使用了細粒度沙箱和操作系統(tǒng)級別的沙箱。

    • 將在后續(xù)講座中更詳細地討論這些問題。

計劃 0:虛擬化所有內(nèi)容(例如,VMs)。

  • 在虛擬化環(huán)境中運行不可信代碼。

  • 許多示例:x86 qemu,FreeBSD jails,Linux LXC,…

  • 幾乎是一種不同類別的機制:嚴格隔離。

  • 優(yōu)勢:VM 內(nèi)部的沙箱代碼幾乎與外部沒有交互。

  • 優(yōu)勢:可以沙箱未經(jīng)修改的代碼,不期望被隔離。

  • 優(yōu)勢:一些 VM 可以由任意用戶啟動(例如,qemu)。

  • 優(yōu)勢:通常與其他隔離技術(shù)可組合,提供額外層次。

  • 缺點:難以允許一些共享:沒有共享進程,管道,文件。

  • 缺點:虛擬化所有內(nèi)容通常會使 VM 相對較重。

    • 每個沙箱都會帶來非常重要的 CPU/內(nèi)存開銷。

計劃 1:自主訪問控制(DAC)。

  • 每個對象都有一組權(quán)限(訪問控制列表)。

    • 例如,Unix 文件,Windows 對象。

    • “自主”意味著應(yīng)用程序在對象上設(shè)置權(quán)限(例如,chmod)。

  • 每個程序都以某些主體的權(quán)限運行。

    • 例如,Unix 用戶/組 ID,Windows SIDs。
  • 當(dāng)程序訪問對象時,檢查程序的權(quán)限以決定。

“環(huán)境特權(quán)”:每次訪問都隱式使用的權(quán)限。

 Name              Process privileges|                       |V                       VObject -> Permissions -> Allow? 
  • 如何在 DAC 系統(tǒng)上(例如,Unix)沙箱化程序?

    • 必須分配一個新的主體(用戶 ID):

      • 否則,現(xiàn)有主體的權(quán)限將被隱式使用!
    • 防止進程讀取/寫入其他文件:

      • 在整個文件系統(tǒng)上更改權(quán)限?繁瑣,不切實際,需要 root 權(quán)限。

      • 即使如此,新程序也可以創(chuàng)建重要的可全球?qū)懭胛募?/p>

      • 替代方案:chroot(同樣,必須是 root)。

    • 允許進程讀/寫某個文件:

      • 如果可能的話,適當(dāng)設(shè)置文件的權(quán)限。

      • 將文件鏈接/移動到沙箱的chroot目錄中?

    • 防止進程訪問網(wǎng)絡(luò):

      • Unix 中沒有真正的答案。

      • 可能配置防火墻?但不是真正針對進程的。

    • 允許進程訪問特定的網(wǎng)絡(luò)連接:

      • 如上所述,在 Unix 中沒有很好的計劃。
    • 控制沙盒可以殺死 / 調(diào)試 / 等的進程:

      • 可以在相同的 UID 下運行,但可能特權(quán)太多。

      • 該 UID 也可能具有其他特權(quán)…

  • **問題:**在大多數(shù) DAC 系統(tǒng)上,只有 root 可以創(chuàng)建新的主體。

    • 例如,Unix,Windows。
  • **問題:**一些對象可能沒有明確可配置的訪問控制列表。

    • Unix:進程,網(wǎng)絡(luò),…
  • **問題:**文件上的權(quán)限可能與沙盒所需的策略不匹配。

    • 可以通過使用chroot對文件進行某種程度的解決,但很麻煩。
  • **相關(guān)問題:**使用子特權(quán)執(zhí)行某些操作。

    • 回想一下 Alice 通過電子郵件將文件發(fā)送到共享組目錄的示例。

      • “混淆副手問題”:程序是多個主體的“副手”。
    • *一個解決方案:*檢查組權(quán)限是否允許訪問(手動,容易出錯)。

    • *替代方案:*明確為每個操作指定特權(quán)。

      • 權(quán)限可以幫助:能力(例如,fd)結(jié)合了對象 + 特權(quán)。

      • 一些 Unix 功能與純能力設(shè)計不兼容(按名稱創(chuàng)建符號鏈接)。

計劃 2:強制訪問控制(MAC)。

  • 在 DAC 中,安全策略由應(yīng)用程序自身設(shè)置(chmod 等)。

  • MAC 試圖幫助用戶/管理員為應(yīng)用程序指定策略。

    • *“強制”*意味著應(yīng)用程序無法更改此策略。

    • 傳統(tǒng)的 MAC 系統(tǒng)試圖強制執(zhí)行軍事機密級別。

*示例:*確保絕密程序無法泄露機密信息。

 Name    Operation + caller process|               |V               VObject --------> Allow?^|Policy -----------+ 
  • *注意:*許多系統(tǒng)在其中具有 DAC + MAC 的方面。

    • 例如,Unix 用戶 ID 是“DAC”,但可以爭論防火墻是“MAC”。

    • 并不重要–了解設(shè)計空間中的極端點是很好的。

  • Windows 強制完整性控制(MIC)/ FreeBSD 中的 LOMAC。

    • 為每個進程跟蹤“完整性級別”。

    • 文件與其關(guān)聯(lián)的最低完整性級別。

    • 進程無法寫入高于其完整性級別的文件。

      • Windows Vista 中的 Internet Explorer 以低完整性運行,無法覆蓋系統(tǒng)文件。
    • FreeBSD LOMAC 還跟蹤進程讀取的數(shù)據(jù)。

      • (類似于許多基于信息流的系統(tǒng)。)

      • 當(dāng)進程讀取低完整性數(shù)據(jù)時,它也變得低完整性。

      • 傳遞性,防止對手間接篡改文件。

    • 對于沙盒化不是立即有用:只有固定數(shù)量的級別。

  • SElinux

    • *想法:*系統(tǒng)管理員指定系統(tǒng)范圍的安全策略。

    • 策略文件指定是否應(yīng)允許或拒絕每個操作。

    • 為了幫助決定是否允許/拒絕,文件標記為“類型”。

      • (另一個整數(shù)值,與 inode 中的 uid、gid 等一起存儲。)
  • Mac OS X 沙盒(“Seatbelt”)和 Linux seccomp_filter。

    • 應(yīng)用程序為是否允許/拒絕每個系統(tǒng)調(diào)用指定策略。

      • (在 MacOSX 的機制中用 LISP 編寫,或者在 Linux 中用 BPF 編寫。)
    • 根據(jù)參數(shù)確定系統(tǒng)調(diào)用的安全影響可能很困難。

      • 路徑名指的是什么?符號鏈接,硬鏈接,競爭條件,…

      • (盡管 MacOSX 的沙盒提供了更多信息)

    • 優(yōu)勢: 任何用戶都可以對任意代碼片段進行沙盒化!

    • 限制: 程序員必須分別編寫策略和應(yīng)用代碼。

    • 限制: 有些操作只能以粗粒度進行過濾。

      • 例如,在 MacOSX 的過濾語言中的 POSIX shm,根據(jù) Capsicum 論文。
    • 限制:策略語言可能使用起來很尷尬,無狀態(tài)等。

      • 例如,如果應(yīng)用程序應(yīng)該與某個服務(wù)器建立精確的一個連接?
    • 注意: seccomp_filter 與常規(guī)/舊版 seccomp 有很大不同,而 Capsicum 論文討論的是常規(guī)/舊版 seccomp。

  • 將策略與應(yīng)用代碼分離是否是個好主意?

    • 取決于總體目標。

    • 如果用戶/管理員想查看或更改策略,可能會很有用。

    • 如果應(yīng)用程序開發(fā)人員需要同時維護代碼和策略,這將成為問題。

    • 對應(yīng)用程序開發(fā)人員來說,可能有助于澄清策略。

    • 較少集中的“MAC”系統(tǒng)(Seatbelt、seccomp)提供了一種折衷方案。

  • 待辦事項: 還要看看《賽里斯墻安全策略》

計劃 3:能力(Capsicum)。

  • 不同的訪問控制計劃:能力。

    • 如果進程有某個對象的句柄(“能力”),就可以訪問它。

      • 能力 --> 對象
    • 沒有特權(quán)、訪問控制列表、策略等的單獨問題。

      • 例如:Unix 上的文件描述符是文件的能力。

      • 程序無法制造未經(jīng)合法獲取的文件描述符。

        • 為什么不? 操作系統(tǒng)創(chuàng)建和管理文件描述符。應(yīng)用程序無法偽造文件描述符。它必須通過漏洞寫入操作系統(tǒng)內(nèi)存。
      • 一旦文件打開,就可以訪問它;檢查發(fā)生在打開時。

      • 可以將打開的文件傳遞給其他進程。

        • 文件描述符也有助于解決“檢查時間與使用時間”(TOCTTOU)漏洞。
    • 能力通常是短暫的:不是磁盤上的 inode 的一部分。

      • 啟動程序的任何內(nèi)容都需要每次重新創(chuàng)建能力。
  • 全局命名空間

    • 為什么這些人如此著迷于消除全局命名空間?

    • 全局命名空間需要一些訪問控制策略(例如,環(huán)境權(quán)限)。

    • 難以控制沙盒對全局命名空間中對象的訪問。

  • 內(nèi)核更改

    • 只是為了再次確認:為什么我們需要內(nèi)核更改?

      • 我們能否將所有內(nèi)容都實現(xiàn)在一個庫中(并通過 LD_PRELOAD 加載)?

      • 需要操作系統(tǒng)在進入能力模式后拒絕應(yīng)用程序訪問全局命名空間

    • 將更多內(nèi)容表示為文件描述符:進程(pdfork)。

      • 一般來說是個好主意。
    • 能力模式: 一旦進程進入 cap 模式,就無法離開(包括所有子進程)。

    • 在能力模式下,只能使用文件描述符 – 沒有全局命名空間。

      • 不能通過完整路徑名打開文件:不需要像 OKWS 中的 chroot。

      • 仍然可以通過相對路徑名打開文件,給定目錄的 fd(openat)。

    • 不能在路徑名或符號鏈接中使用“…”:為什么?

      • 原則上,“…” 可能沒問題,只要“…” 不走得太遠。

      • 難以正確執(zhí)行。

      • 假設(shè)設(shè)計:

        • 禁止在根能力中查找“…”。

        • 路徑名中的非“…”組件不得比“…”多,忽略“.”。

        • 假設(shè)一個進程對 /foo 擁有能力 C1。

        • 在單個進程中的競爭條件,有 2 個線程:

競爭條件示例:

 T1: mkdir(C1, "a/b/c")T1: C2 = openat(C1, "a")T1: C3 = openat(C2, "b/c/../..")   # should return a cap for /foo/aLet openat() run until it's about to look up the first ".."T2: renameat(C1, "a/b/c", C1, "d")T1: Look up the first "..", which goes to "/foo"Look up the second "..", which goes to "/" 
    • Unix 權(quán)限仍然適用嗎?

      • 是的 – 僅因為你對目錄有一個 cap,就不能訪問目錄中的所有文件。

      • 但意圖是沙盒不應(yīng)依賴 Unix 權(quán)限。

    • 對于文件描述符,添加一個存儲允許操作的包裝對象。

    • 內(nèi)核在哪里檢查能力?

      • 內(nèi)核中的一個函數(shù)查找 fd 號碼 – 修改它以檢查能力。

      • 還修改了查找路徑名的 namei 函數(shù)。

      • 良好實踐: 尋找窄接口,否則容易忽略檢查。

  • libcapsicum

    • 應(yīng)用程序開發(fā)人員為什么需要這個庫?

    • 最大的功能:在沙盒中啟動新進程。

  • fd 列表

    • 主要是將大量文件描述符傳遞給子進程的便捷方式。

    • 通過字符串命名文件描述符,而不是硬編碼的 fd 號碼。

  • cap_enter() vs lch_start()

    • 使用 exec 而不是 cap_enter 進行沙盒化的優(yōu)勢是什么?

    • 內(nèi)存中的殘留數(shù)據(jù):例如 OpenSSL/OpenSSH 中的私鑰。

    • 應(yīng)用程序忘記關(guān)閉的殘留文件描述符。

    • 論文中的圖 7:tcpdumpstdin、stdout、stderr 上具有特權(quán)。

    • 論文中的圖 10:dhclient 具有原始套接字,syslogd 管道,租約文件。

  • 優(yōu)點: 任何進程都可以創(chuàng)建一個新的沙盒。

    • (即使沙盒也可以創(chuàng)建沙盒。)
  • 優(yōu)點: 對資源訪問的細粒度控制(如果它們映射到 FD)。

    • 文件、網(wǎng)絡(luò)套接字、進程。
  • 缺點: 對持久文件訪問跟蹤的故事較弱。

  • 缺點: 禁止全局命名空間,需要以不同方式編寫代碼。

替代性的能力設(shè)計:純能力為基礎(chǔ)的操作系統(tǒng)(KeyKOS 等)。

  • 內(nèi)核只提供消息傳遞服務(wù)。

  • 消息傳遞通道(非常類似文件描述符)是能力。

  • 每個應(yīng)用程序都必須以能力樣式編寫。

  • Capsicum 聲稱更加務(wù)實:一些應(yīng)用程序無需更改。

Linux 能力:解決不同的問題。

  • 嘗試將根的特權(quán)劃分為更細粒度的特權(quán)。

  • 由各種能力表示:CAP_KILL, CAP_SETUID, CAP_SYS_CHROOT, …

  • 進程可以以特定能力運行,而不是以 root 的所有特權(quán)。

  • 參考:capabilities(7)

在應(yīng)用程序中使用 Capsicum

  • 計劃: 確保沙盒化進程不使用路徑名或其他全局 NS。

    • 對于可能需要訪問的每個目錄,提前打開 FD。

    • 要打開文件,請使用從這些目錄 FD 開始的 openat()。

      • … 打開大量文件的程序可能會很麻煩。
  • tcpdump

    • 2 行版本:在打開所有 FD 后只需 cap_enter()。

    • 使用 procstat 查看生成的能力。

    • 8 行版本:還限制 stdin/stdout/stderr。

    • 為什么?避免讀取 stderr 日志,更改終端設(shè)置,…

  • dhclient

    • 已經(jīng)進行了特權(quán)分離,使用 Capsicum 來加強沙盒(2 行)。
  • gzip

    • 分叉/執(zhí)行沙盒化的子進程,通過管道使用 RPC 向其提供數(shù)據(jù)。

    • 非平凡的更改,主要是為了為 RPC 編組/解組數(shù)據(jù):409 行代碼。

    • *有趣的錯誤:*一開始忘記傳播壓縮級別。

  • Chromium

    • 在其他平臺上已經(jīng)進行了特權(quán)分離(但在 FreeBSD 上沒有)。

    • ~100 行代碼用于為沙盒化進程包裝文件描述符。

  • OKWS

    • 家庭作業(yè)問題有哪些不同的答案?

Capsicum 是否實現(xiàn)了其目標?

  • 使用起來有多難/容易?

    • 在應(yīng)用程序中使用 Capsicum 幾乎總是需要應(yīng)用程序更改。

      • (許多應(yīng)用程序傾向于通過路徑名打開文件等。)

      • 一個例外:Unix 管道應(yīng)用程序(過濾器)只操作 FD。

    • 對通過 FD 處理數(shù)據(jù)的流式應(yīng)用程序更容易。

    • 其他隔離需要類似的更改(例如,dhclient,Chromium)。

    • 對于現(xiàn)有應(yīng)用程序,延遲初始化似乎是一個問題。

      • 沒有通用解決方案——要么更改代碼,要么早期初始化。
    • 建議的計劃:沙盒化并查看哪些地方出問題。

      • 可能會有微妙之處:gzip壓縮級別錯誤。
  • 它提供了哪些安全保證?

    • 提供給應(yīng)用程序開發(fā)人員的保證:沙盒只能在打開的 FD 上操作。

    • 結(jié)果取決于應(yīng)用程序開發(fā)人員如何劃分應(yīng)用程序、FD。

    • 用戶/管理員無法從 Capsicum 獲得任何直接保證。

    • 保證假設(shè) FreeBSD 內(nèi)核沒有錯誤(大量代碼),并且 Capsicum 開發(fā)人員捕獲了所有通過 FD 而非資源訪問的方式。

  • 性能開銷是多少?(CPU,內(nèi)存)

    • 訪問文件描述符的輕微開銷。

    • 使用fork/exec設(shè)置沙盒需要花費O(1msec),非平凡的。

    • 特權(quán)分離可能需要 RPC / 消息傳遞,可能會引起注意。

  • 采用情況?

    • 在 FreeBSD 的內(nèi)核中,現(xiàn)在默認啟用(從 FreeBSD 10 開始)。

    • 少數(shù)應(yīng)用程序已經(jīng)修改為使用 Capsicum。dhclienttcpdump,自論文撰寫以來還有幾個。參考

    • Casper 守護程序幫助應(yīng)用程序執(zhí)行非能力操作。例如,DNS 查找,查找/etc/passwd中的條目等。參考

    • Capsicum 已經(jīng)移植到 Linux(但不在上游內(nèi)核存儲庫中)。

有哪些應(yīng)用程序不適合 Capsicum?

  • 需要控制對非內(nèi)核管理對象的訪問的應(yīng)用程序。

    • 例如:X 服務(wù)器狀態(tài),DBus,在 Web 瀏覽器中的 HTTP 來源等。

    • 例如:需要確保 DB 文件格式正確的數(shù)據(jù)庫服務(wù)器。

    • Capsicum 將管道視為用戶級服務(wù)器(例如,X 服務(wù)器)的一個能力。

  • 需要從沙盒連接到特定的 TCP/UDP 地址/端口的應(yīng)用程序。

    • Capsicum 通過僅允許對現(xiàn)有打開的 FD 進行操作來工作。

    • 需要其他機制來控制可以打開哪些 FD。

    • 可能的解決方案:輔助程序可以在能力模式之外運行,根據(jù)策略為沙盒化程序打開 TCP/UDP 套接字。

參考

  • 蘋果沙盒指南

  • seccomp_filter

  • 強制完整性控制

  • 賽里斯防火墻安全政策

http://www.risenshineclean.com/news/53774.html

相關(guān)文章:

  • 招聘網(wǎng)絡(luò)營銷推廣人員seo搜索引擎優(yōu)化試題及答案
  • 做空調(diào)的網(wǎng)站蘭州模板網(wǎng)站seo價格
  • php網(wǎng)站開發(fā)技術(shù)廣州seo營銷培訓(xùn)
  • 天津網(wǎng)站建設(shè)索王道下拉怎么做網(wǎng)絡(luò)推廣最有效
  • 廣州網(wǎng)站優(yōu)化渠道北京網(wǎng)站搭建哪家好
  • 臨海建設(shè)局官方網(wǎng)站濟南網(wǎng)絡(luò)優(yōu)化哪家專業(yè)
  • 用html做網(wǎng)站搜索框可以投放廣告的網(wǎng)站
  • 鹽山網(wǎng)站開發(fā)百度競價怎么做開戶需要多少錢
  • 微信公眾號可以做幾個微網(wǎng)站嗎公眾號開發(fā)網(wǎng)站公司
  • 西安誰家做網(wǎng)站seo營銷培訓(xùn)
  • 云南住房和建設(shè)廳網(wǎng)站首頁小程序推廣的十種方式
  • 男女做爰真人視頻免費網(wǎng)站網(wǎng)絡(luò)seo優(yōu)化推廣
  • 公司網(wǎng)站制作天強科技windows優(yōu)化大師和360哪個好
  • 個人網(wǎng)站做什么好北京做網(wǎng)站公司哪家好
  • 徐州有哪些做網(wǎng)站百度推廣
  • 做spa會所網(wǎng)站深圳網(wǎng)絡(luò)推廣收費標準
  • .net做網(wǎng)站用什么的多雅虎搜索
  • 專業(yè)的網(wǎng)站建設(shè)設(shè)計價格怎么做一個免費的網(wǎng)站
  • 制作網(wǎng)站必做步驟域名注冊
  • 專業(yè)公司網(wǎng)站設(shè)計企業(yè)seo網(wǎng)絡(luò)推廣知識
  • 慈溪做無痛同濟&網(wǎng)站福州今日頭條新聞
  • 音響網(wǎng)站模板免費下載網(wǎng)絡(luò)推廣外包要多少錢
  • 網(wǎng)站制作的合同百度指數(shù)查詢排行榜
  • 網(wǎng)站簡單制作免費友情鏈接交換平臺
  • 常州建設(shè)網(wǎng)站公司網(wǎng)站產(chǎn)品推廣文章
  • 大公司做網(wǎng)站營銷策劃書
  • 黃驊市網(wǎng)站建設(shè)價格新聞發(fā)布
  • 長沙網(wǎng)站制作工作室知名公司關(guān)鍵詞有哪幾種
  • 微網(wǎng)站 制作平臺最佳bt磁力狗
  • 一個做BAT筆試題的網(wǎng)站關(guān)鍵詞名詞解釋