samedi 12 novembre 2011, 22:10:02 (UTC+0100)

Traceroute-like HTTP scanner

Side note : I just switched to writing my blog posts in english. I hope they will still be understandable ;-) My french readers are probably fine with this language and should not be negatively impacted by this change.


During some recent pentests, I used the "Max-Forwards" trick to identify some "hidden" reverse HTTP proxies. My customers were surprised by the information found and asked me a copy of the tool. I then choose to take some time to polish and release it. Btw, thanks to Julien Cayssol for the initial versions !


Some background information about the Max-Forwards trick ... The RFC 2616 (HTTP/1.1) and 3261 (SIP) define this HTTP header (resp. in section 14.31 and 8.1.1.6) :

14.31 Max-Forwards

   The Max-Forwards request-header field provides a mechanism with the
   TRACE (section 9.8) and OPTIONS (section 9.2) methods to limit the
   number of proxies or gateways that can forward the request to the
   next inbound server. This can be useful when the client is attempting
   to trace a request chain which appears to be failing or looping in
   mid-chain.

       Max-Forwards   = "Max-Forwards" ":" 1*DIGIT

   The Max-Forwards value is a decimal integer indicating the remaining
   number of times this request message may be forwarded.

   Each proxy or gateway recipient of a TRACE or OPTIONS request
   containing a Max-Forwards header field MUST check and update its
   value prior to forwarding the request. If the received value is zero
   (0), the recipient MUST NOT forward the request; instead, it MUST
   respond as the final recipient. If the received Max-Forwards value is
   greater than zero, then the forwarded message MUST contain an updated
   Max-Forwards field with a value decremented by one (1).

8.1.1.6 Max-Forwards

   The Max-Forwards header field MAY be ignored for all other methods
   defined by this specification and for any extension methods for which
   it is not explicitly referred to as part of that method definition.

   The Max-Forwards header field serves to limit the number of hops a
   request can transit on the way to its destination.  It consists of an
   integer that is decremented by one at each hop.  If the Max-Forwards
   value reaches 0 before the request reaches its destination, it will
   be rejected with a 483(Too Many Hops) error response.

   A UAC MUST insert a Max-Forwards header field into each request it
   originates with a value that SHOULD be 70.  This number was chosen to
   be sufficiently large to guarantee that a request would not be
   dropped in any SIP network when there were no loops, but not so large
   as to consume proxy resources when a loop does occur.  Lower values
   should be used with caution and only in networks where topologies are
   known by the UA.

But this is RFC, not a real life implementation. In fact, the TRACE method is often blocked at the perimeter and we need some smarter ways to identify the reverse proxies. Given my experience, using the TRACE and GET methods is in most cases sufficient to collect weird behaviors. These behaviors are then checked against a few heuristic rules in order to calculate a score. A score greater than zero indicates a possible reverse proxy.


The heuristic rules used are the following :

  • A 502 status code is returned (RFC 2616, section 14.31)
  • A 483 status code is returned (RFC 3261, section 8.1.1.6)
  • When using TRACE, the body contains the 'X-Forwarded-For' string
  • 'Via' or 'X-Via' headers are detected
  • Some fields are different between hops :
    • HTTP status codes
    • 'Server' headers
    • 'Content-Type' headers
    • 'Via' headers
    • HTML titles
    • HTML 'address' tags
    • 'X-Forwarded-For' values in body

The tool was run against the Alexa's Top 100, using the TRACE and GET methods. Some interesting results were identified and we will now examine them.


Target : www.ask.com / Method : TRACE

[==] Target URL : http://www.ask.com:80/
[==] Used method : TRACE
[==] Max number of hops : 3
--------------------------------------------------------------------------------
[---------------------------] Heuristic Report [-------------------------------]
--------------------------------------------------------------------------------
Title:
	Hop #0 : Access Denied
	Hop #1 : Access Denied
	Hop #2 : Access Denied

Server:
	Hop #0 : AkamaiGHost
	Hop #1 : AkamaiGHost
	Hop #2 : AkamaiGHost

Content-Type:
	Hop #0 : text/html
	Hop #1 : text/html
	Hop #2 : text/html

StatusCode:
	Hop #0 : 403 Forbidden
	Hop #1 : 403 Forbidden
	Hop #2 : 403 Forbidden

[--] No reverse proxy

Nothing interesting was detected :-( However, if we switch to GET :

[==] Target URL : http://www.ask.com:80/
[==] Used method : GET
[==] Max number of hops : 3
[++] HTTP 502 : Probably a reverse proxy
--------------------------------------------------------------------------------
[---------------------------] Heuristic Report [-------------------------------]
--------------------------------------------------------------------------------
Title:
	Hop #0 : 502 Proxy Error
	Hop #1 : Undef
	Hop #2 : Ask.com Web Search

Server:
	Hop #0 : Undef
	Hop #1 : Apache
	Hop #2 : Apache

Content-Type:
	Hop #0 : text/html; charset=iso-8859-1
	Hop #1 : text/html
	Hop #2 : text/html;charset=UTF-8

StatusCode:
	Hop #0 : 502 Bad Gateway
	Hop #1 : 500 Internal Server Error
	Hop #2 : 200 OK

[++] Found a reverse proxy, score is 8

The overall score is now 8 and we can assume there's probably 2 reverse-proxies (hops #1 and #2) ! If we try 't.co', we see that TRACE is blocked. However, we have a score of 1 because of the change in 'Server' headers :

[==] Target URL : http://www.t.co:80/
[==] Used method : TRACE
[==] Max number of hops : 3
--------------------------------------------------------------------------------
[---------------------------] Heuristic Report [-------------------------------]
--------------------------------------------------------------------------------
Title:
	Hop #0 : 405 Method Not Allowed
	Hop #1 : 405 Method Not Allowed
	Hop #2 : 405 Method Not Allowed

Server:
	Hop #0 : Apache
	Hop #1 : hi
	Hop #2 : hi

Content-Type:
	Hop #0 : text/html; charset=iso-8859-1
	Hop #1 : text/html; charset=iso-8859-1
	Hop #2 : text/html; charset=iso-8859-1

StatusCode:
	Hop #0 : 405 Method Not Allowed
	Hop #1 : 405 Method Not Allowed
	Hop #2 : 405 Method Not Allowed

[++] Found a reverse proxy, score is 1

't.co' again, now using GET :

[==] Target URL : http://www.t.co:80/
[==] Used method : GET
[==] Max number of hops : 3
[++] HTTP 502 : Probably a reverse proxy
--------------------------------------------------------------------------------
[---------------------------] Heuristic Report [-------------------------------]
--------------------------------------------------------------------------------
Title:
	Hop #0 : Twitter / Over capacity
	Hop #1 : t.co / Twitter
	Hop #2 : t.co / Twitter

Server:
	Hop #0 : Apache
	Hop #1 : hi
	Hop #2 : hi

Content-Type:
	Hop #0 : text/html; charset=UTF-8
	Hop #1 : text/html; charset=utf-8
	Hop #2 : text/html; charset=utf-8

StatusCode:
	Hop #0 : 502 Bad Gateway
	Hop #1 : 200 OK
	Hop #2 : 200 OK

[++] Found a reverse proxy, score is 5

Did you notice the case mismatch between 'utf8' and 'UTF8' ? ;-) There's too some situations where internal IP or hostnames are leaked ! First example, 'typepad.com' and 10.17.*.* :

[==] Target URL : http://www.typepad.com:80/
[==] Used method : TRACE
[==] Max number of hops : 3
[++] "X-Forwarded-For" in body when using TRACE : Probably a reverse proxy
[++] "X-Forwarded-For" in body when using TRACE : Probably a reverse proxy
[++] "X-Forwarded-For" in body when using TRACE : Probably a reverse proxy
--------------------------------------------------------------------------------
[---------------------------] Heuristic Report [-------------------------------]
--------------------------------------------------------------------------------
X-Fwd:
	Hop #0 : [Your_IP], 10.17.141.102
	Hop #1 : [Your_IP], 10.17.141.102
	Hop #2 : [Your_IP], 10.17.141.102

Server:
	Hop #0 : Apache
	Hop #1 : Apache
	Hop #2 : Apache

Content-Type:
	Hop #0 : message/http
	Hop #1 : message/http
	Hop #2 : message/http

StatusCode:
	Hop #0 : 200 OK
	Hop #1 : 200 OK
	Hop #2 : 200 OK

[++] Found a reverse proxy, score is 3

Second example, '163.com' and the host 'stcz164' listening on port 8103 :

[==] Target URL : http://www.163.com:80/
[==] Used method : GET
[==] Max number of hops : 3
[++] "Via" header : Probably a reverse proxy
[++] "Via" header : Probably a reverse proxy
[++] "Via" header : Probably a reverse proxy
--------------------------------------------------------------------------------
[---------------------------] Heuristic Report [-------------------------------]
--------------------------------------------------------------------------------
Via:
	Hop #0 : 1.1 stcz164:8103 (Cdn Cache Server V2.0), 1.1 dg49:8106 (Cdn Cache Server V2.0)
	Hop #1 : 1.1 stcz164:8103 (Cdn Cache Server V2.0), 1.1 dg49:8106 (Cdn Cache Server V2.0)
	Hop #2 : 1.1 stcz164:8103 (Cdn Cache Server V2.0), 1.1 dg49:8106 (Cdn Cache Server V2.0)

Title:
	Hop #0 : ���
	Hop #1 : ���
	Hop #2 : ���

Server:
	Hop #0 : nginx
	Hop #1 : nginx
	Hop #2 : nginx

Content-Type:
	Hop #0 : text/html; charset=GBK
	Hop #1 : text/html; charset=GBK
	Hop #2 : text/html; charset=GBK

StatusCode:
	Hop #0 : 200 OK
	Hop #1 : 200 OK
	Hop #2 : 200 OK

[++] Found a reverse proxy, score is 3

Third example, 'zhaopin.com' and '192.168.9.*' :

[==] Target URL : http://www.zhaopin.com:80/
[==] Used method : TRACE
[==] Max number of hops : 3
[++] "Via" header : Probably a reverse proxy
[++] "X-Forwarded-For" in body when using TRACE : Probably a reverse proxy
[++] "X-Forwarded-For" in body when using TRACE : Probably a reverse proxy
[++] "X-Forwarded-For" in body when using TRACE : Probably a reverse proxy
--------------------------------------------------------------------------------
[---------------------------] Heuristic Report [-------------------------------]
--------------------------------------------------------------------------------
Via:
	Hop #0 : 1.0 squid91 (squid/3.0.STABLE13)
	Hop #1 : Undef
	Hop #2 : Undef

X-Fwd:
	Hop #0 : [Your_IP]
	Hop #1 : [Your_IP], 192.168.9.113
	Hop #2 : [Your_IP], 192.168.9.98

Server:
	Hop #0 : squid/3.0.STABLE13
	Hop #1 : Apache
	Hop #2 : Apache

Content-Type:
	Hop #0 : text/plain
	Hop #1 : message/http
	Hop #2 : message/http

StatusCode:
	Hop #0 : 200 OK
	Hop #1 : 200 OK
	Hop #2 : 200 OK

[++] Found a reverse proxy, score is 9

This can be used on SIP networks too, using the INVITE method and looking for HTTP 483, but I didn't test it. You can download the tool (v0.5) here. Enjoy !


Posted by Nicolas Grégoire | Permanent link

webmaster@agarri.fr
Copyright 2010-2021 Agarri