Pentesting an API for Fun and Learning

3 years ago 287
BOOK THIS SPACE FOR AD
ARTICLE AD

I got an API to (pen)test for potential issues and I was handed over the API spec as well, so therefore, I knew what all APIs are there and what parameters do they expect, etc, etc.

Tools of Trade

I chose Postman for this (pen)test, just because I could define the API collection there (with all the APIs that were in scope for this pentest assignment) and since it can also work well with BurpSuite, so why not!

Prep Work

I had setup Postman with all the APIs that were the part of this pentest:

Created a collection for this projectAdded all the APIs in itDefined the API specific params and request payload

Initial Testing

Initially, when I test an API, I interact with it normally — sending the expected parameters and checking back the responses.

This helps me see if the APIs are returning any excessive data or not.

These bugs are quite easy to locate and thus they happen to be first on my list to look for.

But in this API, I hadn’t found any such issues. It was a platform API — think of it like an API powering NewRelic-like product used for internal purposes.

If I found anything interesting, I jot it down for reporting and also for updating these params in the other requests (for checking broken object level authorization).

Oh btw did I tell you that all the requests worked without an API token as well — broken authentication (except for the dashboard retrieval and deletion API)! Another easy one down the lane!

Now things became quite interesting! The API let’s anyone make requests. This already was quite a serious issue.

But I thought not to stop here and see how far I can go with it.

Then I decided to tamper with the parameters and see if sending wrong parameters works or returns error or worse — crashes the application code, sending back 500 errors!

And what I found was that all the API happily accepted the parameters sent by me, without any validation!

Also, when the input was something unexpected — for instance sending a string when an int was expected — returned back 500. That’s not a security issue but a UX issue!

As a developer you should handle all your errors properly! And it’s quite important to get notified of any errors that happen in production.

For that, you can use Sentry or Rollbar! Maybe there are more, but I was satisfied with Sentry and Rollbar and didn’t had to look much further :)

Anyways back to the pentester’s world!

Sweet! That’s another one of those dumb mistakes I see quite a lot.

So I now knew there’s a lot more juicy stuff I can get out of this API hopefully.

I thought to try out rate-limiting checks too, because why not! They are quite easy to carry out and I can see how the APIs handle that.

I opened the Postman Runner and ran all the APIs 200 times just for fun!

What I found was:

There was no rate-limiting at all!A bunch of 500 errors occasionally.Arbitrary parameters get accepted by all APIs (already saw this one, nothing new)

Nothing very juicy, just got a point that rate-limiting should be added to avoid API abuse — DoW (Denial of Wallet), DoS (Denial of Service), or bruteforce attacks, but other than that I didn’t found any other issues here.

So I moved on to testing other APIs.

Going Pro!

Next I looked for authorization issues, including both broken object level authz and broken function level authz as well as Mass Assignment!

APIs relating to dashboard & alerts retrieval and deletion were interesting because those expected the user id and if I could export dashboard of other users or delete their assets, it could be much more fun!

So I tried to look for these issues and surprise surprise… the authorization checks were broken! All I had to do was — specify the user id of the victim and check the response. No magic. The user id’s were quite big — 10 characters (regex →[A-Za-z0–9]), so bruteforcing was not such a good option I suppose.

And therefore I left this out as is, since there wasn’t a way for me to leak someone’s user id in the first place.

Tried Mass Assignment too, but no luck there xP

Going Expert ;)

Now I was done with all the issues I wanted to try out except the last one — my favourite — Injections!

Image Credits: The mighty — SecurityGOAT! Hahahaha

Command Injection looked impossible due to no such requirement of using OS interpreters, so I didn’t had to go down that road.

SQL Injections then? Yes I looked for this and surprise surprise, even in 2021, a developer was so naive to have added SQL Injections in their code!

It would definitely look something like I had shown here:

So I got the prelim checks all working:

Added a single quote and things broke in the backend — got 500 errorsAdded a ’ OR 1=1 -- and it worked like a charm (ofcourse URL encode your payloads if they are passed as GET params!)

Now I thought to use sqlmap and see how far it can take this — I was expecting the dump of the DB — maybe some API tokens, password hashes (and credit cards too?? haha).

Things looked quite promising when sqlmap confirmed the SQLi, but then came the biggest hurdle — the database couldn’t be detected by sqlmap :/

This was bad. I checked online but didn’t found much to carry on this route. So I had to add the word “potential” to this bug — A “potential” SQLi! Not a huge fan of this drama but at the end of the day if someone finds out the DB and exploits this vulnerability, it’s an issue!

Going Full Force with last 2 tests

Lastly, I just thought why not try out Host Header Injection issues and some business logic flaws!

Host Header Injection issues are quite fun and if I found something, then I could probably have SSRF or web cache poisoning or maybe more.

Went that route but had no success there :/

Then my last hope was business logic flaws and guess what I found some — I was able to bypass they constraints that they had in the battle-tested APIs but not in these staging ones! So I was able to perform restricted actions without getting blocked and locked out. Sweet :)

Read Entire Article