zoukankan      html  css  js  c++  java
  • CTF PHP反序列化

    序列化

    所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。 --php官方文档

    魔术方法

    php官方文档中关于魔术方法的部分

    构造函数和析构函数

    • __construct()
      具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
    • __destruct()
      析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

    也就是说进行反序列化时,完成的就是从字符串创建新对象的过程,刚开始就会调用__construct(),而对象被销毁时,例如程序退出时,就会调用__destruct()

    __sleep()__wakeup()

    __toString()


    echo或者拼接字符串或者其他隐式调用该方法的操作都会触发

    __set(), __get(), __isset(), __unset()

    __invoke(), __call()

    当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
    在对象中调用一个不可访问方法时,__call() 会被调用。

    其他

    __callStatic(), __set_state(), __clone(), __debugInfo()等和序列化没有多大关系,详情参考官网

    序列化细节

    序列的含义

    例如:O:4:"user":2:{s:3:"age";i:18;s:4:"name";s:3:"LEO";}
    O代表对象;4代表对象名长度;2代表2个成员变量;其余参照如下

    类型 结构
    String s:size:value;
    Integer i:value;
    Boolean b:value;(保存1或0)
    Null N;
    Array a:size:{key definition;value definition;(repeated per element)}
    Object O:strlen(object name):object name:object size:{s:strlen(property name):property name:property definition;(repeated per property)}

    public、protected、private下序列化对象的区别

    php v7.x反序列化的时候对访问类别不敏感

    • public变量
      直接变量名反序列化出来
    • protected变量
      x00 + * + x00 + 变量名
      可以用S:5:"0*0op"来代替s:5:"?*?op"
    • private变量
      x00 + 类名 + x00 + 变量名

    反序列化中s和S的区别

    如果类型是S,会调用以下函数,简单来说就是将解释成十六进制,来转成字符

    static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t maxlen)
    {
    	size_t i, j;
    	zend_string *str = zend_string_safe_alloc(1, len, 0, 0);
    	unsigned char *end = *(unsigned char **)p+maxlen;
    
    	if (end < *p) {
    		zend_string_efree(str);
    		return NULL;
    	}
    
    	for (i = 0; i < len; i++) {
    		if (*p >= end) {
    			zend_string_efree(str);
    			return NULL;
    		}
    		if (**p != '\') {
    			ZSTR_VAL(str)[i] = (char)**p;
    		} else {
    			unsigned char ch = 0;
    
    			for (j = 0; j < 2; j++) {
    				(*p)++;
    				if (**p >= '0' && **p <= '9') {
    					ch = (ch << 4) + (**p -'0');
    				} else if (**p >= 'a' && **p <= 'f') {
    					ch = (ch << 4) + (**p -'a'+10);
    				} else if (**p >= 'A' && **p <= 'F') {
    					ch = (ch << 4) + (**p -'A'+10);
    				} else {
    					zend_string_efree(str);
    					return NULL;
    				}
    			}
    			ZSTR_VAL(str)[i] = (char)ch;
    		}
    		(*p)++;
    	}
    	ZSTR_VAL(str)[i] = 0;
    	ZSTR_LEN(str) = i;
    	return str;
    }
    

    反序列化的利用

    • __wakeup失效
      php版本< 5.6.25 | < 7.0.10
      当序列化字符串中,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup()的执行
      例:

      O:4:"Demo":1:{s:10:"Demofile";s:16:"f15g_1s_here.php";}
      O:4:"Demo":2:{s:10:"Demofile";s:16:"f15g_1s_here.php";}
      

      __wakeup()函数失效引发漏洞(CVE-2016-7124)

    • 使用+绕过正则
      例:

      preg_match('/[oc]:d+:/i', $var)
      O:4:"Demo":1:{s:10:"Demofile";s:16:"f15g_1s_here.php";}
      O:+4:"Demo":1:{s:10:"Demofile";s:16:"f15g_1s_here.php";}
      

      代码审计Day11 - unserialize反序列化漏洞

    Session序列化问题

    https://bugs.php.net/bug.php?id=71101

    PHP内置了多种处理器用于存储$_SESSION数据时会对数据进行序列化和反序列化,常用的有以下三种,对应三种不同的处理格式:

    处理器 对应的存储格式
    php 键名 + 竖线 + 经过serialize()函数反序列化处理的值
    php_binary 键名的长度对应的ASCII字符 + 键名 + 经过serialize()函数反序列化处理的值
    php_serialize(php>=5.5.4) 经过serialize()函数反序列处理的数组

    配置选项 session.serialize_handler,通过该选项可以设置序列化及反序列化时使用的处理器,
    ini_set('session.serialize_handler', 'php_binary')
    默认为php。

    如果PHP在反序列化存储的$_SEESION数据时的使用的处理器和序列化时使用的处理器不同,会导致数据无法正确反序列化,通过特殊的伪造,甚至可以伪造任意数据。

    当存储是php_serialize处理,然后调用时php去处理,如果这时注入的数据时a=|O:4:"test":0:{},那么session中的内容是a:1:{s:1:"a";s:16:"|O:4:"test":0:{}";},那么a:1:{s:1:"a";s:16:"会被php解析成键名,后面就是一个test对象的注入

    当配置选项session.auto_start=Off,两个脚本注册session绘画是使用的序列化处理器不同,就会出现安全问题

    <form action="upload.php" method="POST" enctype="multipart/form-data">
    	<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="ryat" />
    	<input type="file" name="file" />
    	<input type="submit" />
    </form>
    

    The key of stored in the session will look like this:

    $_SESSION["upload_progress_ryat"]
    

    ryat的部分可以注入自己的代码,如ryat|序列化字符串

    Phar反序列化

    https://blog.ripstech.com/2018/new-php-exploitation-technique/
    PHAR反序列化拓展操作总结
    SUCTF 2019 出题笔记 & phar 反序列化的一些拓展
    利用 phar 拓展 php 反序列化漏洞攻击面
    创建一个Phar文件

    // create new Phar
    $phar = new Phar('test.phar');
    $phar->startBuffering();
    $phar->addFromString('test.txt', 'text');
    $phar->setStub('<?php __HALT_COMPILER(); ? >');
    
    // add object of any class as meta data
    class AnyClass {}
    $object = new AnyClass;
    $object->data = 'rips';
    $phar->setMetadata($object);
    $phar->stopBuffering();
    

    如果现在通过phar://包装器对我们现有的Phar文件执行文件操作,则其序列化元数据将被反序列化。这意味着我们在元数据中注入的对象被加载到应用程序的范围中。如果此应用程序具有已命名的类AnyClass并且具有魔术方法__destruct()或已__wakeup()定义,则会自动调用这些方法。这意味着我们可以在代码库中触发任何析构函数或唤醒方法。更糟糕的是,如果这些方法对我们注入的数据进行操作,那么这可能会导致进一步的漏洞。

    //通过Phar文件进行PHP对象注入
    class AnyClass {
    	function __destruct() {
    		echo $this->data;
    	}
    }
    // output: rips
    include('phar://test.phar');
    

    phar://在任何文件操作中都会为包装器触发unserialize,可利用函数包括

    ìnclude($_GET['file']);
    fopen($_GET['file']);
    file_get_contents($_GET['file']);
    file($_GET['file']);
    file_exists($_GET['file']);
    md5_file($_GET['file']);
    filemtime($_GET['file']);
    filesize($_GET['file']);
    

    当对上传文件内容有过滤时,可以用gzip压缩来消除敏感字符

    例题:[hitcon2017] Baby^H-master-php-2017 复现
    PS:匿名函数的真正名字为:%00lambda_%d(%d格式化为当前进程的第n个匿名函数)

  • 相关阅读:
    wzplayer2 支持mac 了,最新谍报
    关于duilib的理解
    DMS的实现转载
    视频通话最新谍报
    新人补钙系列教程之:Function类的重要方法apply()
    新人补钙系列教程之:webgame好友模块原型开发一
    新人补钙系列教程之: 大型 webGame 开发系列之 pipes
    新人补钙系列教程之:模拟java多线程Thread类
    flash学习网站
    新人补钙系列教程之:AS3与服务器通信
  • 原文地址:https://www.cnblogs.com/20175211lyz/p/11403397.html
Copyright © 2011-2022 走看看