Rooting a VMC2040 security camera part 4: Deeper analysis

Posted by Marcus Folkesson on Thursday, May 7, 2026

Rooting a VMC2040 security camera part 4: Deeper analysis

Brief

This part will focus on gather as much information as possible about the system. As I in this stage don't know what I'm looking for, I will just try to document everything I find interesting.

/media/tux-analyse.png

The other parts of the series are:

  • Part1: Basic examination
  • Part2: Extract the firmware
  • Part3: Analyse the boot sequence
  • Part4: Deeper analysis
  • Part5: What didn't work
  • Part6: What did work
  • Part7: Conclusion and summary

U-Boot environment

I used dd to extract the U-Boot environment from the NAND dump. The environment is stored in the ENV partition, which starts at offset 0x440000 and is 0x140000 bytes long:

1dd if=nand_no_oob.bin bs=1 skip=$((0x440000)) count=$((0x140000)) of=ENV.img

This is U-boot environment extracted:

bootargs=ubi.mtd=11,2048 root=/dev/mtdblock9 rootfstype=squashfs ro init=/linuxrc LX_MEM=0xffc6000 mma_heap=mma_heap_name0,miu=0,sz=0x2400000 mma_memblock_remove=1 mtdparts=nand0:0xC0000@0x140000(IPL0),0xC0000(IPL_CUST0),0xC0000(UBOOT0),0xC0000(UBOOT1),0x140000(ENV),0x140000(ENV_P1),0xC0000(MANUFACTURE),0x5C0000(KERNEL),0x5C0000(KERNEL_P1),0x1E00000(ROOTFS),0x1E00000(ROOTFS_P1),-(UBI)
bootcmd=nand read.e 0x22000000 KERNEL 0x5C0000; bootm 0x22000000;nand read.e 0x22000000 KERNEL_P1 0x5C0000; bootm 0x22000000
bootdelay=0
bootparts=mtdparts=nand0:0x60000@0x140000(IPL0),0x60000(IPL1),0x60000(IPL_CUST0),0x60000(IPL_CUST1),0xC0000(UBOOT0),0xC0000(UBOOT1),0x60000(ENV0),0x340000(KERNEL),0x340000(RECOVERY),-(UBI)
ethact=usb_ether
ethaddr=00:30:1b:ba:02:db
fileaddr=23ab6698
filesize=282
gatewayip=192.168.201.101
ipaddr=192.168.201.2
mtddevname=IPL0
mtddevnum=0
mtdids=nand0=nand0
mtdparts=mtdparts=nand0:0xC0000@0x140000(IPL0),0xC0000(IPL_CUST0),0xC0000(UBOOT0),0xC0000(UBOOT1),0x140000(ENV),0x140000(ENV_P1),0xC0000(MANUFACTURE),0x5C0000(KERNEL),0x5C0000(KERNEL_P1),0x1E00000(ROOTFS),0x1E00000(ROOTFS_P1),-(UBI)
netmask=255.255.255.0
partition=nand0,0
serverip=192.168.201.101
stderr=serial
stdin=serial
stdout=serial
usb_folder=images
usbnet_devaddr=00:11:22:33:44:55
usbnet_hostaddr=00:11:22:33:44:66

The SquashFS (rootfs)

Root password

/etc/passwd contains the encrypted root password:

root:$5$rounds=999999$3y7Gr3/skKaK$//m/9T7NLLgTyGWnxt17LuYR5CcS28/w.pqUzt70dfD:0:0:root:/home:/bin/sh

SHA-256 crypt ($5$) with 999,999 rounds. Expensive but maybe crackable.

Cloud API endpoints

/env/vzdaemon.*.env and /env/xagent.*.conf contain all of Arlo's cloud environments, baked into the firmware:

Environment Device API Firmware updates
Production deviceapi.messaging.arlo.com updates.arlo.com/.../production
Development deviceapi-goldendev.messaging.arlo.com arloupdates.arlo.com/.../dev
Staging deviceapi-staging.messaging.arlo.com updates.arlo.com/.../staging
QA deviceapi.messaging.arlo.com updates.arlo.com/.../qa

The xagent (cloud registration daemon) talks to:

  • presence.arloxcld.com — device discovery
  • registration.arloxcld.com — device registration and claiming
  • advisor.arloxcld.com — cloud command broker

Development variants use *.dev.arloxcld.com for all of the above.

The pegacmd

/bin/pegacmd is a big shell script for controlling very much on the camera. Running it with no arguments prints the menu:

pegacmd start_media_manufacture
pegacmd start_media_service
pegacmd stop_media_service
pegacmd misc get als
pegacmd misc get nvmode
pegacmd misc get temp
pegacmd misc set console uart
pegacmd misc set console ssh
pegacmd iva get info
pegacmd iva get intrusion
pegacmd iva set on
pegacmd iva set msg
pegacmd lsd set console uart
pegacmd lsd set console ssh
pegacmd lsd set msg
pegacmd lsd get info
pegacmd version camif
pegacmd version pega_misc
pegacmd version audio
pegacmd version iq
pegacmd version vzdaemon
pegacmd json_camif_conn
pegacmd json_dbif_conn
pegacmd regGet <JSON-Reg-Name>
pegacmd regSet <JSON-Reg-Name> <value>

No firewall

There is no iptables/nftables/bpfilter binary on the camera or other configuration files indicating a firewall is in use.

The UBI volume

Device identity

From the UBI config volume (device_config_0.json):

Serial:    A4U222KA0005C
Model:     VMC2040A
Hardware:  1.2
Firmware:  1.22.0.0_33_53d2844_f5c0b43
MAC:       a4:11:62:c1:05:04

Configuration

The UBI config volume (/config/nvram/) holds over 250 configuration files, one key per file. Some notable ones:

  • pega_sys_en_uart_tx = 1 — UART output enabled
  • pega_sys_en_uart_rx = 1 — UART input enabled (not enabled by default)

What the logs reveal

Logs and core dumps from earlier are stored in the UBI nvrservice volume. Here are some information extracted from those.

Boot time sequence

[0s]    Kernel init, module loading
[1s]    syslogd, chronyd (NTP) start
[2s]    camif (camera control daemon) starts
[3s]    Battery: 90%, 30°C, charging
[4–9s]  WiFi: MT7682 firmware wifi_s.0.3.4, EU region, associating
[20s]   vzdaemon (cloud messaging daemon) starts
[30s]   xagent (cloud registration agent) starts
[36s]   Camera online, connected to Arlo cloud

Processes

Thirteen processes, 65 threads total:

 1Sat Jan  6 20:50:41 UTC 2024
 2===== pstree -p    =========
 3linuxrc(1)-+-ArloRateAdapt(867)-+-{ArloRateAdapt}(875)
 4           |                    -{ArloRateAdapt}(876)
 5           |-busybox(887)
 6           |-camif(743)-+-{camif}(751)
 7           |            |-{camif}(752)
 8           |            |-{camif}(753)
 9           |            |-{camif}(754)
10           |            |-{camif}(767)
11           |            |-{camif}(834)
12           |            `-{camif}(1231)
13           |-chronyd(620)
14           |-fflauncher(775)-+-{fflauncher}(784)
15           |                 |-{fflauncher}(785)
16           |                 |-{fflauncher}(786)
17           |                 `-{fflauncher}(901)
18           |-ffserver(1496)-+-{ffserver}(1510)
19           |                |-{ffserver}(1512)
20           |                |-{ffserver}(1513)
21           |                |-{ffserver}(1514)
22           |                `-{ffserver}(1515)
23           |-linuxrc(888)
24           |-pega_misc(744)-+-{pega_misc}(762)
25           |                |-{pega_misc}(763)
26           |                |-{pega_misc}(764)
27           |                |-{pega_misc}(765)
28           |                |-{pega_misc}(766)
29           |                `-{pega_misc}(768)
30           |-pega_watchdog(746)-+-pegacmd(2467)---pstree(2499)
31           |                    `-{pega_watchdog}(749)
32           |-pega_wifilog(745)
33           |-prog_mediaserve(759)-+-{prog_mediaserve}(778)
34           |                      |-{prog_mediaserve}(779)
35           |                      |-{prog_mediaserve}(817)
36           |                      |-{prog_mediaserve}(818)
37           |                      |-{prog_mediaserve}(819)
38           |                      |-{prog_mediaserve}(827)
39           |                      |-{prog_mediaserve}(828)
40           |                      |-{prog_mediaserve}(829)
41           |                      `-{prog_mediaserve}(864)
42           `-syslogd(616)
43 

Message queues

The applications seems to be using Messages queues for IPC communication:

 1===== ipcs -q       ========
 2
 3------ Message Queues --------
 4key        msqid      owner      perms      used-bytes   messages    
 50x050a37ee 65536      root       666        56           2           
 60x050a3397 98305      root       666        732          3           
 70x03d0ad88 131074     root       666        0            0           
 80x04fb620c 196611     root       666        0            0           
 90x0529115c 229380     root       666        0            0           
100x00d5bd10 262149     root       666        0            0           
110x00d5bd08 294918     root       666        0            0           

Netstat

Allmost all network services are listening on localhost only, and there are several established connections to the cloud. No services are exposed on the WiFi interface.

 1===== netstat -npal ========
 2Active Internet connections (servers and established)
 3Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
 4tcp        0      0 127.0.0.1:8090          0.0.0.0:*               LISTEN      1496/ffserver
 5tcp        0      0 127.0.0.1:4000          0.0.0.0:*               LISTEN      743/camif
 6tcp        0      0 0.0.0.0:554             0.0.0.0:*               LISTEN      1496/ffserver
 7tcp        0      1 192.168.20.12:52952     54.247.91.176:443       FIN_WAIT1   -
 8tcp        0      0 127.0.0.1:4000          127.0.0.1:39312         TIME_WAIT   -
 9tcp        0      1 192.168.20.12:59356     23.72.139.27:443        FIN_WAIT1   -
10tcp        0      0 127.0.0.1:4000          127.0.0.1:39310         TIME_WAIT   -
11udp        0      0 169.254.103.8:12345     0.0.0.0:*                           743/camif
12udp        0      0 127.0.0.1:323           0.0.0.0:*                           620/chronyd
13udp        0      0 127.0.0.1:60268         127.0.0.1:8700          ESTABLISHED 775/fflauncher
14udp        0      0 127.0.0.1:8700          0.0.0.0:*                           867/ArloRateAdapt
15Active UNIX domain sockets (servers and established)
16Proto RefCnt Flags       Type       State         I-Node PID/Program name    Path
17unix  11     [ ]         DGRAM                      1950 616/syslogd         /dev/log
18unix  2      [ ]         DGRAM                      1961 620/chronyd         /var/run/chrony/chronyd.sock
19unix  2      [ ]         DGRAM                      2347 775/fflauncher      
20unix  2      [ ]         DGRAM                      2038 744/pega_misc       
21unix  2      [ ]         DGRAM                      2035 743/camif           
22unix  2      [ ]         DGRAM                      5252 1496/ffserver       
23unix  2      [ ]         DGRAM                      2500 867/ArloRateAdapt   
24unix  2      [ ]         DGRAM                      2041 746/pega_watchdog   
25unix  2      [ ]         DGRAM                      1954 620/chronyd         
26unix  2      [ ]         DGRAM                      2336 759/prog_mediaserve 
27unix  2      [ ]         DGRAM                      2040 745/pega_wifilog    

Firmware update analysis

The camera checks for updates at:

https://updates.arlo.com/arlo/fw/fw_deployed/production/updaterules/VMC2040_UpdateRules.json

Which points to:

binaries/VMC2040/VMC2040_OTA-1.22.0.0_33_53d2844_f5c0b43.prod.enc

The .enc extension indicates the firmware package is encrypted. distribution.

The dev branch

Swapping production for dev in the update URL is publicly accessible:

https://updates.arlo.com/arlo/fw/fw_deployed/dev/binaries/VMC2040/VMC2040_OTA-1.24.0.0_61_11739de_f079ad8.dev.enc

Development firmware (version 1.24.0.0) is available to anyone who knows the URL. Whether this is intentional or an oversight I don't know.

Summary

This part only focuses on gathering information about the system, and there are still many things to analyze. In part5 I will go through a few things that I tried but that didn't work so well.