TailTag is a recently publicized social networking app developed by “Finn the Panther”.

As of the time of writing, I have attempted to reach out to Finn regarding the issues I uncovered via 3 separate means of communication, and have yet to receive a response. I am willing, however, to give Finn the benefit of the doubt as I reached out while Anthrocon was taking place. I asked if the support email was still being monitored to which I was told “It was wiped recently during a cleanout”. I was a couple of days away from sending yet another email, but due to the nature of the data and how these vulnerabilities could be abused, as well as the recent widespread use of one of them, I thought it best to publicize this doc as soon as possible.


TL;DR

TailTag is a vibecoded mess of an app and accompanying backend.

Everything you have ever uploaded to TailTag, regardless of your public/private visibility, is publicly accessible in full.

A bad actor could, in theory


The Root Problem

The most blatant and shocking discovery I made during my initial investigation was just how poorly secured the app’s backend was. Upon setting up a rudimentary https proxy, I was able to see all traffic moving to and from the app to the Supabase backend.

Supabase is an open source Firebase alternative. It makes developing backend infrastructure easy and cuts out a lot of the boring and monotonous aspects of developing a connected application. In TailTag’s case, it hosts blob storage, the central database, the REST API and authentication. Used correctly, these “backend-in-a-box” services can be very powerful and cut down significantly on development time especially during the prototyping phase. However, special attention is needed to ensure the relevant security features included with these services are utilized and implemented correctly. Failing this could mean sensitive user data left exposed to bad actors.


Client Side Filtering

In TailTag’s case, I noticed a lot of client side filtering, which is essentially just the client telling the server: “Hey, I am user number 5. Of the top 5 people on the leaderboard who have their visibility set to public, give me their name and bio text”. The server naively accepts the request, checks its database, and returns the filtered response.

This approach sounds great in theory, as it allows you to focus more on the UI and functionality of the client application without needing to work on the server at all. You request data with parameters, the server trusts you and returns it. The problem with this, however, is that any bad actor can utilize the same channels but send a more malicious request. One such request could contain something like “Give me all profiles that have a valid ID and return ALL data for each”.