使用 popen() 将密码短语传输到 Veracrypt - 它安全吗?
Piping a passphrase to Veracrypt with popen() - is it secure?
我正在尝试在 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;
}
代码有效,但我想知道执行后密码是否从内存中正确删除。如上面的代码所示,密码短语在开始时被读入字符数组 buffer,最后用随机值替换。
我的两个问题是:
这种方法是个好主意吗? (安全方面)
buffer 的值是如何通过 popen() 传递给 veracrypt 的? / buffer 是直接从它的位置读取还是被复制因此可以保留在内存中的某个地方?
该方法是可行的,并且与在 shell <<<"myPassword" veracrypt
.
中传输密码一样安全
ps
输出中没有密码。
- 密码仅存储在临时缓冲区中。
- 我认为如果攻击者足够了解您的 application/source 代码,他仍然可以使用一些侧信道攻击来获取密码。
您的代码根本不安全。
- 你没有检查 malloc
的 return 值
- 你没有检查 popen
的 return 值
- 您没有检查 getpass
的 return 值
- 您溢出了为
cc
分配的内存。您没有为终止空字符分配位置。您可以使用 asnprintf
并让 GNU 库为您完成这项工作。
- 因为你没有正确传递
argv[i]
和 argv[i+1]
使用你的程序攻击任何 PC 都很简单,例如:./your_program "; sudo rm -rf <some_file>" "; echo I can run any shell script here"
.
Is this approach a good idea at all? (securitywise)
方法还可以,你方法不对。您的程序泄漏内存并且不检查任何 return 值并且无法控制传递给 popen
的字符串,这是不安全的。使用 system(sudo echo -n)
也是不安全的。至于方法,最好在最后一次使用 memset(buffer, 0, strlen(buffer) + 1)
之后将缓冲区清零(可能多次,比如 5),然后 free(buffer)
.
鉴于最近的 Meltdown 和 Spectre 等攻击,较新的 ssh 版本在从用户收到密码后立即使用长密钥(我认为使用 RSA,不确定)加密密码,并在每次使用时解密。密钥足够长,使得使用此类方法的攻击不太可能或太长。我认为不需要简单的小应用程序来实现这种方法。 source.
How is the value of buffer piped to veracrypt by 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()
还将允许您发送信号并处理子项的 return 值。
Is buffer read directly from its location or is it copied and can therefore remain somewhere in memory?
是的,是的,是的。它直接从 fprintf
调用中的位置读取。它被复制到内部 FILE*
缓冲区。因此它可以保留在内存中的某个地方。
我正在尝试在 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;
}
代码有效,但我想知道执行后密码是否从内存中正确删除。如上面的代码所示,密码短语在开始时被读入字符数组 buffer,最后用随机值替换。
我的两个问题是:
这种方法是个好主意吗? (安全方面)
buffer 的值是如何通过 popen() 传递给 veracrypt 的? / buffer 是直接从它的位置读取还是被复制因此可以保留在内存中的某个地方?
该方法是可行的,并且与在 shell <<<"myPassword" veracrypt
.
ps
输出中没有密码。- 密码仅存储在临时缓冲区中。
- 我认为如果攻击者足够了解您的 application/source 代码,他仍然可以使用一些侧信道攻击来获取密码。
您的代码根本不安全。
- 你没有检查 malloc 的 return 值
- 你没有检查 popen 的 return 值
- 您没有检查 getpass 的 return 值
- 您溢出了为
cc
分配的内存。您没有为终止空字符分配位置。您可以使用asnprintf
并让 GNU 库为您完成这项工作。 - 因为你没有正确传递
argv[i]
和argv[i+1]
使用你的程序攻击任何 PC 都很简单,例如:./your_program "; sudo rm -rf <some_file>" "; echo I can run any shell script here"
.
Is this approach a good idea at all? (securitywise)
方法还可以,你方法不对。您的程序泄漏内存并且不检查任何 return 值并且无法控制传递给 popen
的字符串,这是不安全的。使用 system(sudo echo -n)
也是不安全的。至于方法,最好在最后一次使用 memset(buffer, 0, strlen(buffer) + 1)
之后将缓冲区清零(可能多次,比如 5),然后 free(buffer)
.
鉴于最近的 Meltdown 和 Spectre 等攻击,较新的 ssh 版本在从用户收到密码后立即使用长密钥(我认为使用 RSA,不确定)加密密码,并在每次使用时解密。密钥足够长,使得使用此类方法的攻击不太可能或太长。我认为不需要简单的小应用程序来实现这种方法。 source.
How is the value of buffer piped to veracrypt by 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()
还将允许您发送信号并处理子项的 return 值。
Is buffer read directly from its location or is it copied and can therefore remain somewhere in memory?
是的,是的,是的。它直接从 fprintf
调用中的位置读取。它被复制到内部 FILE*
缓冲区。因此它可以保留在内存中的某个地方。