@@ -1364,21 +1364,24 @@ public function postSend()
1364
1364
*/
1365
1365
protected function sendmailSend ($ header , $ body )
1366
1366
{
1367
- // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped
1368
- // by escapeshellcmd when popen is called due to safe mode.
1369
- if (!empty ($ this ->Sender ) && !ini_get ('safe_mode ' )) {
1367
+ // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
1368
+ if (!empty ($ this ->Sender ) and self ::isShellSafe ($ this ->Sender )) {
1370
1369
if ($ this ->Mailer == 'qmail ' ) {
1371
- $ sendmail = sprintf ( '%s -f%s ' , escapeshellcmd ( $ this -> Sendmail ), escapeshellarg ( $ this -> Sender )) ;
1370
+ $ sendmailFmt = '%s -f%s ' ;
1372
1371
} else {
1373
- $ sendmail = sprintf ( '%s -oi -f%s -t ' , escapeshellcmd ( $ this -> Sendmail ), escapeshellarg ( $ this -> Sender )) ;
1372
+ $ sendmailFmt = '%s -oi -f%s -t ' ;
1374
1373
}
1375
1374
} else {
1376
1375
if ($ this ->Mailer == 'qmail ' ) {
1377
- $ sendmail = sprintf ( '%s ' , escapeshellcmd ( $ this -> Sendmail )) ;
1376
+ $ sendmailFmt = '%s ' ;
1378
1377
} else {
1379
- $ sendmail = sprintf ( '%s -oi -t ' , escapeshellcmd ( $ this -> Sendmail )) ;
1378
+ $ sendmailFmt = '%s -oi -t ' ;
1380
1379
}
1381
1380
}
1381
+
1382
+ // TODO: If possible, this should be changed to escapeshellarg. Needs thorough testing.
1383
+ $ sendmail = sprintf ($ sendmailFmt , escapeshellcmd ($ this ->Sendmail ), $ this ->Sender );
1384
+
1382
1385
if ($ this ->SingleTo ) {
1383
1386
foreach ($ this ->SingleToArray as $ toAddr ) {
1384
1387
if (!@$ mail = popen ($ sendmail , 'w ' )) {
@@ -1424,6 +1427,38 @@ protected function sendmailSend($header, $body)
1424
1427
return true ;
1425
1428
}
1426
1429
1430
+ /**
1431
+ * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
1432
+ *
1433
+ * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
1434
+ * @param string $string The string to be validated
1435
+ * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report
1436
+ * @access protected
1437
+ * @return boolean
1438
+ */
1439
+ protected static function isShellSafe ($ string )
1440
+ {
1441
+ // Future-proof
1442
+ if (escapeshellcmd ($ string ) !== $ string or !in_array (escapeshellarg ($ string ), array ("' $ string' " , "\"$ string \"" ))) {
1443
+ return false ;
1444
+ }
1445
+
1446
+ $ length = strlen ($ string );
1447
+
1448
+ for ($ i = 0 ; $ i < $ length ; $ i ++) {
1449
+ $ c = $ string [$ i ];
1450
+
1451
+ // All other characters have a special meaning in at least one common shell, including = and +.
1452
+ // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
1453
+ // Note that this does permit non-Latin alphanumeric characters based on the current locale.
1454
+ if (!ctype_alnum ($ c ) && strpos ('@_-. ' , $ c ) === false ) {
1455
+ return false ;
1456
+ }
1457
+ }
1458
+
1459
+ return true ;
1460
+ }
1461
+
1427
1462
/**
1428
1463
* Send mail using the PHP mail() function.
1429
1464
* @param string $header The message headers
@@ -1445,8 +1480,8 @@ protected function mailSend($header, $body)
1445
1480
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
1446
1481
if (!empty ($ this ->Sender ) and $ this ->validateAddress ($ this ->Sender )) {
1447
1482
// CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
1448
- if (escapeshellcmd ($ this ->Sender ) === $ this -> Sender && in_array ( escapeshellarg ( $ this -> Sender ), array ( " ' $ this -> Sender ' " , "\" $ this -> Sender \"" ) )) {
1449
- $ params = sprintf ('-f%s ' , escapeshellarg ( $ this ->Sender ) );
1483
+ if (self :: isShellSafe ($ this ->Sender )) {
1484
+ $ params = sprintf ('-f%s ' , $ this ->Sender );
1450
1485
}
1451
1486
}
1452
1487
if (!empty ($ this ->Sender ) and !ini_get ('safe_mode ' ) and $ this ->validateAddress ($ this ->Sender )) {
0 commit comments