Передача парольной фразы в Veracrypt с помощью popen () - это безопасно? - PullRequest
2 голосов
/ 30 июня 2019

Я пытаюсь сделать монтирование нескольких томов Veracrypt с одним и тем же паролем более удобным в командной строке Linux.Поскольку Veracrypt поддерживает только кэширование парольной фразы в GUI-режиме, я написал следующий код, чтобы выполнить эту работу за меня:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<time.h>

int main(int argc, char* argv[]){
if(argc > 1 && argc%2==1){
    srand(time(0));

    //prevent veracrypt from asking for user's passphrase
    system("sudo echo -n");

    char *buffer = getpass("Veracrypt Password:");

    for(int i = 1; i<argc; i+=2){
        char* cc;
        cc = (char *) malloc(57+strlen(argv[i])+1+strlen(argv[i+1]));
        strcpy(cc, "veracrypt -t --protect-hidden=no --keyfiles=\"\" --pim=123 ");
        strcat(cc, argv[i]);
        strcat(cc, " ");
        strcat(cc, argv[i+1]);

        FILE* fChild = popen(cc, "w");
        fprintf(fChild, "%s", buffer);
        pclose(fChild);

        free(cc);
    }
    for(int i = 0; i<strlen(buffer); i++)
    buffer[i] = rand();
}
return 0;
}

Код работает, но мне интересно, правильно ли удалена парольная фраза из памяти послеexcecution.Как показано в коде выше, фраза-пароль читается в массив символов буфер в начале и заменяется случайными значениями в конце.

Мои два вопроса:

  • Является ли этот подход хорошей идеей вообще?(безопасность)

  • Как значение buffer передается в veracrypt с помощью popen ()?/ буфер считывается непосредственно из своего местоположения или он скопирован и поэтому может оставаться где-то в памяти?

1 Ответ

4 голосов
/ 30 июня 2019

Подход в порядке и является таким же безопасным, как и передача пароля в оболочке <<<"myPassword" veracrypt.

  1. Нет пароля в выводе ps.
  2. Пароль хранится только во временных буферах.
  3. Я думаю, что злоумышленник все еще может получить пароль, используя некоторые атаки по побочным каналам, если он знает достаточно о вашем приложении / исходном коде.

Ваш код вообще не защищен.

  1. Вы не проверяете возвращаемое значение malloc
  2. Вы не проверяете возвращаемое значение popen
  3. Вы не проверяете возвращаемое значение getpass
  4. Вы переполняете выделенную память для cc. Вы не выделяли место для завершающего нулевого символа. Вы можете использовать asnprintf и позволить библиотеке GNU делать всю работу за вас.
  5. Поскольку вы не передаете должным образом argv[i] и argv[i+1], очень просто атаковать любой компьютер с помощью вашей программы, например: ./your_program "; sudo rm -rf <some_file>" "; echo I can run any shell script here".

Является ли этот подход хорошей идеей вообще? (Securitywise)

Подход в порядке, как вы подошли, не в порядке. Ваша программа теряет память и не проверяет никаких возвращаемых значений и не контролирует строки, переданные в popen, что просто небезопасно. Использование system(sudo echo -n) также небезопасно. Что касается approuch, было бы лучше обнулить буфер после его последнего использования memset(buffer, 0, strlen(buffer) + 1) (может быть, несколько раз, например, 5), а затем free(buffer).

В свете последних атак, таких как Meltdown, Spectre и других, новые версии ssh шифруют пароль длинным ключом (я думаю, с RSA, не уверен) сразу после получения его от пользователя и дешифруют каждый раз при использовании. Ключ достаточно длинный, чтобы сделать атаки такими методами маловероятными или слишком длинными. Я не думаю, что есть потребность в легком небольшом приложении, чтобы реализовать такой метод. источник .

Как значение буфера передается в veracrypt с помощью popen ()?

Поскольку вы используете fprintf, буфер копируется во внутренний буфер FILE* и затем сбрасывается на новую строку. По умолчанию FILE* потоки буферизируются и сбрасываются на новую строку. Вы можете указать поведение с помощью setvbuf, однако, я не думаю, что это вообще безопасно, так как пароль будет оставаться в буфере FILE* в течение некоторого времени. Затем вызов fprintf записывает содержимое внутреннего буфера FILE* после новой строки в соответствующий дескриптор файла канала с указателем FILE*. Затем ядро ​​передает данные со входа канала в стандартный ввод команды. Немного более безопасный способ (поскольку вам вообще не нужна утилита printf, а просто "%s" ...), вероятно, нужно использовать setvbuf(fChild, NULL, _IONBF, 0), а затем fwrite(buffer, strlen(buffer), 1, fChild).

Правильный подход будет состоять в том, чтобы удалить FILE* и использовать правильные pipe() + fork() + exec() и направить пароль прямо в канал с помощью вызова write(), поэтому вы не используете FILE* внутренняя буферизация. fork() также позволит вам отправлять сигналы и обрабатывать возвращаемое значение дочернего элемента.

Буфер считывается непосредственно из своего местоположения или копируется и поэтому может оставаться где-то в памяти?

Да, и да, и да. Он читается непосредственно из его местоположения внутри fprintf вызова. Копируется во внутренний FILE* буфер. Поэтому он может оставаться где-то в памяти.

...