Blog & News

March 28, 2024

Advisory: Authentication Bypass in Visual Planning REST API (CVE-2023-49231)

Release of SCHUTZWERK-SA-2023-003

preview-image for SCHUTZWERK-SA-2023-003
Hash: SHA512


SCHUTZWERK-SA-2023-003: Authentication Bypass in Visual Planning REST API





CVE reference




Text-only version:

Affected products/vendor

All versions prior to Visual Planning 8 (Build 240207) by STILOG I.S.T.


A wildcard injection inside a prepared SQL statement was found in an undocumented Visual Planning[0] 8 REST API route. The combination of fuzzy matching (via LIKE operator) and user-controlled input allows exfiltrating the REST API key based on distinguishable server responses. If exploited, attackers are able to gain administrative access to the REST API v2.0.


The vulnerability allows attackers to obtain a valid API key for the Visual Planning REST API v2.0. With such a key, attackers can use corresponding endpoints to exfiltrate company data or upload/download files. If no external user management (e.g. LDAP) is configured, the API key can also be used for user management tasks including the creation of administrative users. Since administrators are allowed to upload modules using the Visual Planning Admin Center, a compromise of the underlying server is likely.


During a recent red teaming assessment, Visual Planning was identified as part of the customers internet-facing assets. The software is developed by STILOG I.S.T. and provides resource management and scheduling features. A security assessment conducted by SCHUTZWERK found an authentication bypass in Visual Planning's administrative REST API v2.0.[1]

Corresponding API routes are implemented in the PlanningWSRestV2.java file. A comparison between the documentation and implemented routes revealed an undocumented route (documentation accessed on 2024-03-05), which is externally reachable via a GET request to the /session endpoint.

The following code snippet shows the corresponding undocumented route, which takes the value of the apikey header as an argument:

/*      */   @GET
/*      */   @Path("/session")
/*      */   public Response openSession(@HeaderParam("apikey") String apikey, @HeaderParam("keepalive") String keepalive) {
/*  123 */     if (apikey == null || apikey.trim().isEmpty()) {
/*  124 */       return WSResponse.instance().errorApikey((Response.StatusType)Response.Status.FORBIDDEN, apikey);
/*      */     }
/*      */
/*  127 */     WSSession session = WSSession.existsSession(apikey);
/*  128 */     if (session != null) {
/*  129 */       return WSResponse.instance().error((Response.StatusType)Response.Status.FORBIDDEN, "Already opened session for apikey : ", apikey);
/*      */     }
/*      */
/*  132 */     if (WSSession.getSession(apikey, (keepalive != null && Boolean.parseBoolean(keepalive) == true)) == null) {
/*  133 */       return WSResponse.instance().errorApikey((Response.StatusType)Response.Status.FORBIDDEN, apikey);
/*      */     }
/*  135 */     return WSResponse.instance().success("WSSession created for apikey : " + apikey);
/*      */   }

Line 132 shows a call to the getSession(apikey, ...) method of the WSSession class. Subsequently, the getSession(..) method will call the makeSession(apikey, ..) method of the same class.

The following code snippet shows the makeSession(..) method. Line 646 contains the vulnerable prepared SQL statement, which is prone to wildcard injections[2] due to the usage of the LIKE operator in combination with user-controlled input:

/*      */   private static WSSession makeSession(String apiKey, WSSessionType type) {
/*  634 */     WSSession wsSession = new WSSession();
/*  635 */     WebApplicationContext applicationContext = WebApplicationContext.getDefaultApplication();
/*  636 */     UserSession userSession = applicationContext.createUserSession();
/*      */
/*  638 */     DBConnection connection = applicationContext.createUserSession().getDBConnection();
/*  639 */     String databaseName = applicationContext.getProperty("Application", "Databasename", "VisualPlanning7");
/*      */
/*  641 */     connection.setPoolMode(false);
/*  642 */     connection.setDatabase(databaseName);
/*      */
/*      */     try {
/*  645 */       if (type == WSSessionType.CLIENT) {
/*  646 */         String planningQuery = "SELECT XMLContent FROM Planning WHERE XMLContent LIKE ?";
/*  647 */         PreparedStatement stmt = connection.createPreparedStatement(planningQuery);
/*  648 */         stmt.setString(1, "%<APIKey>" + apiKey + "</APIKey>%");
/*  649 */         ResultSet rs = stmt.executeQuery();
/*      */
/*  651 */         if (!rs.next()) {
/*  652 */           return null;
/*      */         }

The following GET request demonstrates the behavior of injecting a percent sign as wildcard character:

GET /vplanning/api/v2/session HTTP/1.1
Host: vp-host
apikey: %

The server will respond with a success message, indicating that a session was created for the used API key:

HTTP/1.1 200 

WSSession created for apikey : %

Further tests showed that an apikey header payload of '1%' will result in a similar success response, if the api key starts with the character '1'. A payload with a different non-matching first apikey character like '2%' will result in a status code 403 and the error message 'Invalid API key (2%)'.

The proof-of-concept script brute_vp_apikey.py[3] was developed in order to automate the process of exfiltrating the full apikey. The script can be executed as follows against a vulnerable Visual Planning instance and to extract the administrative api key:

$ python3 brute_vp_apikey.py --url
Visual Planning API Key: 79d4add3-6995-8cae-976b-4aaaddd90616


The vendor suggests to update to Visual Planning 8 (Build 240207)

Disclosure timeline

2023-11-01: Vulnerability discovered
2023-11-09: Contact vendor in order to determine security contact
2023-11-10: Received generic sales response from vendor
2023-11-14: Contacted CTO of vendor directly
2023-11-16: Vulnerabilities demonstrated in call with contact at vendor
2023-11-24: CVE assigned by Mitre
2023-11-24: Additional technical details provided to vendor
2023-12-19: Vendor informed SCHUTZWERK that work on fixing the findings is in progress
2024-01-30: Inquired about mitigation status regarding the reported vulnerabilities
2024-01-30: Vendor informed SCHUTZWERK that some of the issues were already fixed
2024-03-08: Sent advisory drafts to vendor
2024-03-28: Received patch information and release of advisory


The vulnerability was discovered by Lennert Preuth of SCHUTZWERK GmbH.


[0] https://www.visual-planning.com/en/
[1] https://app.swaggerhub.com/apis-docs/VisualPlanning/visual-planning_api_rest_v_2_0_us/2.0-oas3
[2] https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05-Testing_for_SQL_Injection#sql-wildcard-injection
[3] https://www.schutzwerk.com/en/43/assets/advisories/brute_vp_apikey.py


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 most recent version of this security advisory can be found at SCHUTZWERK GmbH's website ( https://www.schutzwerk.com ).

Additional information

SCHUTZWERK Advisories: https://www.schutzwerk.com/blog/tags/advisories/

SCHUTZWERK Advisory Policy: https://www.schutzwerk.com/en/advisories/


~ Lennert Preuth

Free Consultation