Skip to content

Instantly share code, notes, and snippets.

@sergio-bobillier
Last active January 17, 2017 22:16
Show Gist options
  • Save sergio-bobillier/32e47e1743cb0b67837145cc111dbf7e to your computer and use it in GitHub Desktop.
Save sergio-bobillier/32e47e1743cb0b67837145cc111dbf7e to your computer and use it in GitHub Desktop.
Patch to fix a remote code execution vulnerability in CodeIgniter 2.x email library (backported from CodeIgniter 3.1.3)
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index ca9cd1e..9310844 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -1528,6 +1528,33 @@ class CI_Email {
// --------------------------------------------------------------------
/**
+ * Validate email for shell
+ *
+ * Applies stricter, shell-safe validation to email addresses.
+ * Introduced to prevent RCE via sendmail's -f option.
+ *
+ * @see https://github.com/bcit-ci/CodeIgniter/issues/4963
+ * @see https://gist.github.com/Zenexer/40d02da5e07f151adeaeeaa11af9ab36
+ * @license https://creativecommons.org/publicdomain/zero/1.0/ CC0 1.0, Public Domain
+ *
+ * Credits for the base concept go to Paul Buonopane <paul@namepros.com>
+ *
+ * @param string $email
+ * @return bool
+ */
+ protected function _validate_email_for_shell(&$email)
+ {
+ if (function_exists('idn_to_ascii') && $atpos = strpos($email, '@'))
+ {
+ $email = substr($email, 0, ++$atpos).idn_to_ascii(substr($email, $atpos));
+ }
+
+ return (filter_var($email, FILTER_VALIDATE_EMAIL) === $email && preg_match('#\A[a-z0-9._+-]+@[a-z0-9.-]{1,253}\z#i', $email));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Send using mail()
*
* @access protected
@@ -1535,7 +1562,11 @@ class CI_Email {
*/
protected function _send_with_mail()
{
- if ($this->_safe_mode == TRUE)
+ // _validate_email_for_shell() below accepts by reference,
+ // so this needs to be assigned to a variable
+ $from = $this->clean_email($this->_headers['From']);
+
+ if ($this->_safe_mode == TRUE || ! $this->_validate_email_for_shell($from))
{
if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str))
{
@@ -1551,7 +1582,7 @@ class CI_Email {
// most documentation of sendmail using the "-f" flag lacks a space after it, however
// we've encountered servers that seem to require it to be in place.
- if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, "-f ".$this->clean_email($this->_headers['From'])))
+ if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, "-f ".$from))
{
return FALSE;
}
@@ -1572,7 +1603,19 @@ class CI_Email {
*/
protected function _send_with_sendmail()
{
- $fp = @popen($this->mailpath . " -oi -f ".$this->clean_email($this->_headers['From'])." -t", 'w');
+ // _validate_email_for_shell() below accepts by reference,
+ // so this needs to be assigned to a variable
+ $from = $this->clean_email($this->_headers['From']);
+ if ($this->_validate_email_for_shell($from))
+ {
+ $from = '-f '.$from;
+ }
+ else
+ {
+ $from = '';
+ }
+
+ $fp = @popen($this->mailpath . " -oi ".$from." -t", 'w');
if ($fp === FALSE OR $fp === NULL)
{
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment