Fixed mailsploit vulnerability. (#1324)

see #1323
This commit is contained in:
Antoine Nguyen
2017-12-08 13:17:53 +01:00
committed by GitHub
parent b924a20793
commit 1f9a4b0abf
3 changed files with 86 additions and 16 deletions
+4 -3
View File
@@ -20,9 +20,10 @@ from modoboa.lib import u2u_decode
class EmailAddress(object):
def __init__(self, address):
self.fulladdress = u2u_decode.u2u_decode(address).strip("\r\t\n")
(self.name, self.address) = parseaddr(self.fulladdress)
if self.name == "":
self.name, self.address = u2u_decode.decode_address(address)
if self.name:
self.fulladdress = "{} <{}>".format(self.name, self.address)
else:
self.fulladdress = self.address
def __str__(self):
+44
View File
@@ -14,6 +14,7 @@ from rest_framework.test import APITestCase
from modoboa.core import models as core_models
from . import sysutils
from . import u2u_decode
try:
s = socket.create_connection(('127.0.0.1', 25))
@@ -103,3 +104,46 @@ class ModoAPITestCase(ParametersMixin, APITestCase):
"""Setup."""
super(ModoAPITestCase, self).setUp()
self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key)
class U2UTestCase(TestCase):
"""Test RFC1342 decoding utilities."""
def test_header_decoding(self):
"""Simple decoding."""
samples = [
("=?ISO-8859-15?Q?=20Profitez de tous les services en ligne sur "
"impots.gouv.fr?=",
"Profitez de tous les services en ligne sur impots.gouv.fr"),
("=?ISO-8859-1?Q?Accus=E9?= de =?ISO-8859-1?Q?r=E9ception?= de "
"votre annonce",
"Accusé de réception de votre annonce"),
("Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord",
"Sm\xf6rg\xe5sbord")
]
for sample in samples:
self.assertEqual(u2u_decode.u2u_decode(sample[0]), sample[1])
def test_address_header_decoding(self):
"""Check address decoding."""
mailsploit_sample = (
"=?utf-8?b?cG90dXNAd2hpdGVob3VzZS5nb3Y=?==?utf-8?Q?=00?="
"=?utf-8?b?cG90dXNAd2hpdGVob3VzZS5nb3Y=?=@mailsploit.com")
expected_result = (
"=?utf-8?b?cG90dXNAd2hpdGVob3VzZS5nb3Y=?==?utf-8?Q??="
"=?utf-8?b?cG90dXNAd2hpdGVob3VzZS5nb3Y=?=@mailsploit.com")
self.assertEqual(
u2u_decode.decode_address(mailsploit_sample),
("", expected_result)
)
mailsploit_sample = (
'"=?utf-8?b?cG90dXNAd2hpdGVob3VzZS5nb3Y=?==?utf-8?Q?=0A=00?="\n'
'<=?utf-8?b?cG90dXNAd2hpdGVob3VzZS5nb3Y=?==?utf-8?Q?=0A=00?='
'@mailsploit.com>')
expected_result = (
'potus@whitehouse.gov',
'=?utf-8?b?cG90dXNAd2hpdGVob3VzZS5nb3Y=?==?utf-8?Q??='
'@mailsploit.com')
self.assertEqual(
u2u_decode.decode_address(mailsploit_sample),
expected_result)
+38 -13
View File
@@ -5,14 +5,17 @@
from __future__ import unicode_literals
from email.header import decode_header, make_header
from email.utils import parseaddr
import re
from django.utils.encoding import smart_text
# check spaces between encoded_words (and strip them)
sre = re.compile(r'\?=[ \t]+=\?')
sre = re.compile(r"\?=[ \t]+=\?")
# re pat for MIME encoded_word (without trailing spaces)
mre = re.compile(r'=\?[^?]*?\?[bq]\?[^?\t]*?\?=', re.I)
mre = re.compile(r"=\?[^?]*?\?[bq]\?[^?\t]*?\?=", re.I)
# re do detect encoded ASCII characters
ascii_re = re.compile(r"=[\dA-F]{2,3}", re.I)
def clean_spaces(m):
@@ -24,6 +27,14 @@ def clean_spaces(m):
return m.group(0).replace(" ", "=20")
def clean_non_printable_char(m):
"""Strip non printable characters."""
code = int(m.group(0)[1:], 16)
if code < 20:
return ""
return m.group(0)
def decode_mime(m):
"""Substitute matching encoded_word with unicode equiv."""
h = decode_header(clean_spaces(m))
@@ -34,20 +45,34 @@ def decode_mime(m):
return u
def clean_header(header):
"""Clean header function."""
header = "".join(header.splitlines())
header = sre.sub("?==?", header)
return ascii_re.sub(clean_non_printable_char, header)
def u2u_decode(s):
"""utility function for (final) decoding of mime header
note: resulting string is in one line (no \n within)
note2: spaces between enc_words are stripped (see RFC2047)
>>> u2u_decode('=?ISO-8859-15?Q?=20Profitez de tous les services en ligne sur impots.gouv.fr?=')
u' Profitez de tous les services en ligne sur impots.gouv.fr'
>>> u2u_decode('=?ISO-8859-1?Q?Accus=E9?= de =?ISO-8859-1?Q?r=E9ception?= de votre annonce')
u'Accus\xe9 de r\xe9ception de votre annonce'
>>> u2u_decode('Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord')
u'Sm\xf6rg\xe5sbord'
"""
s = ''.join(s.splitlines())
s = sre.sub('?==?', s)
u = mre.sub(decode_mime, s)
return u
return mre.sub(decode_mime, clean_header(s)).strip(" \r\t\n")
def decode_address(value):
"""Special function for address decoding.
We need a dedicated processing because RFC1342 explicitely says
address MUST NOT contain encoded-word:
These are the ONLY locations where an encoded-word may appear. In
particular, an encoded-word MUST NOT appear in any portion of an
"address". In addition, an encoded-word MUST NOT be used in a
Received header field.
"""
phrase, address = parseaddr(clean_header(value))
if phrase:
phrase = mre.sub(decode_mime, phrase)
return phrase, address