月度归档:2019年04月

[100DaysML]第 0 天 – Django URL跳转漏洞(CVE-2017-7233)

第 0 天

今天没有学ML,但看了些Web安全,做了个入门小实验
所以,又没有学?啊/(ㄒoㄒ)
Django URL跳转漏洞(CVE-2017-7233)
1. 实验网址
https://www.ichunqiu.com/experiment/detail?id=100986&source=2
2. 攻击点:
Django自带一个函数:django.utils.http.is_safe_url(url, host=None, allowed_hosts=None, require_https=False),用于过滤需要进行跳转的url。如果url安全则返回ture,不安全则返回false。
3. 原理
如果指定了host为blog.neargle.com,则is_safe_url会判断url是否属于blog.neargle.com,如果url是blog.neargle.com或相对路径的url,则判断其url是安全的。
问题就出在该函数对域名和方法的判断,是基于urllib.parse.urlparse的,源码如下(django/utils/http.py):

def _is_safe_url(url, host):
if url.startswith('///'):
    return False
url_info = urlparse(url)
if not url_info.netloc and url_info.scheme:
    return False
if unicodedata.category(url[0])[0] == 'C':
    return False
return ((not url_info.netloc or url_info.netloc == host) and
        (not url_info.scheme or url_info.scheme in ['http', 'https']))

尝试构造urlparse不能处理的特殊情况
>>> urlparse('https:99999999')
ParseResult(scheme='', netloc='', path='https:99999999', params='', query='', fragment='')

可以发现当scheme不等于http,且path为纯数字的时候,urlparse处理例如aaaa:2222222223的情况是不能正常分割开的,会全部归为path。
这时url_info.netloc == url_info.scheme == "",则((not url_info.netloc or url_info.netloc == host) and (not url_info.scheme or url_info.scheme in [‘http’, ‘https’]))为true。
再如以下POC(Proof Of Concept, 观点验证程序):
>>> is_safe_url('ftp:23333333333')
True
>>> is_safe_url('https:2333333333')
True
4. 利用 & Trick
如何让其跳转到指定url而不是https:2333333333这样的url呢?
有什么方法呢?其实ip不仅只有常见的点分十进制表示法,纯十进制数字也可以表示一个ip地址,浏览器也同样支持。 将IP的点分十进制,逐点位(点分隔的部分称为一个位)转换为16进制然后拼接在一起转为10进制即可
例如:127.0.0.1
127 (dec) = 7F (hex)
0 (dec) = 00 (hex)
0 (dec) = 00 (hex)
1 (dec) = 01 (hex)
7F000001 (hex) = 2130706433 (dec)

再如:172.16.12.2
172 (dec) = AC (hex)
16 (dec) = 10 (hex)
12 (dec) = 0C (hex)
2 (dec) = 02 (hex)
AC100C02 (hex) = 2886732802 (dec)