Deaddrop was a HTTP service written in Erlang. It models a simple bulletin board system, where users can create topics and reply to them. Topics can either be public or private (where users have to know their name to access them). Two logical flaws and a path traversal-like vulnerability allow attackers to list the private topics and steal data.
Deaddrop offers just the basic functionality of a bulletin board without fancy UI. In addition users could “subscribe” to topics using a websocket, to see past (“replay”) or future (“subscribe”) messages. There was no authentication in place, access is granted if the topic name is known. Private topics are just not listed. See the API description:
PATCH /add_topic (body: <topic-name>)
POST /publish (body: <topic-name>:<new-message>)
Websocket /subscribe (message: <method>:<topic-name>) // methods: REPLAY or SUBSCRIBE
The storage was equally simple: A file
topics.txt contains a list of all topic names, one per line. Private topics are prefixed with
- , public topics may optionally be prefixed with
For every topic there is a file
<name>.txt which stores all messages written to that topic. Further published messages are appended. The initial message was
All messages sent on topic: <topic-name>\n, or
** <message>\n for further messages.
The gameserver creates a new private topic every round and published the flags as a message to that topic.
Vulnerability 1: replay topics list
To get the flags, we have to first know the names of the private topics and then replay them using the websocket. All topic names are stored in the
Let’s take a look on how topic filenames are created in
The topic file is
PATH/<topic-name>.txt, without any filter on the topic name. If we could replay the topic named
topics, we would get the content of this file. However,
topics is not on the topics list. Let’s see where the topic list is loaded:
There is always a private topic called
- topics present, even if not in the file. That prevents us from adding a new topic with that name, but allows us to replay the private topic
- topics and read all private filenames. We can now open a websocket connection to
/subscribe and use the command
REPLAY:- topics to read the list of all topics, including private ones. That’s a trivial exploit, code is given below.
Vulnerability 2: Publish to topics
We already noticed that
topics is always known as a private topic. We know its name, so we can just publish a message there (which is written to
topics.txt). We publish (=write)
- topics, so the topics list file ends up with a line suggesting that
topics is now public (no
- sign before the name) and a junk line. When looking for a topic, the service takes the first name it finds, and the
- topics line is appended to the file, hence the injected line from the file is considered first. After this message has been published, we can just subscribe and replay to
topics, get the list of all private topics and read them.
Drawback of this attack: It’s clearly visible from the public topic list, and the public list can also be used by other teams.
Vulnerability 3: New topic path traversal
If we could create a new topic with name
topics, it would be publicly readable and backed by the file
topics.txt. However, there is a check in place to prevent this:
We can’t create a new topic with a name that already exists - and the private topic
topics always exists. But the check is for equality, just exactly that name is blocked. We remember that the file belonging to a topic is
PATH/<topic>.txt. No filtering is in place, we can mount a path traversal style attack.
We can simply use
./topics as topic name, which will result in the file
PATH/./topics.txt, but has a different name than
topics - we circumvented the
lists:member check for existing topics. No data is deleted in this process, files are always appended.
topics.txt now contains the line
./topics.txt (our public topic) and some junk (the initial messages for our new topic) which doesn’t matter. We can now open a websocket connection to
/subscribe and use the command
REPLAY:./topics to read the list of all topics, including private ones. We issue a replay on all private topics and get all flags stored in that service. Exploit code is given below. In fact, we could have used this vulnerability to read all files ending on
.txt from the system.
Patching the service
The first vulnerability can be fixed by preventing any replay on topics (just filter it).
The second vulnerability can be removed by prepending
- topics to the topic list, not appending it, or forbidding publish to
The third vulnerability can be fixed by filtering the topic names and remove dangerous characters (
./ for example).
On the other hand, all problems can be mitigated by renaming
topics.txt to something without
.txt extension, to prevent any collision between topic file and topics list, which was the solution we have chosen.
During the competition, we found the last vulnerability and exploited it successfully. After some time we got attacked with the second vulnerability but quickly patched it. We did not see anybody exploiting the first (and easiest) vulnerability, and just found it by accident when writing up.
Despite nobody of us knew Erlang we had fun with this service. Even without deeper language knowledge we could find our way towards our flags.