Re[24]: Безопасность ОС. Можно ли решить кардинально?
От: maxkar  
Дата: 07.10.14 21:30
Оценка: 98 (3)
S>>Велком — рассказывайте, какие операции вы собираетесь поддерживать, и как получается итоговая разметка.
O>Любая операция принимает на вход некие данные, делает над ними некие вычисления, результат которых зависит от самой операции и от входных данных. В результате операции появляются новые данные. Причем совершенно независимо от того что там за операция — imul, xor или SSEэшное перемножение векторов, 'принадлежность' информации которая получается в ее результате однозначно определяется как объединение принадлежностей информации принимаемой на вход (всех операндов, включая регистры и IO порты) и самой операции.

В примере перечислено то, что покроет 95% случаев. А вот специальные 5% инструкций доставят 95% проблем. И эти 5% — условные переходы. Вот как вы собираетесь размечать результат инструкции условного перехода? Все данные после перехода будут помечены тегом данных, которые использовались в условии? А не учитывать условные переходы нельзя, иначе можно создавать информацию из воздуха.
var bankBits = bankData.asLongNumber(); //банковые данные
var myData = 0; //хакерские данные.

for (var i = 0; i < 10000; i++) {
  if (bankBits % 2 == 0)
    myData = myData * 2; //только хакерские данные
  else
    myData = myData * 2 + 1; //тоже хакерские данные

  // Чисто банковские данные. Или нет? Хотя не важно.
  bankBits /= 2;
}
myData.reverseBits();


Вот так на условных переходах теги теряются. Так что нужно бы все условные переходы трейсить и навешивать теги от условия перехода. Чую, так просто от этого не отделаться будет. Расползутся теги от условного перехода по всему приложению. Вот посмотрит браузер на clipping area для вывода чего-нибудь со странички банка, и все, на всем UI-выводе будет висеть тег этого банка. Даже после того, как я закрыл все окна от него.

Еще интересные темы с межпроцессным взаимодействием. Как тегируется новый процесс?

O>Теперь попробуйте придумать вектор атаки, которая это обойдет.


Запросто. И даже теги на IP (instruction pointer) нам не помешают. Будем неторопясь сливать данные в свой "хакерский" домен. Отсутствие информации тоже может быть использовано в качестве информации!

// То, что мы хотим украсть (файл/буфер в памяти).
// Банковский тег на данных. 
// var bankBits = ...;

// Заведомо достаточно для данных, теги на некоторых ячейках будут меняться.
// Заполнен false'ами с хакерскими тегами.
var hLargeBuffer = new boolean[QUITE_BIG_BUFFER]; 

// Наш файл, в который мы пишем. Для простоты пишем биты, хакерский тег.
var hFile = openBitStream(...);

// Хакерские данные. Обязательно без банковского тега.
var hNextWriter = 0;

// Украсть банковские данные.
def stealData() {
  for (var i = 0; i < QUITE_BIG_BUFFER; i++)
    startThread(new KamikazeWriter(i).write);
  for (var i = 0; i <= QUITE_BIG_BUFFER; i++)
    startThread(new KamikazeReader(i).write);
  //Позапускали каких-то потоков. Вроде бы все локально.
}


//Райтер данных из банка. Писатель хакерского буфера.
class KamikazeWriter(pos : Int) {
  def write() = { 
    // на бите будет банковский тег.
    val bit = bankBits.getBitAt(pos); 
    if (bit == 1)
      hLargeBuffer[pos] = true;
    // на hLargeBuffer[pos] будет банковский тег, но только если
    // бит в позиции pos был установлен в 1. Иначе на 
    // hLargeBuffer[pos] не будет банковского тега (мы в него не пишем).
    // При необходимости массив можно развернуть в гору отдельных переменных.
    // Ячейки массива можно заменить на atomic для обеспечения потокобезопасности.
  }
}

//Читатель данных из буфера. Пишет в наш файл очищенные от тегов данные.
class KamikazeReader(order : Int) {
  def write() = {
    //Спим. Интервал должен быть достаточен, чтобы даже с погрешностями
    //планировщика читатели выполнялись в порядке order.
    Thread.sleep(order * SUFFICIENT_INTERVAL);
   
    // Предыдущий читатель наткнулся на коварно установленный банком бит
    // и погиб смертью храбрых защищая чистоту hNextWriter. Почтим память
    // того читателя установкой бита в наших чистых данных.
    if (hNextWriter < order)
      hFile.writeBit(1);
    
    // Пытаемся скопировать свой бит в хакерский файл.
    if (hLargeBuffer[order]) {
      // Все пропало. На нас теперь будет
      // проклятье банковских данных. Оно будет преследовать наш
      // Instruction Pointer до самой смерти. Так что умрем прямо сейчас.
    } else {
      // А бит был false. Это значит, на нем не было метки банка 
      // (мы не пишем false после чтения данных банка). И наш
      // Instruction Pointer еще не имеет ненужных тегов.
      hFile.writeBit(0);
      // Сигналим следующему читателю, что мы живы и единичку писать не надо.
      hNextWriter = order + 1;
    }
  }
}


Вот вам и вектор. Каждый читатель получает информацию из "хакерских данных". Если вдруг он прочитал данные с банковским тегом, он просто умирает (ничего никуда не сигналя!). Так как другие читатели знают протокол обмена, они могут восстановить "ошибки протокола" (т.е. не вышедших на связь в нужное время читателей).
Немного черной магии и данные извлекаются из "отсутствия данных". Чтобы с этим бороться, нужно либо анализировать как-то program flow и возможные affected vairables. Либо радостно развешивать теги на все псевдоглобальное, включая thread.sleep, сетевые взаимодействия и прочие источники, которые могут быть использованы для кражи данных. Но даже там я реализую sleep через большой цикл. И буду с интервалом ридеров пускать, а не ждать в самом ридере.

Что-то мне кажется, что попытки бороться со всем этим приведут к решению "ставим теги на процесс". А это уже совсем не так интересно. Плюс результат будет похож на то, что уже есть сейчас. Всякие apparmor и прочие ограничители прав приложений (т.е. не только файловый доступ, а еще сокеты, системные ресурсы и т.п.).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.