源码:
<?php
//题目环境:php:7.4.8-apache
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
}else if ($pid){
$r=pcntl_wait($status);
if(!pcntl_wifexited($status)){
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]);
}
posix_kill(posix_getpid(), SIGUSR1);
}
这里首先使用了pantl_fork()
,用于创建子进程。成功时,在父进程执行线程内返回产生的子进程的PID$pid
,在子进程执行线程内返回0。失败时,在父进程上下文返回-1,不会创建子进程,并且会引发一个PHP错误。进程从 pcntl_fock 开始,会产生分叉,主进程和子进程都会执行 pcntl_fock 所有的代码,可以通过 $pid 进行条件判断代码是执行在哪个进程;
而主进程使用了pcntl_wait()
,这个主要用于阻塞父进程,为了等待子进程中断并返回,防止子进程成为僵尸进程。
pcntl_wait的定义:
wait函数挂起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。 如果一个子进程在调用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将 被释放。调用pcntl_wait()时,如果没有子进程,将返回-1。
也就是说代码执行顺序是先从子进程开始执行。处理完子进程再执行父进程。
子进程代码中使用了call_user_func_array
,这里函数中被强行添加了几个参数值,这里好像没找到什么办法进行利用。
又看到父进程代码段中的判断条件if(!pcntl_wifexited($status)
是判断子进程是不是正常结束,如果不是就返回phpinfo(),而我们可以通过构造代码使子进程错误退出,从而执行父进程时,条件成立,返回phpinfo的内容。
所以我们需要构造使子进程不正常退出,通过测试,随便找个回调函数回调下即可,参数随意;
?a=call_user_func&b=pcntl_wait()
搜flag: