/*
本文章由 莫灰灰 编写,转载请注明出处。
作者:莫灰灰 邮箱: minzhenfei@163.com
*/
1. KeyStore Service
在Android中,/system/bin/keystore进程提供了一个安全存储的服务。在过去的版本号中。其它程序主要用过UNIX socket的守护进程/dev/socket/keystore去訪问这个服务。
然而。如今我们能够通过Binder机制去訪问它。
每个Android用户都有一块其私有的安全存储区域。
全部秘钥信息使用一个随机key并用AES加密算法加密。加密好的密文採用另外一个key加密后保存到本地磁盘。
(后面的key通过PKCS5_PBKDF2_HMAC_SHA1函数算出来的)
在最近的一些Android版本号中,证书管理(比如RSA算法的私有key)是能够通过专门的硬件做支持的。这也就是说。keystore的key仅仅是用来标识存储在专有硬件上的真正key。
虽然有专有硬件的支持,可是还是会有一些证书,比如VPN PPTP的证书,依旧会保存在本地磁盘上。
图一非常好的阐述了keystore安全存储机制的工作原理。
当然,很多其它的关于keystore服务的一些内部信息大家都能够在网上找到相关资料。
2. Simplicity
通过源码(keystore.c)中的凝视我们能够知道KeyStore被设计出来的时候想的稍微简单了点:
/* KeyStore is a secured storage for key-value pairs. In this implementation, * each file stores one key-value pair. Keys are encoded in file names, and * values are encrypted with checksums. The encryption key is protected by a * user-defined password. To keep things simple, buffers are always larger than * the maximum space we needed, so boundary checks on buffers are omitted.*/代码实现起来尽管简单,可是缓冲区的大小并不总是比他们设想的最大空间要小。
3. Vulnerability
easy被攻击的缓冲区主要是在KeyStore::getKeyForName函数中。
ResponseCode getKeyForName ( <span style="white-space:pre"> </span>Blob * keyBlob , <span style="white-space:pre"> </span>const android :: String8 & keyName , <span style="white-space:pre"> </span>const uid_t uid , <span style="white-space:pre"> </span>const BlobType type ) { char filename [ NAME_MAX ]; encode_key_for_uid ( filename , uid , keyName ); ... }这个函数有好几个调用者,外部程序能够非常easy的通过Binder接口来调用它。(比如。int32_t android::KeyStoreProxy::get(const String16& name, uint8_t** item, size_t*
itemLength))。因此,恶意程序能够非常轻松的控制变量keyName的值和长度。
接下来,encode_key_for_uid函数中调用了encode_key函数,这个函数在没有边界检查的情况下会造成filename的缓冲区溢出。
static int encode_key_for_uid ( char * out , uid_t uid , const android :: String8 & keyName ) { int n = snprintf ( out , NAME_MAX , "% u_ ", uid ); out += n; return n + encode_key ( out , keyName ); } static int encode_key ( char * out , const android :: String8 & keyName ) { const uint8_t * in = reinterpret_cast < const uint8_t * >( keyName . string ()); size_t length = keyName . length (); for ( int i = length ; i > 0; --i , ++ in , ++ out ) { if (* in < '0' || * in > '~ ') { * out = '+' + (* in >> 6); *++ out = '0' + (* in & 0 x3F ); ++ length ; } else { * out = * in ; } } * out = '