alpha_miner Job Scheduler: 194.233.71.223 (Contabo SG)
Date: 2026-05-15 Sector: commercial (quant trading bot platform + commercial proxy resale) Severity: CRITICAL (plugin-loader RCE-by-design + colocated unauth LLM + LLMjacking attribution-laundering risk) Operator confidence: low (Vietnamese-pattern usernames on Indonesian-domain-cluster host) Technical-exposure confidence: high (all claims reproducible from a single unauth GET)
Identity
- IP:
194.233.71.223 - rDNS:
vmi2733226.contaboserver.net - ASN:
AS141995 Contabo Asia Private Limited - Location: Singapore (Contabo Asia Pte Ltd, 8 Robinson Road / International Plaza)
- WHOIS abuse:
abuse@contabo.de - Passive DNS (HackerTarget) cluster on same IP:
aceservice.storeackeliling.store(Bahasa Indonesia: “AC keliling” = mobile AC service)jasatukangac.store(“jasa tukang AC” = AC repairman service)liangserviceac.storewarungngopi.xyz(“warung ngopi” = coffee stall)
DCWF KSAT coverage
Auto-derived from DCWF AI work-role rule files (ksat-tag).
- 672 (AI Test & Evaluation Specialist): K7003, K7004, K7044, S7068, S7070, S7075, T5858, T5904, T5919
- 733 (AI Risk & Ethics Specialist): S7056, T5868, T5882, T5893, T5904
- overlap (Common AI KSATs (all 5 roles)): K108, K1158, K1159, K22, K6311, K6935, K7003, K7048, K942, T5896
The passive-DNS cluster is uniformly Indonesian-language AC-repair brands. The platform user roster is thanhtu (admin) and cuongnv (user). Vietnamese-pattern names. These two attribution signals do not reconcile cleanly; cheap-tier Contabo VPS shared by an Indonesian operator with Vietnamese contracted developers is a viable read, multi-tenant noise is another.
Port surface (Shodan / nu-recon)
| Port | Service | Status |
|---|---|---|
| 22 | OpenSSH 9.6p1 Ubuntu 24.04 | management |
| 8000 | FastAPI/Uvicorn “Job Scheduler” (alpha_miner) | partial-auth (CRITICAL) |
| 10000 / 10001 / 10003 / 10004 / 10007 / 10009 | 3Proxy HTTP proxy | Basic-auth required (paid resale) |
| 11000 / 11001 / 11002 / 11007 | Socks4A | open |
| 11434 | llama.cpp HTTP server | fully unauth (HIGH) |
Stage 2 verification: auth posture per endpoint
| Endpoint | Method | Unauth response |
|---|---|---|
/api/stats/jobs | GET | 400 "Permission denied: system" (enforced) |
/api/users | GET | 200 — full user roster + roles |
/api/users/me | GET | 200 — {"id":0,"roles":[],"username":""} (anon, not 401) |
/api/users/policy | GET | 200 — full RBAC matrix |
/api/plugins | GET | 200 — full installed-plugin list |
/api/plugins/routes | GET | 200 — plugin route mounts |
/api/plugins/download/{name} | GET | 200 (route defined; payload returned SPA-catchall in our probe — auth path needs further test) |
/api/templates | GET | 200 |
/api/plugins/install/{package} | POST | 405 to GET, route exists |
Split-auth posture: the auth scheme (OAuth2PasswordBearer at /auth/token) is real and is enforced on at least one endpoint, but six discoverable endpoints serve sensitive data unauth. Per Insight #16, a 200 is platform identity, not auth state. Here the data-layer probe confirms each 200 is a real exposure, not a documented anonymous shape.
Critical finding: plugin loader RCE-by-design
Full unauth dump of /api/plugins:
[
{"id":1, "package":"plugins.sample_plugin.Plugin", "description":"Sample plugin version 0.1"},
{"id":2, "package":"plugins.backtest_model.Plugin", "description":"Sample plugin version 0.2"},
{"id":6, "package":"alpha_miner.plugins.UatUserCustomConfigPlugin", "description":"custom config worker"},
{"id":7, "package":"alpha_miner.plugins.UatPreviewPlugin", "description":"Uat Plugin for preview only"},
{"id":9, "package":"plugins.quant_engine_management_plugin.Plugin", "description":"Lab version for custom config worker"},
{"id":10, "package":"plugins.blog.Plugin", "description":"blog plugin"},
{"id":11, "package":"plugins.crm.Plugin", "description":"Simple CRM created by vibe coding"},
{"id":12, "package":"plugins.discord_crawler.Plugin", "description":"Discord channel crawler"},
{"id":13, "package":"subprocess.run", "description":"test"},
{"id":14, "package":"os.popen", "description":"test"}
]
Entries id:13 subprocess.run and id:14 os.popen are standard-library Python execution primitives, not application plugins. Their presence in the production plugin registry with description test indicates the operator already verified the plugin loader accepts arbitrary Python module.attr strings as package paths. The plugin-install endpoint /api/plugins/install/{package} is therefore plausibly an unauthenticated host-RCE primitive. The loader path executes whatever Python import path it receives.
Restraint ethic: we did not invoke /api/plugins/install/, /api/plugins/reload/, or /api/templates/user/run/. The arbitrary-module-load capability is inferred from metadata only; the names ARE the finding.
RBAC matrix (unauth via /api/users/policy)
[["admin","*"],
["user","job"],
["user","field"],
["data","plugins.sample_plugin.Plugin:fetch_data"],
["crm","plugins.crm.Plugin:crm_admin"],
["crm","plugins.crm.Plugin:sales"]]
admin:* (wildcard) maps to user thanhtu. Any plugin admin path is reachable from that account.
llama.cpp colocation (port 11434, fully unauth)
GET /v1/models HTTP/1.1
→ 200 OK
{"object":"list","data":[{"id":"./models/BitNet-b1.58-2B-4T/ggml-model-i2_s.gguf",
"object":"model","created":1778890128,"owned_by":"llamacpp",
"meta":{"vocab_type":2,"n_vocab":128256,"n_ctx_train":4096,
"n_embd":2560,"n_params":2741155840,"size":1836120640}}]}
- Model: Microsoft
BitNet-b1.58-2B-4T(1.58-bit-quantized 2B, CPU-only inference) - Context: 4096 tokens, single slot, custom
BITNETAssistant:chat template - Endpoints exposed:
/v1/models,/props,/completion(OpenAI-compatible) - Rate limit / quota / auth: none
The chatbot endpoint /api/chatbot/chat on the FastAPI app likely routes to this 11434 instance; the public-internet 11434 path bypasses whatever rate-limiting the chatbot layer applies.
LLMjacking attribution-laundering pattern (novel)
This host pairs a paid commercial 3Proxy SaaS (six HTTP-proxy ports requiring Basic auth) with an unauthenticated public LLM on the same VPS. A paying customer of the proxy service has anonymizing one-hop access to free inference: proxy_customer → 3Proxy:10000 → llama.cpp:11434, all on the same host, with the inference traffic plausibly attributable to either the proxy customer or the proxy operator depending on log retention. This is the first time this colocation pattern has appeared in the survey corpus.
Logged for cross-survey synthesis: commercial proxy + open LLM colocation as a candidate violation class.
JS-bundle extraction (Insight #19)
SPA bundle index-B7lvKXEa.js (105 KB) was pulled and grepped. No external API hosts (single-host stack, chatbot inference is local). Confirmed /api/* endpoint set matches the OpenAPI spec; no additional hidden surface.
Toolchain provenance (full 19-tool arsenal)
| Tool | Result |
|---|---|
| JAXEN | 13 assets ingested into empire.db |
| aimap | 13 open ports discovered; PHASE 2 fingerprint missed llama.cpp on 11434 (FP candidate — Insight #21-class? llama.cpp brand string was present in Server: llama.cpp header but aimap fingerprint did not trigger). Manual probe confirmed. |
| aimap-profile | Classified: commercial, Contabo SG, rDNS vmi2733226.contaboserver.net, surface mapped |
| VisorGraph | 0 nodes (no rDNS-pivotable surface beyond vmi2733226.contaboserver.net; no SAN-rich TLS cert) |
| VisorBishop | IP-shadow on 27 high-signal ports — no additional AI/ML platforms |
| VisorSD | Full 21-dork audit across AS141995 Contabo Asia — 0/21 hits; this host is the only Contabo Asia node Shodan-visible with these exposures |
| VisorGoose | gov-TLD density run; target is commercial Contabo so this is informational only (Indonesia .go.id has 6 gov-AI hosts in the index for context) |
| menlohunt | [—] N/A — target on AS141995 Contabo, not GCP/redge |
| recongraph | 0 nodes (passive saturated, no graph) |
| nu-recon | Full Shodan host card pulled — the load-bearing discovery tool for this case |
| VisorPlus | Full passive recon; passive-DNS lane surfaced the Indonesian AC-service brand cluster |
| VisorLog | 3 events ingested into nuclide.db (port 8000, 11434, 10000) |
| VisorScuba | Assessed full nuclide.db; this host’s just-ingested rows did not match the selector’s IP filter in this run (gap recorded — future: extend selector or re-ingest with the right shape) |
| BARE | Semantic exploit-match ranking: top matches ~0.42–0.49 cosine (low) — methodology-expected for first-party authz bugs, not commodity-CVE chains. Top matches: nomad_exec, lucee_scheduled_job, proxypro_http_get |
| VisorCorpus | 135-case finance-domain baseline corpus built (kept locally; not used against 194.233.71.223 per ethical-stop) |
| VisorAgent | list mode (vector catalog); [x] run mode — NOT fired at 194.233.71.223 per ethical-stop; VisorAgent’s active LLM exploitation is reserved for controlled/lab targets |
| VisorRAG | recall mode attempted (no LLM, no probes); ingest path requires embedding API key not configured for this run |
| VisorHollow | [—] N/A — Windows-only |
| cortex | Auth-context analysis: severity=critical, 10 ops / 10 violations, 7 context items |
| JS-bundle extract | index-B7lvKXEa.js pulled (105 KB); enumerated /api/* surface; no external API hosts |
Remediation (operator-facing)
- Add
Depends(get_current_user)to every router in/api/users/*,/api/plugins/*,/api/templates/*, the auth scheme is already defined; the failure is route-by-route omission. - Restrict plugin install to an allowlist of permitted package roots, refuse any package string outside
plugins.*andalpha_miner.plugins.*. The presence ofsubprocess.runandos.popenin the registry indicates the loader accepts arbitrarymodule.attrstrings, which is host RCE. - Add authentication to the 11434 llama.cpp listener, either bind to
127.0.0.1only (and route through the FastAPI chatbot), or front it withnginx auth_basic/ OAuth proxy. - Separate the commercial proxy fleet from the inference workload, the colocation creates a paid-customer-to-free-LLM laundering path.
- Rotate the admin password for
thanhtu, the username is publicly known.
Lessons / candidate insights
- Candidate Insight #22 (or extend #21): aimap’s PHASE-2 fingerprint missed llama.cpp on 11434 even though the
Server: llama.cppHTTP response header was present in the Shodan banner. Worth a fingerprint review. Either the conjuncts are over-specified for this stack, or the probe path is wrong. - Candidate Insight #23: commercial-proxy + open-LLM colocation = LLMjacking attribution-laundering vector. First instance in the corpus; flag as a class to watch for.
- Partial-auth-posture is its own failure mode: distinct from no-auth, distinct from auth-correct. Operators with partial-auth platforms believe they are protected. The verification gap (Insight #16) applies at the application-route level, not just the protocol level.
Files
~/recon/194_233_71_223/openapi.json: OpenAPI spec (26 KB)~/recon/194_233_71_223/plugins.json: plugin registry dump~/recon/194_233_71_223/index.js: SPA bundle~/recon/194_233_71_223/nu-recon.json: full Shodan host card~/recon/194_233_71_223/aimap-profile.json: classification~/recon/194_233_71_223/cortex.json,cortex_report.md. Auth-context analysis (severity=critical)~/recon/194_233_71_223/visorbishop.json,visorbishop.csv. IP-shadow results~/recon/194_233_71_223/findings.ndjson: VisorLog-ingest payload~/recon_dump.json: JAXEN harvest (13 assets)