Some time ago I wrote about unsafe deserialization and DoS vulnerabilities I’ve discovered in Apache Olingo. This post describes one more issue in the library. This time, it’s a little flaw in the Olingo client which may allow sending some HTTP requests to arbitrary URLs. The issue has been fixed in the 4.7.1 release.

In case you don’t know, Apache Olingo is a Java library that implements the Open Data Protocol (OData). This protocol allows the creation and consumption of queryable and interoperable RESTful APIs in a simple way.

CVE-2020-1925: Requests to arbitrary URLs in Apache Olingo

The issue

The OData protocol runs over HTTP. Apache Olingo offers an OData client. In particular, the library has the AsyncRequestWrapperImpl class that can send a request to an OData server and then processes a response. When a client asks the OData server to create a new record, the server may not be able to create it right away. In this case, the server may reply with a 202 status code and include the Location header with a URL. The class saves this URL for the future:

private void retrieveMonitorDetails(final HttpResponse res) {
  Header[] headers = res.getHeaders(HttpHeader.LOCATION);
  if (ArrayUtils.isNotEmpty(headers)) {
    this.location = URI.create(headers[0].getValue());
...

Then, the class can send a GET request to the URL to check if the record is ready:

public boolean isDone() {
   if (response == null) {
     // check to the monitor URL
     final HttpResponse res = checkMonitor(location);
 ...
 public R getODataResponse() {
   HttpResponse res = null;
   for (int i = 0; response == null && i < MAX_RETRY; i++) {
     res = checkMonitor(location);
 ...
 protected final HttpResponse checkMonitor(final URI location) {
   if (location == null) {
     throw new AsyncRequestException(
         "Invalid async request response. Missing monitor URL");
   }
   final HttpUriRequest monitor = odataClient.getConfiguration()
       .getHttpUriRequestFactory().create(HttpMethod.GET, location);

Or, it can even send a DELETE request:

public ODataDeleteResponse delete() {
   final ODataDeleteRequest deleteRequest = odataClient.getCUDRequestFactory()
       .getDeleteRequest(location);
   return deleteRequest.execute();
 }
 ...
 public AsyncResponseWrapper asyncDelete() {
   return odataClient.getAsyncRequestFactory().getAsyncRequestWrapper(
       odataClient.getCUDRequestFactory().getDeleteRequest(location)).execute();
 }

The problem is that the class doesn’t check if the URL from the Location header belongs to the server. As a result, the server can trick the client to send a request to an arbitrary URL. For example, the URL can point to a private resource that is accessible by the client but not by the server.

The AsyncBatchRequestWrapperImpl class also has the same issue.

The issue was discovered during code review.

The solution

The issue has been fixed by adding a check that makes sure that a URL from the Location header belongs to the server. The check now is implemented in the checkLocation() method which verifies that the scheme, the domain names and the port match with the server’s URL:

private URI checkLocation(URI uri) {
   if (!this.uri.getScheme().equals(uri.getScheme())) {
     throw new AsyncRequestException("Unexpected scheme in the Location header");
   }
   if (!this.uri.getHost().equals(uri.getHost())) {
     throw new AsyncRequestException("Unexpected host name in the Location header");
   }
   if (this.uri.getPort() != uri.getPort()) {
     throw new AsyncRequestException("Unexpected port in the Location header");
   }
   return uri;
 }

Is my application vulnerable?

The answer to this question highly depends on how a particular application uses Apache Olingo.

First, if the application doesn’t use the AsyncRequestWrapperImpl or AsyncBatchRequestWrapperImpl classes, then it’s not affected.

If the application uses the classes, then the following questions may help to understand how an attacker can take advantage of the issue in these classes:

  1. Can the application connect to an untrusted server?
  2. Is there a way how someone can trick the application to send a request to an untrusted server?
  3. Is there a way how an attacker can modify the data between client and server?

If the answer to at least one of those questions is more yes rather than no, then the application is likely affected.

Conclusion

The issue doesn’t look serious because it may be quite difficult for an adversary to exploit it. Nevertheless, it may be better to update Apache Olingo to 4.7.1 to stay on the safe side.

References