信呼OA办公系统sql注入漏洞分析

漏洞描述

信呼OA办公系统uploadAction存在SQL注入漏洞,攻击者可利用该漏洞获取数据库敏感信息。

环境搭建

源码下载地址:https://github.com/rainrocka/xinhu

下载后解压到本地网站根目录下,配置好数据库,然后安装即可

默认密码是admin/123456,登录进去得更改一次密码

路由分析

在include/View.php中详细介绍了路由的定义

php 复制代码
<?php  
if(!isset($ajaxbool))$ajaxbool = $rock->jm->gettoken('ajaxbool', 'false');  
$ajaxbool   = $rock->get('ajaxbool', $ajaxbool);  
$p  = PROJECT;//define('PROJECT', 'webmain');  
if(!isset($m))$m='index';  
if(!isset($a))$a='default';  
if(!isset($d))$d='';  
$m  = $rock->get('m', $m);  
$a  = $rock->get('a', $a);  
$d  = $rock->get('d', $d);  
​  
define('M', $m);  
define('A', $a);  
define('D', $d);  
define('P', $p);  
​  
$_m = $m;  
if($rock->contain($m, '|')){  
    $_mas = explode('|', $m);//以|分割变量m  
    $m= $_mas[0];  
    $_m = $_mas[1];  
}  
include_once($rock->strformat('?0/?1/?1Action.php',ROOT_PATH, $p));//调用strformat进行格式化,其中?0、?1 等是占位符  
$rand   = date('YmdHis').rand(1000,9999);//随机值  
if(substr($d,-1)!='/' && $d!='')$d.='/';//若$d最后一个字符不是/且$d不是空就在$d后面加一个/  
$errormsg   = '';  
$methodbool = true;  
$actpath    = $rock->strformat('?0/?1/?2?3',ROOT_PATH, $p, $d, $_m);//$actpath:根目录/webmain/$d/$_m  
define('ACTPATH', $actpath);  
$actfile    = $rock->strformat('?0/?1Action.php',$actpath, $m);//$actfile:根目录/webmain/$d/$_m/$mAction.php  
$actfile1   = $rock->strformat('?0/?1Action.php',$actpath, $_m);//$actfile1:根目录/webmain/$d/$_m/$_mAction.php  
$actbstr = null;  
//依次判断$actfile1以及$actfile哪个文件存在,哪个存在包含哪个  
if(file_exists($actfile1))  
    include_once($actfile1);  
if(file_exists($actfile)){  
    include_once($actfile);  
    $clsname    = ''.$m.'ClassAction';  
    $xhrock = new $clsname();//创建一个与$m相关类的对象  
    $actname    = ''.$a.'Action';//在$a后接一个Action  
    if($ajaxbool == 'true')//判断ajaxbool是否为true  
        $actname    = ''.$a.'Ajax';//在$a后接一个Ajax  
    if(method_exists($xhrock, $actname)){//检测类中是否存在该方法  
        $xhrock->beforeAction();  
        $actbstr = $xhrock->$actname();  
        $xhrock->bodyMessage = $actbstr;  
        if(is_string($actbstr)){echo $actbstr;$xhrock->display=false;}  
        if(is_array($actbstr)){echo json_encode($actbstr);$xhrock->display=false;}  
    }else{  
        $methodbool = false;  
        if($ajaxbool == 'false')echo ''.$actname.' not found;';  
    }  
    $xhrock->afterAction();  
}else{  
    echo 'actionfile not exists;';  
    $xhrock = new Action();  
}  
​  
$_showbool = false;  
if($xhrock->display && ($ajaxbool == 'html' || $ajaxbool == 'false')){  
    $xhrock->smartydata['p']    = $p;  
    $xhrock->smartydata['a']    = $a;  
    $xhrock->smartydata['m']    = $m;  
    $xhrock->smartydata['d']    = $d;  
    $xhrock->smartydata['rand'] = $rand;  
    $xhrock->smartydata['qom']  = QOM;  
    $xhrock->smartydata['path'] = PATH;  
    $xhrock->smartydata['sysurl']= SYSURL;  
    $temppath   = ''.ROOT_PATH.'/'.$p.'/';  
    $tplpaths   = ''.$temppath.'/'.$d.''.$m.'/';  
    $tplname    = 'tpl_'.$m.'';  
    if($a!='default')$tplname  .= '_'.$a.'';  
    $tplname       .= '.'.$xhrock->tpldom.'';  
    $mpathname  = $tplpaths.$tplname;  
    if($xhrock->displayfile!='' && file_exists($xhrock->displayfile))$mpathname = $xhrock->displayfile;  
    if(!file_exists($mpathname) || !$methodbool){  
        if(!$methodbool){  
            $errormsg   = 'in ('.$m.') not found Method('.$a.');';  
        }else{  
            $errormsg   = ''.$tplname.' not exists;';  
        }  
        echo $errormsg;  
    }else{  
        $_showbool = true;  
    }  
}  
if($xhrock->display && ($ajaxbool == 'html' || $xhrock->tpltype=='html' || $ajaxbool == 'false') && $_showbool){  
    $xhrock->setHtmlData();  
    $da = $xhrock->smartydata;  
    foreach($xhrock->assigndata as $_k=>$_v)$$_k=$_v;  
    include_once($mpathname);  
    $_showbool = false;  
}

这里用get方式会接收m,d,a,ajaxbool参数

  • a j a x b o o l :用于判断请求是否为 A J A X 请求,默认值从 ajaxbool:用于判断请求是否为AJAX请求,默认值从 ajaxbool:用于判断请求是否为AJAX请求,默认值从rock->jm->gettoken获取。当ajaxbool为false时,是对xxxAction.php的内容访问,当ajaxbool为true时,是对xxxAjax.php的内容进行访问
  • $m, $a, $d:分别代表php文件名(不含Action)、动作名(action)、目录名(webadmin下的子目录),默认值分别为index,default、空字符串。

举例:index.php?a=deluser&m=imgroup&d=&ajaxbool=true&gid=38&sid=1

  • $m:user,表示请求的是webadmin下的imgroup 目录。
  • $a:list,表示请求的方法是 deluser。
  • ajaxbool:true,表示这是一个 AJAX 请求

漏洞分析

漏洞的位置在webmain/task/api/uploadAction.php中

核心代码在getmfilvAction()方法里边

php 复制代码
	public function getmfilvAction()
	{
		$fileid = (int)$this->get('fileid','0');
		$frs 	= m('file')->getone($fileid);
		if(!$frs)return returnerror('不存在');
		
		$lujing	= $frs['filepathout'];
		if(isempt($lujing)){
			$lujing = $frs['filepath'];
			if(substr($lujing,0,4)!='http' && !file_exists($lujing))return returnerror('文件不存在了');
		}
		$fileext = $frs['fileext'];
		
		$fname = $this->jm->base64decode($this->get('fname'));
		$fname = (isempt($fname)) ? $frs['filename'] : ''.$fname.'.'.$fileext.'';
		
		$filepath = ''.UPDIR.'/'.date('Y-m').'/'.date('d').'_rocktpl'.rand(1000,9999).'_'.$fileid.'.'.$fileext.'';
		$this->rock->createtxt($filepath, file_get_contents($lujing));
		
		$uarr = array(
			'filename' => $fname,
			'fileext' => $fileext,
			'filepath' => $filepath,
			'filesize' => filesize($filepath),
			'filesizecn' => $this->rock->formatsize(filesize($filepath)),
			'optid' 	=> $this->adminid,
			'optname' 	=> $this->adminname,
			'adddt' 	=> $this->rock->now,
			'ip' 		=> $this->rock->ip,
			'web' 		=> $this->rock->web,
		);
		$uarr['id'] = m('file')->insert($uarr);
	
		return returnsuccess($uarr);
	}

getmfilvAction 方法的主要功能是从数据库中获取文件信息,读取文件内容,生成新的文件,并将新文件的信息记录到数据库中。最后,返回生成文件的信息。

在该方法中有两个可以控制的参数,一个是fileid另一个是fname,但是fileid参数会进行类型转换为int类型存在注入几率几乎为零,其中fname还进行了base64解码操作,最后将两个参数的内容连同其他文件基本信息进行数据库的插入操作,在这个地方想要确定有sql注入需要确定get方法以及insert方法是否存在sql语句的过滤

进入Model.php中的inser()方法

php 复制代码
	public function insert($arr)
	{
		$nid = 0;
		if($this->record($arr, ''))$nid = $this->db->insert_id();
		return $nid;
	}

对传入的参数进行record方法的校验若不为false,那么就获取到其id值

跟进record方法

再跟进 public function record( t a b l e , table, table,array,$where='')

php 复制代码
	{
		$addbool  	= true;
		if(!$this->isempt($where))$addbool=false;
		$cont		= '';
		if(is_array($array)){
			foreach($array as $key=>$val){
				$cont.=",`$key`=".$this->toaddval($val)."";
			}
			$cont	= substr($cont,1);
		}else{
			$cont	= $array;
		}
		$table = $this->gettables($table);
		if($addbool){
			$sql="insert into $table set $cont";
		}else{
			$where = $this->getwhere($where);
			$sql="update $table set $cont where $where";
		}
		return $this->tranbegin($sql);
	}

代码解读:该方法首先是是对where参数进行非空判断,前面代码是将where设置为空了,那么 a d d b o o l 就是 f a l s e 。接着就是判断 addbool就是false。接着就是判断 addbool就是false。接着就是判断array是否为数组,若是数组就进行遍历将每个字段和对应的值拼接到 c o n t 字符串中并调用 t o a d d v a l 方法确保传入的字符串被正确地格式化为 S Q L 语句中的字符串值,但该方法并没有对 s q l 进行任何过滤 ; 然后调用 g e t t a b l e s 设置表名,接着进入 e l s e 语句,我们清晰的看到 cont字符串中并调用toaddval方法确保传入的字符串被正确地格式化为 SQL 语句中的字符串值,但该方法并没有对sql进行任何过滤;然后调用gettables设置表名,接着进入else语句,我们清晰的看到 cont字符串中并调用toaddval方法确保传入的字符串被正确地格式化为SQL语句中的字符串值,但该方法并没有对sql进行任何过滤;然后调用gettables设置表名,接着进入else语句,我们清晰的看到sql变量直接将$cont语句拼接到了sql语句中

接着我们去get方法中看看该方法对传入的内容有什么过滤,来到rockClass.php中

php 复制代码
	public function get($name,$dev='', $lx=0)
	{
		$val=$dev;
		if(isset($_GET[$name]))$val=$_GET[$name];
		if($this->isempt($val))$val=$dev;
		return $this->jmuncode($val, $lx, $name);
	}

这个方法只是判断是否进行get传参如果传参成功就进行赋值操作,之后进行非空判断,调用jmucade方法()将其值返回。该方法中并没有对sql语句进行过滤

这个文件里有个construct()方法,实例化rockClass对象就会触发

php 复制代码
	public function __construct()
	{		
		$this->ip		= $this->getclientip();
		$this->host		= isset($_SERVER['HTTP_HOST'])		? $_SERVER['HTTP_HOST']		: '' ;
		if($this->host && substr($this->host,-3)==':80')$this->host = str_replace(':80', '', $this->host);
		$this->url		= '';
		$this->isqywx	= false;
		$this->win		= php_uname();
		$this->HTTPweb	= isset($_SERVER['HTTP_USER_AGENT'])? $_SERVER['HTTP_USER_AGENT']	: '' ;
		$this->web		= $this->getbrowser();
		$this->unarr	= explode(',','1,2');
		$this->now		= $this->now();
		$this->date		= date('Y-m-d');
		$this->lvlaras  = explode(',','select ,
		alter table,delete ,drop ,update ,insert into,load_file,/*,*/,union,<script,</script,sleep(,outfile,eval(,user(,phpinfo(),select*,union%20,sleep%20,select%20,delete%20,drop%20,and%20');
		$this->lvlaraa  = explode(',','select,alter,delete,drop,update,/*,*/,insert,from,time_so_sec,convert,from_unixtime,unix_timestamp,curtime,time_format,union,concat,information_schema,group_concat,length,load_file,outfile,database,system_user,current_user,user(),found_rows,declare,master,exec,(),select*from,select*');
		$this->lvlarab	= array();
		foreach($this->lvlaraa as $_i)$this->lvlarab[]='';
	}

这里过滤大部分sql注入一些敏感字符,通过以上分析发现这个fname参数经过get传参后会进行base64decode方法进行base64解密,那么如果我们将filename传入恶意的sql语句进行base64编码,就会绕过rockClass.php中的construct方法中的sql语句的过滤,之后进行base64解密又拼接到sql语句造成sql注入的形成

漏洞验证

payload:

php 复制代码
api.php?a=getmfilv&m=upload|api&d=task&fileid=1&fname=MScgYW5kIHNsZWVwKDMpIw==

payload解释:漏洞的位置在webmain/task/api/uploadAction.php中的getmfilv方法中,d传task参数表示在webadmin/task目录下,m传upload|api,第一部分 upload 会被赋值给 $m,第二部分 api 会被赋值给 $_m,表示

api下的uploadAction.php文件,a传getmfilv文件表示调用uploadAction.php的、getmfilv()方法,fname传sql注入的payload,进行base64编码

成功延时

相关推荐
anddddoooo2 小时前
域内证书维权
服务器·网络·网络协议·安全·网络安全·https·ssl
顾比魁7 小时前
pikachu之CSRF防御:给你的请求加上“网络身份证”
前端·网络·网络安全·csrf
廾匸07057 小时前
《95015网络安全应急响应分析报告(2024)》
网络安全·行业报告·应急响应
仇辉攻防7 小时前
【云安全】云原生- K8S 污点横移
web安全·网络安全·云原生·容器·kubernetes·k8s·安全威胁分析
渗透测试老鸟-九青11 小时前
HW面试经验分享 | 北京蓝中研判岗
网络·经验分享·安全·网络安全·面试·渗透·代码审计
火绒终端安全管理系统1 天前
火绒终端安全管理系统V2.0【系统防御功能】
网络·安全·网络安全·火绒安全·火绒
H轨迹H1 天前
BUUCTF-Web方向16-20wp
网络安全·渗透测试·ctf·buuctf
D-river1 天前
【如何基于Debian构建Kali Linux】
linux·网络·安全·网络安全
donglxd1 天前
防御黑客系列-第一集-电脑登录记录提示和登录远程推送
windows·网络安全·电脑·系统安全
温柔小胖2 天前
sql注入之python脚本进行时间盲注和布尔盲注
数据库·sql·网络安全