Our on-campus start-up was hacked.
The hacker somehow deleted the only admin user… Can you login to the admin interface and revert it?

When we look at the source code, we notice the url of the admin panel.

It is located at /admin. After visiting the /admin url, we encounter with a login form. Let’s try to find SQL injection by sending ‘ as the username.

Looking at the error message, we see that it uses AQL and the query is in the following form.

Let’s try to bypass the password check.

We have bypassed the check by using a logical or at the username and commenting the rest of the query out. However, it says our ‘role’ is not ‘admin’. Let’s try to login as the another user in the database by incrementing the offset of LIMIT and try to find an admin user.

The query failed which simply means there is only 1 user in the users table and that user’s role is not ‘admin’.

Let’s try to check if there is a column named role in users table.

Since the query didn’t fail, there is an existing role column. We know its value is not ‘admin’ and it is probably ‘user’. Let’s check this theory as well.

We have confirmed that the existing user’s role value is ‘user’.

Let’s try to return a fake admin user.

When I googled the error message, I found that the website uses pyArango as the python driver for ArangoDB. Looking at its source code, I found the following snippet.

It tries to access the _id field of the document. Then, I looked at the docs of ArangoDB and found the following information.

Note that all documents created in any collections will automatically get the following server-generated attributes:

  • _id: A unique id, consisting of collection name and a server-side sequence value
  • _key: The server sequence value
  • _rev: The document’s revision id

Then, I found the following example that includes _id.

It simply contain the table name and row id seperated with / symbol. Let’s look at the python line again.

It just needs the collection name from _id and we know its users for our case. Let’s try it again.

It worked. We could also retrieve the _id from the existing user such as:

There is also another and actually a better way of achieving this. I found a MERGE() function that merges objects together. Let’s use it to get the flag.

Here is flag{1_l0v3_a_g00d_1nj3ct10n}.