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
- Astrid Yu managed to dump the firmware for a different model, the Blink Mini, but hasn't quite cracked it yet.
- MattTW reverse engineered the cloud API and there are implementations in Python and in JS.
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.)
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
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
- Called from
- 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 inFirmwareUpdate.firmwareUpdate
, which is sent as a POST body inUpdateFirmwareTask
(along with signatures received by the server). - There are also references to
X-Blink-FW-Signature
andX-V2-Blink-FW-Signature
headers.
- Sending a body of
- 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 calledencrypted_session_key
which is then Base64-decoded and sent as the body with content-typeapplication/octet-stream
. If this succeeds, it moves on to doing something with SSIDs.
- With body
- 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
mentionssetQrCodeScan
—do I need to scan the QR code on the back of the BSM? That QR code contains the textG8T1LN0013620GFL
. Ignoring the hyphens, this matches the "DSN" string under it,G8T1-LN00-1362-0GFL
. It also matches the wifi SSID, which ends in0GFL
. - Class
SMEncryptionData
hasAES_INFO_STRING = "aesblinkob"
andHMAC_KEY = "hmacblinkob"
. There is an AES key and an HMAC key derived from a session insetSession_key
using a KDF. Where does the session come from, though? startProcessOfConnectingPhoneToDeviceViaWifi
looks juicy. References toautomaticOnboarding
. (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 aSyncModule
device type, so maybe I can ignore the others. onNetworkAvailable
callstryConnectionWithDevice
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
, viaOnboardingRedLightFragment$onViewCreated$1
-- for Lotus, not SyncOnboardingWaitingForBlueLightActivity
-- 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.
Jesse Chisholm says:
From my own network sniffing (and reading lots of frustrated people on blogs and forums) I deduce the following:
BSM on initial setup
BSM on power up:
I see no network traffic directly from the cameras through my WIFI router. Only the BSM has an IP to do that, not the cameras.
has 3 parallel tasks
All that assumes (BCM "Outdoor 4" and BSM 2) which is what I have. It further assumes you DO have a subscription for their cloud service.
If you configure the BSM for no-subscription off-line-mode, these things apply:
What I am not sure about yet is:
Does it still require that initial hit to the cloud?
Does it revert to mostly-useless off-line-mode?
Or do the no-subscription off-line-mode you configured it for?
I will find out when my 30-day free subscription expires. ;-)
The Blink docs are poorly written (from a technical viewpoint). Especially how often they conflate WIFI and INTERNET as meaning the same thing.
Jesse Chisholm says:
To continue my sniffing.
But, oddly, I see no network traffic after that from the BSM, by IP or MAC address.
Hmm.
Tim McCormack says:
Interesting, thank you!
I'm intrigued by the possibility of network boot. Probably requires signed code, but maybe there's a vulnerability.
Jesse Chisholm says:
I have confirmed that
On the SD card images/videos are stored
Jesse Chisholm says:
If you have a wifi wardrive app on your phone (e.g., wigle wifi) you can catch the mac addresses of the sync module and the cameras. The sync module is only one visible, and only briefly during initialization. But, search the database for 'blink%' to then find them all.
Then should be able to use wireshark to sniff network for those macs.