一、本文目的
解释sudo提权漏洞
简要说明基础概念在安全领域的重要性
二、漏洞特点。
本漏洞是sudo在处理user id为-1和4294967295(
2
32
−
1
2^{32}-1
232−1)即可,为什么呢?
下面做出解释。
三、漏洞分析
根据Linux Interface Programming的解释,首先我们要知道uid的类型:
struct passwd {
char *pw_name; /* Login name (username) */
char *pw_passwd; /* Encrypted password */
uid_t pw_uid; /* User ID */
gid_t pw_gid; /* Group ID */
char *pw_gecos; /* Comment (user information) */
char *pw_dir; /* Initial working (home) directory */
char *pw_shell; /* Login shell */
};
uid_t呢?uid_t的类型是
typedef unsigned int kernel_uid32_t;
那么,我们就算算,正好 2 32 − 1 2^{32}-1 232−1=4294967295,那现在回答一下-1。
让我们回顾一下,现在的机器的机器码对于负数都是用补码的形式表示的,因此-1的补码正好是全一。
但是,从int到unsigned int,在这种转换下-1就变成了4294967295。
四、程序验证
从CS APP(深入理解操作系统)第三版,英文版111页、中文版52页。大家可以用32位的编译方式验证一下。
我这里用Kali验证了一下:
#include <stdio.h>
int main(void)
{
int x=-1;
printf("x=%d, %u\n", x, x);
return 0;
}
结果是
五、漏洞总结
这个问题是sudo在进行uid判断的时候,没有考虑这种边界情况,擅自改成了0。从而直接使用了root用户的uid,从而提权。
六、额外分析
有群友问了-0,-0的补码计算如下:
先算原码
10000000000000000000000000000000
取反码,保留符号位
1111111111111111111111111111111111111
加一
00000000000000000000000000000000
即补码情况下,-0=0。
七、篇后语
《老子》:合抱之木,生于毫末;九层之台,起于垒土;千里之行,始于足下。