Capture tools for the Swann DVR4-1200 DVR (also known as DVR04B and DM70-D, manufactured by RaySharp), inspired by meatheadmike/swanntools which was developed for DVR8-2600. Whereas the latter's mobile stream script is compatible with the DVR4-1200 (for a 320x240 stream) its media port script (for a higher quality 704x480 stream) is not compatible with the DVR4-1200, hence the creation of this repository.
This repository is part of a long-term project to build a more secure system piggybacking off the DVR4-1200 with cloud backups and much better web/mobile clients.
Project status: Postponed due to a faulty DVR.
The below directory listing explains the structure of this repository.
. ├── src # Source files │ ├── client # Retrieves and forwards DVR camera streams to the server │ │ ├── client.go # Handles forwarding of streams to server │ │ ├── main.go # Helper functions for the client │ │ ├── main.go # Command line point of entry │ │ └── stream.go # Handles connection and receiving streams from the DVR │ ├── server │ │ ├── consumer.go # Performs actions on streams provided by client │ │ ├── helper.go # Helper functions for the server │ │ ├── main.go # Command line point of entry │ │ └── server.go # Handles listening to connections from client │ └── misc │ └── auth # Miscellaneous code to test the web panel login protocol of the DVR, │ │ # made redundant as the DVR authenticates camera streaming separately │ ├── authenticate.go │ └── authenticate_test.go ├── vulnscan │ └── zhuhai-raysharp-cve2015-8286.nse # NSE script for nmap to check IP(s) for affected devices ├── .gitignore ├── LICENSE.md └── README.md
The program requires a folder created with certificate files with file names
server.key. Navigate to a new folder of your choice and generate the required certificates for TLS by running:
openssl req -new -nodes -x509 -out client.pem -keyout client.key && openssl req -new -nodes -x509 -out server.pem -keyout server.key
Work in progress. Usage details are to be determined.
- Create a Go script which can authenticate with the DVR via its media protocol
- Add streaming of cameras to the script
- Receive a continuous stream of a single channel
- Receive a continuous stream of multiple channels
- Implement a TCP proxy
- Implement a client
- Implement a server
- Plan a method to stream the H264 stream to ~~AWS/~~Azure in order to transcode, store and stream video
- Plan must involve creation of an interface to allow creation of an AWS script
- The Azure/azure-sdk-for-go can be used to upload files to cold blob storage
- Create a web client to consume the H264 stream
- Create a script to transfer DVR HDD recordings (which are of much higher quality than live streams) to cold storage just in case the extra resolution is required (otherwise can fall back to stored live streams, e.g., if DVR HDD fails)
The purpose of this section is to detail the network messages sent in Swann's web client.
The following observations can be reproduced using Wireshark captures with the capture filter
host [DVR IP] and port [MEDIA PORT (9000)] while using the web client (default port 85). All communication with the DVR is made over TCP.
- A hex editor such as hexed.it will be very useful for interpreting the hexdumps below. In the case of hexed.it, remember to substitute the dummy characters (e.g.,
X) for real hex values and when pasting the data, specify that the data should be interpreted as hexadecimal.
- You will also find that an ASCII to Hex converter may be useful. I recommend asciitohex.com.
- The command
echo '[INPUT]' | xxd -r -p | nc [DVR IP] [MEDIA PORT]will be very useful as you can immediately reproduce the below messages by directly replacing
[INPUT]with the quoted messages.
- If you have VLC installed,
vlc --demux=h264 [FILE]can play raw camera streams
The below research is in chronological order. Interesting revelations are made throughout.
Authentication (2017-01-10 to 2017-01-12)
The web client's authentication stages are as follows:
- Send a message establishing an intent to authenticate
- Receive a message acknowledging your intent
- Send a message with authentication data
- Receive a response with the outcome of the authentication
To establish an intent to authenticate, send the following message:
Notice the dummy characters
XX. These represent arbitrary hex values (such as '7b') which will be needed in the authentication stage.
You will then receive a response message acknowledging your intent:
Notice that you see
XX will be the same hex values as you provided in the first step (e.g., if you sent your intent message by replacing
7b, the response will have
7b in place of
Next, send an authentication message:
Notice that we have introduced three new dummy characters,
YYis related to
XX. Whatever you set
YYwill be equal to
XX+ 1 (using hexadecimal arithmetic). For example, if
7c. Likewise, if
Pare the hex equivalents for your ASCII username and password respectively. In the above example, the username is five ASCII characters long and the password is six ASCII characters long. For example, if your username is
admin, your run of
Uvalues would be
61646d696e. Likewise, if your password is
passwd, your run of
Pvalues would be
706173737764. If either values is longer than six characters, then simply overwrite the succeeding
00s with more
(Side note: we can see from above that our authentication details is sent over a TCP stream. Furthermore, users are constrained to a small character set with a low character limit for their usernames/passwords. This was a major factor of why I decided to start this project.)
Finally, the DVR will return one of two responses of length 8:
- If authentication succeeded, it will return
08 00 00 00 02 00 00 00.
- If authentication failed, it will return
08 00 00 00 FF FF FF FF.
Observing the above process multiple times, you will find that the value of
XX (and therefore
YY) sent by your web client will increase by one or more every time you log in. I can only guess the possible reasons. Perhaps it may be to track sessions? However, it appears that from running the src/authenticate.go script for multiple intent values, the the intent value does not need to increment and can stay constant.
DVR Settings (and lack of authentication) (2017-01-13)
Immediately after authentication, the web client sends a message to retrieve the DVR settings (containing MAC address, firmware version, SMTP details (inc. password), admin/user login details):
The DVR responds with roughly twelve kilobytes of information.
After replaying the request without any authentication, I realised that you do not need to login to get the same response when you send this packet. I confirmed this by connecting to my media server port from a remote VPS, sending the packet via netcat and seeing the response. I immediately blocked my media port after.
It appears that the only purpose for the authentication protocol is to slow down dumb attackers trying to bruteforce the web client. (Note the use of "slow down", because a maximum of six alphanumeric characters will take no time to bruteforce.) As a result, I am not expecting H264 streaming to require authentication. However, this is good because there will be less code to write, and the purpose of this is to have a Raspberry Pi act as a proxy with proper authentication.
Camera Stream (2017-01-25)
Following on from the last section, I was incorrect in assuming that H264 streaming does not require authentication. That being said, an attacker can use the above method to get the credentials first, defeating the point of the below authentication process.
The following message returns a camera stream:
Notice the following variables:
- U and P are as before (username and password)
- N is your camera channel. This can be
8, for channels 1, 2, 3 and 4 respectively
If the authentication fails, the DVR will return
08 00 00 00 04 00 00 00.
If the authentication succeeds, the following messages are sent in the following order:
- 8 bytes:
- A 1460 byte packet containing the camera stream, with the characters
MDVR96NTnear the start as well as
00dcH264. This is followed up by packets of varying size (mostly 1460 bytes) containing the camera stream, which is received until the connection is terminated
When setting your camera channel to one which does not have a camera connected via BNC, the stream still works. However, you may see
31dcH264 a lot and if you use the
nc command, you may repeatedly hear the system bell sound (probably due to the terminal displaying the bell character).
Zoneminder and Media Port Quality (2017-06-16)
Previously, I made the false impression that the media port for the DVR4-1200 streamed at the same lower resolution as the mobile port based on the statistics provided when piping the media port stream to VLC. However, this is false; the media port does indeed stream at 704x480.
Due to the temporarily halted progress of this repository at the time of writing and the need to move to a better DVR solution which didn't involve buying a not-much-better DVR to replace the DVR4-1200, I recently installed Zoneminder.
The steps to enable the media port to stream to Zoneminder (as opposed to the mobile port which can be done using zmodopipe) are relatively straightforward:
- Install Zoneminder
- Save the following bash script to your home directory:
#!/bin/sh mkfifo /tmp/dvr1 chmod 777 /tmp/dvr1 echo "Recording" while true; do echo '[The camera stream message from the previous journal entry above]' | xxd -r -p | nc [DVR IP] [DVR PORT] > /tmp/dvr1 echo "Recording stopped. Retrying..." # Just in case the stream breaks sleep 2 done
- Add a systemd/init.d/crontab/etc. script to run this script at boot (I use a systemd script which starts tmux with split windows to run two separate scripts to handle two separate channels)
- In the Zoneminder web panel, add a new monitor with the source type
ffmpeg, source path
/tmp/dvr1and capture size
- Wait about half a minute and the stream should be available!
The above code creates a FIFO queue, connects to the DVR using
nc, writes the raw 264 stream to the FIFO queue, and ensures that the stream is restarted if interrupted. (Upon a second look, I just realised that zmodopipe does have a media port option for this DVR model, but it's always nice to a DIY approach.)