@romangol
2016-12-21T17:56:58.000000Z
字数 2913
阅读 1587
research
crypto
本文将不断更新我们搜集到的现实中密码学误用问题
Cryptcat是经典网络工具netcat的加密版,使用twofish算法加密,其密钥协商过程基于发送、接收双发共享的一个password。
Cryptcat密码学模块的源文件主要为farm9crypt.cpp
和twofish.cpp
。farm9crypt.cpp
作为netcat和twofish之间的接口,
twofish.cpp`实现了加解密算法。本次分析的源码来自其sourceforge项目主页 ,unix版的cryptcat密码学模块基本相同
Cryptcat使用了CBC(密文分组链接)模式,大致流程如下:
如上图,b为分组长度,b-d是最后一个分组的填充长度,默认用0填充。在传输密文时,倒数第二个分组()后半部分b-d长度的密文无需传输,因为解密后可以得到这部分的值。所以利用Ciphertext Stealing技术,在无需扩展密文长度的情况下进行分组加密,减少了不必要的网络传输。
keystr为用户输入的password,此处使用了固定值的随机数种子。
extern "C" void farm9crypt_init( char* keystr ) {
printf( "farm9crypt_init: %s\n", keystr );
encryptor = new TwoFish( generateKey( keystr ), false, NULL, NULL );
decryptor = new TwoFish( generateKey( keystr ), true, NULL, NULL );
initialized = true;
srand( 1000 );
}
twofish2.cpp
中的generateKey
函数,功能是将用户提供的字符串password
转换成twofish的128bit密钥。
static char key[32];
char* generateKey( char* s ) {
int sIdx = 0;
for ( int i = 0; i < 32; i++ ) {
char sval = *( s + sIdx );
if (( sval >= '0' ) && ( sval <= '9' )) {
key[i] = sval;
} else if (( sval >= 'a' ) && ( sval <= 'f' )) {
key[i] = sval;
} else {
int q = sval%16;
if ( q < 10 ) {
key[i] = ('0' + q);
} else {
key[i] = ('a' + q - 10);
}
}
sIdx++;
if ( *( s + sIdx ) == 0 ) {
sIdx = 0;
}
}
return( &key[0] );
对password的每个字符,函数用取模的方式只取了4个bit,如果password过短的话函数就对passwrod进行循环,超过32个字符的话则会丢弃剩下的部分。造成多个字符串可以对应相同的twofish密钥,并且还存在默认密码metallica
的情况。
p.s. 这个bug已经有人报告了,但开发者没有回复
iv的产生与farm9crypt.cpp
中的farm9crypt_write
函数有关。
char tempbuf[16];
char outbuf[16];
sprintf( tempbuf, "%d %d", size, rand() );
tempbuf[strlen(tempbuf)] = 'x';
encryptor->setSocket( sockfd );
encryptor->setOutputBuffer( (unsigned char*)&outBuffer[0] );
encryptor->resetCBC();
encryptor->blockCrypt( tempbuf, outbuf, 16 );
encryptor->blockCrypt( tempbuf, outbuf, 16 );
tempbuf的取值来自传输数据的size和rand(),而rand
的种子在farm9crypt_init
中被设定为了1000,因此这里rand并没有产生随机意义。随机的来源还是开辟数组空间后栈上原来的值。
encryptor->blockCrypt( tempbuf, outbuf, 16 );
这一语句出现了两次,blockCrypt函数的作用是对输入进行加密或解密。 第一次将tempbuf加密后发送给接受端,接收端解密后可以获得待发送内容的size 第二次将tempbuf与第一次的密文异或后加密后发送,作为CBC模式的IV.
从接受端的函数farm9crypt_read也能看出这一过程,decryptor也调用了blockCrypt函数两次,第一次得到变量limit,就是所要接受数据的大小,atoi的调用对应了发送端的
sprintf( tempbuf, "%d %d", size, rand() );
第二次则是取得了IV的值。
decryptor->resetCBC();
decryptor->setOutputBuffer( (unsigned char*)&outBuffer[0] );
decryptor->blockCrypt( buf, outbuf, 16 );
decryptor->flush();
decryptor->setOutputBuffer( (unsigned char*)&outBuffer[0] );
decryptor->blockCrypt( buf + 16, outbuf2, 16 );
int limit = atoi( outbuf );
total = 0;
char* inbuf = &inBuffer[0];
while ( total < limit ) {
int result = recv( sockfd, inbuf + total, limit - total, 0 );
if ( result > 0 ) {
total += result;
} else {
break;
}
}
缺少mac验证。尤其是第一个包,包含了待收数据的大小,被攻击者修改后会导致后续的内容都无法解密。
从cryptcat源码分析中看到了一些密码学的使用问题包括默认密码、固定随机数种子、IV随机性不足、缺少mac验证等。