Freebox Server (Ultra V9/ Pop V8/ Delta V7 / Revolution V6 / Mini 4K)

  • État Nouveau
  • Pourcentage achevé
    0%
  • Type Anomalie
  • Catégorie Freebox OS → API
  • Assignée à Personne
  • Système d'exploitation Tous
  • Sévérité Moyenne
  • Priorité Très Basse
  • Basée sur la version A PRECISER
  • Due pour la version Non décidée
  • Échéance Non décidée
  • Votes
  • Privée
Concerne le projet: Freebox Server (Ultra V9/ Pop V8/ Delta V7 / Revolution V6 / Mini 4K)
Ouverte par ltoinel - 15/12/2024
Dernière modification par ltoinel - 15/12/2024

FS#39911 - Freebox API Login Track "/api/v8/login/authorize/"

Bonjour,

Contre toute attente, l’API de Track login sur un id de track inexistant retourne pas mal d’informations qu’elle ne devrait pas forcément retourner dont un password salt et beaucoup de code échappés.
Est-ce la documentation de l’API qui n’est pas à jour ? Où est-ce normal que l’API dump du code en JS ?

NB : Les “XXXX” remplace le code échappé et le sel a été retiré.

Freebox OS : 4.8.16

http://mafreebox/api/v8/login/authorize/666

{
   "success":true,
   "result":{
      "status":"unknown",
      "challenge":[
         "var _tddrqb = '_fzutptw';var _hvlvi = new RegExp(_tddrqb.charAt(eval(unescape('%37'))), 'g');String.fromCharCode(_tddrqb.replace(_hvlvi, 'z').charCodeAt(eval(unescape('XXXXX'))))",
         "var _aethnyfs = '_mtjsaiih';var _nxxvroo = new RegExp(_aethnyfs.charAt(eval(unescape('XXXXX'))), 'g');String.fromCharCode(_aethnyfs.replace(_nxxvroo, 'p').charCodeAt(eval(unescape('%35'))))",
         "var _xphsj = '_agfkirx';var _uobvx = new RegExp(_xphsj.charAt(eval(unescape('%37'))), 'g');String.fromCharCode(_xphsj.replace(_uobvx, 'k').charCodeAt(eval(unescape('%37'))))",
         "var _hotru = { _rehs: '_6rkhldig' }; _hotru._rehs.charAt(eval(unescape('XXXX')))",
         "var _ebcy = { _neoqqe: '_tah\/' }; _ebcy._neoqqe.charAt(eval(unescape('%34')))",
         "var _vxcbrq = { _unqxlpz: '_mvqkbjw' }; _vxcbrq._unqxlpz.charAt(eval(unescape('XXXXX')))",
         "decodeURIComponent('%' + (eval(unescape('%35%37'))).toString(eval(unescape('XXXX'))))",
         "'g'",
         "'p'",
         "decodeURIComponent('%' + (eval(unescape('XXXXX'))))",
         "var _xfozdxm = { _etauaqab: '_pqSd' }; _xfozdxm._etauaqab.charAt(eval(unescape('XXXX')))",
         "var _jsqs = '_fuzldxcj';var _emmk = new RegExp(_jsqs.charAt(eval(unescape('XXXXX'))), 'g');String.fromCharCode(_jsqs.replace(_emmk, 'J').charCodeAt(eval(unescape('%35'))))",
         "'c'",
         "decodeURIComponent('%' + (eval(unescape('XXXX'))).toString(eval(unescape('XXXX'))))",
         "var _fczmbd = { _guhhz: '_dehqlq' }; _fczmbd._guhhz.charAt(eval(unescape(XXXX')))",
         "var _afewzzt = { _mnpstx: '_yu1d' }; _afewzzt._mnpstx.charAt(eval(unescape('XXXXX')))",
         "decodeURIComponent('%' + (eval(unescape('%35%35'))).toString(eval(unescape(XXXX'))))",
         "var _qawcojla = '_bwzhef';var _rulszp = new RegExp(_qawcojla.charAt(eval(unescape('%33'))), 'g');String.fromCharCode(_qawcojla.replace(_rulszp, 'E').charCodeAt(eval(unescape('XXXXX'))))",
         "decodeURIComponent('%' + (eval(unescape('XXXX'))).toString(eval(unescape('XXXX'))))",
         "'G'",
         "var _gcxvax = { _nsfg: '_wdyhcr' }; _gcxvax._nsfg.charAt(eval(unescape('XXXXX')))",
         "var _xtwup = '_jakip';var _factsbsx = new RegExp(_xtwup.charAt(eval(unescape('XXXX'))), 'g');String.fromCharCode(_xtwup.replace(_factsbsx, 'P').charCodeAt(eval(unescape('XXXX'))))",
         "var _pwunhjcf = '_mxno';var _mmoqgw = new RegExp(_pwunhjcf.charAt(eval(unescape('XXXX'))), 'g');String.fromCharCode(_pwunhjcf.replace(_mmoqgw, 'M').charCodeAt(eval(unescape('XXXX'))))",
         "var _wndta = { _bfgrhge: '_zujuQ' }; _wndta._bfgrhge.charAt(eval(unescape('%35')))",
         "var _qbfepmi = '_iwms';var _uffbilk = new RegExp(_qbfepmi.charAt(eval(unescape('%33'))), 'g');String.fromCharCode(_qbfepmi.replace(_uffbilk, 'e').charCodeAt(eval(unescape('%33'))))",
         "var _rijvnj = '_syixk';var _sqaicfto = new RegExp(_rijvnj.charAt(eval(unescape('XXXX'))), 'g');String.fromCharCode(_rijvnj.replace(_sqaicfto, 'b').charCodeAt(eval(unescape('XXXXX'))))",
         "'E'",
         "decodeURIComponent('%' + (eval(unescape('XXXX'))).toString(eval(unescape('XXXX'))))",
         "'s'",
         "var _kjihqn = '_mjyeewoc';var _zktqloe = new RegExp(_kjihqn.charAt(eval(unescape('XXXX'))), 'g');String.fromCharCode(_kjihqn.replace(_zktqloe, 'E').charCodeAt(eval(unescape('XXXXX'))))",
         "'n'",
         "var _rzmbme = { _zmysndbt: '_odcjxnG' }; _rzmbme._zmysndbt.charAt(eval(unescape('XXXX')))"
      ],
      "password_salt":"SALT"
   }
}
nbanba a commenté le 19.12.2024 09:53

Bonjour

perso je ne reproduit pas.

(en utilisant le raccouci 'latest' de l'API v12 sur FreeboxOS 4.8.16)

$ curl -sk https://mafreebox.freebox.fr/api/latest/login/authorize/666| jq '. | .result.password_salt="XXXXXXXXXX" | .result.challenge="YYYYYYYYYY"'
{
  "success": true,
  "result": {
    "status": "unknown",
    "challenge": "YYYYYYYYYY",
    "password_salt": "XXXXXXXXXX"
  }
}

Ou en faisant la requête sur api v8 :

$ curl -sk https://mafreebox.freebox.fr/api/v8/login/authorize/666| jq '. | .result.password_salt="XXXXXXXXXX" | .result.challenge="YYYYYYYYYY"'
{
  "success": true,
  "result": {
    "status": "unknown",
    "challenge": "YYYYYYYYYY",
    "password_salt": "XXXXXXXXXX"
  }
}

Je n'ai pas de tableau pour challenge[] contenant du javascript.

Aussi, le "password_salt": "XXXXXXXXXX" est juste une info supplémentaire utilisable par le user pour ses besoins interne à l'application qu'il développe, j'explique:

Aujourd'hui pour générer un SESSION_TOKEN afin de ne jamais exposer l'APP_TOKEN directement, on calcul un 'password' à partir du APP_TOKEN et du 'challenge'

De mon côté je fais :

[[ "$(openssl version |cut -d' ' -f2 |sed s/[a-z]//)" != "1.1.1" ]] && \
    local password=$(echo -n "$challenge" | openssl dgst -sha1 -hmac "$APP_TOKEN" | sed  's/^SHA1(stdin)= //') || \
    local password=$(echo -n "$challenge" | openssl dgst -sha1 -hmac "$APP_TOKEN" | sed  's/^(stdin)= //')

puis un call avec le 'password' sur https://mafreebox.freebox.fr/api/v12/login/session :

post_fbx_api '/login/session/' "{\"app_id\":\"${APP_ID}\", \"password\":\"${password}\" }

et je récupère le SESSION_TOKEN dans le json en retour à la précédente requête.

Comme vous pouvez le constater, le 'password_salt' intervient null part dans la transaction.

Pour être en mesure de s'auto_relogin à la freebox après expiration du SESSION_TOKEN, il peut être util de publier dans l'environnement une version encryptée de l'APP_TOKEN afin de pouvoir réutiliser ce token pour que l'application se reconnecte automatiquement lors d'une tâche durant plus longtemps que la durée de la session (la session est de 30 minutes, le monitoring de certaines tâches de download ou de gestion des fichiers (ex: faire une grosse archive) peut durer plus de 30 minutes)

De mon côté, les applications demandent au user de saisir un 'strong password' qui sert à encrypter l'APP_TOKEN avec un truc du type :

ENCRYPT_APP=$(echo -n "$USER_STRONG_PASSWORD" | openssl dgst -sha256 -hmac "$password_salt" |sed 's/SHA2-256(stdin)= //')

Puis on utilise la valeur renvoyée pour encrypter l'APP_TOKEN avant de le publier dans la session :

APP_ENCRYPTED_TOKEN=$(echo ${APP_TOKEN}|openssl enc -base64 -e -aes-256-cbc -salt -pass pass:${ENCRYPT_APP} -pbkdf2)

Et on publie l'APP_ENCRYPTED_TOKEN dans la session.

Lorsqu'une requête revient en 'not logged in' l'application est en mesure de déchiffrer le APP_ENCRYPTED_TOKEN et donc de refaire en background une requête de login avec le APP_TOKEN et donc de se reconnecter toute seule à la Freebox (c'est notamment le cas de la 'barre de progression' utilisée dans le monitoring des downloads ou des tâches file_system)

PS: En effet la doc n'est pas à jour, une recherche sur 'password_salt' ne renvoit rien.
Je pense que ça a été ajouté par les devs de FREE depuis l'APIv8 (aujourd'hui on est en v12.x dans FreeboxOS 4.8.16) pour pouvoir authentifier la myriade d'applications Android / IOS développé par Free ces dernières années

Donc oui je pense comme vous que FREE devrait mettre à jour la documentation développeur.
Et j'ajoute 2 choses :
- faire évoluer le calcul du SESSION_TOKEN en utilisant SHA2 / SHA3 et non SHA1
+ se concentrer sur le développement d'1 application unique et bien maintenue faisant tout comme l'était Freebox Compagnon à l'époque

Cordialement
nbanba

nbanba a commenté le 19.12.2024 10:36

Bonjour

Je vous recommande la lecture des commentaires ici :
https://dev.freebox.fr/bugs/task/20609

Visiblement il ne faut pas partager avec une session web les cookies.
(donc normal que je ne reproduise pas avec curl, je n'ai pas de session web ni d'interface graphique sur la machine)

Mais pour moi c'est en effet un bug présent à minima depuis l'API v3

Cordialement
nbanba

ltoinel a commenté le 19.12.2024 23:07

Bien vu nbanba, si tu présentes un cookie d'authentification du portail à l'API, elle te retourne un bloc de JS au lieu d'un challenge comme indiqué dans la doc.

Chargement...

Activer les raccourcis clavier

Liste des tâches

Détails de la tâche

Édition de la tâche