ปัจจุบันโปรแกรมประสงค์ร้าย (Malware) ที่แฝงมากับภัยเงียบในระบบแอนดรอยด์ (Android) มีแนวโน้มการโจมตีบ่อยขึ้นในช่วง 2-3 ปี ให้หลัง ทาง McAiden
จึงได้ทำการวิเคราะห์โปรแกรมดังกล่าว (Application) ที่ถูกเผยแพร่สื่อต่าง ๆ ซึ่งสามารถดาวโหลด เพื่อนำไปศึกษา ได้ตามลิงค์นี้ https://cc2.lol
เขียน Juttikhun Jirathanan
แปล/เรียบเรียง Pongsathon Sirithanyakul และ Warunyou Sunpachit

เมื่อเข้าไปหน้าเว็บไซต์ดังกล่าว (สำหรับเครื่องติดตั้ง Antivirus อาจจะมีการแจ้งเตือหรือป้องกันถึงภัยคุกคาม) จะมีหน้าสำหรับดาวน์โหลดโปรแกรมใน 2 ระบบปฏิบัติการ ทั้ง iOS และ Android แต่เราสามารถดาวน์โหลดได้เฉพาะ Android เท่านั้น

Application Structure
ทางทีมได้ทำการตรวจสอบโครงสร้างของโปรแกรมดังกล่าว โดยใช้วิธีการวิเคราะห์แบบ Static
มีลักษณะดังต่อไปนี้

หลังจากทำการ Decompile
ตัวโปรแกรมดังกล่าว โดยใช้ jadx
(https://github.com/skylot/jadx) พบว่ามีบางไฟล์ที่ดูน่าสนใจ ดังนี้
- “libjiagu.so” ซึ่งใช้สำหรับป้องกันไฟล์
APK
จากการทำReverse engineering
ได้ง่าย ๆ โดยการเก็บขั้นตอนต่าง ๆ ที่โปรแกรมประสงค์ร้ายจะทำงาน (app logics) และเปิดเผยมันออกมาเฉพาะตอนใช้งานแล้วเท่านั้น (Runtime) - “easyagent” ซึ่งเป็นไฟล์ APK
- “tg.iapk” ซึ่งเป็นไฟล์ ZIP และ password ที่ถูกป้องกัน และ APK file ที่ถูกเข้ารหัสไว้
AndroidManifest.xml
โดยทั่วไปโปรแกรมที่จะติดตั้งและใช้งานบน Android จำเป็นต้องประกาศ Permissions
และ Attribute
ในไฟล์ AndriodManifest.xml
เพื่อให้ได้สิทธิต่าง ๆ ในการใช้งานบนระบบปฏิบัติการ Android
จากการวิเคราะห์โปรแกรมสามารถอธิบายสิทธิที่สำคัญ (Android Dangerous Permission) ที่ถูกร้องขอโดยโปรแกรมประสงคร้ายดังนี้
Permission Requested | Description |
android.permission.CALL_PHONE | อนุญาตให้แอปพลิเคชันสามารถโทรออกโดยไม่ต้องมีการกระทำจากผู้ใช้งานบนโทรศัพท์ Android |
android.permission.PROCESS_ OUTGOING_CALLS | อนุญาตให้แอปพลิเคชันแก้ไขการโทรออกเช่น เปลี่ยนหมายเลขโทรออก หรือแม้แต่วางหรือเปลี่ยนเส้นทางการโทร |
android.permission.GET_TASKS | อนุญาตให้แอปพลิเคชันสามารถดึงข้อมูลเกี่ยวกับงาน (tasks) ที่กำลังทำงานอยู่ในขณะนั้นบนอุปกรณ์ Android ได้ |
android.permission.MOUNT_UNMOUNT_ FILESYSTEMS | อนุญาตให้แอปพลิเคชัน Mount และ Unmount ระบบไฟล์ |
android.permission.READ_SMS | อนุญาตให้แอปพลิเคชันอ่าน SMS เช่น OTP |
android.permission.SEND_SMS | อนุญาตให้แอปพลิเคชันส่งข้อความ SMS |
android.permission.RECORD_AUDIO | อนุญาตให้แอปพลิเคชันบันทึกเสียง |
android.permission.REQUEST_ INSTALL_PACKAGES | อนุญาตให้แอปพลิเคชันขอให้ผู้ใช้ติดตั้งแพ็คเกจอื่น ๆ |
android.permission.WRITE_APN_SETTINGS | อนุญาตให้แอปพลิเคชันแก้ไข APN เซลลูลาร์ |
android.permission.WRITE_SETTINGS | อนุญาตให้แอปพลิเคชันแก้ไขการตั้งค่าอุปกรณ์ |
android.permission.INJECT_EVENTS | อนุญาตให้แอปพลิเคชันส่งเหตุการณ์อินพุต (การแตะหน้าจอ ฯลฯ) ไปยังแอปพลิเคชันอื่น |
android.permission.SYSTEM_ALERT_WINDOW | อนุญาตให้แอปพลิเคชันสร้างหน้าต่าง เหนือ Activity อื่นๆ (นำไปสู่การ overlay attack) |
android.permission.BIND_ ACCESSIBILITY_ SERVICE | อนุญาตให้แอปพลิเคชันเข้าถึงเนื้อหาของแอปพลิเคชันที่กำลังทำงานอยู่ (รวมถึงแอปพลิเคชันอื่นๆ) |
ยังมีการอนุญาตอื่น ๆ อีกมากมายที่ร้องขอโดยโปรแกรม ส่วนใหญ่ไม่จำเป็นสำหรับการใช้งานปกติและอาจก่อให้เกิดอันตรายได้หลายอย่าง
File: tg.iapk
ตารางต่อไปนี้อธิบายข้อมูลไฟล์:
File name | tg.iapk |
File signature | 634e70a18127375f47c7b40564142ca5597640056a1745b701f7cec3c7701ff1 |
File Size | 2 MB |
File Type | ZIP & Password Protected & Encrypted |
Decrypting tg.iapk
หลังจากพยายามแตกไฟล์ tg.iapk
พบว่าไฟล์นั้นมีการใส่รหัสผ่านป้องกันเอาไว้

พบรหัสผ่านในส่วนของ Comment
ของ ZIP
ซึ่งเป็นข้อความต่อไปนี้:

Archive: tg.iapk 1@386662363963333636636566653438373535363939303135383730373632356332316564636239302d356463312d343737322d623936352d396361653239613234363637 creating: tg.iapk_unzip/com/plugin/ creating: tg.iapk_unzip/com/plugin/tePlugin/ [tg.iapk] com/plugin/tePlugin/activityUtil.java password:
การแยกค่าฐานสิบหก (Hex) และถอดรหัส จะได้ข้อความต่อไปนี้ (Actual ZIP password):
$ python >>> hexStr = "386662363963333636636566653438373535363939303135383730373632356332316564636239302d356463312d343737322d623936352d396361653239613234363637" >>> bytearray.fromhex(hexStr).decode() '8fb69c366cefe487556990158707625c21edcb90-5dc1-4772-b965-9cae29a24667' >>>
หลังจากแตกไฟล์ tg.iapk แล้วพบว่าไฟล์ในนั้นถูกเข้ารหัสและไม่สามารถอ่านได้ง่าย
$ file tg.iapk_unzip/com/plugin/tePlugin/activityUtil.java tg.iapk_unzip/com/plugin/tePlugin/activityUtil.java: data $ strings tg.iapk_unzip/com/plugin/tePlugin/activityUtil.java 3864303638303136376538646535616239316235373665633534323030363465313637323739363638313330357c3132333435367c313637323739363638313330357c31353239 VTC^A^CNbC^[ ]VAVGVT\VPR G[BP^Y CRg[BP^Y :=^ZGXEC [...] $ hd -C tg.iapk_unzip/com/plugin/tePlugin/activityUtil.java 00000000 00 09 23 83 00 00 00 8e 33 38 36 34 33 30 33 36 |..#.....38643036| 00000000 00 09 23 83 00 00 00 8e 33 38 36 34 33 30 33 36 |..#.....38643036| 00000010 33 38 33 30 33 31 33 36 33 37 36 35 33 38 36 34 |3830313637653864| 00000010 33 38 33 30 33 31 33 36 33 37 36 35 33 38 36 34 |3830313637653864| 00000020 36 35 33 35 36 31 36 32 33 39 33 31 36 32 33 35 |6535616239316235| 00000020 36 35 33 35 36 31 36 32 33 39 33 31 36 32 33 35 |6535616239316235| 00000030 33 37 33 36 36 35 36 33 33 35 33 34 33 32 33 30 |3736656335343230| 00000030 33 37 33 36 36 35 36 33 33 35 33 34 33 32 33 30 |3736656335343230| [...]
รูปต่อไปนี้แสดงตัวอย่างไฟล์ที่เข้ารหัสทั้งหมด (update.json):

หลังจากทำการวิเคราะห์การเข้ารหัสในไฟล์ที่เข้ารหัสแล้ว เราก็หาวิธีค้นหาคีย์เข้ารหัสได้ (encryption key) ไฟล์ถูกเข้ารหัสโดยใช้อัลกอริทึม XOR ไฟล์ต่างๆ จะถูกเข้ารหัสด้วยคีย์ที่แตกต่างกัน หากต้องการค้นหาคีย์สำหรับแต่ละไฟล์ เราจะ XOR ส่วนชื่อไฟล์ที่เข้ารหัสด้วยชื่อไฟล์จริง จากนั้นจึงเปิดเผยคีย์ นี่แสดงโครงสร้างไฟล์:
4 bytes [file magic number] |
4 bytes [file header length] |
N bytes [file header] |
4 bytes [file name length] |
[don’t care] |
N bytes [file name] |
N bytes [file content] |
ในการสาธิต เราใช้ไฟล์เข้ารหัส “update.json” ซึ่งมีเนื้อหาฐานสิบหก (Hex) ดังนี้:
File: update.json
00092383 0000008A 33303336 33323339 33303331 33363337 33313335 36323335 33323633 33303632 33313336 36333331 33333632 36323635 33353633 36363633 36363632 33313634 33313336 33373332 33373339 33363336 33383331 33363330 33323763 33313332 33333334 33353336 37633331 33363337 33323337 33393336 33363338 33313336 33303332 37633334 33340000 000B0000 002C181D 090C1908 43071E02 0316674D 4D4F181D 090C1908 32181F01 4F574D4F 4F41674D 4D4F1B08 1F1E0402 034F574D 4F5F435C 435F4F67 10

File magic number: 00092383
File header length: 0000008A
(138 bytes)
File header content: 33303336 33323339 33303331 33363337 33313335
36323335 33323633 33303632 33313336 36333331 33333632 36323635 33353633
36363633 36363632 33313634 33313336 33373332 33373339 33363336 33383331
33363330 33323763 33313332 33333334 33353336 37633331 33363337 33323337
33393336 33363338 33313336 33303332 37633334 3334
File header (decode as hex):
3036323930313637313562353263306231366331336262653563666366623164313637323739363638313630327c3132333435367c313637323739363638313630327c3434
// decode as hex again
0629016715b52c0b16c13bbe5cfcfb1d1672796681602|123456|1672796681602|44
เลขฐานสิบสุดท้ายระบุความยาวของเนื้อหาไฟล์ ในกรณีนี้คือ 44
ไบต์
ความยาวชื่อไฟล์: 000B0000 (11 bytes)
ชื่อไฟล์: 181D090C 19084307 1E0203
เนื้อหาไฟล์: 16674D4D 4F181D09 0C190832 181F014F 574D4F4F 41674D4D 4F1B081F 1E040203 4F574D4F 5F435C43 5F4F6710
ชื่อไฟล์คือ “update.json” เราสามารถใช้กับ XOR ได้ด้วยชื่อไฟล์ที่เข้ารหัสดังนี้:

คีย์ที่ใช้เข้ารหัสไฟล์คือ “m
” หรือ 0x6d
เราสามารถใช้เพื่อถอดรหัสเนื้อหาไฟล์:

ไฟล์ python ถูกสร้างขึ้นเพื่ออำนวยความสะดวกในการถอดรหัสไฟล์ที่เข้ารหัส:
#!/usr/bin/env python # Author: McAiden Consulting Co., Ltd. (2023.01.23) # To PoC how to decrypt files in tg.iapk import sys import binascii def xor(b1, b2): # use xor for bytes result = b"" for b1, b2 in zip(b1, b2): result += bytes([b1 ^ b2]) return result with open(sys.argv[1], 'rb') as f: fileName = sys.argv[1] data = f.read() fileMagic = binascii.hexlify(data[0:4]).decode("ascii") fileHeaderLengthStr = binascii.hexlify(data[4:8]).decode("ascii") fileHeaderLength = int(fileHeaderLengthStr,16) fileHeader = binascii.hexlify(data[8:8+fileHeaderLength]) decodedFileHeader = bytes.fromhex(bytes.fromhex(fileHeader.decode("ascii")).decode("ascii")).decode("ascii") fileContentLength = int(str(decodedFileHeader).rsplit('|', 1)[-1],0) fileNameLength = len(fileName) encryptedFileName = data[(-1)*(fileNameLength + fileContentLength):len(data) - fileContentLength] fileContent = data[(-1)*fileContentLength:] print("[!] fileMagic: " + str(fileMagic)) print("[!] fileHeaderLength: " + str(fileHeaderLengthStr) + ", " + str(fileHeaderLength)) print("[!] fileHeader: " + str(fileHeader)) print("[!] decodedFileHeader: " + str(decodedFileHeader)) print("[!] fileContentLength: " + str(fileContentLength)) print("[!] fileNameLength: " + str(fileNameLength)) print("[!] encryptedFileName: " + str(binascii.hexlify(encryptedFileName))) print("[!] fileContent: " + str(binascii.hexlify(fileContent))) a = fileName.encode('utf-8')[0:1] b = encryptedFileName[0:1] print("[!] a: " + str(a)) print("[!] b: " + str(b)) c = xor(a,b) key = c print("[!] key: " + str(key)) decryptedContent = b"" for i in range(0,len(fileContent)): decryptedContent += xor(fileContent[i:i+1], key) try: newFileName = "decrypted_"+fileName f = open(newFileName, "wb") f.write(decryptedContent) f.close() print("[!] saved to file: " + newFileName) except Exception as e: raise e
ถอดรหัสไฟล์ (decrypt)


tg.iapk Structure
รูปต่อไปนี้แสดงโครงสร้างไฟล์ tg.iapk:

มีหลายไฟล์ที่น่าสนใจ เราถอดรหัสไฟล์ทั้งหมดเริ่มต้นด้วย main.jar
main.jar ที่ถอดรหัสเป็นไฟล์คลาสJava โดยการถอดรหัสโดยใช้คำสั่ง jadx ต่อไปนี้
$ jadx --comments-level debug decrypted_main.jar -d decrypted_main_jadx
ในไฟล์นี้ เราพบคลาสที่น่าสนใจ ใน main.jar เราพบว่ามีการประกาศซึ่งประกอบด้วยชื่อแพ็กเกจ MOBILE BANKING (9 แอป)
และ CRYPTO WALLET (6 แอป)

มีการประกาศชื่อแพ็คเกจที่ระบุว่ากำหนดเป้าหมายแอพ crypto wallet, แอพธนาคารไทย (7 แอพ)
และสิงคโปร์ (2 แอพ)
นี่ไม่ได้หมายความว่าแอปในรายการมีจุดอ่อนหรือช่องโหว่เฉพาะเจาะจง แต่เป็นเพียงรายการที่แอปมุ่งเน้น
Overlay Attack
ในระหว่างการวิเคราะห์แอป แอปได้ขอสิทธิ์ SYSTEM_ALERT_WINDOW
หากได้รับการอนุมัติ แอปพลิเคชันจะสามารถสร้างหน้าต่างที่มี TYPE_APPLICATION_OVERLAY
ซึ่งหมายความว่า แอปสามารถวางวัตถุหรือวัตถุบนหน้าต่างที่ด้านบนของกิจกรรมอื่นๆ แต่ไม่อยู่เหนือกิจกรรมที่สำคัญของระบบ

หน้าต่างที่แอปสร้างขึ้นจะส่งเหตุการณ์ไปยังกิจกรรมด้านล่างเช่น แตะโดยใช้ FLAG_NOT_TOUCHABLE
ซึ่งหมายความว่าไม่สามารถสัมผัสหน้าต่างซ้อนทับได้โดยตรง (มองไม่เห็นและไม่สามารถสัมผัสได้)
Android Accessibility Service
Android’s Accessibility Services (AAS)
ของ Android เป็นคุณลักษณะที่ออกแบบมาสำหรับผู้ใช้ที่มีความพิการบางอย่าง สามารถใช้งาน Android อย่างสะดวกขึ้น ผู้ใช้ต้องเปิดใช้งานในการตั้งค่าอุปกรณ์

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

การประกาศการเข้าถึงใน AndroidManifest.xml:

นี่คือการกำหนด Accessibility Service ใน a.xml:

ตรวจสอบการตั้งค่า AAS ใน a.xml แอตทริบิวต์ canRetrieveWindowContent
ช่วยให้แอปดึงเนื้อหาจากหน้าต่างที่ใช้งานอยู่ซึ่งผู้ใช้เปิดอยู่ accessibilityEventTypes="typeAllMask"
ซึ่งช่วยให้ AAS จัดการกิจกรรมการเข้าถึงทุกประเภท รวมถึง TYPE_WINDOW_CONTENT_CHANGED
สิ่งนี้นำไปสู่ความเป็นไปได้ในการใช้งานอย่างน้อย 2 ประการ:
- อ่านข้อความจากหน้าจอ เนื่องจากสามารถสังเกตการเปลี่ยนแปลงของเนื้อหาได้ ดังนั้น AAS จึงรู้เนื้อหาบนหน้าจอและบันทึกได้ง่าย
- การบันทึกคีย์ หากผู้ใช้พิมพ์บนหน้าจอและเนื้อหาหน้าจอมีการเปลี่ยนแปลง เพื่อให้ AAS สามารถสังเกตได้
เฉพาะ AAS เท่านั้น หากเปิดใช้งานบนอุปกรณ์สำหรับมัลแวร์ ก็เพียงพอแล้วสำหรับผู้โจมตีที่จะดำเนินกิจกรรมที่อันตราย เช่น การขโมยข้อมูล ในส่วน AAS ไม่เพียงแต่สังเกตหน้าจอเพื่ออ่านเท่านั้น แต่สามารถดำเนินการแทนผู้ใช้ได้ รูปต่อไปนี้แสดงวิธีใช้ AAS เพื่อดำเนินการ ACTION_CLICK(16) โดยการเรียก AccessibilityNodeInfo.performAction(16)
บนหน้าจอโดยที่ผู้ใช้ไม่ต้องสัมผัส:

ขณะนี้ AAS สามารถสังเกตเนื้อหาบนหน้าจอ จากนั้นดำเนินการคลิก สิ่งนี้นำไปสู่ความสามารถดังต่อไปนี้:
- ขออนุญาตใหม่และอนุญาตโดยอัตโนมัติ AAS ช่วยให้แอปสามารถสังเกตเห็นได้หากมีการเปลี่ยนแปลงเนื้อหาของหน้าต่าง จากนั้นแอปจะสามารถดำเนินการคลิกที่ปุ่ม “อนุญาต” ของหน้าต่างโต้ตอบการขออนุญาต
สำหรับนักวิเคราะห์ที่ต้องการวิเคราะห์เพิ่มเติม ให้เริ่มจากคลาส com.gibb.WebService
Dynamic Analysis
Class loading
เนื่องจากแอปพลิเคชันถูกทำการแพ็ค (pack) ด้วยโปรแกรมช่วยแพ็ค (packer) ซึ่งหมายความว่าเป็นการยากที่จะวิเคราะห์แอปแบบ Static เนื่องจากคลาสส่วนใหญ่จะไม่โหลดหากแอปไม่ได้รัน เราจำเป็นต้องเรียกใช้แอพและตรวจสอบการโหลดคลาส สามารถใช้สคริปต์ Frida ต่อไปนี้เพื่อตรวจสอบคลาสภายนอกที่โหลดได้
Java.perform(function() { // awaitForClassLoaded("com.js.main", traceClass); // does not work // let dalvik = Java.use("dalvik.system.DexFile"); // let dalvik2 = Java.use("dalvik.system.DexClassLoader"); let dexclassLoader = Java.use("dalvik.system.DexClassLoader"); dexclassLoader.$init.implementation = function(a, b, c, d) { console.log(colors.green + " [!] DexClassLoader loads class from: ", a, colors.default) this.$init(a, b, c, d) try { // traceClass(this) if (a.includes("main")) { // console.log("[!] main.dex is loaded"); // traceClass(this, "com.js.main"); } } catch (e) { console.log(colors.red + e, colors.default) } } });
รูปต่อไปนี้แสดงคลาสการโหลด DexClassLoader จากไฟล์ภายนอกที่แอปทิ้งระหว่างรันไทม์:

เมื่อใช้การวิเคราะห์แบบ Dynamic เราสามารถข้ามขั้นตอนในทุก ๆ อย่าง (เช่น การแตกและถอดรหัสไฟล์) เพื่อทำการเรียกใช้งาน main.dex (maindex.dex) และ ui.dex ได้ เนื่องจากแอปจะประมวลผลขั้นตอนต่าง ๆ ในขณะใช้งานโปรแกรมและวางไฟล์ที่ถอดรหัสไว้ใน “/files” Directory ซึ่งหมายความว่าเราสามารถแยกไฟล์ที่ถอดรหัสออกจากโฟลเดอร์แอพใน “files” Directory

หลังจากวิเคราะห์แอปแล้ว จะเห็นได้ชัดว่า maindex.dex มี main logics ของแอป อาจใช้คอมโพเนนต์อื่นๆ เช่น easyagent, ui.dex และ defaultplugin.apk เพื่อวัตถุประสงค์เฉพาะ แต่หัวใจของแอปอยู่ที่ maindex.dex
Traffic Monitoring
แอปพลิเคชันสื่อสารกับเซิร์ฟเวอร์โดยใช้ HTTPS และ websocket เราจะดักจับการรับส่งข้อมูลโดยใช้โปรแกรม Burp suite กับเซิร์ฟเวอร์ VPN ด้วยการตั้งค่าเซิร์ฟเวอร์ VPN และให้มือถือเชื่อมต่อกับ VPN เราสามารถจัดการการรับส่งข้อมูลทั้งหมดที่ส่งจากมือถือไปยังทุกที่ที่เราต้องการ ในกรณีนี้ เราจะเปลี่ยนเส้นทางการรับส่งข้อมูลไปที่ Burp Suite อย่างไรก็ตามข้อมูลรับส่งผ่านระหว่างโปรแกรม และ Server จะถูกเข้ารหัสทั้งใน HTTPS และ websocket ดังตัวอย่างต่อไปนี้:

แน่นอนว่าทราฟฟิกถูกเข้ารหัส ในการถอดรหัส WS (Websocket) traffic จะต้องรู้จักอัลกอริทึมและคีย์ เราใช้สคริปต์ Frida ต่อไปนี้เพื่อแยกคีย์และอัลกอริทึม:
function get_IV() { //hooking IvParameterSpec's constructor to get the IV var iv_parameter_spec = Java.use("javax.crypto.spec.IvParameterSpec"); iv_parameter_spec.$init.overload("[B").implementation = function(x) { console.log(colors.yellow,'\t[!] IV found: ' + bytesToHex(new Uint8Array(x)),colors.default); return this.$init(x); } } function get_algorithm() { var Cipher = Java.use("javax.crypto.Cipher"); var algo = null; Cipher.getInstance.overload("java.lang.String").implementation = function(x) { console.log("[!] Hooking javax.crypto.Cipher.getInstance"); console.log("\t[!] Encryption algorithm: " + x); algo = x; return Cipher.getInstance.overload("java.lang.String").call(this, x); } return algo; } function get_crypto_info() { var secretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec'); secretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function(a, b) { var result = this.$init(a, b); console.log("======================================"); console.log("[!] algoritm:" + b + "| HexKey:" + util_bytesToHex(new Uint8Array(a))); return result; } var encKey = "N/A"; var secret_key_spec = Java.use("javax.crypto.spec.SecretKeySpec"); var iv_parameter_spec_gcm = Java.use("javax.crypto.spec.GCMParameterSpec"); var iv_parameter_spec = Java.use('javax.crypto.spec.IvParameterSpec'); var cipher = Java.use("javax.crypto.Cipher"); cipher.init.overload("int", "java.security.Key", "java.security.spec.AlgorithmParameterSpec").implementation = function(x, y, z) { console.log("[!] Hooking AlgorithmParameterSpec"); // 1 means Cipher.MODE_ENCRYPT if (x == 1) { // 1 means Cipher.MODE_ENCRYPT console.log("\t[!] Mode: MODE_ENCRYPT"); } else { // In this android app it is either 1 (Cipher.MODE_ENCRYPT) or 2 (Cipher.MODE_DECRYPT) console.log("\t[!] Mode: MODE_DECRYPT"); } encKey = util_bytesToHex(new Uint8Array(y.getEncoded())); console.log(colors.green,"\t[!] Key: " + encKey, colors.default); try { console.log(colors.yellow,"\t[!] IV: " + util_bytesToHex(new Uint8Array(Java.cast(z, iv_parameter_spec_gcm).getIV())),colors.default); console.log("\t[!] Tag length: " + Java.cast(z, iv_parameter_spec_gcm).getTLen()); } catch (error) { } //init must be called this way to work properly return cipher.init.overload("int", "java.security.Key", "java.security.spec.AlgorithmParameterSpec").call(this, x, y, z); } cipher.doFinal.overload("[B").implementation = function(x) { console.log("\t[!] Calling doFinal(Byte) "); console.log("\t\t[!] Before doFinal: " + util_bytesToHex(new Uint8Array(x))); var ret = cipher.doFinal.overload("[B").call(this, x); console.log("\t\t[!] After doFinal: " + util_bytesToHex(new Uint8Array(ret))); return ret; } cipher.doFinal.overload("[B", "int", "int").implementation = function(x, y, z) { console.log("\t[!] Calling doFinal(Byte, int, int) "); console.log("\t[!] Before doFinal: " + util_bytesToHex(new Uint8Array(x))); var ret = cipher.doFinal.overload("[B", "int", "int").call(this, x, y, z); console.log("\t[!] After doFinal: " + util_bytesToHex(new Uint8Array(ret))); return ret; } cipher.update.overload("[B").implementation = function(x) { console.log("\t[!] Calling update(Byte) "); console.log("\t\t[!] Before update: " + util_bytesToHex(new Uint8Array(x))); var ret = cipher.update.overload("[B").call(this, x); console.log("\t\t[!] After update: " + util_bytesToHex(new Uint8Array(ret))); return ret; } cipher.update.overload("[B", "int", "int").implementation = function(x, y, z) { console.log("\t[!] Calling update(Byte, int, int) "); console.log("\t[!] Before update: " + util_bytesToHex(new Uint8Array(x))); var ret = cipher.update.overload("[B", "int", "int").call(this, x, y, z); console.log("\t[!] After update: " + util_bytesToHex(new Uint8Array(ret))); return ret; } cipher.updateAAD.overload("[B").implementation = function(x) { console.log("\t[!] Calling updateAAD(Byte) "); console.log("\t[!] AAD: " + util_bytesToHex(new Uint8Array(x))); var ret = cipher.updateAAD.overload("[B").call(this, x); return ret; } }
เมื่อแอปเข้ารหัสข้อมูลก่อนที่จะส่งไปยังเซิร์ฟเวอร์ javax.crypto.Cipher.init()
จะถูกเรียกด้วย java.securityKey
เป็นหนึ่งใน Argument

ตัวอย่างข้อความ WS request

ในการถอดรหัสทราฟฟิกอื่น ๆ เราไม่ได้พิสูจน์ว่าแอปทำงานอย่างไร แต่เราสันนิษฐานว่าอาจเป็นไปได้ที่จะแยกคีย์โดยใช้ Frida และใช้เพื่อถอดรหัสทราฟฟิกอื่น ๆ เช่น คำขอ HTTP/ตอบกลับไปยัง/จาก *.xdrig.com
ซึ่งใช้ RC4/ECB/NoPadding
พร้อม hardcoded key
เพื่อเข้ารหัสเนื้อหา
IoC
APK (MD5) | 39425fd18017c751d546b3f839495e8a |
maindex.dex (MD5) | 00563ef29eb24da1e9b6c5717ca81da3 |
tg.iapk (MD5) | f91773645d3fed91e99d7d19c2c01c70 |
ui.dex (MD5) | 7fc744141c4bacdfc38529b203aee8d5 |
defaultplugin.apk (MD5) | 51a211128d77f9d567b2900f2f7be63e |
package installed | com.gzrtnq.Bumble |
สำหรับผู้ใช้ทั่วไปที่ต้องการทราบว่ามีการติดตั้งแอปบนโทรศัพท์ของคุณหรือไม่
ให้ไปที่ Settings > Apps > Manage apps
แล้วค้นหา Bumble
ตรวจสอบชื่อแพ็กเกจแอปและถอนการติดตั้งหากตรงกับข้อมูลด้านบน

หากผู้ใช้ไม่สามารถถอนการติดตั้งโดยใช้ UI ได้ อาจจำเป็นต้องใช้เครื่องมือ ADB และควรดำเนินการโดยผู้เชี่ยวชาญ หากต้องการหยุดแอปเราต้องการ ADB โดยเรียกใช้คำสั่งต่อไปนี้:
$ adb shell am force-stop com.gzrtnq.Bumble
เมื่อแอปหยุดลง ACC จะถูกเพิกถอนจากแอป แต่แนะนำให้ลบออก. หากต้องการถอนการติดตั้งแอป เราต้องใช้ ADB โดยเรียกใช้คำสั่งต่อไปนี้:
$ adb uninstall com.gzrtnq.Bumble
Summary
Android Accessibility Service
ของ Android เป็นสิทธิ์ที่อันตรายที่สุดซึ่งจำเป็นต้องเปิดใช้งานโดยการนำทางผ่านหน้าจอการตั้งค่า Android สามารถใช้งานได้หลายวัตถุประสงค์ เป็นที่ทราบกันมานานหลายปีว่ามี Malware ที่ใช้ความสามารถนี้เพื่อโจมตีแอปทางการเงิน เรากำลังมองหาโซลูชันที่เหมาะสมสำหรับนักพัฒนาเพื่อช่วยบรรเทาการโจมตีจาก Overlay Attack และความสามารถในการเข้าถึง แอพ CoinBase ใช้เพื่อตรวจจับการเข้าถึงและแจ้งให้ผู้ใช้ทราบหาก AAS
เปิดใช้งานและโต้ตอบกับแอพ CoinBase ผู้ใช้ต้องเขย่าโทรศัพท์เพื่อใช้งานแอปต่อเมื่อเปิดใช้งาน AAS
อย่างไรก็ตาม เราขอเตือนผู้ใช้ที่ใช้โทรศัพท์ Android อย่าติดตั้งแอปนอก Play Store
ขณะนี้ Google กำลังพยายามปกป้องผู้ใช้ของตนโดยป้องกันไม่ให้แอปที่ใช้ AAS ในทางที่ผิดไม่ให้แสดงอยู่ใน Play Store ดังนั้นจึงไม่ความจำเป็นที่คุณต้องดาวน์โหลดแอปจากแหล่ง อื่น ๆ
อภิธานศัพท์
-
Malware
ย่อมาจากคำว่า “Malicious software” หรือภาษาไทยเรียกว่า “ซอฟต์แวร์ประสงค์ร้าย” -
Android
เป็นระบบปฏิบัติการสำหรับอุปกรณ์เคลื่อนที่ (mobile operating system) ซึ่งถูกพัฒนาโดยบริษัท Google Application
หรือเรียกสั้น ๆ ว่า “App” ในที่นี้หมายถึง Mobile Application โปรแกรมคอมพิวเตอร์ที่ถูกออกแบบมาให้ใช้งานบนอุปกรณ์เคลื่อนที่ เช่น สมาร์ทโฟน แท็บเล็ต หรืออุปกรณ์เสริมต่าง ๆ-
Static
เป็นกระบวนการตรวจสอบโค้ดหรือโปรแกรมโดยไม่ต้องรันโปรแกรมนั้นๆ โดยวิเคราะห์โค้ดหรือไฟล์ของโปรแกรมเพื่อหาข้อผิดพลาด Decompiling
เป็นกระบวนการแปลงโค้ดที่ถูกคอมไพล์เป็นโค้ดที่เข้าใจง่ายขึ้น โดยปกติแล้วโค้ดที่ถูกคอมไพล์จะถูกแปลงเป็นภาษาเครื่อง (machine language) ซึ่งเป็นภาษาที่ยากต่อการอ่านและแก้ไข แต่ถ้าเราต้องการแก้ไขหรือปรับปรุงโปรแกรมที่ถูกคอมไพล์แล้ว เราจำเป็นต้องมีโค้ดต้นฉบับที่เข้าใจง่ายกว่าภาษาเครื่อง Decompiling สามารถทำได้โดยใช้เครื่องมือ (tool) ที่เรียกว่า decompiler ซึ่งจะแปลงโค้ดเครื่องกลับมาเป็นโค้ดต้นฉบับของภาษาต้นฉบับAPK
ย่อมาจาก Android Package Kit ซึ่งเป็นไฟล์ที่ใช้ในการติดตั้งแอปพลิเคชันบนระบบปฏิบัติการ Android โดยส่วนมากจะถูกนำมาจาก Google Play Store หรือเว็บไซต์อื่น ๆ ที่ให้บริการดาวน์โหลดแอปพลิเคชัน AndroidReverse Engineering
หรือ วิศวกรรมย้อนกลับ คือ กระบวนการค้นหาโครงสร้าง ฟังก์ชันการทำงานของอุปกรณ์หรือระบบหนึ่ง ๆ มักเกี่ยวข้องกับการแยกชิ้นส่วนของอุปกรณ์ออกจากกัน (ได้แก่ เครื่องกลอุปกรณ์อิเล็กทรอนิกส์ซอฟต์แวร์) แล้ววิเคราะห์การทำงานในแต่ละส่วน จากนั้นจึงนำมาสร้างอุปกรณ์ใหม่หรือโปรแกรมใหม่ ที่ทำงานได้เหมือนเดิม โดยปราศจากการคัดลอกจากต้นแบบ-
Runtime
หมายถึงช่วงเวลาที่โปรแกรมหรือแอปพลิเคชันทำงานอยู่ในระบบหรือเครื่องคอมพิวเตอร์ โดยมักใช้ในบริบทของภาษาโปรแกรมมิ่ง ซึ่งระบุถึงระยะเวลาที่โปรแกรมทำงานจริงๆ ตั้งแต่ที่เปิดใช้งานไปจนถึงเมื่อปิดใช้งาน ในระหว่างเวลานี้ โปรแกรมจะทำงานและใช้ทรัพยากรของเครื่องคอมพิวเตอร์ เช่น หน่วยประมวลผล หน่วยความจำ ฯลฯ -
Android Dangerous Permission
หมายถึงสิทธิ์การเข้าถึงข้อมูลหรือทรัพยากรที่อาจเป็นอันตรายหรือเสี่ยงต่อความเป็นส่วนตัวของผู้ใช้งาน ซึ่งต้องได้รับการอนุญาตจากผู้ใช้งานก่อนที่แอปพลิเคชันจะสามารถเข้าถึงได้ โดยสิทธิ์เหล่านี้จะถูกกำหนดในไฟล์ AndroidManifest.xml ของแอปพลิเคชัน -
Android’s Accessibility Services (AAS)
เป็นเทคโนโลยีที่ออกแบบมาเพื่อช่วยให้ผู้ใช้งาน Android ที่มีความบกพร่องทางร่างกายหรือสมองสามารถใช้งานอุปกรณ์ได้ง่ายขึ้น โดย AAS จะช่วยให้ผู้ใช้งานสามารถเข้าถึงและใช้งานฟีเจอร์ต่างๆ บนอุปกรณ์ Android ได้สะดวกมากขึ้น โดยเฉพาะอย่างยิ่งในการทำงานที่ต้องใช้งานหน้าจอของอุปกรณ์ เช่น การเข้าถึงแอปพลิเคชัน การเปิดใช้งานฟีเจอร์ต่างๆ เป็นต้น นอกจากนี้ AAS ยังช่วยให้ผู้ใช้งาน Android ที่มีความบกพร่องทางการมองเห็นสามารถใช้งานอุปกรณ์ได้ง่ายขึ้นด้วยการแสดงผลในรูปแบบของเสียงและสัมผัส โดย AAS จะใช้ API และบริการต่างๆ ของระบบ Android เพื่อช่วยในการทำงานนี้.
อ้างอิง
- https://mcaiden.com/2023/01/25/analysis-on-com-gzrtnq-bumble-part-1/
- https://narunc.medium.com/%E0%B9%80%E0%B8%87%E0%B8%B4%E0%B8%99%E0%B8%96%E0%B8%B9%E0%B8%81%E0%B8%94%E0%B8%B9%E0%B8%94%E0%B8%AB%E0%B8%B2%E0%B8%A2%E0%B8%AB%E0%B8%A1%E0%B8%94%E0%B8%9A%E0%B8%B1%E0%B8%8D%E0%B8%8A%E0%B8%B5-%E0%B8%88%E0%B8%A3%E0%B8%B4%E0%B8%87%E0%B9%86%E0%B9%81%E0%B8%A5%E0%B9%89%E0%B8%A7%E0%B9%80%E0%B8%81%E0%B8%B4%E0%B8%94%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3-%E0%B8%A2%E0%B8%B1%E0%B8%87%E0%B9%84%E0%B8%87-a070fee9e1c7