后续有时间会持续更新补充,当前整理为一些重点漏洞的,如下:
命令注入、
代码注入、
不安全的反序列化、
sql注入、
任意文件下载、
文件上传(文件操作)、
XXE、
SSRF、
XSS、
直接重定向、
日志伪造、
模板注入SSTI、
不安全的随机数、
格式化的字符串
…
…
OS命令注入
主要是程序中通过Python的OS接口执行系统命令,常见的危险函数有等一些接口。
1、os.system( )
def myserve(request,fullname):
os.system("sudo rm -f %s"%fullname)
fullname是用户可控的,恶意用户只需利用shell的拼接符;就可以完成一次很好的攻击。
2、popen( )
os.popen( )
subprocess.popen( )
popen()函数是Python中用于执行外部命令的函数之一。它可以将一个shell命令作为参数传递给操作系统,并在Python中执行该命令的结果。
popen()函数是在subprocess模块中定义的。使用之前,我们需要先导入这个模块:
import subprocess
要执行一个命令,我们可以使用subprocess.Popen()函数。它的基本语法如下:
subprocess.Popen(cmd, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)
3、platform.popen( )
import platform
print platform.popen('dir').read()
4、commands模块的一些方法
import commandscommands.getoutput('ifconfig')
commands.getstatusoutput('ifconfig')
subprocess模块的一些方法
subprocess.run()
subprocess.Popen()
subprocess.call()
subprocess.check_call()
subprocess.getstatusoutput()
subprocess.getoutput()
subprocess.check_output()
subprocess中的shell参数,如果shell=True的话,curl命令是被Bash(Sh)启动,所以支持shell语法。 如果shell=False的话,启动的是可执行程序本身,后面的参数不再支持shell语法。
防御办法需保证
(1)shell=True未设置
(2)转义变量的函数:Python 2.x使用pipes.quote(),Python 3.3或更高版本使用shlex.quote();
PS:其中Java中的Runtime.getRuntime().exec()效果类似shell=False,而PHP中的shell_exec就类似于shell=True,PHP中的exec则类似于shell=False。
spawn( ):
pty.spawn('whoami')
importlib.import_module()
importlib.import_module('os').system('whoami')
importlib.__import__('os').system('whoami')
代码注入
任意代码执行需关注的函数:
**eval():**将字符串str当成有效的表达式来求值并返回计算结果。
**exec():**执行 shell 命令、代码。与 eval() 函数类似,都是用于执行字符串形式的代码。然而,exec() 函数更加强大,可以执行多条语句甚至整个代码块。
eval('__import__("os").system("dir")')
exec('__import__("os").system("dir")')
案例参考:
(1)eval() 函数 :python的序列话函数eval,将字符串str当成有效的表达式来求值并返回计算结果。
代码示例:
>>>x = 7
>>> eval( '3 * x' )
21
例1:request引入的login参数
def eval_test(request,login):
login = eval(login)
如果恶意用户从外界传入 import(“os”).system(“rm /tmp -fr”) 就可以清空tmp目录。
例2:input引入的参数
user input = input("请输入一个表达式:")
result = eval (user input)
print("结果::", result)
**为了避免此类问题,,**可以使用其他替代方法,如
ast.literal_eval () 或 int() 、 float() 、eval ()函数的安全子集
(2)exec函数
exec() 函数与 eval() 函数类似,都是用于执行字符串形式的代码。然而,exec() 函数更加强大,可以执行多条语句甚至整个代码块。因此,不正确使用 exec() 函数可能会导致安全漏洞。下面是一个示例:
user_input = input("请输入一段代码:")
exec(user_input)
上述代码中,恶意用户可以输入恶意代码来执行潜在的危险操作。为了提高安全性,可以限制可执行的代码范围,或使用其他更安全的替代方法。
**execfile():**execfile() 函数可以用来执行一个文件。返回表达式执行结果。
execfile(filename[, globals[, locals]])filename -- 文件名。
globals -- 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
locals -- 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
**compile():**是一个内置函数,用于将字符串形式的 Python 代码编译为可执行的代码对象,或者将 AST(抽象语法树)对象编译为代码对象。
**timeit():**timeit.timeit( ) 创建一个Timer实例,并运行代码进行计时,默认将代码执行一百万次。timeit模块是Python内置的用于统计小段代码执行时间的模块,它同时提供命令行调用接口。
import timeittimeit.timeit("__import__('os').system('dir')",number=1)
**timeit.repeat():**指定重复次数的执行timeit方法,返回一个结果列表。
不安全的反序列化
Python中用于反序列化的模块有:
marshal
PyYAML——>yaml.safe_load()
yaml.load
import yamlyamlString = getYamlFromUser()
yaml.load(yamlString)
pickle/cpickle
shelve
PIL
Unzip
1、pickle模块
pickle 是Python中的一个库,用于序列化和反序列化Python对象。然而,pickle 可能存在安全风险,因为它可以执行任意代码。下面是一个示例:
import pickleclass User:def __init__(self, name):
user = User("Alice")
pickle_data = pickle.dumps(user)
loaded_user = pickle.loads(pickle_data)
print("用户名称:", loaded_user.name)
上述代码中,pickle.dumps() 和 pickle.loads() 函数可将 user 对象序列化和反序列化。然而,如果从不受信任的源加载数据,可能导致执行未经授权的操作。为了避免此类问题,可以使用其他更安全的序列化方法,如 json 或 yaml。
2、cPickle模块
cPickle.loads("cos\nsystem\n(S'uname -a'\ntR.")
Sql注入
在一般的Python web框架中都对sql注入做了防护,但是千万别认为就没有注入风险,使用不当也会导致sql注入。
例如:
%user_idres = cur.execute(sql)
修复:正确的使用(直白一点就是:使用”逗号”,而不是”百分号”)
错误的示范:
def getUsers(user_id):
sql = ‘select * from auth_user where id =%s’
%user_idres = cur.execute(sql)
修复:
execute() 函数本身有接受sql语句参数位的,
可以通过python自身的函数处理sql注入问题。
args = (id, type)cur.execute(
'select id,name from user_table where id = %s and name = %s',args )rs=c.execute(
"select * from log where
f_UserName=:usr",{"usr":"fanfan"})
rs=c.execute(
"select * from log where f_UserName=:1 ",["fanfan"])
使用如此参数带入方式,python会自动过滤args中的特殊字符,制止SQL注入的产生。
错误用法:
sql =
“select id,type,name from xl_bugs
where id = %s and type = %s” % (id, type)
cur.execute(sql)
正确用法:
cursor.execute(
'select name,password fromt b1
where name=%s and password=%s',('min',1234))
result= cursor.fetchone()
print(result)
正确的使用(直白一点就是:使用”逗号”,而不是”百分号”)
任意文件下载
程序员编写了一个下载报表或者任务的功能,如果没有控制好参数就会导致任意文件下载,
例如:fullname参数
def export_task(request,filename):
return HttpResponse(fullname)
文件操作漏洞
Python代码中文件处理需关注的函数有:
file()
file.save()
open()
codecs.open()
示例:以下代码会处理上传的文件,并将它们移到 Web 根目录下的一个目录中。攻击者可以将恶意文件上传到该程序,并随后从服务器中请求这些文件。
from django.core.files.storage import default_storage
from django.core.files.base import File
...
def handle_upload(request):
files = request.FILES
for f in files.values():
path = default_storage.save('upload/', File(f))
...
即使程序将上传的文件存储在一个无法通过 Web 访问的目录中,攻击者仍然有可能通过向服务器环境引入恶意内容来发动其他攻击。
如果程序容易出现
path manipulationcommand injection 或 remote include 漏洞,
那么攻击者就可能上传带恶意内容的文件,并利用另一种漏洞促使程序读取或执行该文件。
XXE
关注Python代码是否导入使用xml处理解析类:
xml.dom.*
xml.etree.ElementTree
xml.sax.*
错误示例代码如:
from xml import etree
tree1 = etree.parse('test.xml')
print etree.tostring(tree1.getroot())
SSRF
关注代码是否存在发起请求的库及函数,常见的有自带库requests.get()及urllib/urllib2库,
用法为
url = request.GET['url']
handle = urllib.urlopen(url)
//urllib.request.urlopen(url)。
XSS
下列代码若输入可控则可造成XSS:
return HttpResponse('hello %s' %(name))
安全的写法为
return render_to_response('hello.html', {'name':name})
直接重定向
以下 Python 代码会在用户单击链接时,指示用户浏览器打开从 dest 请求参数中解析的 URL。
...
strDest = request.field("dest")
redirect(strDest)
...
日志伪造
关注logging()函数及LOGGER 等关键字,查看是否输出口令、密钥和其他敏感信息及输入是否可插入%0A%0D进行日志伪造。
模板注入(SSTI)
所谓模板注入,又称服务器端模板注入(SSTI),通常发生在使用Jinja2、Django等模板引擎在Web应用程序中渲染模板时。
攻击者通过在模板中注入恶意代码,使得渲染后的页面能够执行这些代码。如果服务器端模板引擎没有对输入进行适当的验证和过滤,就可能导致SSTI攻击。
flask的渲染方法有render_template和render_template_string两种,render_template()是用来渲染一个指定的文件的,render_template_string则是用来渲染一个字符串的,不正确的使用flask中的render_template_string方法会引发SSTI。
以下是一个简单的Python SSTI攻击示例,假设你有一个Web应用程序,它使用Flask框架并且存在SSTI攻击:
from flask import Flask, render_template_string
app = Flask(__name__)
@app.route('/')
def index():
name = request.args.get('name', 'Guest')
return render_template_string('Hello, {{ name }}!', name=name)
if __name__ == '__main__':
app.run(debug=True)
攻击者可以通过注入SSTI表达式来改变模板的渲染,例如:
http://your-app.com/?name={{ 7*7 }}
如果存在SSTI攻击,攻击者可以执行恶意的命令,例如:
http://your-app.com/?name={{ config.class.init.globals[‘os’].popen(‘whoami’).read() }}
这将执行os.popen(‘whoami’).read()命令,显示当前操作系统的用户名。
Flask中的SSTI防御策略
(1)使用ORM(Object-Relational Mapping)框架
ORM框架如SQLAlchemy可以为数据库操作提供一层抽象,减少直接编写SQL语句的需求。这降低了SQL注入的风险,间接地防范了SSTI攻击。
(2)验证和过滤输入
在处理用户输入时,应始终进行验证和过滤。对于模板中的变量,应确保其不包含任何恶意代码。在Flask中,可以使用Jinja2模板引擎提供的Markup类来自动转义变量,防止XSS攻击。例如:
from flask import Flask, render_template_string
from jinja2 import Markup, Environment, FileSystemLoaderapp = Flask(__name__)
env = Environment(loader=FileSystemLoader('templates'))
@app.route('/')
def index():
user_input = '<img src=x onerror=alert(1)>' # 恶意代码
template = env.get_template('index.html')
output = template.render(user_input=Markup(user_input)) # 自动转义变量
return output
(3)使用Web框架提供的工具
许多Web框架提供了工具来帮助开发者防范SSTI攻击。在Flask中,可以使用render_template函数来渲染模板,该函数会自动处理输入并进行转义。因此,建议尽可能使用render_template而不是直接使用render_template_string。
(4)保持框架和库的更新
及时更新Flask及其依赖库可以确保你使用的是最新版本,新版本通常会修复已知的安全漏洞。此外,关注安全社区和官方发布的信息,以便及时了解最新的安全威胁和防御策略。
除了上述防御策略外,还应遵循其他一些最佳实践来提高安全性。例如:限制对模板文件的访问权限、避免在模板中存储敏感数据、使用HTTPOnly cookie等。
其他
不安全随机数:
当用于安全加密用途时,不可采用形如random.randint(0, 100)不安全的随机数生成机制;
在Linux和类Unix下用,需使用/dev/random生成安全随机数,在windows下,使用random模块中的SystemRandom类来实现。
格式化字符串:
使用形如下列格式化字符串容易造成敏感信息泄露:
is %s" % (‘jayway’, )或"Myname is {}".format(‘jayway’)
“My name is %(name)%” % {‘name’:‘jayway’}
python代码审计工具:
Bandit安装
Bandit 安装需要Python环境支持,安装Python3后,执行以下命令:
pip install bandit
安装bandit完成后,执行以下命令:
bandit -r F:\PythonSpace\sm\bank\ -f html -o 1.html
其中:
F:\PythonSpace\sm\bank\ 为扫描的源码目录
-f html 指定生成html报告,工具支持多种格式,如csv,custom,html,json,screen,txt,xml,yaml。
-o 1.html 指定导出的文件名