Классификация угроз безопасности web-серверов. Часть 3

4. Выполнение кода (Command Execution)

Все серверы используют данные, переданные пользователем при обработке запросов. Часто эти данные используются при составлении команд, применяемых для генерации динамического содержимого. Если при разработке не учитываются требования безопасности, злоумышленник получает возможность модифицировать исполняемые команды. В настоящее время выделяют следующие типы атак, направленных на выполнение кода на web-сервере:

4.1. Переполнение буфера (Buffer Overflow)

Переполнение буфера (buffer overflows) на сегодняшний день является самой распространенной уязвимостью в области безопасности ПО. Первая атака с применением данной уязвимости использовалась в вирусе-черве Морриса в 1988 году. Напомню, что червь, созданный студентом Корнельского университета, всего через 5 часов спустя активации умудрился инфицировать около 6000 компьютеров — по сегодняшним меркам не очень много, но не будем забывать про год. Среди пострадавших — Агентство Национальной безопасности и Стратегического авиационного командования США; лаборатории NASA (в частности, в вычислительном центре NASA в Хьюстоне червь попытался взять под контроль систему запуска кораблей многоразового использования Space Shuttle); исследовательский центр ВМС США, Калифорнийский НИИ, крупнейшие университеты страны, а также ряд военных баз, клиник и частных компаний… Эксплуатация переполнения буфера позволяет злоумышленнику изменить путь исполнения программы путем перезаписи данных в памяти системы. Переполнение возникает, когда объем данных превышает размер выделенного под них буфера. Когда буфер переполняется, данные переписывают другие области памяти, что приводит к возникновению ошибки. Если злоумышленник имеет возможность управлять процессом переполнения, это может вызвать ряд серьезных проблем. Переполнение буфера может вызывать отказы в обслуживании, приводя к повреждению памяти и вызывая ошибки в программах. Более серьезные ситуации позволяют изменить путь исполнения программы и выполнить в ее контексте различные действия. Это может происходить в нескольких случаях.
Используя переполнение буфера, можно перезаписывать служебные области памяти — например, адрес возврата из функций в стеке. Также при переполнении могут быть переписаны значения переменных в программе. Переполнение буфера является наиболее распространенной проблемой в безопасности и нередко затрагивает web-серверы. Однако атаки, эксплуатирующие эту уязвимость, используются против web-приложений не очень часто. Причина этого кроется в том, что атакующему, как правило, необходимо проанализировать исходный код или образ программы. Поскольку атакующему приходится эксплуатировать нестандартную программу на удаленном сервере, ему приходится атаковать "вслепую", что снижает шансы на успех. Переполнение буфера обычно возникает при создании программ на языках C и C++. Если часть сайта создана с использованием этих языков, сайт может быть уязвим для этой атаки.

4.2. Атака на функции форматирования строк (Format String Attack)

При использовании этих атак путь исполнения программы модифицируется методом перезаписи областей памяти с помощью функций форматирования символьных переменных. Уязвимость возникает, когда пользовательские данные применяются в качестве аргументов функций форматирования строк — таких, как fprintf, printf, sprintf, setproctitle, syslog и т.д. Если атакующий передает приложению строку, содержащую символы форматирования ("%f", "%p", "%n" и т.д.), то у него появляется возможность:

. выполнить произвольный код на сервере;
. считывать значения из стека;
. вызывать ошибки в программе/отказ в обслуживании.

Вот и пример: предположим, web-приложение хранит параметр emailAddress для каждого пользователя. Это значение используется в качестве аргумента функции printf: printf(emailAddress). Если значение переменной emailAddress содержит символы форматирования, функция printf будет обрабатывать их согласно заложенной в нее логике. Поскольку дополнительных значений этой функции не передано, будут использованы значения стека, хранящие другие данные. Возможны следующие методы эксплуатации атак на функции форматирования строк:
— Чтение данных из стека:
Если вывод функции printf передается атакующему, он получает возможность чтения данных из стека, используя символ форматирования "%x". — Чтение строк из памяти процесса:
Если вывод функции printf передается атакующему, он может получать строки из памяти процесса, передавая в параметрах символ "%s".
— Запись целочисленных значений в память процесса:
Используя символ форматирования "%n", злоумышленник может сохранять целочисленные значения в памяти процесса. Таким образом можно перезаписать важные значения — например, флаги управления доступом или адрес возврата.

4.3. Внедрение операторов LDAP (LDAP Injection)

Атаки этого типа направлены на web-серверы, создающие запросы к службе LDAP на основе данных, вводимых пользователем. Упрощенный протокол доступа к службе каталога (Lightweight Directory Access Protocol — LDAP) — открытый протокол для создания запросов и управления службами каталога, совместимыми со стандартом X.500. Протокол LDAP работает поверх транспортных протоколов Internet (TCP/UDP). Web-приложение может использовать данные, предоставленные пользователем для создания запросов по протоколу LDAP при генерации динамических web-страниц. Если информация, полученная от клиента, должным образом не верифицируется, атакующий получает возможность модифицировать LDAP-запрос. Причем запрос будет выполняться с тем же уровнем привилегий, с каким работает компонент приложения, выполняющий запрос (сервер СУБД, web-сервер и т.д). Если данный компонент имеет права на чтение или модификацию данных в структуре каталога, злоумышленник получает те же возможности. Примеры: уязвимый код с комментариями:

line 0: <html>
line 1: <body>
line 2: <%@ Language=VBScript %>
line 3: <%
line 4: Dim userName
line 5: Dim filter
line 6: Dim ldapObj
line 7:
line 8: Const LDAP_SERVER = "ldap.example"
line 9:
line 10: userName = Request.QueryString("user")
line 11:
line 12: if( userName = "" ) then
line 13: Response.Write("<b>Invalid request. Please specify a valid user name</b><br>")
line 14: Response.End()
line 15: end if
line 16:
line 17:
line 18: filter = "(uid=" + CStr(userName) + ")" ' searching for the user entry
line 19:
line 20:
line 21: 'Creating the LDAP object and setting the base dn
line 22: Set ldapObj = Server.CreateObject("IPWorksASP.LDAP")
line 23: ldapObj.ServerName = LDAP_SERVER
line 24: ldapObj.DN = "ou=people,dc=spilab,dc=com"
line 25:
line 26: 'Setting the search filter
line 27: ldapObj.SearchFilter = filter
line 28:
line 29: ldapObj.Search
line 30:
line 31: 'Showing the user information
line 32: While ldapObj.NextResult = 1
line 33: Response.Write("<p>")
line 34:
line 35: Response.Write("<b><u>User information for: " + ldapObj.AttrValue(0) + "</u></b><br>")
line 36: For i = 0 To ldapObj.AttrCount -1
line 37: Response.Write("<b>" + ldapObj.AttrType(i) +"</b>: " + ldapObj.AttrValue(i) + "<br>" )
line 38: Next
line 39: Response.Write("</p>")
line 40: Wend
line 41: %>
line 42: </body>
line 43: </html>

Обратите внимание, что имя пользователя, полученное от клиента, проверяется на наличие в этой строке пустого значения (строка 10-12). Если в переменной содержится какое-то значение, оно используется для инициализации переменной filter (строка 18). Полученное значение используется для построения запроса к службе LDAP (строка 27), который исполняется в строке 29. В приведенном примере атакующий имеет полный контроль над запросом и получает его результаты от сервера (строки 32-40).

4.4 Выполнение команд ОС (OS Commanding)

Атаки этого класса направлены на выполнение команд операционной системы на web-сервере путем манипуляции входными данными. Если информация, полученная от клиента, должным образом не верифицируется, атакующий получает возможность выполнить команды ОС. Они будут выполняться с тем же уровнем привилегий, с каким работает компонент приложения, выполняющий запрос (сервер СУБД, web-сервер и т.д). Пример: язык Perl позволяет перенаправлять вывод процесса оператору open, используя символ '|' в конце имени файла:

# Выполнить "/bin/ls" и передать
#результат оператору open
open(FILE, "/bin/ls|")

Web-приложения часто используют параметры, которые указывают на то, какой файл отображать или использовать в качестве шаблона. Если этот параметр не проверяется достаточно тщательно, атакующий может подставить команды ОС после символа "|". Предположим, приложение оперирует URL следующего вида:

httр://example/cgi-bin/showInfo.pl?name=John&template=tmp1.txt. Изменяя значение параметра template, злоумышленник дописывает необходимую команду (/bin/ls) к используемой приложением:
httр://example/cgi-bin/showInfo.pl?name=John&template=/bin/ls|

Большинство языков сценариев позволяет запускать команды ОС во время выполнения, используя варианты функции exec. Если данные, полученные от пользователя, передаются этой функции без проверки, злоумышленник может выполнить команды ОС удаленно. Следующий пример иллюстрирует уязвимый PHP-сценарий:
exec("ls -la $dir",$lines,$rc);

Используя символ ";" (Unix) или "&" (Windows) в параметре dir, можно выполнить команду операционной системы:
httр://example/directory.php?dir=%3Bcat%20/etc/passwd

В результате подобного запроса злоумышленник получает содержимое файла /etc/passwd.

4.5. Внедрение операторов SQL (SQL Injection)

Эти атаки направлены на web-серверы, создающие SQL-запросы к серверам СУБД на основе данных, вводимых пользователем. Язык запросов Structured Query Language (SQL) представляет собой специализированный язык программирования, позволяющий создавать запросы к серверам СУБД. Большинство серверов поддерживают этот язык в вариантах, стандартизированных ISO и ANSI. В большинстве современных СУБД присутствуют расширения диалекта SQL, специфичные для данной реализации (T-SQL в Microsoft SQL Server, --PL SQL в Oracle и т.д.). Многие web-приложения используют данные, переданные пользователем, для создания динамических web-страниц. Если информация, полученная от клиента, должным образом не верифицируется, атакующий получает возможность модифицировать запрос к SQL-серверу, отправляемый приложением. Запрос будет выполняться с тем же уровнем привилегий, с каким работает компонент приложения, выполняющий запрос (сервер СУБД, web-сервер и т.д). В результате злоумышленник может получить полный контроль над сервером СУБД и даже его операционной системой. С точки зрения эксплуатации SQL Injection очень походит на LDAP Injection.

Пример: предположим, аутентификация в web-приложение осуществляется с помощью web-формы, обрабатываемой следующим кодом: SQLQuery = "SELECT Username FROM Users WHERE
Username = '" & strUsername & "' AND Password = '"
& strPassword & "'" strAuthCheck =
GetQueryResult(SQLQuery)
В этом случае разработчики непосредственно используют переданные пользователями значения strUsername и strPassword для создания SQL- запроса. Предположим, злоумышленник передаст следующие значения параметров:
Login: ' OR ''='
Password: ' OR ''='

В результате серверу будет передан следующий SQL-запрос: SELECT Username FROM Users WHERE Username = '' OR ''='' AND Password = '' OR ''='' Вместо сравнения имени пользователя и пароля с записями в таблице Users данный запрос сравнивает пустую строку с пустой строкой.

Естественно, результат подобного запроса всегда будет равен True, и злоумышленник войдет в систему от имени первого пользователя в таблице. Обычно выделяют два метода эксплуатации внедрения операторов SQL: обычная атака и атака вслепую (Blind SQL Injection). В первом случае злоумышленник подбирает параметры запроса, используя информацию об ошибках, генерируемую web-приложением. Пример: добавляя оператор union к запросу, злоумышленник проверяет доступность базы данных:

httр://example/article.asp?ID=2+union+all+select+name+from+sysobjects

Сервер генерирует сообщение, аналогичное следующему:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]All
queries in an SQL statement containing a UNION
operator must have an equal number of expressions
in their target lists.

Из этого следует, что оператор union был передан серверу, и теперь злоумышленнику необходимо подобрать используемое в исходном выражении select количество параметров. Возможен также вариант внедрения SQL-кода вслепую. В этом случае стандартные сообщения об ошибках модифицированы, и сервер возвращает понятную для пользователя информацию о неправильном вводе. Осуществление SQL Injection возможно и в этой ситуации, однако обнаружение уязвимости затруднено. Наиболее распространенный метод проверки наличия проблемы — добавление выражений, возвращающих истинное и ложное значение.
Выполнение подобного запроса к серверу:

httр://example/article.asp?ID=2+and+1=1
должно вернуть ту же страницу, что и запрос:
httр://example/article.asp?ID=2
поскольку выражение 'and 1=1' всегда истинно.

Если в запрос добавляется выражение, возвращающее значение "ложь": "httр://example/article.asp?ID=2+and+1=0", пользователю будет возвращено сообщение об ошибках или страница не будет сгенерирована. В случае, если факт наличия уязвимости подтвержден, эксплуатация ничем не отличается от обычного варианта.

Продолжение следует.
Ориг. класс. "Web Application Security Consortium"

Бойцев О.М., boyscout_zone@yahoo.com


Компьютерная газета. Статья была опубликована в номере 08 за 2006 год в рубрике безопасность

©1997-2024 Компьютерная газета