0%

Django重置密码表单账户劫持(CVE-2019-19844)漏洞分析

一、漏洞介绍

Django的密码重设表单使用不区分大小写的查询来检索与请求密码重设的电子邮件地址匹配的帐户。因为这通常涉及显式或隐式大小写转换,所以知道与用户帐户关联的电子邮件地址的攻击者可以制作与该帐户关联但地址不同的电子邮件地址。攻击者可以输入精心构造的带Unicode字符的邮箱并让Django解析成受害者的关联邮箱地址,在这种情况下,攻击者可以收到受害者帐户的有效密码重置令牌进行恶意的用户密码重置。

二、功能分析

Django框架有一个内置的email重置功能,用户编写代码时只需要调用django的内置api就可以进行重置,获取django根据特殊算法生成的重置邮箱密码的message(json):

img

之后用户可以直接根据生成的message中的email(To)调用第三方api进行邮件发送。

三、漏洞复现

根据CVe-2019-19844作者公布的poc显示,攻击者可以通过精心构造的带特殊字符邮箱输入来与数据库中存储的邮箱地址所在的用户进行强行关联,这边我们根据poc进行一下复现:

1、创建用户

img

2、重置密码

img

可以看到django把带unicode编码的email地址”xdw123@ıı.com“和“xdw123@ii.com”邮箱所在用户进行了强关联,并生成了XDw用户的专属重置链接,而且生成的message中的To字段被解析成了另一个email地址,一旦应用层的开发者代码是直接取出此字段发送邮件,则攻击者在知道用户绑定邮箱的情况下很有构造特殊输入给攻击者指定的邮箱发送重置链接,造成一个任意用户密码重置的危害。

四、漏洞分析

漏洞的关键点主要在于一旦输入能与数据库中的用户进行关联,则把重置链接发送给用户输入的邮箱地址,而不是数据库中存储的用户地址。

获取输入框邮箱地址:

img

从数据库中找出邮箱匹配的user:

img

发送message:

img

这边不得不吐槽一下django的功能,为什么要允许输入的邮箱地址是unicode,api内解析一下如果是unicode则drop掉可能就没有这个问题了。不过主要漏洞点也还是在于不使用数据库中获取的发送地址进行发送,就算”xdw123@ıı.com”匹配出了”xdw123@ii.com“,但如果还是向”xdw123@ii.com“发送邮件则没有什么问题。

还有一个暂时未搞明白的地方是,为什么”xdw123@ıı.com”会被解析成”xdw123@xn–cfaa.com“。用户传入的带土耳其字符ı的邮箱地址(unicode)应该是会被解析成utf-8格式后再传回给用户的,这边也可以在源码中看到:

img

img

这边手动hook过发现编码确实是utf8,但如何做具体转换的这边暂时还不清楚,一旦知道unicode解析原理,这边就能构造exploit

五、补丁修复

3.0.1对此漏洞进行了修复,主要包含两个patch:

1、输入email匹配user的时候加了一个unicode文本标准化之后的比较,主要用来过滤带不完全相等的邮箱输入

img

2、message中的邮箱换成了数据库中的邮箱

img

漏洞版本检测:

img

开发者建议:

调用框架接口也需要做好过滤,这边可以在邮箱输入框以及使用message ’To‘字段时进行比较和过滤。