Skip to end of metadata
Go to start of metadata

A napokban a blog.fireeye.com oldalon sikerült egy Java 7 sebezhetőséget találni, amelyet kihasználva egy Java applet képes kitörni a sandbox környezetéből. A hiba legvégén a Java 7 platformban megjelent ClassFinder osztály resolveClass metódusát találjuk, amely a sandbox előírásokat megkerülve képes betölteni bármilyen osztályt:

ClassFinder.java
public static Class<?> resolveClass(String name, ClassLoader loader) throws ClassNotFoundException {
    Class<?> type = PrimitiveTypeMap.getType(name);
    return (type == null)
            ? findClass(name, loader)
            : type;
}

A meghívott findClass metódus pedig megkeresi és visszaadja az adott osztályt:

ClassFinder.java
public static Class<?> findClass(String name) throws ClassNotFoundException {
    try {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader == null) {
            // can be null in IE (see 6204697)
            loader = ClassLoader.getSystemClassLoader();
        }
        if (loader != null) {
            return Class.forName(name, false, loader);
        }

    } catch (ClassNotFoundException exception) {
        // use current class loader instead
    } catch (SecurityException exception) {
        // use current class loader instead
    }
    return Class.forName(name);
}

Mivel az átadott ClassLoader példány értéke lehet null, ezért ebben az esetben lekérdezésre kerül a SystemClassLoader, amely vissza fogja adni a kért osztályt. A hiba kihasználásában sokat nem számít, de az itt látható kód igen "szép":

  • catch ágakban el van nyelve a kapott ClassNotFoundException vagy SecurityException kivétel
  • majd ezen hibák esetén a metódus végén a Class.forName meghívásra kerül

Ezt a ClassFinder osztályt nem tudja egy sandbox-ban futó applet meghívni, mivel a com.sun.beans.finder csomagban van, amelyet nem lehet elérni a sandbox környezetből, ám az Java 1.4 verzióban megjelenő java.beans.Expression osztályon keresztül igen:

Exploit.java
new Expression(Class.class, "forName", new Object[] {"sun.awt.SunToolkit"}).invoke();

Az Expression.java osztály forrásában azt láthatjuk, hogy meghívja az ősosztályának a konstruktorát:

Expression.java
@ConstructorProperties({"target", "methodName", "arguments"})
public Expression(Object target, String methodName, Object[] arguments) {
    super(target, methodName, arguments);
}

A Statement.java osztályban láthatjuk az AccessController ellenőrzése alatti invoke hívást:

Statement.java
try {
    return AccessController.doPrivileged(
            new PrivilegedExceptionAction<Object>() {
                public Object run() throws Exception {
                    return invokeInternal();
                }
            },
            acc
    );
}
catch (PrivilegedActionException exception) {
    throw exception.getException();
}

Amely tartalmaz egy szép külön ágat arra az esetre, ha a Class.forName() kerülne meghívásra:

Statement.java
// Class.forName() won't load classes outside
// of core from a class inside core. Special
// case this method.
if (target == Class.class && methodName.equals("forName")) {
    return ClassFinder.resolveClass((String)arguments[0], this.loader);
}

Így viszont visszaadódik a kért osztály, mindenféle ellenőrzés nélkül, mert a ClassFinder bizony nem fog dobni PrivilegedActionException kivételt... ettől a ponttól már "triviális" a sandbox környezetből való kitörés, mivel a SunToolkit és a hozzá hasonló a belső használatú osztályok elvileg nem lennének betölthetőek a sandbox környezetben... (smile)

A Statement.java osztályban is van egy nagyon szép programozástechnikai hiba, amely közel két éve jelent meg a kódbázisban, és azóta is benne fekszik:

--- a/src/share/classes/java/beans/Statement.java	Thu Jan 29 15:34:50 2009 +0300
+++ b/src/share/classes/java/beans/Statement.java	Fri Jul 03 16:56:29 2009 +0400
@@ -66,6 +66,7 @@ public class Statement {
     Object target;
     String methodName;
     Object[] arguments;
+    ClassLoader loader;
 
     /**
      * Creates a new <code>Statement</code> object with a <code>target</code>,
@@ -157,7 +158,7 @@ public class Statement {
         // of core from a class inside core. Special
         // case this method.
         if (target == Class.class && methodName.equals("forName")) {
-            return ClassFinder.resolveClass((String)arguments[0]);
+            return ClassFinder.resolveClass((String)arguments[0], this.loader);
         }
         Class[] argClasses = new Class[arguments.length];
         for(int i = 0; i < arguments.length; i++) {

Noha a hibával nincs kapcsolatban, de belekerült a kódbázisba egy loader nevű példányváltozó, amelyet átadnak paraméterként a resolveClass metódus hívásánál, de sehol nem kap értéket. Elképesztő! Bármelyik statikus kódanalizáló program hangosan ugat egy ilyen hibára, amely két éve a Java egyik fontos osztályában fekszik... (smile)

Veszélyes?

A külföldi és hazai IT portálok igen kényes sebezhetőségként írták le ezt a problémát, de valószínűleg nagyobb a füstje, mint a lángja... szakmailag ugyanis nagyon érdekes egy ilyen hibát megtalálni, felfedni és a sebezhetőség okát kideríteni.. de a hitelesen aláírt Java appletek képesek a sandbox környezeten kívül is futni, és akik vissza szeretnének élni a helyzettel, azoknak nem okozhat nehézséget egy hamis, de érvényes és hiteles aláírás megszerzése... nem hiteles aláírással pedig csak egy felhasználói jóváhagyás kell, a felhasználó pedig jóvá fogja hagyni az applet futását...

...ennek ellenére tartsuk szárazon a puskaport és járjuk nyitott szemmel, de nem feltétlen szükséges a JRE/JDK eltávolítása a gépekről, ahogy azt több helyen is olvashatjuk, a helyzet kezelését rábízhatjuk a víruskereső szoftverekre, amelyeknek illene blokkolnia egy ilyen támadást, illetve legalább egy – akár false positive – figyelmeztetést küldeni a felhasználó felé, ha egy aláírás nélküli applet szeretne elindulni és benne van egy Expression hívás. A legbiztosabb megoldás az, ha letiltjuk a Java plugin futását a böngészőben és csak akkor engedélyezzük, ha arra kifejezetten szükségünk van.

Update

Van rá esély, hogy az OpenJDK 6 is érintett a sebezhetőségben, mivel ezen Java környezet forrásban úgy tűnik, hogy a fenti kódrészletek backportolásra kerültek, ugyanis mind az Oracle oldaláról letölthető OpenJDK 6 update 25 forrása, mind az online források között megtalálható a ClassFinder osztály, amely elvileg a Java 1.7 óta létezik:

JDK Release Genealogy

(forrás: https://blogs.oracle.com/darcy/entry/openjdk_6_genealogy)

Javítócsomag

Megérkezett a patch a hibával kapcsolatban: http://www.oracle.com/technetwork/topics/security/alert-cve-2012-4681-1835715.html

      
      
Page viewed times

9 Comments

  1. Anonymous

    a webstart is erintett es javafx is?

    1. Mivel a webstart (JNLP) nem sandbox-ban fut, ezert igen, erintett es nem, nem erintett. Erintett, mert az exploit fut JNLP-ben is es nem, mert a JNLP szempontjabol ez nem szamit biztonsagi hibanak, hiszen ott nincs sandbox, amibol kitorhetne. Ha jol tudom, JNLP-nel mar csak azok a korlatok vannak meg, amik egy 'java -jar enappom.jar' fellovesekor vannak, a JNLP tok kulon processz.

      1. Anonymous

        hát, azért úgylátom, alapból csak aláírt jar-okkal homokzhatsz (smile)http://webstartfaq.com/#37 de majd meglátjuk, figyeljük a open-jdk8 commitokat serényen (big grin)

        1. Auth Gábor AUTHOR

          Hamis hiteles aláírást is lehet ám szerezni... ha valaki adatokat akar lopni felhasználók gépéről, akkor adja magát a lopott cert...

  2. Számomra ez jól példázza, hogy az oracle fennhatósága alatt romlott a java kódminősége. Remélem nem csökken tovább és éri el az átlagos oracle termék szintjét, mert az nagyon gyenge. A másik pedig az is komolytalan, hogy a cég akinek 4 hónapos kiadási ciklusa van nincs eljárásrendje arra az esetre, ha cikluson kívül kell hibát javítani.

    1. Auth Gábor AUTHOR

      Négy éve került bele a Java7 forrásába ez a módosítás, akkor még bőven Sun volt. Érdemes nézelődni a forrásokban, hogy mit és miért változtattak, sokszor tanulságos, ritkán fájdalmas... (smile)

      1. De a java7-et már az Oracle adta ki, ő mondta meg mikor van kész. És hogy a kódba mikor került valami az nem mérvadó, az inkább, hogy senki ne vette észre hogy az ott úgy nem jó, és ahogy írtad egy egyszerű statikus kódanalizáló kidob egy csomó hibát amit vagy figyelembe vesznek vagy nem. Minden programban vannak hibák, ilyen komplex kód minősége inkább a tesztelés, hibajavítás kódanalizálás színvonalától függ. Meg milyen a minőségbiztosítás, kiadási ciklusok, egyebek. Az Oracle gyorsan ki akarta adni a java7-et, már az ő neve alatt. Az egy másik kérdés, hogy van-e szükség 2-4 évente, új Java verzióra, úgy hogy a régi verziókban is vannak hibák. A fejlesztő cégek meg lassan 2 verzióval vannak lemaradva. Érdekes hogy a security oldalak nem javasolják a 6-osra downgrade-et, mert abban még több hiba van.

        1. Auth Gábor AUTHOR

          Igen, nyilvánvalóan lehetett volna még reszelni és vég nélkül refactor hegyeket dobálni a meglévő kódbázisra, de valamikor ki kellett adni.

          Az Oracle felelőssége inkább az, hogy április óta tudtak a hibáról, de nem javították az akkori update kiadásban.

          A fejlesztő cégek meg lassan 2 verzióval vannak lemaradva. Érdekes hogy a security oldalak nem javasolják a 6-osra downgrade-et, mert abban még több hiba van.

          Ezt mondjuk nem értem, mert a Java 6 még 2013 februárig egy publikusan és ingyen támogatott verzió, ráadásul széles körben használják. Az OpenJDK viszont érintett.

          Az IcedTea-ben egyébként javításra került a hiba: http://mail.openjdk.java.net/pipermail/distro-pkg-dev/2012-August/020083.html

    2. Szerintem semmit sem változott a kódminőség.

      Még mindig ott tartanak csak, hogy a logo-kat cserélik le a doksikban és átformázzák, hogy még véletlenül se lehessen megtalálni benne semmit. (smile)