You may be thinking, “Golly, I’ve never heard of an ID Substitution Attack”. And you would be right.. because I completely made that up. But honestly, it’s a better name than “Insecure Direct Object Reference” (IDOR) – which sounds overly complicated. And we don’t want any of this to be overly complicated, because that’s a misrepresentation. Hacking is not always hard. I want to tell you all about my favorite bug type to hunt. It requires no programming knowledge or expensive software, operating systems, or advanced tools. Yet it makes big money and helps the internet and companies in a very big way. However, let’s not get ahead of ourselves. We first need to know what the heck a “Z-winK” is.
About Z-winK
I started tinkering with computers when I was about seven. The PC components were each about the size of a laptop at that time, and the internet was a series of massive computers connected through screeching dial-up modems. My father managed our AOL (dial-up service) account at the time, and when I asked him to create me a user, he used the automatic username creation feature and “ZwinK” was generated and born. I ended up using the name while gaming all through the 1990’s and 2000’s, and it just stuck. Much of my computer background came from online gaming and all that entails.
I bug bounty hunt from Windows OS, which I think is fairly unique in the field. Because I started on computers so long ago, I grew up on ancient IBM machines running DOS and Windows v3.0, and progressed through the Windows evolutions. To me, it’s an efficiency thing, not a security thing. There is very little I don’t know about the operating system and this allows me to be very, very efficient when bug bounty hunting. You don’t need to run Kali Linux to make $500,000 a year hacking, just an OS you can use well.
Fast forward 30 years and I am now 37 – which is well above the average age of most bug hunters. While I do have college degrees, they aren’t in “computer science”, and I also possess zero cyber security certifications because I don’t value them. I live on the east coast in the United States, and bug bounty hunt part-time. I have a great full-time position as a web penetration tester, which I actually obtained via hacking a program through Bugcrowd. Pretty sweet right?
I started bug bounty hunting with Bugcrowd in October of 2020 – and for reference, that was the year all the toilet paper ran out. So I’ve been at this for about 1.5 years now. I made $100 my first month bug hunting and over $100,000 last month. That’s ludicrous right? Well, I’m here to tell you it’s not magic, it’s mostly broken access control issues. The great thing about crowd-sourced security is that everyone brings something unique to a target, and this is the attack vector I feel rather artisan-level at, if it’s possible to be artisan-level at swapping IDs out. So let’s talk about it!
ID Substitution Attacks
What the heck is an “ID Substitution Attack”? Well, it’s my title for a bug that falls into the broad category of “Broken Access Control” issues, specifically the “Insecure Direct Object Reference”. To break this down, let’s start with a real-world example of what this looks like outside of computers.
A Hypothetical Banking Transaction
So you walk into a bank, and you want to withdraw some cash. But you don’t want to withdraw your money, you want to withdraw someone else’s money because you don’t actually have any. You know someone else’s bank account number.
<begin transaction>
Teller: “Welcome to the National Bank of Awesome. How may I help you”?
Z-winK: “I’d like to withdraw $10,000 in cash please”.
Teller: “Oh that’s amazing, but security is important. Can I please see your license”?
Z-winK: “Sure, here is my license.”
Teller: “Great, I can see that your face matches the photo on your license”.
Z-winK: “Well, my face was furrier then, but I appreciate that.”
Teller: “What is the account number you wish to withdraw $10,000 from?”
Z-winK: “Great question, let’s withdraw from account 523849RS10.1”
Teller: “OK just one second… in what denominations would you like that cash?”
Z-winK: “Surprise me, I’m feeling lucky”.
<end transaction>
So what happened here? The teller checked that Z-winK had a license and that it matched his face to establish valid identity. But the teller did not check that the account number Z-winK provided in fact belonged to Z-winK, despite having a validated identity. That may sound ludicrous, but that is exactly how these attacks work on computer systems. It’s like a bank teller forgetting to check the person withdrawing the cash actually has access to the bank account!
Why Does This Happen on Computers?
In general, proper authorization and access needs (3) steps:
- Does a valid authentication token/cookie exist?
(roughly translated, is the user logged in)- If not, return an Access Denied 401/403 error.
- If auth exists and is valid, does the user have the proper role(s)/scope(s)?
(roughly translated, does the user have access to this feature)- If not, return an Access Denied 401/403 error.
- If authenticated with proper roles, does the user have access to requested IDs?
(roughly translated, can the authenticated user ID access the IDs requested)- If not, return an Access Denied 401/403 error.
A lack of the third check above is generally what leads to this security vulnerability. The server checks that user A is authenticated, and that user A has the right roles/scopes for the request type, but does not check if user A has access to the ID being requested.
It is sometimes a burden for developers to make this extra check, depending on architecture, which is why it is so prevalent in the wild.
Pre-Requisites:
In order for targets to be susceptible to ID substitution attacks, the targets need to utilize IDs which can be substituted with other valid values. IDs can take many forms, but are generally alpha-numeric and iterable. The more objects a particular ID represents (EG: millions of accounts versus 100 accounts), the more likely the ID can be easily substituted with another ID and obtained. Additionally, larger numbers of victims generally equates to higher impact.
To check for ID substitution issues (IDORs) safely, you need two accounts on the target. The goal is to try to get to the 2nd account’s IDs/objects from the first account by simply substituting IDs. So you would authenticate as account #1, and use the cookies/auth tokens from account #1 to try to access the IDs from account #2. If this works, generally it represents a grave PII data leak/breach risk, or integrity impact on the system. And the best part about these vulnerabilities is that each one is usually a separate issue that doesn’t share a single root cause fix, as broken access control is usually a per request problem.
It is highly recommended that you test for these manually, not with automation, because automation misses a lot. I just recently found one of these issues that would dump the credit card information for millions of users, and a bot would have missed it!
Organization:
When testing for these issues, organization is key. Keep two sets of credentials on your clipboard in a notepad or some easily accessible location so it’s easy to substitute IDs you find.
Account1@place.com
Name: Bob Smith UserID: 111111 ProfileID: 54321 BusinessID: XHGFY123 Auth Cookies 1 |
Account2@place.com
Name: Tina Turner UserID: 222222 ProfileID: 12345 BusinessID: YTNFK432 Auth Cookies 2 |
With this organization in place, it’s now very simple to login as Account 1 and any place you see instances of IDs “111111”, “54321”, or “XHGFY123” – try changing the IDs with the information from Account 2 and see what happens. Do you get back the information for account 2 with GET requests? Is the information for account 2 changed with *PUT/POST/PATCH/DELETE requests? If so, you get free money and make the world a safer place! Hooray you!
*Remember when testing PUT/PATCH/POST/DELETE requests for ID substitution issues that you can have a grave integrity impact on the account, so always test these with a 2nd account that YOU own, or ask the program for help. GET requests are generally harmless from an integrity standpoint, but not always – so be careful. This is another reason I don’t recommend testing these with automation.
Testing Methodology:
- Login to Account 1 and click everything you can possibly find. This means filling in information for all features. I mean ALL THE FEATURES. If you skip a feature because you don’t want to enter a valid credit card number, for instance, you are likely missing between 2 and 20 different API calls and 3rd party interactions you could have tested. This is the reason I found a massive credit card leak recently, I guess every other hacker didn’t enter a credit card. Seriously, push all the buttons and do everything you can find to cause as many requests as possible.
- After step 1, go through all the requests and substitute all the IDs for account #2 and see what happens.
- Now, get creative and start looking for hidden URLs the software isn’t actively using. These can be in JS files, found in search engines, the way back engine (historical versions of the website), by brute forcing or fuzzing with wordlists, and so forth. Rinse and repeat.
Brain Training:
Train your brain to spot these in URLs and parameters, both URIs and payload bodies. That is all there is to it.
Example: api.website.com/admin/users/123456789000/creditData/?payment=USD&place=US
* A request just like this returned CC data for any user by ID only. Oops!
Get Started with Bugcrowd
Every minute that goes by, your unknown vulnerabilities leave you more exposed to cyber attacks.