Aren't those RFC docs amazing ? Reading up on standards ?
I needed plenty of time for them, as I encountered some interesting issues. As it turned out, some websites / loadbalancers are overly optimistic in encrypting all the things - actually, in redirecting all the things.
Never trust HTTP(s) clients, and be careful when setting up redirection rules. A non-RFC compliant client can trigger a (difficult to exploit) open redirect vulnerability, due to a non-RFC compliant server.
This vulnerability can be tested using
analyze_hosts.py --http TARGET
See https://github.com/PeterMosmans/security-scripts/ for the latest version of analyze_hosts.py
Be warned, long post ahead: A while ago I came across some servers that, when being sent insecure requests, responded with a redirect to the secure version.
% curl -sI http://VICTIM/
HTTP/1.1 301 Moved Permanently Connection: close Location: https://VICTIM/
So far so good, nothing fancy going on here. In fact, this is excellent behaviour. Insecure requests are immediately upgraded to secure requests.
However, the server seemed to be overly happy in redirecting, as it listened to the client-supplied Host parameter:
% curl -s -I -H "Host: MALICIOUS" http://VICTIM/
And the server responded by
HTTP/1.1 301 Moved Permanently Connection: close Location: https://MALICIOUS/
An attacker is able to perform malicious open redirects, if he is able to forge the client's HTTP header.
Truth be told, this is quite difficult to exploit, as this is currently not possible with e.g. XMLHttpRequest (see http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader%28%29-method).
Still, an attacker could use this attack to redirect unknowing users, or try to poison caches.
What does the standard say ? Semantic-wise (in RFC speak) for the last request, the target URI equals the absolute-URI and is http://VICTIM/. The authority is VICTIM, the request-target is /, and the uri-host is MALICIOUS.
The latest HTTP/1.1 standard RFC 7230, paragraph 5.4, states the following:
"If the target URI includes an authority component, then a client MUST send a field-value for Host that is identical to that authority component".
So, the client isn't compliant... but as an attacker, we don't have to be compliant. We can do whatever we want.
Next, it states our issue:
"Since the Host header field acts as an application-level routing mechanism, it is a frequent target for malware seeking to poison a shared cache or redirect a request to an unintended server."
Exactly what is happening here. But, should it happen ? According to the RFC (bear with me, lots of theory here):
"If the server's configuration (or outbound gateway) provides a fixed URI authority component, that authority is used for the effective request URI.".
Translation: if the server only serves VICTIM, then it should use that to serve the request URI from.
"If not, then if the request-target is in authority-form, the effective request URI's authority component is the same as the request-target."
Translation: if the request contains a host, then the server should 'overrule' the request URI. This is mostly meant for proxies, for example:
% curl -s -H "Host: TARGET" http://VICTIM/ -x http://PROXY:80
Here, the PROXY will receive a request target of HTTP//VICTIM/, and it should use that as authority, instead of PROXY.
"If not, then if a Host header field is supplied with a non-empty field-value, the authority component is the same as the Host field-value.".
Translation: If the Host header is supplied (TARGET), it should be the same as the authority. But in our 'attack' it's not, authority is VICTIM. So, let's move to the next line.
"Otherwise, the authority component is assigned the default name configured for the server and, if the connection's incoming TCP port number differs from the default port for the effective request URI's scheme, then a colon (":") and the incoming port number (in decimal form) are appended to the authority component.".
And this is where the server VICTIM fails to follow the RFC guidelines. The authority component should be specified by VICTIM, and not by the client.
Most of the times this issue is the result of wrong redirection rules for servers and loadbalancers. The rules incorrectly (ab)use the Host field as authority.
How to test
$ curl -sIH "Host: vuln" http://$VICTIM/|grep -q "Location: https\?://vuln/" \ && echo "Server is vulnerable to open secure redirect"
analyze_hosts.pyscript on https://github.com/PeterMosmans/security-scripts supports it now as well, with the
Mitigation / solution
Make sure that you use the correct authority, as specified (or served) by the server, and do not blindly redirect to the Host 'authority', as specified by the client.