小迪安全46WEB 攻防-通用漏洞&PHP 反序列化&原生类&漏洞绕过&公私有属性

#知识点:

1、反序列化魔术方法全解

2、反序列化变量属性全解

3、反序列化魔术方法原生类

4、反序列化语言特性漏洞绕过

-其他魔术方法

-共有&私有&保护

-语言模式方法漏洞

-原生类获取利用配合 

#反序列化利用大概分类三类

-魔术方法的调用逻辑-如触发条件

-语言原生类的调用逻辑-SoapClient

-语言自身的安全缺陷-CVE-2016-7124

#反序列化课程点:

-PHP&Java&Python

序列化:对象转换为数组或字符串等格式

反序列化:将数组或字符串等格式转换成对象

serialize() //将一个对象转换成一个字符串

unserialize() //将字符串还原成一个对象

#PHP 反序列化漏洞

原理:未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而

导致代码执行,SQL 注入,目录遍历等不可控后果。在反序列化的过程中自动触发了某些

魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。

#魔术方法利用点分析:

触发:

unserialize 函数的变量可控,文件中存在可利用的类,类中有魔术方法:

__construct(): //构造函数,当对象 new 的时候会自动调用

__destruct()//析构函数当对象被销毁时会被自动调用

__wakeup(): //unserialize()时会被自动调用

__invoke(): //当尝试以调用函数的方法调用一个对象时,会被自动调用

__call(): //在对象上下文中调用不可访问的方法时触发

__callStatci(): //在静态上下文中调用不可访问的方法时触发

__get(): //用于从不可访问的属性读取数据

__set(): //用于将数据写入不可访问的属性

__isset(): //在不可访问的属性上调用 isset


Ø 方法&属性-调用详解&变量数据详解

Ø CTF-语言漏洞-__wakeup()方法绕过

Ø CTF-方法原生类-获取&利用&配合其他


#方法&属性-调用详解&变量数据详解

对象变量属性:

public(公共的):在本类内部、外部类、子类都可以访问

protect(受保护的):只有本类或子类或父类中可以访问

private(私人的):只有本类内部可以使用

序列化数据显示:

private 属性序列化的时候格式是%00 类名%00 成员名

protect 属性序列化的时候格式是%00*%00 成员名

__destruct方法:

具体代码:

//__construct __destruct 魔术方法 创建调用__construct 2种销毁调用__destruct
class Test{
    public $name;
    public $age;
    public $string;
    // __construct:实例化对象时被调用.其作用是拿来初始化一些值。
    public function __construct($name, $age, $string){
        echo "__construct 初始化"."<br>";
        $this->name = $name;
        $this->age = $age;
        $this->string = $string;
    }
    // __destruct:当删除一个对象或对象操作终止时被调用。其最主要的作用是拿来做垃圾回收机制。
    
//     * 当对象销毁时会调用此方法
 //    * 一是用户主动销毁对象,二是当程序结束时由引擎自动销毁
 //    *//*
    function __destruct(){
       echo "__destruct 类执行完毕"."<br>";
    }
}
// 主动销毁
$test = new Test("Spaceman",566, 'Test String');
unset($test);
echo '第一种执行完毕'.'<br>';
echo '----------------------<br>';
// 程序结束自动销毁
$test = new test("Spaceman",566, 'Test String');
echo '第二种执行完毕'.'<br>';

分析

__destruct:当删除一个对象或对象操作终止时被调用。其最主要的作用是拿来做垃圾回收机制    

一是用户主动销毁对象(unset),二是当程序结束时由引擎自动销毁

__toString方法

具体代码:

class Test
{
    public $variable = 'This is a string';

    public function good(){
        echo $this->variable . '<br />';
    }

    // 在对象当做字符串的时候会被调用
    public function __toString()
{
        return '__toString <br>';
    }
}

$a = new Test();
$a->good();
//输出调用
echo $a;

在对象当做字符串的时候会被调用,即输出变量a——则表示以字符串的形式输出

__CALL方法:

具体代码:

class Test{

    public function good($number,$string){
        echo '存在good方法'.'<br>';
        echo $number.'---------'.$string.'<br>';
    }

    // 当调用类中不存在的方法时,就会调用__call();
    public function __call($method,$args){
        echo '不存在'.$method.'方法'.'<br>';
        var_dump($args);
    }
}

$a = new Test();
$a->good(566,'nice');
$b = new Test();
$b->spaceman(899,'no');

调用某个方法, 若方法存在,则直接调用;若不存在,则会去调用__call函数。

__get()

具体代码:

class Test {
    public $n=123;

    // __get():访问不存在的成员变量时调用
    public function __get($name){
        echo '__get 不存在成员变量'.$name.'<br>';
    }
}

$a = new Test();
// 存在成员变量n,所以不调用__get
echo $a->n;
echo '<br>';
// 不存在成员变量spaceman,所以调用__get
echo $a->spaceman;

读取一个对象的属性时,若属性存在,则直接返回属性值;若不存在,则会调用__get函数与__call方法类似,只不过是其对象属性

__set()方法

具体代码:

class Test{
    public $data = 100;
    protected $noway=0;

    // __set():设置对象不存在的属性或无法访问(私有)的属性时调用
     //__set($name, $value)
    // * 用来为私有成员属性设置的值
    // * 第一个参数为你要为设置值的属性名,第二个参数是要给属性设置的值,没有返回值。
     
    public function __set($name,$value){
        echo '__set 不存在成员变量 '.$name.'<br>';
        echo '即将设置的值 '.$value."<br>";
        $this->noway=$value;
    }

    public function Get(){
        echo $this->noway;
    }
}

$a = new Test();
// 读取 noway 的值,初始为0
$a->Get();
echo '<br>';
// 无法访问(私有)noway属性时调用,并设置值为899
$a->noway  = 899;
// 经过__set方法的设置noway的值为899
$a->Get();
echo '<br>';
// 设置对象不存在的属性spaceman
$a->spaceman = 566;
// 经过__set方法的设置noway的值为566
$a->Get();

设置一个对象的属性时,若属性存在,则直接赋值;若不存在,则会调用__set函数。

__sleep()方法:

具体代码:

class Test{
    public $name;
    public $age;
    public $string;

    // __construct:实例化对象时被调用.其作用是拿来初始化一些值。
    public function __construct($name, $age, $string){
        echo "__construct 初始化"."<br>";
        $this->name = $name;
        $this->age = $age;
        $this->string = $string;
    }

    //  __sleep() :serialize之前被调用,可以指定要序列化的对象属性
    public function __sleep(){
        echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";
        // 例如指定只需要 name 和 age 进行序列化,必须返回一个数值
        return array('name', 'age');
    }
}

$a = new Test("Spaceman",566, 'Test String');
echo serialize($a);

serialize之前被调用,可以指定要序列化的对象属性。

__wakeup方法:

具体代码:

class Test{
    public $sex;
    public $name;
    public $age;

    public function __construct($name, $age, $sex){
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;
    }

    public function __wakeup(){
        echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";
        $this->age = 566;
    }
}

$person = new Test('spaceman',21,'男');
$a = serialize($person);
echo $a."<br>";
var_dump (unserialize($a));

Unserialize反序列化恢复对象之前调用该方法

__isset()方法

具体代码:

class Person{
    public $sex;
    private $name;
    private $age;

    public function __construct($name, $age, $sex){
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;
    }

    // __isset():当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
    public function __isset($content){
        echo "当在类外部使用isset()函数测定私有成员 {$content} 时,自动调用<br>";
        return isset($this->$content);
    }
}

$person = new Person("spaceman", 25,'男');
// public 成员
echo ($person->sex),"<br>";
// private 成员
echo isset($person->name);

检测对象的某个属性是否存在时执行此函数。当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用

__unset()方法

具体代码:

class Person{
    public $sex;
    private $name;
    private $age;

    public function __construct($name, $age, $sex){
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;
    }

    // __unset():销毁对象的某个属性时执行此函数
    public function __unset($content) {
        echo "当在类外部使用unset()函数来删除私有成员时自动调用的<br>";
        echo isset($this->$content)."<br>";
    }
}

$person = new Person("spaceman", 21,"男"); // 初始赋值
echo "666666<br>";
unset($person->name);//调用 属性私有
unset($person->age);//调用 属性私有
unset($person->sex);//不调用 属性共有

在不可访问的属性(private)上使用unset()时触发

__INVOKE()

具体代码:

class Test{
    // _invoke():以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用
    public function __invoke($param1, $param2, $param3)
{
        echo "这是一个对象<br>";
        var_dump($param1,$param2,$param3);
    }
}

$a  = new Test();
$a('spaceman',21,'男');

将对象当做函数来使用时执行此方法,通常不推荐这样做。

#私有和公有属性——案例
public(公共的):在本类内部、外部类、子类都可以访问

protect(受保护的):只有本类或子类或父类中可以访问

private(私人的):只有本类内部可以使用

序列化数据显示:

private 属性序列化的时候格式是%00 类名%00 成员名

protect 属性序列化的时候格式是%00*%00 成员名

具体代码:

class test{
    public $name="xiaodi";
    private $age="29";
    protected $sex="man";
}
$a=new test();
$a=serialize($a);
print_r($a);

将age改为public

所以通常对于序列化的数据,要进行一次编码,要考虑到%00的因素

#CTF-语言漏洞-__wakeup()方法绕过——案例

https://buuoj.cn/challenges

通过路径扫描,扫到一个www.zip的地址,访问便可进行下载去文件

此页面以GET方式select的变量接收,根据代码逻辑,得知__destruct方法在程序结束后被调用且password需等于100,__construct方法用new进行构造接收name和password 的值,__wakeup方法,在使用unserializ后自动调用,此时这里就出现了问题

我们要的是admin,而自动调用的是wakeup方法里的guest,那么就需想想wakeup方法该怎么绕过,通过历史漏洞得知——语言特性漏洞

靶场PHP版本符合

即将构造的序列化,变量个数改为3个,符合条件即可

(修改原理)

成功

#CTF-方法原生类-获取&利用&配合其他 

参考:浅析PHP原生类-安全客 - 安全资讯平台

版本会与原生类有直接的关系

-PHP有那些原生类-见脚本使用

Get.php

<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
    $methods = get_class_methods($class);
    foreach ($methods as $method) {
        if (in_array($method, array(
            '__destruct',
            '__toString',
            '__wakeup',
            '__call',
            '__callStatic',
            '__get',
            '__set',
            '__isset',
            '__unset',
            '__invoke',
            '__set_state'
        ))) {
            print $class . '::' . $method . "\n";
        }
    }
} 

会将魔术方法里自带的类给打印出来

-常见使用的原生类-见参加案例

1.ctf-xss

从此代码可以猜测要用的方法可能是To_string,由于它的本身代码就只有这一点,那么无法通过其他对象进行触发To_string,故需要用原生类的To_string,通过get.php,可以得到相关的To_string对象

Exception对象

对其进行构造,并加入xss攻击

成功

2.Ctfshow259

由于此题的getflag();方法,不知道它的具体是什么才可触发,所以先把它姑且为不可访问的类型,也就是可被__call()、__get()所触发,

查看自身的原生类

对其进行搜索

找到构造payload(题目中要求,ip为127.0.0.1,token为ctfshow)

将flag.txt发送到本地

-原生类该怎么使用-见官方说明

0、生成原生类

补:phar、字符串逃逸

反序列化:自身不是漏洞,只是一种方法,在方法中会引发一些危险函数。

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
靶场,是指为信息安全人员提供实战演练、渗透测试和攻防对抗等训练环境的虚拟或实体场地。在不同的领域中,靶场扮演着重要的角色,尤其是在网络安全领域,靶场成为培养和提高安全专业人员技能的重要平台。 首先,靶场为安全从业者提供了一个模拟真实网络环境的平台。通过构建似实际网络的拓扑结构、部署各种安全设备和应用,靶场可以模拟出多样化的网络攻防场景。这使得安全人员能够在安全的环境中进行实际操作,全面提升其实战能力。 其次,靶场是渗透测试和漏洞攻防演练的理想场所。在靶场中,安全专业人员可以模拟攻击者的行为,发现系统和应用的漏洞,并进行渗透测试,从而及时修复和改进防御机制。同时,这也为防御方提供了锻炼机会,通过对抗攻击提高防御能力。 靶场的搭建还促进了团队协作与沟通。在攻防对抗中,往往需要多人协同作战,团队成员之间需要密切配合,共同制定攻击和防御策略。这有助于培养团队合作意识,提高协同作战的效率。 此外,靶场为学习者提供了一个安全的学习环境。在靶场中,学生可以通过实际操作掌握安全知识,了解攻击技术和防御策略。这样的学习方式比传统的理论课程更加生动直观,有助于深化对安全领域的理解。 最后,靶场也是安全社区交流的平台。在靶场中,安全从业者可以分享攻防经验,交流最新的安全威胁情报,共同探讨解决方案。这有助于建立更广泛的安全社区,推动整个行业的发展。 总体而言,靶场在信息安全领域具有重要地位,为安全专业人员提供了实战演练的机会,促进了团队协作与沟通,为学习者提供了安全的学习环境,同时也是安全社区交流的重要平台。通过靶场的实践操作,安全从业者能够更好地应对不断演变的网络威胁,提高整体的安全水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值