WEb практикум. Web'cepbep
Скачать 4.76 Mb.
|
// Connect to MySQL $connection = new PDO('mysql:host=127.0.0.1;dbname=testdb', 'testuser', 'password'); // execute sql $username=$_GET['username']; $sql = "SELECT * FROM phone WHERE lastname like '". $username . "'"; $query = $connection->prepare($sql); print($sql . " "); $query->execute(); if ($query->errorInfo()[1]) { print_r($query->errorInfo()); } echo "Search: $username"; // show result ?>
Смысл сценария в том, чтобы отобразить поле для ввода имени пользователя. Введенное имя применяется для поиска в таблице базы данных phone. Для поиска используется следующий запрос: SELECT * FROM phone WHERE lastname like '?' На место вопросительного знака попадает значение, которое передается через поле ввода. На рис. 5.3 показаны результат работы сценария и итоговая таблица для случая, когда пользователь передал сценарию знак процента. Процент в данном случае соответствует совершенно любому количеству любых символов, то есть мы увидим все записи. Для данного примера я использую таблицу phone, которую недавно создал, чтобы тестировать запросы, и она тут отлично подойдет. Структура таблицы: CREATE TABLE 'phone' ( 'phoneid' int(11) NOT NULL AUTO_INCREMENT, 'firstname' varchar(20) DEFAULT NULL, 'lastname' varchar(20) DEFAULT NULL, 'phone' varchar(100) DEFAULT NULL, 'cityid' int(11) DEFAULT NULL, 'm' int(11) DEFAULT NULL, PRIMARY KEY ('phoneid'), ) Итак, если указать имя пользователя, то сценарий получит соответствующую строку из базы данных и отобразит ее на web-странице. Если имя указано неверно, то результат будет пустым, то есть результирующая таблица будет пустой. Главная проблема этого сценария в том, что он не проверяет параметр, который вводит пользователь, а пользователь может передать что угодно. Если пользователем является хакер, то он без проблем сможет взломать web-сервер, на котором работает этот сценарий. Теперь самое интересное: что если в качестве имени пользователя передать одинарную кавычку? В результате SQL-запрос будет выглядеть следующим образом: SELECT * FROM phone WHERE firstname=''' Имя posterName сравнивается с тремя одинарными кавычками, что неправильно. Запрос оказывается незавершенным, и в результате мы увидим ошибку (рис. 5.4). Если мы добились ошибки в SQL-запросе, то существует вероятность того, что текущие настройки позволят нам получить доступ к чему-нибудь интересному. Давайте посмотрим, чего именно можно добиться. Но для начала необходимо узнать, сколько полей возвращает SQL-запрос. Да, в данном случае мы можем увидеть это в нашем сценарии, но в реальных условиях нам будет доступно только сообщение об ошибке, в котором нет необходимой информации. А если передать теперь не просто одинарную кавычку, а следующий текст: 1' union all SELECT 1, GRANTEE, PRIVILEGE_TYPE, 1, 1, 1 FROM information schema.USER PRIVILEGES where 1='1 Этот текст превратится в запрос: SELECT * FROM phone WHERE firstname='1' union all SELECT 1, GRANTEE, PRIVILEGE_TYPE, 1, 1, 1 FROM information schema.USER PRIVILEGES where 1=’1
Рис. 5.4. Результат передачи в качестве параметра одинарной кавычки • ® ® injection test X + f 4 С A Not Secure phpbook/sql.php?usemame=l9/o27+union+all+SELECT+1%2C+GRANTEEyo2C+PRIVILE... Hi Apps EfJ Work Й Dev ЁВ Other Bookmarks Injection test Search: 1' union all SELECT 1. GRANTEE. PR1V1LEGE.TYPE. 1.1.1 FROM information.schcma USER PRIVILEGES where 1=1 SELECT • FROM phone WHERE lastnamc like T union all SELECT 1, GRANTEE. PRIVILEGE.TYPE, 1.1,1 FROM information_schcma.USER_PRIVILEGBS where l=rl' Firstname Lastnamc Phone
Это приведет к тому, что мы увидим информацию из системной таблицы о привилегиях (рис 5.5). Если вы до сих пор не знали о проблеме SQL-инъекции, то, надеюсь, этим примером я смог вас заинтересовать. Теперь давайте разберемся с проблемой подробнее. Сбор информации Прежде чем продолжить атаку, хакеру необходимо собрать как можно больше информации о системе, в которую он попал. Желательно получить следующую информацию: Количество колонок, выбираемых запросом, в который мы внедряемся. Как разработчик сценария и таблицы я знал, что в ней 6 колонок и все они выбираются, так что мне не составляло труда эксплуатировать это. Какие именно поля отображаются. Тут я тоже знал, что отображаются второе, третье и четвертое поле из выполняемого запроса. Имея эту информацию, можно его попробовать проникнуть дальше и выяснить имена пользователей в базе данных и пароли. В этом разделе мы поговорим о том, каким образом можно получить максимально возможную информацию. Итак, как можно посчитать количество полей? Самый простой способ — объединение union с другим SQL-запросом. Когда два SQL-запроса объединяются через union, то они будут выполнены, только если оба возвращают одинаковое количество полей и типы соответствующих полей имеют совместимый тип. А с каким SQL-запросом нам произвести объединение? Что он должен возвращать и откуда брать данные? Да ниоткуда и ничего. Он просто должен возвращать нулевое значение. Нулевые значения позволят нам забыть о типах полей. Например, следующий SQL-запрос вполне корректен и возвращает одно пустое значение (null): SELECT null Можно возвращать какую-то строку: SELECT '1' Что выбрать? Зависит от ситуации. В нашем случае нужно второе. Дело в том, что мы внедряемся в запрос между двумя кавычками: SELECT * FROM phone WHERE firstname='' Мы должны передать хотя бы одинарную кавычку и после этого запрос. Если мы передадим: ' union select null то получится: SELECT * FROM phone WHERE firstname='' union select null' Но проблема в том, что в конце осталась одна кавычка, которая открыта, но не закрыта. Чтобы от нее избавиться, можно использовать второй подход с выбором строки, то есть передать: ' union select ' 1 что приведет к этому запросу: SELECT * FROM phone WHERE firstname='' union select '1' В результате сервер отобразит ошибку: The used SELECT statements have a different number of columns что означает — в данном SELECT-запросе используется некорректное число колонок. Это не дословный перевод, но смысл верный. Значит нам нужно добавить еще колонку ' union select '1', '1 Потом еще: ' union select '1', '1' , '1 И таким образом продолжать добавлять, пока запрос не выполнится. Так как запрос в коде возвращает 6 колонок, то мы добьемся результата, добавив 6 колонок: ' union select '1', '1', '1', '1','1','1 Сообщение об ошибке упростило поиск, но в реальной жизни на большинстве сайтов сообщения об ошибках отключены, и в этом случае можно увидеть два варианта поведения сайта — при ошибке он будет отображать ошибку типа 404 или просто пустую страницу. В любом случае — это признак того, что мы предоставили некорректное количество колонок и нужно увеличивать, пока страница не отобразится с какими-то данными. Примечание В SQL-запросе можно вместо пробела указывать знак плюса, например: union+SELECT+null,+null. Иногда такая запись запроса более удобна, особенно если пробелы отфильтровываются сценарием. В некоторых случаях подбор могут упростить символы комментариев — двойное тире или /* на конце. Например, можно попробовать передать: ' union select '1' /* Это работает не всегда — зависит от кода и базы данных, которые использовались на сервере. Но попробовать использовать символы комментария все же можно, и если они работают, то это упростит жизнь. А что означает /* на конце? Эти два символа указывают СУБД, что весь последующий текст в SQL-запросе — это комментарий и его выполнять не нужно. Если не указать этого, то СУБД вернет ошибку в любом случае. Почему? Если после внедрения нашего объединенного SQL-запроса не поставить комментарий в конце, он примет следующий вид: SELECT * FROM smf_polls WHERE posterName='' union SELECT null ' Обратите внимание на одинарную кавычку в конце. Она лишняя, и из-за нее СУБД не сможет выполнить SQL-запрос. Благодаря комментарию мы можем отбросить эту точку: SELECT * FROM smf_polls WHERE posterName='' union SELECT null /* ' Запрос может быть и более сложным: SELECT * FROM smf_polls WHERE posterName='$username' and field2='$param' ORDER BY posterName LIMIT 0, 25 Тут уже после переменной $username еще очень много операторов, и они также представляют для нас проблему. Символ комментария позволяет отбросить эту часть SQL-запроса. Все примеры, которые мы рассматривали ранее, подразумевают, что переменная, в которую мы включили SQL-запрос, является строковой. В запросах переменную такого типа необходимо заключать в одинарные кавычки. Если переменная должна быть числовой, то ее заключение в одинарные кавычки не является обязательным. Например: SELECT * FROM poll WHERE pollid = $pollid В данном случае переменная $pollid не заключается в кавычки, а значит, кавычку не нужно передавать в качестве параметра. Достаточно просто передать вставку SQL-запроса. Помимо стандартных SQL-запросов select каждая СУБД поддерживает свои расширения, с помощью которых можно получить подробную информацию об объектах базы данных. Позже мы обсудим нюансы сбора информации в Microsoft SQL Server, а в этой главе мы говорим о связке PHP + MySQL, и значит, рассматриваем именно MySQL. Начнем с оператора SHOW. Он имеет несколько вариантов, и первый из них, который мы рассмотрим, будет отображать доступные базы данных. Для этого необходимо выполнить оператор show databases. В результате на экране будут отображены имеющиеся базы данных: |