Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
Magento-CVE-2016-4010 — Magento Unautorisierte Remote-Codeausführung (CVE-2016-4010) | Kitploit
Tools/GitHubGitHub/brianwrf/magento-cve-2016-4010
SchwachstellenanalyseCode-AnalyseExploitationWebanwendungs-ExploitationPenetrationstestsLernen & BildungLabs & Praxis
GitHubbrianwrf/magento-cve-2016-4010

Magento-CVE-2016-4010

Magento Unautorisierte Remote-Codeausführung (CVE-2016-4010)

Repository anzeigen
63vor 10 JahrenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

Analyse und Ausnutzung der nicht authentifizierten Remote-Code-Ausführungs-Schwachstelle in Magento (CVE-2016-4010)


0x00 Einleitung

Am 17. Mai veröffentlichte der Sicherheitsforscher Netanel Rubin eine nicht authentifizierte Remote-Code-Ausführungs-Schwachstelle in Magento (CVE-2016-4010). Diese Schwachstelle besteht tatsächlich aus mehreren kleineren Schwachstellen und erlaubt es einem Angreifer, auf einem anfälligen Magento-Server ohne Authentifizierung PHP-Code auszuführen. Magento ist eine sehr beliebte E-Commerce-Plattform, die 2011 von eBay übernommen wurde. Bekannte Unternehmen wie Samsung, Nikon, Lenovo sowie viele kleine E-Commerce-Seiten nutzen sie. Berichten zufolge wird Magento von 250.000 Online-Shops verwendet, mit einem jährlichen Transaktionsvolumen von 60 Milliarden US-Dollar.

0x01 Analyse

Die Nutzungsbedingungen für diese Schwachstelle:

  • Magento hat RPCs (REST oder SOAP) aktiviert, was standardmäßig meist der Fall ist
  • Magento CE & EE Version < 2.0.6

Die Magento-Web-API erlaubt zwei verschiedene Arten von RPCs: REST RPC und SOAP API. Beide bieten die gleichen Funktionen; der einzige Unterschied besteht darin, dass erstere JSON und HTTP-Anfragen verwendet, während letztere XML nutzt.

Um nur die APIs bestimmter Module freizugeben, bietet Magento Entwicklern eine bequeme Methode: Sie deklarieren in der Datei "webapi.xml" nur die APIs der Module, die sie zugänglich machen möchten. Die webapi.xml-Datei enthält alle Klassen und Methoden der Web-APIs, die freigegeben werden sollen. Jede Methode gibt auch die spezifischen erforderlichen Berechtigungen an. Diese Berechtigungen umfassen:

  • anonymous – erlaubt jedem Zugriff auf die Methode
  • self – erlaubt nur registrierten Benutzern und bestimmten Administratoren Zugriff, z. B. erfordert die Berechtigung "Magento_Backend::admin" Zugriff nur für Administratoren, die die Serverkonfiguration bearbeiten können

Natürlich öffnet diese Möglichkeit, dass Entwickler die webapi.xml-Datei für die Kommunikation zwischen dem Frontend und Backend (Web-API) des Systems verwenden können, tatsächlich eine Hintertür direkt in den Kern des Moduls.

Selbst wenn wir die Berechtigung "anonymous" haben, brauchen wir dennoch eine Möglichkeit, Werte dynamisch zu übergeben. Dies bezieht sich auf die verschiedenen Objekte, die im System verwendet werden können. Beispielsweise erlaubt die API-Funktion "CustomerRepositoryInterface::save()" die Verwendung eines Objekts vom Typ "CustomerInterface" in der Variable "$customer". Der Code sieht folgendermaßen aus:

root@kitploit:~
interface CustomerRepositoryInterface
{
/**
 * Create customer.
 */
public function save(\Magento\Customer\Api\Data\CustomerInterface $customer);
}

Wie können wir nun über die RPC-Schnittstelle Objekte erstellen? Tatsächlich liegt die Antwort in der Art und Weise, wie Magento den SOAP-Server konfiguriert.

Magento verwendet den standardmäßig gebündelten PHP-"SoapServer". Für die korrekte Konfiguration benötigt "SoapServer" eine WSDL-Datei, die alle Methoden, Parameter und benutzerdefinierten Typen definiert, die in den eigentlichen RPC-Anfragen verwendet werden. Magento generiert für jedes Modul, das XMLRPC-Funktionen unterstützt, separate WSDL-Dateien und setzt die Werte direkt aus der webapi.xml-Datei des Moduls.

Wenn eine RPC-Anfrage vom Server analysiert wird, verwendet der Server die in der WSDL-Datei gefundenen Daten, um zu prüfen, ob die Anfrage gültig ist, und überprüft die Methode, die Parameter und die Typen der Anfrage. Ist die Anfrage gültig, wird das analysierte Anfrageobjekt an Magento zur weiteren Verarbeitung übergeben. Ein sehr wichtiger Punkt ist, dass "SoapServer" in keiner Weise mit Magento interagiert; alle Informationen über die Methoden und Parameter des Moduls stammen aus der WSDL-Datei. Zu diesem Zeitpunkt besteht die gesendete Anfrage immer noch aus verschachtelten Arrays, und während der Analysephase des SoapServers werden keine Objekte erstellt. Um die erforderlichen Objekte zu erstellen, verarbeitet Magento die Eingabe selbst.

Um die Parameternamen und Datentypen zu extrahieren, holt Magento den Prototyp aus der Methode der Anfrage (siehe vorherigen Code). Für einige grundlegende Datentypen wie Strings, Arrays, Boolesche Werte usw. ordnet das System die Eingabe dem entsprechenden Typ zu. Für Objekttypen ist die Lösung jedoch komplizierter.

Wenn der Datentyp eines Parameters eine Instanz einer Klasse ist, versucht Magento, mit der bereitgestellten Eingabe eine Instanz zu erstellen. Denken Sie daran, dass die Eingabe zu diesem Zeitpunkt nur ein Dictionary ist, dessen Schlüssel die Eigenschaftsnamen und die Werte die Eigenschaftswerte sind.

Zuerst erstellt Magento eine neue Instanz der erforderlichen Klasse. Dann versucht es, sie mit der folgenden Methode zu füllen:

  1. Holt den Eigenschaftsnamen (aus dem Schlüssel des Eingabe-Dictionarys)
  2. Sucht eine öffentliche Methode namens "Set[Name]", wobei [Name] der Eigenschaftsname ist
  3. Wenn eine solche Methode existiert, wird sie mit dem Eigenschaftswert als Parameter ausgeführt
  4. Wenn keine solche Methode existiert, wird die Eigenschaft ignoriert und mit der nächsten fortgefahren

Magento verarbeitet auf diese Weise jede Eigenschaft, die der Benutzer zu setzen versucht. Wenn alle Eigenschaften überprüft wurden, betrachtet Magento die Instanz als fertig eingerichtet und verarbeitet den nächsten Parameter. Nachdem alle Parameter auf diese Weise verarbeitet wurden, führt Magento schließlich die API-Methode aus.

Zusammenfassend lässt sich sagen: Magento erlaubt es Ihnen, ein Objekt zu erstellen, dessen öffentliche Eigenschaften zu setzen und schließlich über seine RPC jede Methode auszuführen, die mit "Set" beginnt. Und genau dieses Verhalten führte zu den Schwachstellen in Magento.

Die Forschung ergab, dass einige API-Aufrufe es erlauben, bestimmte Informationen im Warenkorb zu setzen, wie z. B. unsere Lieferadresse, Produkte oder sogar unsere Zahlungsmethode.

Wenn Magento unsere Informationen in der Warenkorbinstanz setzt, verwendet es die "save"-Methode der Instanz, um die neu hinzugefügten Daten in der Datenbank zu speichern.

Schauen wir uns an, wie die "save"-Methode funktioniert.

root@kitploit:~
/**
* Save object data
*/
public function save(\Magento\Framework\Model\AbstractModel $object)
{
...
// If the object is valid and can be saved
if ($object->isSaveAllowed()) {
    // Serialize whatever fields need serializing
    $this->_serializeFields($object);
    ...
    // If the object already exists in the DB, update it
    if ($this->isObjectNotNew($object)) {
        $this->updateObject($object);
    // Otherwise, create a new record
    } else {
        $this->saveNewObject($object);
    }
     
    // Unserialize the fields we serialized
    $this->unserializeFields($object);
}
...
return $this;
}
// AbstractDb::save()

Magento stellt sicher, dass unser Objekt gültig ist, serialisiert dann alle Teile, die serialisiert werden sollen, speichert sie in der Datenbank und deserialisiert schließlich die zuvor serialisierten Teile.

Das sieht einfach aus, oder? Nicht ganz. Lassen Sie uns weiter sehen, wie Magento bestimmt, welche Teile serialisiert werden sollen.

root@kitploit:~
/**
* Serialize serializable fields of the object
*/
protected function _serializeFields(\Magento\Framework\Model\AbstractModel $object)
{
// Loops through the '_serializableFields' property
// (containing hardcoded fields that should be serialized)
foreach ($this->_serializableFields as $field => $parameters) {
    // Get the field's value
    $value = $object->getData($field);
     
    // If it's an array or an object, serialize it
    if (is_array($value) || is_object($value)) {
        $object->setData($field, serialize($value));
    }
}
}
// AbstractDb::_serializeFields()

Wie wir sehen, werden nur die Felder serialisiert, die im hartcodierten Dictionary "_serializableFields" erscheinen. Wichtig ist, dass diese Methode erst dann mit der Serialisierung fortfährt, wenn sichergestellt ist, dass der Feldwert ein Array oder ein Objekt ist.

Nun sehen wir, wie Magento bestimmt, welche Teile deserialisiert werden sollen.

root@kitploit:~
/**
* Unserialize serializeable object fields
*/
public function unserializeFields(\Magento\Framework\Model\AbstractModel $object)
{
// Loops through the '_serializableFields' property
// (containing hardcoded fields that should be serialized)
foreach ($this->_serializableFields as $field => $parameters) {
    // Get the field's value
    $value = $object->getData($field);
     
    // If it's not an array or an object, unserialize it
    if (!is_array($value) && !is_object($value)) {
        $object->setData($field, unserialize($value));
    }
}
}
// AbstractDb::unserializeFields ()

Nun, das sieht sehr ähnlich aus. Der einzige Unterschied ist, dass Magento diesmal sicherstellen muss, dass der Feldwert kein Array oder Objekt ist. Aufgrund dieser beiden Überprüfungen sollten wir in der Lage sein, einen Objekt-Injektionsangriff durchzuführen, indem wir einfach einen String mit bestimmten Regeln in ein serialisierbares Feld setzen. Wenn wir dies tun, wird das System dieses Feld vor dem Speichern des Objekts in der Datenbank nicht serialisieren, da es kein Objekt oder Array ist. Wenn das System jedoch versucht, es zu deserialisieren, nachdem die Datenbankabfrage ausgeführt wurde, wird es deserialisiert, weil es kein Objekt oder Array ist.

Aber genau diese winzige, fast unsichtbare Bedingung verursacht die Schwachstelle. Es bleibt die Frage, welche Felder als "serialisierbar" gelten und wie wir sie setzen können.

Die erste Frage ist einfach: Ich muss nur nach Klassen suchen, die die Eigenschaft "_serializableFields" enthalten. Schnell wurde eine API-Methode in der "Payment"-Klasse gefunden, aber nicht als Parameter, daher können wir ihre Instanzeigenschaften weder erstellen noch kontrollieren. Wichtig ist, dass ihr serialisierbares Feld "additional_information" nur als Array gesetzt werden kann, und zwar mit der "Set[PROPERTY_NAME]"-Technik als zusätzliche Sicherheitsmaßnahme. Also können wir es nicht nur nicht erstellen, sondern selbst wenn wir es könnten, könnten wir es nicht als String setzen.

Interessanterweise kann es jedoch auf eine andere "originelle" Weise gesetzt werden. Wenn Magento die Eigenschaften einer Parameterinstanz setzt, werden sie nicht wirklich als Eigenschaften gesetzt, sondern in einem Dictionary namens "_data" gespeichert. Wenn eine Eigenschaft einer Instanz verwendet wird, wird dieses Dictionary verwendet. Für uns bedeutet das, dass unser serialisierbares Feld – "additional_information" – tatsächlich in einem internen Dictionary und nicht als normale Eigenschaft gespeichert wird.

Wenn wir also das "_data"-Dictionary vollständig kontrollieren könnten, könnten wir die Array-Beschränkung des "additional_information"-Feldes leicht umgehen, weil wir es manuell setzen könnten, anstatt "Set[PROPERTY_NAME]" aufzurufen.

Aber wie kontrollieren wir dieses sensible Dictionary?

Bevor Magento unsere "Payment"-Instanz speichert, bearbeitet es zuerst ihre Eigenschaften. Magento behandelt unsere API-Eingabe als Zahlungsinformationen, die in der "Payment"-Instanz gespeichert werden sollen, wie folgt:

root@kitploit:~
/**
* Adds a specified payment method to a specified shopping cart.
*/
public function set($cartId, \Magento\Quote\Api\Data\PaymentInterface $method)
{
 
$quote = $this->quoteRepository->get($cartId); // Get the cart instance
$payment = $quote->getPayment(); // Get the payment instance
// Get the data from the user input
$data = $method->getData();
// Check for additional data
if (isset($data['additional_data'])) {
    $data = array_merge($data, (array)$data['additional_data']);
    unset($data['additional_data']);
}
// Import the user input to the Payment instance
$payment->importData($data);
 
...
}
// PaymentMethodManagement::set()

Wie wir sehen, werden die "Payment"-Daten durch Aufruf von "$method->getData()" aus dem Parameter "$method" geholt, der die "_data"-Eigenschaft zurückgibt. Denken Sie daran, dass wir "$method" kontrollieren können, da es ein Parameter der API-Methode ist.

Wenn Magento "getData()" auf unserem "$method"-Parameter aufruft, wird die "_data"-Eigenschaft des Parameters zurückgegeben, die alle von uns eingefügten Zahlungsinformationen enthält. Danach ruft es "importData()" mit der "_data"-Eigenschaft als Eingabe auf und ersetzt die "_data"-Eigenschaft der "Payment"-Instanz durch unsere "_data"-Eigenschaft. Damit sind wir nun in der Lage, die sensitive "_data"-Eigenschaft der "Payment"-Instanz durch unsere kontrollierte "_data"-Eigenschaft zu ersetzen, was bedeutet, dass wir das Feld "additional_information" setzen können.

Damit "unserialize()" wirkt, muss das Feld als String setzbar sein, aber die "Set[PROPERTY_NAME]"-Methode erlaubt nur Arrays. Die Lösung liegt in zwei Codezeilen vor dem Aufruf von "importData()". Magento erlaubt Entwicklern, ihre eigenen Zahlungsmethoden hinzuzufügen und ihre eigenen Daten und Informationen bereitzustellen. Dazu verwendet Magento das Feld "additional_data". Dieses Feld ist ein Dictionary, das mehr Daten zur Zahlungsmethode enthält und vollständig vom Benutzer kontrolliert wird. Um benutzerdefinierte Inhalte zu einem Teil der ursprünglichen Daten zu machen, führt Magento das "additional_data"-Dictionary mit dem ursprünglichen "data"-Dictionary zusammen, was es dem "additional_data"-Dictionary effektiv erlaubt, alle Werte im "data"-Dictionary zu überschreiben, also praktisch eine vollständige Überschreibung. Dies bedeutet, dass das benutzerkontrollierte "additional_data"-Dictionary nach der Zusammenführung zum "_data"-Dictionary des Parameters wird und aufgrund von "importData()" auch zur sensitiven "_data"-Eigenschaft der "Payment"-Instanz. Mit anderen Worten: Wir haben jetzt die vollständige Kontrolle über das serialisierbare Feld "additional_information" und können einen Objekt-Injektionsangriff durchführen.

Da wir nun jeden beliebigen String deserialisieren können, ist es Zeit für den Objekt-Injektionsangriff.

Zuerst benötigen wir ein Objekt mit einer "__wakeup()"- oder "__destruct()"-Methode, damit es automatisch aufgerufen wird, wenn das Objekt deserialisiert oder zerstört wird. Denn obwohl wir die Eigenschaften des Objekts kontrollieren können, können wir seine Methoden nicht aufrufen. Daher müssen wir uns auf die magischen Methoden von PHP verlassen, die automatisch aufgerufen werden, wenn ein bestimmtes Ereignis eintritt.

Das erste Objekt, das wir verwenden werden, ist eine Instanz der Klasse "Credis_Client", die die folgende Methode enthält:

root@kitploit:~
/*
* Called automaticlly when the object is destrotyed.
*/
public function __destruct()
{
if ($this->closeOnDestruct) {
    $this->close();
}
}
/*
* Closes the redis stream.
*/
public function close()
{
if ($this->connected && ! $this->persistent) {
        ...
        $result = $this->redis->close();
}
...
}
// Credis_Client::__destruct(), close()

Wir sehen, diese Klasse hat eine einfache "__destruct"-Methode (die von PHP automatisch aufgerufen wird, wenn das Objekt zerstört wird), die "close()" aufruft. Interessant ist, dass die "close()"-Methode, wenn sie eine aktive Verbindung zu einem Redis-Server feststellt, die "close()"-Methode der "redis"-Eigenschaft aufruft, um sie zu schließen.

Da "unserialize()" es uns erlaubt, alle Objekteigenschaften zu kontrollieren, können wir auch die "redis"-Eigenschaft kontrollieren. Wir können in dieser Eigenschaft jedes beliebige Objekt setzen (nicht nur Redis) und jede "close()"-Methode in jeder Klasse des Systems aufrufen. Dies vergrößert unsere Angriffsfläche erheblich. In Magento gibt es einige "close()"-Methoden, und da diese Methoden normalerweise zum Schließen von Streams, Datei-Handles und zum Speichern von Objektdaten verwendet werden, sollten wir einige interessante Aufrufe finden können.

Wie erwartet, fanden wir die folgende "close()"-Methode in der "Transaction"-Klasse:

root@kitploit:~
/**
* Close this transaction
*/
public function close($shouldSave = true)
{
...
if ($shouldSave) {
    $this->save();
}
...
}
/**
* Save object data
*/
public function save()
{
$this->_getResource()->save($this);
return $this;
}
// Magento\Sales\Model\Order\Payment\Transaction::__destruct(), close()

Das sieht einfach aus: "close()" ruft "save()" auf, das wiederum die "save()"-Methode der "_resource"-Eigenschaft aufruft. Da wir die "_resource"-Eigenschaft kontrollieren, kontrollieren wir auch ihre Klasse und können daher die "save()"-Methode jeder beliebigen Klasse aufrufen.

Ein großer Schritt nach vorn. Wie wir vermuten, wird "save()" normalerweise verwendet, um Daten in verschiedenen Speichermedien (wie Dateisystem, Datenbank usw.) zu speichern. Jetzt müssen wir nur noch eine "save()"-Methode finden, die das Dateisystem als Speichermedium verwendet.

Schnell habe ich eine gefunden:

root@kitploit:~
/**
* Try to save configuration cache to file
*/
public function save()
{
...
// save stats
file_put_contents($this->getStatFileName(), $this->getComponents());
...
}
// Magento\Framework\Simplexml\Config\Cache\File::save()

Diese Methode speichert die Daten aus dem Feld "components" in einer Datei. Da der Dateipfad aus dem Feld "stat_file_name" geholt wird und wir beide Parameter kontrollieren, kontrollieren wir tatsächlich den Dateipfad und den Inhalt, was zu einer Schwachstelle führt, die das Schreiben beliebiger Dateien erlaubt.

Jetzt müssen wir nur noch einen gültigen, beschreibbaren Pfad finden, der vom Webserver aus zugänglich ist. In allen Magento-Installationen gibt es ein "/pub"-Verzeichnis, das zum Speichern von Bildern oder vom Administrator hochgeladenen Dateien dient – ein nutzbarer Pfad.

Schließlich müssen wir nur noch eine PHP-Webshell-Datei auf den Server schreiben, um auf dem Magento-Server ohne Authentifizierung beliebigen PHP-Code ausführen zu können.

0x02 Ausnutzung

Testumgebung aufbauen

  1. Das anfällige Paket herunterladen (hier wird Version 2.0.0 verwendet) Download-Link: https://github.com/magento/magento2/archive/2.0.0.zip
  2. Magento installieren Installationsanleitung: https://github.com/magento/magento2/tree/2.0.0

Hinweis: Hier können einige Probleme auftreten, siehe:

  • http://magento2king.com/magento2-insta-be-downloaded/
  • https://github.com/magento/magento2/issues/2419

Schwachstellenausnutzung

Der auf exploit-db veröffentlichte Exploit für die Schwachstelle ist hier herunterladbar: https://www.exploit-db.com/exploits/39838/

Die Nutzungsmethode ist wie folgt:

  1. Eine anfällige Magento-Website finden Magento-Version online prüfen: http://magentoversion.com/

  2. Ein Produkt in den Warenkorb legen Bildbeschreibung hier eingeben

  3. In den Warenkorb gehen und auf "Zur Kasse" klicken Bildbeschreibung hier eingeben

  4. Die Lieferadresse ausfüllen und die POST-Anfrage an /rest/default/V1/guest-carts/[guestCartId]/shipping-information ansehen und die [guestCartID] abrufen Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

  5. Den obigen Exploit als magento_exp.php speichern und ausführen: php magento_exp.php [Magento_URL] [guestCartID] ([webshell_pfad_zum_schreiben]) Bildbeschreibung hier eingeben

Massenüberprüfung

Nach der Untersuchung des obigen Exploits muss die Nutzung die folgenden Bedingungen erfüllen:

  1. Die Zielseite muss eine Magento-Version kleiner als 2.0.6 haben und die REST-API aktiviert sein
  2. Auf der Startseite der Zielseite muss das folgende JS-Snippet vorhanden sein Bildbeschreibung hier eingeben

Daher wurde ein einfaches Skript zur Massenverifikation geschrieben, das mit dem obigen Exploit zusammenarbeitet:

root@kitploit:~
#!/usr/bin/env python
import urllib
import sys
import socket
timeout = 5
socket.setdefaulttimeout(timeout)

input = sys.argv[1]  # Datei mit URLs von Magento-Websites
output = sys.argv[2] # Ausgabedatei für Ergebnisse, z.B. output.txt

def logFile(str):
    f = open(output,'a')
    f.write(str+"\n")
    f.close()

def checkVul(url):
    try:
	    html = urllib.urlopen(url).read()
	    if "guest-carts" in html:
		    print url,"is vulnerable!"
		    logFile(url)
	    else:
		    print url,"is not vulnerable!"
    except Exception:
	    pass

if __name__ == '__main__':
    inp = open(input,'r')
    for i in inp:
	    url=i.strip()
	    #print url
	    checkVul(url)
    print "All Done!"

Ausführungsergebnis: Bildbeschreibung hier eingeben

0x03 Abwehr

Magento auf die neueste Version (2.0.6) aktualisieren. Download-Link: https://www.magentocommerce.com/download

Referenzen

  • http://netanelrub.in/2016/05/17/magento-unauthenticated-remote-code-execution/

  • https://www.exploit-db.com/exploits/39838/

Tool herunterladen