题目分析 <?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); }}
1.传入str,经过处理反序列化。 2.is_valid过滤:传入的string要是可见字符ascii值为32-125。 3.$op:op=="1"的时候会进入write方法处理,op=="2"的时候进入read方法处理。 利用php>7.1版本对类属性的检测不严格(对属性类型不敏感) 1.正常构造payload的话因为$op、$fliename、$content都是protected属性,序列化的的结果的属性名前面会有/00/00(或者%00%00),/00的ascii为0不可见的字符如下图,就会被is_valid方法拦下来。 2.php>7.1版本对类属性的检测不严格来绕过,将序列化里的portected属性换成public属性,就不会有/00。 3.弱类型绕过然后$obj = unserialize($str);会调用__destruct魔术方法,如果$op="2"的话就把$op="1"
这时候要使op="2"不成立且op=="2"成立,这里可以自己使用op等于整数2使得进入read方法里面。 构造payload
1.非预期的解法
2.通过php伪协议读取
比赛的是通过读取/proc/self/cmdline下的配置文件得到网站的绝对路径然后通过php伪协议读取flag的
参考:(https://blog.csdn.net/Oavinci/article/details/106998738)[https://blog.csdn.net/Oavinci/article/details/106998738]