在 Linux 上使用 Checksec 識(shí)別二進(jìn)制文件的安全屬性

編譯源代碼會(huì)生成一個(gè)二進(jìn)制文件(LCTT 譯注:即 ??.o??? 文件)。在編譯期間,你可以向 ??gcc?? 編譯器提供 標(biāo)志flags,以啟用或禁用二進(jìn)制文件的某些屬性,這些屬性與安全性相關(guān)。
Checksec 是一個(gè)漂亮的小工具,同時(shí)它也是一個(gè) shell 腳本。Checksec 可以識(shí)別編譯時(shí)構(gòu)建到二進(jìn)制文件中的安全屬性。編譯器可能會(huì)默認(rèn)啟用一些安全屬性,你也可以提供特定的標(biāo)志,來(lái)啟用其他的安全屬性。
本文將介紹如何使用 Checksec ,來(lái)識(shí)別二進(jìn)制文件的安全屬性,包括:
- Checksec 在查找有關(guān)安全屬性的信息時(shí),使用了什么底層的命令
- 在將源代碼編譯成二進(jìn)制文件時(shí),如何使用GNU 編譯器套件GNU Compiler Collection(即 GCC)來(lái)啟用安全屬性。
安裝 checksec
要在 Fedora 和其他基于 RPM 的 Linux 系統(tǒng)上,安裝 Checksec,請(qǐng)使用以下命令:
對(duì)于基于 Debian 的 Linux 發(fā)行版,使用對(duì)應(yīng)的 ??apt?? 命令,來(lái)安裝 Checksec。
shell 腳本
在安裝完 Checksec 后,能夠發(fā)現(xiàn) Checksec 是一個(gè)單文件的 shell 腳本,它位于 ??/usr/bin/checksec??,并且這個(gè)文件挺大的。Checksec 的一個(gè)優(yōu)點(diǎn)是你可以通過(guò)快速通讀這個(gè) shell 腳本,從而了解 Checksec 的執(zhí)行原理、明白所有能查找有關(guān)二進(jìn)制文件或可執(zhí)行文件的安全屬性的系統(tǒng)命令:
以下的命令展示了如何對(duì)你每天都會(huì)使用的:??ls?? 命令的二進(jìn)制文件運(yùn)行 Checksec。Checksec 命令的格式是:??checksec --file=??,后面再跟上二進(jìn)制文件的絕對(duì)路徑:
當(dāng)你在終端中對(duì)某個(gè)二進(jìn)制文件運(yùn)行 Checksec 時(shí),你會(huì)看到安全屬性有顏色上的區(qū)分,顯示什么是好的安全屬性(綠色),什么可能不是好的安全屬性(紅色)。我在這里說(shuō) “可能” 是因?yàn)榧词褂行┌踩珜傩允羌t色的,也不一定意味著這個(gè)二進(jìn)制文件很糟糕,它可能只是表明發(fā)行版供應(yīng)商在編譯二進(jìn)制文件時(shí)做了一些權(quán)衡,從而舍棄了部分安全屬性。
Checksec 輸出的第一行提供了二進(jìn)制文件的各種安全屬性,例如 ??RELRO??、??STACK CANARY??、??NX?? 等(我將在后文進(jìn)行詳細(xì)解釋)。第二行打印出給定二進(jìn)制文件(本例中為 ??ls??)在這些安全屬性的狀態(tài)(例如,??NX enabled?? 表示為堆棧中的數(shù)據(jù)沒(méi)有執(zhí)行權(quán)限)。
示例二進(jìn)制文件
在本文中,我將使用以下的 “hello world” 程序作為示例二進(jìn)制文件。
請(qǐng)注意,在編譯源文件 ??hello.c?? 的時(shí)候,我沒(méi)有給 ??gcc?? 提供任何額外的標(biāo)志:
使用 Checksec 運(yùn)行二進(jìn)制文件 ??hello??,打印的某些安全屬性的狀態(tài),與上面的 ??ls?? 二進(jìn)制文件的結(jié)果不同(在你的屏幕上,某些屬性可能顯示為紅色):
(LCTT 譯注:在我的 Ubuntu 22.04 虛擬機(jī),使用 11.3.0 版本的 ??gcc??,結(jié)果與上述不太相同,利用默認(rèn)參數(shù)進(jìn)行編譯,會(huì)得到 RELRO、PIE、NX 保護(hù)是全開(kāi)的情況。)
更改 Checksec 的輸出格式
Checksec 允許自定義各種輸出格式,你可以使用 ??--output?? 來(lái)自定義輸出格式。我將選擇的輸出格式是 JSON 格式,并將輸出結(jié)果通過(guò)管道傳輸?shù)?nbsp;??jq?? 實(shí)用程序,來(lái)得到漂亮的打印。
接下來(lái),確保你已安裝好了 ??jq??,因?yàn)楸窘坛虝?huì)使用 ??jq?? 從 Checksec 的輸出結(jié)果中,用 ??grep?? 來(lái)快速得到某一特定的安全屬性狀態(tài),并報(bào)告該安全屬性是否啟動(dòng)(啟動(dòng)為 ??yes??,未啟動(dòng)為 ??no??):
看一看所有的安全屬性
上面的二進(jìn)制文件 ??hello?? 包括幾個(gè)安全屬性。我將該二進(jìn)制文件與 ??ls?? 的二進(jìn)制文件進(jìn)行比較,以檢查啟用的安全屬性有何不同,并解釋 Checksec 是如何找到此信息。
1、符號(hào)(Symbol)
我先從簡(jiǎn)單的講起。在編譯期間,某些 符號(hào)symbols包含在二進(jìn)制文件中,這些符號(hào)主要用作于調(diào)試。開(kāi)發(fā)軟件時(shí),需要用到這些符號(hào),來(lái)調(diào)試和修復(fù)錯(cuò)誤。
這些符號(hào)通常會(huì)從供用戶普遍使用的最終二進(jìn)制文件中刪除。刪除這些符號(hào)不會(huì)影響到二進(jìn)制文件的執(zhí)行。刪除符號(hào)通常是為了節(jié)省空間,因?yàn)橐坏┓?hào)被刪除了,二進(jìn)制文件就會(huì)稍微小一些。在閉源或?qū)S熊浖校?hào)通常都會(huì)被刪除,因?yàn)榘堰@些符號(hào)放在二進(jìn)制文件中,可以很容易地推斷出軟件的內(nèi)部工作原理。
根據(jù) Checksec 的結(jié)果,在二進(jìn)制文件 ??hello?? 中有符號(hào),但在 ??ls?? 的二進(jìn)制文件中不會(huì)有符號(hào)。同樣地,你還可以用 ??file?? 命令,來(lái)找到符號(hào)的信息,在二進(jìn)制文件 ??hello?? 的輸出結(jié)果的最后,看到 ??not stripped??,表明二進(jìn)制文件 ??hello?? 有符號(hào):
Checksec 是如何找到符號(hào)的信息呢?Checksec 提供了一個(gè)方便的 ??--debug?? 選項(xiàng),來(lái)顯示運(yùn)行了哪些函數(shù)。因此,運(yùn)行以下的命令,會(huì)顯示在 shell 腳本中運(yùn)行了哪些函數(shù):
在本教程中,我試圖尋找 Checksec 查找安全屬性信息時(shí),使用了什么底層命令。由于 Checksec 是一個(gè) shell 腳本,因此你始終可以使用 Bash 功能。以下的命令將輸出從 shell 腳本中運(yùn)行的每個(gè)命令:
如果你滾動(dòng)瀏覽上述的輸出結(jié)果的話,你會(huì)看到 ??echo_message?? 后面有各個(gè)安全屬性的類別。以下顯示了 Checksec 檢測(cè)二進(jìn)制文件是否包含符號(hào)時(shí),運(yùn)行的底層命令:
上面的輸出顯示,Checksec 利用 ??readelf??,來(lái)讀取二進(jìn)制文件,并提供一個(gè)特殊 ??--symbols?? 標(biāo)志,來(lái)列出二進(jìn)制文件中的所有符號(hào)。然后它會(huì)查找一個(gè)特殊值:??.symtab??,它提供了所能找到的條目的計(jì)數(shù)(即符號(hào)的個(gè)數(shù))。你可以在上面編譯的測(cè)試二進(jìn)制文件 ??hello?? 上,嘗試以下命令,得到與 Checksec 查看二進(jìn)制文件類似的符號(hào)信息:
(LCTT 譯注:也可以通過(guò)直接查看 ??/usr/bin/checksec?? 下的 Checksec 源文件。)
如何刪除符號(hào)
你可以在編譯后或編譯時(shí)刪除符號(hào)。
- 編譯后: 在編譯后,你可以使用
strip,手動(dòng)地來(lái)刪除二進(jìn)制文件的符號(hào)。刪除后,使用file命令,來(lái)檢驗(yàn)是否還有符號(hào),現(xiàn)在顯示stripped,表明二進(jìn)制文件hello無(wú)符號(hào)了:
- 編譯時(shí): 你也可以在編譯時(shí),用
-s參數(shù)讓 gcc 編譯器幫你自動(dòng)地刪除符號(hào):
重新運(yùn)行 Checksec,你可以看到現(xiàn)在二進(jìn)制文件 ??hello?? 的 ??symbols?? 這一屬性的值是??no??:
2、Canary(堆棧溢出哨兵)
Canary 是放置在緩沖區(qū)和 棧stack 上的控制數(shù)據(jù)之間的已知值,它用于監(jiān)視緩沖區(qū)是否溢出。當(dāng)應(yīng)用程序執(zhí)行時(shí),會(huì)為其分配兩種內(nèi)存,其中之一就是 棧。棧是一個(gè)具有兩個(gè)操作的數(shù)據(jù)結(jié)構(gòu):第一個(gè)操作 ??push???,將數(shù)據(jù)壓入堆棧;第二個(gè)操作 ??pop??,以后進(jìn)先出的順序從棧中彈出數(shù)據(jù)。惡意的輸入可能會(huì)導(dǎo)致棧溢出,或使用特制的輸入破壞棧,并導(dǎo)致程序崩潰:
Checksec 是如何確定二進(jìn)制文件是否啟用了 Canary 的呢?使用上述同樣的方法,得到 Checksec 在檢測(cè)二進(jìn)制文件是否啟用 Canary 時(shí),運(yùn)行的底層命令:
啟用 Canary
為了防止棧溢出等情況,編譯器提供了 ??-stack-protector-all?? 標(biāo)志,它向二進(jìn)制文件添加了額外的代碼,來(lái)檢查緩沖區(qū)是否溢出:
Checksec 顯示 Canary 屬性現(xiàn)已啟用。你還可以通過(guò)以下方式,來(lái)驗(yàn)證這一點(diǎn):
3、位置無(wú)關(guān)可執(zhí)行文件(PIE)
位置無(wú)關(guān)可執(zhí)行文件Position-Independent Executable(PIE),顧名思義,它指的是放置在內(nèi)存中某處執(zhí)行的代碼,不管其絕對(duì)地址的位置,即代碼段、數(shù)據(jù)段地址隨機(jī)化(ASLR):
通常,PIE 僅對(duì) 庫(kù)libraries啟用,并不對(duì)獨(dú)立命令行程序啟用 PIE。在下面的輸出中,??hello??? 顯示為 ??LSB executable???,而 ??libc??? 標(biāo)準(zhǔn)庫(kù)(??.so???) 文件被標(biāo)記為 ??LSB shared object??:
Checksec 查找是否啟用 PIE 的底層命令如下:
如果你在共享庫(kù)上嘗試相同的命令,你將看到 ??DYN???,而不是 ??EXEC??:
啟用 PIE
要在測(cè)試程序 ??hello.c?? 上啟用 PIE,請(qǐng)?jiān)诰幾g時(shí),使用以下命令:
你可以使用 Checksec,來(lái)驗(yàn)證 PIE 是否已啟用:
現(xiàn)在,應(yīng)該會(huì)顯示為 “PIE 可執(zhí)行pie executable”,其類型從 ??EXEC??? 更改為 ??DYN??:
4、NX(堆棧禁止執(zhí)行)
NX 代表 不可執(zhí)行non-executable。它通常在 CPU 層面上啟用,因此啟用 NX 的操作系統(tǒng)可以將某些內(nèi)存區(qū)域標(biāo)記為不可執(zhí)行。通常,緩沖區(qū)溢出漏洞將惡意代碼放在堆棧上,然后嘗試執(zhí)行它。但是,讓堆棧這些可寫(xiě)區(qū)域變得不可執(zhí)行,可以防止這種攻擊。在使用 ??gcc?? 對(duì)源程序進(jìn)行編譯時(shí),默認(rèn)啟用此安全屬性:
Checksec 使用以下底層命令,來(lái)確定是否啟用了 NX。在尾部的 ??RW??? 表示堆棧是可讀可寫(xiě)的;因?yàn)闆](méi)有 ??E??,所以堆棧是不可執(zhí)行的:
演示如何禁用 NX
我們不建議禁用 NX,但你可以在編譯程序時(shí),使用 ??-z execstack?? 參數(shù),來(lái)禁用 NX:
編譯后,堆棧會(huì)變?yōu)榭勺x可寫(xiě)可執(zhí)行(??RWE??),允許在堆棧上的惡意代碼執(zhí)行:
5、RELRO(GOT 寫(xiě)保護(hù))
RELRO 代表 “重定位只讀Relocation Read-Only”。可執(zhí)行鏈接格式(ELF)二進(jìn)制文件使用全局偏移表(GOT)來(lái)動(dòng)態(tài)地解析函數(shù)。啟用 RELRO 后,會(huì)設(shè)置二進(jìn)制文件中的 GOT 表為只讀,從而防止重定位攻擊:
Checksec 使用以下底層命令,來(lái)查找是否啟用 RELRO。在二進(jìn)制文件 ??hello??? 僅啟用了 RELRO 屬性中的一個(gè)屬性,因此,在 Checksec 驗(yàn)證時(shí),顯示 ??partial??:
啟用全 RELRO
要啟用全 RELRO,請(qǐng)?jiān)?nbsp;??gcc?? 編譯時(shí),使用以下命令行參數(shù):
現(xiàn)在, RELRO 中的第二個(gè)屬性也被啟用,使程序變成全 RELRO:
6、Fortify
Fortify 是另一個(gè)安全屬性,但它超出了本文的范圍。Checksec 是如何在二進(jìn)制文件中驗(yàn)證 Fortify,以及如何在 ??gcc?? 編譯時(shí)啟用 Fortify,作為你需要解決的課后練習(xí)。
其他的 Checksec 功能
關(guān)于安全性的話題是永無(wú)止境的,不可能在本文涵蓋所有關(guān)于安全性的內(nèi)容,但我還想提一下 Checksec 命令的一些其他功能,這些功能也很好用。
對(duì)多個(gè)二進(jìn)制文件運(yùn)行 Checksec
你不必對(duì)每個(gè)二進(jìn)制文件都進(jìn)行一次 Checksec。相反,你可以提供多個(gè)二進(jìn)制文件所在的目錄路徑,Checksec 將一次性為你驗(yàn)證所有文件:
對(duì)進(jìn)程運(yùn)行 Checksec
Checksec 除了能檢查二進(jìn)制文件的安全屬性,Checksec 還能對(duì)程序起作用。以下的命令用于查找你系統(tǒng)上所有正在運(yùn)行的程序的安全屬性。如果你希望 Checksec 檢查所有正在運(yùn)行的進(jìn)程,可以使用 ??--proc-all??,或者你也可以使用進(jìn)程名稱,選擇特定的進(jìn)程進(jìn)行檢查:
對(duì)內(nèi)核運(yùn)行 Checksec
除了本文介紹的用 Checksec 檢查用戶態(tài)應(yīng)用程序的安全屬性之外,你還可以使用它來(lái)檢查系統(tǒng)內(nèi)置的 內(nèi)核屬性kernel properties:
快來(lái)試一試 Checksec 吧
Checksec 是一個(gè)能了解哪些用戶空間和內(nèi)核的安全屬性被啟用的好方法。現(xiàn)在,你就可以開(kāi)始使用 Checksec,來(lái)了解每個(gè)安全屬性是什么,并明白啟用每個(gè)安全屬性的原因,以及它能阻止的攻擊類型。

























