Взлом с помощью инъекций. SQL инъекции

Many web developers are unaware of how SQL queries can be tampered with, and assume that an SQL query is a trusted command. It means that SQL queries are able to circumvent access controls, thereby bypassing standard authentication and authorization checks, and sometimes SQL queries even may allow access to host operating system level commands.

Direct SQL Command Injection is a technique where an attacker creates or alters existing SQL commands to expose hidden data, or to override valuable ones, or even to execute dangerous system level commands on the database host. This is accomplished by the application taking user input and combining it with static parameters to build an SQL query. The following examples are based on true stories, unfortunately.

Owing to the lack of input validation and connecting to the database on behalf of a superuser or the one who can create users, the attacker may create a superuser in your database.

Example #1 Splitting the result set into pages ... and making superusers (PostgreSQL)

$offset = $argv [ 0 ]; // beware, no input validation!
$query = $offset ;" ;
$result = pg_query ($conn , $query );

?>

Normal users click on the "next", "prev" links where the $offset is encoded into the URL . The script expects that the incoming $offset is a decimal number. However, what if someone tries to break in by appending a urlencode() "d form of the following to the URL If it happened, then the script would present a superuser access to him. Note that 0; is to supply a valid offset to the original query and to terminate it.

It is common technique to force the SQL parser to ignore the rest of the query written by the developer with -- which is the comment sign in SQL.

A feasible way to gain passwords is to circumvent your search result pages. The only thing the attacker needs to do is to see if there are any submitted variables used in SQL statements which are not handled properly. These filters can be set commonly in a preceding form to customize WHERE, ORDER BY, LIMIT and OFFSET clauses in SELECT statements. If your database supports the UNION construct, the attacker may try to append an entire query to the original one to list passwords from an arbitrary table. Using encrypted password fields is strongly encouraged.

The static part of the query can be combined with another SELECT statement which reveals all passwords:

" union select "1", concat(uname||"-"||passwd) as name, "1971-01-01", "0" from usertable; --

If this query (playing with the " and -- ) were assigned to one of the variables used in $query , the query beast awakened.

SQL UPDATE"s are also susceptible to attack. These queries are also threatened by chopping and appending an entirely new query to it. But the attacker might fiddle with the SET clause. In this case some schema information must be possessed to manipulate the query successfully. This can be acquired by examining the form variable names, or just simply brute forcing. There are not so many naming conventions for fields storing passwords or usernames.

But if a malicious user submits the value " or uid like"%admin% to $uid to change the admin"s password, or simply sets $pwd to hehehe", trusted=100, admin="yes to gain more privileges, then, the query will be twisted:

// $uid: " or uid like "%admin%
$query = "UPDATE usertable SET pwd="..." WHERE uid="" or uid like "%admin%";" ;

// $pwd: hehehe", trusted=100, admin="yes
$query = "UPDATE usertable SET pwd="hehehe", trusted=100, admin="yes" WHERE
...;"
;

?>

A frightening example of how operating system level commands can be accessed on some database hosts.

If attacker submits the value a%" exec master..xp_cmdshell "net user test testpass /ADD" -- to $prod , then the $query will be: MSSQL Server executes the SQL statements in the batch including a command to add a new user to the local accounts database. If this application were running as sa and the MSSQLSERVER service is running with sufficient privileges, the attacker would now have an account with which to access this machine.

Some of the examples above is tied to a specific database server. This does not mean that a similar attack is impossible against other products. Your database server may be similarly vulnerable in another manner.

Example #5 A more secure way to compose a query for paging

settype ($offset , "integer" );
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset ;" ;

// please note %d in the format string, using %s would be meaningless
$query = sprintf ("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;" ,
$offset );

?>

  • If the database layer doesn"t support binding variables then quote each non numeric user supplied value that is passed to the database with the database-specific string escape function (e.g. mysql_real_escape_string() , sqlite_escape_string() , etc.). Generic functions like addslashes() are useful only in a very specific environment (e.g. MySQL in a single-byte character set with disabled NO_BACKSLASH_ESCAPES) so it is better to avoid them.
  • Do not print out any database specific information, especially about the schema, by fair means or foul. See also Error Reporting and Error Handling and Logging Functions .
  • You may use stored procedures and previously defined cursors to abstract data access so that users do not directly access tables or views, but this solution has another impacts.
  • Besides these, you benefit from logging queries either within your script or by the database itself, if it supports logging. Obviously, the logging is unable to prevent any harmful attempt, but it can be helpful to trace back which application has been circumvented. The log is not useful by itself, but through the information it contains. More detail is generally better than less.

    Перед тем как мы поговорим о том, как выполняется защита сайта от sql инъекций, нам нужно сначала разобраться что это такое и почему этот тип атак настолько опасен. Большинство современных сайтов уже делают все, чтобы на их страницах не было таких мест, через которые злоумышленник мог бы выполнить эту атаку, но все же иногда такие проблемы случаются даже на популярных ресурсах или в часто используемых движках.

    Сегодня мы разберем что такое sql инъекция, как она работает, а также как с ней бороться, как на уровне языка программирования, так и с помощью создания различных помех для злоумышленника с помощью веб-сервера. Это тоже достаточно эффективно.

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

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

    Например, вот так выглядит SQL запрос запроса id статьи при поиске:

    SELECT id,title,content FROM posts WHERE title LIKE "%запрос_пользователя%"

    А теперь представьте, что пользователь вместо ключевых слов из статьи введет такую комбинацию:

    1%"; DROP TABLE posts LIKE "%;

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

    SELECT id,title,content FROM posts WHERE title LIKE "%1%"; DROP TABLE posts LIKE "% %"

    Это сработает с любым запросом, в котором есть ввод данных пользователей, если программист не позаботился о безопасности. Следовательно, чтобы решить эту проблему нужно просто экранировать все кавычки в запросах от пользователя. А теперь перейдем к способам защиты.

    Защита сайта от sql инъекций на уровне PHP

    Защита от sql атак может выполняться различными способами. Первое, на что стоит обратить внимание и что очень важно - это чтобы программист уже во время написания кода занимался экранированием кавычек с помощью таких функций, как mysql_real_escape_string или mysqli_real_escape_string. Если каждая переменная, которая используется в запросах к базе будет профильтрована ими, программистом или на уровне CMS, то никаких проблем не возникнет.

    Но почему же на протяжении последних 14 лет все еще случаются атаки на SQL? Все просто. Программисты ленивы, а делать небезопасные запросы к базе так просто, в то время как безопасные - более сложны. Во всяком случае, сложнее, чем небезопасные.

    Защита на уровне веб-сервера

    Не всегда есть возможность исправить все недоработки в коде. Например, популярный движок Drupal имеет более 20 000 строк кода, WordPress - 60 000, а Joomla - 180 000. Было бы нецелесообразно все это переписывать. Но можно поступить по-другому. Сначала мы отфильтруем все значения из переменной REQUEST в самом начале скрипта. Вставьте этот код после подключения базы данных:

    if (!function_exists("clean")) {
    if (get_magic_quotes_gpc()) {
    function magicquotes_Stripslashes(&$value, $key) {
    $value = stripslashes($value);
    }
    $gpc = array(&$_COOKIE, &$_REQUEST);
    array_walk_recursive($gpc, "magicquotes_Stripslashes");
    }
    function clean(&$value, $key) {
    //эта функция экранирует все кавычки.
    $value = mysql_real_escape_string($value);
    }
    }
    $req = array(&$_REQUEST);
    array_walk_recursive($req, "clean");

    Для PHP 7 вам нужно будет использовать функцию mysqli_real_escape_string, поскольку расширение mysql было удалено из этой версии языка. Для экранирования кавычек используется лишь функция clean и все что ниже нее. То что выше применяется для совместимости с версиями PHP ниже 5.4. В них была настройка Magic Quotes, которая при включении экранировала все кавычки. Чтобы наш скрипт все не портил мы сначала все убираем экранирование если она включена.

    Теперь у вас есть дополнительная защита на уровне PHP. Осталось еще позаботиться про защиту на уровне веб-сервера. Если вы используете Nginx, то можно добавить такие правила в вашу секцию server:

    set $block_sql_injections 0;
    if ($query_string ~ "union.*select.*\(") {
    set $block_sql_injections 1;
    }
    if ($query_string ~ "union.*all.*select.*") {
    set $block_sql_injections 1;
    }
    if ($query_string ~ "concat.*\(") {
    set $block_sql_injections 1;
    }
    if ($block_sql_injections = 1) {
    return 403;
    }

    Здесь мы отфильтровываем все запросы, которые содержат слова select, concat вместе с кавычками. Это явный признак, что пользователь пытается выполнить SQL инъекцию, а значит его запрос нужно заблокировать.

    Также вы можете блокировать подозрительные адреса на уровне веб-сервера Apache, например, выбрать самые часто употребляемые ключевые слова SQL. Правда, это опасно, поскольку могут быть заблокированы и запросы обычных пользователей. Добавьте в вашу секцию VitualHost такие строки:

    RewriteCond %{QUERY_STRING} [^a-z](declare¦char¦set¦cast¦convert¦delete¦drop¦exec¦insert¦meta¦script¦select¦truncate¦update)[^a-z]
    RewriteRule (.*) - [F]

    Но это еще не полное решение, здесь можно пойти дальше. Эта блокировка не защищает от SQL инъекций, выполняемых с помощью POST или RESTful запросов. Еще можно активировать модуль mod_security:

    sudo a2enmod mod_security

    Здесь тоже есть несколько правил, которые защищают от инъекций. Но желательно использовать еще и более комплексный подход.

    Разделение базы данных

    Чтобы сделать вашу базу данных более безопасной, вы можете разделить ее на несколько частей. В области информационной безопасности существует такое понятие, как принцип минимальных привилегий. Суть принципа состоит в том, что программа или пользователь должны иметь доступ только к тому, что им нужно и нечему больше. Например, будет разумно хранить данные о кредитных картах пользователя и форумы в разных базах данных. Особенно, если формы используют устаревшую версию phpBB. Это своеобразная дополнительная защита sql injection.

    Анализ запросов перед приложением

    Еще один вариант - это использования более сложных систем защиты. Это может быть аппаратное решение, которое работает поверх iptables или ipfw или же система обнаружения вторжений на сервере HIDS, такая, как OSSEC. Но такое решение намного сложнее, чем нужно и не предназначено для решения нашей задачи. Можно использовать специальные брандмауэры веб-приложений, с помощью которых, в том числе, выполняется защита от SQL инъекций. Это такие свободные решения, как ModSecurity или IronBee.

    Выводы

    Нет идеального решения или волшебной палочки, с помощью которой бы получалась стопроцентная защита сайта от sql инъекций, хотя PHP стремится быть все более защищенным. Начиная с версии 7.0 была удалена поддержка расширения MySQL. Теперь необходимо переходить на MySqli или PDO. И это хорошо, потому что эти расширения делают проще использование данных с подготовленными операторами. Хотя для этого все еще требуется написать несколько строк.

    Существует множество способов выполнения SQL атак, но до тех пор, пока разработчики не будут писать правильный код, а на веб-серверах не будут на максимум использоваться средства защиты, эти атаки не исчезнут из списка ТОП 10 OSWAP. Настройте вашу систему так, чтобы защитить свои данные и базы данных.

    Похожие записи:


    Суть SQL-инъекций

    Наверное, уже слышали шутку из Интернета: «Почему во всех уроках рисования одно и тоже: Например, урок по рисованию совы. Сначала полчаса долго в деталях рисуем глаз совы. А потом - раз - за пять минут - рисуем оставшуюся часть совы ».

    Вот даже картинка по этому поводу есть:

    По SQL-инжектам материала море: статьи, книги, видеокурсы (платные и бесплатные). При этом не многие из них прибавляют понимания по этому вопросу. Особенно если вы новичок. Я хорошо помню свои ощущения: вот он кружок, вот он остаток совы…

    Цель этой заметки - натянуть глаз на сову дать нормальное просто объяснение, что же такое SQL-инъекции, в чём заключается их суть, насколько и почему они опасны .

    Для опытов, у нас будет очень простой и уязвимый к SQL-инъекции скрипт:

    Для доступа к Бобруйской районной библиотеке введите Ваши учётные данные:

    Введите ваше имя

    Введите ваш пароль


    query("SET NAMES UTF8"); $mysqli->query("SET CHARACTER SET UTF8"); $mysqli->query("SET character_set_client = UTF8"); $mysqli->query("SET character_set_connection = UTF8"); $mysqli->query("SET character_set_results = UTF8"); } $name = filter_input(INPUT_GET, "name"); $password = filter_input(INPUT_GET, "password"); if ($result = $mysqli->query("SELECT * FROM `members` WHERE name = "$name" AND password = $password")) { while ($obj = $result->fetch_object()) { echo "

    Ваше имя: $obj->name

    Ваш статус: $obj->status

    Доступные для Вас книги: $obj->books


    "; } } else { printf("Ошибка: %sn", $mysqli->error); } $mysqli->close(); ?>

    Вы намного больше поймёте, если будете всё делать вместе со мной. Поэтому вот . В нём два файла: index.php и db_library.sql . Файл index.php разместите в любое место на сервере - это и есть наш уязвимый скрипт. А файл db_library.sql нужно импортировать, например, при помощи phpMyAdmin.

    В файл index.php в качестве имени пользователя базы данных задан root, а пароль - пустой. Вы можете вписать свои данные, отредактировав строчку:

    $mysqli = new mysqli("localhost", "root", "", "db_library");

    По легенде, это форма входа в он-лайн версию Бобруйской районной библиотеки. Нам уже дали учётные данные: имя пользователя - Demo, пароль - 111 .

    Давайте введём их и посмотрим:

    Наши учётные данные приняты, на экраны выведено наше имя, статус и доступные для нас книги. Можете попробовать, с любыми другими данными (если поменять имя или пароль) мы не сможем войти и посмотреть доступные для чтения книги. Также мы не можем узнать, какие книги доступны для остальных, поскольку мы не знаем их имени и пароля.

    Подсмотрим в исходный код, чтобы понять, как произошёл запрос к базе данных:
    Слово SELECT в SQL-запросе показывает, какие данные нужно получить. Например, можно было бы указать SELECT name, или SELECT name, password. Тогда в первом бы случае из таблицы было бы получено только имя, а во втором - только имя и пароль. Звёздочка говорит, что нужно получить все значения. Т.е. SELECT * - это означает получить все значения.

    FROM говорит откуда их нужно получить. После FROM следует имя таблицы, т. е. запись FROM `members` говорит, получить из таблицы `members`.

    Далее WHERE , если вы изучали какие-либо языки программирования, то это слово больше всего напоминает «Если». А дальше идут условия, эти условия могут быть истинными (1) или ложными (0). В нашем случае

    (name = ‘$name’) AND (password =’$password’)

    означает, что условие будет истинным, если переданная переменная $name будет равна значению поля name в таблице и переданная переменная ‘$password будет равна значению поля password в таблице. Если хотя бы одно условия не выполняется (неверное имя пользователя или пароль), то из таблицы ничего не будет взято., т. е. выражение SELECT * FROM `members` WHERE name = ‘$name’ AND password =’$password’ означает: в таблице `members` взять значения всех полей, если для них выполняется условие - совпадают переданное имя пользователя и пароль с теми, которые встречаются в таблице.

    Это понятно. Давайте теперь, например, с именем пользователя подставим одиночную кавычку:

    Адресная строка:

    Http://localhost/test/mysql-inj-lab1/index.php?name=Demo’&password=111

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

    " AND password ="111"

    Оно-то всё и ломает, поскольку количество открывающих и закрывающих кавычек не равно. Можно, например, подставить ещё одну кавычку:
    Адресная строка:

    Http://localhost/test/mysql-inj-lab1/index.php?name=Demo»&password=111

    Ошибка исчезла, но осмысленности это в запрос не добавило. Нам мешает бессмысленный хвост запроса. Как бы нам от него избавиться?

    Ответ есть - это комментарии.

    Комментарии в MySQL можно задать тремя способами:

    1. # (решётка - работает до конца строки)
    2. - (два тире - работают до конца строки, нужен символ пробела после двух тире)
    3. /* это комментарий */ группа из четырёх символов - всё, что внутри - это комментарий, всё, что до или после этой группы символов, не считается комментарием.
    Давайте в наш запрос с одной кавычкой, после этой кавычки поставим знак комментария, чтобы отбросить хвостик, и знак +, который обозначает пробел, чтобы запрос получился таким:
    Адресная строка:

    Http://localhost/test/mysql-inj-lab1/index.php?name=Demo’-+&password=111

    Ошибка не только исчезла, но и выведены корректные данные для пользователя Demo. Поскольку теперь наш запрос приобрёл вид
    ведь хвостик -+ ‘ AND password =’111’ превратился в комментарий и больше на запрос не влияет.

    Посмотрите ещё раз внимательно на новый запрос:
    И в нём больше не проверяется пароль! Т.е. зная имена легитимных пользователей, но не зная их паролей, мы можем просматривать их личные данные. Т.е. мы уже начали эксплуатировать SQL-инъекцию.

    К сожалению, я не знаю ни одного легитимного имени и мне нужно придумать что-то другое.

    Посмотрим внимательно на эту часть запроса:
    Помните про AND, которое используется в первом запросе? Оно означает логическую операции «И». Напомню, логическая операции «И» выдаёт «истина» (1) только если оба выражения являются истиной. Но логический оператор «ИЛИ» выдаёт «истина» (1) даже если хотя бы одно из выражений является истиной. Т.е. выражение
    всегда будет истиной, всегда будет возвращать 1. Поскольку одно из двух сравниваемых выражений всегда возвращает 1.

    Т.е. нам нужно составить выражение, которое будет выгладить так:
    Адресная строка:

    Http://localhost/test/mysql-inj-lab1/index.php?name=Demo’ OR 1 -+ &password=111

    Результат:

    Результат отличный! Мы получили список всех записей в таблице.

    ORDER BY и UNION - главные друзья SQL-инъекций

    Мы уже сейчас получили данные, которые были недоступны тем, у кого нет валидных имени пользователя и пароля. Можно ли что-то ещё получить? Да, можно получить полный дамп этой таблицы (напомню, у нас по прежнему нет паролей. Более того, мы можем получить все данные из всех баз на этом сервере через одну крошечную дырочку!

    UNION позволяет объединять SQL-запросы. В реальной жизни у меня задачи простые, поэтому и простые запросы к базам данных и возможностями UNION я не пользуюсь. Но вот для SQL-инъекций ценнее этого слова нет.

    UNION позволяет довольно гибко объединять SQL-запросы с SELECT, в том числе и от разных баз данных. Но есть важное требование к синтаксису: количество столбцов в первом SELECT должно равняться количеству столбцов во втором SELECT.

    ORDER BY задаёт сортировку полученных из таблицы данных. Можно задавать сортировку по имени столбца, а можно по его номеру. Причём, если столбца с таким номером нет, то будет показана ошибка:

    Адресная строка:

    Http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ORDER BY 1 -+ &password=111

    Запрос выглядит так:
    Мы заменили имя пользователя на -1 чтобы не выводились никакие данные.

    Ошибки нет, также нет ошибки и при запросах
    А вот запрос
    ему соответствует адресная строка

    Http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ORDER BY 6 -+ &password=111

    Выдал ошибку

    Это означает, что из таблицы выбираются данные по пяти колонкам.

    Конструируем наш запрос с UNION:

    Как я сказал, количество полей должно быть в обоих SELECT одинаковое, а вот что в этих полях - не очень важно. Можно, например, прописать просто цифры - и именно они и будут выведены. Можно прописать NULL – тогда вместо поля ничего не будет выведено.
    Адресная строка:

    Http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,5 -+ &password=111

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

    Делайте так пока не исчезнет сообщение об ошибке.

    Обратите внимание, что содержимое некоторых полей UNION SELECT 1,2,3,4,5 выводится на экран. Вместо цифр можно задать функции.

    Что писать в SELECT

    Есть некоторые функции, которые можно писать непосредственно в UNION:

    • DATABASE() - показать имя текущей базы данных
    • CURRENT_USER() - показывает имя пользователя и имя хоста
    • @@datadir - выводит абсолютный путь до базы данных
    • USER() - имя пользователя
    • VERSION() - версия базы данных
    В нашем примере выводятся поля 2, 4 и 5. Т.е. мы можем использовать любое из этих полей.

    Используем DATABASE() в UNION SELECT

    Адрес:

    Http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,DATABASE() -+ &password=111

    Результат:

    Получение имён таблицы, полей и дамп базы данных

    В базе данных information_schema есть таблица, которая называется tables . В этой таблице содержится список всех таблиц, которые присутствуют во всех базах данных этого сервера. Мы можем отобрать наши таблицы, ища в поле table_schema название нашей базы данных - ‘db_library’ (имя мы узнали с помощью DATABASE()).

    Это называется полная техника UNION. Материала по ней предостаточно в Интернете. На моём же MySQL сервере полная техника UNION не работает. У меня появляется ошибка
    Не работает не из-за кривизны рук, поскольку у sqlmap также эта техника не приносит результатов:

    Something went wrong with full UNION technique (could be because of limitation on retrieved number of entries). Falling back to partial UNION technique

    Возможно, это связано с версией MySQL 5.6. Т.к. привести практических примеров я не могу, а переписывать чужие неработающие команды мне не интересно - сейчас и без меня в Интернете развелось «великих теоретиков» сколько угодно, то я решил сразу перейти к рассмотрению частичной технике UNION. Но это не самая простая техника, да и статья уже получилась достаточно большой.

    В следующей части статьи мы изучим частичную технику UNION, с её помощью мы получим все данные на сервере: имена баз данных, имена их таблиц и полей в этих таблицах, а также их содержимое. Пока ждёте появления второй части - тренируйтесь, почитайте о SQL-инъекциях и технике UNION, дополнительно рекомендуются к ознакомлению следующие статьи:

    П.с. ах да, забыл про LIMIT. Тоже в следующий раз расскажу о роли LIMIT в SQL-инъекциях.

    Допустим нам известно о недостаточной фильтрации параметра id в скрипте

    Http://site/test.php?id=18

    Для того, чтобы выявить эти второстепенные признаки, следует составить http запросы, про которые известно, который приведет к правильному (но возвращающему пустой вывод) SQL запросу, и который приведет к неверному SQL запросу. Например, при не фильтруемом параметре id Код: http://site/test.php?id=99999 , вероятно, будет возвращен пустой sql запрос, в то время, как Код: http://site/test.php?id=99999 " должен породить ошибку.

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

    Рассмотрим случай, когда иньекция происходит после where. Если мы рассматриваем MySQL базу данных, то получение информации из базы данных может быть возможным только, если сервер имеет версию 4.*, те имеется возможность вставить в запрос union

    1 количество полей между select и where

    Пробуем последовательно, пока не получим верный запрос: Код: http://site/test.php?id=99999+union+select+null/ *

    текст файлов через MySQL инъекцию.

    Если пользователь, под которым осуществляется доступ к бд, имеет права file_priv, то можно получить текст произвольного файла Код: http://site/test.php?id=9999+union+select+null,LOAD_FILE ("/etc/passwd"),null/* запись файлов в веб директорию (php shell).

    Как показала практика, если мы имеем права file_priv, директорию, доступную на запись всем пользователям, доступную кроме того из web, (иногда, директории upload, banners и тд.), а так же знаем имя хотя бы одной таблицы (mysql.user, например сойдет, если имеется доступ к mysql базе данных), то можно выгрузить произвольный файл на сервер используя инъекцию подобного типа. Код: http://site/test.php?id=9999+union+select+null ,"",null+from+table1+into+outfile+"/usr/local/site/www/banners/cmd.php"/* При этом конструкция from table1 обязательна.

    Если кроме того, на сайте имеется уязвимость, позволяющая выполнять произвольные файлы на сервере, (include(»/path/$file.php»)), то, в любом случае можно закачать php shell, например в директорию /tmp/, и затем подцепить этот файл оттуда при помощи уязвимости в include.

    инъекция после limit.

    Довольно части возможность SQL инъекции возникает внутри параметра, передающегося к limit. Это может быть номер страницы и тд и тп.

    Практика показывает, что все вышесказанное может быть применено и в этом случае.

    MySQL корректно реагирует на запросы типа:

    Select … limit 1,2 union select….

    Select … limit 1 union select….

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

    Select … limit 99999,1 union select…. Либо, Select … limit 1,0 union select….

    некоторые «подводные камни».

    Наиболее частым подводным камнем может оказаться включение магических кавычек в конфигурации php. В случае строковых параметров это вообще позволит избежать возможности SQL инъекции, а в случае целый (дробных) параметров, в подобных запросах невозможно будет использовать кавычки, а следовательно и строки.

    Частично, решить эту проблему поможет нам функция char, которая возвращает строке по кодам символов. Например Код: http://site/test.php?id=9999+union+select+char (116,101,115,116),null,null/* Код: http://site/test.php?id=9999+union+select+char (116,101,115,116),null,null+from_table1/* Код: http://site/test.php?id=9999+union+select+null,LOAD_FILE (char(47,101,116,99,47,112,97,115,115,119,100)),null/* Единственное ограничение. В случае, если хочется сделать into outfile, то а качестве имени файла, необходимо передать имя файла в кавычках. into outfile char(…) выдает ошибку.

    Казалось бы, этот модуль веб сервера apache, делает невозможным эксплуатацию уязвимости SQL инъекции. Однако, при некоторых конфигурациях PHP и этого модуля, атаку можно провести прозрачно для этого модуля.

    Конфигурация по умолчанию модуля mod_security не фильтрует значение, переданные как cookie. Одновременно, в некоторых случаях, а также в некоторых конфигурациях по умолчанию php, переменные cookie регистрируются автоматически.

    Таким образом, злонамеренные значения переменных, абсолютно прозрачно для mod_security можно передать как cookie значения.

    DOS в MySQL инъекции.

    Если не имеется возможности применения union в запросе, например, MySQL имеет версию 3.*, то, тем не менее, инъекцию можно эксплуатировать, например, для того, чтобы заставить сервер базы данных исчерпать все свои ресурсы.

    Для этого, будем использовать функцию BENCHMARK, которая повторяет выполнение выражения expr заданное количество раз, указанное в аргументе count. В качестве основного выражения возьмем функцию, которая сама по себе требует некоторого времени. Например, md5(). В качестве строки возьмем current_date, чтобы строка не содержала кавычек. Функции BENCHMARK можно вкладывать друг в друга. И так, составляем запрос: Код: http://site/test.php?id=BENCHMARK (10000000,BENCHMARK(10000000,md5(current_date))) 1000000 запросов md5 выполняются (в зависимости от мощности сервера), примерно 5 секунд, 10000000 будут выполнятся около 50 секунд. Вложенный benchmark будет выполняться очень долго, на любом сервере. Теперь останется отправлять до нескольких десятков подобных http запросов в секунду, чтобы ввести сервер в беспробудный даун.

    другие типа MySQL инъекции.

    Фильтровать целые значения для целых параметров и кавычки для строковых параметров порой недостаточно. Иногда к незапланируемой функциональности может привести применение % и _ специальных символов внутри like запроса. Например: Код: mysql_query(«select id from users where password like "».addslashes($password).»" and user like "».addslashes($user).»"»); в этом случае к любому пользователю подойдет пароль %

    apache mod_rewrite

    В некоторых случаях, СКЛ инъекция возможна даже в параметре, который преобразуется методами mod_rewrite модуля apache, к GET параметру скрипта.

    Например, скрипты типа /news/127.html преобразуются к Код: /news/news.php?id=127 следующим правилом: Код: RewriteRule ^/news/(.*)\.html$ »/news/news.php?id=$1» Это позволит передать злонамеренные значения параметра скрипту. Так, например /news/128-1.html

    Если выводятся подробные сообщения об ошибках, то можно сразу узнать адрес скрипа, и далее, подобрав параметр работать уже с ним. Если же нет, то можно исследовать уязвимость, прямо редактируя имя файла.

    коротко о защите.

    Для защиты от всего вышесказанного достаточно придерживаться нескольких простых правил.

    1 для целых и дробных величин, перед их использованием в запросе достаточно привести величину к нужному типу. Код: $id=(int)$id; $total=(float)$total; Вместо этого можно вставить систему слежения за тестированием на SQL инъекцию. Код: if((string)$id<>(string)(int)$id) { пишем в лог о попытке Код: die("ops"); } 2 для строковых параметров, которые не используются в like, regexp и тд, экранируем кавычки. Код: $str=addslashes($str); или, лучше, Код: mysql_escape_string($str) 3 в строках, которые предполагается использовать внутри like, regexp и тд, необходимо так же заэкранировать специальные символы, применяющиеся в этих операторах, если это необходимо. В противном случае, можно задокументировать использование этих символов. __

    SQL-инъекции — встраивание вредоносного кода в запросы к базе данных — наиболее опасный вид атак. С использованием SQL-инъекций злоумышленник может не только получить закрытую информацию из базы данных, но и, при определенных условиях, внести туда изменения.

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

    Пример уязвимости

    Предположим, имеется скрипт, отображающий список пользователей из данного города, принимающий в качестве GET-параметра id города. Обращение к скрипту будет происходить с помощью HTTP по адресу /users.php?cityid=20

    В скрипте выше разработчик вставляет GET-параметр в SQL-запрос, подразумевая, что в GET-параметре всегда будет число. Злоумышленник может передать в качестве параметра строку и тем самым повредить запрос. Например, он обратится к скрипту как /users.php?cityid=20; DELETE * FROM users
    SQL-запрос получится таким:

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

    Как защититься?

    Давайте заключим пользователькую информацию в одинарные кавычки. Поможет ли это?

    Из примера выше видно, что заключить в одиночные кавычки недостаточно. Необходимо также экранировать все кавычки, содержащиеся в строке. Для этого в PHP предусмотрена функция mysql_real_escape_string(), которая добавляет обратный слеш перед каждой кавычкой, обратной кавычкой и некоторыми другим спецсимволами. Рассмотрим код:

    Итак, чтобы защититься от SQL-инъекций, все внешние параметры, которые могут содержать текст, должны быть перед включением в SQL-запрос обработаны с помощью mysql_real_escape_string() и заключены в одиночные кавычки.

    Если известно, что параметр должен принимать числовое значение числовым, его можно привести к числовому виду явно с помощью функции intval() или floatval() . В данном примере мы могли бы использовать:

    $sql = "SELECT username, realname
    FROM users
    WHERE cityid=""
    .intval ( $_GET [ "cityid" ]) .""" ;

    Отличия mysql_real_escape_string() и mysql_escape_string()

    mysql_real_escape_string() является усовершенствованной версией функции mysql_escape_string(), широко применяемой для формирования безопасных запросов к БД MySQL. Отличия этих двух функций в том, что mysql_real_escape_string() правильно работает с многобайтовыми кодировками.

    Предположим, в обрабатываемых данных есть символ (скажем, в UTF-8), код которого состоит из двух байт — шестнадцатеричных 27 и 2B (десятичные 39 и 43 соответственно). mysql_escape_string() воспринимает каждый байт передаваемых ей данных как отдельный символ (точнее, как код отдельного символа) и решит, что последовательность байт 27 и 2B — это два разных символа: одинарная кавычка (") и плюс (+). Поскольку функция воспринимает кавычку как специальный символ, перед байтом с кодом 27, который на самом деле является частью какого-то безобидного символа, будет добавлен слэш (\). В результате данные отправятся в базу в искаженном виде.

    Стоит отметить, что mysql_real_escape_string() работает правильно во всех случаях и может полностью заменить mysql_escape_string().

    mysql_real_escape_string() доступна в PHP с версии 4.3.0.

    Дополнительные примеры

    Мы рассмотрели наиболее простой пример, но на практике уязвимый запрос может быть более сложным и не отображать свои результаты пользователю. Далее рассмотрим примеры SQL-инъекций в некоторых более сложных случаях, не претендуя на полноту.

    Инъекция в сложных запросах

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

    В результате условие age<35 не будет влиять на выборку, т.к. оператор OR имеет более низкий приоритет, чем AND, и WHERE из приведённого выше запроса по-другому можно записать в виде WHERE (cityid="20" AND 1 ) OR ("1" AND age<"35" ) (напомним, что выражение WHERE 1 истинно всегда). В результате под условие подойдут и те строки, у которых cityid="20", и те, у которых age<35, причем наличие последних не обязательно.

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

    Результаты запроса не отображаются пользователю

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

    $sql = "SELECT count(*)
    FROM users
    WHERE userid=""
    .$_GET [ "userid" ] .""" ;

    Запрос выше всего лишь проверяет наличие пользователя с данным userid: если он возвращает любую отличную от нуля величину — показывается профиль пользователя с соответствующим userid, если же возвращён 0 (то есть, нет пользователей, удовлетворяющих критерию запроса) — сообщение "пользователь не найден".

    В этом случае определение пароля (или другой информации) производится перебором. Взломщик передает в качестве параметра userid строку 2" AND password LIKE "a% . Итоговый запрос:

    SELECT count (*) FROM users WHERE userid= "2" AND password LIKE "a% "

    Взломщик получит "пользователь не найден", если пароль не начинается на букву "a", или стандартную страницу с профилем пользователя, в противном случае. Перебором определяется первая буква пароля, затем вторая и.т.д.

    Выводы

    • Все запросы, использующие внешние данные, требуется защитить от SQL-инъекций. Внешние данные могут быть переданы не только в качестве GET-параметров, но и методом POST, взяты из COOKIE, со сторонних сайтов или из базы данных, в которую пользователь имел возможность занести информацию.
    • Все числовые параметры следует явно преобразовывать в числовой вид с помощью функций intval() и floatval()
    • Все строковые параметры следует экранировать с помощью mysql_real_escape_string() и заключать в кавычки.
    • Если построить SQL-инъекцию сложно, не следует ожидать, что злоумышленник не догадается как это сделать. Особенно это относится к движкам, исходный код которых является публичным.

    Удачи в построении безопасных приложений!