【问题标题】:Piping a passphrase to Veracrypt with popen() - is it secure?使用 popen() 将密码传递给 Veracrypt - 它安全吗?
【发布时间】:2019-06-30 00:26:54
【问题描述】:

我正在尝试在 Linux 命令行上使用相同的密码更方便地安装多个 Veracrypt 卷。由于 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;
}

代码有效,但我想知道执行后密码是否从内存中正确删除。如上面的代码所示,密码短语在开头被读入 char-arraybuffer,最后被替换为随机值。

我的两个问题是:

  • 这种方法是个好主意吗? (安全方面)

  • buffer 的值如何通过 popen() 传送到 veracrypt? / buffer 是直接从它的位置读取,还是被复制,因此可以保留在内存中的某个位置?

【问题讨论】:

  • 对我来说似乎很好。您不会在内存中留下任何密码痕迹。从 popen() 开始,我不确定它是否被缓冲

标签: c linux pipe popen veracrypt


【解决方案1】:

该方法没问题,并且与在 shell &lt;&lt;&lt;"myPassword" veracrypt 中通过管道传输密码一样安全。

  1. ps 输出中没有密码。
  2. 密码仅存储在临时缓冲区中。
  3. 我认为,如果攻击者对您的应用程序/源代码有足够的了解,它仍然可以使用一些旁道攻击来获取密码。

您的代码根本不安全。

  1. 你没有检查 malloc 的返回值
  2. 你没有检查popen的返回值
  3. 你没有检查getpass的返回值
  4. cc 分配的内存溢出。您没有为终止空字符分配位置。您可以使用 asnprintf 并让 GNU 库为您完成这项工作。
  5. 由于您没有正确传递 argv[i]argv[i+1],因此使用您的程序攻击任何 PC 非常简单,例如:./your_program "; sudo rm -rf &lt;some_file&gt;" "; echo I can run any shell script here"

这种方法是个好主意吗? (安全方面)

方法没问题,但你的方法不行。您的程序泄漏内存并且不检查任何返回值,并且无法控制传递给popen 的字符串,这是不安全的。使用system(sudo echo -n) 也是不安全的。至于方法,最好在最后一次使用memset(buffer, 0, strlen(buffer) + 1)(可能多次,如5)后将缓冲区清零,然后free(buffer)

鉴于 Meltdown 和 Spectre 等最近的攻击,较新的 ssh 版本在从用户那里收到密码后立即使用长密钥(我认为是 RSA,不确定)加密密码,并在每次使用时解密。密钥足够长,以使使用此类方法的攻击不太可能或太长。我认为不需要简单的小型应用程序来实现这种方法。 source.

popen() 如何将缓冲区的值通过管道传递给 veracrypt?

因为您使用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* 缓冲区中。因此它可以保留在内存中的某个位置。

【讨论】:

  • 非常感谢您对我的问题的详细回答。我会根据你的建议改进我的代码。为了澄清起见,我想指出,这段代码并不意味着在我的本地机器之外的任何地方实现。为方便起见,我没有检查任何返回值或 argv[] 的值,因为我只是手动调用它。在最终版本中,我绝对应该这样做。
  • 有一件事对我来说仍然不明显:“使用 system(sudo echo -n) 也是不安全的。”这行代码只是为了询问用户他的密码,以防止 veracrypt 要求它。 Veracrypt 需要安装卷的权限,并且可以在尚未缓存时询问密码。通过这一行,我首先不必以这种情况为条件,其次我不必担心还将用户的密码传递给 veracrypt。我认为 sudo 完成的缓存相当安全,并且没有看到任何问题。请您详细说明一下。
  • 还有一个问题:关于在上次使用缓冲区后对缓冲区进行 bzero,我担心提供有关密码长度的信息。将随机数写入该位置(可能多次)不是更安全的方法吗?
  • 最好将 veracrypt 添加到 sudoersNOPASSWD: 然后每次都要求输入 sudo 密码。我想我是认真的。
猜你喜欢
  • 2018-06-21
  • 2017-08-16
  • 2017-09-02
  • 2011-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-02
相关资源
最近更新 更多