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

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

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

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

工具目录

分类

查看所有分类
Loading categories
CVE-2019-16784-POC — 针对 PyInstaller CVE-2019-16783 的概念验证漏洞利用 | Kitploit
工具/GitHubGitHub/ckrielle/cve-2019-16784-poc
权限提升漏洞分析漏洞利用学习与教育Payload 开发二进制利用实验室与实践
GitHubckrielle/cve-2019-16784-poc

CVE-2019-16784-POC

针对 PyInstaller CVE-2019-16783 的概念验证漏洞利用

查看仓库
22年前尚未审核

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2019-16784 POC

这就是我为 Windows PyInstaller 版本 < 3.6 中存在于 --onefile 选项的漏洞所写的 POC。攻击者可以通过劫持 PyInstaller 二进制文件所使用的 Python 解释器 DLL 所导入的 DLL,实现命令执行和可能的本地权限提升(LPE)。下面将简要解释该漏洞及利用过程。感谢 Alter Solutions 发现该漏洞,并在 PagedOut #3(PDF 第 55 页)上撰写了相关发现。

漏洞概述

该漏洞是由于 PyInstaller 在运行时为已执行二进制文件创建的目录存在弱安全性所致。PyInstaller 在用户的临时目录中构建一个名为 _MEI*PIDX* 的目录,并将不同的内容放入其中,例如用于运行打包进 PE 可执行文件中的 Python 代码的 Python 解释器 DLL。

此过程的问题在于:为 NT AUTHORITY\SYSTEM 构建的目录是 C:\Windows\Temp,这使得攻击者既可以猜到该目录,也可以向其中写入内容。因此,例如在 Python 解释器执行时,就可以实现 DLL 劫持。这里 是修复该漏洞的提交。开发人员没有仅仅依赖某些标准 API 函数来创建目录,而是实现了自己的函数,以更好地控制目录的创建过程。

利用过程

由于这是我的第一个 POC,我将讨论在此过程中遇到的一些问题。

搭建测试环境

首先,为这个 POC 搭建环境并不特别困难,因为只需要正确的包版本即可。然而,当我安装该版本时,它会崩溃:

root@kitploit:~
Traceback (most recent call last):
  File "c:\users\ckrielle\appdata\local\programs\python\python38\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
...
  File "c:\users\ckrielle\appdata\local\programs\python\python38\lib\site-packages\PyInstaller\building\utils.py", line 653, in <genexpr>
    strip_paths_in_code(const_co, new_filename)
  File "c:\users\ckrielle\appdata\local\programs\python\python38\lib\site-packages\PyInstaller\building\utils.py", line 660, in strip_paths_in_code
    return code_func(co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize,
TypeError: an integer is required (got type bytes)

经过一番搜索,我发现是我的 Python 版本(我使用的是 3.8.10)导致了这个问题。我的 Python 版本使得安装其他任何 3.8.x 版本变得困难,因为安装程序会检测到我现有的 Python38 版本并返回错误。我也尝试从源码构建另一个 3.8 版本,但由于它需要 Visual Studio 2015,而我只有 2022,因此未能成功。最后,我选择下载 Python 3.7.5,该版本运行良好。于是我为 3.7 版本创建了一个虚拟环境。

我意识到,搭建环境可能从已准备就绪到需要花费大量时间来正确配置。尽管这很重要,但它可能会削弱我们实际利用目标时的乐趣。

编写 Exploit

我们的利用过程分为两步:找到 PyInstaller 打包进程的目录,以及将我们的恶意 DLL 写入该目录。对于第一步,我们可以通过标准 WINAPI 函数 (CreateToolhelp32Snapshot、Process32First、Process32Next) 找到 PID。这一步很顺利。之后,我们需要找到 _MEI 目录的最后一位数字。原始利用建议直接使用 GetFileAttributesA 函数,并检查返回的状态码是否为 FILE_ATTRIBUTE_DIRECTORY。但这对我不起作用。我的解决方案是在每个候选目录中创建一个文件,如果文件成功创建,则说明该目录存在。出于某种原因,GetFileAttributesA 返回的文件状态是 FILE_ATTRIBUTE_ARCHIVE,因此我检查该状态。在继续之前,搜索 PID 时的循环是因为我们希望在目标进程执行时注入我们的 DLL,以便在加载发生之前准备好它们。

对于 DLL,我们需要劫持 Python 解释器 (python37.dll) 所导入的某个 DLL。要获取解释器导入的 DLL,我们可以用 PE-Bear 打开该 DLL。其中导入的一个系统 DLL 是 version.dll。因此,我们可以创建自己的 DLL,并利用 Windows 加载器的搜索顺序,将其放置在 PyInstaller 打包进程的临时目录中。这样,当它想要导入该 DLL 时,它将导入我们的恶意 DLL,而不是正确的那个。

python37.dll 导入的 DLL

然而,仅仅这样做是行不通的。原因是 Python 解释器从 version.dll 中调用的函数(具体见图片中的 VerQueryValueW)的导入无法解析。因此程序会崩溃。为了解决这个问题,我们需要进行 DLL 代理。简而言之,我们将恶意 DLL 配置为导出原始 DLL 的函数,并将原始 DLL 重命名后放在同一目录下,以便它可以加载并从中调用函数。因此,对于我们的利用,我们将编译一个带有 DllMain 的恶意 DLL,从而实现代码执行。我们将导出 version.dll 的所有函数,并将原始系统 version2.dll 复制到同一目录。这样,version.dll 就可以将函数调用转发给 version2.dll。一旦我们有了这些 DLL,只需将它们复制到进程的目录中,然后等待获得代码执行即可。关于 DLL 代理/劫持 的更详细解释,请阅读链接的文章。

恶意 version.dll 导出的函数

尽管事后看来这些步骤都很简单直接,但在开发 POC 的过程中并非如此。不久前,我花了一些时间来理解利用过程,而一旦开始编码,我理解得就更多了。至于 DLL,我尝试按照 Alter Solutions 的 POC 仓库中暗示的方式自行编译。他们有一个用于代码执行的单独 DLL,以及另一个用于代理的 DLL,后者在运行时加载了 payload.dll(DllMain 在加载时被调用)。然而,我无法正确编译它。最后,在阅读了上述文章后,我决定使用 DLLProxyProject 仓库,它编译了我想要的 DLL,并且通常可用于生成用于代理目的的 DLL。我尝试复制它的 DLLMain.cpp 文件,并使用 exports.h 头文件进行编译。虽然它确实运行了,但产生了一个错误:

root@kitploit:~
Fatal Python error: init_sys_streams: can't initialize sys standard streams
OSError: [WinError 6] The handle is invalid

Current thread 0x00002ea8 (most recent call first):

这个错误可能可以通过 Utils.cpp 文件来避免,因此我决定保留该项目用于我的 DLL 编译(尽管如果我能完全自己完成会更好)。

利用目标

搭建好环境后(使用 psexec 获取 NT AUTHORITY\SYSTEM 的 shell),我运行了 exploit,然后运行目标二进制文件,最终以管理员身份获得了代码执行。

POC 成功

结语

这是一次非常棒的经历,我很高兴经历了它。我真的很想为其他 CVE 编写更多的 POC,而这是一个完美的第一个目标。阅读漏洞描述很有趣,因为我理解到,自己实现 POC 的主要难点在于填补作者未解释(无论是有意还是无意)的空白,以及理解你正在阅读的内容。这是一个简单的漏洞,所以我没有花费太多精力去理解它。在不久之后,等我再完成几个 POC,我将尝试为一个内存损坏漏洞编写 POC。

下载工具