Thursday 23 February 2017

Reverse Engineering VstarCam C95


I got this cheap little toy from eBay to replace an older dedicated LCD system that wasn't working right.  It automatically makes a reverse proxy into your network and it's running a bunch of sketchy services and it's another IoT device I did not trust on my LAN with internet access.  (In case you haven't noticed a trend I don't like IoT devices to connect to the internet)

I connected and setup this unit with the Eye4 application via the "playing a sound method" (where your WiFi SSID and password are encoded into an audio clip the device can interpret) to add it.  The device would not support a hidden SSID nor would it support a WPA2 PSK with any symbols in it (It took me an hour to figure this out as it acted like everything was fine but it just would not accept the WiFi settings when the sound played)

After it was online everything worked great.  I could access the camera and intercom and the doorbell button would sometimes play a ringing on my phone.  I wanted to do more with this device and I wanted it off the internet however.

The device runs a telnet server but Vstar will not give the root password and none of the usual suspects work but the fact that it has a hardcoded "secret" root password is bad, who knows what else this thing is doing behind your back...

It did not want to work reliably if it couldn't setup it's tunnel and it would not do anything when you pressed the button on it, something had to be done!

It will also try to use google's public dns at 8.8.8.8 but having it's gateway as a server instead of the router will stop it from getting to the internet.

After some Wireshark I logged into the unit's HTTP interface and set the DNS and Gateway (Setup -> Basic Network) to one of my servers.

I setup a maraDNS server on said server with the following configuration:

mararc:
csv2["eye4.cn."] = "eye4.txt"
csv2["vstarcam.com."] = "vstar.txt"
csv2["baidu.com."] = "baidu.txt"
csv2["windows.com."] = "windows.txt"

eye4.txt:
s2.% 192.168.x.xxx ~
device-abnormal.% 192.168.x.xxx ~
authentication.% 192.168.x.xxx ~
ntp2.% 192.168.x.xxx ~
ntp.% 192.168.x.xxx ~
push.% 192.168.x.xxx ~

vstar.txt:
s3.% 192.168.x.xxx ~

windows.txt:
time.% 192.168.x.xxx ~

baidu.txt:
www.% 192.168.xx.xxx ~

This would direct all the standard DNS queries to my server instead of their internet counterparts, however it's INITIAL query is a HTTP request to a webbased DNS resolver with a hard-coded IP address which would thwart the whole process...

So I added the IP of that DNS resolver (119.29.29.29) to my servers network card.

I added this to my Apache config as it uses a few different ports:
Listen 8087
Listen 119.29.29.29:80
Listen 808

# =================================================
# Fake Web to DNS Server thing for doorbell
# ===============================================
NameVirtualHost 119.29.29.29:80
<VirtualHost 119.29.29.29:80>
DocumentRoot "c:/doorbell"
<Directory "c:/doorbell">
Allow from all
Order allow,deny
</Directory>
</VirtualHost>

# =================================================
# Doorbell Alarm Port
# ==============================================
NameVirtualHost *:808

<VirtualHost *:808>
DocumentRoot "/doorbell"
AliasMatch ^/(.*)$ c:/doorbell/push.py
<Directory "c:/doorbell">
Allow from all
Satisfy Any
Options Includes ExecCGI
</Directory>
</VirtualHost>

NameVirtualHost *:8087
<VirtualHost *:8087>
DocumentRoot "/doorbell"
<Directory "c:/doorbell">
Allow from all
Satisfy Any
</Directory>
</VirtualHost>      

You will also need an SSL site setup with the following entry:

<Directory "c:/www/GetToken">
Allow from all
Satisfy Any
</directory>


When the unit boots the first thing is does is makes two HTTP requests:
GET http://119.29.29.29/d?dn=authentication.eye4.cn
GET http://119.29.29.29/d?dn=push.eye4.cn
Therefore c:\doorbell\d contains the IP address of my server 192.168.x.xxx and nothing else this will then direct the camera to connect to my server for it's web and auth requests.

The next stage of the process is to request a token from "the authentication server". Since it's using my server for DNS and HTTP DNS I have given my servers IP as the answer to those DNS entries.

And it then sends this HTTPS request to my server (where XXXX will be unique to your camera and account you setup when you added it to eye4:
GET https://authentication.eye4.cn/GetToken/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXC95/VSTC/VSTXXXXXXXXXXXX/48.61.72.75/1

I created the path for /GetToken/XXXXX.../VSTC/VSTXXX/etc but you can use an aliasmatch if you want to do it the easy way.  The only part of that path that will ever change is the number at the end it could be 1 or 2 or 3, I've never seen it go above 5 so I made the files 1-5 in that directory with all the same contents:

# = digit, L = Letter. It shouldn't matter what you put in here, this is telling the camera what token and user ID it will use when sending data to the remote server. I included the formatting just incase the camera cares.  You can also use the GetToken string your camera sends you to get your actual Token by browsing to the URL on your PC and then putting the response in a text file.

{"token":[{"class":"E","access_token":"VSTB######LLLLL:##LL##L###LLL###L#L#L##
##LL#L#"}],"errcode":0,"push":[{"user":"#######","name":"Doorbell1"}]}

At this point the camera is happily registered and will occasionally send UDP packtes to your server which will just get dropped. This doesn't seem important...

The Eye4 app will still work locally at this point and you can integrate it with your own DVR software that supports ONVIF or MJPEG.

Now for the fun part, when someone presses the Doorbell button...
The unit will connect on port 808 and make a HTTP request to some long string including the token given above.
The alias match will force rediect it to run push.py which is the script shown below.
My script uses the Pushover API as it is way more reliable than their built in app, however the script will show you how to register to their API server and send push messages to the Eye4 app running on your phone.  You can add whatever other events you want to the code when someone pushes the button like turn on a porch light, send a text or anything.
I have added comments in-line to explain what it is doing.

If you don't want to use the eye4 server at all (meaning their app) your push.py script is very simple:

push.py:
#!c:\python27\python.exe -W ignore

import urllib2, time

# This will tell the camera/doorbell unit that the request was processed correctly.
print "Content-type: text/html\n\n"
print '{"message":"OK","errSubCode":"200","errCode":200}'

# this is where you would execute anything you want to happen when the doorbell is rung.
# You can do this very simply in PHP as well as you just need to return a string to the camera so it doesn't have an error.

If you do want to use the eye4 app and their push server this code will do that...
push.py:
#!c:\python27\python.exe -W ignore

# This program is called when someone presses the doorbell button on my front door
# Eye4 subroutine will use the eye4 server to relay the message so the Eye4 app will ring

import urllib2, time


def eye4():
# use eye4 server to send doorbell notice

# Get token - You will need to replace the # and L with your cameras ID by looking at your HTTPD logs when it calls GetToken on startup.
req = urllib2.Request('https://authentication.eye4.cn/GetToken/#LL#L#LLL####L#L#######LL#L#LLL##L###C95/VSTC/VSTL######LLLLL/48.61.72.75/1')
response = urllib2.urlopen(req)
the_page = response.read()

# The VSTL## etc should match the VSTL## in the GetToken.  This parses out the token used for requests.
token = the_page.split('VSTL######LLLLL:')[1].split('"')[0]

# Generate the current time/date stamp that will show up on your phone.  This uses real-time but you can make up your own as long as it's in the correct format.
datestr = str(time.localtime()[0])+str(time.localtime()[1])+str(time.localtime()[2])+str(time.localtime()[3])+str(time.localtime()[4])+str(time.localtime()[5])

# create the GET request to send the message to the server so it can push to your phone
# Again you need to edit the VSTL######LLLLL part with the info from above.
# the ####### at the end of the request will be the account user ID. You can find this in the Token file at ("user":"#######")
# You can change Front%20Door to anything you want this will show up under the bell on the Eye4 app.

url = 'http://push.eye4.cn:810/push/VSTC/'+datestr+'/10/20/0/0/VSTL######LLLLL:'+token+'/Front%20Door/#######/0'

# Send Ring notice
req = urllib2.Request(url)
response = urllib2.urlopen(req)
the_page = response.read()
if the_page.find('OK') != -1:
return 0
else:
print the_page




# This will tell the camera/doorbell unit that the request was processed correctly.
print "Content-type: text/html\n\n"
print '{"message":"OK","errSubCode":"200","errCode":200}'

# This is where you can call any other scripts you want when someone rings the bell.
try:
# Send the message via eye4 app
eye4()
except:
pass


If for some reason the camera gets a message that's abnormal it will make a HTTP request to your server to the abnromal script to report an error.  The request will contain the problem.  I don't have a sample of this handy as the above should stop it from ever entering an abnormal state.  If however it should get there you need to fix the problem before it will send alarm/push requests and there may be issues accessing the camera with Eye4 on the network.

You can also use webcam viewer apps on your phone to access this unit even ones that are not Eye4, however you may not be able to talk to the unit unless you use Eye4.  The profile VStarcam C7816 works best if your app supports it that will get you video and listen and talk will work but it will not sound right.

I have yet to work out how the audio part works, I may at some point in the future work on that...

Hopefully this helps anyone that wants to do more with this neat little camera or wants to use it locally with a DVR rather than have it make holes in your network.

As a site note the App makes you change the admin password after installation (I believe it's 888888 by default) The web interface has a bug in the code that will only allow your password to be 3 characters consisting of a letter and 2 numbers. I have written a proxy server that will remove that Javascript code while leaving the rest of the interface intact if anyone wants it post a comment.  This will allow you to change the password without Eye4 and will allow you to add the Operator and Visitor users.

5 comments:

  1. I'm desperately trying to receive a video-stream from the C95 in VLC-Media-Player.

    To get hold of the stream-adress I captured some traffic between the Eye4-PC-Client and the C95 and got:
    GET /livestream.cgi?streamid=10&substream=0&loginuse=admin&loginpas=888888&user=admin&pwd=888888&

    The data packets let me also assume, that the video-stream is encoded as h.264.

    However I was not able to connect to the stream and play it in VLC-Media-Player.
    I tried:
    http://192.168.178.49:10197/livestream.cgi?streamid=10&substream=0&loginuse=admin&loginpas=888888&user=admin&pwd=888888&

    rtsp://192.168.178.49:10197/livestream.cgi?streamid=10&substream=0&loginuse=admin&loginpas=888888&user=admin&pwd=888888&

    udp://192.168.178.49:10197/livestream.cgi?streamid=10&substream=0&loginuse=admin&loginpas=888888&user=admin&pwd=888888&

    Any guiding thoughts?

    ReplyDelete
    Replies
    1. Hello, did you manage to open the video stream from VLC?

      Delete
  2. Thanks for these amazing instructions and information! Really interesting and well written. I've followed them for the most part with my c95. But stuck with Https and a valid certificate for my internal server for when the c95 does the token thing. I've done a self issued certificate but my nginx logs (using nginx instead of Apache) suggest that the c95 can't make the request. Having said that there is no explicit Https request in the logs. My "d" is working fine. Will keep plugging away, any help appreciated!

    ReplyDelete
  3. Ok I figured it out. It seems, or at least it is on my unit, that this GET is no longer made for authentication:

    And it then sends this HTTPS request to my server (where XXXX will be unique to your camera and account you setup when you added it to eye4:
    GET https://authentication.eye4.cn/GetToken/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXC95/VSTC/VSTXXXXXXXXXXXX/48.61.72.75/1

    Instead the C95 makes a POST encoded in java. Quite simple in the end - I just used your formatting for the token and had my web server reply to the POST with the token and it worked. Also I figured out that I need to run my SSL witha self signed cert for eye4.cn. All in all its quite a lot of work just to stop this thing calling back to China but its sensible and I learned about Maradns, self signed certs, nGrep and Pound server so it was definitely worth it!

    ReplyDelete
  4. Hello,
    Can someone explain where is the Wifi Module? Is it in the doorbell or in the indoor white unit?
    How the external doorbell is connected to the indoor unir? RF? What is the real range? There is any way to connect both units by cable?
    Thanks for all the details provided above.

    ReplyDelete