マイライブラリ
マイライブラリ

+ マイライブラリに追加

電話

お問い合わせ履歴

電話(英語)

+7 (495) 789-45-86

Profile

ウイルスライブラリ

サイバー犯罪者に悪用されるテクノロジーを解析することによって、今後の効果的なウイルス防御対策を策定することが可能になります。感染したシステムにおける各マルウェアの挙動およびその防御対策について、もっと詳しく知りたい方は、ぜひこのページをご覧ください。

Android.Spy.SpinOk ウイルスライブラリ内:

Frist record added to the Dr.Web virus database: 2023-04-18

Description

Android.Spy.SpinOk virus records are used to detect different versions of a marketing module with hidden spyware functionality and also the apps in which it is embedded. This malicious SDK collects information on files stored on Android devices and is able to transfer them to attackers; it can also substitute and upload clipboard contents to a remote server.

Operating routine

Upon initialization, Android.Spy.SpinOk sends a request to the remote host at the https[:]//d3hdbjtb1686tn[.]cloudfront[.]net/gpsdk.html. address. The incoming response has an x-origin header and contains the current active C&C server address. At the time of the analysis, this was https[:]//s[.]hisp[.]in.

The code responsible for obtaining the C&C server address:


// s1 = https[:]//d3hdbjtb1686tn[.]cloudfront[.]net/gpsdk.html
httpURLConnection0 = CLS21213.openConnection(s1);
httpURLConnection0.setRequestMethod("GET");
if(httpURLConnection0.getResponseCode() == 200) {
      Map map0 = httpURLConnection0.getHeaderFields();
      if(map0 == null || (map0.isEmpty())) {
        goto label_19;
      }
      s = (String)((List)map0.get("x-origin")).get(0);
      Log.e(("host : " + s));
      goto label_17;
}

To display banners with ads, Android.Spy.SpinOk sends a request to the C&C server as follows: https[:]//s[.]hisp[.]in/v1/spin/init. In this request, a large amount of technical data about the infected device is sent. Included are data from various sensors, e.g., gyroscope, magnetometer, etc., that can be used to detect an emulator environment. In addition, the trojan module ignores proxy settings to conceal malicious network connections during a threat analysis.

An example of such a request:


{
   "appk":"*",
   "fit":*,
   "flt":*,
   "zo":180,
   "tz":"*",
   "session":"*",
   "uid":"*",
   "did":"*",
   "oaid":"",
   "lang":"en",
   "jb":1,
   "bundle":"com.dewmobile.kuaiya.play",
   "make":"*",
   "brand":"*",
   "model":"*",
   "serial":"unknown",
   "os":1,
   "osv":31,
   "appv":"6.4 (US)",
   "sdkv":"2.3.1",
   "width":*,
   "height":*,
   "ts":*,
   "contype":2,
   "carrier":"",
   "mccmnc":"00",
   "lcountry":"US",
   "battery":100,
   "btch":1,
   "lowp":0,
   "btime":*,
   "fm":*,
   "ram":6893047520,
   "vpn":0,
   "igp":1,
   "gcy":"*",
   "cdid":"",
   "mac":"",
   "ssd":"*",
   "android":{
      "device":"*",
      "product":"*",
      "screen_density":2,
      "screen_size":2,
      "cpu_abi":"arm64-v8a",
      "cpu_abi2":"",
      "cpu_abilist":"arm64-v8a,armeabi-v7a,armeabi",
      "cpu_abilist32":"arm64-v8a,armeabi-v7a,armeabi",
      "cpu_abilist64":"arm64-v8a",
      "api_level":31,
      "d_dpi":450,
      "dim_size":2,
      "xdp":"403",
      "ydp":"402",
      "dfpid":"*",
      "arch":"",
      "chipname":"",
      "bridge":"0",
      "nativebridge":"",
      "bridge_exec":"",
      "isax86_features":"",
      "isa_x86_variant":"",
      "zygote":"zygote64_32",
      "mock_location":"0",
      "isa_arm":"",
      "isa_arm_features":"default",
      "isa_arm_variant":"cortex-a9",
      "isa_arm64_features":"default",
      "isa_arm64_variant":"generic",
      "build_user":"dpi",
      "kernel_qemu":"0",
      "hardware":"qcom",
      "tdm":******,
      "ifgp":0,
      "sensor_size":30,
      "sensors":[
         {
            "sT":2,
            "sN":"LSM6DSO Accelerometer",
            "sV":"STMicro",
            "sVS":[
               -0.49687726,
               4.0287142,
               7.660931
            ],
            "sVE":[
               -0.89480275,
               4.401784,
               2.5893003
            ]
         },
         {
            "sT":2,
            "sN":"AK09918 Magnetometer",
            "sV":"akm",
            "sVS":[
               -1.9,
               -2.50833827,
               -313.59038
            ],
            "sVE":[
               -0.23,
               0.098,
               -184.57023
            ]
         },
         {
            "sT":4,
            "sN":"LSM6DSO Gyroscope",
            "sV":"STMicro",
            "sVS":[
               0.2752669139,
               7.2253818E-4,
               6.789339E-4
            ],
            "sVE":[
               -0.073490673,
               0.0055892063,
               0.001782567
            ]
         }
      ],
      "fb_id":"",
      "build":"*",
      "langname":"English"
   }
}

In response, the C&C server sends the configuration with the data necessary to display ads. An example of such a configuration is shown below:


{
   "api":{
      "lad":"{https[:]//s[.]hisp[.]in/v1/spin/lad"},
      "log":"{https[:]//s[.]hisp[.]in/v1/spin/log"},
      "al":"{https[:]//s[.]hisp[.]in/v1/spin/al"},
      "cud":"{https[:]//s[.]hisp[.]in/v1/spin/cdid"}
   },
   "events":[
       
   ],
   "okid":"*",
   "ri":60,
   "pls":[
      {
         "id":3758,
         "type":2,
         "model":2,
         "link":"{https[:]//s[.]hisp[.]in/v1/spin/tml?pid=*&did=*&os=*&lang=*&contype=*&reqId=*&sdkv=*&appv=*&osv=*&fit=*&flt=*&zo=*&tz=Europe%2F*&session=*&uid=*&jb=*&bundle=*&make=*&brand=*&model=*&appk=*&width=*&height=*&mccmnc=*&gcy=*&sdk=*"},
         "tml":"{https[:]//cdn[.]hisp[.]in/tml/ba.html?v=*"},
         "crs":[
            {
               "img":{
                  "2941":"{https[:]//cdn[.]hisp[.]in/img/*.gif"}
               },
               "id":*
            }
         ],
         "mi":30,
         "click":"{https[:]//s[.]hisp[.]in/v1/spin/log?pid=*&tag=*&eid=*&did=*os=*&lang=*&contype=*&reqId=*&sdkv=*&appv=*&osv=*&fit=*&flt=*&zo=*&tz=Europe%2F*&session=*&uid=*&jb=*&bundle=*&make=*&brand=*&model=*&appk=*&height=*&mccmnc=*&gcy=*&sdk=*"},
         "fi":0,
         "fc":0,
         "fu":0,
         "pt":1
      }
   ]
}

Next, Android.Spy.SpinOk creates a WebView and opens the URLs specified in the tml parameter of the configuration; it also uses web addresses from the link parameter to redirect to targeted webpages.

An sdk JavascriptInterface is added to these WebViews. This interface grants access to the methods of the com.spin.ok.gp.web.BaseJsInterface and com.spin.ok.gp.code.ʼʽʾʿ classes.

The com.spin.ok.gp.web.BaseJsInterface class contains the following method:


@JavascriptInterface
public void executeM(String s) {
    try {
        Log2.i(("JsProxy" + (" executeMethod params: " + s)));
        JSONObject jSONObject0 = new JSONObject(s);
        String s1 = jSONObject0.optJSONObject("data").optString("m");
        JsProxy.onDispatchMethod(this.mWebView, s1, jSONObject0);
    }
    catch(Exception exception0) {
        StringBuilder stringBuilder0 = CLS21196.MTH135618("JsProxy executeMethod error: ");
        stringBuilder0.append(exception0.getMessage());
        Log2.w(stringBuilder0.toString());
    }
}

A Java code, whose fragment is shown below, is executed in JsProxy.onDispatchMethod.


...
BaseJsInterface.class.getMethod(s, WebView.class, JSONObject.class, String.class).invoke(null, webView0, jSONObject1, s2);

where:

  • s—the method name of the com.spin.ok.gp.web.BaseJsInterface class.
  • webView0—an exemplar of the WebView class that called executeM.
  • jSONObject1—the parameters for executing an s method from the com.spin.ok.gp.web.BaseJsInterface class.
  • s2—the name of the JavaScript function that will be called after an s execution. The result from executing this method is used as arguments.

Thus, a JavaScript code executed within loaded WebViews is capable of running static methods of the com.spin.ok.gp.web.BaseJsInterface class with arguments (WebView, JSONObject, String) that lack a @JavascriptInterface annotation.

The most dangerous methods

The public static void listFiles(WebView webView0, JSONObject jSONObject0, String s) method. It tries to obtain a list of files in the directories specified in the param field of the parameter jSONObject0. Next, a JavaScript code is executed in the webView0. This code executes an s function, using a list of files as an argument.

The public static void fileExist(WebView webView0, JSONObject jSONObject0, String s) method. It verifies if the file listed in the param field exists.

The public static void getFileContent(WebView webView0, JSONObject jSONObject0, String s) method. It reads the contents of the file specified in the param of the jSONObject0. The contents are encoded with Base64; an s JavaScript function is then called; the encoded contents of the file are then transferred to it as an argument.

As a result, a JavaScript code executed on loaded webpages that contain ads is capable of stealing user files and files from apps that contain this trojan SDK.

The public static void readClipboard(WebView webView0, JSONObject jSONObject0, String s) method. It allows the contents of the clipboard to be read.

The public static void writeClipboard(WebView webView0, JSONObject jSONObject0, String s) method. It allows the contents of the clipboard to be modified.

At the same time, a com.spin.ok.gp.code.ʼʽʾʿ class that expands the capabilities of the com.spin.ok.gp.web.BaseJsInterface class also has methods available via the @JavascriptInterface annotation. With their help, a JavaScript code can request additional system permissions, open new WebViews, and perform other actions.

The complete list of BaseJsInterface methods:

Method performed action
SHA256(WebView, JSONObject, String) To calculate a SHA256 hash.
UUID(WebView, JSONObject, String) To generate a UUID.
cancelTask(WebView, JSONObject, String) To cancel a task set in sheduleTask.
cleartextTrafficPermitted(WebView, JSONObject, String) Returns a value that indicates if network traffic with plain text is allowed (for example, HTTP, FTP, XMPP, IMAP, SMTP—without TLS or STARTTLS) for communicating with the host name for this process.
execSQL(WebView, JSONObject, String) To execute an SQL request to the SDK database.
@JavascriptInterface
executeM(String)
To execute the specified Java method.
executeMow(WebView, JSONObject, String) To execute a window.executeM(params) JavaScript code in another WebView.
fileExist(WebView, JSONObject, String) Verifies if a specified file exists.
getBundleId(WebView, JSONObject, String) Downloads and unpacks a ZIP file into the files directory of the app.
getByteBufferWrapLong(WebView, JSONObject, String) To decode data.
getChangedPackages(WebView, JSONObject, String) Returns names of the packets that have been changed (for example, those added, deleted, or updated) since the last boot of the device.
getElapsedRealtime(WebView, JSONObject, String) Returns information on how much time has elapsed since the last system booting.
getFileContent(WebView, JSONObject, String) Reads a file, encodes the copy of its contents with Base64 and sends this data to the JavaScript function.
getInitInstalledPackages(WebView, JSONObject, String) Returns a list of installed apps.
getInstalledPackages(WebView, JSONObject, String) Returns a list of installed apps.
getLastPauseTime(WebView, JSONObject, String) Time of the latest pause for the app, according to the app’s data.
getPackageInfo(WebView, JSONObject, String) The information on a specified app: its version, name, the installation time, the update time.
getStackTopPkg(WebView, JSONObject, String) Returns data on the app currently displayed on the screen.
getTjFq(WebView, JSONObject, String) A calculation of specified dates.
getUsageEventTime(WebView, JSONObject, String) Returns the time of the latest device power on.
hasMethod(WebView, JSONObject, String) Verifies whether the BaseJsInterface has a specified method.
insertTable(WebView, JSONObject, String) To add data into the SDK database.
isDebug(WebView, JSONObject, String) Verifies whether a debugging flag is set in the SDK.
isScreenOff(WebView, JSONObject, String) Verifies whether the device screen is on.
listFiles(WebView, JSONObject, String) Returns lists of files located in specified directories.
mowResponse(WebView, JSONObject, String) Calls a JavaScript function in the WebView specified in the parameters and from which the executeMow function was called earlier.
openMarket(WebView, JSONObject, String) To open an app store.
queryTable(WebView, JSONObject, String) A request to the SDK database.
queryUsageEvents(WebView, JSONObject, String) To obtain usage statistics for apps.
queryUsageStats(WebView, JSONObject, String) To obtain usage statistics for apps.
readClipboard(WebView, JSONObject, String) Returns the contents of the clipboard.
saveFiles(WebView, JSONObject, String) Uploads files onto the external storage of the device (into available photo and video directories).
scheduleTask(WebView, JSONObject, String) A postponed execution of a JavaScript function. Another call of the function within the set amount of time is possible.
@JavascriptInterface sendRequest(String) To perform a request to a remote host on a specified URL. The method parameter contains a JSON object with the request parameters. These include an URL, the headers, the body of the request, and the name of the JavaScript function in which the results of this request are sent.
setOkid(WebView, JSONObject, String) Adds data into the SDK database.
setUid(WebView, JSONObject, String) Adds an UUID parameter into the SharedPreferences.
sha256(WebView, JSONObject, String) Calculates a SHA256 hash.
share(WebView, JSONObject, String) To share an object (an URL, a text, or an image).
showRewardToast(WebView, JSONObject, String) Depending on the OS version, displays a notification with an ad, or a pop-up window.
shutdown(WebView, JSONObject, String) Disables a postponed-tasks execution set via the scheduleTask.
updateTable(WebView, JSONObject, String) Changes data in the SDK database.
writeClipboard(WebView, JSONObject, String) Adds data to the clipboard.

The methods list for the com.spin.ok.gp.code.ʼʽʾʿ class that expands the BaseJsInterface.

Method Performed action
@JavascriptInterface String checkPermissions(String) Obtains the permissions list and returns the list of those available.
@JavascriptInterface close() Closes the activity of the SDK.
@JavascriptInterface long getAppUsage(String) Returns the usage time for a specified app.
@JavascriptInterface String getBasicFields() Returns a large volume of data about the device.
@JavascriptInterface String getConfigs(String) Receives an SDK configuration.
@JavascriptInterface String getDownloadApps(String) No action in the current version.
@JavascriptInterface String getHostApp() Returns data on the app containing the embedded SDK (the name, the icon, and its package name).
@JavascriptInterface String getInstalledApp(String) To obtain information about the installed applications.
@JavascriptInterface String getMowBasicFields() To obtain a large volume of technical data on the device.
@JavascriptInterface String getOfferWallPid() Returns an ID from the app’s configuration.
@JavascriptInterface String getPlacement() No action.
@JavascriptInterface grantPermission(String) To request a system permission.
@JavascriptInterface installApp(String) No action in the current version.
@JavascriptInterface boolean isResumed() A parameter value.
@JavascriptInterface jsLoaded() Sets app’s parameters values and cleans up the WebView history.
@JavascriptInterface onOwTaskCreated(String) Adds data into the app’s database.
@JavascriptInterface onUserInteraction(String) An event for the app that contains this SDK.
@JavascriptInterface openApp(String) Launches a specified app (opens the activity).
@JavascriptInterface openBrowser(String) Opens the browser.
@JavascriptInterface openInteractive() Opens a com.spin.ok.gp.activity.WebActivity activity.
@JavascriptInterface openPermissionSettings() Opens the system settings window.
@JavascriptInterface openWebview(String) Opens a com.spin.ok.gp.activity.WebActivity activity and transferring an URL to it, received in parameters.
@JavascriptInterface pageError(String) Error processing.
@JavascriptInterface pushEvent(String) Sends data to the server.
@JavascriptInterface reDownload(String) No action.
@JavascriptInterface setCloseVisible(boolean) Changes the display’s visibility settings. Presumably, this is used to disable or enable visibility for the UI icon that closes ads.
@JavascriptInterface setConfigs(String) Adds data into the SDK database.

Indicators of compromise

News about the trojan

Android 特有の脆弱性

統計データによると、5つの Android対応ソフトウェアのうち、1つのソフトウェアが脆弱性(「セキュリティホール」)を抱えています。こうした状況下では、サイバー犯罪者はモバイルデバイス上にトロイの木馬を仕掛け、それを操ることができます。

Dr.Web for Androidに含まれるSecurity Auditor が、デバイスのセキュリティ上の問題を検出し、問題および脆弱性に対処するソリューションを提供します。