php反序列化漏洞

发布于 2024-07-30  129 次阅读


php反序列化

基础反序列化示例

常用魔术方程

  • __construct($a) 当一个对象被创建时自动调用这个方法

    • ($a=传入值取决于类定义时构造函数的参数)
  • __destruct() 当一个对象被销毁时自动调用这个方法

  • __call($a,$b) 在对象中调用一个不存在的方法时自动调用这个方法,可以用来实现动态方法调用

    • ($a=字符串,被调用的方法名称,$b=数组,传递给方法的参数)
  • __callStatic($a,$b) 在静态上下文中调用一个不存在的方法时自动调用这个方法,本身也为静态,不用new

    • ($a=字符串,被调用的静态方法名称,$b=数组,传递给方法的参数)
  • __get($a) 当一个对象的(不存在,不可达)属性被读取时自动调用这个方法,可以用来实现属性的访问控制

    • ($a=字符串,被访问的属性名称)
  • __set($a,$b) 当一个对象的(不存在,不可达)属性被设置时自动调用这个方法,可以用来实现属性的访问控制

    • ($a字符串,被设置的属性名称,$b=属性的值)
  • __isset($a) 当使用 isset()empty() 测试一个对象的属性时自动调用这个方法,可以用来实现属性的访问控制

    • ($a=字符串,检测是否设置的属性名称)
  • __unset($a) 当使用 unset() 删除一个对象的属性时自动调用这个方法,可以用来实现属性的访问控制

    • ($a=字符串,被 unset() 的属性名称)
  • __toString() 当一个对象直接被打印时(被作为字符串)自动调用这个方法,可以用来实现对象的字符串表示。

  • __invoke($a) 当一个对象被作为函数调用时自动调用这个方法,可以用来实现对象的可调用性

    • ($a=取决于调用时传递的参数)
  • __set_state($a) 当使用 var_export() 导出一个对象时自动调用这个方法,可以用来实现对象的序列化和反序列化

    • ($a=数组,包含对象所有的公共、保护和私有属性)
  • __clone() 当一个对象被克隆时自动调用这个方法$obj2 = clone $obj;

  • __debugInfo() 当使用 var_dump()print_r() 输出一个对象时自动调用这个方法,可以用来控制对象的调试信息输出。

  • __sleep() 在对象被序列化之前自动调用这个方法,可以用来控制哪些属性被序列化。

  • __wakeup() 在对象被反序列化之后自动调用这个方法,可以用来重新初始化对象的属性。

基础php函数

  • unserialize()反序列化
  • serialize()序列化
  • urlencode()来编码输出,防止识别问题
  • str_replace(替换内容,替换后内容,替换对象)然后直接赋值给一个变量
  • session_start()开启了一个会话,会将赋给$_SESSION['键名']的值以键名存在本地
  • include() 函数用于包含并运行指定文件
  • require() 函数也是用来包含文件。如果要包含的文件不存在,require() 会发出一个致命错误
  • include_once() 函数在整个脚本的执行过程中只包含并运行一次指定文件
  • PHP 的文件操作和检查函数
    • fileatime() 返回文件最后被访问的时间。
    • filectime() 获取文件的 inode 修改时间。
    • file_exists() 检查文件或目录是否存在。
    • file_get_contents() 将整个文件读入一个字符串。
    • file_put_contents() 将一个字符串写入文件,如果文件不存在尝试创建之。
    • file() 把整个文件读入一个数组中。
    • filegroup() 获取文件的组 ID。
    • fileowner() 获取文件的所有者 ID。
    • fileperms() 获取文件的权限。
    • fileinode() 获取文件的 inode 号。
    • filemtime() 获取文件的最后修改时间。
    • fopen() 打开一个文件或 URL。
    • is_dir() 判断给定文件名是否是一个目录。
    • is_executable() 判断给定文件名是否可执行。
    • is_file() 判断给定文件名是否为一个常规的文件。
    • is_link() 判断给定文件名是否是一个符号链接。
    • is_readable() 判断给定文件名是否可读。
    • is_writable(), is_writeable() 判断给定文件名是否可写。
    • parse_ini_file() 解析一个配置文件(ini文件),并返回一个数组。
    • readfile() 读取一个文件并写入到输出缓冲。
    • copy() 拷贝文件。
    • unlink() 删除文件。
    • stat() 给出文件的信息。

更多参见文档

知识点

  • 引用:
    指向在变量前加一个&(类似快捷方式),反序列化后标为r
  • 反序列化漏洞CVE-2016-7124(PHP5 < 5.6.25,PHP7 < 7.0.10 ):
    序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行
  • session_start()将赋给$_SESSION['键名']的值以键名存在本地,同时是以序列化方式储存的
    • 默认以php的形式序列化即键名|内容的序列化
    • ini_set('session.serialize_handler','php_serialize')设置iniserialize的形式储存
    • 表现为序列化后的数组
    • ini_set('session.serialize_handler','php_binary')设置inibinary的形式储存
    • 表现为二进制image-20240730152918814
  • phar文件
    • 文件头为xxx<?php xxx; __HALT_COMPiLER();?>;
    • 文件属性为序列化后的内容
    • 构造phar文件
<?php
class TTT{
    var $test='';
}
@unlink('test.phar');//删除原有的test.phar
$phar=new Phar('test.phar');//创建一个phar对象
$phar->startBuffering();//开始写入文件
$phar->setStub('<?php __HALT_COMPILER();  ?>');//写入文件头
$o=new TTT();
$o->test='eval($_GET["a"]);';
$phar->setMetadata($o);//写入内容
$phar->addFromString("test.txt","test");//添加压缩文件
$phar->stopBuffering();
?>
  • phar漏洞原理
    • 调用phar伪协议phar://XXX.phar读取phar
    • 解析文件时会自动触发属性字段来反序列化
    • phar需要PHP>=5.2并在iniphar.readonly值为Off
    • image-20240730162312199
  • 构造完成后可以使用urlencode()来编码输出,防止识别问题
  • 构造代码写入文件时可以使用 file_put_contents("文件路径","文件内容") 写入文件
  • PHP 回调函数格式PHP 中,如果有一个数组 array($object, 'methodName'),并将其作为函数调用,PHP 会尝试在指定的对象 $object 上调用 'methodName' 方法:例如return ($this->b) ();时使用

基础漏洞思路

  • 分析魔术方法,构造对应的反序列化字符,来pop链构造

    • 构造pop链的时候从入口开始反推,同时类可以把方法都先删了避免影响判断
  • 一些简单的替换waf,可以利用替换字符str_replace的差异达成字符串逃逸,增加或减少达成某个属性的内容变化

  • 利用反序列化漏洞

  • 利用错误解释session

  • phar漏洞条件

    • phar文件可以上传
    • 有可用的反序列化魔术方法
    • 有文件操作函数
    • 文件操作函数的参数可控且无过滤
  • flag除了可能在文件中,也可能在phpinfo();

    • 文件中可以用system("find / -type f -name 'f*'");
    • system('grep -ri "flag" /')
QQ:2219349024
最后更新于 2024-09-04