2012 年,美國舊金山,山景城。
這是一個陰沉的下午,窗外不見陽光。13 度的陰風從沙丘上吹來,穿過園區外圍的樹叢,掃在了大樓的玻璃外牆上。平時聒噪的烏鴉已經不知所蹤,隔着玻璃外牆,裏面的安卓安全團隊會議室是一片死寂。
三個身影圍坐在桌子旁邊,看着投影儀投射出的 log,誰也沒有說話。從後面看過去他們就像是一座座墓碑。
其中一位率先打破了沈默,
🧑🏻🦲「KingRoot 把 4.1 給 root 了?誰能給我解釋一下?💢」
🧑🏼💻「me……mediaserver 被攻破了!這是個 system 用戶的 daemon……」
🧑🏻🦲「蛤?要知道 👉🏻zergRush 和 👉🏻GingerBreak 才過去多久!這整的都是些什麼爛活??」
🧑🏻🦲「那 OEM 那邊的事情有結果了嗎?」
👩🏻「是……是!經過我們的調查,那家出貨量快一千萬的 M 字頭公司,他們的 daemon 不僅用 root 權限運行,還留下了一個萬人騎的 socket……😅」
🧑🏼💻「我也看了下,一套絲滑連招我就跑到內核裏面去了……😅」
🧑🏻🦲「放肆!我可沒忘記峰会上被蘋果指着鼻子笑『安卓安全』的屈辱!我要求這個項目馬上上馬!!」

🧑🏻🦲「SELinux,啟動!!!💢💢」
於是一個時代拉開了序幕……
Contents
Ⅰ Linux 文件權限的邊界
如果妳也是一位從古早時代而來的鐵安卓人,那妳應該體會過安卓 5.0/6.0 升級所帶來的震撼。砍掉重練的精美 UI,劃時代的 App 權限管理和飛躍一般的性能提升,一時間彷彿讓安卓玩家有了碰瓷蘋果的底氣。

然而,如果換一個視角,以一位 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
| 可讀 | 可寫 | 可執行 | |
|---|---|---|---|
| 自己 | ✅ | ✅ | ✅ |
| 同組成員 | ✅ | ||
| 所有人 |
最後,各個用戶訪問這個文件的權限如下:
- root:可以為所欲為
- kasumi:可以讀寫執行
- alisa:可以讀
- sayo:只能看到文件名等信息,不能訪問內容
這些設定只能控制訪問該文件的權限。而一個用戶的其他操作權限,比如說聯網,都是默認允許的。
在 Linux Server 上,這一套文件權限管理已經足夠,管理員稍微懂點電腦,不亂玩的話就不會有大問題。但是問題就出在這裏,安卓工程師不能指望手機用戶能擁有 Linux 管理員那樣的安全素質。更可怕的是,手機用戶會主動去下載大量第三方軟件,壞東西混進來是遲早的事。
事實上,4.2 之前版本的安卓運用的都是這一套權限系統。通過文件權限,系統對各個 App 施加了一些限制:
- 每個 App 都是一個獨立的 Linux 用戶,並且只能訪問自己的文件。
- 然而一些系統 App 擁有高權限,比如說 system 甚至 root。
知道了這些以後,我們可以開始看看歷史上都發生過些什麼了……😁
💥 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 FAQ
從歷史的角度上看,安卓擁抱 SELinux 無疑是成功的,是衆望所歸的選擇。不過在開始手撕 SELinux 的規則之前,我想先快速地回答幾個常見問題。
🤔 什麼是 SELinux?
💬 SELinux 是一個 Linux 上的強化訪問控制系統。與文件權限不同,它制定規則形如「A 可以對 B 進行 C 操作」來控制系統中的行為。規則裏面沒寫的全部拒絕,就連 root 用戶也要嚴格遵守規則。🤔 SELinux 在哪裏?
💬 SELinux 是一個 Linux 內核的安全模組,它在內核的關鍵路徑上設置了大量的檢查點(鉤子)。當程序想執行某些操作的時候,就需要經過這些鉤子。這就使得 SELinux 可以攔截檢查行為是否符合規定。🤔 為什麼我覺得 Linux 那麼熟悉,SELinux 卻那麼陌生?💢
💬 常見的系統如 Ubuntu 默認不啟用 SELinux 策略。在其他系統上就算 SELinux 開着,如果設定得好,它也不會沒事跳出來咬人。而且就算被咬了,很多人因為不會改規則也都是直接一關了事。🤔 SELinux 的目的是什麼?
💬 SELinux 可以解決文件權限機制解決不了的問題。比如「讀寫執行」的權限就過於粗放,而且對於不涉及文件的操作無能為力。其次 root 用戶可以為所欲為,一旦 root 的程序淪陷,那黑客就能夠接管整個系統。SELinux 通過制定「誰可以幹啥」,實現專人專事,把權力關進制度的籠子裏。如此一來,就算程序被黑客控制,黑客也只能執行白名單裏面的操作,大大減少攻擊面。🤔 如果 root 用戶都不能為所欲為,那誰能管理 SELinux?
💬 妳問得非常好!!1!實際上,系統管理員會在一開始就新增一條規則,規定只有「我自己的 shell 進程」可以修改 SELinux 設定。這樣,只有坐在屏幕前的真人才擁有修改 SELinux 的權限,其他程序就算拿到了 root,如果沒有規則允許也沒法對 SELinux 動手腳。🤔 SELinux 可以防止 QQ 掃盤嗎?
💬 可以!SELinux 管理的是進程,而不是用戶。假設在一台 Linux 電腦上,由於 QQ 繼承了當前的桌面用戶權限,理論上牠可以把用戶文件夾掃個精光——但是實際上,由於 SELinux 的限制,牠只能讀取白名單裏面的文件。🤔 要是內核有漏洞,SELinux 還能防得住嗎?
💬 也許,可以!想要利用內核漏洞進行提權的病毒,最開始都是要從 userspace 入手的。SELinux 通過限制用戶空間的行為,基本可以把利用漏洞的企圖扼殺在搖籃之中。(除了拼多多的那次😁)🤔 我的手機遙遙領先,內核後庭大開,妳又如何應對??
💬 要是對面能在內核中執行任意代碼,那誰也救不了。妳這已經不是手機上有個後門,屬於是後門上長了個手機了,而妳卻還有心情拿着一台後門在這邊看我的文章,😅,6
一言以蔽之,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其中:
- type:
container_t - category: 沒有
我還有一堆文件,我也給他們打上標籤如下:
system_u:system_r:container_file_t:s0其中:
- type: container_file_t
- category: 沒有
然後規定「container_t 只可以查看 container_file_t 的文件」,再加上這兩者都沒有 category 進行隔離,那麼我的程序就被允許訪問這些文件了。
分享一些 Linux 上的進程常用的 type:
| SELinux type | 何意味 |
|---|---|
init_t | systemd / init |
unconfined_t | interactive shells (真人命令行) |
sshd_t | SSH daemon |
httpd_t | Apache / nginx |
container_t | container processes |
crond_t | cron jobs |
例:我在一開始安裝系統的時候就設定規則「unconfined_t 可以魔改 SELinux 設定」來允許人類用戶改變 SELinux 的規則和設定。
還有一些文件的 type:
| SELinux type | 何文件 |
|---|---|
etc_t | config files |
bin_t | binaries |
var_log_t | logs |
home_root_t | /home |
httpd_sys_content_t | web content |
妳沒有必要牢記這些 type,甚至也沒必要自己寫規則,因為大佬都提前寫好了,打包安裝就完事了。萬一真出現了訪問被攔截的情況,俺們也只需要微調一下規則或者配置就行了。
SELinux 具有精細的權限設定和以 category 為基礎的隔離能力,彷彿就是為了手機量身定製的 App 沙盒。在安卓上,程序的權限等級森嚴,由 type(也稱為 domain)決定。每個程序只被允許做自己該做的事,下面是一些安卓上的 SELinux domain。
| SELinux type | 何程序 |
|---|---|
init | init process |
zygote | App spawner |
system_server | 核心系統程序 |
untrusted_app | 第三方 App |
magisk | Magisk 開的後門 |
安卓上的 SELinux policy 規定了:
- zygote 啟動 App 的時候,要以
untrusted_app的 type 來啟動 untrusted_app只能訪問自己的文件夾- 誰也不能碰
/system分區
舉個例子,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 內實現橫向隔離。

現在假設其中一個 Telegram 其實是病毒看看會發生什麼,讓我們考慮以下三種情況:
- 內核或者驅動有 bug,病毒拿到了 root 權限;
- 內核有嚴重 bug,導致病毒可以隨意讀寫內存;
- 某個高權限進程有 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 年,美國舊金山,山景城,的一個陰沉的下午,開始說起。

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