Work in progress: Reversing Blink cameras

I received some Blink outdoor security cameras a while ago but haven't been able to make use of them because they require a smartphone and internet access to set up, and I don't have a smartphone and don't want these things streaming video of my comings and goings to Amazon.

According to what I've read, I should be able to turn off cloud streaming and have the cameras instead stream to the sync module, which will store videos on a thumb drive. That's what I'd like to do, but without the cloud connection. Maybe I could set them up on a temporary wifi network and then remove their internet access again? But again, I don't have a smartphone. I'd have to borrow one.

In the meantime. I've decided to try reverse-engineering them to see if I can make them into something useful—and also just for the fun of it. I haven't succeeded, but I've made a little bit of progress, so I'm posting what I have in the hopes that someone else will be able to build on it.

I'll update if I learn more.

Background

What I have consists of three cameras and a sync module. The product is just named "Blink", or possibly "Blink outdoor", with no indication of generation numbers. (I saw a reference to a gen 4 release online, but haven't determined a timeline.) Everything is branded Amazon, but the hardware and software were actually made by Immedia Semiconductor, which Amazon acquired.

The cameras are supposed to be outdoors, powered by removable AA form-factor lithium batteries, and talk to the indoor sync module wirelessly. The sync module has a mini USB port for power and a regular USB A port to accept USB data storage.

The sync module broadcasts a wireless access point that your phone is supposed to connect to for onboarding. I believe it is then supposed to be reconfigured to connect to your home wireless in order to talk to Amazon/Immedia's servers.

Prior art

Teardown

I'm not going to be approaching this from a hardware angle, but of course I went poking around inside anyhow.

Sync module

The sync module (or "BSM") has a sticker on the back with the following information:

MODEL: BSM00300U

MADE IN CHINA

DSN: G8T1-LN00-1362-0GFL
MAC: 74:AB:93:95:23:BB
FCC ID: 2AF77-H1621502
IC: 20741-H1621502

SSID: BLINK-0GFL

(Some of that might be used for onboarding, or identifying of my location if someone scrapes wifi SSIDs, so in general these might not be safest things to share.)

Front of sync module case
The front of the sync module has a wifi LED indicator that turns blue, and a power indicators that can be red or green.
Back of sync module case
The back just has a sticker. The QR code here repeats the DSN information, but without hyphens.
Inside view of sync module from rear
Prying off the black panel on the front reveals this circuit board. I can see the antenna, LEDs, two USB ports, and reset button. There's a Zentel surface-mount chip.
Peeking under the antenna
Looking under the antenna, I see "blink" and "ISI-1503-SA SYNC REV A0"
Back side of sync board
The back side of the sync's PCB is dominated by a chip with a sticker on it reading "SYTA01K 01A840" and a matching QR code.
Chip without sticker
Removing the sticker, the chip is revealed to be an Atheros AR9331, which seems to be for wifi.

Camera module

I also opened up one of the three cameras. The model number BCM00400U starts with BCM; I infer that this means Blink Camera Module, in analogy to the sync module.

The back unscrews to reveal a battery compartment. There are bays for two batteries, a USB port (maybe for power?), what appears to be a reset button, and two rubber-capped screws. The sticker shows:

DSN: G8T1-GH00-1362-8NGQ
MAC: 74:AB:93:8D:2F:B1
FCC ID: 2AF77-H2041670
IC: 20741-H2041670
MODEL: BCM00400U
MADE IN CHINA
Inside the camera battery compartment
Opening the battery compartment reveals the info sticker. There is a QR code matching the DSN.
Full PCB assembly
Removing the underside of the battery compartment shows several PCBs. The closest, to the back of the device, I've termed the "sensor board". It is black and I can see the reset button and the USB port. It is soldered to another board at the top at right angles.
Sensor board front
The front of the sensor board includes the camera itself and what might be an infrared motion sensor. It connects to a parallel board via 10-pin and 18-pin headers at the bottom.
Peeking under the camera
I also carefully took a peek under the camera to see the CCD (and photograph some more printing).
Top of the antenna board
The top of the small board has what appears to be a serpentine printed antenna.
Underside of antenna board, #1
The antenna board also has the battery contacts. Notice the word "walnut"—this shows up in the software as well.
Underside of antenna board, #2
The other end of the underside of the antenna board.
Underside of antenna board, #3
A slightly different angle so that more printing is visible.
Underside of antenna board, #4
The rest of the underside of the antenna board, visible from the back of the unit. Amazon has stuck their name in here for some reason.
Network board, exposed
With the sensor board removed, I see the back of the network board. It has a chip marked CYW43438 in microscopic print, which appears to support several wireless technologies. There are also headers for the other board.
Network board back detail closeup #1
Closeup of the upper left of the network board.
Network board back closeup #2
Closeup of the middle left.
Network board back closeup #3
Closeup of the wireless chip.
Network board front view
Here's the front side of the network board, including a light sensor and the pressure contact with the speaker module.
Inside of the front of the camera case
The inside of the front of the camera case has several rubber seals. I *think* that's where the white thing on the left belongs—it fell out during disassembly. The speaker is at the bottom
Closeup of speaker
The speaker is mounted on a compressible foam and has a large seal around it.

Second camera

The second camera I looked at had the same information on its sticker except for the DSN (and its matching QR code) and the MAC address:

  • DSN: G8T1-GH00-1362-8MXQ
  • MAC: B4:E4:54:C2:7D:20

What's a little odd here is that these two cameras in the same box have MAC addresses with different vendor prefixes.

Poking at it over wifi

When I plug the sync module into USB power, it pulses its LEDs wifi=blue and power=red once, then goes to solid wifi=blue while it boots up, and finally solid power=green with blinking wifi=blue. (During boot, it will also briefly access a USB stick that is present, but does not write anything to it.) It is now broadcasting the BLINK-0GFL SSID that is listed on its sticker. When I connect to it, my laptop IP address is 172.16.97.108 and the BSM's address is 172.16.97.199.

Scanning the BSM with nmap (nmap -n -T4 -p 1-65535 172.16.97.199) finds that TCP ports 53 and 80 are open.

Scanning UDP ports (with -sU) is harder. It takes longer and gets stuck at about 1% of the way through the range. I think maybe the wireless connection drops after something in the module times out? Ports 53 and 67 are open to UDP but I didn't go beyond 3000.

Port 80 is an HTTP server with very terse HTTP/1.0 responses:

$ curl -sS -i http://172.16.97.199/
HTTP/1.0 404 Not Found
Connection: close
Content-Length: 0

And port 53 is a DNS server:

$ dig +short -x 172.16.97.199 @172.16.97.199
blink_sync_module.lan.
$ dig +short blink_sync_module.lan @172.16.97.199
172.16.97.199

Package reversing

I downloaded Blink Home Monitor 6.30.0 - Apkpure 2023-10-20.apk from https://apkpure.com/blink-home-monitor/com.immediasemi.android.blink on 2023-10-20. It's listed as version 6.30.0 from 2023-09-28, package com.immediasemi.android.blink and signature 65902c52c99a37502bdd4f342db2180c4c73b578.

I used the online tool http://www.javadecompilers.com/ to decompile it with JADX. I've attached just the immediasemi packages, but please bear in mind that if you are planning on writing alternative firmware or management software, exploring this source code may expose your implementation to intellectual property risk. Anything with a free license will need to be a cleanroom implementation, written only with a reverse-engineered API to refer to.

(I first used Apktool to convert the dex classes into Smali files, but they're pretty hard to read. JADX can mostly convert the Smali into Java, and leaves what it can't convert as commented-out Smali.)

The main code in the APK is under the package com.immediasemi.blink.

HTTP endpoints

Can I use the APK to find the right HTTP endpoints to talk to?

Even without decompiling, I found that classes6.dex in the APK contained a string referencing the BSM's IP address: http://172.16.97.199/api/set/app_fw_update. This means I should check for other instances of /api/ as well.

Checking for /api/ (using grep -P '(?<![a-z])/api/') reveals a number of other HTTP calls. However, some of them appear to be calls to the Blink cloud servers, not the BSM.

The most relevant code I've found so far is in SyncModuleService, an interface that defines HTTP endpoints for talking to the BSM:

  • GET /api/get_fw_version: Returns {"version":"4.2.9","encryption":"2"}
    • Called from AddDeviceViewModel
  • GET /api/logs: 401 Unauthorized request
  • GET /api/ssids: 401 Unauthorized request
  • GET /api/version: 404 Not Found
  • POST /api/set/app_fw_update
    • Sending a body of foo to /api/set/app_fw_update causes it to hang -- and not respond to more requests (until I power cycled it and reconnected to the WiFi, although there might be a shorter path).
    • OnboardingBaseActivity's getFirmwareUpdate does something curious—it concatenates JSON containing a serial number with what appears to be a firmware download from Immedia's servers. This ends up in FirmwareUpdate.firmwareUpdate, which is sent as a POST body in UpdateFirmwareTask (along with signatures received by the server).
    • There are also references to X-Blink-FW-Signature and X-V2-Blink-FW-Signature headers.
  • POST /api/set/key
    • With body foo: 420 Previous endpoint failed
    • AddDeviceViewModel.sendKeyToSm calls this, and it is used to send either of two different types of session key. This key is sourced from something called encrypted_session_key which is then Base64-decoded and sent as the body with content-type application/octet-stream. If this succeeds, it moves on to doing something with SSIDs.
  • POST /api/set/ssid
    • I'm guessing this is intended to tell the BSM the user's home wireless credentials.
    • With body foo: 420 Parsing set_ssid request failed

SyncModuleService is used from three places:

  • OnboardingBaseActivity probably checks the BSM to see if it's usable.
  • AddDeviceViewModel probably does the actual add-device flow, taking over from the initial onboarding.
  • EncryptionInterceptor is used to encrypt all requests to the device with AES-CBC except for a few hardcoded request paths. (isSecureRequest exempts /api/get_fw_version, /api/set/key, and /api/version.)

It's concerning that these are the only references. It might be that the phone app is only used for getting the BSM its connection to the cloud, and then after that the phone and BSM only communicate via the cloud. This could mean that firmware flashing is the only route to jailbreaking these things.

Onboarding

Probably the most effective way of reversing the API will be to step through the onboarding logic in the app. This will help us figure out keys and encryption as well.

Some leads:

  • Class OnboardingWaitingForBlueLightActivity mentions setQrCodeScan—do I need to scan the QR code on the back of the BSM? That QR code contains the text G8T1LN0013620GFL. Ignoring the hyphens, this matches the "DSN" string under it, G8T1-LN00-1362-0GFL. It also matches the wifi SSID, which ends in 0GFL.
  • Class SMEncryptionData has AES_INFO_STRING = "aesblinkob" and HMAC_KEY = "hmacblinkob". There is an AES key and an HMAC key derived from a session in setSession_key using a KDF. Where does the session come from, though?
  • startProcessOfConnectingPhoneToDeviceViaWifi looks juicy. References to automaticOnboarding. (There's a code branch for SDK > 29, maybe newer Android has easier APIs for something here.)
  • I see references to Lotus, Watson, Owl, and Hawk. Code names for products? They show up in DeviceType. They have different code paths. There's a SyncModule device type, so maybe I can ignore the others.
  • onNetworkAvailable calls tryConnectionWithDevice and those look enticing but might be irrelevant, as they call the /api/version endpoint that doesn't return anything.

2023-10-24: Update 2

I'm going to focus on /api/set/key since it appears to be performed before the other POST request (ssid).

The only call to /api/set/key is AddDeviceViewModel.sendKeyToSm. Recap: It gets a SyncModuleService instance and then checks if the encryption type is 1 or 2. This is probably the "encryption" field from /api/get_fw_version, which in my case is 2. It retrieves SMEncryptionData.getInstance().encrypted_session_key_v2 and Base64 decodes it, then sends it with a Content-Type of application/octet-stream. If this succeeds, it calls sendKeyToSmOnResult and the connection state machine moves on to send SSID info.

All this business of an "encrypted session key" is interesting. The protocol happens over insecure HTTP, so it has to have its own (probably dodgy) encryption system. I wonder if the QR code on the back is used to create a shared secret between the phone and the BSM (very low entropy, though). What's the session key? Maybe the user has to log into the cloud service first and they gain a session key, and then that session key is transmitted to the BSM so it can itself authenticate to the cloud service?

The only thing that sets encrypted_session_key_v2 is AddDeviceViewModel$startOnboardingSyncModule$1, called from startOnboardingSyncModule(onboardingType2, longValue, this.qrCodeScan, context);, in turn from AddDeviceViewModel.startOnboardingDevice. This involves a network ID (long), something about a QR code (boolean), and a Context of some sort. That can be called from either:

  • startOnboardingDevice$default, via OnboardingRedLightFragment$onViewCreated$1 -- for Lotus, not Sync
  • OnboardingWaitingForBlueLightActivity -- promising!
  • retryOnboardingWithNewStartCommand -- less promising, since it's a retry path

Via the blue light path, network is from Camera.getServerIdFromLocalId(longExtra) == longExtra & 72057594037927935L. longExtra is the device ID, or a default of 0. This is set to SMEncryptionData.getInstance().device_id by EnterWifiCredentialsActivity.goToBlueLightVisibleScreen. I don't see it being set in BSM code paths, so it might just be 0. So network ID is also 0, I think?

...but in any case, the encrypted_session_key_v2 is read from a Command object, and nothing explicitly sets that field. I think it must be set in AddDeviceRepository.m4550startSyncModuleOnboardingBWLJW6A, which I believe calls the Immedia servers with the serial number and returns a Command -- perhaps an object mapped from a server response.

So yes, it looks like the server exchanges a serial number for an encrypted session key. Gross.

Things to explore

  • There were some references to serial numbers in the onboarding or add-device code. Does the DSN form part of a shared key?
  • Check what the SSID POST body is supposed to look like.
  • If I manage to connect the BSM to wifi, what DNS and HTTP requests does it make? Can I MITM it? (What CAs does it trust?)

Other avenues

  • There are USB ports on the BSM and camera. Are they just for power, or is there a factory USB protocol?
  • When the BSM accesses a USB stick during boot, is it looking for firmware to load, or maybe config files?
  • Maybe just try blind fuzzing the BSM's HTTP and DNS servers.

No comments yet. Feed icon

Self-service commenting is not yet reimplemented after the Wordpress migration, sorry! For now, you can respond by email; please indicate whether you're OK with having your response posted publicly (and if so, under what name).