Rob Sears bio photo

Rob Sears

       

Rocket scientist. Computer hacker. Geek before it was cool.

BTC Donations:
1AU9qGkSubhR24r8Y4WEoV8bccZjeT2dKg

A while back, I picked up some Wyze Cam V2 units on clearance at Micro Center. They’re pretty sweet little network cameras; tiny, chic, and 1080P streaming video in day/night conditions, all for the low price of around $20/each with no subscription necessary. They’re dope:

I picked them up for $15/each, thinking I’d do… something with them. Maybe a robotics project, maybe home security, or maybe just to fill up the boxes of gadgets and gizmos I’ve marked for hacking on Some Day™.

I finally pulled the cams out this spring when I started hacking on OpenCV again. I had the fever dream of building a cluster of Raspberry Pis that analyze video streams from security cameras, perform object detection, and trigger automations in Home Assistant.

RTSP on Wyze Cam V2

I was all stoked to starting piping hot fresh RTSP video to my cluster, only to learn that feature was deprecated by the Wyze team:

I searched high and low online, and found plenty of message board threads and other documentation about enabling RTSP, but the firmware itself wasn’t readily available for download. It had been scrubbed from the site altogether.

But not from the Internet Archives lol:

And as luck would have it, the download link is still good! I was able to pull it down locally and unzip it

wget https://download.wyzecam.com/firmware/rtsp/demo_v2_rtsp_4.28.4.49.bin.zip

unzip demo_v2_rtsp_4.28.4.49.bin.zip

# In case you want to see the hash of the ZIP file:
md5sum demo_v2_rtsp_4.28.4.49.bin.zip 
3bc249b4a2fe456ef52da8f0f352306f  demo_v2_rtsp_4.28.4.49.bin.zip

# And the hash of the firmware
md5sum demo_v2_rtsp_4.28.4.49.bin   
cdc70f0ee13386460ab6f0e7ea579e24  demo_v2_rtsp_4.28.4.49.bin

I was able to flash this to the cameras and then follow the normal instructions for setting up RTSP.

The downside of this is that it basically voids the warranty (when does hacking NOT do that?), and you’re not really able to take advantage of new features and other cool stuff the Wyze team is building. That’s fine for my purposes, since I want to offload any AI/ML workloads to the OpenCV stack on my RPi cluster anyway. But be warned.

Now What?

After playing around with the cameras for a bit, I realized something troubling: they are constantly streaming data to remote servers. That itself isn’t a problem, because the cameras are designed and advertised to give you remote access and cloud storage. So of course they’re streaming data 24/7.

What I don’t like is that there’s no way to turn those features off. That’s a privacy concern IMHO because the video data is always sending back to a third party and I have no way of knowing what they’re doing with it. It’d be one thing if I could configure the camera to push my video feeds to a NAS or even an encrypted S3 bucket or something instead of Wyze’s cloud. But going to a 3rd party who may or may not be eavesdropping or inspecting feeds to train ML models, build advertising profiles, etc? No thanks.

Besides, with the RTSP feed alone, I can always use the RPi cluster as a proxy that records the video stream to a NAS device or S3 bucket. So I need to find a way to let the camera stream RTSP to my local network, and nothing else. I’ll let a proxy service decide where the video goes when the cluster is done with it.

IPTables

Most modern internet routers aren’t just switches, they’re also firewalls. They come with a lot of basic settings in place, and usually offer some kind of UI you can access over the web that provides a very limited ability to add/change how it behaves. The software most commonly deployed for the firewall stuff is a tool called iptables.

IPTables is a Linux-based utility that gives sysadmins fine-grained control over packet filtering. It’s been around a long time, is fast and stable, and can do a lot of amazing things. Fortunately, when my ISP set up my internet line, I tossed the garbage router they supplied me with, and replaced it with a better router that I flashed with a flavor DD-WRT.

I was able to configure the router with a temporary SSH daemon and generate a public key that let me SSH into the router and play with iptables. I’d write more about this, but it’s so device specific and esoteric that it’s just not worth it.

With iptables, I can tell the router to drop any packets from the Wyze cameras to any host outside the local network. It’s a little wonky though, and the best way to approach it is to show the iptables commands and then explain what’s happening:

iptables -t filter -N WyzeCameras

# Repeat these two lines for each camera
iptables -t filter -I WyzeCameras -m mac --mac-source XX:XX:XX:XX:XX:XX -j DROP
iptables -t filter -I WyzeCameras -m mac --mac-source XX:XX:XX:XX:XX:XX -d 192.168.1.0/24 -j ACCEPT

iptables -t filter -I FORWARD -j WyzeCameras

The first line creates a new chain called WyzeCameras on the filter table. The filter table is like the main security guard for the firewall. Like a bouncer at a club, this table is where decisions about who goes in or out is made.

A chain on a table is simply a collection of rules, where packets matching some pattern are forwarded on to some target. The filter table has 3 default chains: INPUT (packets received by the router), OUTPUT (packets sent by the router) and FORWARD (packets passing through the router).

Using the FORWARD chain for this purpose would be fine, but it had a lot of stuff already, and I think it’s a lot cleaner and easier to manage if I have a custom chain to hold all the Wyze cameras.

The next two lines create rules on the WyzeCameras chain. The -I parameter tells iptables to insert the rule at the top line, so that iptables -t filter -I WyzeCameras means “insert this rule at the top of the list of rules on the WyzeCameras chain in the filter table.”

The -m mac setting loads the MAC address-matching module. I want to be able to match packets by originating MAC address rather than IP address, because the latter can change for a variety of reasons, and I don’t want the cameras to get unblocked after a reboot or something. True, a lot of routers allow you to reserve IP addresses for specific devices, so I could associate an IP to a MAC address in the router UI, and then have my IPtables rules match against source IP. But that wouldn’t make things easier so why bother?

--mac-source XX:XX:XX:XX:XX:XX does about what you’d expect. It means “match a packet originating from this MAC address.”

-j DROP means, simply: “drop any matching packet; do not pass Go, do not collect $200.”

The next line is identical up to matching the MAC address, except it adds -d 192.168.1.0/24, which means “the packet originates from this MAC address and is destined for a device on the 192.168.1.0/24 CIDR block (ie, the local network).”

Finally, the -j ACCEPT bit just means “let this packet through!”

When you run these two lines in sequence, the WyzeCameras chain ends up with a list of rules that looks like this (note the first line entered is now last in the list):

Chain WyzeCameras (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     all  --  *      *       0.0.0.0/0            192.168.1.0/24       MAC XX:XX:XX:XX:XX:XX
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            MAC XX:XX:XX:XX:XX:XX

What this says is: “If the packet comes from any IP address with the MAC address of (whatever) and is destined for any host in the local network, then let it pass. If the packet comes from any IP address with the MAC address of (whatever) and is destined for any host anywhere, then drop it.”

When IPTables evaluates a packet, it will only take action on the first match. That’s why the order of this table is so important. If these rules were swapped, then every packet from the cameras would be dropped, regardless of destination. But in this order, they will pass if they go to the local network, and drop otherwise.

Boot Scripts

The thing about IPTables is that any changes you make to the rules aren’t persisted to disk unless you explicitly save them. If you reboot for some reason, your rules are gone. Ever had to reboot a router? Yeah, it happens. And if it happens to me, my Wyze cameras will suddenly go back to broadcasting my feeds to the cloud. Gotta fix that.

For most distros, you have a command called iptables-save which will let you write your rules out to a file like /etc/iptables.conf. So something like sudo iptables-save > /etc/iptables.conf. But for my router, where this is all happening, that’s not an option for whatever reason.

My router has some limited flash memory (JFFS) that can persist across restarts, and it lets me add some special boot scripts to this area. If I create a file called /jffs/scripts/firewall-start, it will run the script at boot, once iptables is up and running. So that’s what I did:

# List of Wyze camera MAC addresses:
# (need to do it this way because it's a Bourne
# shell, not Bash, so we don't have arrays):
set "XX:XX:XX:XX:XX:XX" \
    "YY:YY:YY:YY:YY:YY" \
      "ZZ:ZZ:ZZ:ZZ:ZZ:ZZ"

# Create a chain just for the Wyze cameras:
iptables -t filter -N WyzeCameras

# Iterate over the list of MAC addresses
for mac in $@; do
  # Add the rules (last line will be first rule due to -I flag):
  iptables -t filter -I WyzeCameras -m mac --mac-source ${mac} -j DROP
  iptables -t filter -I WyzeCameras -m mac --mac-source ${mac} -d 192.168.1.0/24 -j ACCEPT
done

# Make the cameras the first rules evaluated, because they have
# terminating targets
iptables -t filter -I FORWARD -j WyzeCameras

This way if I ever add another camera, I can simply boot it up, flash the RTSP firmware and set up all the things, grab the MAC address and just add it to this script on the router. Then reboot the router, and it will automatically start blocking cloud access to the new camera.

Final Steps

I was able to confirm the RTSP feeds worked on my local network using VLC. I couldn’t use the Wyze app to see the streams because (I guess) there’s some kind of STUN service or whatever in place that requires access to a remote server to establish the local connection. I did some other network analysis and confirmed that, yup, the video streams aren’t escaping the home network.

It’s a bummer that the Wyze app doesn’t work even within the local network, because otherwise I could always set up a VPN connection to my home network and remotely manage cameras and see feeds that way. It’s possible that some more work on IPTables would let me write a rule that would allow limited, whitelisted access to a Wyze STUN/TURN service. But that’s a lot of work and only based on a guess about what they’re doing. It would be so much easier to use VLC over VPN to look at the feeds.