Бинарные уязвимости/ROP

Материал из SecSem Wiki
Перейти к навигации Перейти к поиску

Возвратно-ориентированное программирование (return-oriented programming, ROP) — техника эксплуатации перезаписи стека вызовов.

Описание техники

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

Но сперва нужно помнить, что, в соотвествии с System V AMD64 ABI, для передачи аргументов функций используются регистры. Значит, аргументы нельзя просто так положить на стек, их нужно как-то поместить в регистры.

Мы рассматриваем, в первую очердь, подобные фрагменты кода (x86_64):

pop rdi
ret

pop rsi
pop r15
ret

Например, если в стеке на место адреса возврата положить последовательно адрес гаджета pop rdi; ret, число 4, и адрес функции f, то это будет иметь такой же эффект, как будто функция f была вызвана с аргументом 4.

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

Где искать гаджеты

Гаджеты pop rdi; ret с самыми интересными нам регистрами не встречаются естественным образом в типичной программе. Однако они встречаются, если рассматривать не только начала легитимных инструкций, но и середины. Для автоматического поиска гаджетов можно использовать, например, ROPgadget или ropper.

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

Средства защиты

Во-первых, stack canary. Функция перед выходом перепроверяет, что секретное значение перед адресом возврата не было испорчено. Чтобы это обойти, нужно или как-то писать сразу за "канарейку", или предварительно узнать значение "канарейки", чтобы при перезаписи её оставить прежним.

Во-вторых, ASLR. Сама программа, библиотеки, и стек будут находиться на случайных смещениях в памяти, изменяющихся при каждом запуске. Если не обойти это, узнав каким-нибудь образом фактические адреса загрузки, составить ROP-цепочку не получится, поскольку туда нужно записывать абсолютные адреса функций/строк/etc..

См. также