1
2
3
4
5
6
7
8
9
10
11
12
13
|
private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {
getFragmentManager().popBackStack(BACK_STACK_PREFS,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
if (!isValidFragment(fragmentName)) {
throw new IllegalArgumentException( "Invalid fragment for this activity: "
+ fragmentName);
}
Fragment f = Fragment.instantiate( this , fragmentName, args);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.replace(com.android.internal.R.id.prefs, f);
transaction.commitAllowingStateLoss();
}
|
Android API 对于isValidFragment()的解释
protected booleanisValidFragment (String fragmentName)
Subclasses should override this method and verify that the given fragment is a valid type to be attached to this activity. The default implementation returnstrue
for apps built for android:targetSdkVersion
older thanKITKAT
. For later versions, it will throw an exception.
Parameters
fragmentName | the class name of the Fragment about to be attached to this activity. |
---|
Returns
- true if the fragment class name is valid for this Activity and false otherwise.
比如 :
Set<String> sKnownFragments = new HashSet<String>(Arrays.asList(
"com.android.test1Fragment",
"com.android.test2Fragment",
"com.android.test3Fragment"
));
@Override
protected boolean isValidFragment(String fragment){
return sKnownFragments.contains(fragment);
}
假如我现在想访问com.android.test4Fragment 这个fragment,但是没有在sKnownFragments 集合里面加入。那么isValidFragment 方法返回false,应用将报出IllegalArgumentException : invalid fragment of this activity
一,Android应用程序的UI构建块
Android应用的UI由activities组成。一个activity提供了一个单一的屏幕和一些功能(例如:浏览器的书签管理器)。一个Fragment 可以被认为是sub-activity,这是应用程序UI的一个小部分。Fragments能够灵活的允许在不同的activities重用。当一个Fragment实例在Activity里面驻留耦合时,不同实例可以嵌入到不同的Activity中。图1即Activity和Fragment的关系。
图1:Fragment和activity的关系
二,Android沙盒,权限,Inter-app通信和恶意程序攻击
Android系统中,应用程序是相互孤立并且受制于他们声明的权限运行于沙盒中。通常应用是不能正常访问其他程序的敏感数据。但也就是说,通过其他方式应用还是可以调用其他应用组件(如 activities)特征重用。例如,谷歌Chrome浏览器调用Google play应用打开Google play URLs。这个调用使用 Intents即IPC对象从源应用程序传递到相应的API。 Intents不只是指定目标,他们在其他两个地方也包含了数据。第一个位置是data属性(URI类型),第二个是一个字典(Bundle)可以包含任意数量的信息(也称为Intent extras)。如果Activities在应用程序的manifest文件(ApplicationManifest.xml)中声明exported,则其可以被外部应用调用,即此形成了一个activity公开(exported)创建的Android沙箱潜在突破口。当Activities访问输入Intent的数据时,一个恶意的应用可以调用exported的activity并为其提供恶意数据,如果数据不正确santizied(santizied不理解)或未经目标应用的验证,则可能会触发漏洞。而Fragment可以接收通过访问嵌入activity的输入,即Intent或Fragment-specific参数。图2为攻击大概。
图2:攻击exported activities.
三,PreferenceActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
if
(savedInstanceState !=
null
) {
}
else
{
if
(initialFragment !=
null
&& mSinglePane) {
// If we are just showing a fragment, we want to run in
// new fragment mode, but don't need to compute and show
// the headers.
switchToHeader(initialFragment, initialArguments);
}
else
{
if
(mHeaders.size() >
0
) {
if
(!mSinglePane) {
if
(initialFragment ==
null
) {
}
else
{
switchToHeader(initialFragment, initialArguments);
}
}
}
}
}
}
public
void
switchToHeader(String fragmentName, Bundle args) {
setSelectedHeader(
null
);
switchToHeaderInner(fragmentName, args,
0
);
}
private
void
switchToHeaderInner(String fragmentName, Bundle args,
int
direction) {
getFragmentManager().popBackStack(BACK_STACK_PREFS,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fragment f = Fragment.instantiate(
this
, fragmentName, args);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.replace(com.android.internal.R.id.prefs, f);
transaction.commitAllowingStateLoss();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
static
Fragment instantiate(Context context, String fname, Bundle args) {
try
{
Class<?> clazz = sClassMap.get(fname);
if
(clazz ==
null
) {
// Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname);
sClassMap.put(fname, clazz);
}
Fragment f = (Fragment)clazz.newInstance();
if
(args !=
null
) {
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments = args;
}
return
f;
}
...
}
|
图4:Android 4.3 Fragment.instantiate的实例
四,漏洞
恶意应用程序可以调用任何exported的PreferenceActivity类,并提供:android:show_fragment Intent extra来执行加载任意类。攻击者的目标是执行一些代码来打破Android沙箱,来访问脆弱应用敏感信息,或滥用权限。由于攻击者不能提供自己的类,他只是有限的寻找脆弱应用的类加载器(Android框架类和应用程序类),而攻击并不简单。在我们的白皮书中描述了几个exploit技巧,其中一个相当酷。恶意应用可以使PreferenceActivity加载脆弱应用的任意Fragment ,这通常是装载在一个non-exported的Activity中的,几乎相当于把它从安居所放入了危险区,从而被不信任的恶意应用控制。参见图5的攻击大纲。
图5:攻击 Fragment
五,攻击Android Settings
正如我们上面所提到的,所有的应用用到PreferenceActivity都是脆弱的。我们针对Settings应用,因为它是一个高度特权应用,通过exploiting漏洞我们设法成功地破坏其完整性。应用程序的主activity( 已exported)com.android.settings.Settings由于扩展PreferenceActivity而变得脆弱。我们在应用包中寻找到有意思的fragments,其中一个是ChooseLockPassword$ChooseLockPasswordFragment,这个 Fragment是负责处理验证设备锁屏和设备管理策略信息的改变,Fragment所在的ChooseLockPassword Activity是non-exported的,通常Fragment先要求用户输入他的旧凭证(参见图6),除非嵌入的activity配置了一个名为“confirm_credentials” 的Intent extra,并设置为false。
图6:ChooseLockPasswordFragment的默认行为
由于activity是未exported的,该参数不容易被恶意应用操作,然而利用Fragments注入漏洞,我们可以把ChooseLockPassword$ChooseLockPasswordFragment嵌入exported 的activity,com.android.settings.Settings中,并提供恶意数据,confirm_credentials设置为false。攻击轮廓如图7所示,图8为结果。由于这种exploit需要用户介入而不能使用远程操作,因此需要攻击者实体改变证书。请注意,利用此漏洞的攻击者可以覆盖设备管理策略,如最低密码要求(不太理解the minimum password requirements,估计可能是PIN吧)。
图7:攻击Android的设置
图8:用户不需要提供他的凭证