I am in charge of the security of several websites. Recently we bought the Acunetix WVS and performed several tests that gave positive on SQL injection. Not only am I trying to replicate these tests but I also trying to find out if it is possible to extract relevant information, given that we have a WAF/IPS deployed that mitigates the more critical SQL injections.
I am obviously not allowed to disclose the website I am currently testing on, for that I will refer to it as http://example.com.
I was able to find out that on the page http://example.com/show_results.php?group=1&query=members there are 2 queries being used to display the results. So maybe, this is related to Routed SQL Injection. I have applied several SQL injection techniques, and through the error messages I have identified that they take the form of:
SELECT 1 FROM ... WHERE group='1' LIMIT 0,1
SELECT 1,2,3,4 FROM ... WHERE group='1' AND `user_type` NOT LIKE 'colaborators' ORDER BY 'name'
Both queries are displayed on individual <div>. user_type represents the members on the URL. The first query is obviously very easy to inject. I can get the database name by simply changing the URL to:
http://example.com/show_results.php?group=1' union select database() -- - &query=
The obvious question here is: How to get the table names for instance. Something like:
GROUP_CONCAT(table_name) FROM information_schema.tables WHERE TABLE_SCHEMA=database()
This is tricky because both queries use the same group parameter, so it is not entirely Routed SQL Injection in its simplest form. Something like this:
http://example.com/show_results.php?group=1' union select database() -- -&query=members
Note the difference brom above on the query parameter. This shows me the error of The used SELECT statements have a different number of columns (obviously because both queries have a different number of columns).
Related
I apologize if the question asked is simple and far too stretched out
I've been studying the fundamentals of SQL Injection attacks and at a point, I've read that a potential method of verifying the data type of a particular field in a UNION SQL Injection attack is by making certain changes to the request that we make to the target server.
The software being used is BurpSuite, and the target is a practice lab on the website https://portswigger.net. It is told that the category field is vulnerable, and that the query for the category returns two fields, so the following "code" is appended to the category part of the HTTPS GET message that we send to the server in an attempt to know the data type of the first field
...?category=Gift'+UNION+SELECT+'abc',NULL+FROM+table_that_contains_target_information--
According to the manuals for the lab, what we're doing with the 'abc',NULL part is that we're verifying that the first field in the "table_that_contains_target_information", is of the type string/varchar. This seems to work and aids one in determining the data type of that particular field, as I checked in the lab, since if the first field's datatype is not a string/varchar, I would get an internal server error.
However, later on, when performing a query on a general database to check what the 'abc',NULL part does in a general SELECT query, the results that come are somewhat different.
The general database question is one that is available to use for learning SQL at https://www.w3schools.com/mysql. It has a table named Customers with several fields. I know for a fact that the first field is an integer (via verification with information_schema.columns), and the rest are varchars. For context, in the Customers table, CustomerID is an integer field, and CustomerName is a varchar field. I ran the following query on the server
|-------This part is the original query-------||This is our inserted code to find out information about the database|
SELECT CustomerID, CustomerName FROM Customers UNION SELECT 'abc',NULL FROM Customers
Disclaimer: My intentions were not to cause any harm to the database, only to learn some SQL
According to what I've learnt from the Lab manuals, I should be getting an error of sorts right?, since I'm checking the datatype of a field in a method that and unethical hacker could potentially take. However, all I get are the results of the original query and an extra record with values 'abc' and the second field is NULL.
I've tried to read up on documentation regarding the SELECT query in SQL, but as far as I was able to find, most of them tell about the general SELECT * FROM table_name or SELECT column_name, other_column_name FROM table_name. I wasn't able to find anything about the SELECT 'abc' FROM table_name.
Could anyone please explain what is going on here? Does the SELECT 'abc' FROM table_name just give me an extra record with 'abc'? If so, then why is this same query used to verify the datatypes in the lab at https://portswigger.net, and it seems to work its purpose there.
Thank you for answering in advance!
I'm currently thinking about a database schema in MySQL where I store SELECT queries into a certain table column, just to execute them on-the-fly when getting selected, and having the result passed instead of the actual query.
Would this be possible somehow? Or may this be bad practice? Is it even technically possible to have a result table passed to a single field, at least so I could run the query through PDO to get back a nested result array? Are there any alternatives?
I've read that this may be achieved through stored procedures, and although I grip the concept of those I can't think of how I could use those to achieve that.
You could do this, but what purpose do you have for doing it?
I would suggest using views:
The syntax should be valid when the view is created, unlike storing
the SQL in a field which may have invalid syntax.
It's easier to debug and modify.
For example, let's say one of the queries you want to store is:
SELECT product_category, COUNT(*) AS category_count
FROM product
GROUP BY product_category;
You can create a new "view" object that defines this query:
CREATE VIEW prod_cat_count AS
SELECT product_category, COUNT(*) AS category_count
FROM product
GROUP BY product_category;
Now, the object called "prod_cat_count" is stored in the database. Internally, the database just knows that "prod_cat_count" is equal to the SELECT query we mentioned. When the view is created, the database validates the syntax (checks that all columns exist, checks you haven't forgotten the GROUP BY, for example)
Then, whenever you want to get this data/run this query, you can run this statement (in SQL or in application code, for example):
SELECT product_category, category_count
FROM prod_cat_count;
If you then decide you want to change the way the product categories are counted, you can adjust the view:
SELECT product_category, COUNT(*) AS category_count
FROM product
GROUP BY product_category
ORDER BY product_category;
Hope that helps!
I'm doing a school project about webapp security and vulnerabilities, and for that i've made my own very simple website with a login and a search bar.
Now, i've made the login silent on purpose, while the search bar shows output on SQL injections. So for demonstration purposes I would like to do a time-based completely blind SQL injection attack on the login... But i'm a bit stuck.
I have no idea how I can enumerate how many columns are in the table for the login query, in a time-based situation:
SELECT * FROM customer WHERE cMAIL='' AND cPWD='';
I cannot do ORDER BY n;#, since I get absolutely no error output when something goes wrong. The only way I can get any indication is through SLEEP() or BENCHMARK().
But I cannot put ORDER BY into a SELECT IF() statement. So how can I find out how many columns exist? (it makes unions impossible for me).
Thank you!
EDIT: It might be worth mentioning, the site is very unsecure on purpose (doesn't use mysqli, just mysql). Since it is just for demonstration
Okay, I found an answer to my own question:
Instead of using ORDER BY, I used SELECT and bruteforced my way to see how many columns existed:
SELECT * FROM customer WHERE cMAIL='' AND cPWD='';
Can be enumerated in the dark by doing:
SELECT * FROM customer WHERE cMAIL='' UNION SELECT null,null,null AND SLEEP(5);# AND cPWD=''
You just keep adding more nulls to the select until the database sleeps for 5 seconds. Then you know how many columns are in the given table.
Hope this can help someone else.
The table names are variable, but what is certain is that SELECT only is allowed and certain tables are excluded (ie Users, Log). I'm making a reporting form where a user can just enter sql queries to make template reports.
SELECT 'field1' As 'foo', 'field2' as 'bar'.. 'fieldn'
FROM 'table1',..'tablen'
JOIN ... ON ...
WHERE CONDITION
Although I'm thinking I can have the table names in a html select list of existing tables.
Also make a user reporter_appname#localhost with SELECT access only to all tables except Users and Log? In that case I won't need to bother with a regex check of the query?
(This would be in PHP)
(Ideally I just wanted a single textarea where the admin can just type their query, my report function would then take the output and present it nicely etc.)
I suggest you re-think your design.
Identifying valid select statements (and excluding all other statements) is basically impossible without completely parsing SQL. A regex is not going to be up to the task.
Even if you allow only select statements, users could perform denial-of-service attacks on your database. It is very easy to create select statements that run forever (we've all done it). A malicious user could crash your site in a hurry. And even well-intentioned users might do this by accident.
It would be much better to give the users more limited options for creating reports. Let them select certain tables and columns from a list, and create the appropriate query for them.
There is probably free MySQL reporting software out there that could serve as a good starting point, though I don't have any experience with this myself.
I think that you should rethink the design of your application.
The Users and Log tables should be on one database and the tables with the data for the reports should be on another database.
If you have them all in one database already just create another database, link them and then create synonyms from one database to another only for the tables that the user can access via his queries.
The user will run his queries on the database you have just created and he will be limited to those tables that have synonyms on it.
I do not know if this would be the best option because your description of the case is relatively vague but based on the information I have this could be a solution.
Is there a way that I can do a select as such
select * from attributes where product_id = 500
would return
id name description
1 wheel round and black
2 horn makes loud noise
3 window solid object you can see through
and the query
select * from attributes where product_id = 234
would return the same results as would any query to this table.
Now obviously I could just remove the where clause and go about my day. But this involves editing code that I don't really want to modify so i'm trying to fix this at the database level.
So is there a "magical" way to ignore what is in the where clause and return whatever I want using a view or something ?
Even if it was possible, I doubt it would work. Both of those WHERE clauses expect one thing to be returned, therefore the code would probably just use the first row returned, not all of them.
It would also give the database a behaviour that would make future developers pull their hair out trying to understand.
Do it properly and fix the code.
or you could pass "product_id" instead of an integer, if there's no code checking for that...so the query would become:
select * from attributes where product_id = product_id;
this would give you every row in the table.
If you can't edit the query, maybe you can append to it? You could stick
OR 1=1
on the end.
You may be able to use result set metadata to get what you want, but a result set won't have descriptions of fields. The specific API to get result set metadata from a prepared query varies by programming language, and you haven't said what language you're using.
You can query the INFORMATION_SCHEMA for the products table.
SELECT ordinal_position, column_name, column_comment
FROM INFORMATION_SCHEMA.columns
WHERE table_name = 'products' AND schema_name = 'mydatabase';
You can restructure the database into an Entity-Attribute-Value design, but that's a much more ambitious change than fixing your code.
Or you can abandon SQL databases altogether, and use a semantic data store like RDF, which allows you to query metadata of an entity in the same way you query data.
As far out as this idea seems I'm always interested in crazy ways to do things.
I think the best solution I could come up with is to use a view that uses the products table to get all the products then the attributes table to get the attributes, so every possible product is accounted for and all will get the same result