Wednesday, February 15, 2012

Trendnet Security Issues, Updated Firmware & SSH / Dropbear

So, it seems that after several years of these cameras being on the market it appears there are some very basic security holes. To be honest I am not surprised, these cameras are *very* budget and so given that I was using them to watch over the little one I didn't expose them on the internet but have used a cracking piece of software zoneminder to host and record the video when I want to access it outside of the homestead.

However, in an effort to at least try and make them more secure, I have enabled telnet and inserted the boot script to version 1.1.1.74 of the firmware which was released by Trendnet as a result of  the excellent work done by Console Cowboys; you can download the new and improved firmware here. The upgrade procedure released by Trendnet suggests that you need to first install 1.1.0.67 before upgrade which can be found here.

Whilst we are talking about security, if you are concerned about telnet being open, I suggest you turn it off in the startup scripts on USB or SMB. You can then use dropbear to have ssh connectivity if you still want the command line as below:

I have also compiled dropbear with its associated support files (dbclient, dropbearkey and scp); it take a little bit of effort to get this running as you need to copy the libutil.so.0 into the lib directory, generate the keys and add users to the device. As user management is not included in the base busybox provided I have also compiled a more complete version. All these files can found here, I'll add more complete instructions to automate this when I get some time.




Tuesday, September 27, 2011

The Finished Article (or at least the first release)

So I actually finished the first release a couple of months ago but wanted to have it running for a while so I can be sure it is stable before releasing it.

I have modified the firmware with a startup script (which you can download for viewing here) which checks for scripts to be found on either the associated SMB share or a USB stick on startup; this should allow for the camera to load additional code at boot without the need to revise the firmware constantly. The other advantage is the firmware has very little available space, and there wasn't enough space for the graphics to be stored for the text and volume meters.

The firmware can be found here; as before, whilst it works great for me, your mileage may vary.

The firmware released by Trendnet has been updated due to the security holes found by Console Cowboys - I have a new version of the firmware here.

I have also included the files I have put onto USB sticks on the camera, if you extract the following files (found here) into the root directory of a FAT fomatted memory stick and insert it into the camera it will start the baby camera volume meter on reboot.

The additional files are very conservative with the timings so the camera boot time takes about 1 minute longer. I don't consider this to be an issue as they are rarely restarted.

You can also pop the files on a samba / windows share and connect to it from the camera's interface and it will load the files over the network on reboot - freeing up the USB (although as the network link is torn down when I unload the existing camera module to load my own so it doesn't work for me with my apps)

For visualisation I am using Jogglers from O2. I bought these from the O2 store when they went on firesale for £50 and they are perfect for the job. I have leveraged work done by far smarter folks than me and they are now running Ubuntu 11.04 and I am using VLC to display the video. I added a couple of shortcuts to the desktop so the other half can easily start the video if they are rebooted. We have had them running for about 2 months without a restart so I consider them to be perfectly stable enough for our use (they even survived my typical curse; whenever I go travelling for work it is inevitable that something I have hacked together fails - in this instance they have survived all trips without needing any assistance from me)

The URL's I use for VNC are:

http://admin:admin@nurserycam/cgi/mjpg/mjpg.cgi

- or -

rtsp://nurserycam/mpeg4

you will need to substitute the appropriate hostname for your purposes.


I still have some to-do's which I will get round to shortly and post updates:

Add OpenSSH Server:

This is compiled and tested, I just need to add it to the startup script

Update Busybox

This is compiled and tested, I just need to add it to the startup script

Add USB Temperature Sensor support

This is proving to be tricky, the USB support on the camera seems to be the bare minimum to support storage devices, I can't seem to get generic support working - if anyone out there has any ideas I would be grateful for suggestions

Add 2 way audio / mp3 playback

The little fella finds it easier to sleep with white noise and I would like to be able to fire it off with perhaps a gradual fade out over time to ease him to sleep. This does require adding an external speaker to the camera which is not ideal.

If any of you have any other suggestions, please let me know.

As ever, I welcome your feedback, please let me know your thoughts (good or bad).

Wednesday, June 29, 2011

Adding visual audio level monitors

With the ability to overlay images to the video stream tackled the next task was to analyse the audio and overlay VU meters to allow us to see how noisy the little tyke is without having to actually listen to him (it is a him btw, he popped out on the 22nd June).

The camera uses the linux open sound system (OSS), which has been replaced by ALSA in linux 2.5 and above and isn't particulary nice to work with. The camera streaming server (camserv) , for which there is no source code, opens the audio-in device on startup and as it can only be opened once (and without it being available camserv refuses to work) I had to look at other avenues to get the audio.

Fortunately, camserv actually offers the audio out in a mangled stream which can be found at http://[cam-ip]/cgi/audio/audio.cgi - this stream provides chunks of audio data in 16bit PCM stream at what appears to be about 8KHz (although the frequency is only being guessed at). The stream isn't presented in a friendly format (i.e. browsers don't recognise it and simply try and download it as a series of files) but it is quite easy to write a bit of code to connect to the port and analyse the audio.

I put together some code to connect to the stream and display the current, peak and average volume overlayed on the video stream. It uses some of the same code used for the text display on the previous entry and seems to run around 1/4 sec behind the actual audio (this is due to the audio being streamed locally, analysed and then overlayed onto the video).

I have included the source code and a compiled version here, and below is a short video of it being streamed to an iPad:


video

And here it is on Apple TV2 via XBMC:

video

To use the code on your camera it does need to authenticate to the local camera web server; it defaults to username:admin pasword:admin as do the cameras out of the box, if you are using your own username and password (and I strongly suggest you do) you will need to specify the base64 encode of the username and password on the command line via -a . There are a number of websites which can do is encode so I haven
't included this in the code; the format needs to be a hash of the username:password (e.g. 'admin:admin' which has a hash of 'YWRtaW46YWRtaW4=')


Wednesday, June 8, 2011

Overlaying text and images into the video stream

Having tinkered around with the toolchain for a while I have turned my attention to one of the core tasks of the project - that is overlaying text and images in the video stream.

The video capture, encoding and motion detection are all handled by the plmedia.o module; fortunately the source code for this module is provided in the toolchain, although there is no documentation.

After a fair bit of scratching my head it is apparent that the video capture hardware writes directly to memory on demand (by the module) and then a further request is sent to the encoder hardware which encodes/compresses it directly to another buffer. This is the buffer which is used by the device when stremaing video. There is further hardware which performs motion detection, using the video capture buffer.

I decided to inject into the video stream directly before the encoding takes place in order to be sure (with as much certainty as possible) that the frame has been captured in full.

plmedia.o source is made up of 3 core components: plgrabber, plencoder and plmd. plencoder is the component responsible for managing the encoding process and this is where I have added my code.

The video is captured in YCbCr format with 4.1.1 ratio between the luminence and the chroma channels - that is, the Y channel is at 640x480 resolution with 8 bpp and the Cb & Cr channels are at 320x240 resolution with 8 bpp.

Each channel is captured simultaniously in its own buffer, I added an IOCTL command to pass a structure which contains the dimensions of the image to be added, a TTL (so images can expire) and pointers to buffers which contain appropriate images. The definitions for the IOCTL command and the buffer can be found in the source below in include/video/plmedia.h.

The heavy lifting is done in the plencoder.c source; look for a function called OverlayImages which iterates through the image buffers before each encode - it is interesting to note that I was having huge issues with image corruption which took some real head scratching to solve - the images would appear to be corrupt with some elements of the video image showing through - it turns out that the processor has a little cache which needs to be flushed otherwise it doesn't write to the buffer before the encoder kicks in.

In EncIOCTL you will find the new function to add an image to the buffer list - there is functionality to replace an existing buffer or add a new one - the replace function is useful for adding animation or moving images to the buffer (such as a time stamp) without needlessly wasting resources.

Finally in InitEncoder and CleanupEncode is addional code to create and release the buffers.

There is an arbitary limit of upto 6 independent overlays and this can be increased by modifying the IMAGEBUFFERS constant in the plencoder.h

The image buffers must be passed in the right format - and this is where it can get tricky - images need to be converted to YCbCr format and at the right resolution for each channel - the co-ordinates and dimensions refer to the Y channel and are appropriatly converted for the CbCr channels.

I have also created an applet to add text to the video stream as part of the babycam project; the code is attached below. It uses a pre-created BMP consisting of a matrix of characters white on black. The code extracts the relevant characters, builds a buffer, converts it and then sends it to the module. The same code can be used to send a BMP although it needs to be in 24bpp uncompressed format (regular windows .bmp's are fine).

The sample below shows a screen grab of the the module working with 2 overlays added by the displaytext applet - although they are in black and white there is full colour support.















And here is the same scene being streamed to an iPad:




The source and cross compiled code for the module can be downloaded here

The source and cross compiled code for the text overlay applet can be downloaded here

Wednesday, April 27, 2011

Telnetting to the device

As per my previous post, I provided instructions on how to enable telnet on the device - for those of you who would like to have access but don't have the time to install the toolchain and mess about I have posted a modified firmware file here.

Please be aware if you chose to use this - it is at your own risk - although it worked for me, I will not be held responsible if it bricks or otherwise kills your camera, cat, car or grandmother.

Once installed you should be able to telnet onto the camera on port 15566; The telnetd limits it to 2 simultanious connections and there is no user level security.

I will post a further firmware shortly with SSH and SCP along with lots of other goodies when I get past the testing stage.

Thursday, March 17, 2011

Under the hood

Poking around the shell after telnet'ing in has revealed the following hardware, a bit of googling later and it actually is very impressive given the relative low cost of the device:

Prolific PL1029 Arm Processor


OmniVision OV7720 CMOS Sensor


4 Mbytes NOR (Flash)

32 Mbytes SDRAM

RALink Technology RT61 WiFi Adapter (802.11b/g)

Ethernet Adapter

2 x Status LED connected via GPIO
1 x IR LED connected via GPIO
1 x light sensor connected via GPIO
1 x momentary push button connected via GPIO

Wednesday, March 16, 2011

Taking a peek at the firmware

TRENDnet offer a download for the GPL source at their website; a quick download later allowed me to take a peek at what they had used to pull it together. Annoyingly, they had only included the bare esentials with no buildchain or any other tools to build a custom firmware.

A bit more research showed that they have used a relatively common platform to build the camera (using the PL1209 chipset) which a very capable and self contained device (more on this later).

It seems that other companies also make use of this platform for these type of devices (indeed some look identical to the 312W I have here) and so I had a look around to see if anyone else has had any joy in getting into the firmware.

A company snappily called Airlink 101 Network Solutions offer a similar looking device; the AICN500W which has a very similar case and the same functionality. A quick look around their website and a download later and I have a much more complete firmware source tree and crucially the tools necessary to build a complete firmware image.

I have republished their toolchain here under the terms of the GPL.

The instructions provided with the image indicate that fedora 3 is needed to use the buildchain; not having the desire to have yet another image on my Mac I took the chance that it would work with Ubuntu; a couple of hours later (and a couple of fixes to the make files provided) and I can get the entire toolchain to compile providing the compiler, headers and source files for all the opensource components.

Not wanting to risk installing the firmware from another manufacturer - and bricking the camera - I looked around for a description of the firmware format to see if the 312W resembled that used in the Airlink devices.

I stumbled across a chap who has already done this for the AICN747W which looks similar enough - and interestingly he has used it to enable telnet on the camera. A quick peek at his instructions and a little tinkering later, using the fwpacker source provided in the tool chain to reverse engineer the process and I wrote a script to extract the components in order to modify the firmware provided with the 312W.

You can download the source here

#!/usr/bin/perl
# TrendNet firmware unpacker
# Matt Brain (matt.brain@gmail.com)
#
# Usage: fwunpack.pl
#
# Extracts: prostub.bin
# vmlinuz
# minix.gz
# autoboot.bat
# footer.bin
#
# Writes log to fwunpack.log

use strict;

my $scriptversion=1; #Version of this script

my $headerPointer=0x04; #Header reference in firmware
my $versionLocation=0x08; #Version location in firmware
my $md5Location=0x0c; #md5 location in firmware
my $md5Size=16; #md5 length
my $crcLocation=$md5Location+$md5Size; #crc location
my $crcSize=4; #crc length
my $pkgSizeLocation=$crcLocation+$crcSize; #package size location
my $headerSize=4096; #size of header found at $headerPointer

my $model="Unknown";
my $headerLocation=0x00;
my $tVersion=0x00;
my @version=(0,0,0,0);

my @chunkStart=(0);
my @chunkLength=(0);
my @chunkName=("vmlinuz", "minix.gz", "autoboot.bat");
my @chunk;

my $footer="";
my $prostub="";
my $numArgs=$#ARGV+1;




sub Usage {
print "Usage: fwunpack \n\n";
exit;
}



if ($numArgs<1) {
&Usage;
}

print "** fwunpack version $scriptversion **\n\n";
print "Opening $ARGV[0]\n";
open(FIRMWARE,"<$ARGV[0]");
binmode(FIRMWARE);

#read the header location
seek(FIRMWARE,$headerPointer,0);

read(FIRMWARE,$headerLocation,4) or die "FATAL: Couldn't read the header location\n";

#firmware location is stored in little endian format so need to fiddle the bits
$headerLocation = unpack('L<',$headerLocation);
printf "Header location at 0x%x\n",$headerLocation;

seek(FIRMWARE,$versionLocation,0);
read(FIRMWARE,$tVersion,4) or die "FATAL: Couldn't read the version\n";
@version=unpack('cccc',$tVersion);
printf "Firmware version $version[3].$version[2].$version[1].$version[0]\n";
#read the model name
seek(FIRMWARE,$headerLocation+4088,0);
read(FIRMWARE,$model,8);
print "Model $model\n";

my $chunkCursor=$headerLocation+4096;
#load the header
for (my $counter=0;$counter < 511;$counter++) {
seek(FIRMWARE,$headerLocation+($counter*8),0);
read(FIRMWARE,$chunkStart[$counter],4);
read(FIRMWARE,$chunkLength[$counter],4);
$chunkStart[$counter] = unpack('L<',$chunkStart[$counter]);
$chunkLength[$counter] = unpack('L<',$chunkLength[$counter]);
if ($chunkStart[$counter]>0) {
printf "%s found, deploy at:0x%x length:%d\n",$chunkName[$counter],$chunkStart[$counter],$chunkLength[$counter];
seek(FIRMWARE,$chunkCursor,0);
read(FIRMWARE,$chunk[$counter],$chunkLength[$counter]);
$chunkCursor = tell(FIRMWARE);
open(CHUNK,">$chunkName[$counter]") or die $!;
binmode CHUNK;
print CHUNK $chunk[$counter];
close CHUNK;
}
}

seek(FIRMWARE,$chunkCursor,0);
print "writing footer.bin\n";

read(FIRMWARE,$footer,64);
open(FOOTER,'>footer.bin');
binmode FOOTER;
print FOOTER $footer;
close FOOTER;

print "writing prostub.bin\n";

seek(FIRMWARE,0,0);
read(FIRMWARE,$prostub,$headerLocation);
open(PROSTUB,'>prostub.bin');
binmode PROSTUB;
print PROSTUB $prostub;
close PROSTUB;

close FIRMWARE;


Using the instructions provided with the AICN500W firmware I was then able to rebuild the firmware and do a comparison between the 2 - this was critical to show that i was pulling all the bits out as expected and was able to reconstruct the bits into an identical firmware file. I had to create an ipcam.dsc file to describe the components in the firmware for the fwpacker tool as below and append the footer.bin to the output of fwpacker.

# ipcam.dsc

# describe the firmware version
@version 1.0.0.3

# describe the model which maxium 8 characters
@model IPCAM001

# describe nor flash type and size
@norinit 1 "8x8,64x63"

# describe deployment location and input file
@deployment
vmlinuz 0x20000 777008
minix.gz 0xe0000 2836160
autoboot.bat 0x2000 8192
@end

I then referred back to the instructions found earlier and
enabled telnet access by uncommenting 2 lines in etc/rc.d/init.d/daemon.sh:

# echo "Starting telnetd ..."
# telnetd -p 15566

gzipping the disk image back up produced a smaller image which concerned me - but rebuilding the firmware and uploading proved it worked (bit of a gamble I know) - I had telnet access on 15566.

You may download a bundle which contains the source for the fwunpacker along with the fwpacker and associated files from here