PHP危险函数

一、变量覆盖问题

1.extract函数引发的覆盖问题

举一个ctf的题目用于记忆,如下:

$ceshi='1111';
$b='flag';
extract($_GET); //危险函数 payload:ceshi=1&b=1
if(isset($ceshi))
{
    $content=$b;
    echo $ceshi.$content;
    if($ceshi==$content) 
    {
        echo'flag{xxxxxxx}';
    }
    else
    {
        echo'Oh.no';
    }
}

通过GET请求任意给ceshi和b这两个参数赋值获取flag。

2.parse_str()函数

拿ctf题举例

@parse_str("admin=admin&password=123456&user=yxy");
echo  "权限"."$admin; 密码 $password; 用户$user;"; //测试输出

error_reporting(0);
$flag = "flag{yx001-2022}";
if (empty($_GET['id'])) {
    show_source(__FILE__);
    die();
} else {
    $a = "www.yx001.com";
    $id = $_GET['id'];
    @parse_str($id);  //修改输入值覆盖a[0]的值
    if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
        echo $flag;
    } else {
        exit("no no");
    }
}

3.$$动态变量覆盖

指把之前定义的变量通过赋值进行修改,发生覆盖行为。

$flag="No No No";
$value="flag";
$$value="flag"; //此处的情况是 $value=flag,$$value=$fla,$flag="flag"
echo $flag;  //输出的结果是flag 初始的"No No NO"被覆盖

4.ini_get函数

🎈这个函数用来获取php.ini内部的配置信息,根据传入的string获取value,只了解了并未深入。

二、代码执行函数

1.eval函数

$a=$_GET['id']; //接收一个用户参数
eval($a); 

🎈不多阐述这个函数,可以验证一个phpinfo或者一句话mm “eval($_POST[‘xxxxxxx’]);”
当然这个函数有几个需要注意的点,和一些简单的绕过方式。
错误的尝试:

$z="ev";
$k="al";
$c=$z.$k;
$c(phpinfo());//会报错,不能这样拼接eval函数进行代码执行

特殊的利用方法:
1.如下两总eval的函数执行方式,却有不同的结果

$data=$_GET['id'];
eval("\$ret=$data;"); //可行,把$data的值赋给ret,此时get一个id参数为phpinfo()成功打印信息
eval($data);//直接报错,这里$data传入的参数会被当成字符串。
$a = "\$ret=$data;";
eval($a);//可以执行

2.闭合字符绕过执行函数

$data=$_GET['id'];
eval("\$ret =strtolower('$data');"); //strtolower字符串大写转小写
//此时输入phpinfo() ret接收的仍然是一个字符串,不会被定义为函数。

上述存在绕过方式:首先需要闭合单引号和括号这里直接 ‘)此时strtolower(’$data’);等于strtolower(‘’)xxxxx’);最后加上分号和注释末尾的所有符号,最终的payload为[ ');phpinfo();//]
得到结果:
在这里插入图片描述
3.关于闭合绕过的其他姿势

<?php
$d=$_GET['id'];
$data="array('a' => 'b',
'c'=>'$d'
)";
$str="\$arr=$data";
eval($str);

如上这个代码情况payload应该写成[ ');phpinfo();/* ] 双斜杠此时无法注释后面的内容,使用 /* 可执行eval。

2.assert函数

🎈:这个函数和eval函数非常相似,在低版本的php中assert有这样一个用法:

$a = "ass";
$b = "ert";
$c=$a.$b; //允许拼接
$data =$_GET['id'];
$d =
    "\$ret=$data;";
$c($d);

在eval中却无法这样使用,在php7高版本中这个拼接的方式就失效了。

3.preg_replace函数

函数的初始使用功能:

$a = $_GET['id'];
echo preg_replace('/demo/',$a,'demo'); //查找demo并且替换为当前$a变量的值。

在php低版本中,此函数可以进行一个任意函数执行,选择的测试版本为5.5.9在这个版本下的php都存在这样的危险行为。
代码阐述:

	$a = $_GET['id'];
	@preg_replace('/fl../ie',$a,"flxxxxx"); // (/fl../)匹配带有fl开头的任意两个字符除(/n)

给id传一个phpinfo();
执行结果:
在这里插入图片描述
在php7中不存在这个执行。

正则匹配时的执行操作:

$a = $_GET['id'];
$str = "<data>$a</data>";
echo @preg_replace('/<data>(.*)<\/data>/ie','\\1',$str); //(\\1)表示取标签中的值

和上面一个执行一致,原理不变。

4.create_function函数

这里用代码解释该函数是如何产生危险的。

function Demo($a,$b){
    echo $a;
    return 1 * strnatcasecmp();}phpinfo();/*,$b); 
}

如上的代码,是可以直接执行phpinfo的,不会有人这样直接写一个phpinfo给你执行的,一般的代码都是这样的:

function Demo($a,$b){
    echo $a;
    return 1 * strnatcasecmp($a,$b); //);}phpinfo();/*
}
$a=$_GET['id'];
Demo($a,2);
die();

如果在id中给一个参数:**);}phpinfo();/***就变成了如上可以执行shell的形式了,但是在常规的函数体中,用户传进来的参数只是字符串罢了,内部是不给予执行的,所以create_function给了我们写shell的机会。
看一串代码:

$a=$_GET['id'];
$str='strnatcasecmp';
$function= 'return 1 * '.$str.'("'.$a.'",$b);';
$func=create_function('$a,$b',$function);
$func($a,1);

这串代码和初始的方法其实效果是一样的,在create_function中id给一个:**");}phpinfo();/***能直接执行shell,思路相同闭合双引号回到初始第一次的执行情况,执行用户输入的非法请求中带有的特殊函数。
注:该方法在php7.2版本之后弃用了。

5.一些类似的危险函数

array_map函数

该函数简单的讲就是遍历一次数组,并且把数组中的每一个值都传入需要调用的方法中,函数格式array_map(function,array)。
函数执行的代码:

function demo($a){
    return $a*2;
}
$b = array(1,2,3,4,5);
print_r(array_map("demo",$b)); //循环调用demo方法把数组的值按顺序传入,接收一个数组类型的返回值
die();

输出结果:Array ( [0] => 2 [1] => 4 [2] => 6 [3] => 8 [4] => 10 )
当函数调用名和数组可控时,会产生高度的危险行为如:

$func = $_GET['input'];
$cmd = $_GET['cmd'];
$array[0]=$cmd;
array_map($func,$array);

在这里插入图片描述
会导致直接执行shell。

call_user_func函数

利用原理简单,直接写执行代码:

$a=$_GET['id'];
$b=$_GET['cmd'];
call_user_func($a,$b);

在这里插入图片描述
注:id给eval函数不可执行

array_filter函数

一样的利用方式

$func = $_GET['input'];
$cmd = $_GET['cmd'];
$array[0]=$cmd;
array_filter($array,$func); //前一个为数组值,后一个为方法名

利用方法一致。

三、命令执行函数

1.有回显

system

passthru

命令执行与函数执行有所不同,在于命令执行执行的是计算机中的指令。

system("ping 127.0.0.1");
passthru("ping 127.0.0.1");

都是直接执行计算机指令在浏览器页面打印出回显信息。
在命令执行中需要注意逻辑符号的使用:
如:| ,||,&,&&

system("ping -n 1 127.0.0.1 || whoami"); 
system("ping -n 1 127.0.0.1 | whoami");

||,只执行print,whoami不会被执行(前一个条件不满足时才执行后面的)。
|,会同时执行两个命令(无论第一个条件是否满足都会执行下一个)。
&,与 | 相同。
&&,必须同时满足。

2.无回显

无回显则必须输出。

exec

shell_exec

两个函数都是无回显类型。
需要添加echo进行一个输出。
exec函数 只能回显最后一行,这里只能看见whoami的结果。

echo  exec("ping -n 1 127.0.0.1 && whoami");

shell_exec则不同可以查看全部结果。

echo shell_exec("ping -n 1 127.0.0.1 && whoami");

反引号

反引号的效果也是一样的,只能回显最后一行。

$a="ping -n 1 127.0.0.1 | whoami";
echo `$a`; //直接执行

popen

popen必须传两个参数commed(要执行的命令),mode(规定连接模式 r,w 读或写)。
popen的命令执行是比较特殊的,它不像system那样可以自动打印在页面上,也不像exec那样echo打印出函数的返回值,popen需要手动去取出它所操作成功的值。类似开辟缓冲区方便操作和转移执行结果。

$com =$_GET['com'];
$handle=popen($com,'r');
while(!feof($handle)) {
    echo fread($handle, 1024);//打印全部 这里可以进行读写的操作
} 

proc_open

大部分的固定格式比较好识别,注意参数意思。

$com = $_GET['com'];
$des = array(
 0 =>array(0=>"pipe",1=>"r"),
  1=> array("pipe","w"),
);
$des= array(
    $handle=proc_open($com,$des,$pipes));
if(is_resource($handle)){
    while (!feof($pipes[1])) { //取返回值数组的1号索引
        echo fread($pipes[1], 1024); //与popen一样的取参格式
    }
}

3.缓冲区执行

ob_start

ob_end_flush

这两个函数可以进行配合产生命令执行。

ob_start("system");
echo "whoami";
ob_end_flush();

echo 这个位置可以被system直接执行了,因为flush的刷新机制执行echo中的命令,也只能回显最后一行。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

让我在看看

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值