任意文件读取在web中是一个很常见的漏洞,常规形式比如
http://www.baidu.com/file?filename=1.jpg
没有限制目录和过滤请求的时候就会产生利用目录跨越的形式造成任意文件读取
就是传参的形式不同,但还有一种不同于前的文件读取。比如Spring的CVE-2018-1271。
/spring-mvc-showcase/resources/%255c%255c..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/windows/win.ini
django的老版本也出现过类似问题,CVE-2009-2659
先在flask上简单模拟一个文件读取,其实有点类似前面的SSRF的形式,传入参数为file协议的形式就可以读取系统上任意文件。
@app.route('/read')
def readfile():
if request.values.get('file'):
file = request.values.get('file')
req = urllib.request.urlopen(file)
return Response(req.read().decode('utf-8'))
else:
return Response('<p>请输入file地址</p>')
使用其他的文件读取模块来读取
def READFILE(request):
if request.GET.get('file'):
file = request.GET.get('file')
file = open(file)
return HttpResponse(file)
else:
return HttpResponse('<p>请输入file地址</p>')
如果使用不当,自定义静态资源文件目录,或者上传文件目录,但是没有严格控制参数和使用方式,也会出现类似如上的问题,定义一个读取上传文件的方法
@app.route('/uploadfile/<path:file>')
def readfile(file):
with open('./uploadfile/%s' %file, 'r') as f:
content = f.read()
return Response(content)
当执行如下操作的时候,就会读取系统文件。
curl -i http://127.0.0.1:5000/uploadfile/..%2f..%2f..%2f..%2f..%2f..%2f..%2fwindows/win.ini
说到文件读取还是因为直接读取了传入的参数值,或者不正确使用某些方法导致的。
可以采用过滤的形式去除../,或者限制读取在某一个固定目录
@app.route('/uploadfile/<path:file>')
def readfile(file):
dir = os.path.abspath(os.path.join('/uploadfile', file)) #abspath跟getcwd有关,需要确保工作目录
if os.path.dirname(dir) == os.path.join(os.getcwd(), 'uploadfile'):
with open(dir, 'r') as f:
content = f.read()
return Response(content)
else:
return Response('文件读取失败')
#return send_from_directory(os.path.join(os.path.dirname(__file__), 'uploadfile'), file)
flask中有一个文件读取下载的方法send_from_directory
,其中有一个safe_join
来判断参数中是否存在..
这种类型的地址。
如果是传参的形式导致目录遍历的文件读取,可以参考以下的方式
def READFILE(request):
file = request.GET.get('path')
path = os.path.join('/var/www/images/', file) #images为限制的读取目录
if os.path.abspath(path).startswith('/var/www/images/') is False:
raise Http404
else:
with open(path, "rb") as f:
content = f.read()
return HttpResponse(content)
还可以使用os.path.realpath
或者真实路径再去判断。flask修改静态文件的配置
app = Flask( __name__,static_folder=,template_folder=)
如果在django下,不是很介意目录的存在形式的话,可以利用静态目录设置多级目录来区分资源文件。
STATIC_URL = '/file/' #静态资源路由
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"), #文件夹
]
然后创建file文件夹,里面设置静态和其他文件目录,可以通过链接直接访问,只要上传文件没错误就行了。
http://127.0.0.1:8000/file/upload/2.txt
django当然也有自己的资源文件的设置
MEDIA_ROOT = os.path.join(BASE_DIR,'media') #以后会自动将文件上传到指定的文件夹中
MEDIA_URL = '/media/' #以后可以使用这个路由来访问上传的媒体文件
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('', views.IndexView.as_view()),
]+static(settings.MEDIA_URL,document_roo = settings.MEDIA_ROOT)
关于非常规的文件读取漏洞,可以查看https://www.leavesongs.com/PENETRATION/arbitrary-files-read-via-static-requests.html