Analyze the network traffic of a TV

Philips FL5008H/12

I recently bought a new Philips television 32PFL5008H/12. Most new televisions are ‘smart’ and this device is nothing different. It can connect to the Internet via a wired or wireless connection. I used the wired connection and disabled wireless. I also disabled most of the ‘smart’ features because they are not useful for my usage.

According to the included licenses this device is build on a Linux Kernel 3.0.13 and includes a number of open source packages (Sawman, openssl, busybox, sqlite, microhttpd).

Capturing the traffic

I was interested in the network traffic generated by this device. I did not intend to run a full forensic investigation, I was only interested in having a global view on what the device was doing.

I configured a Kali VM as a gateway. The VM served IP addresses via a DHCP server (range 192.168.199.100/25) with the DHCP lease containing the gateway address 192.168.199.1 as the default route, the DNS server and the NTP server. I also enabled a catch-all web server on the gateway on port 80, a DNS server with dnschef and I ran tcpdump to capture all the traffic.

The TV was assigned the DHCP lease for 192.168.199.100 and the NTP server replied to all time queries with the current -VM- time.

I did two tests.

  • A test where all the traffic was sent to the gateway (192.168.199.1) and no outside -Internet- connection was available. The catch-all web server replied to all the http queries and dnschef returned 192.168.199.1 as the reply for all DNS queries. The traffic captures below were made with this test;
  • A test where all the http traffic was redirected to a local running ZAP proxy. The VM was set as a gateway, forwarding the network packets and dnschef proxied all DNS requests to the Google nameserver (8.8.8.8).

The TV did DNS queries for these hosts

  • 2.pool.ntp.org
  • pool.ntp.org
  • ntp1.pouf.org
  • time-A.timefreq.bldrdoc.gov
  • certs.opera.com
  • www.ecdinterface.philips.com
  • www.mediatek.com

In order to generate traffic, I disconnected and connected the power cord, turned it on and off a couple of times, browsed a number of channels, went through some of the menus and left it sit idle for a couple of minutes. All in all this generated different packet dumps, each more than 2000 packets for about 50 minutes (approx. 3100 seconds).

First test, traffic ending at the gateway

This test configuration did not allow traffic to leave the local network. All communication was between the TV and the Kali VM. The web requests were against the catch-all web server on the gateway. This web server returned a http return code 200 for every request.

The first four hosts (.ntp.org, .pouf.org, .bldrdoc.gov) have something to do with getting the current time. There was a request for the A record of time-A.timefreq.bldrdoc.gov and then an NTP query to this host.

322	0.001655	1546.292488	192.168.199.100	192.168.199.1	DNS	Standard query 0x06fd  A time-A.timefreq.bldrdoc.gov	40338	87
...
327	0.002354	1546.307426	192.168.199.100	192.168.199.1	NTP	NTP Version 1, client	40761	90
328	0.001030	1546.308456	192.168.199.1	192.168.199.100	NTP	NTP Version 1, server	40851	90

The device also requested pages from the Opera Rootstore (certs.opera.com).

101	0.692437	28.085218	192.168.199.100	192.168.199.1	DNS	Standard query 0x4604  A certs.opera.com	11343	75
102	0.006318	28.091536	192.168.199.1	192.168.199.100	DNS	Standard query response 0x4604  A 192.168.199.1	11434	91
...
115	0.008366	28.222030	192.168.199.100	192.168.199.1	TCP	60250 > https [SYN] Seq=0 Win=14600 Len=0 MSS=1460 SACK_PERM=1 TSval=4294913316 TSecr=0 WS=2	12668	74

The domain www.ecdinterface.philips.com as used to sent http queries to. I could not see any requests to www.mediatek.com.

The query for www.ecdinterface.philips.com was followed by an HTTP POST request to the URI http://www.ecdinterface.philips.com:80/perl/ecdav.

245	0.029453	27.548512	192.168.199.100	192.168.199.1	DNS	Standard query 0x9292  A www.ecdinterface.philips.com	3794	88
246	0.019135	27.567647	192.168.199.1	192.168.199.100	DNS	Standard query response 0x9292  A 192.168.199.1	3898	104

...

247	0.002744	27.570391	192.168.199.100	192.168.199.1	TCP	57415 > http [SYN] Seq=0 Win=14600 Len=0 MSS=1460 SACK_PERM=1 TSval=4294913177 TSecr=0 WS=16	3972	74
248	0.000084	27.570475	192.168.199.1	192.168.199.100	TCP	http > 57415 [SYN, ACK] Seq=0 Ack=1 Win=14480 Len=0 MSS=1460 SACK_PERM=1 TSval=2835244 TSecr=4294913177 WS=1024	4046	74
249	0.000567	27.571042	192.168.199.100	192.168.199.1	TCP	57415 > http [ACK] Seq=1 Ack=1 Win=14608 Len=0 TSval=4294913177 TSecr=2835244	4112	66

The full web request was sent to what I assume is a Perl script on the Philips server.

POST http://www.ecdinterface.philips.com:80/perl/ecdav HTTP/1.0
Host: www.ecdinterface.philips.com:80
Accept: */*
Content-Type: application/octet-stream
Content-Length: 01253
Session-Key: 01310000

I had a Perl script that captured the requests and logged the variables.

#!/usr/bin/perl -w

print "Content-type: text/html\r\n\r\n";
print "<html><head></head><body><h1>OK</h1></body></html>";

if ($ENV{'REQUEST_METHOD'} eq "GET") { 
  $request = $ENV{'QUERY_STRING'}; 
} elsif ($ENV{'REQUEST_METHOD'} eq "POST") { 
  read(STDIN, $request,$ENV{'CONTENT_LENGTH'}) 
  || die "Could not get query\n";
}

my $timestamp = localtime(time);

open (MYFILE, '>>/tmp/perl.txt');
print MYFILE $timestamp;
print MYFILE " : ";
print MYFILE $request;
print MYFILE "\n\n";
close(MYFILE);

The post request always had two parameters

  • eui64 : a 16 characters long value;
  • args : a value resembling application code. I could not decode or decipher it (it started with ‘<9C>^S‘).
  • The parameter eui64 refers to a 64-bit global identifier. Basically it’s build from your network MAC address by inserting FF-FE. You can watch this YouTube movie to understand how it works. Unfortunately I don’t have an IPv6 configured network / device at home so I could not investigate further to check what was send over IPv6.

    Second test, the gateway as a NAT device

    In this test I had the Kali VM configured as a NAT device. Before starting the TV I launched the ZAP proxy to track every web request. I used a simple iptables and ip forwarding setup.

    modprobe iptable_nat
    echo 1 > /proc/sys/net/ipv4/ip_forward
    iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    iptables -A FORWARD -i eth1 -j ACCEPT
    iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 8080
    

    The last iptables rule redirects all the web traffic to the ZAP proxy (running on tcp/8080)

    I could not see any different behavior, except a longer communication with the Philips server. This is normal because the server now returned a valid answer to the request. Compared to the first test, the request was similar but had ‘Content-Length’ set to 01288 instead of 01253. I used the ZAP proxy to save a request and response for later analysis.

    The response from the web server contained unreadable application data. Similar to the request, I could not decode or decipher it.

    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Type: application/octet-stream; charset=utf-8
    Server: Microsoft-IIS/7.5
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Connection: close
    

    Traffic sent to philips.com

    I’m not sure how to interpret the traffic sent to philips.com. It can be a status update or similar. I did not saw an increase in frequency or size when I continuously browsed the TV-channels. You can always get in touch with me if you want a copy of the request / response for further investigations.

    TV services

    While I left the TV idle for a couple of minutes I started nmap to check what network services are provided by the TV itself (note that I disabled most of its ‘smart’ features). I could not find any UDP services but got replies for four TCP services.

    PORT      STATE SERVICE
    1925/tcp  open  unknown
    2323/tcp  open  3d-nfsd
    35375/tcp open  unknown
    44375/tcp open  unknown
    

    When I connected with telnet to 35375/tcp and 44375/tcp I got a reply similar to that of a web server. I then used the http* scripts of nmap. Below is a (filtered) output.

    35375/tcp open  http    Mongoose httpd
    | http-brute:   
    |_  Path "/" does not require authentication
    |_http-chrono: Request times for /; avg: 210.46ms; min: 183.98ms; max: 260.11ms
    | http-headers: 
    |   Content-Type: text/plain
    |   Content-Length: 30
    |   Connection: close
    |   
    |_http-methods: No Allow or Public header in OPTIONS response (status code 400)
    | http-sitemap-generator: 
    |   Directory structure:
    |   Longest directory structure:
    |     Depth: 0
    |     Dir: /
    |   Total files found (by extension):
    |_    
    | http-slowloris: 
    |   Vulnerable:
    |   the DoS attack took +2m12s
    |   with 1001 concurrent connections
    |_  and 17 sent queries
    |_http-stored-xss: Couldn't find any stored XSS vulnerabilities.
    |_http-title: Site doesn't have a title (text/plain).
    | http-vhosts: 
    | intranet
    |_27 names had status 404
    
    44375/tcp open  http    Mongoose httpd
    ...
    | http-vhosts: 
    |_28 names had status 404
    

    Both scans seemed to return the same results but there’s a slight difference in the http-vhosts results. Port tcp/35375 returned ‘intranet’ as a valid vhost name whereas port tcp/44375 did not return any valid vhost names. I then used nikto with the vhost setting but it did not return any useful results.

    When I rebooted the TV and did the nmap scan again I noticed something strange. The network ports tcp/1925 and tcp/2323 were responding, the two ‘higher’ ports however did not reply but two other ‘high’ ports did reply. It turned out that after the reboot the ports tcp/47341 and tcp/59741 were active and no longer the ports tcp/44375 and tcp/35375. I rebooted the TV a couple of times and concluded that after every reboot the two ports tcp/1925 and tcp/2323 are immediately available. A couple of seconds later, two other -high- TCP ports become available and behave as a web server. I do not know the reason for this change.

    TV : tcp/1925

    The port tcp/1925 was also a web service. I tried fingerprinting the service (nmap, nikto, …) but I did not get a useful result. The web server replied with different http status code

    1. 403 : if a path existed but you tried to do directory listing instead of retrieving a specific file;
    2. 404 : if a path did not exist;
    3. 200 : if a file existed.

    When I browsed through the menus and licenses I already learned that this device was a Linux flavor. So I tried the most obvious thing, retrieving the /etc/passwd file. It worked. Retrieving the shadow file did not work.

    root:x:0:0:root:/basic:/bin/sh
    

    I then tried /proc/cpuinfo. It also worked.

    Processor	: ARMv7 Processor rev 0 (v7l)
    BogoMIPS	: 1694.10
    Features	: swp half thumb fastmult vfp edsp vfpv3 vfpv3d16 
    CPU implementer	: 0x41
    CPU architecture: 7
    CPU variant	: 0x3
    CPU part	: 0xc09
    CPU revision	: 0
    
    Hardware	: MT5880
    Revision	: 0000
    Serial		: 0000000000000000
    

    I didn’t wanted to go through the process of testing all the URLs by hand so I wrote a script to test a list of (mostly Linux) resources on a web server. It’s tiny Web Url Scanner. The output of the different scans (I filtered out the 404 results) is

    Start scanning
     Base URL: http://192.168.199.100:1925/ 
    
    "Code", "URL", "Server", "Last Modified", "Content Type", "Cache Control"
    "403", "http://192.168.199.100:1925/tmp", "", "", "", ""
    "403", "http://192.168.199.100:1925/home", "", "", "", ""
    "200", "http://192.168.199.100:1925/etc/passwd", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/etc/resolv.conf", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/etc/inittab", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/etc/hosts", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/etc/profile", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/etc/nsswitch.conf", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/etc/sysctl.conf", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/etc/fstab", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/proc/cpuinfo", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/proc/devices", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/proc/net/netstat", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/proc/sys/kernel/pid_max", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/proc/version", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/proc/partitions", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/proc/loadavg", "", "", "application/octet-stream", "no-cache"
    "200", "http://192.168.199.100:1925/proc/cmdline", "", "", "application/octet-stream", "no-cache"
    
    Scan finished
    

    Running the script returned a number of files with interesting system content. I’ve listed a couple of them below.

    /etc/fstab

    # /etc/fstab: static file system information.
    #
    # <file system>	<mount point>	<type>	<options>			<dump>  <pass>
    /dev/root	/		auto	noauto,rw,errors=remount-rw	0 0
    none		/proc		proc	defaults		0 0
    none		/sys		sysfs	defaults		0 0
    none		/dev/pts	devpts  noauto,gid=5,mode=620   0 0
    none		/tmp            tmpfs   defaults                0 0
    none		/opt            tmpfs   defaults                0 0
    none		/dev/shm	tmpfs	noauto			0 0
    none		/var/run	tmpfs	defaults		0 0
    

    /etc/inittab

    # /etc/inittab: init(8) configuration.
    # $Id: inittab,v 1.91 2002/01/25 13:35:21 miquels Exp $
    
    # The default runlevel.
    id:3:initdefault:
    
    #1:2345:respawn:/sbin/getty -n -L ttyMT0 115200 vt100 -l /sbin/autologin
    1:2345:respawn:/sbin/mnet_down.script
    
    # Boot-time system configuration/initialization script.
    # This is run first except when booting in emergency (-b) mode.
    si::sysinit:/etc/init.d/rcS
    

    /etc/profile

    echo "Loading /etc/profile..."
    
    # We don't need HWCAP support
    export LD_HWCAP_MASK=0
    
    export PATH=/bin:/usr/bin:/sbin:/usrbin:/3rd/AdobeAIR/stagecraft-2.5.1.340/lib
    
    #move to 3rd?
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/basic:/basic/lib:/basic/lib/alsa-lib-1.0.24.1/lib:/3rd/lib
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/3rd/AdobeAIR/stagecraft-2.5.1.340/lib:/3rd/flash_lite/lib
    
    #MyNetworkPlace
    export PATH=$PATH:/3rd/samba/bin:/3rd/samba/sbin
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/3rd/lib/iconv/lib
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/3rd/samba/lib
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/3rd/netflix/flash-app/lib
    
    # flash player 10
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/3rd/gtkdfb/lib:/3rd/libxml2/libxml2-2.7.8/lib:/3rd/mtal
    export FONTCONFIG_FILE='/3rd/gtkdfb/etc/fonts/fonts.conf'
    if [ -f /3rd/gtkdfb/lib/pango/1.6.0/modules/pangorc ]; then
    export PANGO_RC_FILE='/3rd/gtkdfb/lib/pango/1.6.0/modules/pangorc'
    fi
    
    #
    # browser
    #
    export FREETYPE_FONT_SET=YES
    export PATH=$PATH:/usr/opera
    export OPERA_DIR=/usr/opera/opera_dir
    export OPERA_HOME=/usr/opera/opera_home
    export OPERA_FONTS=/usr/opera/fonts
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/3rd_rw/nettv:/3rd/nettv:/basic
    
    #wifi tools
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/3rd/wpa_supplicant/atheros
    export MTK_3RD_EXE_PATH=/3rd
    export MTK_3RD_DATA_PATH=/3rd_rw
    export TRUSTZONE=enable
    

    /proc/version

    Linux version 3.0.13 (yuchili@tpv-desktop) (gcc version 4.5.1 (GCC) ) #1 PREEMPT Mon Oct 7 19:24:27 CST 2013
    

    In inittab there’s a call to /sbin/mnet_down.script. This script monitors changes in a file and tracks interface changes. A comment in the script refers to another script, bgm_interface.script. I tried Googling the authors and some of the strings found in the script but the search did not return any useful results.

    #!/bin/sh
    #	@author Prince Wen
    #	@date	2013/06/18
    #	@brief
    #		monitor /tmp/interface_down
    #
    
    while true
    do
    	if [ -e /tmp/interface_down ] ; then
    #		echo "bgm_interface.script down on =======mnet_down.script====="
    		ifconfig eth0 down
    		ifconfig ra0 down		
    		sleep 0.1s
    	else
    		sleep 0.5s
    	fi
    done
    
    #!/bin/sh
    #	@author CC Ma
    #	@date	2009/03/13
    #	@brief
    #		ifconfig event handling script
    #
    
    usage() {
    	echo "[$0] Usage: $0 {up/down}...."
    	exit 1
    }
    
    case "$1" in
    	up)
    		# [IF Up]
    		rm -rf /tmp/interface_down
    		ifconfig eth0 up
    		ifconfig ra0 up
    		;;  
    	down)
    		# [IF Down]
    		touch /tmp/interface_down
    		ifconfig eth0 down
    		ifconfig ra0 down
    		;;    
    esac
    

    I suspect that the web server on tcp/1925 is (or is based on) a kernel build-in web server. I don’t think that it is TUX because I could not find any of its usual files. Maybe it’s the microhttpd? For a future test I want to compare the responses of these requests with the responses from a kernel of light web servers that I configured.

    TV : tcp/2323

    I could not fingerprint this port. Connecting to the port returned a reply but there was no banner or information on what this port was supposed to do. It might have to do with dealing with the external keyboard driver …

    Conclusion

    It was a fun experience to use different tools and scraping together different bits of information. At first glance I could not see anything worrying in the traffic but then again I could not analyze what was in the data packet sent to the Philips servers.

    I will have a next go on the traffic analysis with capturing the IPv6 traffic and analyzing the data in the request / response to the Philips servers.

8 thoughts on “Analyze the network traffic of a TV

    • Thanks! Coincidentally, this week I ordered a raspberry pi to play with during the Xmas holidays 😉 Going to use your blog as one of the resources!

  1. I just realized after sending my comment that I blogged this in german. if you think this could help just drop me a mail and I’ll do an english translation. Regards

  2. sbellon on said:

    I have a PFL6007 and was very happy to read your network traffic analysis.

    I have a few things to add:

    Port 1925 is used to control various aspects of the TV while it is turned on. It uses jointSPACE (see http://jointspace.sourceforge.net/). As I also own Philips Hue and Living Color lamps I wrote a small Python script to real-time sync the light of the TV’s Ambilight with the Living Color lamps in the back of the room using the two JSON interfaces of the TV and the Hue bridge. Works really nicely. 🙂

    I would be very interested in further decoding the traffic with http://www.ecdinterface.philips.com:80/perl/ecdav. Why? Because The 2012 series is very bugged regarding the firmware. I’m still using a VERY old firmware version (0.132.5.3) that was shipped when I bought the TV because according to forum posts the picture quality got worse with every release afterwards. As I only use the TV as a display device and don’t use the built-in tuners, WLAN, etc. it is ok with me to stay on the old firmware. However, on each startup I get the annoying message that a new firmware version is available. I turned off that message by redirecting http://www.ecdinterface.philips.com to a network-local host in my BIND configuration. However this means that the SmartTV features are also unavailable.

    I already toyed with a small proxy that redirects those requests to http://www.ecdinterface.philips.com with the intention to filter out the “firmware update” message, but leaving the rest intact. However the communication between the TV and http://www.ecdinterface.philips.com seems to be encoded.

    I’d be happy to have more information regarding this traffic available. I’m willing to help further analyze the traffic, if I get some hints.

    Are you planning to further investigate the network traffic of the TV or is this project finished for you?

    • admin on said:

      Useful remark for tcp/1925, I did not realize I could actually control stuff on the TV via that interface, thanks for the lead!

      I’m planning on further investigating the traffic when there’s a new holiday period. Currently I’ve been busy with other projects to pursue this one but it’s still on my “ToDo” list. Hopefully I can get it started somewhere round April. Any new discoveries will be posted here.

  3. luvrajind on said:

    I run TCP server on my laptop and listens on port 1925.
    I receive following msg from philips android app(i set manual IP address of my laptop in android app)

    GET /1/channellists HTTP/1.1
    Host: 192.168.1.100:1925
    Connection: Keep-Alive

    My question is, what i should sent back so that android app will be connected and i could check other keycodes??

    Please reply to me on luvrajind@gmail.com

  4. Sreccko on said:

    Hello,

    I have Philips smart tv bought in last year.
    I’m running asp net aplication (showing time, IIS) on server,and tv is client( Internet explorer).
    Prooblem is update, when update comes everything stops.
    How can I disable update, setting router ? On tv itself there is no option to disable it.

    Kind regards,
    Sreccko

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.