Simon's Blog

That time I discovered a full account takeover vulnerability

November 04, 2022

Important note

Similar to the accidental pentest, this story happened over two years ago and the details have been fudged to protect both the innocent and the guilty.


One of our clients had a successful website and were looking to expand into the mobile space. The task to build this mobile application was assigned to me.

The supplier which built and operated their website for them was also going to provide the backend for us to wire up the App to.

One of the first items I requested was any sort of API documentation they had and a staging environment so that I could test the integration without worrying about affecting their production instance.

The project

The first issue arose when they came back to us stating that they were having difficulty setting up a staging server for us and that we should use the production environment for testing.

This immediately rubbed me the wrong way. Did they not even have a staging environment for their own testing? Turns out, no, they did not.

I did my best to not allow this issue to get to me: I could understand that, on occasion, corners are cut, especially when resources are scarce (whether its time, financial or competence). I decided to, with sufficient CYA, proceed to use their production API whilst exercising caution.

The API documentation (provided in a Word document) was mostly sufficient for us to proceed with a good part of the work, which was a counter-balancing relief to any issues so far.

Working in production

We started to work our way through creating the screens that our designer had given us. We prioritised the work where the API was already available and using mocks for where it was not.

Overall the project proceeded quite smoothly from there.

Testing in production

At one point I wanted to test how the user profile updates itself by changing something on my own profile on the production environment.

I navigated to the user profile page, which had a URL similar to this:

…and decided to change my name whilst watching the network tab to observe the behaviour.

Funnily enough, whilst other parts of the website performed asynchronous requests using JavaScript, this one did not. It did not even perform a POST request from a form. This implementation smell triggered the curiosity snowball that eventually formed this entire story.

The smell

My profile had clearly been updated as I could see the new value, both on the website and on the API. So how did the website manage to update it?

Then I realised that the URL had changed: it was no longer but instead it had become something similar to the following:

This was stinky and smelly. Not only did the website perform an update using a GET request, but as a result of doing so, it had to pass all the values as query parameters. To confirm my suspicion, I changed the surname query parameter value and retried the URL, succeeding in changing the saved value.

Admittedly, while smelly, meaning it is the kind of code that I would have flagged as an issue in a code review, it does the job it is meant to do. This blog post however is not about smelly code, but rather about…

The vulnerability

Between the lack of staging server, the API docs in a Word document and the GET request performing modifications, it was becoming clear to me that corners had been cut on this backend.

From experience I also know that when corners are being cut, security tends to be one of the first that are cut.

So the thought passed through my head: given what I know so far about the supplier, what are the chances that they are ignoring extra keys on this GET request that modifies the User Profile?

It was super easy to test out. I simply typed out the following URL into my browser:

I logged out, then attempted to log into with password foobar123 and sure enough… I got in.

Raising the issue

I had successfully reproduced a full account takeover. I also knew that this was a non-trivally popular website in Malta, with multiple thousands of registered users, each with a certain set of PII and potentially sensitive data in their accounts.

I could not keep this to myself. I raised it through my then manager who raised it with the client who took it up with the supplier. The issue was quickly patched, faster than I had seen any other issue solved in this project so far.

In Conclusion

The client shared with us that they had been having issues with the supplier for a while and wished to switch away, however their hands were tied for now.

I got what amounted to a pat on the back and acquired the knowledge about how to avoid creating this vulnerability in the first place.

This was yet another simple issue that could have been easily solved long before the code hit production with a simple automated test.

Written by Simon who lives in Malta. You can find out more about me on the about page, or get in contact.