前言
关键词:[json|unicode]
<?php
error_reporting(0);
if (isset($_GET['source'])) {
show_source(__FILE__);
exit();
}
function is_valid($str) {
$banword = [
// no path traversal
'\.\.',
// no stream wrapper
'(php|file|glob|data|tp|zip|zlib|phar):',
// no data exfiltration
'flag'
];
$regexp = '/' . implode('|', $banword) . '/i';
if (preg_match($regexp, $str)) {
return false;
}
return true;
}
$body = file_get_contents('php://input');
$json = json_decode($body, true);
if (is_valid($body) && isset($json) && isset($json['page'])) {
$page = $json['page'];
$content = file_get_contents($page);
if (!$content || !is_valid($content)) {
$content = "<p>not found</p>\n";
}
} else {
$content = '<p>invalid request</p>';
}
// no data exfiltration!!!
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{<censored>}', $content);
echo json_encode(['content' => $content]);
题解
首先看php://input
,而非$_GET
,$_POST
,$_REQUEST
,所以不会进行urldecode,发什么包就是什么包。
源码的最后有个将flag给替换的操作,明显是不能明文读取,需要加密一下。
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{<censored>}', $content);
注意看代码,是is_valid($body)
,也就是说检测的是json字符串,而file_get_contents()
读取的是json解码后的字符。
可见问题就出在这,思路就是$body
检测原字符通过,json解码字符读取flag。
所以搜搜看json_decode()
可得
json_decode()
会自动解析unicode字符
所以可以用unicode编码绕过正则
payload:
{ "page" : "\u0070\u0068\u0070://filter/convert.base64-encode/resource=/\u0066\u006c\u0061\u0067"}
注意,需用burpsuite或其他办法发包,浏览器不可