This question is to settle an argument between me and a coworker.

Let's say we have the following query, executed on a standard LAMP server.

SELECT field1, field2, field3
FROM some_table
WHERE some_table.field1 = 123
ORDER BY field2 DESC
LIMIT 0, 15

Now let's assume the limit clause is vulnerable to SQL injection.

LIMIT [insert anything here], [also insert anything here]

The point of my coworker is that there is no way to exploit this injection, so there's no need to escape it (since it take more processing power and stuff).

I think her reasoning is stupid, but I can't figure out how to prove her wrong by finding an example.

I can't use UNION since the query is using an ORDER BY clause, and the MySQL user running the query doesn't have the FILE priviledge so using INTO OUTFILE is also out of the question.

So, can anyone tell us who is right on this case?

Edit: the query is executed using PHP, so adding a second query using a semicolon won't work.

有帮助吗?

解决方案

The LIMIT clause is vulnerable to SQL injection, even when it follows an ORDER BY, as Maurycy Prodeus demonstrated earlier this year:

mysql> SELECT field FROM user WHERE id >0 ORDER BY id LIMIT 1,1
       procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);
ERROR 1105 (HY000): XPATH syntax error: ':5.5.41-0ubuntu0.14.04.1'

Voilà! The above solution is based on handy known technique of so-called error based injection. If, therefore, our vulnerable web application discloses the errors of the database engine (this is a real chance, such bad practices are common), we solve the problem. What if our target doesn’t display errors? Are we still able to exploit it successfully?

It turns out that we can combine the above method with another well-known technique – time based injection. In this case, our solution will be as follows:

SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 1,1
PROCEDURE analyse((select extractvalue(rand(),
concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)

It works. What is interesting that using SLEEP is not possible in this case. That’s why there must be a BENCHMARK instead.

其他提示

I would insert this:

1; DELETE FROM some_table WHERE 1; --

Just after the limit, that will select 1 row from some_table, then DELETE all some_table rows. then the rest will be considered as a comment.

SQL Injection occurs if “externally-influenced input […] could modify the intended SQL command”. And in this case it’s clear that user input can modify the intended SQL command.

However, exploitability is another question. You may not be able to exploit it today. But maybe someday someone is able to exploit it because:

  • The database connection layer has changed and it is possible to execute multiple statements at once.
  • The statement has changed and it is possible to use UNION.
  • The user privileges have changed and it is possible to use INTO OUTFILE/INTO DUMPFILE.
  • Someone finds a way that you may not have thought of. Have you noticed you can store the result in variables and/or execute a stored procedure?

If you are creating the SQL query from user input, then it will be vulnerable, unless you are using strong typing way before you get anywhere near generating the query. In such a case, there's no way you could make an integer become text saying to drop a table, for example.

Never trust user input, and always perform bounds checking. For example, don't even bother doing the query if the limit values are negative.

You should come with a coding example, not a query example as you do now. She is right on the fact that you cannot really alter a statement since the order by function is always "last". So yes, when you are manually inputting those query's on a sql server, you simply cannot alter the query to output something different rather than a error message. Yet, when you simply add an other query.. you can :)

Finish the last user input as a true value to let the first query run successfully, and add a ';'. Now you can start your own query.

I just have done this on my local mysql server:

SELECT * FROM city order by ID desc limit 0,15; SELECT * FROM city

Even so, in a strong case where there is absolute 0% chance someone could alter the statement, you simply do not even want to receive possible altered data. There should be a reason you use the LIMIT dynamically. Once a person can change your reason, you already have failed. Its not even about risking damage, losing data or what ever. You do not want any manipulation in any way.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top