Как можно использовать целочисленное переполнение?

У кого-нибудь есть подробное объяснение того, как можно использовать целые числа? Я много читал о концепции, и я понимаю, что это такое, и я понимаю переполнение буфера, но я не понимаю, как можно надежно изменить память или каким-то образом изменить поток приложения, сделав целое число больше, чем его определенная память....

5 ответов

Решение

Это определенно пригодно для использования, но, конечно, зависит от ситуации.

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

Подробнее о переполнении целых чисел в ssh: http://www.kb.cert.org/vuls/id/945216

Подробнее о целочисленном переполнении: http://projects.webappsec.org/w/page/13246946/Integer%20Overflows

Я использовал APL/370 в конце 60-х годов на IBM 360/40. APL - это язык, на котором по существу все является многомерным массивом, и есть удивительные операторы для манипулирования массивами, включая изменение формы от N измерений до M измерений и т. Д.

Неудивительно, что массив из N измерений имел индексные границы 1..k с различным положительным k для каждой оси... и юридически k всегда было меньше 2^31 (положительные значения в 32-битном машинном слове со знаком). Теперь массив из N измерений имеет место, назначенное в памяти. Попытки получить доступ к слоту массива с использованием индекса, слишком большого для оси, проверяются по верхней границе массива с помощью APL. И, конечно, это относится к массиву из N измерений, где N == 1.

APL не проверял, делал ли ты что-то невероятно глупое с оператором RHO (изменение формы массива). APL допускает не более 64 измерений. Таким образом, вы можете создать массив размером 1-64, и APL сделает это, если все размеры массива будут меньше 2^31. Или вы можете попытаться создать массив из 65 измерений. В этом случае APL обделался и неожиданно вернул массив из 64 измерений, но не смог проверить размеры осей. (Это действительно там, где произошло "переполнение целых чисел"). Это означало, что вы можете создать массив с размерами осей 2 ^ 31 или более... но интерпретируемые как целые числа со знаком, они рассматриваются как отрицательные числа.

Правильное заклинание оператора RHO, примененное к такому массиву, могло бы уменьшить размерность до 1, с верхней границей, получить это "-1". Назовите эту матрицу "червоточиной" (вы поймете, почему в данный момент). Такой массив червоточин имеет место в памяти, как и любой другой массив. Но все обращения к массиву проверяются по верхней границе... но проверка привязки к массиву оказалась выполненной сравнением без знака APL. Таким образом, вы можете получить доступ к WORMHOLE[1], WORMHOLE[2], ... WORMHOLE[2^32-2] без возражений. По сути, вы можете получить доступ ко всей памяти машины.

У APL также была операция присваивания массива, в которой вы могли заполнить массив значением. WORMHOLE[]<- 0, таким образом, обнуляет всю память.

Я сделал это только один раз, так как это стерло память, содержащую мое рабочее пространство APL, интерпретатор APL и, очевидно, критическую часть APL, которая обеспечивала разделение времени (в те дни он не был защищен от пользователей)... его обычное состояние механически очень шумное (у нас было 2741 терминалов Selectric APL), чтобы замолчать примерно через 2 секунды. Сквозь стекло в компьютерную комнату я видел оператора, пораженного светом на 370, когда они все погасли. Много бега вокруг последовало.

Хотя в то время это было забавно, я держал рот на замке.

С некоторой осторожностью, очевидно, можно было вмешаться в ОС произвольным образом.

Распространенным случаем может быть код, который предотвращает переполнение буфера, запрашивая количество вводимых данных, а затем пытаясь применить это ограничение. Рассмотрим ситуацию, когда я утверждаю, что предоставляю 2^30+10 целых чисел. Принимающая система выделяет буфер 4*(2^30+10)=40 байт (!). Поскольку выделение памяти прошло успешно, я могу продолжить. Проверка буфера ввода не остановит меня, когда я отправлю свой 11-й вход, так как 11 < 2^30+10. И все же я переполню фактически выделенный буфер.

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

С другой стороны, я видел много такого кода, который не проверяет пользовательский ввод (хотя этот пример надуман):

int pricePerWidgetInCents = 3199;
int numberOfWidgetsToBuy = int.Parse(/* some user input string */);
int totalCostOfWidgetsSoldInCents = pricePerWidgetInCents * numberOfWidgetsToBuy; // KA-BOOM!

// potentially much later
int orderSubtotal = whatever + totalCostOfWidgetInCents;

Все неуклюжие до того дня, когда вы продаете 671 299 виджетов за -$21 474 817,95. Босс может быть расстроен.

Я просто хотел обобщить все, что я узнал о моем первоначальном вопросе.

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

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

Надеюсь это поможет.

Другие вопросы по тегам