Skip to content

Instantly share code, notes, and snippets.

@munkiepus
Last active March 14, 2019 10:52
Show Gist options
  • Save munkiepus/537d502c97aaeaef63ddfab907811854 to your computer and use it in GitHub Desktop.
Save munkiepus/537d502c97aaeaef63ddfab907811854 to your computer and use it in GitHub Desktop.
Mitigate against Codeigniter CSV formula injection attacks in
This vuln was reported here https://github.com/bcit-ci/CodeIgniter/issues/5600
but looks like the devs wont fix it so here's how to mitigate if you're stuck with CodeIgniter.
This overides the offending class without touching the core files.
First create a new override for the Codeigniter CI_DB_utility::csv_from_result in application/libraries/MY_DB_utility.php
<?php
/**
* Extends Database Utility Class
*
* @category Database
* @author Tony Dunlop
*/
class MY_DB_utility extends CI_DB_utility {
function __construct($params){
parent::__construct($params);
log_message('info', 'Extended MY_DB_utility class instantiated!');
}
/**
* Generate CSV from a query result object. Override the standard
* csv_from_result class from codeigniter and adds a tab character in before any
* characters which could be used to inject formulas into downloaded sheets.
* see:
* @link https://www.contextis.com/blog/comma-separated-vulnerabilities
* @link http://georgemauer.net/2017/10/07/csv-injection.html
*
* @author Tony Dunlop
* @param object $query Query result object
* @param string $delim Delimiter (default: ,)
* @param string $newline Newline character (default: \n)
* @param string $enclosure Enclosure (default: ")
* @return string
*/
public function csv_from_result($query, $delim = ',', $newline = "\n", $enclosure = '"')
{
if ( ! is_object($query) OR ! method_exists($query, 'list_fields'))
{
show_error('You must submit a valid result object');
}
//these are the characters at the start of a cell which cause the vuln
$unsafe_chars=array('=','+','-','@');
$out = '';
// First generate the headings from the table column names
foreach ($query->list_fields() as $name)
{
$out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim;
}
$out = substr($out, 0, -strlen($delim)).$newline;
// Next blast through the result array and build out the rows
while ($row = $query->unbuffered_row('array'))
{
$line = array();
foreach ($row as $item)
{
//custom vuln mitigation code adds a tab char in front of any unsafe characters
$first = substr($item, 0, 1);
$prepend='';
if (in_array($first, $unsafe_chars)){
$prepend="\t";
}
$line[] = $enclosure.$prepend.str_replace($enclosure, $enclosure.$enclosure, trim($item)).$enclosure;
}
$out .= implode($delim, $line).$newline;
}
return $out;
}
}
?>
then add the custom override to a custom loader in application/core/MY_Loader.php
<?php
class MY_Loader extends CI_Loader {
/**
* Overide the standard the Database Utilities Class with the custom
* MY_DB_utility if it is avaliable. Taken from standard Loader.php and
* custom loader code added.
*
* @author Tony Dunlop
* @param object $db Database object
* @param bool $return Whether to return the DB Utilities class object or not
* @return object
*/
public function dbutil($db = NULL, $return = FALSE)
{
$CI =& get_instance();
if ( ! is_object($db) OR ! ($db instanceof CI_DB))
{
class_exists('CI_DB', FALSE) OR $this->database();
$db =& $CI->db;
}
require_once(BASEPATH.'database/DB_utility.php');
require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php');
$class = 'CI_DB_'.$db->dbdriver.'_utility';
// start of custom
// path of default db utility file
$default_utility = BASEPATH . 'database/DB_utility.php';
// path of my custom db utility file
$my_utility = APPPATH . 'libraries/MY_DB_utility.php';
// set custom db utility file if it exists
if (file_exists($my_utility))
{
$utility = $my_utility;
$extend = 'MY_DB_';
}
else
{
$utility = $default_utility;
$extend = 'CI_DB_';
}
// load db utility file
require_once($utility);
// set the class
$class = $extend.'utility';
// << END custom
if ($return === TRUE)
{
return new $class($db);
}
$CI->dbutil = new $class($db);
return $this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment