Browse Source

Add IDN homograph attack (spoofing) detection [CVE-2019-15237] (#6891)

pull/7983/head
Aleksander Machniak 4 years ago
parent
commit
b913d2fbde
  1. 1
      CHANGELOG
  2. 7
      program/actions/mail/index.php
  3. 24
      program/actions/mail/show.php
  4. 64
      program/lib/Roundcube/rcube_spoofchecker.php
  5. 1
      program/localization/en_US/messages.inc
  6. 2
      skins/larry/mail.css
  7. 41
      tests/Framework/Spoofchecker.php

1
CHANGELOG

@ -2,6 +2,7 @@ CHANGELOG Roundcube Webmail
===========================
- Upgrade to TinyMCE 5.7.1
- Add IDN homograph attack (spoofing) detection [CVE-2019-15237] (#6891)
- Plugin API: Allow modification of 'error' argument in 'message_send_error' hook (#7914)
- Enigma: Fix bug where signature verification could fail for non-ascii bodies (#7919)
- Enigma: Fix invalid expiration dates of PGP keys on a 32bit system (#7531)

7
program/actions/mail/index.php

@ -34,6 +34,7 @@ class rcmail_action_mail_index extends rcmail_action
protected static $PRINT_MODE = false;
protected static $REMOTE_OBJECTS;
protected static $SUSPICIOUS_EMAIL = false;
/**
* Request handler.
@ -1383,6 +1384,7 @@ class rcmail_action_mail_index extends rcmail_action
// phishing email prevention (#1488981), e.g. "valid@email.addr <phishing@email.addr>"
if (!$show_email && $valid && $name && $name != $mailto && strpos($name, '@')) {
$name = '';
self::$SUSPICIOUS_EMAIL = true;
}
// IDNA ASCII to Unicode
@ -1394,6 +1396,11 @@ class rcmail_action_mail_index extends rcmail_action
}
$mailto = rcube_utils::idn_to_utf8($mailto);
// Homograph attack detection (#6891)
if (!self::$SUSPICIOUS_EMAIL) {
self::$SUSPICIOUS_EMAIL = rcube_spoofchecker::check($mailto);
}
if (self::$PRINT_MODE) {
$address = '&lt;' . rcube::Q($mailto) . '&gt;';
if ($name) {

24
program/actions/mail/show.php

@ -276,6 +276,29 @@ class rcmail_action_mail_show extends rcmail_action_mail_index
return html::div($attrib, $msg . '&nbsp;' . html::span('boxbuttons', $buttons));
}
/**
* Display a warning whenever a suspicious email address has been found in the message.
*
* @return string HTML content of the warning element
*/
public static function suspicious_content_warning()
{
if (empty(self::$SUSPICIOUS_EMAIL)) {
return '';
}
$rcmail = rcmail::get_instance();
$attrib = [
'id' => 'suspicious-content-message',
'class' => 'notice',
];
$msg = html::span(null, rcube::Q($rcmail->gettext('suspiciousemail')));
return html::div($attrib, $msg);
}
public static function message_buttons()
{
$rcmail = rcmail::get_instance();
@ -314,6 +337,7 @@ class rcmail_action_mail_show extends rcmail_action_mail_index
$content = [
self::message_buttons(),
self::remote_objects_msg(),
self::suspicious_content_warning(),
];
$plugin = $rcmail->plugins->exec_hook('message_objects',

64
program/lib/Roundcube/rcube_spoofchecker.php

@ -0,0 +1,64 @@
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| Copyright (C) Kolab Systems AG |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| E-mail/Domain name spoofing detection |
+-----------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+-----------------------------------------------------------------------+
*/
/**
* Helper class for spoofing detection.
*
* @package Framework
* @subpackage Utils
*/
class rcube_spoofchecker
{
/** @var array In-memory cache of checked domains */
protected static $results = [];
/**
* Detects (potential) spoofing in an e-mail address or a domain.
*
* @param string $domain Email address or domain (UTF8 not punnycode)
*
* @return bool True if spoofed/suspicious, False otherwise
*/
public static function check($domain)
{
if (($pos = strrpos($domain, '@')) !== false) {
$domain = substr($domain, $pos + 1);
}
if (isset(self::$results[$domain])) {
return self::$results[$domain];
}
// Spoofchecker is part of ext-intl (requires ICU >= 4.2)
$checker = new Spoofchecker();
// Note: The constant (and method?) added in PHP 7.3.0
if (defined('Spoofchecker::HIGHLY_RESTRICTIVE')) {
$checker->setRestrictionLevel(Spoofchecker::HIGHLY_RESTRICTIVE);
}
$result = $checker->isSuspicious($domain);
// TODO: Use areConfusable() to detect ascii-spoofing of some domains, e.g. paypa1.com?
// TODO: Domains with non-printable characters should be considered spoofed
return self::$results[$domain] = $result;
}
}

1
program/localization/en_US/messages.inc

@ -59,6 +59,7 @@ $messages['contactexists'] = 'A contact with the same email address already exis
$messages['contactnameexists'] = 'A contact with the same name already exists.';
$messages['blockedimages'] = 'To protect your privacy, remote images are blocked in this message.';
$messages['blockedresources'] = 'To protect your privacy remote resources have been blocked.';
$messages['suspiciousemail'] = 'This message contains suspicious email addresses that may be fraudulent.';
$messages['encryptedmessage'] = 'This is an encrypted message and can not be displayed. Sorry!';
$messages['externalmessagedecryption'] = 'This is an encrypted message and can be decrypted with your browser extension.';
$messages['nopubkeyfor'] = 'No valid public key found for $email';

2
skins/larry/mail.css

@ -843,7 +843,7 @@ div.hide-headers {
color: #960;
border: 1px solid #ffdf0e;
background-color: #fef893;
background-position: 5px -83px;
background-position: 5px -87px;
padding: 6px 12px 6px 30px;
white-space: normal;
}

41
tests/Framework/Spoofchecker.php

@ -0,0 +1,41 @@
<?php
/**
* Test class to test rcube_spoofchecker class
*
* @package Tests
*/
class Framework_Spoofchecker extends PHPUnit\Framework\TestCase
{
/**
* Test data for test_check()
*/
function data_check()
{
return [
// Valid:
['test@paypal.com', false],
['postbаnk@gmail.com', false], // ignore spoofed local part
['мон.мон', false],
// Suspicious:
['test@Рaypal.com', true],
['test@postbаnk.com', true],
['aaa.мон', true],
// TODO: Non-working as expected:
// ['test@paypa1.com', true],
// ['test@paypal' . "\xe2\x80\xa8" . '.com', true],
// ['test@paypal' . "\xe2\x80\x8b" . '.com', true],
// ['adoḅe.com', true], // ???????
];
}
/**
* @dataProvider data_check
*/
function test_check($email, $expected)
{
$this->assertSame($expected, rcube_spoofchecker::check($email));
}
}
Loading…
Cancel
Save