Skip to content

【安卓安全】SELinux,熟悉又陌生

Published:
14
6

2012 年,美國舊金山,山景城。

這是一個陰沉的下午,窗外不見陽光。13 度的陰風從沙丘上吹來,穿過園區外圍的樹叢,掃在了大樓的玻璃外牆上。平時聒噪的烏鴉已經不知所蹤,隔着玻璃外牆,裏面的安卓安全團隊會議室是一片死寂。

三個身影圍坐在桌子旁邊,看着投影儀投射出的 log,誰也沒有說話。從後面看過去他們就像是一座座墓碑。

其中一位率先打破了沈默,

🧑🏻‍🦲「KingRoot 把 4.1 給 root 了?誰能給我解釋一下?💢」
🧑🏼‍💻「me……mediaserver 被攻破了!這是個 system 用戶的 daemon……」
🧑🏻‍🦲「蛤?要知道 👉🏻zergRush👉🏻GingerBreak 才過去多久!這整的都是些什麼爛活??」

🧑🏻‍🦲「那 OEM 那邊的事情有結果了嗎?」
👩🏻「是……是!經過我們的調查,那家出貨量快一千萬的 M 字頭公司,他們的 daemon 不僅用 root 權限運行,還留下了一個萬人騎的 socket……😅」
🧑🏼‍💻「我也看了下,一套絲滑連招我就跑到內核裏面去了……😅」

🧑🏻‍🦲「放肆!我可沒忘記峰会上被蘋果指着鼻子笑『安卓安全』的屈辱!我要求這個項目馬上上馬!!」

POV:Android Security Team be like
POV:Android Security Team be like

🧑🏻‍🦲「SELinux,啟動!!!💢💢

於是一個時代拉開了序幕……

Contents

Ⅰ Linux 文件權限的邊界

如果妳也是一位從古早時代而來的鐵安卓人,那妳應該體會過安卓 5.0/6.0 升級所帶來的震撼。砍掉重練的精美 UI,劃時代的 App 權限管理和飛躍一般的性能提升,一時間彷彿讓安卓玩家有了碰瓷蘋果的底氣。

安卓 5.0,告別醜逼 UI
安卓 5.0,告別醜逼 UI

然而,如果換一個視角,以一位 root 玩家的身分來看的話,5.0/6.0 無疑就是安卓被套上緊箍咒的耻辱柱。以往熟悉的 root 工具在一夜之間失效,而要通過刷機才能安裝的新型 root 工具登上了歷史舞台。同時,6.0 開始對強制全盤加密和煩人的 Bootloader 鎖進行普及,彷彿以往「自由開放」的口號已經成為了一紙空談。

許多人都知道安卓基於 Linux 內核開發,但是一和自由開放的 Linux 做對比,安卓的這些封閉化措施簡直就是倒反天罡。要理解這些變遷背後的祕密,就不得不提到一個深藏幕後的安全組件:SELinux。而要理解 SELinux,就必須要從 Linux 權限控制的缺陷開始說起。


我希望妳還記得 Linux 文件權限是怎麼工作的。如果不記得了,讓我簡單幫妳恢復一下記憶。

現在假設 Linux 上有 4 個用戶,分別是 root, kasumi, alisa 和 sayo。其中 root 是超級管理員,kasumi 和 alisa 屬於同一個用戶組 Poppin’Party。

然後 kasumi 創建了一個文件,設定了權限如下:

所有者:kasumi
所屬用戶組:Poppin'Party

可讀可寫可執行
自己
同組成員
所有人

最後,各個用戶訪問這個文件的權限如下:

這些設定只能控制訪問該文件的權限。而一個用戶的其他操作權限,比如說聯網,都是默認允許的。

在 Linux Server 上,這一套文件權限管理已經足夠,管理員稍微懂點電腦,不亂玩的話就不會有大問題。但是問題就出在這裏,安卓工程師不能指望手機用戶能擁有 Linux 管理員那樣的安全素質。更可怕的是,手機用戶會主動去下載大量第三方軟件,壞東西混進來是遲早的事。

事實上,4.2 之前版本的安卓運用的都是這一套權限系統。通過文件權限,系統對各個 App 施加了一些限制:

知道了這些以後,我們可以開始看看歷史上都發生過些什麼了……😁

💥 zergRush (2011–2012) 💥

安卓系統上有一個叫 vold 的程序,負責管理儲存空間的掛載,擁有 root 權限。不幸的是,這個程序存在緩衝區溢出漏洞,惡意 App 可以觸發這個漏洞,強行使用 vold 的權限執行任意代碼。

一套連招打下去:

mount -o remount,rw /system  # 掛載系統分區
cp su /system/xbin           # 寫入 su 程序
chmod 6755 su                # 賦予 su 權限
su                           # su,啟動!我現在是 root 了

直接絲滑拿到 root。後果就是整個手機都被控制,惡意軟件叱吒四方,重啟也不能恢復。事實上,這也是那些「一鍵 root」軟件所利用的漏洞。

類似的漏洞不少,除此之外還有更可怕的:

😁 Stagefright (2015) 😁

安卓的 mediaserver 使用的媒體處理庫裏面有個漏洞,會造成緩衝區溢出。危險的是,mediaserver 的權限特別高,而且需要處理來自四面八方的媒體文件,包括相機,音樂和圖片等。黑客可以直接往受害者手機上發送一張帶毒的圖片,然後遠程接管整個手機。


普通的文件權限控制,並不能防止高權限程序中的漏洞被大規模濫用,也不能防止某些技術低劣的手機廠商主動引入安全漏洞。

谷歌面對 OEM 漏洞的時候 be like:妳的意思是,M 記系統用 root 權限運行系統應用,還開放調試後門,這個後門還是個大比人人插的權限,然後黑客用後門黑了手機,把系統分區都給揚了,恢復出廠都救不回來,這種事情,要怪在我頭上,妳是這個意思嗎?😅

安全措施的薄弱導致彼時的安卓生態極其惡劣,病毒橫行,一旦中毒就會被隨意強姦內核,安插系統後門,盜竊私人數據。用戶不僅察覺不到,更沒法修復。

可見,即使同為 Linux 內核,安卓手機和服務器所處的環境完全不同:

環境服務器安卓手機
管理員擁有專職管理員不存在
用戶會主動保護系統會主動破壞系統
安裝軟件開源軟件、知名軟件的多國產軟件、流氓軟件氾濫
廠商水平會負責任地維護會無止境地搞事
攻擊方式來自外部網路來自系統內部

到了這個份上,這已經不是某個系統漏洞的問題,而是安全模型的塌房。Linux 的權限系統哪裏見過這種大場面,屬於是無力迴天。谷歌也嘗試過譬如增加權限種類,校驗數字簽名和更用力地警告用戶等改善方法,但均收效甚微。汗流浹背之中,谷歌決定要給現狀一個迎頭痛擊。於是,SELinux 就在安卓 4.2 上轟轟烈烈地登場了。

SELinux,啟動!
SELinux,啟動!

⭐️ 幕間:SELinux FAQ

從歷史的角度上看,安卓擁抱 SELinux 無疑是成功的,是衆望所歸的選擇。不過在開始手撕 SELinux 的規則之前,我想先快速地回答幾個常見問題。

一言以蔽之,SELinux 通過規定「什麼進程可以對哪個東西進行何種操作」來實行最小權限原則和精細訪問控制。每個程序都只能拿到所需的最低權限,極大降低了濫權的可能性。

Ⅱ 安卓安全➡️蘋果安全

SELinux 有兩種模式,分別是「Permissive」和「Enforcing」。Enforcing 模式會強制執行每一條規則,猛猛打擊違規的進程;而 Permissive 則是睜一隻眼閉一隻眼,它只會把違規行為記錄到 log 中,以供開發人員查閱。

SELinux 對於安卓來說屬實是一劑猛藥,它的藥效過於強大,以至於谷歌也沒能在一夜之間啟用它。安卓 4.2 引入了 SELinux,之後的安卓 4.3 啟用了 SELinux,只不過是 Permissive 模式。各個手機廠商和開發人員在這個過渡階段進行適應,改進 SELinux 規則和自己程序的行為。

到了安卓 4.4,谷歌開始用 enforcing 模式來保護一些關鍵的系統程序,防止系統漏洞被利用。而在安卓 5.0 中,SELinux 被完全打開,整個系統都受到 enforcing 模式的管理。這是一個偉大的里程碑,過去那些能直接破解 root 的惡性漏洞都將不復存在。

此外,谷歌規定手機廠商不能隨便篡改 SELinux 規則,否則不頒發 CTS 認證。以往頻頻做妖的手機廠商在這一瞬間變得儒雅隨和——否則就要被掃地出門。

安卓手機好像要摘掉安卓安全的帽子了。6.0 開始先後引入的運行時權限,全盤加密,Verified Boot,只讀 /system 分區和防回滾等安全措施,本質上都是在 SELinux 的地基上進行的施工。時至今日,安卓安全這座大廈已經建起,iPhone 能被 FBI 破解,Pixel 卻不能;某國政府要破解國產手機,甚至不得不親自部署後門。安卓已經成為宇宙第一安全的操作系統……就好了 😅

安卓安全?蘋果安全!
安卓安全?蘋果安全!

安全攻防是一個永恆的話題,妳永遠不知道下一個漏洞會從哪裏冒出來。SELinux 的存在確實極大減少了攻擊面,可是它卻對一些人為引入的 bug 無能為力。和上游的 AOSP 和 Kernel 開發者相比,OEM 廠商的水平更加低下,更加容易犯低級錯誤,漏洞也更容易利用。一個最典型的例子就是 2022 年的拼多多漏洞利用事件。

且慢!在繼續講故事之前,先讓我們一起學習一下 SELinux 的工作原理!

🌙 幕間:SELinux 原理

上文已經說過,SELinux 通過設定規則,對「哪個進程對於什麼對象可以進行何種操作」進行限制。此處的對象可以是文件和目錄,也可以是 socket 或者硬件,還可以是內存甚至內核數據結構等。如此一來,SELinux 就對全世界上可能進行的操作進行了網羅。

要讓 SELinux 發揮作用,系統管理員先要對各個進程和各個對象打上標籤,再編寫規則。規則中是形如「標籤 A 的進程可以對標籤 B 的對象進行 C 的操作」,規則中沒寫的就全部攔截。

標籤(學名:Security Contexts)大概長這樣:

user : role : type : level [:categories]

其中 user 和 role 一般不看,最重要的是 type 和 categories。比如說我有一個 Docker 程序,我給他打上標籤如下:

system_u:system_r:container_t:s0

其中:

我還有一堆文件,我也給他們打上標籤如下:

system_u:system_r:container_file_t:s0

其中:

然後規定「container_t 只可以查看 container_file_t 的文件」,再加上這兩者都沒有 category 進行隔離,那麼我的程序就被允許訪問這些文件了。

分享一些 Linux 上的進程常用的 type:

SELinux type何意味
init_tsystemd / init
unconfined_tinteractive shells (真人命令行)
sshd_tSSH daemon
httpd_tApache / nginx
container_tcontainer processes
crond_tcron jobs

例:我在一開始安裝系統的時候就設定規則「unconfined_t 可以魔改 SELinux 設定」來允許人類用戶改變 SELinux 的規則和設定。

還有一些文件的 type:

SELinux type何文件
etc_tconfig files
bin_tbinaries
var_log_tlogs
home_root_t/home
httpd_sys_content_tweb content

妳沒有必要牢記這些 type,甚至也沒必要自己寫規則,因為大佬都提前寫好了,打包安裝就完事了。萬一真出現了訪問被攔截的情況,俺們也只需要微調一下規則或者配置就行了。

SELinux 具有精細的權限設定和以 category 為基礎的隔離能力,彷彿就是為了手機量身定製的 App 沙盒。在安卓上,程序的權限等級森嚴,由 type(也稱為 domain)決定。每個程序只被允許做自己該做的事,下面是一些安卓上的 SELinux domain。

SELinux type何程序
initinit process
zygoteApp spawner
system_server核心系統程序
untrusted_app第三方 App
magiskMagisk 開的後門

安卓上的 SELinux policy 規定了:

舉個例子,Telegram 在我的手機上的 domain 是這樣的(ps -AZ 命令,我安裝了兩個版本)

u:r:untrusted_app:s0:c165,c256,c512,c768  org.telegram.messenger
u:r:untrusted_app:s0:c195,c256,c512,c768  org.telegram.messenger.web

可見,他們都屬於 untrusted_app 的種類,但是 category 不同,一個是 c165,c256,c512,c768 另一個是 c195,c256,c512,c768,這叫做「基於 category 的 App 隔離」。每個 App 會被分配一組隨機 category,從而在同一個 untrusted_app domain 內實現橫向隔離。

App 橫向隔離,只能訪問自己的文件
App 橫向隔離,只能訪問自己的文件

現在假設其中一個 Telegram 其實是病毒看看會發生什麼,讓我們考慮以下三種情況:

  1. 內核或者驅動有 bug,病毒拿到了 root 權限;
  2. 內核有嚴重 bug,導致病毒可以隨意讀寫內存;
  3. 某個高權限進程有 bug,讓病毒可以注入代碼。

1️⃣ 內核或者驅動有 bug,病毒拿到了 root 權限

這是一種常見的情況,世界上有問題的驅動太多了,導致病毒有可能拿到 UID 0。但是只要漏洞沒能幹爛 SELinux 或者篡改 domain,它仍然不能魔改系統文件,強姦其他程序,直接操作硬件,甚至也不能讀寫其他 App 的文件夾。進程依然會被鎖在 untrusted_app 這個籠子裡。

這種情況就是 SELinux 發光發熱的地方。


2️⃣ 內核有嚴重 bug,導致病毒可以隨意讀寫內存

萬一真有這種 bug 那基本上完蛋了,病毒可以通過修改內核內存的方式來強姦 SELinux 並掌管整個系統。但是這並不意味着手機報廢,因為安卓上還有 Verified Boot 來防止系統文件篡改,還有 TEE 來防止密鑰泄漏。用戶仍然能夠重啟到安全模式並嘗試清除病毒。

非常危險,但還剩一口氣,,,🥶


3️⃣ 某個高權限進程有 bug,讓病毒可以注入代碼

這就相當危險了,因為病毒並沒有親自進行操作,而是催眠了一個高權限的傀儡來借刀殺人。上面提到的拼多多漏洞就屬於這種情況。拼多多本來是 untrusted_app,但是卻通過合法的 IPC 遙控了 system_server,使得他可以在 SELinux 的眼皮子底下,通過 system_server 的身分進行權限內允許的操作。

這種情況就是 SELinux 鞭長莫及的地方。


綜上所述,SELinux 將進程和目標對象用標籤進行歸類,並以規則為白名單實現了精細的訪問控制,極大地減少了權限野放的情況。SELinux 的正常工作依賴於內核的安全,一個有後門的內核會推倒一切安全的前提條件。另外,SELinux 也不能防止廠商引入的弱智 bug 遭到爆破。

SELinux 不是萬能的,但是它是組成安卓安全願景的最大的一塊拼圖。每年,安卓手機的出貨量上億,其數量遠超人類所擁有的 Linux 服務器的總和。安卓手機是全世界分佈最廣的 SELinux 集群。而這其中的一台也許就在妳的手中。

Ⅲ 結語:Linux 服務器安全

遙想 SELinux 第一次被加入 Linux 內核,已是 20 多年前。然而這麼多年過去,除了資深企業級開發者以外,SELinux 的存在恐怕鮮為人知。學校課上不會教,教程裏面不會講,對 Linux 的加固措施彷彿也僅限於設定 ssh 密鑰。對於絕大多數人來說,SELinux 就像是一個只存在於傳說中的怪物。

由於歷史的原因,我這一代的 VPS 玩家很多都是從搭建 WordPress 入的門。鋪天蓋地的教程都是基於 LNMP 和 CentOS,而這些教程的第一步永遠是 setenforce 0,第二步是去 /etc/selinux/config 裏面把 SELinux 關掉。SELinux 是妖魔鬼怪,是洪水猛獸,是碰都不能碰的遠古詛咒。如果不狠狠封印,WordPress 將永無出頭之日,,,

從另一個角度上看,俺們會覺得 SELinux 麻煩,那只是因為俺們還沒遇到安卓當年遇到的那種敵人。

或者學習一下,怎麼把敵人當隊友😁
或者學習一下,怎麼把敵人當隊友😁

後來,SEAndroid 的出現和發展,讓 SELinux 得到了前所未有的實踐機會。安卓手機的分佈上到宇宙,下到深海,組成了龐大的 SELinux 試驗場。業界終於有機會大規模測試並驗證 SELinux 的可行性,多年的運用經驗又反哺了 SELinux 在 Linux 社區的推廣和利用。與此同時,酷安小鬼們在嘗試摧毀自己的手機的時候,SELinux 總會跳出來提醒一下它的存在。潛移默化之中,人們似乎在面對 SELinux 時不再聞風喪膽,變得更加自信了。

時至今日,新的契機到來,雲計算和邊緣計算得到了極大的發展,容器化服務已經成為開發和部署的最佳實踐。如何安全地在雲上運行阿貓阿狗寫的 Docker 程序成了難題,而 SEAndroid 無疑提供了寶貴的經驗——SELinux 曾經幫助過安卓手機在遍佈了火山,地雷和惡龍的環境中生存。現在,通過在各個容器上設定 domain, category 和規則,SELinux 將給容器化服務帶去強制隔離的恩惠。

就算妳是一個自建玩家,當妳在絞盡腦汁地給 Dockerfile 設置用戶,給 volume 設置權限,給網路設置防火牆,掂量着要不要加這個 --privileged——為了確保自己的 docker-compose 的安全而苦戰的時候——我也想要和妳講個故事。

而這個故事,要從 2012 年,美國舊金山,山景城,的一個陰沉的下午,開始說起。


Next Post
【2026 Kickoff】宇宙的盡頭是銷售(上篇)

評論區

妳的評論和建議是我前進的動力!
我很需要妳的評論!無論長短還是水,我都會非常高興 😘

darkreader icon 請停用 Dark Reader

親愛的 Dark Reader 用戶:

本博客已內置了深色模式,並且 Dark Reader 會導致部分元素顯示錯誤.
請在本博客上停用 Dark Reader,謝謝,,,

🌸
🌸
🌸