Передача парольной фразы в Veracrypt с помощью popen() - это безопасно?
Я пытаюсь сделать монтирование нескольких томов 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;
}
Код работает, но мне интересно, правильно ли удалена ключевая фраза из памяти после исключения. Как показано в приведенном выше коде, фраза-пароль вначале считывается в буфер массива символов и заменяется случайными значениями в конце.
Мои два вопроса:
Этот подход - хорошая идея вообще? (Securitywise)
Как значение буфера передается в veracrypt с помощью popen()? / Чтение буфера непосредственно из его местоположения или оно скопировано и может поэтому оставаться где-то в памяти?
1 ответ
Подход в порядке и так же безопасен, как и передача пароля в оболочке <<<"myPassword" veracrypt
,
- Там нет пароля в
ps
выход. - Пароль хранится только во временных буферах.
- Я думаю, что злоумышленник все еще может получить пароль, используя некоторые атаки по побочным каналам, если он знает достаточно о вашем приложении / исходном коде.
Ваш код вообще не защищен.
- Вы не проверяете возвращаемое значение malloc
- Вы не проверяете возвращаемое значение popen
- Вы не проверяете возвращаемое значение getpass
- Вы переполняете выделенную память для
cc
, Вы не выделяли место для завершающего нулевого символа. Вы могли бы использоватьasnprintf
и пусть библиотека GNU сделает всю работу за вас. - Потому что вы не проходите должным образом
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, было бы лучше, чтобы bzero буфер после его последнего использования 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*
буфер. Поэтому он может остаться где-то в памяти.