隐私合规—浅谈污点分析的应用

利用污点分析的思想,将漏洞挖掘的方法应用于隐私合规扫描。

伴随近年来监管,用户,厂商等多方对隐私合规问题的逐渐重视,各APP需要梳理其隐私相关行为,按照合规要求进行整改。然后问题在于,大型APP很难完全整理出隐私相关的调用链路,所以本篇文章从静态扫描的角度来分析如何排查完整隐私调用链路,以半自动化污点分析工具Jandroid为例,来说明污点分析在隐私合规中的实际应用。

隐私合规

2021年开始,从十四五规划中可以看出,在安全领域隐私治理,反数据垄断成为新提出的重点方向。《个人信息保护法》,《数据安全法》等相关法律条令相继颁布并落地,监管加大对各APP的监察力度,多次指令下架多款APP,同时苹果,华为,小米等手机厂商对各自应用商店的上架要求也更为严格,由此可以总结出隐私合规四大趋势:

  • 个人信息保护法律健全完善
  • 监管常态化巡检与专项治理
  • 用户隐私感知能力重点关注
  • 厂商审计监察标准逐步提升

污点分析

简单介绍污点分析思想:

污点分析可以抽象成一个三元组<sources,sinks,sanitizers>的形式,其中,source 即污点源,代表直接引入不受信任的数据或者机密数据到系统中;sink 即污点汇聚点,代表直接产生安全敏感操作(违反数据完整性)或者泄露隐私数据到外界(违反数据保密性);sanitizer 即无害处理,代表通过数据加密或者移除危害操作等手段使数据传播不再对软件系统的信息安全产生危害.污点分析就是分析程序中由污点源引入的数据是否能够不经无害处理,而直接传播到污点汇聚点.如果不能,说明系统是信息流安全的;否则,说明系统产生了隐私数据泄露或危险数据操作等安全问题.

污点分析主要又分为静态污点分析和动态污点分析

  • 静态污点传播分析(简称静态污点分析)是指在不运行且不修改代码的前提下,通过分析程序变量间的数据依赖关系来检测数据能否从污点源传播到污点汇聚点。
  • 动态污点传播分析(简称动态污点分析)是指在程序运行过程中,通过实时监控程序的污点数据在系统程序中的传播来检测数据能否从污点源传播到污点汇聚点。

在本篇文章中主要介绍静态污点分析的应用,Jandroid基于androguard的函数调用关系,应用结构分析,是F-Secure LABS团队开发的用于半自动化逻辑漏洞挖掘工具,同时Jandroid也可以应用于隐私合规,静态代码分析出隐私API链路分析,从而帮助开发者排除整改违规隐私调用。

Jandroid源码分析

首先新建一个Android项目作为例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.example.webviewdemo;

import android.os.Bundle;
import android.webkit.WebView;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

wrap();
}

public void wrap() {
WebView webView = (WebView) findViewById(R.id.webview);
webView.loadUrl("https://baidu.com/");
}

}

配置下项目环境,编译好的apk文件方apps目录,jandroid.py是主程序

分析函数为WebView.loadUrl,反向推演出调用链路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"METADATA": {
"NAME": "JSbridgeBrowsable"
},
"CODEPARAMS": {
"TRACE": {
"TRACEFROM": "<method>:Landroid/webkit/WebView;->loadUrl(Ljava/lang/String;)V",
"TRACETO": "<method>:Lcom/example/webviewdemo/MainActivity;->wrap()V",
"TRACELENGTHMAX": 10,
"RETURN": "<tracepath> AS @tracepath_browsablejsbridge"
}
},
"GRAPH": "@tracepath_browsablejsbridge WITH <method>:<desc>:<class> AS attribute=nodename"
}

开始断点调试来分析Jandroid的运行流程:

  1. 直接进入到fn_analyse_apps函数,之前是参数解析,验证,是否从手机拉取APP等
1
2
3
4
# Create two process queues: one for sending data to,
# and one for receiving data from, worker processes.
process_send_queue = JoinableQueue()
process_receive_queue = JoinableQueue()

创建两个队列来作为生产者和消费者进行对dex的处理,包括对队列的停止消息发送,异常处理

  1. 跟进到fn_perform_analysis,根据template生成bug object对每个app,进行一些初始化处理

  2. fn_per_bug_analysis根据参数是CODEPARAMS还是MANIFESTPARAMS进行分别处理

  3. fn_handle_code_analysis记录满足bug elements的数量

  4. fn_perform_code_analysis处理是否满足整个搜索链路

  5. fn_determine_action根据SEARCH还是TRACE

  6. fn_perform_code_trace处理trace有多少个满足

  7. fn_process_individual_trace_list_item中来分析处理trace_from,trace_to

  8. fn_trace_through_code拆解出来class, method, decs,然后进入正式的搜索

  9. fn_trace_reverse拿到starting point

  10. fn_get_calls_to_method中找到符合sig的方法,然后从这个方法搜索xref_from,也就是调用这个方法的交叉引用

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
# First get all methods that match the given signature.
method_objs = self.fn_get_methods(
class_part,
method_part,
desc_part
)

# Now check the xref_from (i.e., calls to) for the method(s).
calling_methods = []
for method_obj in method_objs:
for xref_from_elem in method_obj.get_xref_from():
# The xref_from_elem is a tuple where the second element
# is the EncodedMethod object.
method_name = xref_from_elem[1].get_name()
# If we don't want anything with user interaction.
if self.keep_user_interaction == False:
is_user_interaction = \
self.fn_check_for_user_interaction_element(
method_name
)
# If the method is to do with user interaction,
# don't include it.
if is_user_interaction == True:
continue
if xref_from_elem[1] not in calling_methods:
calling_methods.append(xref_from_elem[1])
return calling_methods

然后同时搜索trace_from要搜索的类的子类中搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
# Include subclasses.
all_subclasses = []
all_subclasses.extend(
self.inst_analysis_utils.fn_find_subclasses(class_part)
)
for subclass in all_subclasses:
starting_points.extend(
self.inst_analysis_utils.fn_get_calls_to_method(
subclass,
method_part,
desc_part
)
)

然后还有一些不是直接调用的方法,例如OnCreate

1
2
3
4
5
6
7
8
9
10
11
12
# We want to also add the original method to the search as it might not be directly called, for example OnCreate.
if desc_part != '.':
desc_part = re.escape(desc_part)
class_part = re.escape(class_part)
method_part = re.escape(method_part)

mathcing_methods = self.androguard_dx.find_methods(
class_part,
method_part,
desc_part)
for method in mathcing_methods:
starting_points.append(method.get_method())
  1. fn_analyse_trace_point进行具体的分

  2. fn_trace_reverse搜索starting point在xref_from里面的是否在trace_to里面,如果不是继续往上递归搜索xref_from的结果

  3. 如果匹配到链路,那么这次搜索成功,一路向上返回。

  4. 最终可以生成一个图的结果来看。可以看到调用链路

    1
    wrap:()V:Lcom/example/webviewdemo/MainActivity;  -> loadUrl:(Ljava/lang/String;)V:Landroid/webkit/WebView;

其中最为核心的就是利用Androguard搜索xref_from,然后看调用这个函数的是否有匹配链路的,没有就继续往上搜索,同时要注意处理一些特殊情况例如子类,特殊函数OnCreate等,就这样利用交叉引用搜索出整个的调用链路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def fn_get_methods(self, class_part, method_part, desc_part):
"""Gets all methods that satisfy a certain signature.

:param class_part: name of the method class as string
:param method_part: name of the method as string
:param desc_part: descriptor as string
:returns: list of Androguard MethodAnalysis objects
"""
method_objs = []
if desc_part != '.':
desc_part = re.escape(desc_part)
class_part = re.escape(class_part)
method_part = re.escape(method_part)

for method in self.androguard_dx.find_methods(
class_part,
method_part,
desc_part
):
method_objs.append(method)
return method_objs

Androguard的源码分析暂时不在本文中展开。交叉引用的概念可以参考:https://androguard.readthedocs.io/en/latest/intro/xrefs.html

经过上述复杂的调用链路,完成了一次静态污点分析的调用链路排查,通过这样的思想和方法我们就可以完成对相关隐私API链路的调用,梳理出来大部分调用链路,但是这样子的静态分析必然是无法覆盖全面的调用链路的,例如动态调用代码,反射等一些比较特殊的处理都是很难利用静态扫码完整回溯出来的,同时缺乏运行时的信息也是很难完整还原真实的APP隐私行为。

总结

静态扫描作为隐私治理的一种手段,以较低的成本对APP隐私水位有了大致的感知能力。通过借鉴漏洞分析中的污点分析思想,将其应用在隐私治理中,这也是非常有效和常用的,值得我们去更多挖掘类似的方法,之后会对iOS的静态扫描做同样的分析。

参考