forked from Trietptm-on-Security/WooYun-2
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Android安全开发之Provider组件安全.html
338 lines (184 loc) · 130 KB
/
Android安全开发之Provider组件安全.html
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
<html>
<head>
<title>Android安全开发之Provider组件安全 - 阿里移动安全</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>原文地址:<a href="http://drops.wooyun.org/mobile/16382">http://drops.wooyun.org/mobile/16382</a></h1>
<p>
<p><strong>作者:<a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="a541192f430d10462524403423422e35e54c3d1a4c22294d243f400b2c40200d">[email protected]</a><script data-cfhash='f9e31' type="text/javascript">/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */</script></strong></p>
<h1>0x00 Content Provider组件简介</h1>
<hr />
<p>Content Provider组件是Android应用的重要组件之一,管理对数据的访问,主要用于不同的应用程序之间实现数据共享的功能。Content Provider的数据源不止包括SQLite数据库,还可以是文件数据。通过将数据储存层和应用层分离,Content Provider为各种数据源提供了一个通用的接口。</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010720478267011.png" alt="" /></p>
<!--more-->
<p>创建一个自己的Content Provider需要继承自ContentProvider抽象类,需要重写其中的<code>onCreate()</code>、<code>query()</code>、<code>insert()</code>、<code>update()</code>、<code>delete()</code>、<code>getType()</code>六个抽象方法,这些方法实现对底层数据源的增删改查等操作。还需在AndroidManifest文件注册Content Provider,注册时指定访问权限、exported属性、authority属性值等。</p>
<p><img src="http://static.wooyun.org//drops/20160601/20160601072051918472.png" alt="" /></p>
<p>其它APP使用ContentResolver对象来查询和操作Content Provider,此对象具有Content Provider中同名的方法名。这样其他APP接就可以访问Content Provider对应的数据源的底层数据,而无须知道数据的结构或实现。 </p>
<p>如何定位到具体的数据? </p>
<p>采用Content Uri,一个Content Uri如下所示:</p>
<pre><code>content://com.jaq.providertest.friendsprovider/friends
</code></pre>
<p>它的组成一般分为三部分:</p>
<ol>
<li><p><code>content://</code>:作为 content Uri的特殊标识(必须);</p></li>
<li><p>权(<code>authority</code>):用于唯一标识这个Content Provider,外部访问者可以根据这个标识找到它;在AndroidManifest中也配置的有;</p></li>
<li><p>路径(<code>path</code>): 所需要访问数据的路径,根据业务而定。</p></li>
</ol>
<p>这些内容就不具体展开详谈了,详见参考【1】【4】。</p>
<h1>0x01 风险简介</h1>
<hr />
<p>如果在AndroidManifest文件中将某个Content Provider的exported属性设置为true,则多了一个攻击该APP的攻击点。如果此Content Provider的实现有问题,则可能产生任意数据访问、SQL注入、目录遍历等风险。</p>
<h2>1.1 私有权限定义错误导致数据被任意访问</h2>
<p>私有权限定义经常发生的风险是:定义了私有权限,但是却根本没有定义私有权限的级别,或者定义的权限级别不够,导致恶意应用只要声明这个权限就能够访问到相应的Content Provider提供的数据,造成数据泄露。</p>
<p><strong>以公开的乌云漏洞WooYun-2014-57590为例:</strong></p>
<p>某网盘客户端使用了自己的私有权限,但是在AndroidManifest中却没有定义私有权限,其它APP只要声明这个权限就能访问此网盘客户端提供的Provider,从而访问到用户数据。</p>
<p>在网盘客户端的AndroidManifest中注册Provider时,声明了访问时需要的读写权限,并且权限为客户端自定义的私有权限:</p>
<p><img src="http://static.wooyun.org//drops/20160601/20160601072056586143.jpeg" alt="" /></p>
<p>但是在AndroidManifest中却没有见到私有权限“<code>com.huawei.dbank.v7.provider.DBank.READ_DATABASE</code>”和“<code>com.huawei.dbank.v7.provider.DBank.WRITE_DATABASE</code>”的定义:</p>
<p><img src="http://static.wooyun.org//drops/20160601/20160601072058565874.png" alt="" /></p>
<p>反编译客户端后查看到的URI,根据这些可以构造访问到Provider的URI:</p>
<p><img src="http://static.wooyun.org//drops/20160601/20160601072101904585.png" alt="" /></p>
<p><strong>编写POC</strong></p>
<p>以查看网盘下载的文件列表为例, </p>
<p>在POC的AndroidManifest中声明私有权限,权限的保护级别定义为最低级“<code>normal</code>”:</p>
<p><img src="http://static.wooyun.org//drops/20160601/20160601072103142396.jpeg" alt="" /></p>
<p>主要代码为:</p>
<p><img src="http://static.wooyun.org//drops/20160601/20160601072105446467.jpeg" alt="" /></p>
<p>拿到数据库中保存的下载列表数据:</p>
<p><img src="http://static.wooyun.org//drops/20160601/20160601072108624008.png" alt="" /></p>
<p>对应的数据库:</p>
<p><img src="http://static.wooyun.org//drops/20160601/20160601072110321799.png" alt="" /></p>
<p>这样任意的恶意应用程序就可以访问到用户网盘的上传、下载记录,网盘里面存的文件列表等隐私信息。</p>
<p><strong>再以公开的乌云漏洞wooyun-2013-039697为例:</strong></p>
<p>定义了私有权限,但是保护等级设置成为了<code>dangerous</code>或者<code>normal</code>,这样的保护等级对于一些应用的Provide重要性相比保护级低了。 </p>
<p>Provider为:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721122900910.jpeg" alt="" /></p>
<p>私有权限“<code>com.renren.mobile.android.permission.PERMISSION_ADD_ACCOUNT</code>”的定义为:</p>
<p><img src="http://static.wooyun.org//drops/20160601/2016060107211455076111.png" alt="" /></p>
<p>反编译客户端,看到<code>AcountProvider</code>对应的实现:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721177460512.png" alt="" /></p>
<p>编写POC: </p>
<p>在<code>AndroidManifest</code>中定义和声明权限:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721197127913.jpeg" alt="" /></p>
<p>主要代码为:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721219500114.jpeg" alt="" /></p>
<p>可看到用户的账户信息,包括uid,手机号,加密后的密码等:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721232686515.png" alt="" /></p>
<h2>1.2 本地SQL注入</h2>
<p>当Content Provider的数据源是SQLite数据库时,如果实现不当,而Provider又是暴露的话,则可能会引发本地SQL注入漏洞。</p>
<p><code>Content Provider的query( )</code>的方法定义为:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721247357216.jpeg" alt="" /></p>
<p>其中参数:</p>
<ul>
<li><p><code>uri</code>: 为content Uri,要查询的数据库;</p></li>
<li><p><code>projection</code>:为要查询的列名;</p></li>
<li><p><code>selection</code>和<code>selectionArgs</code>:要指定查询条件;</p></li>
<li><p><code>sortOrder</code>:查询结果如何排序。</p></li>
</ul>
<p><code>query()</code> 与 SQL 查询对比如下:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721261770717.png" alt="" /></p>
<p>如果<code>query( )</code>中使用的是拼接字符串组成SQL语句的形式去查询底层的SQLite数据库时,容易发生SQL注入。</p>
<p><strong>以乌云公开漏洞wooyun-2016-0175294为例:</strong></p>
<p>客户端的<code>com.sohu.sohuvideo.provider.PlayHistoryProvider</code>的<code>exported</code>属性为“true”:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721284747418.png" alt="" /></p>
<p>反编译客户端,追踪<code>PlayHistoryProvider</code>的实现,发现是用拼接字符串形式构造原始的SQL查询语句:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721306881419.png" alt="" /></p>
<p>使用drozer工具,证明漏洞:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721327080220.png" alt="" /></p>
<p>对外暴露的Content Provider实现了<code>openFile()</code>接口,因此其他有相应调用该Content Provider权限的应用即可调用Content Provider的<code>openFile()</code>接口进行文件数据访问。但是如果没有进行Content Provider访问权限控制和对访问的目标文件的Uri进行有效判断,攻击者利用文件目录遍历可访问任意可读文件,更有甚者可以往手机设备可写目录中写入任意数据。</p>
<p><strong>例子1</strong> </p>
<p>以乌云公开漏洞wooyun-2013-044407为例: </p>
<p>此APP实现中定义了一个可以访问本地文件的Content Provider组件,为<code>com.ganji.android.jobs.html5.LocalFileContentProvider</code>,因为使用了<code>minSdkServison=“8”</code>,<code>targetSdkVersion=”13”</code>,即此Content Provider采用默认的导出配置,即<code>android:exported=”true”</code>:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721371117421.png" alt="" /></p>
<p>该Provider实现了<code>openFile()</code>接口:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721406879022.png" alt="" /></p>
<p>通过此接口可以访问内部存储app_webview目录下的数据,由于后台未能对目标文件地址进行有效判断,可以通过”<code>../</code>”实现目录遍历,实现对任意私有数据的访问。</p>
<p><strong>例子2</strong></p>
<p>某社交应用客户端,使用了的<code>minSDKVersion</code>为8,定义了私有权限,并且<code>android:protectionLevel</code>设为了<code>signature</code></p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721423199023.png" alt="" /></p>
<p>有一个对外暴露的Content Provider,即<code>com.facebook.lite.photo.MediaContentProvider</code>,此Provider没有设置访问权限,而另外一个Provider是设置了访问权限的:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721441521824.png" alt="" /></p>
<p>在<code>MediaContentProvider</code>中实现了<code>openFile()</code>接口,没有对传入的URI进行限制和过滤:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721472031725.png" alt="" /></p>
<p>此接口本来只想让用户访问照片信息的,但是却可以突破限制,读取其他文件: </p>
<p>POC:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721497714326.jpeg" alt="" /></p>
<p>读取到其他文件的内容为:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721512583627.jpeg" alt="" /></p>
<p>另外看到<code>Openfile()</code>接口的实现中,如果要访问的文件不存在,就会创建此文件,还有可能的风险就是在应用的目录中写入任意文件。</p>
<h1>0x02 阿里聚安全开发者建议</h1>
<hr />
<p>在进行APP设计时,要清楚哪些Provider的数据是用户隐私数据或者其他重要数据,考虑是否要提供给外部应用使用,如果不需要提供,则在AndroidManifes文件中将其exported属性显式的设为“false”,这样就会减少了很大一部分的攻击面。</p>
<p>人工排查肯定比较麻烦,建议开发者使用阿里聚安全提供的安全扫描服务,在APP上线前进行自动化的安全扫描,尽早发现并规避这样的风险。</p>
<p><strong>注意:</strong></p>
<p>由于Android组件Content Provider无法在Android 2.2(即API Level 8)系统上设为不导出,因此建议声明最低SDK版本为8以上版本(这已经是好几年前的SDK了,现在一般都会大于此版本); </p>
<p>由于API level 在17以下的所有应用的“<code>android:exported</code>”属性默认值都为<code>true</code>,因此如果应用的Content Provider不必要导出,建议显式设置注册的Content Provider组件的“<code>android:exported</code>”属性为<code>false</code>; </p>
<p>如果必须要有数据提供给外部应用使用,则做好设计,做好权限控制,明确什么样的外部应用可以使用,如对于本公司的应用在权限定义时用相同签名即可,合作方的应用检查其签名;不过还是尽量不提供用户隐私敏感信息。</p>
<p>对于必须暴露的Provider,如第二部分遇到的风险解决办法如下:</p>
<h2>2.1 正确的定义私有权限</h2>
<p>在AndroidManifest中定义私有权限的语法为:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721539643728.jpeg" alt="" /></p>
<p>其中<code>android:protectionLevel</code>的可选值分别表示:</p>
<ul>
<li><p><code>normal</code>:默认值,低风险权限,在安装的时候,系统会自动授予权限给 application。</p></li>
<li><p><code>dangerous</code>:高风险权限,如发短信,打电话,读写通讯录。使用此protectionLevel来标识用户可能关注的一些权限。Android将会在安装程序时,警示用户关于这些权限的需求,具体的行为可能依据Android版本或者所安装的移动设备而有所变化。</p></li>
<li><p><code>signature</code>: 签名权限,在其他 app 引用声明的权限的时候,需要保证两个 app 的签名一致。这样系统就会自动授予权限给第三方 app,而不提示给用户。</p></li>
<li><p><code>signatureOrSystem</code>:除了具有相同签名的APP可以访问外,Android系统中的程序有权限访问。</p></li>
</ul>
<p>大部分开放的Provider,是提供给本公司的其他应用使用的,一般的话一个公司打包签名APP的签名证书都应该是一致的,这种情况下,Provider的<code>android:protectionLevel</code>应为设为“<code>signature</code>”。</p>
<h2>2.2 防止本地SQL注入</h2>
<p>注意:一定不要使用拼接来组装SQL语句。 </p>
<p>如果Content Provider的数据源是SQLite数据库,如果使用拼接字符串的形式组成原始SQL语句执行,则会导致SQL注入。 </p>
<p>如下的选择子句:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721552576629.jpeg" alt="" /></p>
<p>如果执行此操作,则会允许用户将恶意 SQL 串连到 SQL 语句上。 </p>
<p>例如,用户可以为 <code>mUserInput</code> 输入“<code>nothing; DROP TABLE ** ;</code>”,这会生成选择子句</p>
<pre><code>#!sql
var = nothing; DROP TABLE **;
</code></pre>
<p>由于选择子句是作为SQL语句处理,因此这可能会导致提供程序擦除基础 SQLite 数据库中的所有表(除非提供程序设置为可捕获 SQL 注入尝试)。</p>
<p><strong>使用参数化查询:</strong></p>
<p>要避免此问题,可使用一个“ <code>?</code> ” 作为可替换参数的选择子句以及一个单独的选择参数数组。 </p>
<p>执行此操作时,用户输入直接受查询约束,而不解释为 SQL 语句的一部分。 </p>
<p>由于用户输入未作为 SQL 处理,因此无法注入恶意 SQL。</p>
<p>请使用此选择子句,而不要使用串连来包括用户输入:</p>
<pre><code>#!java
String mSelectionClause = “var = ?”;
</code></pre>
<p>按如下所示设置选择参数数组:</p>
<pre><code>#!java
String[] selectionArgs = {“”};
</code></pre>
<p>按如下所示将值置于选择参数数组中:</p>
<pre><code>#!java
selectionArgs[0] = mUserInput;
</code></pre>
<p>还可调用SQLiteDatabase类中的参数化查询query()方法:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010721581035530.jpeg" alt="" /></p>
<h2>3.3 防止目录遍历</h2>
<p>1、去除Content Provider中没有必要的openFile()接口。 </p>
<p>2、过滤限制跨域访问,对访问的目标文件的路径进行有效判断: </p>
<p>使用<code>Uri.decode()</code>先对<code>Content Query Uri</code>进行解码后,再过滤如可通过“<code>../</code>”实现任意可读文件的访问的Uri字符串,如:</p>
<p><img src="http://static.wooyun.org//drops/20160601/201606010722002263531.jpeg" alt="" /></p>
<h2>2.4 通过检测签名来授权合作方应用访问</h2>
<p>如果必须给合作方的APP提供Provider的访问权限,而合作方的APP签名证书又于自己公司的不同,可将合作方的APP的签名哈希值预埋在提供Provider的APP中,提供Provider的APP要检查请求访问此Provider的APP的签名,签名匹配通过才让访问。</p>
<h1>0x03 参考</h1>
<hr />
<p>【1】《内容提供程序基础知识》<a href="https://developer.android.com/guide/topics/providers/content-provider-basics.html">https://developer.android.com/guide/topics/providers/content-provider-basics.html</a> </p>
<p>【2】《Android app端的sql注入》<a href="http://zone.wooyun.org/content/15097">http://zone.wooyun.org/content/15097</a></p>
<p>【3】《Android - Content Providers》 <a href="http://www.tutorialspoint.com/android/android_content_providers.htm">http://www.tutorialspoint.com/android/android_content_providers.htm</a> </p>
<p>【4】 <a href="http://www.compiletimeerror.com/2013/12/content-provider-in-android.html">http://www.compiletimeerror.com/2013/12/content-provider-in-android.html</a></p>
<p>【5】 <a href="https://developer.android.com/guide/topics/manifest/permission-element.html?hl=zh-cn">https://developer.android.com/guide/topics/manifest/permission-element.html?hl=zh-cn</a></p>
<p>【6】 <a href="https://developer.android.com/guide/topics/manifest/permission-element.html">https://developer.android.com/guide/topics/manifest/permission-element.html</a></p>
<p>【7】 <a target="_blank" href="http://www.wooyun.org/bugs/wooyun-2013-039697">WooYun: 人人客户端权限问题导致隐私泄露</a> </p>
<p>【8】 <a target="_blank" href="http://www.wooyun.org/bugs/wooyun-2014-057590">WooYun: 华为网盘content provider组件可能泄漏用户信息</a> </p>
<p>【9】 《Android Content Provider Security》<a href="http://drops.wooyun.org/tips/4314">http://drops.wooyun.org/tips/4314</a></p>
<p>【10】 <a target="_blank" href="http://www.wooyun.org/bugs/wooyun-2016-0175294">WooYun: 搜狐app注入泄露观看历史</a> </p>
<p>【11】《Android Content Provider Security》<a href="http://drops.wooyun.org/tips/4314">http://drops.wooyun.org/tips/4314</a></p>
<p>【12】 <a target="_blank" href="http://www.wooyun.org/bugs/wooyun-2013-044407">WooYun: 赶集网Android客户端Content Provider组件任意文件读取漏洞</a> </p>
<p>【13】 <a target="_blank" href="http://www.wooyun.org/bugs/wooyun-2013-044411">WooYun: 58同城Android客户端远程文件写入漏洞</a> </p>
<p>【14】 《Content Provider文件目录遍历漏洞浅析》,<a href="https://jaq.alibaba.com/blog.htm?id=61">https://jaq.alibaba.com/blog.htm?id=61</a> </p>
<p>【15】 <a href="https://github.com/programa-stic/security-advisories/tree/master/FacebookLite">https://github.com/programa-stic/security-advisories/tree/master/FacebookLite</a></p> </p>
</body>
</html>