freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

代码审计:python代码审计汇总(持续更新中)
2024-07-01 16:06:52

后续有时间会持续更新补充,当前整理为一些重点漏洞的,如下:

命令注入、

代码注入、

不安全的反序列化、

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 commands

commands.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 timeit

timeit.timeit("__import__('os').system('dir')",number=1)

timeit.repeat():指定重复次数的执行timeit方法,返回一个结果列表。

不安全的反序列化

Python中用于反序列化的模块有:

marshal

PyYAML——>yaml.safe_load()

yaml.load

import yaml

yamlString = getYamlFromUser()
yaml.load(yamlString)

pickle/cpickle

shelve

PIL

Unzip


1、pickle模块

pickle 是Python中的一个库,用于序列化和反序列化Python对象。然而,pickle 可能存在安全风险,因为它可以执行任意代码。下面是一个示例:

import pickle

class 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, FileSystemLoader

app = 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 指定导出的文件名



# web安全 # 代码审计 # python安全 # 代码分析 # 代码安全
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录