December 3, 2020

GHSL-2020-204: Server-Side Template Injection in Corona Warn App Server

Alvaro Muñoz

Summary

A Server-Side Template Injection was identified in Corona Warn App Server enabling attackers to inject arbitrary Java EL expressions, leading to un-auth Remote Code Execution (RCE) vulnerability.

Product

Corona Warn App Server

Tested Version

latest commit to the date of testing: 3fd6baf

Details

Remote Code Execution - JavaEL Injection

It is possible to run arbitrary code on the server (with Submission service account privileges) by injecting arbitrary Java Expression Language (EL) expressions.

Submission server uses Java Bean Validation (JSR 380) custom constraint validators such as ValidSubmissionPayload: When building custom constraint violation error messages, it is important to understand that they support different types of interpolation, including Java EL expressions. Therefore if an attacker can inject arbitrary data in the error message template being passed to ConstraintValidatorContext.buildConstraintViolationWithTemplate() argument, they will be able to run arbitrary Java code. Unfortunately, it is common that validated (and therefore, normally untrusted) bean properties flow into the custom error message. In this case there are, at least, two paths where attacker controlled strings are included in custom constraint error validation messages:

    private boolean checkVisitedCountriesAreValid(SubmissionPayload submissionPayload,
        ConstraintValidatorContext validatorContext) {
      if (submissionPayload.getVisitedCountriesList().isEmpty()) {
        return true;
      }
      Collection<String> invalidVisitedCountries = submissionPayload.getVisitedCountriesList().stream()
          .filter(not(supportedCountries::contains)).collect(toList());

      if (!invalidVisitedCountries.isEmpty()) {
        invalidVisitedCountries.forEach(country -> addViolation(validatorContext,
            "[" + country + "]: Visited country is not part of the supported countries list"));
      }
      return invalidVisitedCountries.isEmpty();
    }

and

    private boolean checkOriginCountryIsValid(SubmissionPayload submissionPayload,
        ConstraintValidatorContext validatorContext) {
      String originCountry = submissionPayload.getOrigin();
      if (submissionPayload.hasOrigin() && !originCountry.isEmpty()
          && !supportedCountries.contains(originCountry)) {
        addViolation(validatorContext, String.format(
            "Origin country %s is not part of the supported countries list", originCountry));
        return false;
      }
      return true;
    }

The ValidSubmissionPayload annotation is used to validate data received by the Submission service controller exposed in /version/v1/diagnosis-keys:

  /**
   * Handles diagnosis key submission requests.
   *
   * @param exposureKeys The unmarshalled protocol buffers submission payload.
   * @param tan          A tan for diagnosis verification.
   * @return An empty response body.
   */
  @PostMapping(value = SUBMISSION_ROUTE, headers = {"cwa-fake=0"})
  @Timed(description = "Time spent handling submission.")
  public DeferredResult<ResponseEntity<Void>> submitDiagnosisKey(
      @ValidSubmissionPayload @RequestBody SubmissionPayload exposureKeys,
      @RequestHeader("cwa-authorization") String tan) {
    submissionMonitor.incrementRequestCounter();
    submissionMonitor.incrementRealRequestCounter();
    return buildRealDeferredResult(exposureKeys, tan);
  }

Spring security policies are defined to protect the endpoints but, in this case, all requests to the Submission endpoint are allowed with no authentication/authorization:

  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .mvcMatchers(HttpMethod.GET, HEALTH_ROUTE, PROMETHEUS_ROUTE, READINESS_ROUTE, LIVENESS_ROUTE).permitAll()
        .mvcMatchers(HttpMethod.POST, SUBMISSION_ROUTE).permitAll()
        .anyRequest().denyAll()
        .and().csrf().disable();
    http.headers().contentSecurityPolicy("default-src 'self'");
  }

Impact

This issue leads to Remote Code execution

Coordinated Disclosure Timeline

  • 10/21/2020: Reported through SAP Trust Center
  • 10/22/2020: Issue reception is acknowledged
  • 10/23/2020: Issue is fixed in public repo
  • 10/28/2020: SAP confirms that the issue is fixed in release 1.5.1 which was deployed on 10/27/2020. SAP also informs GHSL that BSI (Bundesamt für Sicherheit in der Informationstechnik is currently testing the fix and asks to keep the issue confidential till BSI has done their tests and has confirmed that the fix is okay.
  • 11/01/2020: A more robust fix is merged.
  • 11/09/2020: SAP told us that BSI has confirmed the fix.

Resources

SAP ID SR-20-00362

Credit

This issue was discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2020-204 in any communication regarding this issue.