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.

This post describes a little vulnerability that I recently discovered in Apache Olingo. The issue has been fixed in the 4.7.0 release.

CVE-2019-17555: DoS via Retry-After header in Apache Olingo

The issue

OData protocol runs over HTTP. Apache Olingo implements an OData client. In particular, it offers the AsyncRequestWrapperImpl class which sends a request to an OData server and then handles a response. When a client asks a server to create a new record, the server may not be able to create a record immediately. In this case, the server may reply with a 202 status code and include the following HTTP headers into the response:

  • Location header with a URL to a monitor which the client can check to see if the record is ready.
  • Retry-After header with a number of seconds which the client should wait before checking the monitor.

After sending a request, the AsyncRequestWrapperImpl class pass a received response to the AsyncResponseWrapperImpl. If the response contains the 202 status code, then the AsyncResponseWrapperImpl class starts checking the monitor. If the monitor returns the 202 code, then the class waits for the number of seconds which it got from the Retry-After header. Here is what it looked like:

@Override
public R getODataResponse() {
  HttpResponse res = null;
  for (int i = 0; response == null && i < MAX_RETRY; i++) {
    res = checkMonitor(location);

    if (res.getStatusLine().getStatusCode() == HttpStatusCode.ACCEPTED.getStatusCode()) {
      final Header[] headers = res.getHeaders(HttpHeader.RETRY_AFTER);
      if (ArrayUtils.isNotEmpty(headers)) {
        this.retryAfter = Integer.parseInt(headers[0].getValue());
      }

      try {
        // wait for retry-after
        Thread.sleep((long)retryAfter * 1000);
      } catch (InterruptedException ignore) {
        // ignore
      }

The problem here is that the header value goes directly to the Thread.sleep() method. A malicious server can return a huge value in the Retry-After header which makes the client’s thread sleep for a long time. It may help to implement a denial-of-service attack, however, it may be difficult for an attacker to trick the client to talk to the malicious server.

The solution

The issue has been fixed by checking if the Retry-After header doesn’t exceed the maximum allowed value. If the header is greater than the maximum allowed value, then the default delay is used.

Conclusion

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

References