0x01 前言
最近在学习php代码审计,刚好端午在家的时候菜的没事做,看了下cnvd,发现这个cms中sql注入问题挺多的,hhh,复现玩玩。主要目标就是复现一下网站前台的sql注入。所有均为个人理解,可能有理解错误的地方,还请师傅们指正。
此Cms下载链接https://www.jizhicms.cn/thread-95-1-1.html
0x02 cms目录架构
这个cms目录架构大致如下:
├── 404.html
├── A 后台控制文件
├── Conf公共函数
├── FrPHP框架
├── Home前台控制文件
├── Public公共静态文件
├── README.md
├── admin.php后台入口
├── backup备份
├── cache缓存
├── favicon.ico
├── index.php前台入口
├── install
├── readme.txt
├── static静态文件
└── web.config
0x03 函数
主要分析一下进行了过滤的函数(jizhicms_Beta1.6.7\FrPHP\lib\Controller.php)和进行sql处理的函数(jizhicms_Beta1.6.7\FrPHP\lib\Model.php)
3.1 frparam()函数
在此cms中大量调用了frparam()
函数,具体代码如下:
jizhicms_Beta1.6.7\FrPHP\lib\Controller.php
// 获取URL参数值
public function frparam($str=null, $int=0,$default = FALSE, $method = null){
$data = $this->_data;
if($str===null) return $data;
if(!array_key_exists($str,$data)){
return ($default===FALSE)?false:$default;
}
if($method===null){
$value = $data[$str];
}else{
$method = strtolower($method);
switch($method){
case 'get':
$value = $_GET[$str];
break;
case 'post':
$value = $_POST[$str];
break;
case 'cookie':
$value = $_COOKIE[$str];
break;
}
}
return format_param($value,$int);
}
这里跟进一下format_param()
jizhicms_Beta1.6.7\FrPHP\common\Functions.php
/**
参数过滤,格式化
**/
function format_param($value=null,$int=0){
if($value==null){
return '';}
switch ($int){
case 0://整数
return (int)$value;
case 1://字符串
$value=htmlspecialchars(trim($value), ENT_QUOTES);
if(!get_magic_quotes_gpc())$value = addslashes($value);
return $value;
case 2://数组
if($value=='')return '';
array_walk_recursive($value, "array_format");
return $value;
case 3://浮点
return (float)$value;
case 4:
if(!get_magic_quotes_gpc())$value = addslashes($value);
return trim($value);
}
}
从这可以看出来,format_param()
函数将传递进来的$value
变量通过$int
变量判断他是整数还是字符串然后做一个相应的处理。
总的来说frparam()
函数会对传递的参数进行过滤。
3.2 sql处理函数update()
// 修改数据
public function update($conditions,$row)
{
$where = "";
$row = $this->__prepera_format($row);
if(empty($row))return FALSE;
if(is_array($conditions)){
$join = array();
foreach( $conditions as $key => $condition ){
$condition = '\''.$condition.'\'';
$join[] = "{
$key} = {
$condition}";
}
$where = "WHERE ".join(" AND ",$join);
}else{
if(null != $conditions)$where = "WHERE ".$conditions;
}
foreach($row as $key => $value){
if($value!==null){
$value = '\''.$value.'\'';
$vals[] = "{
$key} = {
$value}";
}else{
$vals[] = "{
$key} = null";
}
}
$values = join(", ",$vals);
$table = self::$table;
$sql = "UPDATE {
$table} SET {
$values} {
$where}";
return $this->runSql($sql);
}
第28行$sql = "UPDATE {$table} SET {$values} {$where}";
可以知道是进行的一个数据的修改操作这里跟进一下runsql($sql)
jizhicms_Beta1.6.7\FrPHP\lib\Model.php
//执行SQL语句返回影响行数
public function runSql($sql)
{
return $this->db->exec($sql);
}
然后继续跟进exec($sql)
jizhicms_Beta1.6.7\FrPHP\db\DBholder.php
//执行一条 SQL 语句,并返回受影响的行数
public function exec($sql)
{
$this->arrSql[] = $sql;
$n = $this->pdo->exec($sql);
if(!$n){
$msg = $this->pdo->errorInfo();
if($msg[2]) Error_msg('数据库错误:' . $msg[2] . end($this->arrSql));
}
return $n;
}
可以注意到第七行代码$msg = $this->pdo->errorInfo();
也就是说这里会在错误的情况下会有个打印的操作。
由于本身sql处理的函数没有对参数值进行过滤,参数如果没有经过frparam()
函数进行处理就会造成sql注入。
3.3 sql处理函数findAll()
首先整个cms的sql执行的函数是在
jizhicms_Beta1.6.7\FrPHP\lib\Model.php下
// 查询所有
public function findAll($conditions=null,$order=null,$fields=null,$limit=null)
{
$where = '';
if(is_array($conditions)){
$join = array();
foreach( $conditions as $key => $value ){
$value = '\''.$value.'\'';
$join[] = "{
$key} = {
$value}";
}
$where = "WHERE ".join(" AND ",$join);
}else{
if(null != $conditions)$where = "WHERE ".$conditions;
}
if(is_array($order)){
$where .= ' ORDER BY ';
$where .= implode(',', $order);
}else{
if($order!=null)$where .= " ORDER BY ".$order;
}
if(!empty($limit))$where .= " LIMIT {
$limit}";
$fields = empty($fields) ? "*" : $fields;
$table = self::$table;
$sql = "SELECT {
$fields} FROM {