[Switch] PegaSwitch : un toolkit d’exploit WebKit pour les développeurs

Les hackers sont toujours prêts, dès la sortie d’une nouvelle console, à trifouiller et bidouiller jusqu’aux moindres recoins afin de trouver et exploiter des failles. Le bébé de Nintendo, la Switch, ne déroge pas à la règle : alors qu’une faille WebKit vient d’être trouvée, voilà que la Team ReSwitched publie déjà son toolkit (boîte à outils), destinés aux développeurs expérimentés souhaitant profiter et utiliser l’exploit WebKit.


Comme expliqué plus haut, PegaSwitch est un toolkit (boîte à outils en français) pour les développeurs. Il permet d’ores et déjà de lire/écrire dans la mémoire, appeler des fonctions natives et explorer les fonctionnalités de la Switch à partir du processus WebKit. Il ne permet pas encore de coder des homebrews, mais a surtout été conçu pour permettre à d’autres hackers de travailler ensemble vers cet objectif.

Installation

Installez Node, Python 2.7 et Ruby.

Ouvrez les ports UDP 53 ainsi que TCP 80 et 8100 dans le pare-feu.

Lancez npm install, pip2 install flask, gem install rubydns.

Démarrez le serveur DNS avec la commande suivante : (drop sudo pour Windows) :

sudo ruby rdns.rb $yourlocalIP

Lancez le serveur web :

sudo python serve.py

Lancez watchify :

npm start

Démarrez le shell avec :

node debug.js

Pointez votre Switch vers le serveur DNS.

Accédez à l’eShop pour déclencher le portail captif.

Regardez la connexion au shell.

Utilisation

La manière par défaut de travailler avec PegaSwitch se fait via le shell. Tapez help une fois que la Switch sera connectée pour obtenir une liste des commandes disponibles.

Voilà ce que vous devriez voir en lançant la commande help dans le shell de PegaSwitch

Pour désactiver le shell (et travailler simplement avec l’API), commentez la ligne suivante dans le fichier « exploit/main.js » :

setupListener(sc);

API

Conventions

Les valeurs de 64 bits (les pointeurs, principalement) sont représentées par un tableau JavaScript contenant [lo, hi], où chacun fait 32 bits.

Fonctions utilitaires
  • paddr(address) — Convertit une valeur de 64 bits en hexadécimal
  • add2(a, b) — Ajoute deux valeurs de 64 bits ou ajoute une valeur de 64 bits plus  un nombre
  • nullptr(address) — Retourne true si le valeur de 64 bits donnée vaut 0
  • eq(a, b) — Retourne true si les deux valeurs de 64 bits sont égales
  • parseAddr(address) — Prend une chaîne de caractères hexadécimale et le parse en une valeur de 64 bits’
SploitCore

SploitCore est le point central de PegaSwitch, fournissant toutes les fonctionnalités de base et la partie la plus importante de l’API.

  • dumpaddr(address, count) — Prend une adresse et un nombre de 32 bits pour se connecter
  • read4(address, offset) — Lit une valeur de 32 bits depuis address + offset * 4
  • read8(address, offset) — Lit une valeur de 64 bits depuis address + offset * 4
  • write4(value, address, offset) — Écrit une valeur de 32 bits vers address + offset * 4
  • write8(value, address, offset) — Écrit une valeur de 64 bits vers address + offset * 4
  • memview(address, size, cb) — Appelle cb avec un ArrayBuffer pointant vers la « vue » de la mémoire demandée. NE GARDEZ PAS cette vue ou tout objet l’utilisant autour, ou vous allez remplir le GC et bloquer votre Switch
  • getAddr(obj) — Retourne l’adresse d’un objet JavaScript donné
  • mref(offset) — Retourne l’adresse du module principal (le binaire de l’application lui-même) plus l’offset (de 32 bits) donné
  • getBase() — Retourne l’adresse de base de WebKit
  • getSP() — Retourne le pointeur stack actuel (actuel comme un appel de fonction en JS), principalement utiles pour les chaînes JOP/ROP
  • malloc(bytes) — Retourne l’addresse d’un buffer alloué
  • free(addr) — Libère (free) un buffer alloué
  • bridge and call — Documentation plus bas
  • svc(id, registers, dump_regs) — Appelle un SVC spécifique, en passant un tableau de registres et optionnellement en dumpant tous les registres (mettre dump_regs sur true ou false)
  • getTLS()Obtient l’adresse de TLS
  • str2buf(str)Alloue un buffer pour une chaîne terminée par un null et retourne l’adresse
  • readString(addr, length) — Lit une chaîne de caractères depuis addr. Si la longueur length n’est pas passée en argument ou vaut -1, la fonction s’attend à ce que la chaîne de caractère soit terminée par un null
  • gc() — Force le « ramassage des ordures » (garbage collection).
Call

sploitcore.call permet d’appeler des fonctions natives avec l’adresse. Il prend les paramètres suivants, le premier étant toujours nécessaire :

  • address – L’adresse de la fonction. Soit un offset de 32 bits de l’adresse du module principal, soit un pointeur absolu de 64 bits
  • args – Un tableau d’arguments, qui ira dans x0+
  • fargs – Un tableau de nombres float, qui ira dans d0+
  • registers – Un tableau de registres bruts (x16 et x30 ne sont pas assignables)
  • dump_regs – Un booléen pour définir si les registres doivent être dumpés au retour.

Cette fonction retourne toujours la valeur de 64 bits dans in x0.

Bridge

Bridge vous permet d’envelopper une fonction native dans une fonction JavaScript. Exemple :

var strlen = sc.bridge(0x43A6E8, int, char_p);
log(strlen('foo')); // Logs 3 to the console

Le premier paramètre est l’adresse (même format que call), le second est le type de la valeur de retour, le reste sont les arguments.

Voici les types valides :

  • null — Utilisé pour les fonctions de type void
  • int
  • void_pPointeur arbitraire
  • char_p — Pointeur sur chaîne de caractères
  • float — Argument flottant (actuellement uniquement pris en charge pour les arguments, pas pour les retours).

Crédits

  • Daeken ;
  • Dazzozo ;
  • SciresM ;
  • yupferris ;
  • SomeoneWeird ;
  • Normmatt ;
  • crowell ;
  • Jaames ;
  • shutterbug2000 ;
  • Et bien d’autres de la Team ReSwitched