# Rapport d'analyse statique : app Android Greenwood School App : com.botieducation.greenwood 2.4.14 (versionCode 24140) XAPK sha256 : f46aa8b126f8f8856ea15e10ed12fa259d02e383f8921b0645139521b7820e69 Arborescence d'analyse : ~/android_reverse/greenwood/ (XAPK original intact, copie analysée) ## Architecture - Coquille Capacitor 5 (assets/capacitor.config.json), SPA Ionic + Angular dans assets/public/ - La couche native n'est que des plugins capacitor : camera, filesystem, geolocation, push (FCM), preferences, file-opener/picker, voice recorder - Toute la logique métier et l'accès API vivent dans le bundle JS : assets/public/main.js + ~200 chunks paresseux (nNNN.js) - ngsw.json : service worker Angular présent (origine PWA) ## Backend - Constante d'URL de base dans main.js (module 35548, export JW) : https://boti.education/p/greenwood/botiapi/ - Motif par école : https://boti.education/p//botiapi/ - Hôte média/statique : media.boti.education (URLs signées expirantes) - Éditeur : Boti Education (boti.education), logo boti_robot.png, préfixes « boti » partout ## Protocole d'authentification (vérifié en direct le 2026-09-01) - POST {base}login, multipart/form-data : login=, password=, privacy, remembreMe - Réponse : {status, msg, error, keyToken, userId, parentId, eleves[], _acces, parent{}} - Auth de session sur les appels suivants : key= (paramètre GET / champ POST) - Voie moderne également prise en charge (présente dans le code, vide à la connexion ici) : Authorization: Bearer , en-tête X-refresh-token - Réponse avec "disconnect":true force navigateRoot("/") = kill de session côté serveur - Champs du formulaire de connexion : login, password, privacy, remembreMe (extracted/assets/public/1028.js) ## Service API (main.js, classe apiservice) - get(params, endpoint) : URLSearchParams, Content-Type x-www-form-urlencoded - post(params, endpoint) : FormData ; objets imbriqués aplatis en key[subkey] ; tableaux en key[i] ; File/Blob ajoutés bruts ; ajoute automatiquement key= - getBlob(path, params, absolute=false) : téléchargement de blob brut (médias/PDF) - ~110 points d'accès au total (le nom du point d'accès est le 2e argument de get/post dans les chunks). Liste complète via l'expression '\.(post|get|getBlob)\(...,"endpoint"' sur assets/public/*.js ## Constats notables 1. Compte de démo/développement oublié, codé en dur dans la build de production : extracted/assets/public/1028.js : reconnectUser={id:12745, phone:"0649014703", password:"0649014703"} 2. Mots de passe envoyés en champs de formulaire en clair sur TLS (pas de hachage côté client) ; keyToken est une clé de session hex nue de 40 caractères 3. POST device_token {token, user_id, key, uid} pour l'enregistrement push FCM 4. console.log détaillés des paramètres/en-têtes laissés dans le bundle de production 5. Pas d'épinglage de certificat, pas d'obfuscation de la SPA ## Vérification Un POST login réel avec des identifiants de test a renvoyé HTTP 200, error:false, keyToken, fiche parent + _acces=parent. Protocole confirmé de bout en bout. Identifiants internes censurés.