จากบทความเกี่ยวกับแอป Bumble ที่เป็นอันตรายในตอนที่ 1 เราได้แนะนำเกี่ยวกับการ Packing และวิธีการสื่อสารกับเซิร์ฟเวอร์ ในบทความนี้ต้องการเน้นย้ำถึง การโจมตีที่ได้รับความนิยมในมัลแวร์ Android ในช่วงไม่กี่ปีที่ผ่านมา ได้แก่ การใช้การซ้อนทับ (Overlaying) และใช้ Abusing Android Accessibility Service ของ Android ในทางที่ผิด (เราจะเรียกว่า “AAS”)
เขียน Juttikhun Jirathanan
แปล/เรียบเรียง Pongsathon Sirithanyakul และ Warunyou Sunpachit
Overlays
การซ้อนทับ (Overlaying) เป็นการแสดงหน้าต่างประสงค์ร้าย ซ้อนทับด้านบนของแอปพลิเคชัน เพื่อหลอกล่อผู้ใช้กรอกข้อมูลที่สำคัญ (sensitive information) บนแอปที่เป็นอันตราย
ตัวอย่างต่อไปนี้แสดงให้เห็นว่าแอปที่เป็นอันตรายสามารถใช้ SYSTEM_ALERT_WINDOW
ทำการสร้าง ImageView
ที่ซ้อนทับได้อย่างไร:

เขียนโปรแกรมให้ทำการสร้าง ภาพหุ่นยนต์ ซ้อนทับและแสดงบน Chrome Browser

แต่อย่างไรก็ตาม มีหน้าต่างสำคัญที่มัลแวร์ประเภทนี้ไม่สามารถซ้อนทับได้ เช่น การขออนุญาตหรือการตั้งค่าความปลอดภัย

แอป Bumble ได้รับการออกแบบมาอย่างดีพอที่จะแก้ปัญหานี้ได้ แทนที่จะใช้ SYSTEM_ALERT_WINDOW
จะใช้ TYPE_ACCESSIBILITY_OVERLAY
ด้วยคุณลักษณะการเข้าถึงประเภทนี้ Bumble สามารถสร้างหน้าต่างซ้อนทับบนแอปพลิเคชันใดๆ บนอุปกรณ์ แม้ว่าในหน้าต่างการตั้งค่าความปลอดภัยจะไม่สามารถทำได้โดยใช้สิทธิ์ SYSTEM_ALERT_WINDOW

รูปภาพต่อไปนี้แสดงโค้ดที่ใช้ TYPE_ACCESSIBILITY_OVERYLAY
ในมัลแวร์ Bumble:

รูปภาพต่อไปนี้แสดงหน้าต่างซ้อนทับของ Bumble เมื่อผู้ใช้เปิดใช้คุณลักษณะการช่วยสำหรับการเข้าถึงในแอป Bumble ผู้ใช้จะสร้างหน้าต่างซ้อนทับเพื่อให้ครอบคลุมถึงสิ่งที่กำลังทำอยู่ เช่น การขอสิทธิ์อื่นๆ และอนุญาตด้วยตัวเอง (ดูหัวข้อถัดไป AAS):

Android Accessibility Service (AAS)
เราได้แนะนำบริการการเข้าถึงของ Android (AAS) ไปแล้วในตอนที่ 1 สำหรับบทความนี้ จะอธิบายคุณลักษณะบางอย่างที่มัลแวร์อาจนำไปใช้ในทางที่ผิดได้
AAS อนุญาตให้แอปพลิเคชันที่มีความสามารถในการดำเนินการมากกว่าแอปปกติเพื่อช่วยเหลือผู้พิการ ในบทความนี้ เราครอบคลุมความสามารถต่อไปนี้:
- อ่านเนื้อหาจากหน้าจอของแอปพลิเคชันใดก็ได้
- อ่านเนื้อหาจากปุ่มที่ผู้ใช้สัมผัส
- ค้นหามุมมองที่ต้องการ (ในกรณีนี้คือปุ่ม) เพื่อดำเนินการกับมัน
- จำลองการสัมผัสแทนผู้ใช้จริง
ด้วยความสามารถที่กล่าวมานี้ ก็เพียงพอแล้วสำหรับมัลแวร์ที่จะขโมยเงินจากธนาคารบนมือถือ โอนสกุลเงินดิจิทัลจากแอปพลิเคชันกระเป๋าเงินดิจิทัล และดักจับข้อมูลบนแอปพลิเคชันที่เป็นเป้าหมายของมัลแวร์อย่างเงียบ ๆ
เนื่องจาก AAS มีประสิทธิภาพมาก Google จึงพยายามห้ามแอปพลิเคชันที่ใช้ AAS ในทางที่ผิดใน Play Store ดูเพิ่มเติม
https://support.google.com/googleplay/android-developer/answer/10964491?hl=th
1. สามารถอ่านเนื้อหาได้จากหน้าจอ
จากที่กล่าวไว้ในบทความแรก คำสั่ง accessibilityEventTypes="typeAllMask"
ที่ประกาศใน service property file (.xml)
อนุญาตให้แอป lสามารถรับประเภทของเหตุการณ์ การช่วยเหลือสำหรับการเข้าถึง (Accessibility) หนึ่งในนั้นคือ TYPE_WINDOW_CONTENT_CHANGED
ทำให้แอปสามารถ ตรวจสอบได้ว่าเนื้อหาของหน้าต่างปัจจุบันมีการเปลี่ยนแปลงไปหรือไม่ โดยไม่จำเป็นต้องโฟกัสไปที่แอปเป้าหมาย (ไม่ต้องสนใจว่ามัลแวร์จะอยู่ background หรือรันเป็น service) การเปลี่ยนแปลงเนื้อหาที่ทำให้เกิดการเรียกใช้เหตุการณ์ประเภทนี้ ได้แก่ การนำทางไปยังหน้าอื่น การเปลี่ยนแปลงข้อความ การเปลี่ยนแปลงมุมมอง เป็นต้น
รูปต่อไปนี้แสดงแอปพลิเคชันเหยื่อตัวอย่างที่ต้องใช้ชื่อผู้ใช้ (อีเมล) และรหัสผ่านพร้อมรหัสผ่านในกิจกรรมการเข้าสู่ระบบ:

แม้มุมมองจะไม่แสดงรหัสผ่านแก่ผู้ใช้ แต่มาดูกันว่า AAS ดึงข้อมูลอะไรมา รูปภาพต่อไปนี้แสดงค่าที่ดึงมาจากโหนด EditText ชื่อผู้ใช้

ตามที่คาดไว้ อีเมลได้รับในรูปแบบข้อความที่ชัดเจน แต่มาดูกันว่ารหัสผ่านเป็นอย่างไร รูปภาพต่อไปนี้แสดงวิธีการดึงรหัสผ่านจากโหนด EditText:

รหัสผ่านที่เราพิมพ์คือ “temp1234” ดังที่คุณเห็นในภาพด้านบน AAS สามารถดึงข้อมูลอักขระแต่ละตัวก่อนที่จะแปลงเป็น “*” เพื่อซ่อนเอาไว้ ไม่ต้องสงสัยเลยว่าสามารถรับรหัสผ่านแบบเต็มได้
ณ จุดนี้ เนื่องจากเราไม่ได้เผยแพร่ source code ของโปรแกรมตัวอย่า อาจมีบางคนสงสัยว่าเราเขียนแอปที่มีช่องโหว่เพื่อเปิดเผยหรืออำนวยความสะดวกให้ Demo App ของเรา ดึงชื่อผู้ใช้และรหัสผ่านได้ง่ายขึ้นหรือเปล่า ดังนั้นเราทดสอบกับแอปพลิเคชันชื่อดังอย่างแอป Google Authenticator และใช้แอป AAS ที่เราเขียน เพื่อดึงรหัส 2FA จากแอป Google

2. สามารถค้นหาปุ่มที่คลิก (Touched)
เราสงสัยว่า AAS สามารถดึงข้อมูลตำแหน่งที่แน่นอนของวัตถุหรือมุมมองที่ผู้ใช้คลิกได้หรือไม่ เราพบสิ่งที่น่าสนใจคือ สามารถเรียกเหตุการณ์การคลิกได้โดยการกรองประเภทเหตุการณ์ TYPE_VIEW_CLICKED

เราใช้เครื่องคิดเลขบนแอปพลิเคชั่น Xiaomi Redmi 8 เพื่อทดสอบว่าสามารถเรียกคืนเหตุการณ์การคลิกได้หรือไม่ และเป็นไปตามที่คาดไว้ รูปภาพต่อไปนี้แสดงค่าปุ่มเครื่องคิดเลขเมื่อเราพิมพ์ “785”

รูปภาพต่อไปนี้แสดงวิธีดึงค่าของ TextView แต่ละรายการเมื่อถูกคลิก (ลักษณะการทำงานจะเหมือนกัน เมื่อพูดถึง Button class) ซึ่งหมายความว่าแม้แต่แอปที่มีแป้นพิมพ์ของตัวเองและสุ่มตำแหน่งพินทุกครั้ง ก็ไม่ได้หมายความว่าจะไม่ปลอดภัยจาก AAS เนื่องจากไม่ได้ขึ้นอยู่กับตำแหน่ง แต่ขึ้นอยู่กับค่าบนปุ่ม/มุมมองข้อความหรือองค์ประกอบที่คลิกได้

สรุปได้ว่า ได้ว่า AAS นำไปสู่การบันทึกคีย์ (Key logging)
3. สามารถค้นหาปุ่มที่ต้องการและจำลองการแตะ
เนื่องจาก AAS ดึงข้อมูลจำนวนมากของ UI เราจึงสามารถใช้เพื่อค้นหาองค์ประกอบที่เราต้องการ เรา Demo กรณีนี้โดยใช้แอป AAS เพื่อขออนุญาต READ_SMS
โดยอัตโนมัติ และค้นหาปุ่ม “ALLOW” ก่อนทำการแตะที่ปุ่มนั้น สิ่งนี้ทำให้แอป AAS สามารถให้สิทธิ์ใด ๆ ที่ต้องการโดยอัตโนมัติเพื่อดำเนินการอื่น เช่น อ่าน SMS, ส่ง SMS, โทรเข้าโทรศัพท์, เข้าถึงตำแหน่ง, อ่านรายชื่อติดต่อ, เขียนการตั้งค่า, ป้องกันไม่ให้ผู้ใช้เข้าถึงการตั้งค่าเฉพาะและอื่น ๆ อีกมากมาย
แอปจำเป็นต้องขออนุญาตในระหว่างรันไทม์ ขณะขอสิทธิ์ com.google.android.permissioncontroller
โดยที่หน้าจอ หรือ activity
จะถูกเรียกใช้และแสดงหน้าต่างต่อไปนี้เพื่อให้ผู้ใช้ ALLOW หรือ DENY คำขอนั้น จากหน้าต่างตัวควบคุมสิทธิ์ จะเห็นว่ามี 3 ปุ่มคือ “ALLOW”, “DENY” และ “DENY & DON’T ASK AGAIN”

เราต้องหาปุ่ม “ALLOW” เพื่อที่จะคลิกในภายหลัง Codeต่อไปนี้แสดงตัวอย่างการค้นหาปุ่มที่ต้องการโดยใช้ AAS:

ดังที่เห็นด้านบน หลังจากพบปุ่ม “ALLOW” แล้ว nodeI.performAction (ACTION_CLICK) ถูกเรียกเพื่อทำการคลิกที่ปุ่มนั้น
FLAG_SECURE
FLAG_SECURE
เป็นหนึ่งในตัวเลือก WindowManager.LayoutParams
ที่ช่วยให้นักพัฒนาสามารถรักษาความปลอดภัยของหน้าต่างกิจกรรมได้ โดยสรุป การป้องกันต่อไปนี้เกิดขึ้นเมื่อ FLAG_SECURE ถูกตั้งค่าให้กับ Activity ต่าง ๆ :
- ป้องกันการจับภาพหน้าจอ
- ป้องกันการบันทึกหน้าจอ
- ป้องกันการแชร์หน้าจอ (เช่น แคสต์ไปยังทีวี)
- ป้องกันแอปการควบคุมระยะไกล เช่น TeamViewer และ AnyDesk
- ป้องกันไม่ให้ OS ถ่ายภาพหน้าจอพื้นหลัง
ตัวอย่างเช่น รูปภาพต่อไปนี้แสดง Activity ที่มีการตั้งค่า FLAG_SECURE เมื่อมีการใช้งาน

รูปภาพต่อไปนี้แสดงแอป mctargetdemo1 ซึ่ง FLAG_SECURE ถูกตั้งค่าใน Activity ทำให้ในขณะที่ถูกย้ายไปที่พื้นหลัง (สลับแอป) หน้าจอจะได้รับการปกป้อง และอีกภาพหนึ่ง (ขวา) แสดงถึงแอปเมื่อหน้าจอถูกถ่าย แต่ FLAG_SECURE ป้องกันไว้

FLAG_SECURE จะไม่มีประโยชน์เมื่อแอปที่เป็นอันตรายใช้ AAS เพราะว่า AAS สามารถดึงข้อมูลจากองค์ประกอบ UI ที่ได้รับการป้องกันโดย FLAG_SECURE โดยไม่ต้องพยายามอะไรเพิ่มเติม

รูปภาพต่อไปนี้แสดงชื่อผู้ใช้และรหัสผ่านที่สามารถรับได้โดยใช้ AAS


แม้แต่ FLAG_SECURE ก็ไม่สามารถปกป้องกิจกรรมจาก AAS ได้ แต่จำเป็นต้องป้องกันไม่ให้แอปจับภาพหน้าจอ/บันทึกและควบคุมระยะไกลโดยใช้ฟีเจอร์แชร์หน้าจอ
IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
สำหรับ View instance ผู้พัฒนาสามารถใช้ค่าสถานะ IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
โดยเรียกเมธอด setImportantForAccessibility()
วิธีการนี้ใช้สำหรับพิจารณาว่า View มีความสำคัญเพียงใดเมื่อ AAS สอบถามหน้าจอและเมื่อแอปเริ่มการทำงานของ AAS การตั้งค่าสถานะ
IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS จะบอกเหตุการณ์ AAS ที่เริ่มทำงานโดยแอปว่าโหนดจะไม่ถูกรายงานไปยัง AAS ดังนั้น AAS จึงไม่สามารถเข้าถึงมุมมองที่ตั้งค่าสถานะไว้
ตัวอย่างเช่น เราตั้งค่า IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS ใน usernameEditText EditText ดังนี้:

ดังนั้นแอปที่ถูกโจมตีจึงไม่สามารถเข้าถึงข้อมูลใน View (ไม่สามารถเข้าถึงฟิลด์ชื่อผู้ใช้):


อย่างไรก็ตาม จากการตั้ง Flag เราเจอคำถามนี้: หากแอปของคุณรองรับการเข้าถึงเพื่อช่วยเหลือผู้พิการ การตั้งค่าสถานะ IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS จะป้องกันไม่ให้ AAS เข้าถึงข้อมูลใน Activity นั้น
Detect Accessibility Binding With Untrusted App
บริการ Android Accessibility เป็น API อย่างเป็นทางการสำหรับนักพัฒนาเพื่อปรับปรุงการทำงานของแอพเพื่อช่วยให้ผู้พิการสามารถใช้แอพได้ง่ายขึ้น แอพส่วนใหญ่ใช้ AAS เพื่อจุดประสงค์ที่ดีในลักษณะเดียวกับความตั้งใจของ Google แต่แอพบางตัวใช้ AAS ในทางที่ผิดเพื่อขโมยข้อมูลและทำธุรกรรมที่เป็นอันตราย ดังที่ได้กล่าวไว้ก่อนหน้านี้ Google พยายามป้องกันไม่ให้แสดงแอปที่เป็นอันตรายประเภทนั้นใน Play Store แต่เราไม่สามารถไว้วางใจในทุกเหตุการณ์ของแอพที่มาจาก Play Store
แอปพลิเคชันที่เปิดใช้งาน AAS เป็นหนึ่งในตัวบ่งชี้ที่บอกเราว่าแอปพลิเคชันนั้นอาจเป็นอันตราย เราสามารถใช้สิ่งนี้เพื่อตั้งค่าสถานะบนแอปพลิเคชันได้ แต่ยังไม่แม่นยำร้อยเปอร์เซ็นต์ ดังนั้นเราจึงต้องการการยืนยันจากผู้ใช้เมื่อเราพบแอปพลิเคชันที่มี AAS (เช่น การเขย่าอุปกรณ์เพื่อยืนยันการใช้งานในขณะที่เปิดใช้งาน AAS)
Code ต่อไปนี้สามารถใช้เพื่อตรวจสอบว่า AAS เปิดใช้งานอยู่หรือไม่ และมีการเชื่อมโยงกับกิจกรรมของแอปพลิเคชันบนอุปกรณ์หรือไม่
public static boolean isAccessibilityEnabled(Context context) { AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); List<AccessibilityServiceInfo> runningServices = am.getEnabledAccessibilityServiceList(AccessibilityEvent.TYPES_ALL_MASK); for (AccessibilityServiceInfo service : runningServices) { Log.d("[kapiD]", service.toString()); // if (ids.contains(service.getId())) { // Compare your whitelist with the service ID //} return true; } return false; } // Use it somewhere, usually at onCreate(), onResume(), onStart(), or during sensitive actions like transfer money/crypto and 2FA [...] if (isAccessibilityEnabled(this)) { Toast.makeText(LoginActivity.this, "[!] AAS enabled app found, ask user", Toast.LENGTH_LONG).show(); } else { Toast.makeText(LoginActivity.this, "[!] No AAS enabled app found", Toast.LENGTH_LONG).show(); }

Check Installing Package of Suspected App
Android มี API สำหรับตรวจสอบชื่อแพ็คเกจการติดตั้งของแอพ ซึ่งช่วยให้นักพัฒนาสามารถดึงข้อมูลเกี่ยวกับแพคเกจที่ใช้ในการติดตั้งแอป เราแนะนำ 2 API
- สำหรับ API level 29 และต่ำกว่า แอพสามารถใช้ API
PackageManager.getInstallerPackageName()
เพื่อรับชื่อแพ็คเกจการติดตั้งของแอพ - สำหรับ API level 30 ขึ้นไป แอปสามารถใช้ API
PackageManager.getInstallSourceInfo().getInitiatingPackageName()
ขอแนะนำให้ใช้getInitiatingPackageName()
แทนชื่ออื่นๆ เนื่องจากชื่อแพ็คเกจการติดตั้งสามารถเปลี่ยนแปลงได้โดยการเรียกPackageManager.setInstallerPackageName(String, String) getInitiatingPackageName()
ส่งคืนชื่อแพ็คเกจการติดตั้งเริ่มต้น
Code ต่อไปนี้แสดงตัวอย่างการตรวจสอบตัวติดตั้งของแพ็คเกจ:
boolean verifyInstallerId(Context context, String packageName) { String installerPackageName = null; Boolean result; List<String> trustedInstallers = new ArrayList<>(Arrays.asList("com.android.vending")); // Kapi: getInstallSourceInfo() comes with SDK 30+, otherwise use getInstallerPackageName() // Kapi: Because the installing package name can be changed by calling PackageManager#setInstallerPackageName(String, String), getInitiatingPackageName() return the initial installing package name if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { try { installerPackageName = context.getPackageManager().getInstallSourceInfo(packageName).getInitiatingPackageName(); Log.i("[KapiD]","[!] Package: " + packageName + ", installer (SDK 30+): " + installerPackageName); } catch (PackageManager.NameNotFoundException e) { Log.i("[KapiD]", "[!] Package not found: " + packageName); } } else { installerPackageName = context.getPackageManager().getInstallerPackageName(packageName); Log.i("[KapiD]","[!] Package: " + packageName + ", installer: " + installerPackageName); } // Kapi: Compare the installer with trusted installer packages result = trustedInstallers.contains(installerPackageName); return result; }
ชื่อแพ็คเกจการติดตั้งสามารถเป็นได้หลากหลาย ตัวอย่างเช่น:
- เมื่อติดตั้งแอปจาก Google Play Store เราพบว่าชื่อแพ็กเกจการติดตั้งจะเป็น com.android.vending
- เมื่อติดตั้งแอปโดยการดาวน์โหลดจากเว็บแอปพลิเคชัน ชื่อแพ็คเกจการติดตั้งจะเป็น com.google.android.packageinstaller
- เมื่อ ADB ติดตั้งแอปแล้ว ชื่อแพ็กเกจการติดตั้งจะเป็น null
Summary
มัลแวร์ Android กลายเป็นวิธีการยอดนิยมสำหรับอาชญากรในการขโมยข้อมูลหรือทำธุรกรรมที่เป็นอันตรายบนอุปกรณ์ Android Google พยายามป้องกันการใช้ AAS ในทางที่ผิดโดยป้องกันไม่ให้แอป ที่ใช้ AAS ในทางที่ผิดแสดงอยู่ใน Google Play Store แต่อาชญากรยังสามารถหลอกล่อผู้ใช้ให้ติดตั้งแอปที่เป็นอันตรายไม่ทางใดก็ทางหนึ่ง วิธีที่มีประสิทธิภาพในการลดอาชญากรรมทางไซเบอร์บนแอพมือถือประเภทนี้ ในความเห็นของเรา คือการรวมตัวกันระหว่างผู้พัฒนาและผู้ใช้
ผู้พัฒนาควรใส่ใจเรื่องความปลอดภัยให้มากกว่าเดิม เมื่อมีเทคโนโลยีใหม่เข้ามา อาจเป็นคนที่พยายามใช้มันในทางที่ไม่ได้ตั้งใจ เราในฐานะส่วนหนึ่งของ Security community พยายามช่วยสื่อสารและให้คำแนะนำแก่นักพัฒนาโดยการค้นคว้าและเขียนบทความ ที่เป็นประโยชน์ เราหวังว่าบทความนี้จะช่วยให้ทั้งนักพัฒนาและผู้ใช้มีความเข้าใจมากขึ้นเกี่ยวกับเทคนิคของมัลแวร์และสามารถนำไปปฏิบัติในชีวิตจริงได้
ผู้ใช้ควรดูแลและตระหนักในสิ่งที่พวกเขากำลังทำอยู่ มี 2 สิ่งที่เราต้องการเน้นคืออย่าติดตั้งแอปที่ไม่ได้มาจาก official store แม้เราจะรู้จักเว็บไซต์ รู้จักผู้ที่นำแอปมา หรือแม้แต่เราเป็นนักพัฒนา นั่นไม่ได้หมายความว่าเรารู้ว่าแอปกำลังทำอะไรอยู่ อีกอันหนึ่งคือไม่ให้ Accessibility feature แก่ แอปใด ๆ เว้นแต่จะรู้จักและจำเป็นต้องใช้แอปนั้น
สิ่งที่ผู้ใช้สามารถทำได้เมื่อคุณรู้ว่าคุณติดตั้งมัลแวร์:
- ตัดการเชื่อมต่อจากอินเทอร์เน็ตทุกวิถีทาง (Wi-Fi, Cellular ฯลฯ)
- พิจารณา Factory Reset ของคุณ
- พิจารณาเปลี่ยนรหัสผ่านของบัญชีของคุณที่เคยเข้าถึงบนโทรศัพท์เครื่องนั้น
References
- https://mcaiden.com/2023/02/12/analysis-on-com-gzrtnq-bumble-part-2/
- https://mcaiden.com/2023/01/25/analysis-on-com-gzrtnq-bumble-part-1/
- https://developer.android.com/reference/android/content/pm/InstallSourceInfo#getInitiatingPackageName()
- https://valsamaras.medium.com/tapjacking-attacks-a-thorough-guide-2cd6486d0fc9
- https://www.srlabs.de/bites/flubot-abuses-accessibility-features-to-steal-data
- https://labs.withsecure.com/publications/how-are-we-doing-with-androids-overlay-attacks-in-2020