HacKerQWQ的博客空间

强网杯拟态easy filter解析

Word count: 585Reading time: 3 min
2021/11/05 Share

Easy Filter

看了P神的easy filter的分析,简单做一下记录

题目如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
ini_set("open_basedir","./");
if(!isset($_GET['action'])){
highlight_file(__FILE__);
die();
}
if($_GET['action'] == 'w'){
@mkdir('./files/');
$content = $_GET['c'];
$file = bin2hex(random_bytes(5));
file_put_contents("./files/".$file,base64_encode($content));
echo "./files/".$file;
}elseif($_GET['action'] == 'r'){
$r = $_GET['r'];
$file = "./files/.$r";
include("php://filter/resource=$file");
}

按照上一篇文章的远程调试c代码的方法配置好后,在ext/standard/php_fopen_wrapper.cphp_stream_url_wrap_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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const char
*path, const char *mode, int options,
zend_string **opened_path,
php_stream_context *context STREAMS_DC) /* {{{ */
{
int fd = -1;
int mode_rw = 0;
php_stream * stream = NULL;
char *p, *token = NULL, *pathdup;
zend_long max_memory;
FILE *file = NULL;
#ifdef PHP_WIN32
int pipe_requested = 0;
#endif
if (!strncasecmp(path, "php://", 6)) {
path += 6;
}
if (!strncasecmp(path, "temp", 4)) {...}
if (!strcasecmp(path, "memory")) {...}
if (!strcasecmp(path, "output")) {...}
if (!strcasecmp(path, "input")) {...}
if (!strcasecmp(path, "stdin")) {
...
} else if (!strcasecmp(path, "stdout")) {
...
} else if (!strcasecmp(path, "stderr")) {
...
} else if (!strncasecmp(path, "fd/", 3)) {
...
} else if (!strncasecmp(path, "filter/", 7)) {
/* Save time/memory when chain isn't specified */
if (strchr(mode, 'r') || strchr(mode, '+')) {
mode_rw |= PHP_STREAM_FILTER_READ;
}
if (strchr(mode, 'w') || strchr(mode, '+') || strchr(mode, 'a')) {
mode_rw |= PHP_STREAM_FILTER_WRITE;
}
pathdup = estrndup(path + 6, strlen(path + 6));
p = strstr(pathdup, "/resource=");
if (!p) {
zend_throw_error(NULL, "No URL resource specified");
efree(pathdup);
return NULL;
}
if (!(stream = php_stream_open_wrapper(p + 10, mode, options,
opened_path))) {
efree(pathdup);
return NULL;
}
*p = '\0';
p = php_strtok_r(pathdup + 1, "/", &token);
while (p) {
if (!strncasecmp(p, "read=", 5)) {
php_stream_apply_filter_list(stream, p + 5, 1, 0);
} else if (!strncasecmp(p, "write=", 6)) {
php_stream_apply_filter_list(stream, p + 6, 0, 1);
} else {
php_stream_apply_filter_list(stream, p, mode_rw &
PHP_STREAM_FILTER_READ, mode_rw & PHP_STREAM_FILTER_WRITE);
}
p = php_strtok_r(NULL, "/", &token);
}
efree(pathdup);
if (EG(exception)) {
php_stream_close(stream);
return NULL;
}
return stream;
} else {
return NULL;
}
...
}

由于使用了php://协议,strcasecmp(返回出现搜索字符串的首次出现位置)初步判断后进入了filter分支,首先判断是否有resource=,如果没有则return NULL

然后通过php_stream_open_wrapper函数递归解析resource=后的内容,如果解析出错则返回NULL

最后用php_strtok_r(pathdup + 1, "/", &token);/号分割resouce=后的字符串查找里面的read=write=作为过滤器对读取文件进行解析

payload:

1
?action=r&r=./read=convert.base64-decode/../files/540d629f15
CATALOG
  1. 1. Easy Filter