Discovering Misconfigured Amazon permissions in Chromebook monitoring software

Published on July 11, 2019

I’ve been putting this post off for a long time. The events took place the in high school, but I always meant to make some sort of blog post or write-up about this. Me and my friend Winston found a series of vulnerabilities related to misconfigured IAM and S3 permissions in software used by our school district. I wrote most of this over a year ago but didn’t get around to publishing it. A lot of this is pieced together from old chat logs and emails, but I don’t guarantee complete accuracy. For reference this would’ve been a little over four years ago, but I’ll leave the exact dates vague.

One thing I decided before publishing was to remove references to the specific company. It’s not a secret, but this far from the event I’m sure they have much better security practices and it’s not the most relevant part of the story.

Spoiler: we responsibly disclose the vulnerability at the end.

The Story

At the time of these events we were seniors at Morristown High School. Morris School District was just starting its Chromebook Initiative to “cultivate and support learning that reflects contemporary exchanges and interactions.” To us, this meant that every student in the school received a Chromebook (typically a Dell Chromebook 11 or Samsung Series 3 Chromebook) managed by the high school.

Our interest in discovering vulnerabilities in the Chromebooks followed rumors that someone had already discovered issues in the software. I never heard specific details and never confirmed the rumors. True or not, they peaked our interest and led us to further investigate the nature of the software.

The Chromebooks given to students were set up as managed devices owned by the district. Access to the Developer Tools were disabled, Chromebooks were automatically updated whenever a new Chrome OS version came out, and the monitoring software was preinstalled on all Chromebooks. Despite these restrictions, we could still view installed extensions, install anything from the Chrome Web Store and prepend view-source: to any URL or path to get the source.

I don’t recall all the individual steps we took or red herrings we followed before finding the two issues we would eventually report. It took about a week of trial and error both in class and after practice before finding everything. Since these wrong turns are not recorded in the chat, what’s here is a far more streamlined version of what actually happened.

Our investigation started with looking for the source of the management software. It was quite obvious that there was such software installed on the Chromebooks, as attempting to access certain sites (such as the beloved were blocked locally. Furthermore, some of the more technologically-savvy teachers had begun to utilize the classroom-facing features of the software, and it is there that we learned that the software was developed by redacted. However, there were no extensions proudly labelled “[company name]” or “Chromebook Management” so we focused on the two extensions that came preinstalled with each Chromebook. Hidden in the minified source of one of these extensions was a call to the AWS javascript client which included an accessKeyId and secretAccessKey. This seemed like a promising start, so we tried to find looking for what these keys had access to.

Setting up the AWS cli and plugging in the discovered IAM keys revealed two services we had access to: dynamodb and kinesis. We only had readonly access to dynamodb and the data looked uninteresting so we moved on to kinesis. Although it took some reading and fiddling trying to figure out what kinesis actually did and how to use it we eventually found we had the ability to read from streams of student data spread across several so-called “shards.” The cli proved ineffective at sampling the data passing through the shards so I wrote the following script to assist us (apologies for code quality).

import boto3
import json
import sys

strName = sys.argv[1]

client = boto3.client("kinesis");

response0 = client.describe_stream(
print("Found an "+response0["StreamDescription"]["StreamStatus"]+" stream!")
print("Stream has "+str(len(response0["StreamDescription"]["Shards"]))+" shards.")
print("Retention Period: "+str(response0["StreamDescription"]["RetentionPeriodHours"])+" hours.")
for s in response0["StreamDescription"]["Shards"]:
    print("  Shard: "+s["ShardId"])
    response1 = client.get_shard_iterator(
    shard = response1["ShardIterator"];
    response2 = client.get_records(
    records = response2["Records"]
    if ( len(records) < 1 ):
        print("No records in shard.")
    data = records[0]["Data"]
    print( data )

The output was JSON-formatted data clearly sent from student chromebooks across the country announcing its user had accessed a site with a bad word. At this point we decided to look for someone to contact at at the company, although in the day it took to send the email we managed to find something else more interesting.

Although knowing which students are visiting naughty pages is somewhat interesting, we were convinced the company was collecting more detailed information from students. We had already found debugging the extension on the Chromebooks was a lost cause so I decided to try my luck debugging the software on a personal computer. However, the management extensions and restrictions, discussed earlier, are loaded into Chrome after associating a user profile with the school Google account. This meant that all the policies were also active on our personal devices. Winston managed to circumvent the devtools restrictions through the --remote-debugging-port switch, which opens up the managed Chrome instance to debugging from another Chrome instance’s dev tools. By starting up a personal Chrome instance to do just that, we hit the second roadblock: the district had configured the software to disable itself when not running on a Chromebook. After looking through the pretty-printed code we determined that it was simply checking for a Chrome OS user agent, and so we were able to trick the software by spoofing our user-agent through another command-line switch, --user-agent. After successfully enabling the extension and looking at its network activity through the ‘remote’ Chrome instance, we saw the software occasionally uploading screenshots to an S3 bucket. These screenshots revealed the other half of what we had found in the kinesis shards: whenever someone visits a page with blacklisted words, the page URL is sent to kinesis while a screenshot of the page is uploaded to S3. The URL was random and there was far too much entropy to randomly guess other screenshots, but Winston discovered that crucially, the root path of the bucket was unsecured and had a listing of all the screenshots collected by the software. Furthermore, the screenshots could be filtered by query parameters, such as by organization or even by user. Due to the amount of personally-identifiable information freely searchable in the bucket we contacted the company with both issues the next day.

It took a few times contacting the company who managed the software before getting a response. Unfortunately they did not have a bug bounty program at the time, but eventually we were able to get a response. Despite my worries, they seemed really thankful for our efforts discovering and reporting everything and we were given a nice reward for our efforts.

If there’s a moral to this story, it’s probably that Amazon permissions are confusing and to be careful how you architect things with regards to security. I know there’s been some high-profile data breaches related to AWS permissions, and that Amazon has acted on them to make these types of events less likely. Also, don’t put things off because it might be four years until you get to them.

Update (2020-03-30): I removed some references to the company that made the software and replaced it with “the company” or “the software” as appropriate.