漏洞之战:https://bbs.kanxue.com/thread-269211.htm
OVAA:https://oversecured.com/vulnerabilities
数据统计 统计时间范围:2020.1 - 2025.2
内容范围:google android framework and system
EoP:提权 RCE:远程代码执行
ID:信息泄露 DoS:拒绝服务
可以看到近5年批漏的漏洞分类比重大概是6:2:1:1
EoP漏洞主要的话还是深度依赖于代码审计包括一些Fuzz之类的手段去挖掘。这个大比重也从侧面反映了近10年在Android安全研究中权限机制占比是非常之深的【可见S&P词云分析】
ID漏洞挖掘以自动化工具主导,主要就是对于数据流的分析,近十年来创造的各种xxxdroid非常多,占比也会稍微高一点
DoS更多的依赖于fuzz+人工验证,漏洞成因多是应用层组件(如Activity、BroadcastReceiver)未校验输入导致崩溃,或内核资源耗尽(如触发OOM)等等。可以通过Fuzzing批量发现,但是一般不影响系统完整性,可能优先级就不会太高了
RCE通常需绕过沙箱隔离(如浏览器引擎漏洞、媒体解析漏洞),结合EoP实现完全控制,价值大但是挖掘难度也大,再加上地址空间随机化、沙箱隔离等等安全机制相对少是正常的
常规漏洞点 Manifest.xml
1 2 3 4 5 6 7 8 9 android:debuggable="true" //调试 android:exported="true" //可导出 android:allowBackup="true" //备份 android:usesCleartextTraffic="true" //明文传输 android:protectionLevel //保护等级 - Normal - dangerous path参数置空 - path=""
Activity
activity的利用通常会是整个利用链中比较关键的一环,例如通过启动未导出activity实现越权、扩展攻击面到整个app
LaunchAnyWhere
隐式Intent启动Activity 1 2 3 4 5 6 <activity android:name =".SecondActivity" > <intent-filter > <action android:name ="com.example.test.ACTION_START" /> <category android:name ="android.intent.category.DEFAULT" /> </intent-filter > </activity >
不指出特定包特定Activity,可直接编写恶意Activity让受害app拉起
1 2 Intent intent = Intent("com.example.test.ACTION_START" );startActivity(intent);
Activity劫持
PendingIntent的劫持重定向
敏感页面无防截屏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
禁止截图 :屏幕内容无法通过普通的截图功能捕获(如按键截图或屏幕录制)。
保护在最近任务中显示 :在最近任务列表中,应用窗口内容会被隐藏或模糊,防止敏感信息泄露。
限制非安全显示设备 :如果屏幕内容被投屏到外部设备(如智能电视或投影仪),则会阻止显示。
Content Provider sql注入
目录遍历
1 2 3 4 5 6 private static String IMAGE_DIRECTORY = localFile.getAbsolutePath();public ParcelFileDescriptor openFile (Uri paramUri, String paramString) throws FileNotFoundException { File file = new File (IMAGE_DIRECTORY, paramUri.getLastPathSegment()); return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); }
1 2 3 4 5 6 7 8 9 private static String IMAGE_DIRECTORY = localFile.getAbsolutePath(); public ParcelFileDescriptor openFile (Uri paramUri, String paramString) throws FileNotFoundException { File file = new File (IMAGE_DIRECTORY, Uri.parse(paramUri.getLastPathSegment()).getLastPathSegment()); return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); }
CVE-2021-41256【FileProvider】
Broadcast Receiver 基于广播优先权的发送竞争
粘滞广播导致信息泄露
粘滞广播在发送后会一直保持有效状态,允许后续注册的接收器在注册时立即接收到该广播的最后一个粘滞事件
sendStickyBroadcast
广播无反注册
1 2 3 BroadcastReceiver broadcastReceiver=null ; contextWrapper.registerReceiver(broadcastReceiver,null ); contextWrapper.unregisterReceiver(broadcastReceiver);
Fragment问题
信息泄露 敏感信息外部存储 1 2 3 4 5 6 7 8 9 10 11 12 13 private String filename = "myfile" ; private String string = "sensitive data such as credit card number" ; FileOutputStream fos = null ; try { File file = new File (getExternalFilesDir(TARGET_TYPE), filename); fos = new FileOutputStream (file, false ); fos.write(string.getBytes()); } catch (FileNotFoundException e) { } catch (IOException e) { } finally {}
1 2 3 4 5 6 7 8 9 10 11 12 private String filename = "myfile" ; private String string = "sensitive data such as credit card number" ; FileOutputStream fos = null ; try { fos = openFileOutput(filename, Context.MODE_PRIVATE); fos.write(string.getBytes()); } catch (FileNotFoundException e) { } catch (IOException e) { } finally {
Log打印敏感信息(可使用ProGuard编写规则自动化删除特定日志) 1 2 3 4 5 Log.d(): 用于打印调试信息。示例:Log.d(TAG, "Debug message" ); Log.i(): 用于打印一般信息。示例:Log.i(TAG, "Info message" ); Log.w(): 用于打印警告信息。示例:Log.w(TAG, "Warning message" ); Log.e(): 用于打印错误信息。示例:Log.e(TAG, "Error message" ); Log.v(): 用于打印详细信息(Verbose)。示例:Log.v(TAG, "Verbose message" );
数据缓存泄露 1 2 3 4 5 例如: web:历史url、表单输入、cookie等 键盘输入缓存 GUI对象缓存 图像缓存
1 2 3 webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
合规处理
clearCache()方法
LOAD_NO_CACHE标记
ZIP路径穿越
当然不只是zip,所有可填写路径的地方不做过滤自然有可能绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 BufferedOutputStream dest = null ;ZipInputStream zis = new ZipInputStream (new BufferedInputStream (new FileInputStream ("/Download/test.zip" )));ZipEntry entry; while ((entry = zis.getNextEntry()) != null ) { int count; byte data[] = new byte [BUFFER]; String entryName = entry.getName(); FileOutputStream fos = new FileOutputStream (entryName); dest = new BufferedOutputStream (fos, BUFFER); while ((count = zis.read(data, 0 , BUFFER)) != -1 ) { dest.write(data, 0 , count); } dest.flush(); }
CVE-2018-1261 CVE-2018-8084 CVE-2018-10067 CVE-2018-5722
Webview 部分敏感行为
其实客观来讲这些操作被使用太正常了,全靠校验防护,白名单什么的
setJavaScriptEnabled(True):允许执行JS代码
setPluginState(True):启用Webview插件
setAllowFileAccess(True):启用webview中的文件访问【默认true】
setAllowContentAccess(True):启用webview中的内容URL访问【默认true】
setAllowFileAccessFromFileURLs(True):允许webview中页面使用file协议访问本地文件
setAllowUniversalAccessFromFileURLs(True):允许webview使用file协议访问外部资源
setWebContentsDebuggingEnabled(true):允许调试
白名单校验对抗
URL无校验 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class MyBrowser extends Activity { @Override public void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); WebView webView = (WebView) findViewById(R.id.webview); WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true ); String url = getIntent().getStringExtra("URL" ); webView.loadUrl(url); } } String pkg = "jp.vulnerable.android.app" ; String cls = pkg + ".DummyLauncherActivity" ; String uri = "file:///[crafted HTML file]" ; Intent intent = new Intent (); intent.setClassName(pkg, cls); intent.putExtra("url" , uri); this .startActivity(intent);
Intent Scheme URI 安全风险 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 47 48 49 50 51 52 53 54 webView.setWebViewClient(new WebViewClient () { @Override public boolean shouldOverrideUrlLoading (WebView view, String url) { Context context = null ; Intent intent; try { if (url.startsWith("android-app://" )) { intent = Intent.parseUri(url, Intent.URI_ANDROID_APP_SCHEME); } else { intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); } ResolveInfo ri = context.getPackageManager().resolveActivity(intent, 0 ); if (ri == null ) { return true ; } startActivity(intent); return true ; } catch (Exception e) { Log.d(TAG, "Bad URI" + url + " : " + e.getMessage()); } return false ; } }); webView.setWebViewClient(new WebViewClient () { @Override public boolean shouldOverrideUrlLoading (WebView view, String url) { Context context = null ; Intent intent; try { if (url.startsWith("intent://" )) { intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); intent.addCategory("android.intent.category.BROWSABLE" ); intent.setComponent(null ); intent.setSelector(null ); } else { return true ; } ResolveInfo ri = context.getPackageManager().resolveActivity(intent, 0 ); if (ri == null ) { return true ; } startActivityIfNeeded(intent, -1 ); return true ; } catch (Exception e) { Log.d(TAG, "Bad URI" + url + " : " + e.getMessage()); } return false ; } });
一些文章
Deeplink deeplink作为一个启动组件的桥梁,主要的问题来源于deeplink的部分参数可控导致攻击面的暴露
xss
结合webview loadurl
历史上pwn2own爆的洞很多
验证码/账密
通信 网络通信
MITM
信息泄露
url校验
证书校验
Crypto攻击,私钥泄露
进程通信 Handler内存泄露 原因
Handler一般作为Activity内部类,可以发送延迟执行的消息
在延迟阶段,把Activity关掉
Activity还被内部类Handler持有,导致Activity无法回收,造成内存泄露
防护
1.
Handler定义为静态内部类,持有Activity的弱引用。在Activity的onDestroy中调用handler.removeCallbacksAndMessages(null)及时移除所有消息
这一个方法在《Android开发》的代码中也有用到 2. 把Handler抽离作为BaseHandler,在Activity需要用Handler时extends BaseHandler
异常
这块儿大概有两大类吧 信息泄露 & 拒绝服务
异常提示本身就是一种信息,自然就存在信息泄露的可能。像文件不存在这种提示信息,只要有足够的时间 就有 理论的文件目录枚举爆破可能
另一方面如果有异常被遗漏,没有捕捉到,就会存在被拒绝服务的可能,轻则卡顿,重则闪退乃至利用异常扩展攻击面
异常名称
可能导致的利用
java.sql.SQLException
数据库结构、用户名枚举
java.net .BindException
客户端可以选择服务器端口时,枚举开放端口
java.util.ConcurrentModificationException(结构体被同时遍历时触发)
可能提供有关线程不安全代码的信息
java.util.MissingResourceException
资源枚举
java.lang.OutOfMemoryError
拒绝服务
java.lang.StackOverflowError
拒绝服务
java.io .FileNotFoundException
文件系统结构、用户名枚举
javax.naming.InsufficientResourcesException
服务器资源不足(可能有助于 DoS)
零散 数据格式转换导致精度丢失、功能失效、触发异常等 1 2 3 4 5 BigInteger x = new BigInteger ("530500452766" ); byte [] byteArray = x.toByteArray(); String s = new String (byteArray); byteArray = s.getBytes(); x = new BigInteger (byteArray);
正则校验可绕过 1 2 3 4 5 public static void FindLogEntry (String search) { String regex = "(.*? +public\\[\\d+\\] +.*" + search + ".*)" ; }
native函数公开–>函数输入参数可控–>二进制攻击面 1 2 3 4 5 6 7 8 9 public final class NativeMethod { public native void nativeOperation (byte [] data, int offset, int len) ; static { System.loadLibrary("NativeMethodLib" ); } }
数据公开可读可写权限
MODE_WORLD_WRITABLE | MODE_WORLD_READABLE
1 2 3 4 openFileOutput("someFile" , MODE_WORLD_READABLE); openFileOutput("someFile" , MODE_PRIVATE);
Janus签名漏洞
CVE/顶会/Blackhat Deeplink、webview One-click Open-redirect to own Samsung S22 at Pwn2Own 2022
Binder通信
parcel序列化问题
权限机制问题
BlackHat EU 2021:PendingIntent重定向
DOS
零散 DirtyStream
CVE-2024-0015【DayDream】
CVE-2015-3878【屏幕录制UI欺骗】
屏幕录制用户请求代码没有做显示的文字长度限制,可以伪造信息不显示屏幕录制的相关提示,欺诈用户实现录屏
类似还有CVE-2018-9432 、CVE-2017-13242 。基本都是显示的页面元素长度可控,利用各种换行、替换等操作隐藏真实目的的显示
小米
敏感API C 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 gets strcpy strcat sprintf scanf sscanf fscanf vfscanf vsprintf vscanf vsscanf streadd strecpy strtrns realpath syslog getopt getopt_long getpass getchar fgetc getc read bcopy fgets memcpy snprintf strccpy strcadd strncpy vsnprintf
Java/Smail 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 startActivityForResult(Intent, int ) setResult getSystemService getMacAddress getHardwareAddress ClipboardManager;->setPrimaryClip\|ClipboardManager;->setText ClipboardManager;->setPrimaryClip ClipboardManager;->setText Landroid/content/Context;->openOrCreateDatabase Landroid/database/sqlite/SQLiteDatabase Landroid/util/Log Ljava/io/PrintStream getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences Landroid/content/Context;->openFileOutput Landroid/content/Context;->getDir【参数非0 】 Intent.FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION Ljava/lang/reflect/ Landroid/preference/PreferenceActivity .method protected isValidFragment(Ljava/lang/String;)Z Landroid/preference/PreferenceActivity;->isValidFragment(Ljava/lang/String;)Z Ljava/util/zip /ZipInputStream Ljava/util/zip /ZipEntry;->getName()Ljava/lang/String Landroid/view/Window;->setFlags Landroid/view/Window;->addFlags【忽略第二个参数为0x2000 】 public ParcelFileDescriptor openFile getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); android:filterTouchesWhenObscured="true" public void setFilterTouchesWhenObscured (boolean enabled) 重写View的onFilterTouchEventForSecurity Runtime.getRuntime().exec (cmd);