今天我們來聊聊因為 sudo 設定錯誤導致一般使用者變成特權帳號 root 的案例,我們先了解什麼是 sudo?
如果你在 Linux 裡面輸入
man sudo
你會看到 sudo 在官方文件的說明是這樣的
因此可以知道,sudo 是一個讓使用者在授權範圍內,以另一個使用者的身分執行特定指令的工具。它的目的,是讓低權限帳號在有正當需求時,不必直接取得完整的高權限帳號,也能完成需要較高權限的操作,例如使用 sudo dnf install httpd 安裝 Apache HTTP Server。
正因為 sudo 是一個切換執行身分的工具,因此每當攻擊者取得初始 shell 後,sudo -l 幾乎是最早執行的枚舉動作之一。
在開始之前,先把靶機的 sudo 設定攤開來,方便大家自己重現。
user ALL=(root) NOPASSWD: /usr/bin/less /var/log/lab06/app.log
user ALL=(root) NOPASSWD: /opt/lab06/maintenance.sh
user ALL=(svcbackup) NOPASSWD: /usr/bin/cat
| 項目 | 內容 |
|---|---|
| Starting Privilege | user,一般帳號,無 root |
| Target | 取得 root shell |
| Prerequisite | maintenance.sh 對 group ops06 可寫;/tmp 未以 nosuid 掛載 |
| Expected Result | id 顯示 uid=0(root) |
⚠️ 風險提醒:此操作建議在虛擬機上面執行,並先建立快照方便還原。
sudo -l 這個指令會去解析 /etc/sudoers 與 /etc/sudoers.d/*,列出目前使用者被授權的 sudo 權限。承接昨天的權限模型,我們先用 id 確認自己的身分,再用 sudo -l 看看手上有哪些權限。
id
sudo -l

先看 id:
uid=1000(user) gid=1000(user) groups=1000(user),1001(opsadmin),1004(ops06)
這裡有兩件事先記著,第二條會用到:
user,不是 root
ops06 群組(groups=…,1004(ops06))—— 這代表凡是「group 為 ops06 且群組可寫」的檔案,我都改得動接著看 sudo -l,可以知道 user 這個帳號能用 sudo 做三件事:
less 查看 /var/log/lab06/app.log
/opt/lab06/maintenance.sh
cat
這三條裡有兩個提權風險 + 一個身分橫移,我們一條一條看。
less 是很多工程師在查看 log 的好夥伴,比 more 好用的地方在於可以往回捲動。其中 less 的官方說明有提到一個功能:
這個功能的意思是 less 可以呼叫一個 shell 來執行命令(俗稱 shell escape),一邊查 log 一邊操作的時候很方便。既然 sudoers 讓 less 以 root 身分執行,那麼從 less 裡面逃逸出來的 shell,自然也是 root 的 shell,使用者就間接拿到了最高權限。
我們先利用 less 開啟檔案:
接著我們輸入 !id 按下 Enter,會發現 UID / GID 都顯示成 root:
再用同樣的方式執行 !/bin/bash,就成功拿到一個 root shell:

我們再來看 /opt/lab06/maintenance.sh 的權限:
ls -l /opt/lab06/maintenance.sh

這裡我們可以看到檔案 owner 是 root,但 group ops06 卻有寫入權(w)。這邊的風險在於:這是一個「內容我能改」而且「我能用 sudo 叫它以 root 身分跑」的腳本。當我把命令寫進去、再用 sudo 觸發它時,那行命令就會以 root 執行。
實際操作:
echo 'cp /bin/bash /tmp/rootbash; chmod u+s /tmp/rootbash' >> /opt/lab06/maintenance.sh
sudo /opt/lab06/maintenance.sh # root 會執行你加的那行
/tmp/rootbash -p # -p 保留 euid=0 ⇒ root shell

解釋一下上面的 payload:
/bin/bash 複製到 /tmp/rootbash
/tmp/rootbash 執行 chmod u+s 設定 SUID,讓它之後被執行時 euid = 檔案 owner(也就是 root)sudo /opt/lab06/maintenance.sh,這一步由 root 執行整個腳本,包含我們注入的那行,所以 /tmp/rootbash 是以 root 身分被建立、SUID 也設定成功/tmp/rootbash -p 取得 root shell-p?我們來看下面這張表
| 概念 | 意義 | 在這個情境 |
|---|---|---|
| ruid(real uid) | 你「真正是誰」 | 仍然是 user |
| euid(effective uid) | 核心用「誰的身分」做權限檢查 | SUID 讓它變成 owner root |
也就是說,SUID 讓 /tmp/rootbash 執行時 euid=root、ruid=user。而 bash 有一個內建的自我保護機制:
man bash:若 shell 啟動時 effective uid 不等於 real uid,且未提供-p選項,bash 會主動把 euid 降回 ruid,並且不讀取啟動檔。
所以如果不加 -p,bash 一啟動就把自己降權回 user,SUID 就白設了。-p 就是在跟 bash 說「別降權」。
Prerequisite 提醒:如果
/tmp是以nosuid掛載,SUID 位元會被核心忽略,這招會直接失效。可以先確認:findmnt -no OPTIONS /tmp
第三條乍看比較單純:用 svcbackup 的身分跑 cat,可以看到 svcbackup 讀得到、但 user 讀不到的檔案。
在這邊可以多問問自己:「svcbackup 這個帳號,能讀到什麼 user 讀不到的高價值檔案?」備份服務帳號常見的目標:
sudo -u svcbackup cat /home/svcbackup/.ssh/id_rsa # 私鑰 → 橫向到其他主機
sudo -u svcbackup cat /opt/backup/backup.sh # 腳本內硬編碼的 DB / 雲端憑證
sudo -u svcbackup cat /var/backups/*.sql # 資料庫備份 → 資料外洩
今天我們簡單的實作了三種 sudo 設定錯誤,讓我們看到 sudo 需要被嚴格管理的重要性。其實就算不靠 sudo,Linux 上還有很多提權管道。今天提到的 SUID 明天會完整展開,還會加上 SGID 的提權方式。那我們期待下一天的主題-SUID / SGID:一個執行權限如何把普通使用者送上 Root。