На чем основана анатомия уязвимостей? Уязвимость — это ошибка в программе, которую можно использовать для получения несанкционированного доступа к системе или отказа в обслуживании для уязвимого сервиса. Администратор, а тем более конечный пользователь, не в состоянии изменить природу вещей, зато они могут минимизировать возможность взлома системы или его последствия.
Не каждая ошибка в программе может быть использована как уязвимость, но есть четкий критерий, соответствие которому делает ошибку уязвимостью. Этот критерий — возможность внедрения вредоносного кода в тело программы.
Однозначно подпадают под этот критерий ошибки, связанные с переполнением буфера, такие, как выход за границы диапазона для индексных массивов, переполнение стека и т.п. Не менее опасны ошибки, связанные с некорректным преобразованием типов данных. Вместе они составляют 90% всех уязвимостей, и лишь оставшиеся 10% связаны с недостатками в реализации логики программы.
Столь большой процент уязвимостей, основанных на переполнении буфера и некорректном преобразовании типов данных, не случаен. Он связан с тем, что с середины 70-х годов, когда язык С пришел на замену ассемблеру, ключевыми требованиями к ПО были компактность и высокая производительность, а вся ответственность за контроль программного кода возлагалась на программиста. Тогда это было оправдано, поскольку все серьезное ПО писалось под мейнфреймы, а персоналки еще только появлялись, и объем доступной оперативной памяти в них измерялся килобайтами.
Но время шло, мощность компьютеров выросла, появлялись языки с четкой типизацией данных и средствами контроля за переполнением буфера. Первой ласточкой был Pascal, который сочетал в себе как преимущества компилируемых языков, так и некоторые из возможностей динамических языков программирования благодаря развитой библиотеке времени исполнения, которая получила наибольшее развитие с появлением Object Pascal и его реализаций: Delphi и FreePascal.
Появление Java перевело языки программирования на новую эволюционную ступень, где программист мог абстрагироваться от архитектуры компьютера и операционной системы. Ее виртуальная машина, будучи в состоянии препятствовать переполнению буфера и ошибкам преобразования типов, тоже не решила проблему. Вышедшая в свет платформа .Net, под которую успешно перенесено большинство языков программирования и создан язык C#, тем не менее, имеет те же трудности с безопасностью.
Почему проблема осталась? Объяснение заключается в происхождении языков программирования, их своего рода «этимологии».
На чем разработана виртуальная машина Java? На С. С платформой .Net ситуация немногим лучше — она создана на С++ с использованием библиотек языка С. На чем разрабатываются компоненты современных операционных систем (а это миллионы строк исходного кода)? Тоже на C/C++, это относится и к драйверам устройств.
Львиная доля прикладного ПО разработана на С++.
Таким образом, заложенные в С источники уязвимостей все никак не прерываются. Они кочуют из этого языка во все новые средства разработки, а оттуда и во все программное и системное обеспечение.
Какова вероятность исключить ошибки в программе, состоящей из сотен или тысяч строк кода? Очевидно, что их отсутствие обеспечить просто невозможно, отсюда и ежегодный прирост числа уязвимостей. Когда-то при использовании С предполагалось, что программист должен обеспечивать проверку…. Теперь, при росте функций и архитектурной сложности программ, это стало превышать не только человеческие возможности, но и экономические ограничения — стоимость тестирования и отладки всех возможных состояний систем не оправдана коммерчески.
Все это данность, но в отношении нее все же можно что-то сделать для снижения рисков эксплуатации уязвимостей. Во-первых, в настройках брандмауэра можно ограничить доступ к потенциально уязвимым службам. Во-вторых, службы надо запускать с правами непривилегированной учетной записи. В-третьих, есть возможность использовать виртуализацию для запуска уязвимых сервисов/демонов. В-четвертых, помогает установка обновлений, исправляющих ошибки в ПО.
Дальнейшие рекомендации зависят от типа используемой операционной системы.
Для Windows:
- Использовать Data Execution Prevention (DEP) для защиты приложений
- Ограничить доступ служб к объектам ОС при помощи групповых политик
Для UNIX-совместимых систем (Linux, FreeBSD и MacOS X):
- Включить поддержку NX-бита в ядре ОС для защиты демонов и приложений
- Использовать политики (для Linux — SELinux или AppArm, а для FreeBSD — MAC) для того, чтобы ограничить доступ демонов к системным ресурсам
- Запускать демоны в chroot-окружении для защиты файловой системы
- Использовать сетевой суперсервер для ограничения доступа к демонам
Таким образом, соблюдая ряд предосторожностей, можно существенно снизить риск эксплуатации уязвимостей используемого вами ПО.