Linux - 使用setuid位进行提权
Posted 王万林 Ben
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux - 使用setuid位进行提权相关的知识,希望对你有一定的参考价值。
Linux - 使用setuid位进行提权
业务需求
设计一个计分程序,程序文件的owner=thesre;计分文件的owner=thesre,mode=644。
要求:
- 该计分程序可以被其它非root账号执行,并对计分文件进行更新;
分析需求
由于计分文件的权限为644,只能被其owner更新。而上述要求中,又提到说其它非root账号通过执行该计分程序对计分文件进行更新。
这里,我们就要利用setuid的优势来完成该程序的开发了。
setuid能做什么
简单地说setuid,就是运行程序时,能够临时将权限提升至程序owner的权限,然后做一些只有程序owner才能有权限的操作。
像普通用户执行passwd修改密码、执行at命令设置一次性定时任务,都是利用这个原理来实现提权的操作。
下面我们就来看看本需求的代码实现。
代码
/* filename: caber-toss.c */
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
/* Remember the effective and real UIDs. */
static uid_t euid, ruid;
char* SCORES_FILE = "/tmp/scores.txt";
char* cuserid(char *string);
/* Restore the effective UID to its original value. */
void
do_setuid (void)
{
int status;
#ifdef _POSIX_SAVED_IDS
status = seteuid (euid);
#else
status = setreuid (ruid, euid);
#endif
if (status < 0) {
fprintf (stderr, "Couldn't set uid.\\n");
exit (status);
}
}
/* Set the effective UID to the real UID. */
void
undo_setuid (void)
{
int status;
#ifdef _POSIX_SAVED_IDS
status = seteuid (ruid);
#else
status = setreuid (euid, ruid);
#endif
if (status < 0) {
fprintf (stderr, "Couldn't set uid.\\n");
exit (status);
}
}
/* Record the score. */
int
record_score (int score)
{
FILE *stream;
char *myname;
/* Open the scores file. */
do_setuid ();
stream = fopen (SCORES_FILE, "a");
undo_setuid ();
/* Write the score to the file. */
if (stream) {
myname = cuserid (NULL);
if (score < 0)
fprintf (stream, "%10s: Couldn't lift the caber.\\n", myname);
else
fprintf (stream, "%10s: %d feet.\\n", myname, score);
fclose (stream);
return 0;
}
else
return -1;
}
/* Main program. */
int
main (int argc, char *argv[])
{
int feet_in_int;
/* Parse arguments */
if ( argc == 2 ) {
feet_in_int = atoi(argv[1]);
printf ("You're adding %d feet.\\n", feet_in_int);
}
else {
printf ("# of args is wrong.\\n");
exit(-1);
}
/* Remember the real and effective user IDs. */
ruid = getuid ();
euid = geteuid ();
printf ("Current ruid is %d, euid is %d\\n", ruid, euid);
undo_setuid ();
/* Do the game and record the score with checking exception. */
if (record_score (feet_in_int) == -1) {
printf("Error: failed to write file %s.\\n", SCORES_FILE);
}
}
编译
thesre@HP-Z420-Workstation:~/c_programming$ gcc -o ./caber-toss caber-toss.c
thesre@HP-Z420-Workstation:~/c_programming$ ls -al ./caber-toss
-rwxrwxr-x 1 thesre thesre 17408 6月 28 22:34 ./caber-toss
测试
程序文件未加setuid
thesre@HP-Z420-Workstation:~/c_programming$ ls -al ./caber-toss
-rwxrwxr-x 1 thesre thesre 17408 6月 28 22:34 ./caber-toss
thesre账号运行,
thesre@HP-Z420-Workstation:~/c_programming$ ./caber-toss 2
You're adding 2 feet.
Current ruid is 1000, euid is 1000
thesre@HP-Z420-Workstation:~/c_programming$ cat /tmp/scores.txt
thesre: 2 feet.
thesre@HP-Z420-Workstation:~/c_programming$ ls -al /tmp/scores.txt
-rw-rw-r-- 1 thesre thesre 20 6月 28 22:35 /tmp/scores.txt
thesre02(非计分文件owner)运行,可以是看到更新失败的!
thesre02@HP-Z420-Workstation:~$ id
用户id=1002(thesre02) 组id=1002(thesre02) 组=1002(thesre02)
thesre02@HP-Z420-Workstation:~$ ~thesre/c_programming/caber-toss 5
You're adding 5 feet.
Current ruid is 1002, euid is 1002
Error: failed to write file /tmp/scores.txt.
thesre02@HP-Z420-Workstation:~$
程序文件加setuid
thesre@HP-Z420-Workstation:~/c_programming$ chmod u+s ./caber-toss
thesre@HP-Z420-Workstation:~/c_programming$ ls -al ./caber-toss
-rwsrwxr-x 1 thesre thesre 17408 6月 28 22:34 ./caber-toss
thesre账号运行,
thesre@HP-Z420-Workstation:~/c_programming$ ./caber-toss 10
You're adding 10 feet.
Current ruid is 1000, euid is 1000
thesre@HP-Z420-Workstation:~/c_programming$ cat /tmp/scores.txt
thesre: 2 feet.
thesre: 10 feet.
thesre@HP-Z420-Workstation:~/c_programming$
thesre02(非计分文件owner)运行,这次我们成功地将/tmp/scores.txt文件更新!!!
thesre@HP-Z420-Workstation:~/c_programming$ su - thesre02
Password:
thesre02@HP-Z420-Workstation:~$ ~thesre/c_programming/caber-toss 20
You're adding 20 feet.
Current ruid is 1002, euid is 1000
thesre02@HP-Z420-Workstation:~$ cat /tmp/scores.txt
thesre: 2 feet.
thesre: 10 feet.
thesre02: 20 feet.
thesre02@HP-Z420-Workstation:~$
参考资料
https://www.gnu.org/software/libc/manual/html_node/Setuid-Program-Example.html
https://en.wikipedia.org/wiki/Setuid
https://man7.org/linux/man-pages/man2/setuid.2.html
以上是关于Linux - 使用setuid位进行提权的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Perl 的 Archive::Tar 保存 tar 档案中的 setuid 位?
漏洞预警公告Linux pkexec 本地提权 (CVE-2021-4034)
linux sudo命令失败 提示sudo:/usr/bin/sudo 必须属于用户 ID 0(的用户)并且设置 setuid 位