kam*_*iro 23 c++ memory security passwords cryptography
在这里讨论之后,如果你想要一个安全的类来存储内存中的敏感信息(例如密码),你必须:
所以这听起来不错,我创建了一个测试类来查看它是否有效.所以我做了一个简单的测试用例,我继续添加单词"LOL"和"WUT",然后在这个安全缓冲类中加上一个数字大约一千次,破坏该对象,然后再做一些导致核心转储的事情.
由于该类应该在破坏之前安全地清除内存,因此我不应该在coredump上找到"LOLWUT".但是,我设法找到它们,并想知道我的实现是否只是错误.但是,我尝试使用CryptoPP库的SecByteBlock做同样的事情:
#include <cryptopp/osrng.h>
#include <cryptopp/dh.h>
#include <cryptopp/sha.h>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include <cryptopp/filters.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
int main(){
{
CryptoPP::SecByteBlock moo;
int i;
for(i = 0; i < 234; i++){
moo += (CryptoPP::SecByteBlock((byte*)"LOL", 3));
moo += (CryptoPP::SecByteBlock((byte*)"WUT", 3));
char buffer[33];
sprintf(buffer, "%d", i);
string thenumber (buffer);
moo += (CryptoPP::SecByteBlock((byte*)thenumber.c_str(), thenumber.size()));
}
moo.CleanNew(0);
}
sleep(1);
*((int*)NULL) = 1;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
然后编译使用:
g++ clearer.cpp -lcryptopp -O0
Run Code Online (Sandbox Code Playgroud)
然后启用核心转储
ulimit -c 99999999
Run Code Online (Sandbox Code Playgroud)
但是,启用核心转储并运行它
./a.out ; grep LOLWUT core ; echo hello
Run Code Online (Sandbox Code Playgroud)
给出以下输出
Segmentation fault (core dumped)
Binary file core matches
hello
Run Code Online (Sandbox Code Playgroud)
是什么造成的?由于SecByteBlock附加引起的重新分配,应用程序的整个内存区域是否重新分配?
编辑:使用vim检查核心转储后,我得到了这个:http: //imgur.com/owkaw
edit2:更新的代码,因此它更易于编译,并且编译指令
最终编辑3:看起来memcpy是罪魁祸首.请参阅mymemcpy
下面的答案,了解Rasmus的实施情况.
and*_*otn 23
尽管在coredump中出现,但在清除缓冲区后密码实际上不再存在于内存中.问题是,memcpy
一个足够长的字符串会将密码泄漏到SSE寄存器中,这些都会
出现在coredump中.
当size
参数to memcpy
大于某个阈值-80- 字节时,则使用SSE指令进行内存复制.这些指令更快,因为它们可以并行复制16个字节,而不是逐个字符,逐个字节或逐个字.这是来自Mac上Libc的源代码的关键部分
:
LAlignedLoop: // loop over 64-byte chunks
movdqa (%rsi,%rcx),%xmm0
movdqa 16(%rsi,%rcx),%xmm1
movdqa 32(%rsi,%rcx),%xmm2
movdqa 48(%rsi,%rcx),%xmm3
movdqa %xmm0,(%rdi,%rcx)
movdqa %xmm1,16(%rdi,%rcx)
movdqa %xmm2,32(%rdi,%rcx)
movdqa %xmm3,48(%rdi,%rcx)
addq $64,%rcx
jnz LAlignedLoop
jmp LShort // copy remaining 0..63 bytes and done
Run Code Online (Sandbox Code Playgroud)
%rcx
是循环索引寄存器,%rsi
是小号乌尔斯河地址寄存器,并且%rdi
是d estination地址寄存器.每次循环运行,64个字节从源缓冲区复制到4个16字节SSE寄存器
xmm{0,1,2,3}
; 然后将这些寄存器中的值复制到目标缓冲区.
在该源文件中有更多的东西,以确保副本仅在对齐的地址上发生,以填充在执行64字节块之后剩余的副本部分,并处理源和目标重叠的情况.
但是 - SSE寄存器在使用后不会被清除!这意味着复制的64字节缓冲区仍然存在于xmm{0,1,2,3}
寄存器中.
这是Rasmus的程序的修改,显示了这一点:
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <emmintrin.h>
inline void SecureWipeBuffer(char* buf, size_t n){
volatile char* p = buf;
asm volatile("rep stosb" : "+c"(n), "+D"(p) : "a"(0) : "memory");
}
int main(){
const size_t size1 = 200;
const size_t size2 = 400;
char* b = new char[size1];
for(int j=0;j<size1-10;j+=10){
memcpy(b+j, "LOL", 3);
memcpy(b+j+3, "WUT", 3);
sprintf((char*) (b+j+6), "%d", j);
}
char* nb = new char[size2];
memcpy(nb, b, size1);
SecureWipeBuffer(b,size1);
SecureWipeBuffer(nb,size2);
/* Password is now in SSE registers used by memcpy() */
union {
__m128i a[4];
char c;
};
asm ("MOVDQA %%xmm0, %0": "=x"(a[0]));
asm ("MOVDQA %%xmm1, %0": "=x"(a[1]));
asm ("MOVDQA %%xmm2, %0": "=x"(a[2]));
asm ("MOVDQA %%xmm3, %0": "=x"(a[3]));
for (int i = 0; i < 64; i++) {
char p = *(&c + i);
if (isprint(p)) {
putchar(p);
} else {
printf("\\%x", p);
}
}
putchar('\n');
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在我的Mac上,这打印:
0\0LOLWUT130\0LOLWUT140\0LOLWUT150\0LOLWUT160\0LOLWUT170\0LOLWUT180\0\0\0
Run Code Online (Sandbox Code Playgroud)
现在,检查核心转储,密码只发生一次,并且就像确切的0\0LOLWUT130\0...180\0\0\0
字符串一样.核心转储必须包含所有寄存器的副本,这就是字符串存在的原因 - 它是xmm{0,1,2,4}
寄存器的值.
因此,调用后密码实际上不在RAM中
SecureWipeBuffer
,它似乎只是因为它实际上在某些寄存器中才出现在coredump中.如果你担心
memcpy
有一个可以被RAM冻结利用的漏洞,那就不用再担心了.如果在寄存器中有密码副本困扰您,请使用memcpy
不使用SSE2寄存器的修改,或在完成后清除它们.如果你对此非常偏执,请继续测试你的coredumps,以确保编译器没有优化你的密码清除代码.
这是另一个程序,可以更直接地再现问题:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
inline void SecureWipeBuffer(char* buf, size_t n){
volatile char* p = buf;
asm volatile("rep stosb" : "+c"(n), "+D"(p) : "a"(0) : "memory");
}
void mymemcpy(char* b, const char* a, size_t n){
char* s1 = b;
const char* s2= a;
for(; 0<n; --n) *s1++ = *s2++;
}
int main(){
const size_t size1 = 200;
const size_t size2 = 400;
char* b = new char[size1];
for(int j=0;j<size1-10;j+=10){
memcpy(b+j, "LOL", 3);
memcpy(b+j+3, "WUT", 3);
sprintf((char*) (b+j+6), "%d", j);
}
char* nb = new char[size2];
memcpy(nb, b, size1);
//mymemcpy(nb, b, size1);
SecureWipeBuffer(b,size1);
SecureWipeBuffer(nb,size2);
*((int*)NULL) = 1;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果更换memcpy
用mymemcpy
或使用较小尺寸的问题消失了,所以我最好的猜测是,内置的memcpy做一些事情,离开在内存中拷贝数据的一部分.
我想这只是表明从内存中清除敏感数据实际上是不可能的,除非它从头开始设计到整个系统中.
归档时间: |
|
查看次数: |
5782 次 |
最近记录: |