## Python Checklist ### Code & Command Execution - eval - execfile ```python # python2.X only # user_request_data_file.data 文件内容为__import__('os').system('id') execfile("./user_request_data_file.data") ``` - compile - map - input ```python # python2.x only # input 函数接收标准输入的内容,然后当作 python 代码解释执行 input("plz input data:") # input __import__('os').system('id') ``` - os.system - os.popen - os.popen2 - os.popen3 - os.popen4 - os.execl - os.execle - os.execlp - os.execlpe - os.execv - os.execve - os.execvp - os.execvpe - os.spawnl - os.spawnle - os.spawnlp - os.spawnlpe - os.spawnv - os.spawnve - os.spawnvp - os.spawnvpe - subprocess.call - subprocess.check_call - subprocess.check_output - subprocess.Popen - commands.getstatusoutput - commands.getoutput - commands.getstatus - glob.glob - linecache.getline - io.open - popen2.popen2 - popen2.popen3 - popen2.popen4 - timeit.timeit ```python import timeit timeit.timeit("__import__('os').system('id')", number=1000000) ``` - timeit.repeat - sys.call_tracing - code.interact - code.compile_command - codeop.compile_command - pty.spawn - posixfile.open - posixfile.fileopen - platform.popen - importlib.import_module - importlib.\_\_import\_\_ - Image.open ```python # 来自 https://xz.aliyun.com/t/44 from PIL import Image def get_img_size(filepath=""): '''获取图片长宽''' if filepath: img = Image.open(filepath) img.load() return img.size return (0, 0) ``` - types.FunctionType ```python (types.FunctionType(marshal.loads(base64.b64decode(code_serialized)), globals(), ''))() ``` - asyncio ```python import asyncio # 如下函数同理: # asyncio.get_event_loop().subprocess_exec # asyncio.get_event_loop().subprocess_shell asyncio.create_subprocess_shell(cmd) ``` - profile ```python import profile profile.run("__import__('os').system('id')") profile.runctx("__import__('os').system('id')") ``` - cProfile ```python import cProfile cProfile.run("__import__('os').system('id')") cProfile.runctx("__import__('os').system('id')", None, None) ``` - getattr 获取模块中的危险属性 ```python import random cls = random path = '_os.system' for name in path.split('.'): cls = getattr(cls, name) print(cls) # prints ' ``` Celery – CVE-2021-23727(https://snyk.io/blog/python-rce-vulnerability/) ```python Python 3.8.9 (default, Aug 3 2021, 19:21:54) [Clang 13.0.0 (clang-1300.0.29.3)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> exc = { ... 'exc_module':'os', ... 'exc_type':'system', ... 'exc_message':'id' ... } >>> from celery.backends.base import Backend >>> from celery import Celery >>> b = Backend(Celery()) >>> b.exception_to_python(exc) uid=501(calumh) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),701(com.apple.sharepoint.group.1),33(_appstore),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh),400(com.apple.access_remote_ae) ``` ### SSTI (jinja2, Mako, Tornado, Django) ```python # 1、flask: flask.render_template_string第一个参数、 Environment.from_string第一个参数 flask.render_template第一个参数指向的文件内容、 Environment.get_template第一个参数指向的文件内容、 jinja2.Template继承jinja2.BaseLoader类并作为loader参数传递给jinja2.Environment的模板加载器,查看实现的模板加载逻辑是否安全 # 2、Django: django.template.Template类构造函数的第一个参数、django.template.loader.get_template_from_string函数的第一个参数、django.template.loader.render_to_string函数第一个参数指向的文件内容、 django.shortcuts.render函数第二个参数指向的文件内容、 django.template.loader.get_template函数第一个参数指向的文件内容 # 3、Tonado: tornado.template.Template类第一个参数 tornado.template.Loader类第一个参数指向的路径 tornado.web.RequestHandler类第一个参数指向的文件内容 tornado.web.RequestHandler类render函数第一个参数指向的文件内容 tornado.web.UIModule类render_string函数第一个参数指向的文件内容 ``` ### File - open - shutil.copyfileobj - shutil.copyfile - shutil.copy - shutil.copy2 - shutil.move - shutil.make_archive - dircache.listdir - dircache.opendir - os.remove - os.open - os.pipe - os.listdir - os.access - os.symlink - os.walk - os.fwalk - os.rename - os.renames - os.replace - os.rmdir - os.scandir - os.removedirs - os.unlink - os.chdir - os.link - os.mkdir - os.makedirs - os.readlink - types ```python import types types.FileType("/etc/passwd").read() ``` - Flask - send_file - send_from_directory - send_static_file(?) - distutils.file_util.copy_file - distutils.file_util.move_file - distutils.file_util.write_file ### Deserialization - pickle.load - pickle.loads - cPickle.load - cPickle.loads - yaml.load - shelve(本质上还是pickle) ```python import shelve import os class eee(object): def __reduce__(self): return (os.system,('calc',)) with shelve.open('test.db') as db: db['a'] = eee() with shelve.open('test.db') as db: db['a'] ``` ### CRLF - httplib - urllib - urllib2 ### SSRF - httplib - http.client(python3将`httplib`改名为`http.client`) - urllib - urllib2 - urllib3(python3) - pycurl ### XXE - python2.x | kind | sax | etree | minidom | pulldom | xmlrpc | | ------------------------------------------------------------ | -------------- | -------------- | -------------- | -------------- | -------------- | | billion laughs | **Vulnerable** | **Vulnerable** | **Vulnerable** | **Vulnerable** | **Vulnerable** | | quadratic blowup | **Vulnerable** | **Vulnerable** | **Vulnerable** | **Vulnerable** | **Vulnerable** | | external entity expansion | **Vulnerable** | Safe (1) | Safe (2) | **Vulnerable** | Safe (3) | | [DTD](https://en.wikipedia.org/wiki/Document_type_definition) retrieval | **Vulnerable** | Safe | Safe | **Vulnerable** | Safe | | decompression bomb | Safe | Safe | Safe | Safe | **Vulnerable** | 1. [`xml.etree.ElementTree`](xml.etree.elementtree.html#module-xml.etree.ElementTree) doesn't expand external entities and raises a ParserError when an entity occurs. 2. [`xml.dom.minidom`](xml.dom.minidom.html#module-xml.dom.minidom) doesn't expand external entities and simply returns the unexpanded entity verbatim. 3. [`xmlrpclib`](xmlrpclib.html#module-xmlrpclib) doesn't expand external entities and omits them. - python3.x | kind | sax | etree | minidom | pulldom | xmlrpc | | ------------------------------------------------------------ | -------------- | -------------- | -------------- | -------------- | -------------- | | billion laughs | **Vulnerable** | **Vulnerable** | **Vulnerable** | **Vulnerable** | **Vulnerable** | | quadratic blowup | **Vulnerable** | **Vulnerable** | **Vulnerable** | **Vulnerable** | **Vulnerable** | | external entity expansion | Safe (4) | Safe (1) | Safe (2) | Safe (4) | Safe (3) | | [DTD](https://en.wikipedia.org/wiki/Document_type_definition) retrieval | Safe (4) | Safe | Safe | Safe (4) | Safe | | decompression bomb | Safe | Safe | Safe | Safe | **Vulnerable** | 1. [`xml.etree.ElementTree`](mk:@MSITStore:D:\software\Python\Python38\Doc\python383.chm::/library/xml.etree.elementtree.html#module-xml.etree.ElementTree) doesn't expand external entities and raises a `ParserError` when an entity occurs. 2. [`xml.dom.minidom`](mk:@MSITStore:D:\software\Python\Python38\Doc\python383.chm::/library/xml.dom.minidom.html#module-xml.dom.minidom) doesn't expand external entities and simply returns the unexpanded entity verbatim. 3. `xmlrpclib` doesn't expand external entities and omits them. 4. Since Python 3.7.1, external general entities are no longer processed by default. ### XSS - autoescape ```python {% autoescape off %} {{ title }} {% end autoescape %} # 模板引擎选项设置禁用自动转义导致模板输出数据不会被转义处理,有风险 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', ..., 'OPTIONS': {'autoescape': False} } ] # render函数通过设置上下文变量autoescape=False禁用引擎自动转义 # 本次输出用户不可信数据不会被自动转义 render(request, "index.html", {"my_name": "", "autoescape": False}) # 同上设置Context的autoescape属性为False template = Template("My name is {{ my_name }}.") context = Context({"my_name": ""}, autoescape=False) template.render(context) # render_to_string 渲染函数上下文设置autoescape=False render_to_string("index.html", {"my_name": "", "autoescape": False}) # 这种属性输出没有被单或者双引号包围,可以导致额外的属性注入 # 进而导致XSS, id='123 onerror=javascript:alert(123) ' # 直接输出到script标签内的语句django无能为力,没法防XSS # name='alert(123)' ``` - safe ```python # safe filter 处理过的输出参数不会被转义可导致问题 {{ title | safe | escape }} ``` - safeseq ```python # safeseq filter 处理过的输出参数不会被转义可导致问题 {{ title_list | safeseq | join:", " }} ``` - mark_safe ```python # mark_safe 标记过的输出内容不会被转义 mark_safe("%s" % title) ``` - SafeString ```python # SafeString 标记过的输出不会被转义 SafeString("%s" % title) ``` - is_safe ```python # 注册带 is_safe=True 属性的过滤器,输出结果不会被转义 @register.filter(is_safe=True) def myfilter(value): return value {{ title | myfilter }} ``` - html_safe ```python # html_safe 装饰器定义封装__str__的__html__类方法, # 导致__str__输出的内容不会被转义 @html_safe class RawHtml(str): def __str__(self): return data ``` ### Redirect - redirect ```python return redirect(url) ``` - make_response ```python return make_response(("", 302, {"Location":url})) ``` - django.views.static.serve(https://github.com/neargle/django-cve-hub/tree/master/CVE-2017-7234) ```python urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^staticp/(?P.*)$', serve, {'document_root': os.path.join(settings.BASE_DIR, 'staticpath')}) ] # payload: /staticp/%5C%5Cblog.neargle.com ``` - [CVE-2018-14574](https://github.com/vulhub/vulhub/tree/master/django/CVE-2018-14574) ```python http://your-ip:8000//www.example.com ``` ### SQL Injection - django - User.objects.raw - User.objects.extra - User.objects.RawSQL - User.objects.order_by(CVE-2021-35042) ```python # Django QuerySet.order_by SQL Injection Vulnerability (CVE-2021-35042) # https://github.com/vulhub/vulhub/tree/master/django/CVE-2021-35042 http://your-ip:8000/vuln/?order=vuln_collection.name);select%20updatexml(1,%20concat(0x7e,(select%20@@basedir)),1)%23 ``` - [CVE-2019-14234](https://github.com/vulhub/vulhub/tree/master/django/) ```python # json键名注入 # Django JSONField/HStoreField SQL Injection Vulnerability (CVE-2019-14234) http://your-ip:8000/admin/vuln/collection/?detail__a%27b=123 ```