Aimes-tu le Java?
Au cours des derniers jours, certaines équipes de développements ont certainement abusé de la boisson tant aimée... Le crime organisé, tant à lui a eu l'extrème plaisir d'abuser Java lui-même. Le présent article relate l'ordre des évènements des derniers mois qui ont menés à la semaine délicieuse que nous avons eu du 27 au 31 aout 2012 en présentant une critique sur l'évolution de la situation. Cependant, l'article ne reste pas dans les hautes sphères bien longtemps, en effet, une analyse de l'exploit de preuve de concept disponible en ligne pour une des failles Java 7 est disponible en fin d'Article.
Plusieurs informations sur la ligne du temps qui suit sont disponibles sur le site de Security Explorations. (http://www.security-explorations.com/en/SE-2012-01-status.html).
2 Avril 2012:
Une firme polonaise de recherche en sécurité informatique Security Explorations publie un communiqué de presse(http://www.security-explorations.com/en/SE-2012-01-press.html) annoncant que leur équipe a trouvé 19 failles de sécurité dans Java 7. L'équipe a contacté Oracle pour annoncer la découverte. Le lendemain, Oracle confirme la réception de l'information. Ce qui est intéressant, c'est que l'annonce de la compagnie ne donne pas de détails techniques sur les failles en question. L'équipe se contente simplement d'annoncer que les failles sont, pour la majorité d'entre-elles possibles en raison du non respect des bonnes pratiques de développement Java.
4 au 27 avril 2012
12 autres vulnérabilités sont rapportées à Oracle par la même firme de recherche. Ceci porte le total à 31.
23 aout 2012
Oracle informe l'équipe de Security Exploration que 19 failles ont été corrigées et qu'un correctif sera prêt pour le prochain cycle de mise à jour de Java(16 octobre 2012). Les autres problématiques sont toujours sous investigation et seront corrigées éventuellement.
C'est ici que les choses deviennent intéressante. L'équipe de recherche, étant consciente de l'impacte des vulnérabilités découvertes, a porté une attention particulière à ne pas trop donner de détails techniques sur celles-ci. Hors, vers la fin de la semaine du 20 au 24 aout 2012, on note une augmentation importante de la propagation de malware par l'utilisation de d'applet java malicieux. Le 26 aout, un exploit de preuve de concept(http://pastie.org/4594319) pour une des vulnérabilités ciblées par l'équipe se retrouve librement sur Internet. Plusieurs blog(http://blog.fireeye.com/research/2012/08/zero-day-season-is-not-over-yet.html) parlent de l'exploit et la nouvelle se répend comme une trainé de poudre dans le milieu de la sécurité.
Le problème majeur avec ces failles de Java: il n'y a aucun moyen pour sécuriser les postes utilisateurs. Le seul moyen étant de désactiver java, ce qui ne peut pas toujours être fait, les utilisateurs sont donc laissé à eux-même face à une des grosses problématiques de sécurité notées cette année. De plus, comme si ce n'était pas assez, cette faille concerne Java. N'oublions pas que l'idée de base de Java... C'est d'être indépendant de la plateforme d'exécution. Ainsi, le nombre de cible potentiel (http://www.securityfocus.com/bid/52016) est énorme.
Dans les heures qui ont suivies la publication de la preuve de concept, l'exploit était déjà intégré au kit d'exploitation Blackhole (http://en.wikipedia.org/wiki/Blackhole_exploit_kit) et à l'outil Metasploit(http://www.metasploit.com/). On peut dire que les principaux intéressés n'ont pas chaumé! Par la suite, assez rapidement, plusieurs domaines ( http://blog.fireeye.com/research/2012/08/java-zero-day-first-outbreak.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+FE_research+(FireEye+Malware+Intelligence+Lab) ) ont participé dans des schémas d'infection à l'aide des failles de Java 7.
Éventuellement, coup de théatre, le 30 aout, alors que plusieurs pensaient qu'ils devraient vraiment attendre le 16 octobre pour une mise à jour de Java, Oracle annonce qu'une mise à jour(Java 7 update 7) est diponible(http://www.oracle.com/technetwork/topics/security/alert-cve-2012-4681-1835715.html) pour téléchargement. Amen.
Par contre... Je vous pose une question:
Combien d'entre-vous avez vraiment procédé, immédiatement, à la mise à jour de votre système pour éviter que ce dernier soit vulnérable? Intéressant...
Bien que plusieurs problématiques aient été résolues, le travail n'est cependant pas terminé... L'ensemble des failles rapportées par Security-Explorations n'a pas été corrigée. De plus, en date du 31 aout 2012, date de l'écriture de cet article, une nouvelle faille à été raportée par la même équipe...
Analyse de la preuve de concept
Ce qui ouvre la porte sur la preuve de concept elle-même. La présente analyse explique simplement le fonctionnement de la preuve de concept.
Sur un système Microsoft Windows affecté par la vulnérabilité, l'éxécution du code résulte simplement en l'ouverture de la calculatrice de windows. Le code ici (http://www.hackfest.ca/wp-content/uploads/2012/09/JavaPOC.txt.zip) présenté à été analysé et peut être exécuté en toute sécurité sur vos systèmes.
La Figure 1 montre les lignes 69 à 73 du fichier Gondvv.java (le fichier de la preuve de concept). Il est possible d'y voir un appel à la méthode disableSecurity(). De plus, il est aussi possible d'y voir un appel à la méthode exec() de la classe Runtime qui permet d'exécuter la calculatrice de windows. Il faut noter que le code de l'exploit est en réalité un applet Java. Ainsi, l'applet ne devrait pas avoir les droits requis pour lancer l'application calc.exe car l'applet devrait être contenu par le sandbox de Java.
-Figure 1
disableSecurity();
Process localProcess = null;
localProcess = Runtime.getRuntime().exec("calc.exe");
if(localProcess != null);
localProcess.waitFor();
Ainsi, la méthode disableSecurity() est utilisée dans l'exploit pour contourner les mesures de sécurité normalement implémenté au niveau des applets Java. La Figure 2 montre le code de la méthode disableSecurity() présente entre les lignes 33 et 41 de l'exploit.
-Figure 2
Statement localStatement = new Statement(System.class, "setSecurityManager", new Object[1]);
Permissions localPermissions = new Permissions();
localPermissions.add(new AllPermission());
ProtectionDomain localProtectionDomain = new ProtectionDomain(new CodeSource(new URL("file:///"), new Certificate[0]), localPermissions);
AccessControlContext localAccessControlContext = new AccessControlContext(new ProtectionDomain[] { localProtectionDomain });
SetField(Statement.class, "acc", localStatement, localAccessControlContext);
localStatement.execute();
L'auteur commence par déclarer et initialiser l'objet localStatement de type Statement. La classe Statement est normalement utilisée pour pouvoir faire un appel, sur demande, à une méthode d'une classe en spécifiant une liste de paramètres lorsque le Statement est exécuté. Dans le cas présent, l'exécution du Statement résultera en l'appel de la méthode setSecurityManager() avec en paramètre un objet vide. Cette méthode est utilisée pour changer le SecurityManager utilisé par le système. SecurityManager est une classe chargée, entre autre, de vérifier les droits lors des appels de méthodes. Ainsi, lorsqu'une application tente de faire une action non autorisée, une exception SecurityException est soulevée par le SecurityManager.
Par la suite, l'auteur déclare et initialise un objet localPermissions de type Permissions. Cet objet contient une liste de permissions qui peut être utilisée pour... Valider les permissions. L'objet localPermission contient, dans le cas présent, les permissions AllPermission(). Ainsi, l'ensemble de toutes les permissions sont contenues par cet objet. Suivant ce traitement, l'objet localProtectionDomain de type ProtectionDomain est créé et initialisé. Il est important de savoir que la Classe ProtectionDomain permet de configurer le niveau de droit sur un domaine. Dans le cas présent, il est possible d'observer que le domaine de protection spécifié est file:/// ce indique la racine du système hôte. De plus, l'objet localPermission est utilisé pour assigner les permissions au domaine de protection. Ainsi, il en résulte un domaine de protection possédant "AllPermissions". Par la suite, l'objet localAccessControlContext de type AccessControlContext est créé en utilisant le domaine de protection créé plus tôt. La classe AccessControlContext est utilisée pour prendre les décisions d'accès en se basant sur le domaine/contexte qui lui est associé. Étant associé à un domaine qui possède l'ensemble des permissions sur la racine du système, toutes actions entreprises dont les droits sont vérifiés à partir de localAccessControlContext et dont l'action se produit à partir de la racine du système seront autoriées. Hors, il faut encore que ce contexte soit utilisé pour procéder à la vérification des droits... L'auteur appel donc la méthode SetField présente entre les lignes 54 et 63 de l'exploit. Le code interne de la méthode est présenté à la Figure 3.
-Figure 3
Object arrayOfObject[] = new Object[2];
arrayOfObject[0] = paramClass;
arrayOfObject[1] = paramString;
Expression localExpression = new Expression(GetClass("sun.awt.SunToolkit"), "getField", arrayOfObject);
localExpression.execute();
((Field)localExpression.getValue()).set(paramObject1, paramObject2);
Les trois premières lignes de cette méthode commencent par la création d'un tableau d'objets avec "Statement.class" comme premier objet et "acc" comme deuxième objet. Une fois le tableau prêt, l'auteur procède à la création d'un objet nommé localExpression de type Expression. La classe Expression hérite directement de la classe Statement et peut donc ainsi être utilisée de la même manière. Hors dans le cas présent, la classe ciblée par l'exécution de l'expression sera sun.awt.SunToolkit. Cette classe est obtenue par l'appel de la méthode GetClass() programmée par l'auteur de l'exploit. Lorsque l'expression sera exécuté, elle retournera un objet Field faisant référence à l'attribut "acc" de la classe Statement. Il s'avère cependant que, tel que le montre la Figure 4 exposant la déclaration de l'attribut acc de la classe Statement présent dans le fichier Statement.java (http://www.docjar.org/html/api/java/beans/Statement.java.html), est privé. Un utilisateur ne devrait donc pas être en mesure d'ateindre l'attribut de manière directe.
-Figure 4
private final AccessControlContext acc = AccessController.getContext();
Le paramètre acc est donc le paramètre contenant l'objet AccessControlContext responsable de valider les permissions lors de l'éxécution du statement. Ainsi, la Figure 5 montre le code qui est exécuté lorsque la méthode getField() est appelée. Ce code est tiré du fichier SunToolkit.java (http://www.docjar.com/html/api/sun/awt/SunToolkit.java.html).
-Figure 5
return AccessController.doPrivileged(new PrivilegedAction() {
public Field run() {
try {
Field field = klass.getDeclaredField(fieldName);
assert (field != null);
field.setAccessible(true);
return field;
} catch (SecurityException e) {
assert false;
} catch (NoSuchFieldException e) {
assert false;
}
return null;
}//run
});
Il est possible de voir qu'une action privilégiée est exécutée pour obtenir accès au field acc de la classe Statement. Une fois l'accès obtenue, le field est mis accessible pour l'utilisateur. Le code est ainsi exécuté lorsque la méthode execute() de l'objet localExpression est appelée à la ligne 61 de l'exploit. L'utilisateur peut donc contrôler l'AccessControlContext qui sera utilisé pour valider ses permissions. Puis, à la ligne 62, la méthode getValue() de l'objet localExpression est utilisée pour récupérer l'objet Field retourné par le code montré précédement. Cet objet Field est configuré à l'aide des objets paramObject1 et paramObject2 faisant respectivement référence aux objets localStatement et localAccessControlContext. Le résultat est donc que le programmeur contrôle les droits de l'objet localStatement. L'utilisateur est donc en mesure de provoquer une escalade de privilège lui permettant de modifier les droits d'exécution d'un statement de son choix. L'execution retourne par la suite à la méthode disableSecurity() pour l'exécution du statement. Ainsi, à l'exécution du statement, comme l'utilisateur a élevé ses droits, le SecurityManager du système est remplacé par un objet n'ayant aucune capacité sur la gestion des droit donnant effectivement l'ensemble des droits disponible sur le système au code Java exécuté dans l'applet en question. Ainsi, si vous vous faites prendre sur un site exécutant un tel code... Et que vous êtes sur un système Windows... Et que... Comme... Presque tous les utilisateurs de windows vous avez des droits d'administrateur... L'attaquant obtient donc les droits d'administrateur sur votre système.
Ainsi, on peu constater que cette faille est possible en raison d'un erreur de design de Java et du non respect des bonne pratique de la part des développeurs du produit.
Le cas de la preuve de concept de cette faille soulève aussi un certain questionnement sur les habitudes de publication de code de preuve de concept pour les failles de sécurité. En effet, tel que vous pouvez l'imaginer, il suffit pour un attaquant de modifier la ligne 71 de la preuve de concept pour exécuter du code de son choix et transformer la preuve de concept en un exploit utilisable dans le cadre d'une attaque de grande envergure. En fait, le code de cet exploit est tellement simple à configurer pour le transformer en arme que c'est exactement ce que les développeurs du kit Blackhole ont fait (http://www.f-secure.com/weblog/archives/00002414.html).
La conclusion de cet article est simple. Il a été possible de constater que malheureusement, les failles demeures présentes sur les systèmes bien longtemps après qu'elles aient été découvertes. De plus, contrairement à la croyance populaire, il a été démontré que, souvent, plusieurs des failles importantes sont extrèmement simple à exploitées.
Note: Entre le début et la fin de l'écriture de cet article, une nouvelle faille... pour Java 7 update 7 (la patche de la faille présentée ici... Sortie il y a moins de 36h) a été découverte (http://www.securityfocus.com/archive/1/524073) par la même équipe portant le total de faille à 32.