Информационная безопасность
[RU] switch to English


Digital Scream

январь, 2003

[email protected]

Целочисленное переполнение:защита

Философия

Эта статья является логическим продолжением прошлой темы о уязвимостях целочисленного переполнения (Integer overflow). Многие считают что данная уязвимость не исправима. К сожалению именно так считают и авторы журнала Phrack. Я дословно привожу их мнение по этому поводу: "Integer overflows cannot be detected after they have happened, so there is not way for an application to tell if a result it has calculated previously is in fact correct." Для тех, кто не знаком с английским, перевожу общее содержание вышеизложеного : "Целочисленное переполнение не может быть определено, поэтому у приложения нет возможности сообщить о правильности подсчитаного результата.". Насколько правдиво это, мы убедимся дальше...

Ассемблер

Допустим у нас есть два 32-х битные числа intVariable и intIncrement. Их сложение может быть реализовано и со вставками assambler'a:
   
   ......
   mov eax,intVariable;
   add eax,intIncrement;
   ......
В результате регистр eax будет содержать результат операции сложения. Если занести в intVariable и intIncrement, 0xFFFFFFFF и 0x00000001 соответственно, то сложение этих чисел в двоичной форме выглядит так:
    11111111111111111111111111111111
   +00000000000000000000000000000001
    --------------------------------
   100000000000000000000000000000000
Как видно, один бит вываливается за область значения типа Unsigned Integer. Система это регистрирует и заносит в флаги CF и OF значения 1. Все что нам осталось, это проверить их значения.

Безопасная арифметика

Начиная с МП Pentium Pro, в систему команд общего назначения включена группа команд, выполняющих условную пересылку целых CMOVcc. Они копируют содержимое операнда источника в приемник только при выполнении условия, указанного в имени команды. В данный ммомент нас интересует комманда cmovo. Она копирует в приемник некое значение при условии, что флаг OF равен единице. Исходя из выше изложенного, можно написать функцию которая будет определять был ли факт переполнения, и, если оно произошло, то значение результата устанавливается в максимальное Unsigned Integer(4294967295).
  .......
  int intSafeValue=4294967295;
  .......
  unsigned int SafeCalc(
	unsigned int intVariable,unsigned int intIncrement
  )
  {
    asm {
     xor ebx,ebx;
	// заносим 0 в ebx,
     mov eax,intVariable;
	// а в eax нашу переменную
     add eax,intIncrement;
	// и прибавляем к ней intIncrement
     cmovo eax,intSafeValue;
	// если было переполнение, занести в eax
	// значение intSafeValue
     mov intVariable,eax;
	// результат заносим обратно в intVariable
    }
    return intVariable;
  }
  ......

Патч

Как видно, нет никаких сложностей с определением целочисленного переполнения. Используя этот метод можно контролировать и операции вычитания, умножения, деления и т.д. Но такая реализация неудобна для конечного розработчика ПО и кому-то может показатся утомительной. Для упрощения ситуации можно создать новый тип SafeInt со свойствами типа Integer(int). Единственное его отличие будет контроль за результатом выполнения арифметических операторов. Его описание с исключительной поддержкой сумирования будет иметь следующий вид:
  class SafeInt{
   public:
    unsigned int intVariable;

  SafeInt(unsigned int intData){
   intVariable=intData;
  }

  unsigned int operator= (SafeInt siData){
   intVariable=sintData.intVariable;
   return 0;
  }

  unsigned int operator+ (SafeInt siData){
   return SafeCalc(intVariable,sintData.intVariable);
  }
 }
Этот примитивный класс не поддерживает взимодействия с некоторыми типами даных. Но это легко исправить, сделав перегрузку описаных функций. Как результат, давайте изменим первый пример прошлой статьи так чтобы он стал безопасным в плане целочисленного переполнения:
//Стандартное описание типа SafeInt:
//-----------------------------------
  int intSafeValue=4294967295;

  unsigned int SafeCalc(
	unsigned int intVariable,unsigned int intIncrement
  )
  {
    asm {
     xor ebx,ebx;            
	// заносим 0 в ebx,
     mov eax,intVariable;
	// а в eax нашу переменную
     add eax,intIncrement;
	// и прибавляем к ней intIncrement
     cmovo eax,intSafeValue;
	// если было переполнение, занести в eax
	// значение intSafeValue
     mov intVariable,eax;
	// результат заносим обратно в intVariable
    }
    return intVariable;
  }

  class SafeInt{
   public:
    unsigned int intVariable;

  SafeInt(unsigned int intData){
   intVariable=intData;
  }

  unsigned int operator= (SafeInt siData){
   intVariable=sintData.intVariable;
   return 0;
  }

  unsigned int operator+ (SafeInt siData){
   return SafeCalc(intVariable,sintData.intVariable);
  }
 }
//-----------------------------------
//Сама программа:

int main(int argc,char **argv) {

//создаем переменные для хранения данных
char chLogin[100];
char chPassword[100];
int intPasswordLength;

//заносим в них пользовательские данные
strcpy(chLogin,argv[1]);
strcpy(chPassword,argv[2]);
intPasswordLength=atoi(argv[3]);

//флаг дающий пользователю права администратора
int admin=0;
char chOriginalPassword[100]="administrator";
//простая проверка
if(intPasswordLength<1)intPasswordLength=0;

//Это место изменено
SafeInt sintPwdLen=intPasswordLength;
sintPwdLen=sintPwdLen+1;
intPasswordLength=sintPwdLen;

if(chLogin="admin"){
admin=1;
for(i=0;i<=intPasswordLength;i++)
	if((chPassword[i])!=chOriginalPassword[i])
		admin=0;
}
setUserStastusAdmin(admin);
}
Как видно мы заменили одну строку тремя, но это лишь из-за незавершенного описания класса SafeInt. Но уже робочего! Из вышесказанного можно сделать вывод, что есть возможность написания патча устраняющего данную уязвимость, так как было сделано с переполнение стека. Остается только ждать и писать КОРРЕКТНЫЕ программы.
О сайте | Условия использования
© SecurityVulns, 3APA3A, Владимир Дубровин
Нижний Новгород