CVE-2019-17555: DoS via Retry-After header in Apache Olingo
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.
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.