一個漏洞泄露你的郵箱的所有秘密
谷歌近期對外公布了2016年10月的Nexus Security Bulletin ,這其中包含一個由360手機衛士阿爾法團隊(Alpha Team)提交的電子郵件信息泄露漏洞(CVE-2016-3918 ),谷歌對此漏洞的評級為高危險等級。該漏洞可導致惡意應用獲取到電子郵件內的數據,可能是電子郵件內容、電子郵件附件甚至賬號密碼。目前谷歌已經修復該漏洞并向OEM廠商推送了補丁,本文將對此漏洞進行分析。
本文的測試環境和代碼版本如下:
- SDK Version: 23,
- Android 6.0.1 Build: MOB30Y
- Branch: android-6.0.1_r60
漏洞成因
在Android AOSP的Email應用程序的源碼中,我們可以看到在AndroidManifest.xml 文件中存在名為AttachmentProvider的ContentProvider。
- <provider
- android:name=".provider.AttachmentProvider"
- android:authorities="com.android.email.attachmentprovider"
- android:grantUriPermissions="true"
- android:exported="true"
- android:readPermission="com.android.email.permission.READ_ATTACHMENT"
- />
其主要屬性如下:
- exported true即對外開放
- authorities com.android.email.attachmentprovider 即URI唯一標識
- readPermission com.android.email.permission.READ_ATTACHMENT 即讀取需要此權限
通過查詢我們可以了解到com.android.email.permission.READ_ATTACHMENT權限的protectionLevel為 dangerous,即可被第三方應用獲取到。
- <permission
- android:name="com.android.email.permission.READ_ATTACHMENT"
- android:permissionGroup="android.permission-group.MESSAGES"
- android:protectionLevel="dangerous"
- android:label="@string/permission_read_attachment_label"
- android:description="@string/permission_read_attachment_desc"/>
在確定此ContentProvider可以被第三方應用接觸到之后,我們定位AttachmentProvider的源碼。 源碼路徑如下:
- /packages/apps/Email/provider_src/com/android/email/provider/AttachmentProvider.java
通過閱讀源碼我們可以發現AttachmentProvider中實現了一個public的openFile 接口,該接口會返回一個ParcelFileDescriptor類型的對象供調用者打開文件。
- public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
進入openFile接口立刻就會判斷mode值是否為 "w" ,如果為w則會返回一個可寫的文件描述符。但在返回之前,函數的實現代碼進行了權限檢查,如果調用者沒有com.android.email.permission.ACCESS_PROVIDER 權限的話是會拋異常的。而該權限的聲明如下:
- <permission
- android:name="com.android.email.permission.ACCESS_PROVIDER"
- android:protectionLevel="signature"
- android:label="@string/permission_access_provider_label"
- android:description="@string/permission_access_provider_desc"/>
可以看到該權限是無法被第三方應用獲取到的,所以獲取可寫的權限是不可行的。 繼續往下分析代碼。
- List<String> segments = uri.getPathSegments();
- String accountId = segments.get(0);
- String id = segments.get(1);
- String format = segments.get(2);
- if (AttachmentUtilities.FORMAT_THUMBNAIL.equals(format)) {
- int width = Integer.parseInt(segments.get(3));
- int height = Integer.parseInt(segments.get(4));
- ...
- }
- else {
- return ParcelFileDescriptor.open(
- new File(getContext().getDatabasePath(accountId + ".db_att"), id),
- ParcelFileDescriptor.MODE_READ_ONLY);
- }
接下來一系列代碼會從uri.getPathSegments()中分割開不同的字段,并從中讀取相應的配置參數。 當 format參數不等于"THUMBNAIL"時,該代碼將會直接返回一個getDatabasePath()該目錄下名為id 的文件的文件描述符。 而id參數是上面從uri.getPathSegments().get(1) 得到的,獲取之后則沒有任何處理。 uri.getPathSegments()方法的作用是將字符串以"/"進行分割,但由于其沒有對經過url 編碼的字符串進行解碼,導致在處理過程中,對于/編碼之后的%2f則將不做處理,從而繞過getPathSegments的分割。
漏洞利用
根據上面我們對于代碼的分析,可以得出AttachmentProvider的uri如下:
- content://accountId/id/format/width/height
我們如果想利用此漏洞讀取數據,為使程序流能夠成功運行至目標位置,需要構造如下uri
- content://com.android.email.attachmentprovider/1/file_position/1/1/1
而且,getDatabasePath()的目錄是/data/user/0/com.android.email/databases/,如果我們要讀取Email 郵件的數據,則需要跳轉至目標目錄/data/data/com.android.email/來讀取Email應用的sqlite數據庫文件,我們需要構造如下 uri:
- content://com.android.email.attachmentprovider/1/..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fdata%2Fdata%2Fcom.android.email%2Fdatabases%2FEmailProvider.db/1/1/1
而EmailProvider.db中存儲了我們已登陸賬戶的賬戶名和密碼,對應的結構為HostAuth表的login和password 字段。 我們構造的PoC截圖如下:
總結
至此,CVE-2016-3918漏洞的分析和利用已經完成。 經過我們的測試,目前多款主流手機機型的自帶電子郵件客戶端都存在該問題,如小米 NOTE、華為P9、三星S5等機型,為了賬戶的安全,請使用存在漏洞手機的用戶暫停使用電子郵件客戶端并清除賬戶信息,換至其他郵件客戶端。