来源:同程安全应急响应中心
作者:Nearg1e@YSRC
来自 @Phithon 的一个漏洞。
问题出现在:django.views.static.serve()
函数上。该函数可以用来指定web站点的静态文件目录。如:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^staticp/(?P<path>.*)$', serve, {'document_root': os.path.join(settings.BASE_DIR, 'staticpath')})
]
这样django项目根目录下staticpath中的所有文件,就可以在staticp/目录中访问。e.g. http://127.0.0.1:8000/staticp/test.css
这种方法是不被django官方推荐在生成环境使用的,对安全性和性能都有一定影响。
问题代码如下 (django/views/static.py):
path = posixpath.normpath(unquote(path))
path = path.lstrip('/')
newpath = ''
for part in path.split('/'):
if not part:
### Strip empty path components.
continue
drive, part = os.path.splitdrive(part)
head, part = os.path.split(part)
if part in (os.curdir, os.pardir):
### Strip '.' and '..' in path.
continue
newpath = os.path.join(newpath, part).replace('\\', '/')
if newpath and path != newpath:
return HttpResponseRedirect(newpath)
path既我们传入的路径,如果传入的路径为 staticp/path.css
,则path=path.css
。跟踪代码可知,path经过了unquote进行url解码,后来又 replace('\\', '/')
,进入HttpResponseRedirect,很诡异的逻辑看起来很有问题。一般遇到这类型的函数我们会先试着找看看,任意文件读漏洞,但是这个对’.’和’…’进行了过滤,所以这边这个HttpResponseRedirect函数就成了帅的人的目标。
我们的最终目的是 HttpResponseRedirect('//evil.neargle.com')
或者 HttpResponseRedirect('http://evil.neargle.com')
,那么就要使 path != newpath
,那么path里面就必须带有’\‘,好的现在的我们传入 ’/staticp/%5C%5Cblog.neargle.com’
,则path=’\\blog.neargle.com’,newpath=’//blog.neargle.com’,HttpResponseRedirect
就会跳转到 ’blog.neargle.com’ 造成跳转漏洞。
嗯,官方表示自己也不知道为什么要写这串代码,删了这一串代码然后用safe_url函数代替。