Week 7 - OverlayFS
OverlayFS 是一種分層式的 file system,將多個 file system 的資料堆疊,上層的檔案會覆蓋下層同路徑檔案,呈現在使用者面前。
- Upper layer
最上層的 layer,可讀寫。 - Lower layer
至多 500 層,可讀。 - Workdir
OverlayFS 用來做額外操作的暫存空間,不會呈現給使用者。
當使用者刪除下層的檔案,OverlayFS 對檔案標記 whiteout,檔案看似在使用者面前消失,仍不影響下層檔案。
若使用者編輯下層檔案,則複製一份至 Upper layer,此行為稱為 Copy-up。
- inode: file system 裡的檔案 metadata,file 結構會包含 inode, path, owner等資訊。
- dentry: 一組 path 和 inode 的映射,一個 inode 可能被很多 path 映射
掛載 OverlayFS:
1 | mount -t overlay overlay -o lowerdir=lower1:lower2,upperdir=upper,workdir=work merged |
OverlayFS 結構
ovl_config
OverlayFS 一開始的掛載設定,mount 時參數將存入裡面,ovl_fill_super
藉由裡面的資訊建構整個 ovl_fs
。
1 | struct ovl_config { |
ovl_sb
紀錄掛載的 upperdir, lowerdir 的 fs 類型,是 ovl_layer
的一部分,每一層對應一個 ovl_sb
。sb
指向管理該 fs 的 super_block
結構,pseudo_dev
是 overlayfs 使用的虛擬 device number。
1 | struct ovl_sb { |
ovl_layer
存放 overlayfs 的各個 layer 資訊。
mnt
→ 該層的掛載資訊trap
→ 用來避免 inode cache 錯誤的一個虛擬 inodefs
→ 這一層對應的ovl_sb
idx
→ 在整個 overlayfs 是第幾層fsid
→ 對應的唯一檔案系統 ID
1 | struct ovl_layer { |
ovl_fs
管理整個 overlayfs。indexdir
透過 ovf_config->index
啟用,紀錄所有檔案 inode 以加快查詢速度。
1 | /* private information held for overlayfs's superblock */ |
ovl_path
紀錄一個在 overlayfs 的路徑,對應到哪個 layer 的實際的 path。
1 | struct ovl_path { |
ovl_entry
紀錄一個目錄在每個 file system 的真實位置。
1 | struct ovl_entry { |
ovl_inode
overlayfs 自己的 inode 物件:
cache
/lowerdata
→ 目錄的快取 / lower 層的 inoderedirect
→ 是否有目錄重導向version
→ inode 版本vfs_inode
→ VFS 的inode
__upperdentry
→ 這個 inode 對應的 upper 層 dentry,如果檔案沒被修改就不需要lowerpath
→ lower 層對應的ovl_path
lock
→ 用來保護 copy-up 的 mutex lock1
2
3
4
5
6
7
8
9
10
11
12
13
14
15struct ovl_inode {
union {
struct ovl_dir_cache *cache; /* directory */
struct inode *lowerdata; /* regular file */
};
const char *redirect;
u64 version;
unsigned long flags;
struct inode vfs_inode;
struct dentry *__upperdentry;
struct ovl_path lowerpath;
/* synchronize copy up and more */
struct mutex lock;
};
ovl_inode
和 ovl_entry
有點像,但前者紀錄 overlayfs 的一個檔案指向哪個實際檔案,後者紀錄一個 directory 在哪些層有資料。因為 dir 在不同 layer 的子目錄、檔案可能不同,所以它記錄很多 layer。
ovl_dir_file
管理已開啟的目錄,cursor
用來記錄 readdir()
操作進度。
1 | struct ovl_dir_file { |
ovl_dir_cache
儲存目錄底下的檔案列表。
1 | struct ovl_dir_cache { |
CVE-2023-0386
先看 patch:
ovl_copy_up_one
用來將檔案 copy-up 到 upper layer,而 patch 增加了 user namespace 的檢查。ctx.stat
是從 lower 拿到的 file stat,若擁有者的 uid, gid 沒被當前 namespace 映射到,就直接 return。
本漏洞的利用是透過 FUSE 和 OverlayFS 的組合,創建一個 setuid 的程式,允許普通使用者以 root 身份執行。
在 linux 執行 setuid 需符合以下條件:
當前登入使用者有
CAP_SETUID
權限執行檔標示
setuid
權限,可用 chmod 賦予權限。1
chmod u+s filename
檔案系統支援
setuid
擁有者是 root 或目標的 uid
並且,只要一個檔案 owner 是 root 且檔案有 setuid
權限就能提權。
已知 FUSE 是使用者自定義的 file system,可回傳任意的 file stat 或內容,當然也能回傳一個 setuid 的惡意程式。
然而,FUSE 雖可改 owner 為 root 並 setuid 權限,但由於 mount 時都會強制加上 nosuid,無法真的執行 setuid。
1 | mount | grep fuse |
不過,若能夠將檔案的內容和 stat,複製到一個正常的 file system,且這個 file system 無 nosuid 標誌,是不是就能成功提權呢?
OverlayFS 的 copy-up 就能做到這件事,本漏洞即是讓 FUSE 作為 lower layer,複製檔案到正常的 upper layer 達成提權。
然而,mount 一個 OverlayFS 需要 root 權限,必須新增一個 user namespace 和 mount namespace。
1 | unshare -Urm |
在 OverlayFS touch 一個檔案時,就算檔案已經存在,也會更新其時間戳,因此可用來提升到 upper layer。
1 | mkdir -p fuse_dir upper merged workdir |
而 patch 檢查 file owner 是否在 namespace 裡。
1 |
|