Dissecting a Live Hook Android Banking Trojan C2: Architecture, Exploitation Surface, and What the Operator Got Wrong
Published: 2026-03-08 Tags: threat-intelligence, android-malware, banking-trojan, Hook, ERMAC, C2-analysis, Laravel, Socket.IO TLP: WHITE (IOCs sanitized for public sharing)
TL;DR
A ThreatFox-sourced IOC led to a live Hook Android banking trojan C2 panel at 31.57.216.126. Through systematic probing across 26 phases, we mapped the complete infrastructure: a Laravel/PHP backend, React SPA frontend, Workerman WebSocket relay for real-time VNC and file management, an unauthenticated Socket.IO event bus, and an internet-facing MySQL 8.0.31 instance. Two endpoints lack authentication middleware entirely, the Socket.IO server accepts arbitrary subscriptions without credentials, and the operator's database appears broken -- exposing the C2's full API surface, bot command vocabulary, and architectural weaknesses to anyone willing to look.
1. Discovery and Initial Reconnaissance
The panel surfaced via a same-day ThreatFox report flagging 31.57.216.126 as a Hook C2. Initial HTTP fingerprinting revealed a split-service architecture:
| Port | Service | Role |
|---|---|---|
| 22 | OpenSSH | Remote administration |
| 80 | nginx/1.29.5 | React SPA frontend |
| 3306 | MySQL 8.0.31 | Database (internet-facing) |
| 3434 | Socket.IO (Workerman) | Real-time bot event relay |
| 8000 | Workerman/4.1.9 | WebSocket server (VNC/file manager) |
| 8089 | nginx/1.29.5 -> PHP/8.2.30 | Laravel API backend |
The frontend is a compiled React SPA -- a 3.6MB JavaScript bundle (main.1e9a5134.js) and 673KB CSS bundle -- served through nginx on port 80. The login page features a video background (/assets/images/login_sd.mp4) and multi-language support across English, Turkish, Russian, and Traditional Chinese. The Turkish-language strings (e.g., "ROOT kullanici olustur", "ID botu") suggest a Turkish operator, though the multi-language support is a standard Hook panel feature.
Hook descends from the ERMAC malware family and represents one of the most capable Android banking trojans in active circulation. It combines traditional overlay injection attacks with VNC-based remote control, giving operators hands-on-keyboard access to victim devices.
2. API Surface Mapping
By extracting endpoint paths from the decompiled JavaScript bundle and systematically probing the API, we confirmed 24 live endpoints across the Laravel backend:
Authentication
POST /api/v1/sign-in {token, password} -> {payload.token, payload.user}
GET /api/v1/getUserInfo -> user profile
GET /sanctum/csrf-cookie -> CSRF cookie (204 No Content)
The sign-in endpoint uses token (operator name) and password (codeword) as field names -- not username/password. This is a Hook panel convention. The panel also supports optional Telegram-based 2FA, indicated by HTTP 406 responses.
Bot Management
POST /api/v1/bots/getBotsList {page, per_page, filters}
POST /api/v1/bots/sendBotsCommand {command, payload, botIds}
POST /api/v1/bots/editBotComment PUT method
DELETE /api/v1/bots/deleteBot
DELETE /api/v1/bots/deleteAllRemovedApp
POST /api/v1/bots/{id}/setBotType
POST /api/v1/bots/{id}/settings/updateBotSettings
POST /api/v1/bots/{id}/injects/updateBotInjections
POST /api/v1/bots/{id}/commands/getCommandsList
POST /api/v1/bots/{id}/fileManager/list
GET /api/v1/bots/getBotsFilter
Data Exfiltration & Management
POST /api/v1/logs/getLogsList {type, bot_id, application, page, per_page}
POST /api/v1/injects/getInjectionsList
POST /api/v1/injects/createInjection
POST /api/v1/counts/getCounts
POST /api/v1/statistics/getStats
POST /api/v1/permissions/getPermissionsList
POST /api/v1/smartInjections/getSessions [NO AUTH MIDDLEWARE]
Operator & Telegram Management
POST /api/v1/accounts/getAccountsList
POST /api/v1/accounts/createAccount
POST /api/v1/accounts/sendTelegramMessage
PUT /api/v1/accounts/unbindTelegram
PUT /api/v1/accounts/updateTelegramBot
PUT /api/v1/accounts/updateTelegramInjection
Role Hierarchy
The JavaScript bundle references three user roles:
- ROOT -- highest privilege, can create operators
- ADMIN -- standard operator access
- SEO -- limited, likely view-only role
3. The Bot Command Vocabulary
Extraction of the command dispatch logic from the JS bundle reveals Hook's full device control capabilities:
| Category | Commands |
|---|---|
| SMS | getSMS, sendSMS, forwardSMS |
| Calls | makeCall, forwardCall, calling |
| Contacts | getContacts, addContact |
| Injection | startInject, updateInjects, updateInjectAndListApps |
| Keylogging | keylogger |
gmailTitles, getGmailMessage | |
| Screenshot | screenshot |
| Apps | openApp, startApp, deleteApp, getInstalledApps |
| Location | getLocation, geolocation |
| Messaging | WhatsApp message sending |
| Finance | USSD commands, crypto SEED phrase theft |
| Media | getImages |
| Social Engineering | Push notification spoofing |
| Credential Theft | getAccounts (Android AccountManager) |
| Remote Control | VNC (via WebSocket port 8000), file manager |
| Smart Injection | Phishing overlay sessions |
| Destruction | killMe (self-destruct/uninstall) |
The openurl command is particularly notable -- it allows operators to redirect victim browsers to arbitrary URLs, enabling on-the-fly phishing or drive-by download chains.
Panel Route Structure
The frontend routes expose the full operational workflow:
/banks Banking injection targets
/bots Bot management (online/offline/uninstalled)
/cards Stolen credit cards
/crypt Cryptocurrency theft
/email Stolen email data
/injections HTML overlay injection management
/shops Stolen data marketplace interface
/stealer Stealer module output
/wallet Cryptocurrency wallets
/storage/emulated/0 Remote file browser on victim devices
/stats Operations dashboard
/users Operator management
4. Vulnerability Analysis: What We Found
Our 26-phase exploitation attempt covered sign-in fuzzing, Laravel debug mode probes, known Laravel CVE paths, auth bypass attempts, JWT forgery, Sanctum CSRF flow analysis, credential brute force, endpoint discovery, Socket.IO exploitation, and MySQL authentication. Here is what yielded results.
4.1 Unauthenticated Endpoints (HIGH)
Phase 14's comprehensive auth check distinguished between endpoints returning 401 Unauthenticated (auth middleware present) and those returning 500 Server Error (no auth middleware -- crashes directly into application code):
Endpoints returning 500 (NO AUTH MIDDLEWARE):
* /api/v1/sign-in (POST)
* /api/v1/smartInjections/getSessions (POST)
All other endpoints: 401 Unauthenticated
The smartInjections/getSessions endpoint is the critical finding. This endpoint has no authentication middleware applied -- requests bypass the Sanctum guard entirely and hit the database layer directly. It currently returns 500 because the database is broken, but if the operator fixes their DB, this endpoint would immediately leak SmartInjection phishing session data (stolen credentials captured via overlay attacks) to any unauthenticated caller.
4.2 Socket.IO Accepts Unauthenticated Connections (HIGH)
The Socket.IO server on port 3434 accepts connections with no authentication whatsoever:
# EIO=3 handshake -- no credentials required
GET /socket.io/?EIO=3&transport=polling
# Response:
{"sid":"bc7","upgrades":["websocket"],"pingInterval":20000,"pingTimeout":60000}
We successfully sent subscription events that the server accepted without challenge:
42["subscribe","bots"] -> accepted
42["subscribe","logs"] -> accepted
42["subscribe","all"] -> accepted
42["newBot",{"tag":"test"}] -> accepted
42["update",{}] -> accepted
The server crashed (500) when attempting to fulfill subscriptions due to the same database connectivity issue. In a functioning deployment, an attacker could passively monitor all bot check-ins, log entries, and operator activity in real time by subscribing to the event stream -- or inject fake bot events via newBot.
The hardcoded Socket.IO connection URL extracted from the JS bundle:
var lqe = sqe()("http://31.57.216.126:3434/", {transports: ["polling"]});
4.3 MySQL Exposed to the Internet (CRITICAL)
Port 3306 is directly accessible from the internet. MySQL 8.0.31 responds to connection attempts with its full banner:
MySQL 8.0.31 | Auth: caching_sha2_password
Connection IDs observed: 25499-25510 (high activity indicator)
We tested 22 credential combinations across common usernames (root, hook, admin, laravel, forge, homestead, panel, mysql, app, debian-sys-maint) with common passwords. All returned "Access denied." The caching_sha2_password plugin and credential failure suggest the operator at least set non-default DB credentials -- though exposing MySQL to the internet is itself a critical misconfiguration.
4.4 CORS Fully Open (MEDIUM)
Every API response includes:
Access-Control-Allow-Origin: *
This means any website on the internet can make authenticated cross-origin requests to the Hook API. If an operator visits a malicious page while logged in, the attacker's JavaScript can call any API endpoint using the operator's session.
4.5 Sign-In Backend Failure (MEDIUM)
The sign-in endpoint crashes on all valid-looking inputs:
# String credentials -> 500
curl -X POST http://31.57.216.126:8089/api/v1/sign-in \
-H "Content-Type: application/json" \
-d '{"token":"admin","password":"admin"}'
# {"message":"Server Error"} HTTP 500
# Non-string inputs -> 302 redirect (validation layer)
curl -X POST http://31.57.216.126:8089/api/v1/sign-in \
-H "Content-Type: application/json" \
-d '{"token":null,"password":null}'
# 302 -> http://31.57.216.126:8089
The behavioral difference is telling: non-string types are caught by Laravel's validation middleware and redirected, while string values pass validation and crash when the application tries to query the users table. APP_DEBUG=false prevents stack trace leakage, returning only {"message":"Server Error"}.
5. What We Tried and What Failed
Thoroughness matters. Here is a summary of techniques that did not yield access:
| Technique | Result |
|---|---|
SQL injection on sign-in (' OR 1=1--, " OR ""=") | 500 (same as normal -- DB broken) |
NoSQL injection ({"$gt":""}) | 302 redirect (type validation) |
Mass assignment (role=admin, is_admin=true) | 500 (ignored by Laravel) |
Laravel debug tools (/_debugbar, /telescope, /horizon, /log-viewer) | 404 |
Sensitive file access (/.env, /storage/logs/laravel.log, /vendor/) | 404 on port 8089 |
PHPUnit eval-stdin (/vendor/phpunit/.../eval-stdin.php) | 404 |
Swagger/API docs (/swagger, /api/documentation, /docs) | 404 |
| Bearer token bypass (null, empty, "0", undefined) | 401 |
JWT forgery -- alg:none | 401 |
JWT forgery -- HS256 with weak secrets (secret, password, hook, key) | 401 |
| Sanctum session cookie auth flow | No XSRF-TOKEN set |
| X-Forwarded-For / X-Real-IP bypass (127.0.0.1) | 401 |
Laravel Ignition RCE (/_ignition/execute-solution) | 404 |
| PHP deserialization via malformed session cookies | 500 |
Nginx path traversal (/../.env, /..;/.env, URL-encoded) | Normalized/blocked |
| phpinfo endpoints | 404 |
OAuth token endpoints (/oauth/token, /oauth/personal-access-tokens) | 404 |
| Credential brute force (20 common pairs) | All 500 (DB down) |
| MySQL auth brute force (22 user/pass combos) | All access denied |
6. Architecture Deep Dive
Infrastructure Diagram
βββββββββββββββββββ
β Port 80 β
β nginx/1.29.5 β
β React SPA β
β (3.6MB JS) β
ββββββββββ¬βββββββββ
β API calls
βΌ
ββββββββββββββββ βββββββββββββββββββ ββββββββββββββββ
β Port 3434 β β Port 8089 β β Port 3306 β
β Socket.IO ββββββββββΊβ Laravel API ββββββββββΊβ MySQL 8.0.31 β
β (Workerman) β events β PHP/8.2.30 β ORM β (EXTERNAL!) β
β NO AUTH β β Sanctum auth β ββββββββββββββββ
ββββββββββββββββ βββββββββββββββββββ
β
β WS relay
βΌ
βββββββββββββββββββ
β Port 8000 β
β Workerman/4.1.9β
β WebSocket β
β VNC + FileMgr β
βββββββββββββββββββ
Authentication Flow
The panel uses Laravel Sanctum for API token authentication:
- Operator submits
token(username) +passwordvia/api/v1/sign-in - Optional: Telegram 2FA challenge (HTTP 406 triggers 2FA flow)
- Backend returns a Bearer token stored in
localStoragekey"token" - All subsequent API requests include
Authorization: Bearer <token> - Session cookie
hook_session(Laravel encrypted,httponly,samesite=lax) provides server-side session state
Database Failure Root Cause Analysis
The backend MySQL instance is accessible on port 3306 and responds to the MySQL wire protocol, but the Laravel application cannot query it. Possible causes:
- Wrong credentials in
.env-- Laravel's DB credentials don't match MySQL's configured users - Schema not migrated -- Tables don't exist (would explain crashes on any query)
- Permission issue -- Laravel's DB user lacks SELECT/INSERT privileges
- Connection pool exhaustion -- Connection IDs near 25,500 suggest heavy traffic
This is notable because it means the C2 was deployed but never properly configured -- or it was configured, used, and then the database was broken (accidentally or through counter-operations).
7. MITRE ATT&CK Mapping
| Technique ID | Name | Context |
|---|---|---|
| T1437.001 | Application Layer Protocol: Web Protocols | Laravel API + Socket.IO for C2 comms |
| T1481.002 | Web Service: Bidirectional Communication | WebSocket (Workerman) for VNC/file manager |
| T1406 | Obfuscated Files or Information | Compiled React SPA, minified JS bundle |
| T1417.001 | Input Capture: Keylogging | keylogger bot command |
| T1417.002 | Input Capture: GUI Input Capture | Smart Injections (overlay attacks) |
| T1516 | Input Injection | VNC remote control via WebSocket |
| T1513 | Screen Capture | screenshot bot command |
| T1430 | Location Tracking | getLocation, geolocation commands |
| T1636.004 | Protected User Data: SMS Messages | getSMS, forwardSMS commands |
| T1636.003 | Protected User Data: Contact List | getContacts command |
| T1636.001 | Protected User Data: Calendar Entries | Overlay injection targeting banking apps |
| T1624.001 | Event Triggered Execution: Broadcast Receivers | SMS interception |
| T1582 | SMS Control | sendSMS command |
| T1616 | Call Control | makeCall, forwardCall commands |
| T1398 | Boot or Logon Initialization Scripts | Persistence via Android services |
| T1407 | Download New Code at Runtime | updateInjects, openurl commands |
| T1632.001 | Subvert Trust Controls: Code Signing Policy | Sideloaded APK installation |
8. Indicators of Compromise
Network Indicators
| Type | Value | Context |
|---|---|---|
| IPv4 | 31.57.216.126 | Hook C2 panel IP |
| Port | 80/tcp | React SPA frontend (nginx/1.29.5) |
| Port | 3306/tcp | MySQL 8.0.31 (internet-facing) |
| Port | 3434/tcp | Socket.IO event bus (unauthenticated) |
| Port | 8000/tcp | Workerman WebSocket (VNC/file manager) |
| Port | 8089/tcp | Laravel PHP API backend |
| URL | http://31.57.216.126:8089/api/v1/sign-in | Panel login endpoint |
| URL | http://31.57.216.126:3434/socket.io/ | Socket.IO handshake |
Application Fingerprints
| Indicator | Value |
|---|---|
| JS Bundle | main.1e9a5134.js (3,668,590 bytes) |
| CSS Bundle | main.2a8075f7.css (673,200 bytes) |
| Session Cookie | hook_session (Laravel encrypted) |
| Auth Token Storage | localStorage key "token" |
| Server Header | nginx/1.29.5 |
| PHP Version | 8.2.30 |
| MySQL Version | 8.0.31 |
| WebSocket Framework | Workerman/4.1.9 |
| MySQL Auth Plugin | caching_sha2_password |
| Font | Mulish (custom) |
Hook Panel Detection Signatures
The following URL patterns are characteristic of Hook C2 panels and can be used for hunting:
/api/v1/sign-in
/api/v1/bots/getBotsList
/api/v1/bots/sendBotsCommand
/api/v1/smartInjections/getSessions
/api/v1/counts/getCounts
/api/v1/injects/getInjectionsList
/api/v1/accounts/sendTelegramMessage
HTTP response fingerprint for unauthenticated requests:
{"message":"Unauthenticated."}
With Content-Type: application/json and Access-Control-Allow-Origin: *.
CORS Header (Universal)
Access-Control-Allow-Origin: *
Present on all API responses -- usable as a detection heuristic when combined with the endpoint patterns above.
9. Defensive Recommendations
For threat intelligence teams tracking Hook deployments:
- Monitor port 3434 -- Unauthenticated Socket.IO endpoints are a reliable fingerprint for poorly configured Hook panels. A simple EIO=3 handshake probe will confirm.
- Probe
/api/v1/smartInjections/getSessions-- If this returns anything other than 401, the panel is leaking SmartInjection session data (phished credentials) without authentication. - MySQL on 3306 -- Internet-facing MySQL is common in hasty Hook deployments. Banner grabbing reveals version, connection count (activity level), and auth plugin.
- JS bundle hash --
main.1e9a5134.jsidentifies this specific Hook panel build. Different hashes indicate different builds or versions.
For organizations defending against Hook:
- Block the IP
31.57.216.126at the network perimeter - Monitor for Hook's characteristic overlay injection behavior on Android devices
- Implement phishing-resistant MFA to defeat credential overlay attacks
- Deploy mobile threat defense solutions that detect accessibility service abuse
10. Conclusion
This Hook panel is a textbook example of a threat actor deploying sophisticated malware on a poorly secured infrastructure. The malware itself -- with VNC, smart injections, keylogging, SMS interception, and crypto theft -- represents a formidable threat to Android users. But the operator made fundamental mistakes: an internet-facing MySQL instance, an unauthenticated Socket.IO event bus, a missing auth middleware on a sensitive endpoint, and a wildcard CORS policy.
The broken database is the most interesting detail. Whether it represents a botched deployment, a competed-over panel, or the result of counter-operations, it means this C2 is currently inert -- but the infrastructure is standing and could become active at any time if someone fixes the database connection. We will continue monitoring.
Investigation conducted 2026-03-07. All probing was performed against infrastructure confirmed as malicious C2 via ThreatFox.