Why your perfectly checked input is now tainted. And how to FIX IT.
You’re an amazing developer, you taint check all of your incoming data and you religiously guard against SQL injection and you check for possible buffer-overflow data patterns and you…you… you check for everything.
…you are religious and you are perfect in every way — nobody can taint your incoming user code… impossible!
There is one thing you’ve never thought about and is already being exploited without any press coverage. Your MOBILE APP, your API or your website could well be vulnerable and although this is a sophisticated incredibly simple hack there are general lessons to be applied regardless.
Current “thinking” is way too orientated to “tainted” input.
(I will update this article later for a few more scenarios but let me give you a typical one.)
The “ADD NEW CLIENT” scenario:
- Your business, your mobile app, your website or your API allows a business to sign up to your “product” or “service”. Of course!
- And your product or service allows employees/clients/xyz of that business to also sign up to it. Of course, collaborate that’s what we do!
- And the first person to sign up for that company is the “Administrator account”, they typically set up the billing and invite all other users and set up the “settings”… Of course!
….the parameter attack coming up….
The “ADD NEW CLIENT” developer TRAP scenario:
- If there is already a business with the “same address” and the “same name” then tell any user creating an account a message similar to
Hey, a business at your address with a similar/same name as you specified has already been added to our system. Please contact your IT or HR department at your company in-order to gain access as this company is already registered.
- As a developer, you recognize that you need to alert potential users of the fact that actually their business, organization has already got an account! Of course!
Stay with me…. imagination required… this is not your normal injection!
Here might be your typical DEVELOPER coding steps:
- Analyse ADDRESS of the business, get address_id.
- Analyse “SUPPLIED NAME” of the business against ALL existing BUSINESS_NAMES which are in the DB and recorded with address_id.
- Use a standard Damerau–Levenshtein distance algorithm to determine if the SUPPLIED NAME is similar or the same to the DB name. (very common, this is just ONE example of much)
- If too similar, tell client “Hey, a business at your address with a similar/same name has already been added to the system…”
- If NO MATCH, add the client with the logged in user as ADMIN.
I will update with some tables and stuff cos I hope I think it sounds complex when I read it back but really it is quite simple.
So here is the thing:
- There is a BUG in the Damerau–Levenshtein library you are using.
- With completely normal input it can sometimes return NO MATCH.
- When “NO MATCH” is returned you add an ADMIN USER to the DB.
- You use an “INSERT INTO ON DUPLICATE ” statement to add the ADMIN USER.
- Actually there was an IDENTICAL MATCH but the BUG…
- You have now added a new ADMINISTRATOR to that client when you thought the client did not exist but really it already has 7,000+ users attached to it but the specific sequence of characters in it’s name meant it was vulnerable to that bug and although 1 in 10,000 company names don’t work… then 1 in 10,000 do.
- The ON DUPLICATE meant when you added the DUPLICATE the ID was returned of the existing CLIENT and you just added that as an ADMIN.
You now have total control of the entire company with 7,000+ users.
HOW CAN YOU PREVENT A PARAMETER HACK?
- Parameter attacks mostly (always in practise) work on unique values as unique values are typically attached to “an account”, “a security level”, “a resource”. If it’s not UNIQUE, then probably it’s not a killer driller….
- If your code derives a UNIQUE value from any library, module, external API or ANYTHING that you do not directly control then….
- ONCE the UNIQUE value has been derived (from the library, module, etc) Spend an additional SQL/DB/LOOKUP call to actually check that the derived UNIQUE value actually EXISTS or DOES NOT EXIST within your data-sphere.
- If “NO MATCH” is returned… CHECK IT WITH A DIRECT CALL WITH THE UNIQUE VALUE.
- You are using the LIBRARY to help you derive something… this is find, good and dandy… BUT… if that thing is somehow a UNIQUE FIELD then make an EXTRA CALL TO CHECK THAT UNIQUE FIELD.
- Simply, check “NO MATCH” and check “MATCH” → In your DB store.
- DO NOT TRUST THE LIBRARY — TRUST YOUR DATABASE.
- As the library can have BUGS and unless you CHECK then your amazingly super input which you have checked can be vulnerable to a BUG whose input is perfectly in-line with everything.