Connaissez-vous Zip-Slip, une vulnérabilité de traversée de répertoires lors de la décompression de Zip?
Je laisserai les détails à d'autres articles tels que ↓, mais cette fois je vais essayer de réparer la bibliothèque appelée zip4j en manipulant le code d'octet. Impact de la vulnérabilité de la traversée des répertoires Zip Slip sur de nombreux projets Java
À propos, les versions de contre-mesures de vulnérabilité de chaque bibliothèque qui gère Zip sont répertoriées ici. snyk/zip-slip-vulnerability: Zip Slip Vulnerability (Arbitrary file write through archive extraction) Il semble que zip4j n'ait pas de distribution jar ... Si vous prenez la position de distribuer le code source et d'en faire un pot vous-même, je pense qu'il sera plus probable que vous n'utiliserez pas la dernière version ...
↓ données de test zip-slip zip-slip-vulnerability/archives at master · snyk/zip-slip-vulnerability
import javassist._
import javassist.expr.ExprEditor
import javassist.expr.MethodCall
import net.lingala.zip4j.core.ZipFile
/**
*Patch de sécurité dynamique Zip4j par réécriture de code d'octet
*/
object Zip4jSecurity {
def main(args: Array[String]): Unit = {
patch()
//Testez si la validation fonctionne. Zip *: français-Une exception se produit en raison de la validation du bordereau.
val zipFile = new ZipFile("/home/momose/Documents/zip-slip.zip")
zipFile.extractAll("/home/momose/Documents/tmp/")
}
/**
*Appliquer le patch
*/
def patch(): Unit = {
val cp = ClassPool.getDefault
val classLoader = Thread.currentThread().getContextClassLoader()
cp.appendClassPath(new LoaderClassPath(classLoader))
val cc = cp.get("net.lingala.zip4j.unzip.Unzip")
val zipSlipValidationEditor = new Zip4jSecurity.ZipSlipValidationEditor
cc.instrument(zipSlipValidationEditor)
//Inscrivez-vous avec le chargeur de classe
cc.toClass(null, clazz.getProtectionDomain)
}
private class ZipSlipValidationEditor extends ExprEditor {
//Indicateurs qui s'appliquent uniquement au premier appel de méthode
private var unedited = true
override def edit(m: MethodCall): Unit = {
if (unedited &&
m.getClassName.equals("net.lingala.zip4j.model.CentralDirectory") &&
m.getMethodName.equals("getFileHeaders")) {
//Code de validation
val statement ="""
java.io.File outputDir = new java.io.File(outPath);
java.util.List fileHeaders = $0.getFileHeaders($$);
for( int i=0; i<fileHeaders.size(); i++ ){
net.lingala.zip4j.model.FileHeader e = (net.lingala.zip4j.model.FileHeader)fileHeaders.get(i);
if (!new java.io.File(outputDir, e.getFileName()).getCanonicalPath().startsWith(outputDir.getCanonicalPath())) {
throw new RuntimeException("ZIP illégal");
}
}
$_ = fileHeaders;"""
m.replace(statement)
unedited = false
}
}
}
}
Exception in thread "main" java.lang.RuntimeException:ZIP illégal
at net.lingala.zip4j.unzip.Unzip.extractAll(Unzip.java:50)
at net.lingala.zip4j.core.ZipFile.extractAll(ZipFile.java:488)
at net.lingala.zip4j.core.ZipFile.extractAll(ZipFile.java:451)
at com.github.momosetkn.zip.Zip4jSecurity$.main(Zip4jSecurity.scala:18)
at com.github.momosetkn.zip.Zip4jSecurity.main(Zip4jSecurity.scala)
Il valide le résultat de la méthode Java getCanonicalPath
, tel que realpath
dans les commandes C et Linux.
↓ Matériaux de référence
IDS02 \ -J . Normaliser les chemins d'accès avant validation
Uniquement lorsqu'il est utilisé dans la méthode net.lingala.zip4j.core.ZipFile # extractAll
Nous n'avons pas confirmé qu'il s'agira d'une contre-mesure, de sorte que des vulnérabilités peuvent se développer dans d'autres cas.
Je veux que vous le testiez vous-même.
↓ Cette partie est préparée dans l'appel de méthode net.lingala.zip4j.model.CentralDirectory # getFileHeaders ()
.
zip4j/Unzip.java at master · supasate/zip4j
Avec Scala, les littéraux de chaîne devraient être faciles à gérer. De plus, il est agréable de traiter toutes les exceptions comme des exceptions non vérifiées. Lors de la réécriture avec javassist, il est difficile d'écrire dans du code Java qui correspond à la version Java à réécrire.
Recommended Posts