Skip to content

Instantly share code, notes, and snippets.

@Ackthbpt
Last active May 9, 2026 03:48
Show Gist options
  • Select an option

  • Save Ackthbpt/8dc6cce560d62a9a5540fce11e03fef8 to your computer and use it in GitHub Desktop.

Select an option

Save Ackthbpt/8dc6cce560d62a9a5540fce11e03fef8 to your computer and use it in GitHub Desktop.
Litter-Robot 5 Pro — Local Camera Interface Reverse Engineering

Litter-Robot 5 Pro - Local Camera Interface Reverse Engineering

Status: Work in progress - login credentials not yet cracked
Device: Litter-Robot 5 Pro (serial format: LR5-XX-XX-XX-XXXX-XXXXXX)
Camera OEM: iENSO Inc. (Toronto, ON, Canada) - ienso.com
Goal: Local RTSP access to the integrated camera without Whisker+ cloud subscription


Background

The Litter-Robot 5 Pro ships with dual integrated 1080p HD cameras (front-facing and inner-facing) manufactured by iENSO, a Canadian embedded vision OEM. Camera features like live streaming, recording history, and cat facial recognition are paywalled behind Whisker's Whisker+ subscription. The free tier limits you to 5 minutes of daily live streaming and 2 days of cloud-stored event recordings.

The camera module runs its own embedded Linux system with a full-featured web interface on the local network - completely independent of Whisker's cloud. This document describes what's been found so far.


Network Discovery

Open Ports

A full 65,535-port TCP scan reveals three open ports:

Port Protocol Service Notes
80 HTTP lighttpd/1.4.66 Serves the same React SPA as port 443
443 HTTPS lighttpd/1.4.66 Primary web interface (React SPA)
8888 HTTP lighttpd (assumed) Secondary login page (Bootstrap-based)

No other TCP ports are open - notably no RTSP (554), no telnet (23), no SSH (22). RTSP appears to require explicit activation through the web UI after authentication.

MAC Address / OUI

Field Value
MAC 04:A1:6F:10:9D:8E
OUI Block 04:A1:6F:10:00:00/28
Registered To iENSO Inc.

This is a /28 block (only 16 addresses), indicating a small-batch OEM allocation specific to iENSO's embedded camera modules.

TLS Certificate (Port 443)

Issuer:  C=CA, ST=ON, O=iENSO, CN=evpass
Subject: C=CA, ST=ON, O=iENSO, CN=evpass
Validity: 2020-12-31 to 2030-12-29
Key: RSA 4096-bit
Self-signed, Version 1 (v1)

The CN evpass likely refers to iENSO's EVPaaS (Embedded Vision Platform as a Service) product line.


Web Interfaces

Port 443 / Port 80 - Full React Application

A minified React single-page application (main.0e1380ba.js, ~2MB). The HTML shell references a CCTV camera favicon:

<link rel="icon" href="/cctv-camera-icon.svg" type="image/svg+xml">

All CSS class names use the ienso- prefix (e.g., ienso-button, ienso-input, ienso-video__video-controls__play-button), confirming this is stock iENSO firmware.

Authentication is via POST to /system/login with JSON body:

{"username": "...", "password": "..."}

All API endpoints return {"error":"unauthorized","success":false} without valid credentials. Invalid credentials return {"error":"invalid credentials","success":false} - no username enumeration (same error regardless of username).

Port 8888 - Simplified Bootstrap Interface

A simpler login page titled "Device Login - iENSO" using Bootstrap CSS and jQuery. Uses the same /system/login endpoint with the same JSON format. Likely a legacy or maintenance interface.


API Endpoints (Discovered via JS Bundle Analysis)

All endpoints require authentication. Extracted from the minified React bundle on port 443:

Authentication

Method Endpoint Description
POST /system/login Login (JSON: username, password)
POST /system/logout Logout

Streaming

Method Endpoint Description
GET /api/stream/settings Stream configuration
GET /api/stream/settings/options Available stream options
GET /api/stream/is_rtsp_up Check if RTSP is enabled
POST /api/stream/start_rtsp Enable RTSP streaming
POST /api/stream/start_webrtc Start WebRTC stream
POST /api/stream/start_webrtc_signaling WebRTC signaling
GET /stream/demo/channel/0/webrtc WebRTC stream path

Snapshots & Recording

Method Endpoint Description
POST /api/snapshot/perform Take a snapshot
GET /api/snapshot/list List saved snapshots
GET /api/snapshot/settings Snapshot configuration
GET /api/snapshot/settings/options Snapshot options
POST /api/recording/start Start recording
POST /api/recording/stop Stop recording
GET /api/recording/state Recording status
GET /api/recording/list List recordings
GET /api/recording/diskinfo Local storage info
POST /api/recording/remove_all Delete all recordings
POST /api/recording/remove_selected Delete selected recordings
GET /api/recording/download_zip Download recordings
GET /media/download Download media files

Camera & Image Settings

Method Endpoint Description
GET /api/camera/settings Camera configuration
GET /api/image/settings Image settings (brightness, contrast, etc.)
GET /api/image/settings/options Available image options
POST /api/image/zoom Digital zoom control
GET /api/day_night_mode Day/night mode status
GET /api/day_night_mode/options Day/night options

Network

Method Endpoint Description
GET /api/network/ip IP configuration
GET /api/network/properties Network properties
GET /api/network/settings Network settings

Device Management

Method Endpoint Description
GET /api/system/info System information
GET /api/system/logs System logs
POST /api/system/reboot Reboot camera
POST /api/system/reset Factory reset
POST /api/system/format/memory Format local storage
GET /api/device/settings Device configuration
GET /api/device/settings/schema Device settings schema
GET /api/device/time Date/time settings
GET /api/server/settings Server configuration
POST /api/settings/reset Reset all settings

User Management

Method Endpoint Description
POST /api/user/change_password Change admin password

AI / Analytics

Method Endpoint Description
GET /api/data-processing/settings AI/analytics configuration

Firmware

Method Endpoint Description
POST /api/firmware/update Upload firmware

SDK / App Runner

Method Endpoint Description
POST api/app-runner/deploy Deploy SDK application

UI Feature Set (from JS String Extraction)

The web interface includes controls for:

  • Live View - WebRTC-based live streaming with play/pause, snapshot, and fullscreen
  • RTSP Streaming - Toggle to enable RTSP with a URL displayed in the UI (note: "When RTSP is enabled, Live View stream is paused")
  • Recording - Start/stop recording, file browser, preview, download (ZIP), delete
  • Snapshots - Capture and manage snapshots
  • Image Settings - White balance, brightness, saturation, sharpness, contrast, hue, noise reduction, dewarp, HDR, auto exposure, AE metering mode, gain, exposure time
  • Day/Night Mode - Automatic IR switching
  • Mirror / Flip - Image orientation (with warning: "if AI is enabled at the same time, data labels will appear reversed")
  • AI Detectors - Motion detection with configurable zones/sensitivity, object detection (people, vehicles)
  • Network Settings - IPv4 (DHCP/static), DNS, gateway, subnet, hostname, MTU, HTTP/HTTPS ports
  • User Management - Change administrator login (username + password)
  • Firmware - Version info, firmware update upload, restart, restore, factory reset
  • Storage - Local storage status, capacity, format
  • Logs - Downloadable system logs

Password Requirements (for password changes via UI)

These are client-side validation rules on the change-password form. The factory default password may or may not conform to these:

  • Minimum 10 characters
  • At least 1 lowercase letter
  • At least 1 uppercase letter
  • At least 1 number
  • Cannot match the username

What Hasn't Worked

Credential Guessing

The following username/password combinations have been tried and all return {"error":"invalid credentials","success":false}:

Usernames tried: admin, root, evpass

Passwords tried: admin, Admin1234567, Password1234, Evpass123456, evpass, iENSO, ienso, password, whisker, plus various MAC-derived and serial-derived combinations.

Other Approaches

  • Unauthenticated API access - All endpoints return {"error":"unauthorized","success":false}
  • WebRTC demo path - /stream/demo/channel/0/webrtc returns the SPA shell (React router catch-all), no unauthenticated stream
  • RTSP probing - Port 554 is closed; ffprobe to various RTSP URLs returns connection refused
  • Full port scan - Only 80, 443, 8888 open across all 65,535 TCP ports

Hardware Details

  • Platform: Ambarella CV-series SoC (iENSO's EVPaaS uses Ambarella exclusively for their vision modules)
  • Web Server: lighttpd 1.4.66
  • Frontend: React SPA (minified, ~2MB bundle)
  • Secondary UI: Bootstrap 5 + jQuery 3.6.0
  • OS: Embedded Linux (assumed, based on lighttpd + BusyBox conventions in similar Ambarella platforms)

Next Steps

  1. Contact iENSO - Small 37-person company in Richmond Hill, ON. Contact: Vision@iENSO.com / 905-763-6938. Ask for default credentials on the EVPaaS local web interface.
  2. Contact Whisker support - Ask about local camera interface credentials. Likely to deflect, but worth trying.
  3. Physical disassembly - Look for labels on the camera module board, or UART debug pads. Ambarella SoCs typically expose a debug UART. A USB-UART adapter + serial console would yield a root shell and direct access to the credential store.
  4. Firmware extraction - If a firmware update file can be obtained (via the update mechanism or intercepted OTA), it could be unpacked to extract credentials or password hashes.
  5. MITM the cloud connection - The camera communicates with Whisker's cloud for streaming. Intercepting this traffic might reveal auth tokens or provisioning data, though it's likely TLS-pinned.

Why This Matters

The Litter-Robot 5 Pro's camera is a fully capable IP camera with local RTSP streaming, recording, AI detection, and a complete web management interface - all running on your local network. Whisker charges a subscription (Whisker+) to access basic camera features through their cloud, but the hardware is capable of operating entirely locally.

If the login credentials can be determined:

  • RTSP streaming can be enabled and pointed directly at home NVR software (Frigate, Blue Iris, etc.)
  • Local recording to the camera's onboard storage becomes available
  • AI detection (motion, people, vehicles) can be configured independently
  • Image tuning (exposure, white balance, HDR, etc.) can be adjusted
  • No cloud dependency - everything runs on the LAN

This would make the LR5 Pro camera a first-class citizen in a self-hosted smart home setup, which is exactly what the Home Assistant / Frigate community has been asking for.


Contributing

If you've made progress on this or have an LR5 Pro you'd like to investigate, please comment below. Key things that would help:

  • Default credentials for iENSO EVPaaS camera modules
  • UART pinout for the LR5 Pro camera board
  • Firmware dumps or update files
  • Packet captures from the Whisker app during camera onboarding
  • Any response from iENSO or Whisker regarding local access

Last updated: April 16, 2026

@eyanric
Copy link
Copy Markdown

eyanric commented May 9, 2026

## MITM & Local Interface Research — May 2026

**Device:** Litter-Robot 5 Pro, iENSO EVPaaS embedded camera module  
**Camera MAC:** `04:a1:6f:10:32:7c` · **Fixed LAN IP:** `192.168.1.199`  
**Serial:** `LR5-02-40-01-2603-252AFE`

---

### Confirmed Architecture

The camera runs a full Linux stack (NetworkManager, not BusyBox) on an **Ambarella CV Series SoC**. It exposes three HTTP surfaces:

| Port | Server | Interface |
|------|--------|-----------|
| 80 | lighttpd 1.4.66 | React SPA (main app) |
| 443 | lighttpd 1.4.66 | Same React SPA over TLS |
| 8888 | lighttpd 1.4.66 | Bootstrap + jQuery admin panel |

The TLS cert on port 443 is self-signed: `CN=evpass, O=iENSO, C=CA, ST=ON`, RSA 4096-bit, valid Dec 2020–Dec 2030, Version 1 (no SANs).

All three surfaces share the same backend and authentication. Login is:

POST /system/login
Content-Type: application/json

{"username": "...", "password": "..."}


Success → session cookie. Failure → `{"error":"invalid credentials","success":false}` with HTTP **400** (not 401 — minor oddity).

Every API endpoint returns `{"error":"unauthorized","success":false}` with HTTP **401** without a valid session. No unauthenticated endpoints were found on any port.

---

### Credential Provisioning — How It Works

Credentials are **not static defaults**. They are dynamically generated and pushed to the device by `watford.ienso-dev.com` (iENSO's provisioning backend, hosted on AWS CloudFront at `13.226.209.x`) during the factory onboarding flow.

Confirmed provisioning sequence (from packet captures):
1. Factory reset → DHCP
2. Connectivity check → `nmcheck.gnome.org`
3. TLS 1.2 → `watford.ienso-dev.com` (device registration)
4. NTP sync
5. TLS 1.2 → `watford.ienso-dev.com` (credential provisioning — **this is where username/password are sent to the device**)
6. Bluetooth confirmation to phone app

Password policy (extracted from React SPA bundle `/static/js/main.0e1380ba.js`):
- Minimum 10 characters
- At least 1 lowercase, 1 uppercase, 1 number
- Cannot equal username

This means the provisioned password is a randomly generated string meeting these rules. It is unique per device and per provisioning event.

---

### MITM Attempt — TLS Certificate Validation

We attempted to intercept the provisioning connection from the camera to `watford.ienso-dev.com` using mitmproxy in reverse proxy mode, combined with DNS poisoning to redirect the camera to our machine.

**DNS setup:**
- Custom Python DNS server (`dnslib`) served `192.168.1.21` for `watford.ienso-dev.com` A queries and returned empty NOERROR for AAAA queries (to kill IPv6 escape via CloudFront's AAAA records — this is critical, CloudFront returns ~8 real IPv6 addresses alongside any custom A record and the camera will use them preferentially if available)
- UniFi DHCP DNS Server set to `192.168.1.21` so the camera queried our server directly

**Issues encountered and fixed:**
- Initial mitmproxy config used `reverse:https://watford.ienso-dev.com` while our machine's DNS also resolved that hostname to `192.168.1.21` → upstream loop, 30+ self-connections, nothing logged. Fixed by removing the UniFi Local DNS Record so our machine resolved via real DNS while the camera resolved via our poison.
- CloudFront AAAA records caused the camera to bypass our IPv4 proxy via IPv6 in early attempts. Fixed by blocking AAAA in the custom DNS server.

**Result:**

The camera connected to our proxy (confirmed via TCP `ESTABLISHED` / `TIME_WAIT` states from `192.168.1.199:443`), but **rejected our mitmproxy-issued certificate**:

[TLS] upstream TLS OK ← mitmproxy reached real CloudFront fine
[TLS] camera connecting ← camera hit our proxy
[TLS] camera TLS FAILED ← camera rejected our CA cert


The camera performs standard TLS certificate chain validation against its system CA bundle. mitmproxy's dynamically generated cert (signed by its own self-signed CA) is not trusted. **This approach is definitively blocked without the ability to add a trusted CA to the camera's filesystem** — which requires the very access we're trying to get.

Cert pinning against `watford.ienso-dev.com`'s specific leaf cert was not confirmed (we can't distinguish pinning from standard CA validation at this level), but either way the result is the same.

---

### Local API Surface — Full Endpoint Map

Extracted from `assets/js/settings.js`, `system.js`, `login.js`, and the React bundle:

**Port 8888 (jQuery panel):**

POST /system/login
POST /system/logout
GET /api/camera/settings
GET /api/camera/settings/options
GET /api/day_night_mode
GET /api/day_night_mode/options
GET /api/device/settings
GET /api/device/settings/schema
GET /api/device/time
GET /api/image/settings
GET /api/image/settings/options
GET /api/network/properties
GET /api/network/settings
GET /api/server/settings
GET /api/snapshot/settings
GET /api/snapshot/settings/options
GET /api/snapshot?tm=
GET /api/stream/settings
GET /api/stream/settings?stream=recording
POST /api/system/sleep {"sleep_type":"coldboot","duration_ms":}


**Port 80/443 (React SPA):**

POST /system/login
POST /system/logout
GET /api/stream/is_rtsp_up
POST /api/stream/start_rtsp
POST /api/stream/start_webrtc
POST /api/stream/start_webrtc_signaling


All endpoints require an authenticated session. No unauthenticated endpoints, no path traversal (lighttpd blocks it), no user creation bootstrap endpoint, no password reset path accessible without auth.

**Notable:** Static JS files (`/assets/js/*.js`) are served without authentication — only the HTML pages redirect to `/login.html`. This is useful for endpoint discovery but doesn't give access.

---

### Dead Ends — Save Yourself the Time

| Approach | Why it fails |
|----------|-------------|
| Default/common credentials | Credentials are device-unique, dynamically provisioned |
| Serial/MAC-derived passwords | No correlation found; provisioning server generates random passwords |
| MITM `watford.ienso-dev.com` | Camera validates TLS CA chain; rejects mitmproxy cert |
| IPv6 bypass of DNS poison | Camera prefers AAAA records; CloudFront returns ~8 of them. Must also block AAAA at DNS level |
| Unauthenticated API endpoints | None exist; every endpoint returns 401 without valid session |
| Path traversal on lighttpd 1.4.66 | Blocked; no known relevant CVEs |
| Phone-side MITM | Litter-Robot app pins `watford.ienso-dev.com` cert |

---

### What Might Still Work (Not Attempted)

1. **UART console on the Ambarella board** — The CVx series consistently exposes a 3.3V UART debug port. This would give root shell access and bypass all of the above. Requires opening the unit and identifying the header pads. Not pursued here due to hardware risk.

2. **Android app credential extraction** — The LR5 app authenticates to the camera to display live view. The credentials are likely cached in the app's local storage (SharedPreferences or SQLite). On a rooted Android device, or using Frida to bypass SSL pinning, these could be extracted without touching the camera hardware.

3. **Firmware extraction via USB** — Ambarella devices support firmware recovery/update via USB. If a firmware image is available or can be extracted, the credential generation logic and any hardcoded seeds would be visible.

4. **iENSO/Whisker support channel** — This is a B2B platform. There may be a legitimate support path for OEM partners or customers to get local interface access.

---

### Environment Notes

- The camera's DNS server assignment comes via DHCP from UniFi. UniFi's "Local DNS Record" feature only adds an A record — it does **not** suppress the real AAAA records that UniFi's resolver still returns. You must set the DHCP DNS Server to your own resolver (not just add a local record) and block AAAA in that resolver to prevent IPv6 bypass.
- The camera responds on `nmcheck.gnome.org` for connectivity checks — standard NetworkManager behavior, confirms full Linux stack.
- lighttpd serves the web UI; the backend application (likely a custom iENSO binary) handles all `/api/` and `/system/` routing and session management.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment