Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Reverse Engineering Axis

143 findings from static reverse engineering of Axis ACAP applications across ARTPEC-5/6/7/8 and CV25 platforms

findings critical high targets python ablation

OverviewToolingMethodFindingsAttack ClassesCoverageModulesQueryReport


What is Axis ACAP

AXIS Camera Application Platform (ACAP) is the SDK and runtime environment for third-party applications installed on Axis IP cameras and video encoders. ACAPs ship as .eap packages: gzip'd tar archives containing ELF binaries, Lua scripts, manifests, and CGI configuration. They run inside an application sandbox on the camera's embedded Linux OS, with access to camera firmware services via D-Bus, axparameter, libaxhttp, and libaxevent APIs.

Platforms covered: ARTPEC-5 (MIPS32/Lua), ARTPEC-6/7 (ARM32 armhf/armv7hf), ARTPEC-8 (aarch64), CV25 (Ambarella aarch64). Most analytics ACAPs ship multi-arch EAP packages covering all four.


Tooling

Binary analysis was done with Ablation - a custom reverse engineering tool built around semantic BERT embeddings. Ablation reads stripped ELF binaries with no symbols, no source, and no debug info. It encodes what each function does using BinFuse opcode normalization (11 architecture-agnostic categories: DATA_TRANSFER_OP, ARITH_OP, BRANCH_OP, etc.) combined with Markov transitions, then runs cosine similarity queries against a vulnerability pattern corpus using sentence-transformers/all-MiniLM-L6-v2 to surface candidates. Cross-version and cross-arch homolog tracking uses BERT-encoded seed functions swept against multiple binary corpora - which matters here because most ACAP binaries are fully stripped and ship no debug information.

The RE modules in this repo are Ablation's output format: structured Python dicts with full technical detail, binary evidence, and prerequisite conditions attached to each finding.


Method

Static analysis only. No live camera access.

  1. Obtain .eap packages from Axis public FTP mirror (ftp.axis.com/pub/axis/software/ACAP/applications/) and the Axis SDK FTP archive.
  2. Extract package contents: tar xzf <app>.eap yields the ELF binary, Lua scripts, manifest.json, param.conf, cgi.conf, package.conf, and shell scripts.
  3. Run Ablation semantic sweep against stripped ELF binaries. Query profiles targeting: popen/format string injection, tar path traversal, unchecked dlopen, hardcoded credential strings, dlsym + LD_PRELOAD detection, unauthenticated socket listeners.
  4. Cross-reference CGI access levels from cgi.conf and manifest.json against HTTP handler code.
  5. Extract shell scripts (post_install.sh, param.conf, privacy.sh) for plaintext credential and injection surface analysis.
  6. Examine manifest.json D-Bus requiredMethods for wildcard permission grants.
  7. Confirm each candidate: locate the responsible code path, identify preconditions, determine blast radius (APPUSR=root vs sdk sandbox).

The finding modules are Ablation's structured output - each finding records the binary evidence (string constant, dlopen path, CGI handler name, D-Bus method) alongside the exploitation prerequisite and severity.


Notable Findings

Critical

ID Application Title
AXIS-SBP-01 sbplayer UpdateURL checksum-optional library load: server omits checksum header → dlopen attacker-supplied .so
AXIS-PC-01 People Counter 4.6.110 Shell injection via curl format strings in popen (runs as sdk)
AXIS-PC-07 People Counter 4.6.110 TrueviewVAPIX legacy account: default credential VAPIX admin access
AXIS-PC40-01 People Counter 4.0.0 TrueviewVAPIX account not removed in postinst - persists on all 4.0.0 installs
AXIS-BR-01 Barcode Reader Physical access bypass: NBIX IndicateRemoteActivities accepts any barcode token with no server-side crypto validation
AXIS-OE-01 Occupancy Estimator Tarslip via .restore_backup as root (APPUSR=root)
AXIS-COG-01 Cognimatics tvpc base Tarslip as root - all tvpc variants (People Counter, OE, DD, QM)
AXIS-DD-03 Direction Detector Dual .restore_backup tarslip entry points, both as root
AXIS-DI-01 Demographic Indicator Tarslip as root
AXIS-SDM-01 StoreDataManager Hardcoded backdoor: support@cognimatics.com:pass123 (admin-equivalent, every installation)
AXIS-SDM-02 StoreDataManager Static salt qvUReYOddaOWw7qkb6QfHAd3kgUAo6 - all password hashes pre-crackable

High (selected)

ID Application Title
AXIS-SMD-01 Sensor Metrics Dashboard Modbus TCP SSRF: camera pivots to internal OT/ICS network; chains with CVE-2019-14463
AXIS-MDP-01 Metadata Provider MQTT bridge SSRF + /etc/hosts injection via echo $1 $2 >> /etc/hosts
AXIS-LSS-07/08 Body Worn LSS 2.0.x GraphQL standalone config mutations at operator; CA path injection via ENV var
AXIS-UCS-03 UCS SIP liblicensekey.so stub bypass via dlopen RUNPATH (LD_PRELOAD blocked; RUNPATH is not)
AXIS-IC-01 InformaCast ServerAddress SSRF: audio alert stream routed to attacker-controlled InformaCast server
AXIS-PC5-01 People Counter 5.x Rust popen shell injection via allparams generation survives Rust rewrite
AXIS-QM3-01 Queue Monitor 3.x Rust WebReportUpload SSRF: full URL from operator axparam with TLS bypass flag
AXIS-LPS-03 Live Privacy Shield Privacy masking DoS: process exit disables all video masking (GDPR implication)
AXIS-LSS-11 Body Worn LSS LD_LIBRARY_PATH injection → coturn/lib code execution

Attack Classes

Tarslip (path traversal in tar extraction)

Multiple ACAP backup/restore endpoints invoke tar xzf without sanitizing archive entry paths. Malicious .tar.gz with ../../ traversal entries writes to arbitrary camera filesystem paths.

Seven instances run as APPUSR=root (Cognimatics tvpc family): full camera filesystem write with no sandbox boundary - /etc/init.d/, /usr/sbin/, cron entries.

Affected: people_counter, occupancy_estimator, direction_detector, queue_monitor, demographic, lpv_remaining, people_counter_400, cognimatics_tvpc, queue_monitor_v3, people_counter_v5.

Unauthenticated TCP listeners

  • TCP 23456: passage event injection port present in all tvpc variants including the Rust 5.x rewrite. Any LAN host sends synthetic count events without credentials.
  • TCP 4066: master/slave synchronization with no TLS. SlavePass in cleartext (2.x) or libsodium-encrypted with unknown key derivation (5.x/3.x).

Shell injection via popen

popen() is used for backup generation across both C and Rust codebases. Operator-writable axparameter values are interpolated into shell commands without quoting. The Rust 5.x rewrite of People Counter and Queue Monitor preserved popen() in the parambackup path.

SSRF

Camera-to-internal-network pivoting via configurable connection targets:

  • Modbus TCP (sensor_metrics): camera connects as Modbus client to admin-configured IP:502. Chains with CVE-2019-14463 (libmodbus 3.1.11 heap corruption) if attacker controls the Modbus server response.
  • MQTT bridge (metadata_provider): addhost.sh writes unsanitized args to /etc/hosts; bridge connects to attacker-supplied MQTT broker URL.
  • WebReportUpload (queue_monitor, queue_monitor_v3): curl with AllowInsecure flag and operator-controlled URL.
  • SocketCameraContainer (loitering_guard, fence_guard, motion_guard): socket connections to admin-configured remote camera hosts.
  • ServerAddress (informacast): audio alerts and metadata to attacker-controlled Singlewire InformaCast server on port 8081.

License bypass: liblicensekey.so

Axis license verification loads liblicensekey.so via dlopen. Three bypass paths:

  1. LD_PRELOAD stub - viable on InformaCast (no detection code). Blocked by explicit test_ld_preload / dir_contains_overriding_lib checks in UCS/SipThirdPartyIntegration.
  2. dlopen RUNPATH - place stub at /usr/local/packages/<app>/liblicensekey.so; not blocked by the LD_PRELOAD detection.
  3. SBP-01 variant - server omits checksum header → binary logs "checksum missing, ignoring download" and calls dlopen() on the downloaded library regardless.

UCS TOCTOU: anti-tamper checks run only at STARTMODE=once install time. Library placed after install check completes evades all detection.

Unauthenticated SNMP PTZ control

axptzoversnmp (appId 47267) accepts SNMP SET with community string public for NTCIP 1205 PTZ operations (OIDs: pan, tilt, zoom, preset load/store). No authentication required. Writable OIDs include absolute PTZ positioning and preset poisoning.

Hardcoded credentials

Credential Location Impact
support@cognimatics.com:pass123 StoreDataManager postinst Admin backdoor on every installation
ftp://root:pass@192.168.0.90:21 Occupancy Estimator binary Dev FTP credential in production EAP
root:pass Cognimatics tvpc debugar.cgi.org Dev endpoint credential in production binary
admin:1password23 3D People Counter Python pyc Developer credential in pyc string constants

Static salt - StoreDataManager

STATIC_SALT = "qvUReYOddaOWw7qkb6QfHAd3kgUAo6" hardcoded with comment "DON'T CHANGE". Login SQL: MD5(CONCAT(STATIC_SALT, ?, password_salt)). Known salt enables precomputed rainbow tables against all installed user password hashes. Migration path (MigrateCompany.php:423) produces MD5(STATIC_SALT + "" + dynamicSalt) = empty password for migrated users.

D-Bus wildcard permissions

Facedetector 2.x manifests declare com.axis.Param.* (wildcard) as required D-Bus methods - grants read/write access to any camera parameter including stored credentials and network config. CV25 variant additionally declares com.axis.VideoObjectDetection1.* wildcard, enabling suppression or spoofing of object detections in co-resident analytics ACAPs (Object Analytics, Loitering Guard, Fence Guard).

Privacy masking DoS

Live Privacy Shield (appId 346005): single point of failure. "Failed to start PrivacyShield service. Exiting" - crash disables all video privacy masking until restart. On streams configured for privacy protection of natural persons: unmasked exposure is a reportable GDPR incident.

PII exposure at viewer privilege

  • Demographic Indicator: estimated age range and gender (GDPR Art. 9 special category biometric data) accessible at viewer level without operator/admin auth.
  • Occupancy Estimator: live counting API at viewer level.
  • Radar Data Visualizer: /consume.cgi viewer-accessible, exposes real-time radar target positions, velocities, zone assignments.

Severity Summary

Severity Count
CRITICAL 11
HIGH 44
MEDIUM 62
LOW 25
INFO 1
Total 143

Coverage

Module Application appId Arch
lpv/axis_lpv_sqli.py AXIS License Plate Verifier (fflprapp) 333330 aarch64
lpv/axis_lpv_shell_inject.py AXIS License Plate Verifier (fflprapp) 333330 aarch64
lpv/axis_lpv_remaining.py AXIS License Plate Verifier - LPV-03 to LPV-07 333330 aarch64
media/axis_sbplayer.py AXIS Player for Soundtrack Business 413325 aarch64 + ARM32
media/axis_audio_spectrum_viz.py AXIS Audio Spectrum Visualizer 2.3.0 413132 aarch64
tvpc/axis_people_counter.py AXIS People Counter 4.6.110 211490 aarch64
tvpc/axis_people_counter_400.py AXIS People Counter 4.0.0 / S5L 211490 aarch64
tvpc/axis_people_counter_v5.py AXIS People Counter 5.0.5 (Rust) 211490 aarch64
tvpc/axis_occupancy_estimator.py AXIS Occupancy Estimator 413742 ARM32
tvpc/axis_direction_detector.py AXIS Direction Detector 3.16.3 220302 armv7hf
tvpc/axis_cognimatics_tvpc.py Cognimatics tvpc base (shared binary) multiple ARM32
tvpc/axis_queue_monitor.py AXIS Queue Monitor 2.x (C) 211492 armv7hf
tvpc/axis_queue_monitor_v3.py AXIS Queue Monitor 3.0.20 (Rust) 211492 aarch64
tvpc/axis_demographic.py AXIS Demographic Indicator (tvgd) - ARM32
radar/axis_ptz_snmp.py AXIS PTZ over SNMP 47267 MIPS32
radar/axis_ptz_remote.py AXIS p-ptz Remote Connection 413658 aarch64
radar/axis_radar_microbus.py AXIS Radar Integration for Microbus 414271 ARM32
radar/axis_radar_visualizer.py AXIS Radar Data Visualizer 3.3.2 414283 aarch64
radar/axis_speed_monitor.py AXIS Speed Monitor 1.1.7 413872 ARM32
access-control/axis_barcode_reader.py AXIS Barcode Reader 413766 ARM32 + aarch64
access-control/axis_door_controller.py AXIS Door Controller Extension 414114 ARM32
communication/axis_bodyworn_lss.py AXIS Body Worn Live Self-hosted Server 414710 aarch64
communication/axis_informacast.py AXIS Speaker for Singlewire InformaCast 414050 ARM32
communication/axis_ucs_sip.py AXIS Client for Unified Communication Systems 414930 aarch64 + ARM32
analytics/axis_facedetector.py AXIS Face Detector 412581 aarch64 + ARM32
analytics/axis_digital_autotrack.py AXIS Digital Auto Tracking 6789 Lua/ARTPEC-5
analytics/axis_sensor_metrics.py AXIS Sensor Metrics Dashboard 413965 aarch64
analytics/axis_metadata_provider.py AXIS Metadata Provider 413493 ARM32
analytics/axis_3dpc.py AXIS 3D People Counter 211491 MIPS32
infrastructure/axis_storedatamanager.py AXIS StoreDataManager (server Debian pkg) - PHP5/MySQL
video-analytics/axis_vmd.py AXIS Video Motion Detection 4.4.4 143440 ARM32
video-analytics/axis_vmd_458.py AXIS Video Motion Detection 4.5.8 143440 aarch64
video-analytics/axis_vmd3_lua.py AXIS Video Motion Detection 3 (Lua) 46396 Lua/ARTPEC-5
video-analytics/axis_loitering_guard.py AXIS Loitering Guard 2.3.8 46775 ARM32
video-analytics/axis_fence_guard.py AXIS Fence Guard 2.3.8 47775 aarch64
video-analytics/axis_motion_guard.py AXIS Motion Guard 2.3.8 48170 aarch64
video-analytics/axis_cross_line.py AXIS Cross Line Detection 1.1.5 (Lua) 3051 Lua/ARTPEC-5
video-analytics/axis_live_privacy_shield.py AXIS Live Privacy Shield 2.8.8 346005 ARM32

Module Structure

Each module exports a FINDINGS list. Common fields:

FINDING = "AXIS-XX"
LABEL   = "short description"

FINDINGS = [
    {
        "id":           "AXIS-XX-01",
        "severity":     "CRITICAL",      # CRITICAL / HIGH / MEDIUM / LOW / INFO
        "title":        "...",
        "detail":       "Full technical detail with binary evidence ...",
        "prerequisite": "Admin/Operator/Viewer/network access ...",
        "status":       "UNPATCHED",
        "cve":          None,            # CVE string where applicable
    },
]

Some modules also export supporting data structures: WRITABLE_OIDS (ptz_snmp), MOSQUITTO_CONFIG (metadata_provider), LIBMODBUS_CVES (sensor_metrics), NBIX_DBUS_CALLS (barcode_reader), HARDCODED_ARTIFACTS (3dpc), FLASK_ROUTES (3dpc), LD_PRELOAD_DETECTION_FUNCS (ucs_sip).


Querying Findings

import glob

critical = []
for path in glob.glob('**/*.py', recursive=True):
    ns = {}
    exec(open(path).read(), ns)
    for f in ns.get('FINDINGS', []):
        if f.get('severity') == 'CRITICAL':
            critical.append((path, f['id'], f['title']))

for path, fid, title in critical:
    print(f"[{fid}] {title}  ({path})")

Disclosure

Findings have been or are in the process of being disclosed to Axis Communications through their responsible disclosure program (security@axis.com).

Authorization: Analysis performed on EAP packages obtained from Axis public software distribution (ftp.axis.com/pub/axis/software/). No production systems were accessed. Static analysis only.

About

Static reverse engineering of Axis ACAP applications: 143 findings across 35 target groups on ARTPEC-5/6/7/8 and CV25 platforms

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages