欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

由浅入深剖析序列化攻击(一)

来源:本站整理 作者:佚名 时间:2019-05-06 TAG: 我要投稿

近期因为内部培训有序列化的需求,于是趁此机会由浅入深的剖析一下序列化相关内容。
之前也写过由浅入深的xml漏洞系列,欢迎阅读:
https://skysec.top/2018/08/17/浅析xml及其安全问题/
https://skysec.top/2018/08/18/浅析xml之xinclude-xslt/
序列化的概念
简单概括来说,序列化即保存对象在内存中的状态,也可以说是实例化变量。在传递一个对象的时候,或是需要把对象保存在文件/数据库中时,就必须用序列化。
序列化样例
以php官方手册样例为例:
var;
    }
}
?>
这样一来我们写了一个简单的类样例,类中包含一个属性和一个方法。
我们可以通过如下方式对类的属性进行赋值,对类的方法进行调用:
$sky = new SimpleClass();
$sky->var = 'sky is cool!';
$sky->displayVar();
我们观察一下序列化后字符串的格式:
$sky = serialize($sky);
var_dump($sky);
得到如下内容:
O:11:"SimpleClass":1:{s:3:"var";s:12:"sky is cool!";}
O代表存储的是对象(object),11表示对象的名称有11个字符,"SimpleClass"表示对象的名称,1表示有一个值。
大括号内s表示字符串,3表示该字符串的长度,"var"为字符串的名称,紧跟着是该字符串的值,规则同理。
相同的,如果序列化数组,得到结果如下:
$sky1 = new SimpleClass();
$sky1->var = 'sky is cool!';
$sky2 = new SimpleClass();
$sky2->var = 'wq is cool!';
$sky3 = new SimpleClass();
$sky3->var = 'sy is cool!';
$sky4 = array($sky1,$sky2,$sky3);
var_dump(serialize($sky4));
得到如下内容:
a:3:{i:0;O:11:"SimpleClass":1:{s:3:"var";s:12:"sky is cool!";}i:1;O:11:"SimpleClass":1:{s:3:"var";s:11:"wq is cool!";}i:2;O:11:"SimpleClass":1:{s:3:"var";s:11:"sy is cool!";}}
与之前不同的,多了a和i,a表示数组,数字3表示数组中有3个元素,i:0表示第一个元素,i:1表示第二个元素,i:2表示第三个元素。其他规则与之前一致。
相应的,将这组字符串传递后,我们接受后,使用unserialize()进行反序列化,如下:
$sky1 = 'a:3:{i:0;O:11:"SimpleClass":1:{s:3:"var";s:12:"sky is cool!";}i:1;O:11:"SimpleClass":1:{s:3:"var";s:11:"wq is cool!";}i:2;O:11:"SimpleClass":1:{s:3:"var";s:11:"sy is cool!";}}';
$sky2 = 'O:11:"SimpleClass":1:{s:3:"var";s:12:"sky is cool!";}';
var_dump(unserialize($sky1));
var_dump(unserialize($sky2));

发现反序列化成功,我们已将之前存储的对象成功复原。
魔法方法漏洞
魔法方法样例
了解之前的原理后,我们首先看一个最简单的反序列化漏洞:

还是之前的代码,我们发现最后我们并没有进行方法调用,但成功触发了__toString()方法,这就是魔法方法的魅力。
魔法方法往往不需要用户调用,在特定条件下会自动触发,相关魔法方法在php官方手册中写的非常清楚了,就不再赘述:
https://www.php.net/manual/zh/language.oop5.magic.php
这里的__toString方法之所以触发成功,是因为我们将对象当做字符串输出,符合__toString方法的条件,所以成功触发了该方法。如果将echo换成var_dump则不会触发该方法。
魔法方法实战(一)
例如在Jarvis OJ上的一题:
http://web.jarvisoj.com:32768
index.php
readfile();
?>
shield.php
 file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE 
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
?>
我们可以看到是一个非常简单的类,其中定义了1个属性和2个方法,其中便有魔法方法__construct(),通过查阅官方手册我们知道:具有构造函数的类会在每次创建新对象时先调用此方法。所以刚方法在初始化的时候便会自动调用,那么这里要涉及一个先后顺序,是我们赋值先进行,还是__construct()先进行,这里做一个简单测试:

从该测试不难看出,在new的时候__construct()已经出发,下一次赋值后即可将var属性覆盖。
回到题目中,在反序列化后,题目进行了如下调用:
echo $x->readfile();
而该方法有任意文件读取问题。
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE 
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
所以答案也呼之欲出了,我们将file的值赋值为pctf.php即可getflag,需要注意的是我们的赋值是在魔法方法__construct()之后,所以并不会被置空。
魔法方法实战(二)
刚才的案例或许比较简单,我们在这样的基础上提高难度。
a);
       return '1';
    }

[1] [2] [3]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载